diff options
Diffstat (limited to 'percona/5.0.84-b18-20090811/mirror_binlog.patch')
-rw-r--r-- | percona/5.0.84-b18-20090811/mirror_binlog.patch | 2694 |
1 files changed, 2694 insertions, 0 deletions
diff --git a/percona/5.0.84-b18-20090811/mirror_binlog.patch b/percona/5.0.84-b18-20090811/mirror_binlog.patch new file mode 100644 index 0000000..d52e806 --- /dev/null +++ b/percona/5.0.84-b18-20090811/mirror_binlog.patch @@ -0,0 +1,2694 @@ +diff -r 66cc9e0a6768 mysql-test/lib/mtr_cases.pl +--- a/mysql-test/lib/mtr_cases.pl Thu Dec 04 21:37:12 2008 -0800 ++++ b/mysql-test/lib/mtr_cases.pl Thu Dec 04 21:46:15 2008 -0800 +@@ -334,6 +334,10 @@ + + $tinfo->{'slave_num'}= 1; # Default for rpl* tests, use one slave + ++ if ( $tname eq 'rpl_mirror_binlog' ) ++ { ++ $tinfo->{'slave_num'}= 3; ++ } + } + + if ( defined mtr_match_prefix($tname,"federated") ) +@@ -344,15 +348,20 @@ + + my $master_opt_file= "$testdir/$tname-master.opt"; + my $slave_opt_file= "$testdir/$tname-slave.opt"; +- my $slave_mi_file= "$testdir/$tname.slave-mi"; ++ my $slave_mi_files= ["$testdir/$tname.slave-mi", ++ "$testdir/$tname.1.slave-mi", ++ "$testdir/$tname.2.slave-mi"]; + my $master_sh= "$testdir/$tname-master.sh"; + my $slave_sh= "$testdir/$tname-slave.sh"; + my $disabled_file= "$testdir/$tname.disabled"; + my $im_opt_file= "$testdir/$tname-im.opt"; + +- $tinfo->{'master_opt'}= []; +- $tinfo->{'slave_opt'}= []; +- $tinfo->{'slave_mi'}= []; ++ $tinfo->{'master_opt'}= []; ++ $tinfo->{'slave_opt'}= []; ++ $tinfo->{'slave_mi'}= {}; ++ $tinfo->{'slave_mi'}{0}= []; ++ $tinfo->{'slave_mi'}{1}= []; ++ $tinfo->{'slave_mi'}{2}= []; + + if ( -f $master_opt_file ) + { +@@ -427,9 +436,14 @@ + push(@{$tinfo->{'slave_opt'}}, @$slave_opt); + } + +- if ( -f $slave_mi_file ) ++ my $mi_idx= 0; ++ foreach my $slave_mi_file ( @$slave_mi_files ) + { +- $tinfo->{'slave_mi'}= mtr_get_opts_from_file($slave_mi_file); ++ if ( -f $slave_mi_file ) ++ { ++ $tinfo->{'slave_mi'}{$mi_idx}= mtr_get_opts_from_file($slave_mi_file); ++ } ++ $mi_idx+= 1; + } + + if ( -f $master_sh ) +diff -r 66cc9e0a6768 mysql-test/mysql-test-run.pl +--- a/mysql-test/mysql-test-run.pl Thu Dec 04 21:37:12 2008 -0800 ++++ b/mysql-test/mysql-test-run.pl Thu Dec 04 21:46:15 2008 -0800 +@@ -275,6 +275,7 @@ + our $opt_stress_test_file= ""; + + our $opt_warnings; ++our $opt_slave_innodb= 0; + + our $opt_skip_ndbcluster= 0; + our $opt_skip_ndbcluster_slave= 0; +@@ -299,6 +300,8 @@ + our $used_binlog_format; + our $used_default_engine; + our $debug_compiled_binaries; ++ ++our $current_testname= ""; + + our %mysqld_variables; + +@@ -645,6 +648,7 @@ + 'testcase-timeout=i' => \$opt_testcase_timeout, + 'suite-timeout=i' => \$opt_suite_timeout, + 'warnings|log-warnings' => \$opt_warnings, ++ 'slave-innodb' => \$opt_slave_innodb, + + # Options which are no longer used + (map { $_ => \&warn_about_removed_option } @removed_options), +@@ -1001,6 +1005,14 @@ + { + $ENV{'BIG_TEST'}= 1; + } ++ ++ # -------------------------------------------------------------------------- ++ # Big test flags ++ # -------------------------------------------------------------------------- ++ if ( $opt_big_test ) ++ { ++ $ENV{'BIG_TEST'}= 1; ++ } + + # -------------------------------------------------------------------------- + # Gcov flag +@@ -1885,7 +1897,9 @@ + $ENV{'SLAVE_MYSOCK'}= $slave->[0]->{'path_sock'}; + $ENV{'SLAVE_MYPORT'}= $slave->[0]->{'port'}; + $ENV{'SLAVE_MYPORT1'}= $slave->[1]->{'port'}; ++ $ENV{'SLAVE_MYSOCK1'}= $slave->[1]->{'path_sock'}; + $ENV{'SLAVE_MYPORT2'}= $slave->[2]->{'port'}; ++ $ENV{'SLAVE_MYSOCK2'}= $slave->[2]->{'path_sock'}; + $ENV{'MYSQL_TCP_PORT'}= $mysqld_variables{'port'}; + $ENV{'DEFAULT_MASTER_PORT'}= $mysqld_variables{'master-port'}; + +@@ -2375,6 +2389,8 @@ + if ( ! $glob_win32 ) + { + symlink("$glob_mysql_test_dir/std_data", "$opt_vardir/std_data_ln"); ++ my @a = ("chmod", "-R", "o+r", "$glob_mysql_test_dir/std_data"); ++ system(@a) == 0 or die "system @ failed: $?" + } + else + { +@@ -3466,6 +3482,8 @@ + $ENV{'TZ'}= $tinfo->{'timezone'}; + mtr_verbose("Setting timezone: $tinfo->{'timezone'}"); + ++ $current_testname= $tinfo->{'name'}; ++ + my $master_restart= run_testcase_need_master_restart($tinfo); + my $slave_restart= run_testcase_need_slave_restart($tinfo); + +@@ -3881,7 +3899,8 @@ + unless $mysqld->{'type'} eq 'slave'; + + mtr_add_arg($args, "%s--init-rpl-role=slave", $prefix); +- if (! ( $opt_skip_slave_binlog || $skip_binlog )) ++ ++ if (! ($opt_skip_slave_binlog or ($current_testname eq 'rpl_mirror_binlog')) ) + { + mtr_add_arg($args, "%s--log-bin=%s/log/slave%s-bin", $prefix, + $opt_vardir, $sidx); # FIXME use own dir for binlogs +@@ -4568,7 +4587,7 @@ + if ( ! $slave->[$idx]->{'pid'} ) + { + mysqld_start($slave->[$idx],$tinfo->{'slave_opt'}, +- $tinfo->{'slave_mi'}); ++ $tinfo->{'slave_mi'}{$idx}); + + } + } +@@ -4580,7 +4599,6 @@ + # Wait for clusters to start + foreach my $cluster (@{$clusters}) + { +- + next if !$cluster->{'pid'}; + + if (ndbcluster_wait_started($cluster, "")) +@@ -5179,6 +5197,7 @@ + skip-im Don't start IM, and skip the IM test cases + big-test Set the environment variable BIG_TEST, which can be + checked from test cases. ++ + + Options that specify ports + +diff -r 66cc9e0a6768 mysql-test/r/rpl_mirror_binlog.result +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/mysql-test/r/rpl_mirror_binlog.result Thu Dec 04 21:46:15 2008 -0800 +@@ -0,0 +1,441 @@ ++stop slave; ++drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; ++reset master; ++reset slave; ++drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; ++start slave; ++drop table if exists t1; ++create table t1(n int) engine = InnoDB; ++insert into t1 values (300); ++insert into t1 values (299); ++insert into t1 values (298); ++insert into t1 values (297); ++insert into t1 values (296); ++insert into t1 values (295); ++insert into t1 values (294); ++insert into t1 values (293); ++insert into t1 values (292); ++insert into t1 values (291); ++insert into t1 values (290); ++insert into t1 values (289); ++insert into t1 values (288); ++insert into t1 values (287); ++insert into t1 values (286); ++insert into t1 values (285); ++insert into t1 values (284); ++insert into t1 values (283); ++insert into t1 values (282); ++insert into t1 values (281); ++insert into t1 values (280); ++insert into t1 values (279); ++insert into t1 values (278); ++insert into t1 values (277); ++insert into t1 values (276); ++insert into t1 values (275); ++insert into t1 values (274); ++insert into t1 values (273); ++insert into t1 values (272); ++insert into t1 values (271); ++insert into t1 values (270); ++insert into t1 values (269); ++insert into t1 values (268); ++insert into t1 values (267); ++insert into t1 values (266); ++insert into t1 values (265); ++insert into t1 values (264); ++insert into t1 values (263); ++insert into t1 values (262); ++insert into t1 values (261); ++insert into t1 values (260); ++insert into t1 values (259); ++insert into t1 values (258); ++insert into t1 values (257); ++insert into t1 values (256); ++insert into t1 values (255); ++insert into t1 values (254); ++insert into t1 values (253); ++insert into t1 values (252); ++insert into t1 values (251); ++insert into t1 values (250); ++insert into t1 values (249); ++insert into t1 values (248); ++insert into t1 values (247); ++insert into t1 values (246); ++insert into t1 values (245); ++insert into t1 values (244); ++insert into t1 values (243); ++insert into t1 values (242); ++insert into t1 values (241); ++insert into t1 values (240); ++insert into t1 values (239); ++insert into t1 values (238); ++insert into t1 values (237); ++insert into t1 values (236); ++insert into t1 values (235); ++insert into t1 values (234); ++insert into t1 values (233); ++insert into t1 values (232); ++insert into t1 values (231); ++insert into t1 values (230); ++insert into t1 values (229); ++insert into t1 values (228); ++insert into t1 values (227); ++insert into t1 values (226); ++insert into t1 values (225); ++insert into t1 values (224); ++insert into t1 values (223); ++insert into t1 values (222); ++insert into t1 values (221); ++insert into t1 values (220); ++insert into t1 values (219); ++insert into t1 values (218); ++insert into t1 values (217); ++insert into t1 values (216); ++insert into t1 values (215); ++insert into t1 values (214); ++insert into t1 values (213); ++insert into t1 values (212); ++insert into t1 values (211); ++insert into t1 values (210); ++insert into t1 values (209); ++insert into t1 values (208); ++insert into t1 values (207); ++insert into t1 values (206); ++insert into t1 values (205); ++insert into t1 values (204); ++insert into t1 values (203); ++insert into t1 values (202); ++insert into t1 values (201); ++insert into t1 values (200); ++insert into t1 values (199); ++insert into t1 values (198); ++insert into t1 values (197); ++insert into t1 values (196); ++insert into t1 values (195); ++insert into t1 values (194); ++insert into t1 values (193); ++insert into t1 values (192); ++insert into t1 values (191); ++insert into t1 values (190); ++insert into t1 values (189); ++insert into t1 values (188); ++insert into t1 values (187); ++insert into t1 values (186); ++insert into t1 values (185); ++insert into t1 values (184); ++insert into t1 values (183); ++insert into t1 values (182); ++insert into t1 values (181); ++insert into t1 values (180); ++insert into t1 values (179); ++insert into t1 values (178); ++insert into t1 values (177); ++insert into t1 values (176); ++insert into t1 values (175); ++insert into t1 values (174); ++insert into t1 values (173); ++insert into t1 values (172); ++insert into t1 values (171); ++insert into t1 values (170); ++insert into t1 values (169); ++insert into t1 values (168); ++insert into t1 values (167); ++insert into t1 values (166); ++insert into t1 values (165); ++insert into t1 values (164); ++insert into t1 values (163); ++insert into t1 values (162); ++insert into t1 values (161); ++insert into t1 values (160); ++insert into t1 values (159); ++insert into t1 values (158); ++insert into t1 values (157); ++insert into t1 values (156); ++insert into t1 values (155); ++insert into t1 values (154); ++insert into t1 values (153); ++insert into t1 values (152); ++insert into t1 values (151); ++insert into t1 values (150); ++insert into t1 values (149); ++insert into t1 values (148); ++insert into t1 values (147); ++insert into t1 values (146); ++insert into t1 values (145); ++insert into t1 values (144); ++insert into t1 values (143); ++insert into t1 values (142); ++insert into t1 values (141); ++insert into t1 values (140); ++insert into t1 values (139); ++insert into t1 values (138); ++insert into t1 values (137); ++insert into t1 values (136); ++insert into t1 values (135); ++insert into t1 values (134); ++insert into t1 values (133); ++insert into t1 values (132); ++insert into t1 values (131); ++insert into t1 values (130); ++insert into t1 values (129); ++insert into t1 values (128); ++insert into t1 values (127); ++insert into t1 values (126); ++insert into t1 values (125); ++insert into t1 values (124); ++insert into t1 values (123); ++insert into t1 values (122); ++insert into t1 values (121); ++insert into t1 values (120); ++insert into t1 values (119); ++insert into t1 values (118); ++insert into t1 values (117); ++insert into t1 values (116); ++insert into t1 values (115); ++insert into t1 values (114); ++insert into t1 values (113); ++insert into t1 values (112); ++insert into t1 values (111); ++insert into t1 values (110); ++insert into t1 values (109); ++insert into t1 values (108); ++insert into t1 values (107); ++insert into t1 values (106); ++insert into t1 values (105); ++insert into t1 values (104); ++insert into t1 values (103); ++insert into t1 values (102); ++insert into t1 values (101); ++insert into t1 values (100); ++insert into t1 values (99); ++insert into t1 values (98); ++insert into t1 values (97); ++insert into t1 values (96); ++insert into t1 values (95); ++insert into t1 values (94); ++insert into t1 values (93); ++insert into t1 values (92); ++insert into t1 values (91); ++insert into t1 values (90); ++insert into t1 values (89); ++insert into t1 values (88); ++insert into t1 values (87); ++insert into t1 values (86); ++insert into t1 values (85); ++insert into t1 values (84); ++insert into t1 values (83); ++insert into t1 values (82); ++insert into t1 values (81); ++insert into t1 values (80); ++insert into t1 values (79); ++insert into t1 values (78); ++insert into t1 values (77); ++insert into t1 values (76); ++insert into t1 values (75); ++insert into t1 values (74); ++insert into t1 values (73); ++insert into t1 values (72); ++insert into t1 values (71); ++insert into t1 values (70); ++insert into t1 values (69); ++insert into t1 values (68); ++insert into t1 values (67); ++insert into t1 values (66); ++insert into t1 values (65); ++insert into t1 values (64); ++insert into t1 values (63); ++insert into t1 values (62); ++insert into t1 values (61); ++insert into t1 values (60); ++insert into t1 values (59); ++insert into t1 values (58); ++insert into t1 values (57); ++insert into t1 values (56); ++insert into t1 values (55); ++insert into t1 values (54); ++insert into t1 values (53); ++insert into t1 values (52); ++insert into t1 values (51); ++insert into t1 values (50); ++insert into t1 values (49); ++insert into t1 values (48); ++insert into t1 values (47); ++insert into t1 values (46); ++insert into t1 values (45); ++insert into t1 values (44); ++insert into t1 values (43); ++insert into t1 values (42); ++insert into t1 values (41); ++insert into t1 values (40); ++insert into t1 values (39); ++insert into t1 values (38); ++insert into t1 values (37); ++insert into t1 values (36); ++insert into t1 values (35); ++insert into t1 values (34); ++insert into t1 values (33); ++insert into t1 values (32); ++insert into t1 values (31); ++insert into t1 values (30); ++insert into t1 values (29); ++insert into t1 values (28); ++insert into t1 values (27); ++insert into t1 values (26); ++insert into t1 values (25); ++insert into t1 values (24); ++insert into t1 values (23); ++insert into t1 values (22); ++insert into t1 values (21); ++insert into t1 values (20); ++insert into t1 values (19); ++insert into t1 values (18); ++insert into t1 values (17); ++insert into t1 values (16); ++insert into t1 values (15); ++insert into t1 values (14); ++insert into t1 values (13); ++insert into t1 values (12); ++insert into t1 values (11); ++insert into t1 values (10); ++insert into t1 values (9); ++insert into t1 values (8); ++insert into t1 values (7); ++insert into t1 values (6); ++insert into t1 values (5); ++insert into t1 values (4); ++insert into t1 values (3); ++insert into t1 values (2); ++insert into t1 values (1); ++"The following are SLAVE." ++select count(distinct n) from t1; ++count(distinct n) ++300 ++select min(n) from t1; ++min(n) ++1 ++select max(n) from t1; ++max(n) ++300 ++show slave status; ++Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master ++Waiting for master to send event 127.0.0.1 root 9306 1 master-bin.000014 2849 # # master-bin.000014 Yes Yes # 0 0 2849 # None 0 No # ++show master status; ++File Position Binlog_Do_DB Binlog_Ignore_DB ++master-bin.000014 2849 ++"The following are SLAVE1." ++start slave; ++select count(distinct n) from t1; ++count(distinct n) ++300 ++select min(n) from t1; ++min(n) ++1 ++select max(n) from t1; ++max(n) ++300 ++show slave status; ++Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master ++Waiting for master to send event 127.0.0.1 root 9308 1 master-bin.000014 2849 # # master-bin.000014 Yes Yes # 0 0 2849 # None 0 No # ++"The following are SLAVE." ++MAKE MASTER MASTER_LOG_FILE='master-bin', ++MASTER_SERVER_ID=2, ++INDEX='replication-log'; ++ERROR HY000: Could not initialize master info structure; more error messages can be found in the MySQL error log ++stop slave; ++MAKE MASTER MASTER_LOG_FILE='master-bin', ++MASTER_SERVER_ID=2, ++INDEX='replication_log'; ++ERROR HY000: Could not initialize master info structure; more error messages can be found in the MySQL error log ++MAKE MASTER REVOKE SESSION WITH KILL; ++MAKE MASTER MASTER_LOG_FILE='master-bin', ++MASTER_SERVER_ID=2, ++INDEX='replication_log' ++ WITH BINLOG; ++MAKE MASTER GRANT SESSION; ++delete from t1 where n > 250; ++select count(distinct n) from t1; ++count(distinct n) ++250 ++"The following are SLAVE1." ++select count(distinct n) from t1; ++count(distinct n) ++250 ++select min(n) from t1; ++min(n) ++1 ++select max(n) from t1; ++max(n) ++250 ++"The following are SLAVE2." ++start slave; ++select count(distinct n) from t1; ++count(distinct n) ++250 ++select min(n) from t1; ++min(n) ++1 ++select max(n) from t1; ++max(n) ++250 ++show slave status; ++Slave_IO_State Master_Host Master_User Master_Port Connect_Retry Master_Log_File Read_Master_Log_Pos Relay_Log_File Relay_Log_Pos Relay_Master_Log_File Slave_IO_Running Slave_SQL_Running Replicate_Do_DB Replicate_Ignore_DB Replicate_Do_Table Replicate_Ignore_Table Replicate_Wild_Do_Table Replicate_Wild_Ignore_Table Last_Errno Last_Error Skip_Counter Exec_Master_Log_Pos Relay_Log_Space Until_Condition Until_Log_File Until_Log_Pos Master_SSL_Allowed Master_SSL_CA_File Master_SSL_CA_Path Master_SSL_Cert Master_SSL_Cipher Master_SSL_Key Seconds_Behind_Master ++Waiting for master to send event 127.0.0.1 root 9308 1 master-bin.000015 189 # # master-bin.000015 Yes Yes # 0 0 189 # None 0 No # ++drop table t1; ++drop table t1; ++"The following are SLAVE." ++show master logs; ++Log_name File_size ++master-bin.000001 4214 ++master-bin.000002 4212 ++master-bin.000003 4212 ++master-bin.000004 4212 ++master-bin.000005 4212 ++master-bin.000006 4212 ++master-bin.000007 4212 ++master-bin.000008 4212 ++master-bin.000009 4212 ++master-bin.000010 4194 ++master-bin.000011 4190 ++master-bin.000012 4190 ++master-bin.000013 4190 ++master-bin.000014 2849 ++master-bin.000015 265 ++show master status; ++File Position Binlog_Do_DB Binlog_Ignore_DB ++master-bin.000015 265 ++"The following are SLAVE2." ++show master logs; ++Log_name File_size ++master-bin.000001 4214 ++master-bin.000002 4212 ++master-bin.000003 4212 ++master-bin.000004 4212 ++master-bin.000005 4212 ++master-bin.000006 4212 ++master-bin.000007 4212 ++master-bin.000008 4212 ++master-bin.000009 4212 ++master-bin.000010 4194 ++master-bin.000011 4190 ++master-bin.000012 4190 ++master-bin.000013 4190 ++master-bin.000014 2849 ++master-bin.000015 265 ++show master status; ++File Position Binlog_Do_DB Binlog_Ignore_DB ++master-bin.000015 265 ++purge master logs to 'master-bin.000006'; ++show master logs; ++Log_name File_size ++master-bin.000006 4212 ++master-bin.000007 4212 ++master-bin.000008 4212 ++master-bin.000009 4212 ++master-bin.000010 4194 ++master-bin.000011 4190 ++master-bin.000012 4190 ++master-bin.000013 4190 ++master-bin.000014 2849 ++master-bin.000015 265 ++reset master; ++ERROR HY000: Binlog closed, cannot RESET MASTER +diff -r 66cc9e0a6768 mysql-test/t/rpl_mirror_binlog-master.opt +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/mysql-test/t/rpl_mirror_binlog-master.opt Thu Dec 04 21:46:15 2008 -0800 +@@ -0,0 +1,1 @@ ++-O max_binlog_size=4096 +diff -r 66cc9e0a6768 mysql-test/t/rpl_mirror_binlog-slave.opt +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/mysql-test/t/rpl_mirror_binlog-slave.opt Thu Dec 04 21:46:15 2008 -0800 +@@ -0,0 +1,1 @@ ++--rpl_mirror_binlog_enabled=1 --log-bin-index=replication_log +diff -r 66cc9e0a6768 mysql-test/t/rpl_mirror_binlog.1.slave-mi +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/mysql-test/t/rpl_mirror_binlog.1.slave-mi Thu Dec 04 21:46:15 2008 -0800 +@@ -0,0 +1,1 @@ ++--master-user=root --master-connect-retry=1 --master-host=127.0.0.1 --master-password="" --master-port=9308 --server-id=3 +diff -r 66cc9e0a6768 mysql-test/t/rpl_mirror_binlog.2.slave-mi +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/mysql-test/t/rpl_mirror_binlog.2.slave-mi Thu Dec 04 21:46:15 2008 -0800 +@@ -0,0 +1,1 @@ ++--master-user=root --master-connect-retry=1 --master-host=127.0.0.1 --master-password="" --master-port=9308 --server-id=4 +diff -r 66cc9e0a6768 mysql-test/t/rpl_mirror_binlog.test +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/mysql-test/t/rpl_mirror_binlog.test Thu Dec 04 21:46:15 2008 -0800 +@@ -0,0 +1,119 @@ ++-- source include/master-slave.inc ++-- source include/have_innodb.inc ++connect (slave_sec,localhost,root,,test,$SLAVE_MYPORT1,$SLAVE_MYSOCK1); ++connect (slave_ter,localhost,root,,test,$SLAVE_MYPORT2,$SLAVE_MYSOCK2); ++ ++connection master; ++--disable_warnings ++drop table if exists t1; ++--enable_warnings ++create table t1(n int) engine = InnoDB; ++ ++let $i=300; ++while ($i) ++{ ++ eval insert into t1 values ($i); ++ dec $i; ++} ++ ++save_master_pos; ++ ++connection slave; ++sync_with_master; ++ ++echo "The following are SLAVE."; ++select count(distinct n) from t1; ++select min(n) from t1; ++select max(n) from t1; ++--replace_column 8 # 9 # 18 # 23 # 33 # ++show slave status; ++show master status; ++ ++connection slave_sec; ++echo "The following are SLAVE1."; ++start slave; ++sync_with_master; ++ ++select count(distinct n) from t1; ++select min(n) from t1; ++select max(n) from t1; ++--replace_column 8 # 9 # 18 # 23 # 33 # ++show slave status; ++ ++# make the slave the new master ++connection slave; ++echo "The following are SLAVE."; ++ ++# The first 1201 error is caused by running slave. ++--error 1201 ++MAKE MASTER MASTER_LOG_FILE='master-bin', ++ MASTER_SERVER_ID=2, ++ INDEX='replication-log'; ++stop slave; ++ ++# The second 1201 error is caused by failover mode. ++--error 1201 ++MAKE MASTER MASTER_LOG_FILE='master-bin', ++ MASTER_SERVER_ID=2, ++ INDEX='replication_log'; ++ ++MAKE MASTER REVOKE SESSION WITH KILL; ++MAKE MASTER MASTER_LOG_FILE='master-bin', ++ MASTER_SERVER_ID=2, ++ INDEX='replication_log' ++ WITH BINLOG; ++ ++MAKE MASTER GRANT SESSION; ++ ++delete from t1 where n > 250; ++save_master_pos; ++ ++select count(distinct n) from t1; ++ ++connection slave_sec; ++echo "The following are SLAVE1."; ++ ++sync_with_master; ++select count(distinct n) from t1; ++select min(n) from t1; ++select max(n) from t1; ++ ++connection slave_ter; ++echo "The following are SLAVE2."; ++start slave; ++sync_with_master; ++ ++select count(distinct n) from t1; ++select min(n) from t1; ++select max(n) from t1; ++ ++--replace_column 8 # 9 # 18 # 23 # 33 # ++show slave status; ++ ++connection master; ++drop table t1; ++ ++connection slave; ++drop table t1; ++save_master_pos; ++ ++connection slave_sec; ++sync_with_master; ++ ++connection slave; ++echo "The following are SLAVE."; ++ ++show master logs; ++show master status; ++ ++ ++connection slave_ter; ++echo "The following are SLAVE2."; ++sync_with_master; ++ ++show master logs; ++show master status; ++purge master logs to 'master-bin.000006'; ++show master logs; ++--error 1186 ++reset master; +diff -r 66cc9e0a6768 patch_info/mirror_binlog.info +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/patch_info/mirror_binlog.info Thu Dec 04 21:46:15 2008 -0800 +@@ -0,0 +1,6 @@ ++File=mirror_binlog.patch ++Name=Mirroring binary logs on slave ++Version=V1 ++Author=Google ++License=GPL ++Comment=contains FastMaster promotion patch +diff -r 66cc9e0a6768 sql/Makefile.am +--- a/sql/Makefile.am Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/Makefile.am Thu Dec 04 21:46:15 2008 -0800 +@@ -68,7 +68,7 @@ + sql_array.h sql_cursor.h \ + examples/ha_example.h ha_archive.h \ + examples/ha_tina.h ha_blackhole.h \ +- ha_federated.h ++ ha_federated.h repl_mule.h + mysqld_SOURCES = sql_lex.cc sql_handler.cc \ + item.cc item_sum.cc item_buff.cc item_func.cc \ + item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \ +@@ -105,7 +105,7 @@ + sp_cache.cc parse_file.cc sql_trigger.cc \ + examples/ha_example.cc ha_archive.cc \ + examples/ha_tina.cc ha_blackhole.cc \ +- ha_federated.cc ++ ha_federated.cc repl_mule.cc + + gen_lex_hash_SOURCES = gen_lex_hash.cc + gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) +diff -r 66cc9e0a6768 sql/Makefile.in +--- a/sql/Makefile.in Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/Makefile.in Thu Dec 04 21:46:15 2008 -0800 +@@ -152,7 +152,7 @@ + sp_rcontext.$(OBJEXT) sp.$(OBJEXT) sp_cache.$(OBJEXT) \ + parse_file.$(OBJEXT) sql_trigger.$(OBJEXT) \ + ha_example.$(OBJEXT) ha_archive.$(OBJEXT) ha_tina.$(OBJEXT) \ +- ha_blackhole.$(OBJEXT) ha_federated.$(OBJEXT) ++ ha_blackhole.$(OBJEXT) ha_federated.$(OBJEXT) repl_mule.$(OBJEXT) + mysqld_OBJECTS = $(am_mysqld_OBJECTS) + mysqld_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_2) \ +@@ -516,7 +516,7 @@ + sql_array.h sql_cursor.h \ + examples/ha_example.h ha_archive.h \ + examples/ha_tina.h ha_blackhole.h \ +- ha_federated.h ++ ha_federated.h repl_mule.h + + mysqld_SOURCES = sql_lex.cc sql_handler.cc \ + item.cc item_sum.cc item_buff.cc item_func.cc \ +@@ -554,7 +554,7 @@ + sp_cache.cc parse_file.cc sql_trigger.cc \ + examples/ha_example.cc ha_archive.cc \ + examples/ha_tina.cc ha_blackhole.cc \ +- ha_federated.cc ++ ha_federated.cc repl_mule.cc + + gen_lex_hash_SOURCES = gen_lex_hash.cc + gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) +@@ -748,6 +748,7 @@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/protocol.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/records.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/repl_failsafe.Po@am__quote@ ++@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/repl_mule.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/set_var.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slave.Po@am__quote@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sp.Po@am__quote@ +diff -r 66cc9e0a6768 sql/lex.h +--- a/sql/lex.h Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/lex.h Thu Dec 04 21:46:15 2008 -0800 +@@ -292,6 +292,7 @@ + { "LONGTEXT", SYM(LONGTEXT)}, + { "LOOP", SYM(LOOP_SYM)}, + { "LOW_PRIORITY", SYM(LOW_PRIORITY)}, ++ { "MAKE", SYM(MAKE_SYM)}, + { "MASTER", SYM(MASTER_SYM)}, + { "MASTER_CONNECT_RETRY", SYM(MASTER_CONNECT_RETRY_SYM)}, + { "MASTER_HOST", SYM(MASTER_HOST_SYM)}, +diff -r 66cc9e0a6768 sql/log.cc +--- a/sql/log.cc Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/log.cc Thu Dec 04 21:46:15 2008 -0800 +@@ -79,7 +79,9 @@ + + bool binlog_init() + { +- return !opt_bin_log; ++ if (!opt_bin_log) ++ binlog_hton.prepare = NULL; ++ return 0; /* return !opt_bin_log; */ + } + + static int binlog_close_connection(THD *thd) +@@ -406,6 +408,7 @@ + :bytes_written(0), last_time(0), query_start(0), name(0), + prepared_xids(0), log_type(LOG_CLOSED), file_id(1), open_count(1), + write_error(FALSE), inited(FALSE), need_start_event(TRUE), ++ mule_binlog_(0), + description_event_for_exec(0), description_event_for_queue(0) + { + /* +@@ -506,7 +509,10 @@ + const char *log_name) + { + File index_file_nr= -1; +- DBUG_ASSERT(!my_b_inited(&index_file)); ++ ++ /* If the index is already opened, do not open it again. */ ++ if (my_b_inited(&index_file)) ++ return FALSE; + + /* + First open of this class instance +@@ -750,7 +756,7 @@ + if (file >= 0) + my_close(file,MYF(0)); + end_io_cache(&log_file); +- end_io_cache(&index_file); ++ close_index_file(); + safeFree(name); + log_type= LOG_CLOSED; + DBUG_RETURN(1); +@@ -768,7 +774,10 @@ + int MYSQL_LOG::raw_get_current_log(LOG_INFO* linfo) + { + strmake(linfo->log_file_name, log_file_name, sizeof(linfo->log_file_name)-1); +- linfo->pos = my_b_tell(&log_file); ++ if (!mule_binlog_) ++ linfo->pos = my_b_tell(&log_file); ++ else ++ linfo->pos = my_b_filelength(&log_file); + return 0; + } + +@@ -935,6 +944,11 @@ + if (need_lock) + pthread_mutex_lock(&LOCK_index); + safe_mutex_assert_owner(&LOCK_index); ++ ++ if (open_index_file(index_file_name, NULL) != 0) { ++ error = -1; ++ goto err; ++ } + + /* As the file is flushed, we can't get an error here */ + (void) reinit_io_cache(&index_file, READ_CACHE, linfo->index_file_offset, 0, +@@ -1446,18 +1460,19 @@ + SYNOPSIS + new_file() + need_lock Set to 1 if caller has not locked LOCK_log ++ logfile_name the specified log filename. + + NOTE + The new file name is stored last in the index file + */ + +-void MYSQL_LOG::new_file(bool need_lock) ++void MYSQL_LOG::new_file(bool need_lock, const char* log_filename) + { + char new_name[FN_REFLEN], *new_name_ptr, *old_name; + enum_log_type save_log_type; + + DBUG_ENTER("MYSQL_LOG::new_file"); +- if (!is_open()) ++ if (!is_log_open()) + { + DBUG_PRINT("info",("log is closed")); + DBUG_VOID_RETURN; +@@ -1496,7 +1511,9 @@ + We have to do this here and not in open as we want to store the + new file name in the current binary log file. + */ +- if (generate_new_name(new_name, name)) ++ if (log_filename) { ++ fn_format(new_name,log_filename,mysql_data_home,"",4); ++ } else if (generate_new_name(new_name, name)) + goto end; + new_name_ptr=new_name; + +@@ -1571,7 +1588,7 @@ + bytes_written+= ev->data_written; + DBUG_PRINT("info",("max_size: %lu",max_size)); + if ((uint) my_b_append_tell(&log_file) > max_size) +- new_file(0); ++ new_file(0); + + err: + pthread_mutex_unlock(&LOCK_log); +@@ -1600,8 +1617,14 @@ + bytes_written += len; + } while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint))); + DBUG_PRINT("info",("max_size: %lu",max_size)); +- if ((uint) my_b_append_tell(&log_file) > max_size) +- new_file(0); ++ ++ /* If max_size is BINLOG_NOSWITCH_SIZE, binlog would not switch because ++ * of file size limit. ++ */ ++ if (max_size != BINLOG_NOSWITCH_SIZE && ++ (uint) my_b_append_tell(&log_file) > max_size) { ++ new_file(0); ++ } + + err: + if (!error) +@@ -2492,6 +2515,17 @@ + DBUG_VOID_RETURN; + } + ++int MYSQL_LOG::flush_log_file() { ++ return flush_io_cache(&log_file); ++} ++ ++int MYSQL_LOG::close_index_file() { ++ if (my_b_inited(&index_file)) { ++ end_io_cache(&index_file); ++ my_close(index_file.file, MYF(0)); ++ } ++ return 0; ++} + + /* + Check if a string is a valid number +diff -r 66cc9e0a6768 sql/log_event.h +--- a/sql/log_event.h Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/log_event.h Thu Dec 04 21:46:15 2008 -0800 +@@ -94,6 +94,14 @@ + #define LINE_TERM_EMPTY 0x4 + #define LINE_START_EMPTY 0x8 + #define ESCAPED_EMPTY 0x10 ++ ++/* This server-id value is used to indicate a special master-info event ++ * in relay-log. ++ * We will enforce in database that replication can not set this value ++ * as the server-id. ++ */ ++#define MASTER_INFO_SERVER_ID 0xffffffff ++ + + /***************************************************************************** + +diff -r 66cc9e0a6768 sql/mysql_priv.h +--- a/sql/mysql_priv.h Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/mysql_priv.h Thu Dec 04 21:46:15 2008 -0800 +@@ -462,6 +462,7 @@ + /* BINLOG_DUMP options */ + + #define BINLOG_DUMP_NON_BLOCK 1 ++#define BINLOG_MIRROR_CLIENT 0x0004 + + /* sql_show.cc:show_log_files() */ + #define SHOW_LOG_STATUS_FREE "FREE" +@@ -1374,6 +1375,7 @@ + extern const char **errmesg; /* Error messages */ + extern const char *myisam_recover_options_str; + extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond; ++extern char *opt_binlog_index_name; + extern const char * const triggers_file_ext; + extern const char * const trigname_file_ext; + extern Eq_creator eq_creator; +@@ -1875,6 +1877,10 @@ + extern "C" void unireg_abort(int exit_code); + void kill_delayed_threads(void); + bool check_stack_overrun(THD *thd, long margin, char *dummy); ++extern my_bool rpl_mirror_binlog_enabled; ++extern ulong sync_mirror_binlog_period; ++extern my_bool rpl_mirror_binlog_no_replicate; ++extern ulong rpl_mirror_binlog_clients, rpl_mirror_binlog_status; + #else + #define unireg_abort(exit_code) DBUG_RETURN(exit_code) + inline void kill_delayed_threads(void) {} +diff -r 66cc9e0a6768 sql/mysqld.cc +--- a/sql/mysqld.cc Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/mysqld.cc Thu Dec 04 21:46:15 2008 -0800 +@@ -555,6 +555,7 @@ + pthread_mutex_t LOCK_global_user_client_stats; + pthread_mutex_t LOCK_global_table_stats; + pthread_mutex_t LOCK_global_index_stats; ++pthread_mutex_t LOCK_failover_master; + /* + The below lock protects access to two global server variables: + max_prepared_stmt_count and prepared_stmt_count. These variables +@@ -584,13 +585,15 @@ + char *master_ssl_key, *master_ssl_cert; + char *master_ssl_ca, *master_ssl_capath, *master_ssl_cipher; + ++char *opt_binlog_index_name; ++ + /* Static variables */ + + static bool kill_in_progress, segfaulted; + static my_bool opt_do_pstack, opt_bootstrap, opt_myisam_log; + static int cleanup_done; + static ulong opt_specialflag, opt_myisam_block_size; +-static char *opt_logname, *opt_update_logname, *opt_binlog_index_name; ++static char *opt_logname, *opt_update_logname; + static char *opt_tc_heuristic_recover; + static char *mysql_home_ptr, *pidfile_name_ptr; + static char **defaults_argv; +@@ -598,6 +601,32 @@ + + static my_socket unix_sock,ip_sock; + struct rand_struct sql_rand; // used by sql_class.cc:THD::THD() ++ ++/* When set, we are inside a failover slave and deny all non-super access */ ++bool failover_deny_access= 0; ++ ++/* When set, binlog will be mirrored on the replica. */ ++my_bool rpl_mirror_binlog_enabled; ++ ++/* Sync the mirrored binlog to disk after every #th event. */ ++ulong sync_mirror_binlog_period; ++ ++/* The fixed size for replication event buffer. Replication event can exceed ++ * the size. ++ */ ++//ulong rpl_event_buffer_size; ++ ++/* This is a mirror binlog status variable on the primary to indicate how many ++ * mirror binlog servers are connecting. ++ */ ++ulong rpl_mirror_binlog_clients = 0; ++ ++/* This indicates whether mirror binlog is working on a replica database. It ++ * requires: ++ * . rpl_mirror_binlog_enabled = 1 ++ * . the slave I/O thread is running and mirror binlog is also dumped ++ */ ++ulong rpl_mirror_binlog_status = 0; + + /* OS specific variables */ + +@@ -1315,6 +1344,7 @@ + (void) pthread_cond_destroy(&COND_flush_thread_cache); + (void) pthread_cond_destroy(&COND_manager); + (void) pthread_mutex_destroy(&LOCK_stats); ++ (void) pthread_mutex_destroy(&LOCK_failover_master); + (void) pthread_mutex_destroy(&LOCK_global_user_client_stats); + (void) pthread_mutex_destroy(&LOCK_global_table_stats); + (void) pthread_mutex_destroy(&LOCK_global_index_stats); +@@ -3164,6 +3194,7 @@ + (void) pthread_cond_init(&COND_rpl_status, NULL); + #endif + (void) pthread_mutex_init(&LOCK_stats, MY_MUTEX_INIT_FAST); ++ (void) pthread_mutex_init(&LOCK_failover_master, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_global_user_client_stats, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_global_table_stats, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_global_index_stats, MY_MUTEX_INIT_FAST); +@@ -3398,39 +3429,8 @@ + + if (opt_bin_log) + { +- char buf[FN_REFLEN]; +- const char *ln; +- ln= mysql_bin_log.generate_name(opt_bin_logname, "-bin", 1, buf); +- if (!opt_bin_logname && !opt_binlog_index_name) +- { +- /* +- User didn't give us info to name the binlog index file. +- Picking `hostname`-bin.index like did in 4.x, causes replication to +- fail if the hostname is changed later. So, we would like to instead +- require a name. But as we don't want to break many existing setups, we +- only give warning, not error. +- */ +- sql_print_warning("No argument was provided to --log-bin, and " +- "--log-bin-index was not used; so replication " +- "may break when this MySQL server acts as a " +- "master and has his hostname changed!! Please " +- "use '--log-bin=%s' to avoid this problem.", ln); +- } +- if (ln == buf) +- { +- my_free(opt_bin_logname, MYF(MY_ALLOW_ZERO_PTR)); +- opt_bin_logname=my_strdup(buf, MYF(0)); +- } +- if (mysql_bin_log.open_index_file(opt_binlog_index_name, ln)) +- { +- unireg_abort(1); +- } +- +- /* +- Used to specify which type of lock we need to use for queries of type +- INSERT ... SELECT. This will change when we have row level logging. +- */ +- using_update_log=1; ++ if (make_master_open_index(&opt_bin_logname, opt_binlog_index_name) != 0) ++ unireg_abort(1); + } + + if (xid_cache_init()) +@@ -3480,9 +3480,10 @@ + unireg_abort(1); + } + +- if (opt_bin_log && mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0, +- WRITE_CACHE, 0, max_binlog_size, 0)) +- unireg_abort(1); ++ if (opt_bin_log && ++ make_master(NULL, opt_bin_logname, opt_binlog_index_name, NULL) != 0) { ++ unireg_abort(1); ++ } + + #ifdef HAVE_REPLICATION + if (opt_bin_log && expire_logs_days) +@@ -5098,6 +5098,8 @@ + OPT_INNODB_READ_IO_THREADS, + OPT_INNODB_WRITE_IO_THREADS, + OPT_INNODB_ADAPTIVE_HASH_INDEX, ++ OPT_RPL_MIRROR_BINLOG, ++ OPT_SYNC_MIRROR_BINLOG, + OPT_FEDERATED, + OPT_INNODB_USE_LEGACY_CARDINALITY_ALGORITHM + }; +@@ -5725,6 +5728,11 @@ + {"rpl-recovery-rank", OPT_RPL_RECOVERY_RANK, "Undocumented.", + (gptr*) &rpl_recovery_rank, (gptr*) &rpl_recovery_rank, 0, GET_ULONG, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, ++ {"rpl_mirror_binlog_enabled", OPT_RPL_MIRROR_BINLOG, ++ "1 = support mirroring binlogs. 0 = disable mirroring binlogs", ++ (gptr*) &rpl_mirror_binlog_enabled, ++ (gptr*) &rpl_mirror_binlog_enabled, 0, GET_BOOL, NO_ARG, ++ 0, 0, 1, 0, 1, 0}, + {"safe-mode", OPT_SAFE, "Skip some optimize stages (for testing).", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + #ifndef TO_BE_DELETED +@@ -5849,6 +5857,11 @@ + {"symbolic-links", 's', "Enable symbolic link support.", + (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG, + IF_PURIFY(0,1), 0, 0, 0, 0, 0}, ++ {"sync-mirror-binlog", OPT_SYNC_MIRROR_BINLOG, ++ "Sync the mirrored binlog to disk after every #th event. " ++ "#=0 (the default) does no sync. Syncing slows MySQL down", ++ (gptr*) &sync_mirror_binlog_period, ++ (gptr*) &sync_mirror_binlog_period, 0, GET_ULONG, REQUIRED_ARG, 0, 0, ~0L, 0, 1, 0}, + {"sysdate-is-now", OPT_SYSDATE_IS_NOW, + "Non-default option to alias SYSDATE() to NOW() to make it safe-replicable. Since 5.0, SYSDATE() returns a `dynamic' value different for different invocations, even within the same statement.", + (gptr*) &global_system_variables.sysdate_is_now, +@@ -6625,6 +6638,7 @@ + {"Delayed_errors", (char*) &delayed_insert_errors, SHOW_LONG}, + {"Delayed_insert_threads", (char*) &delayed_insert_threads, SHOW_LONG_CONST}, + {"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG}, ++ {"Failover_deny_access", (char*) &failover_deny_access, SHOW_LONG}, + {"Flush_commands", (char*) &refresh_version, SHOW_LONG_CONST}, + {"Handler_commit", (char*) offsetof(STATUS_VAR, ha_commit_count), SHOW_LONG_STATUS}, + {"Handler_delete", (char*) offsetof(STATUS_VAR, ha_delete_count), SHOW_LONG_STATUS}, +diff -r 66cc9e0a6768 sql/repl_mule.cc +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/sql/repl_mule.cc Thu Dec 04 21:46:15 2008 -0800 +@@ -0,0 +1,466 @@ ++/* ++ Copyright (C) 2007 Google Inc. ++ ++This program is free software; you can redistribute it and/or ++modify it under the terms of the GNU General Public License ++as published by the Free Software Foundation; either version 2 ++of the License, or (at your option) any later version. ++ ++This program is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with this program; if not, write to the Free Software ++Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++*/ ++ ++#include "mysql_priv.h" ++#include <my_dir.h> ++#include "slave.h" ++#include "repl_mule.h" ++ ++/* max log size: 2GB */ ++#define MAX_LOG_SIZE BINLOG_NOSWITCH_SIZE ++ ++ReplMule::ReplMule(THD* thd, MASTER_INFO *mi, RelayStatus status, ++ my_off_t file_size, const char *binlog_indexname, ++ MYSQL_LOG *binlog, ulong sync_period) ++ : desc_event_(new Format_description_log_event(BINLOG_VERSION)), ++ io_thd_(thd), mi_(mi), status_(status), dump_position_(0L), ++ file_size_(file_size), mule_log_(binlog), ++ mule_log_sync_period_(sync_period), mule_log_event_counter_(0) { ++ char llbuf1[22], llbuf2[22]; ++ ++ DBUG_ENTER("ReplMule::ReplMule"); ++ ++ /* Indicate that we are in replication mule mode. */ ++ mule_log_->set_mule_mode(); ++ ++ strmake(curr_log_filename_, mi->master_log_name, ++ sizeof(curr_log_filename_)-1); ++ strmake(mule_indexname_, binlog_indexname, sizeof(mule_indexname_)-1); ++ ++ /* Open the mule log file */ ++ if (!mule_log_->is_log_open()) { ++ /* Do not open binlog file when master_log_name is not specified. We ++ * are at the I/O thread initialization time and we do not know what ++ * filename we are going to dump. ++ * We wait for the next rotation event to indicate the filename. ++ */ ++ if (strlen(curr_log_filename_) > 0 && ++ mule_log_->open(curr_log_filename_, LOG_BIN, NULL, ++ SEQ_READ_APPEND, true, MAX_LOG_SIZE, 0) != 0) { ++ sql_print_error("ReplMule: open binlog failed: %s", ++ curr_log_filename_); ++ status_ = MULE_ERROR; ++ DBUG_VOID_RETURN; ++ } ++ } ++ ++ switch (status_) { ++ case MULE_BEHIND: ++ dump_position_ = mi->master_log_pos; ++ mi->master_log_pos = file_size_; ++ sql_print_information("ReplicationMule: MULE_BEHIND - new(%s), old(%s)", ++ llstr(mi->master_log_pos, llbuf1), ++ llstr(dump_position_, llbuf2)); ++ break; ++ case RELAY_MATCH_MULE: ++ case RELAY_MATCH_MULE_RUN: ++ dump_position_ = mi->master_log_pos; ++ sql_print_information("ReplicationMule: RELAY_MATCH_MULE."); ++ break; ++ case MULE_VERIFY: ++ case MULE_VERIFY_RELAY_BEHIND: ++ dump_position_ = mi->master_log_pos; ++ mi->master_log_pos = BIN_LOG_HEADER_SIZE; ++ sql_print_information( ++ "ReplicationMule: MULE_VERIFY - old(%s), file_size(%s)", ++ llstr(dump_position_, llbuf1), llstr(file_size_, llbuf2)); ++ ++ /* seek to the beginning of the file for verification */ ++ seekToPosition(BIN_LOG_HEADER_SIZE); ++ break; ++ } ++ ++ DBUG_VOID_RETURN; ++} ++ ++ReplMule::~ReplMule() { ++ DBUG_ENTER("ReplMule::~ReplMule"); ++ ++ if (mule_log_->is_log_open()) ++ mule_log_->close(LOG_CLOSE_INDEX); ++ mule_log_->clear_mule_mode(); ++ ++ /* If we are still in MULE_BEHIND or MULE_VERIFY state and we exit from ++ * I/O thread, it means we encountered some errors. ++ * mi->master_log_pos might be used by later slave start. It is being ++ * changed here to do event dumping or event verification. So, we should ++ * restore it to its original value. ++ */ ++ switch (status_) { ++ case MULE_BEHIND: ++ case MULE_VERIFY: ++ if (mi_->master_log_pos < dump_position_) ++ mi_->master_log_pos = dump_position_; ++ break; ++ } ++ ++ delete desc_event_; ++ ++ DBUG_VOID_RETURN; ++} ++ ++ReplMule::WriteStatus ReplMule::writeEvent(const char* buf, ulong event_len) { ++ WriteStatus dump_status = WRITE_RELAY; ++ char llbuf1[22], llbuf2[22], llbuf3[22]; ++ char *verify_event; ++ bool verified = false; ++ bool skip_event = false; ++ ++ DBUG_ENTER("ReplMule::dumpEvent"); ++ switch (status_) { ++ case MULE_VERIFY: ++ case MULE_VERIFY_RELAY_BEHIND: ++ if (buf[EVENT_TYPE_OFFSET] == ROTATE_EVENT && ++ IsFakeRotation(buf, event_len)) { ++ /* Do not verify the faked rotate event */ ++ if (status_ == MULE_VERIFY) ++ dump_status = SKIP_RELAY; ++ break; ++ } ++ verify_event = new char[event_len]; ++ if (verify_event == NULL) { ++ sql_print_error( ++ "ReplMule::dumpEvent - insufficient memory in verification, " ++ "position(%s), event_len(%d).", ++ llstr(mi_->master_log_pos, llbuf1), event_len); ++ dump_status = WRITE_ERROR; ++ break; ++ } ++ if (my_b_read(mule_log_->get_log_file(), (byte*) verify_event, ++ event_len) != 0) { ++ sql_print_error( ++ "ReplMule::dumpEvent - read log error in verification, " ++ "position(%s), event_len(%d).", ++ llstr(mi_->master_log_pos, llbuf1), event_len); ++ dump_status = WRITE_ERROR; ++ delete verify_event; ++ break; ++ } ++ verified = (memcmp(buf, verify_event, event_len) == 0); ++ delete verify_event; ++ if (!verified) { ++ sql_print_error( ++ "ReplMule::dumpEvent - event does not match at position(%s)", ++ llstr(mi_->master_log_pos, llbuf1)); ++ dump_status = WRITE_ERROR; ++ break; ++ } ++ /* fall through */ ++ case MULE_BEHIND: ++ dump_status = SKIP_RELAY; ++ if (status_ == MULE_BEHIND && ++ queueEvent(buf, event_len, &skip_event) != 0) { ++ dump_status = WRITE_ERROR; ++ break; ++ } ++ ++ /* Skip faked rotation event */ ++ if (!skip_event) ++ mi_->master_log_pos += event_len; ++ ++ if (mi_->master_log_pos == dump_position_) { ++ if (dump_position_ < file_size_) { ++ status_ = MULE_VERIFY_RELAY_BEHIND; ++ } else { ++ status_ = RELAY_MATCH_MULE; ++ } ++ sql_print_information( ++ "ReplMule::dumpEvent - new status(%d) " ++ "master_log_pos(%s), dump_pos(%s), file_size(%s)", status_, ++ llstr(mi_->master_log_pos, llbuf1), llstr(dump_position_, llbuf2), ++ llstr(file_size_, llbuf3)); ++ } else if (mi_->master_log_pos == file_size_) { ++ if (dump_position_ > file_size_) { ++ status_ = MULE_BEHIND; ++ } else { ++ status_ = RELAY_MATCH_MULE; ++ } ++ sql_print_information( ++ "ReplMule::dumpEvent - new status(%d) " ++ "master_log_pos(%s), dump_pos(%s), file_size(%s)", status_, ++ llstr(mi_->master_log_pos, llbuf1), llstr(dump_position_, llbuf2), ++ llstr(file_size_, llbuf3)); ++ } else if (status_ != MULE_VERIFY_RELAY_BEHIND && ++ mi_->master_log_pos > dump_position_) { ++ sql_print_error( ++ "ReplMule::dumpEvent - mule position(%s) does not match " ++ "relay-log position(%s).", ++ llstr(mi_->master_log_pos, llbuf1), llstr(dump_position_, llbuf2)); ++ dump_status = WRITE_ERROR; ++ } ++ break; ++ case RELAY_MATCH_MULE_RUN: ++ if (buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT) { ++ sql_print_information(" RELAY_MATCH_MULE event %d", buf[EVENT_TYPE_OFFSET] ); ++ /* Do not write format description record if size is the same */ ++ break; ++ } ++ case RELAY_MATCH_MULE: ++ if (queueEvent(buf, event_len, &skip_event) != 0) ++ dump_status = WRITE_ERROR; ++ break; ++ } ++ ++ DBUG_RETURN(dump_status); ++} ++ ++int ReplMule::appendEvent(const char* buf, ulong event_len) { ++ char llbuf1[22]; ++ int error; ++ ++ DBUG_ENTER("ReplMule::appendEvent"); ++ ++ error = mule_log_->appendv(buf,event_len,0); ++ if (error != 0) { ++ sql_print_error("ReplMule::appendEvent - append error at %s(%s)", ++ mi_->master_log_name, ++ llstr(mi_->master_log_pos, llbuf1)); ++ } else if (mule_log_->flush_log_file() != 0) { ++ sql_print_error("ReplMule::appendEvent - flush error at %s(%s)", ++ mi_->master_log_name, ++ llstr(mi_->master_log_pos, llbuf1)); ++ error = -1; ++ } else if (mule_log_sync_period_ > 0) { ++ mule_log_event_counter_++; ++ if (mule_log_event_counter_ >= mule_log_sync_period_) { ++ mule_log_event_counter_ = 0; ++ error = my_sync(mule_log_->get_log_file()->file, MYF(MY_WME)); ++ if (error != 0) ++ sql_print_error("ReplMule::appendEvent - sync error at %s(%s)", ++ mi_->master_log_name, ++ llstr(mi_->master_log_pos, llbuf1)); ++ } ++ } ++ ++ DBUG_RETURN(error); ++} ++ ++int ReplMule::queueEvent(const char* buf, ulong event_len, bool *skip_event) { ++ int error = 0; ++ ++ DBUG_ENTER("ReplMule::queueEvent"); ++ ++ *skip_event = false; ++ ++ mule_log_->lock_log(); ++ if (buf[EVENT_TYPE_OFFSET] == ROTATE_EVENT) { ++ Rotate_log_event rev(buf, event_len, desc_event_); ++ ++ /* If this is a faked rotate event and the specified filename is ++ * the same as the current binlog filename, ignore the event. ++ */ ++ if (IsFakeRotation(rev)) { ++ *skip_event = true; ++ DBUG_PRINT("info",("skipped faked rotation event")); ++ } else { ++ /* Only append real events. */ ++ if (rev.when != 0) ++ error = appendEvent(buf, event_len); ++ ++ /* Only rotate file when append succeeds. */ ++ if (error == 0) { ++ /* Create a new file: lock both index and log. */ ++ if (strlen(curr_log_filename_) == 0) { ++ /* If curr_log_filename_ is not specified, then this is the first ++ * valid rotation event to indicate the filename. ++ */ ++ error = mule_log_->open(rev.new_log_ident, LOG_BIN, NULL, ++ SEQ_READ_APPEND, true, MAX_LOG_SIZE, 0); ++ } else { ++ mule_log_->new_file(0, rev.new_log_ident); ++ } ++ ++ strmake(curr_log_filename_, rev.new_log_ident, ++ strlen(rev.new_log_ident)); ++ ++ DBUG_PRINT("info",("rotate file: %s", rev.new_log_ident)); ++ } ++ } ++ } else { ++ error = appendEvent(buf, event_len); ++ } ++ mule_log_->unlock_log(); ++ ++ DBUG_RETURN(error); ++} ++ ++void ReplMule::seekToPosition(my_off_t pos) { ++ DBUG_ENTER("ReplMule::seekToPosition"); ++ DBUG_PRINT("enter",("seek_pos: %ld", (ulong) pos)); ++ ++ my_b_seek(mule_log_->get_log_file(), pos); ++ DBUG_VOID_RETURN; ++} ++ ++bool ReplMule::IsFakeRotation(const char* buf, ulong event_len) { ++ DBUG_ENTER("ReplMule::IsFakeRotation"); ++ ++ Rotate_log_event rev(buf, event_len, desc_event_); ++ DBUG_RETURN(IsFakeRotation(rev)); ++} ++ ++bool ReplMule::IsFakeRotation(const Rotate_log_event& rev) { ++ DBUG_ENTER("ReplMule::IsFakeRotation"); ++ DBUG_RETURN(rev.when == 0 && ++ rev.ident_len == strlen(curr_log_filename_) && ++ strcmp(rev.new_log_ident, curr_log_filename_) == 0); ++} ++ ++/* createReplicationMule: ++ * Create a mule that relays master's replication binlog and ++ * generate an exact same copy on the local filesystem. ++ * ++ * Code flow: ++ * last_mulelog = scan the existing mule log index to find it ++ * if (mulelog index is not created or there is no mule log inside it) ++ * old_mule_log <- requested dumping position ++ * requested dumping position <- 0 in the file ++ * else ++ * check whether the mule log matches the requested dump ++ * (whether the last mule log name/size matches) ++ * if the mule log name does not match ++ * exit with an error ++ * if (the mule log size does not match the requested dump position) ++ * request the dump from position 0 and read all events ++ * verify all events with the corresponding events in mule log ++ * if (the verification succeeds) ++ * continue the dump ++ * else ++ * exit with an error ++ */ ++ReplMule* ReplMule::createReplicationMule( ++ THD* thd, MASTER_INFO *mi, const char *binlog_indexname, ++ MYSQL_LOG *binlog) { ++ ReplMule *mule = NULL; ++ LOG_INFO linfo; ++ bool index_opened = false; ++ ++ DBUG_ENTER("ReplMule::createReplicationMule"); ++ ++ /* binlog_indexname must be set to some real value. */ ++ DBUG_ASSERT(binlog_indexname); ++ ++ /* Lock binlog index for all binlog operations */ ++ binlog->lock_index(); ++ index_opened = binlog->open_index_file(binlog_indexname, NULL); ++ DBUG_PRINT("info",("open index file succeed: %d", index_opened)); ++ sql_print_information("createReplicationMule"); ++ ++ /* Scan the existing binlog index to find the last relayed binlog */ ++ if (index_opened || ++ binlog->find_log_pos(&linfo, NullS, false) != 0) { ++ /* binlog index is not created or has no log file inside: ++ * . old_relay_binlog <- requested dumping position ++ * . requested dumping position <- 0 in the file ++ */ ++ if (mi->master_log_pos == BIN_LOG_HEADER_SIZE) { ++ mule = new ReplMule(thd, mi, RELAY_MATCH_MULE, BIN_LOG_HEADER_SIZE, ++ binlog_indexname, binlog, sync_mirror_binlog_period); ++ } else { ++ mule = new ReplMule(thd, mi, MULE_BEHIND, BIN_LOG_HEADER_SIZE, ++ binlog_indexname, binlog, sync_mirror_binlog_period); ++ } ++ ++ if (mule == NULL) { ++ sql_print_error("Mule malloc operation failed."); ++ } ++ } else { ++ IO_CACHE* log_file; ++ MY_STAT stat; ++ char last_binlog_name[FN_REFLEN]; ++ ++ /* Find the last log file from the binlog index. ++ * Check whether the last binlog matches the requested dump for both ++ * binlog name and binlog size. ++ */ ++ for (;;) { ++ strmake(last_binlog_name, linfo.log_file_name, FN_REFLEN); ++ last_binlog_name[FN_REFLEN - 1] = '\0'; ++ if (binlog->find_next_log(&linfo, false)) ++ break; ++ } ++ DBUG_PRINT("info",("the last binlog: %s", last_binlog_name)); ++ ++ /* if the binlog name does not match, exit with an error. */ ++ if (strcmp(last_binlog_name+dirname_length(last_binlog_name), ++ mi->master_log_name) != 0) { ++ sql_print_error("Mule binlog(%s) does not match new relay-binlog(%s)", ++ last_binlog_name, mi->master_log_name); ++ } /* Open the last binlog. */ ++ else if (binlog->open(last_binlog_name, LOG_BIN, NULL, ++ SEQ_READ_APPEND, true, MAX_LOG_SIZE, 0) != 0) { ++ sql_print_error("Mule open last binlog failed: %s", last_binlog_name); ++ } else { ++ bool valid_file_size = true; ++ ++ /* Get the binlog size. */ ++ log_file = binlog->get_log_file(); ++ if (my_fstat(log_file->file, &stat, MYF(0)) == 0) { ++ /* If the binlog size does not match the requested dump position, then ++ * request the dump from position 0 and verify all events, we need to ++ * verify events because the mule log might be used for serving during ++ * anytime. We must be sure that they are correct. ++ */ ++ sql_print_information("Binglog size %d", stat.st_size); ++ if (stat.st_size == mi->master_log_pos) { ++ mule = new ReplMule(thd, mi, RELAY_MATCH_MULE_RUN, stat.st_size, ++ binlog_indexname, binlog, ++ sync_mirror_binlog_period); ++ } else if (stat.st_size > BIN_LOG_HEADER_SIZE) { ++ mule = new ReplMule(thd, mi, MULE_VERIFY, stat.st_size, ++ binlog_indexname, binlog, ++ sync_mirror_binlog_period); ++ } else if (stat.st_size == BIN_LOG_HEADER_SIZE) { ++ mule = new ReplMule(thd, mi, MULE_BEHIND, BIN_LOG_HEADER_SIZE, ++ binlog_indexname, binlog, ++ sync_mirror_binlog_period); ++ } else { ++ char llbuf[22]; ++ valid_file_size = false; ++ sql_print_error("Mule binlog file(%s) invalid size: %s", ++ last_binlog_name, llstr(stat.st_size, llbuf)); ++ } ++ } else { ++ valid_file_size = false; ++ sql_print_error("Mule binlog file(%s): fstat failed.", ++ last_binlog_name); ++ } ++ ++ if (valid_file_size) { ++ if (mule == NULL) { ++ sql_print_error("Mule malloc operation failed."); ++ } else if (mule->status_ == MULE_ERROR) { ++ /* If mule creation fails, indicate the error. */ ++ delete mule; ++ mule = NULL; ++ } ++ } ++ } ++ } ++ ++ /* Clear the mule binlog mode if there are errors. */ ++ if (mule == NULL) { ++ binlog->clear_mule_mode(); ++ binlog->close_index_file(); ++ } ++ ++ /* Unlock binlog index */ ++ binlog->unlock_index(); ++ ++ DBUG_RETURN(mule); ++} +diff -r 66cc9e0a6768 sql/repl_mule.h +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/sql/repl_mule.h Thu Dec 04 21:46:15 2008 -0800 +@@ -0,0 +1,166 @@ ++/* ++ Copyright (C) 2007 Google Inc. ++ ++This program is free software; you can redistribute it and/or ++modify it under the terms of the GNU General Public License ++as published by the Free Software Foundation; either version 2 ++of the License, or (at your option) any later version. ++ ++This program is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with this program; if not, write to the Free Software ++Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ++*/ ++ ++#ifndef SQL_REPL_MULE_H__ ++#define SQL_REPL_MULE_H__ ++ ++/* Replication Mule is the class that is responsible for generating ++ * an exact copy of the binlog from a master database. We call this feature ++ * mirror binlog and it can be enabled by setting rpl_mirror_binlog. We ++ * need to keep the same copy for the following purposes: ++ * . The replica can serve the binlog transparently as if they are the ++ * master database. This can relieve master connection overhead. ++ * . During failover, the replica can become the new master and serve ++ * old binlogs transparently. ++ * (The Mule name comes from the popular P2P software eMule.) ++ * ++ * Internally, we call the mirrored binlog mule log. ++ */ ++ ++class THD; ++class Rotate_log_event; ++class Format_description_log_event; ++typedef struct st_master_info MASTER_INFO; ++ ++class ReplMule { ++ public: ++ /* Because I/O thread also creates relay-binlog, instead of an exact ++ * copy of the original master's binlog, we have two resources that ++ * might get out of sync. ++ * This enum indicates the status: ++ * MULE_BEHIND - the mule's header is behind: ++ * (mule is activated for the first time) ++ * RELAY_MATCH_MULE - mule matches relay-log ++ * RELAY_MATCH_MULE_RUN - mule matches relay-log and it was not empty binlog ++ * MULE_VERIFY - mule has more events than the relay-log and needs ++ * verification; we can not verify based on relay-log ++ * events because events might get changed a little; ++ * verification starts with downloading all events in ++ * the last binlog from the master and compare with ++ * all events in the mule log; ++ * MULE_VERIFY_RELAY_BEHIND - mule has more events than the relay-log ++ * and relay-log needs to write events ++ * MULE_ERROR - mule detects errors in event duplicate ++ * ++ * When the mule mirrors binlogs, it writes an event into the mule log ++ * first. Then, I/O thread writes the event into the relay log. ++ */ ++ enum RelayStatus { ++ MULE_BEHIND = 1, ++ RELAY_MATCH_MULE = 2, ++ RELAY_MATCH_MULE_RUN = 7, ++ MULE_VERIFY = 3, ++ MULE_VERIFY_RELAY_BEHIND = 4, ++ MULE_ERROR = 5, ++ }; ++ ++ enum WriteStatus { ++ WRITE_RELAY = 1, ++ WRITE_ERROR = 2, ++ SKIP_RELAY = 3, ++ }; ++ ++ private: ++ const Format_description_log_event *desc_event_; ++ THD *io_thd_; ++ MASTER_INFO *mi_; ++ ++ /* ++ * I/O thread will write both mule log for mirror binlog and relay log ++ * for SQL thread. ++ * The variable indicates whether the two are in sync. ++ */ ++ RelayStatus status_; ++ ++ /* The starting event writing position. */ ++ my_off_t dump_position_; ++ ++ /* During the initial setup, the last mule log's file size. */ ++ my_off_t file_size_; ++ ++ /* Internally, we call the mirrored binlog mule log. */ ++ MYSQL_LOG *mule_log_; ++ ++ /* Sync the mule log to disk for every #N events. */ ++ ulong mule_log_sync_period_; ++ ulong mule_log_event_counter_; ++ ++ /* mule log's index filename */ ++ char mule_indexname_[FN_REFLEN]; ++ ++ /* the current mule log's filename */ ++ char curr_log_filename_[FN_REFLEN]; ++ ++ ReplMule(THD* thd, MASTER_INFO *mi, RelayStatus status, ++ my_off_t file_size, const char *binlog_indexname, ++ MYSQL_LOG *binlog, ulong sync_period); ++ ++ /* ++ * Queue the event into the current mule log. If it is a rotation ++ * event, generate a new mule log file. ++ * Indicate whether the event is skipped because it is an fake event. ++ * A fake event is generated by the master to indicate the current ++ * reading position. ++ */ ++ int queueEvent(const char* buf, ulong event_len, bool *skip_event); ++ ++ /* Append the event to the current mule log. */ ++ int appendEvent(const char* buf, ulong event_len); ++ ++ bool IsFakeRotation(const char* buf, ulong event_len); ++ bool IsFakeRotation(const Rotate_log_event& rev); ++ ++ /* Seek to the specified position in the current open mule log. */ ++ void seekToPosition(my_off_t pos); ++ ++ public: ++ ++ ~ReplMule(); ++ ++ /* Dump the event into mule binlog. ++ * Input: ++ * buf (IN) - replication event buffer ++ * event_len (IN) - the event length ++ * ++ * Return: ++ * . WRITE_RELAY: the relay log needs to writing the event ++ * . WRITE_ERROR: the writing encountered errors ++ * . SKIP_RELAY: the relay log should skip the event ++ */ ++ WriteStatus writeEvent(const char* buf, ulong event_len); ++ ++ /* createReplicationMule: ++ * Create a mule that relays master's replication binlog and ++ * generate an exact same copy on the local filesystem. ++ * ++ * Input: ++ * thd (IN) - replication I/O thread ++ * mi (IN) - master info struct for I/O thread's progress ++ * binlog_indexname (IN) - filename for binlog's index ++ * binlog (IN) - replication binlog ++ * ++ * Return: ++ * . a replication mule if success ++ * . NULL if there are any errors ++ */ ++ static ReplMule *createReplicationMule(THD* thd, MASTER_INFO *mi, ++ const char *binlog_indexname, ++ MYSQL_LOG *binlog); ++}; ++ ++#endif /* SQL_REPL_MULE_H__ */ +diff -r 66cc9e0a6768 sql/set_var.cc +--- a/sql/set_var.cc Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/set_var.cc Thu Dec 04 21:46:15 2008 -0800 +@@ -345,6 +345,8 @@ + slog_verb); + sys_var_long_ptr sys_rpl_recovery_rank("rpl_recovery_rank", + &rpl_recovery_rank); ++sys_var_bool_ptr sys_rpl_mirror_binlog_enabled("rpl_mirror_binlog_enabled", ++ &rpl_mirror_binlog_enabled); + sys_var_long_ptr sys_query_cache_size("query_cache_size", + &query_cache_size, + fix_query_cache_size); +@@ -364,6 +366,9 @@ + sys_var_thd_ulong sys_trans_prealloc_size("transaction_prealloc_size", + &SV::trans_prealloc_size, + 0, fix_trans_mem_root); ++sys_var_long_ptr sys_sync_mirror_binlog_period( ++ "sync_mirror_binlog_period", ++ &sync_mirror_binlog_period); + + #ifdef HAVE_QUERY_CACHE + sys_var_long_ptr sys_query_cache_limit("query_cache_limit", +@@ -774,6 +779,7 @@ + &sys_relay_log_purge, + #endif + &sys_rpl_recovery_rank, ++ &sys_rpl_mirror_binlog_enabled, + &sys_safe_updates, + &sys_secure_auth, + &sys_secure_file_priv, +@@ -1113,6 +1119,8 @@ + {"relay_log_space_limit", (char*) &relay_log_space_limit, SHOW_LONGLONG}, + #endif + {sys_rpl_recovery_rank.name,(char*) &sys_rpl_recovery_rank, SHOW_SYS}, ++ {sys_rpl_mirror_binlog_enabled.name, ++ (char *) &sys_rpl_mirror_binlog_enabled, SHOW_SYS}, + {"secure_auth", (char*) &sys_secure_auth, SHOW_SYS}, + {"secure_file_priv", (char*) &sys_secure_file_priv, SHOW_SYS}, + #ifdef HAVE_SMEM +diff -r 66cc9e0a6768 sql/slave.cc +--- a/sql/slave.cc Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/slave.cc Thu Dec 04 21:46:15 2008 -0800 +@@ -25,6 +25,7 @@ + #include <thr_alarm.h> + #include <my_dir.h> + #include <sql_common.h> ++#include "repl_mule.h" + #include <errmsg.h> + #include <mysys_err.h> + +@@ -3527,6 +3528,7 @@ + RELAY_LOG_INFO *rli= &mi->rli; + char llbuff[22]; + uint retry_count; ++ ReplMule *mule = NULL; + + // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff + my_thread_init(); +@@ -3609,6 +3611,23 @@ + if (get_master_version_and_clock(mysql, mi)) + goto err; + ++ if (rpl_mirror_binlog_enabled && !mule) { ++ if (opt_binlog_index_name == NULL) { ++ sql_print_error("\"log-bin-index\" must be set in mirror binlog."); ++ goto err; ++ } ++ ++ /* Create the mule to generate the exact copy of the binlog */ ++ mule = ReplMule::createReplicationMule( ++ thd, mi, opt_binlog_index_name, &mysql_bin_log); ++ ++ /* If we could not create the mule, we stop the I/O thread and report ++ * an error. ++ */ ++ if (mule == NULL) ++ goto err; ++ } ++ + if (mi->rli.relay_log.description_event_for_queue->binlog_version > 1) + { + /* +@@ -3624,6 +3643,7 @@ + DBUG_PRINT("info",("Starting reading binary log from master")); + while (!io_slave_killed(thd,mi)) + { ++ const char* event_buf; + bool suppress_warnings= 0; + thd_proc_info(thd, "Requesting binlog dump"); + if (request_dump(mysql, mi, &suppress_warnings)) +@@ -3754,10 +3774,25 @@ + goto connected; + } // if (event_len == packet_error) + ++ event_buf = (const char*)mysql->net.read_pos + 1; ++ ++ if (mule) { ++ ReplMule::WriteStatus d_status = ++ mule->writeEvent(event_buf, event_len); ++ switch (d_status) { ++ case ReplMule::WRITE_RELAY: ++ break; ++ case ReplMule::SKIP_RELAY: ++ /* Skip writing relay event; go back to read the next event */ ++ continue; ++ case ReplMule::WRITE_ERROR: ++ goto err; ++ } ++ } ++ + retry_count=0; // ok event, reset retry counter + thd_proc_info(thd, "Queueing master event to the relay log"); +- if (queue_event(mi,(const char*)mysql->net.read_pos + 1, +- event_len)) ++ if (queue_event(mi, event_buf, event_len)) + { + sql_print_error("Slave I/O thread could not queue event from master"); + goto err; +@@ -3847,6 +3882,7 @@ + change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE); + DBUG_ASSERT(thd->net.buff != 0); + net_end(&thd->net); // destructor will not free it, because net.vio is 0 ++ delete mule; + close_thread_tables(thd, 0); + pthread_mutex_lock(&LOCK_thread_count); + THD_CHECK_SENTRY(thd); +diff -r 66cc9e0a6768 sql/sql_class.h +--- a/sql/sql_class.h Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/sql_class.h Thu Dec 04 21:46:15 2008 -0800 +@@ -152,6 +152,12 @@ + #define LOG_INFO_FATAL -7 + #define LOG_INFO_IN_USE -8 + ++/* If the maximum size is equal to this value, binlog would not rotate on ++ * size limit. ++ */ ++#define BINLOG_NOSWITCH_SIZE ((ulong) -1) ++ ++ + /* bitmap to SQL_LOG::close() */ + #define LOG_CLOSE_INDEX 1 + #define LOG_CLOSE_TO_BE_OPENED 2 +@@ -245,6 +251,9 @@ + bool no_auto_events; + friend class Log_event; + ++ /* mule replication mode */ ++ bool mule_binlog_; ++ + public: + /* + These describe the log's format. This is used only for relay logs. +@@ -317,7 +326,8 @@ + } + bool open_index_file(const char *index_file_name_arg, + const char *log_name); +- void new_file(bool need_lock); ++ int close_index_file(); ++ void new_file(bool need_lock= 1, const char* log_filename= NULL); + bool write(THD *thd, enum enum_server_command command, + const char *format, ...) ATTRIBUTE_FORMAT(printf, 4, 5); + bool write(THD *thd, const char *query, uint query_length, +@@ -357,7 +367,27 @@ + int get_current_log(LOG_INFO* linfo); + int raw_get_current_log(LOG_INFO* linfo); + uint next_file_id(); +- inline bool is_open() { return log_type != LOG_CLOSED; } ++ ++ /* Because mysql use is_open() to check whether replication is on, ++ * we will let the check fail during binlog mule mode. Mule replication ++ * and normal master replication can not be on at the same time. ++ * ++ * is_log_open(): the binlog file is open for either purpose ++ * ++ * is_open(): the binlog is open for master replication. ++ * is_mule_open(): the binlog is open for mirror binlog or for ++ * replication mule; refer repl_mule.h for details ++ */ ++ bool is_log_open() { ++ return log_type != LOG_CLOSED; ++ } ++ bool is_open() { ++ return (!mule_binlog_) && is_log_open(); ++ } ++ bool is_mule_open() { ++ return (mule_binlog_) && is_log_open(); ++ } ++ + inline char* get_index_fname() { return index_file_name;} + inline char* get_log_fname() { return log_file_name; } + inline char* get_name() { return name; } +@@ -366,8 +396,18 @@ + + inline void lock_index() { pthread_mutex_lock(&LOCK_index);} + inline void unlock_index() { pthread_mutex_unlock(&LOCK_index);} ++ inline void lock_log() { pthread_mutex_lock(&LOCK_log);} ++ inline void unlock_log() { pthread_mutex_unlock(&LOCK_log);} + inline IO_CACHE *get_index_file() { return &index_file;} + inline uint32 get_open_count() { return open_count; } ++ /* Look in file repl_mule.h for the definition of mule. */ ++ void set_mule_mode() { ++ mule_binlog_ = 1; ++ } ++ void clear_mule_mode() { ++ mule_binlog_ = 0; ++ } ++ int flush_log_file(); + }; + + /* +diff -r 66cc9e0a6768 sql/sql_lex.h +--- a/sql/sql_lex.h Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/sql_lex.h Thu Dec 04 21:46:15 2008 -0800 +@@ -104,6 +104,7 @@ + // TODO(mcallaghan): update status_vars in mysqld to export these + SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS, + SQLCOM_SHOW_CLIENT_STATS, ++ SQLCOM_MAKE_MASTER, + /* This should be the last !!! */ + SQLCOM_END + }; +@@ -171,6 +172,12 @@ + char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher; + char *relay_log_name; + ulong relay_log_pos; ++ ++ /* the following fields are used for make master command */ ++ char *log_index_name; ++ bool in_failover; ++ bool kill_session; ++ bool with_old_binlog; + } LEX_MASTER_INFO; + + +diff -r 66cc9e0a6768 sql/sql_parse.cc +--- a/sql/sql_parse.cc Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/sql_parse.cc Thu Dec 04 21:46:15 2008 -0800 +@@ -402,6 +402,15 @@ + passwd_len ? "yes": "no", + thd->main_security_ctx.master_access, + (thd->db ? thd->db : "*none*"))); ++ ++ /* If we are in failover mode, reject all non-super user connections. */ ++ if (is_in_failover() && ++ !(thd->main_security_ctx.master_access & SUPER_ACL)) { ++ net_send_error(thd, ER_SPECIFIC_ACCESS_DENIED_ERROR, ++ "super-user only during failover"); ++ DBUG_RETURN(-1); ++ } ++ + + if (check_count) + { +@@ -3470,6 +3479,22 @@ + else + res = load_master_data(thd); + break; ++ ++ case SQLCOM_MAKE_MASTER: ++ { ++ thd_proc_info(thd, "Making master"); ++ ++ if (check_global_access(thd, SUPER_ACL)) ++ goto error; ++ res = make_master(thd, NULL, NULL, &lex->mi); ++ if (res == 0) { ++ // TODO -- wei is this OK, setting it to NULL? ++ thd_proc_info(thd, 0); ++ send_ok(thd); ++ } ++ break; ++ } ++ + #endif /* HAVE_REPLICATION */ + #ifdef HAVE_NDBCLUSTER_DB + case SQLCOM_SHOW_NDBCLUSTER_STATUS: +diff -r 66cc9e0a6768 sql/sql_repl.cc +--- a/sql/sql_repl.cc Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/sql_repl.cc Thu Dec 04 21:46:15 2008 -0800 +@@ -20,11 +20,19 @@ + #include "log_event.h" + #include <my_dir.h> + ++extern pthread_mutex_t LOCK_failover_master; ++extern bool failover_deny_access; ++ + int max_binlog_dump_events = 0; // unlimited + my_bool opt_sporadic_binlog_dump_fail = 0; + #ifndef DBUG_OFF + static int binlog_dump_count = 0; + #endif ++ ++static int make_master_open_log(MYSQL_LOG *log, const char *opt_name, ++ bool no_auto_events, ulong max_size); ++static int set_in_failover(bool kill_session); ++static void clear_in_failover(void); + + /* + fake_rotate_event() builds a fake (=which does not exist physically in any +@@ -255,7 +263,7 @@ + bool purge_master_logs(THD* thd, const char* to_log) + { + char search_file_name[FN_REFLEN]; +- if (!mysql_bin_log.is_open()) ++ if (!mysql_bin_log.is_log_open()) + { + send_ok(thd); + return FALSE; +@@ -308,6 +316,44 @@ + return error; + } + ++/* Show processlist command dump the binlog state. ++ * ++ * Input: ++ * output_info - (OUT) the output proc_info ++ * output_len - (IN) output proc_info's length ++ * thd - (IN) the thread ++ * input_msg - (IN) the input proc_info ++ * log_file_name - (IN) binlog file name ++ * log_pos - (IN) binlog position ++ */ ++static void processlist_show_binlog_state(char *output_info, ++ int output_len, ++ THD *thd, ++ const char *input_msg, ++ const char *log_file_name, ++ my_off_t log_pos) { ++ DBUG_ENTER("processlist_show_binlog_state"); ++ ++ /* Point to input_msg in case "show processlist" access it before the copy ++ * is finished. ++ */ ++ thd_proc_info(thd, input_msg); ++ ++ if (snprintf(output_info, output_len, "%s :%s:%lld:", input_msg, ++ log_file_name + dirname_length(log_file_name), ++ log_pos) > 0) { ++ thd_proc_info(thd, output_info); ++ } ++ ++ DBUG_VOID_RETURN; ++} ++ ++static void repl_cleanup(ushort flags) { ++ if (flags & BINLOG_MIRROR_CLIENT) { ++ /* One less mirror binlog client. */ ++ thread_safe_sub(rpl_mirror_binlog_clients, 1, &LOCK_stats); ++ } ++} + + /* + TODO: Clean up loop to only have one call to send_file() +@@ -319,6 +365,11 @@ + LOG_INFO linfo; + char *log_file_name = linfo.log_file_name; + char search_file_name[FN_REFLEN], *name; ++ ++ /* This buffer should be enough for "comments + :file_name:file_pos:". */ ++ char binlog_state_msg[FN_REFLEN + 100]; ++ int binlog_state_msg_len = FN_REFLEN + 100; ++ + IO_CACHE log; + File file = -1; + String* packet = &thd->packet; +@@ -335,6 +386,15 @@ + + bzero((char*) &log,sizeof(log)); + ++ sql_print_information("Start %s binlog_dump to slave_server(%d), pos(%s, %lu)", ++ "asynchronous", ++ thd->server_id, log_ident, (ulong)pos); ++ ++ if (flags & BINLOG_MIRROR_CLIENT) { ++ /* One more mirror binlog clients. */ ++ thread_safe_increment(rpl_mirror_binlog_clients, &LOCK_stats); ++ } ++ + #ifndef DBUG_OFF + if (opt_sporadic_binlog_dump_fail && (binlog_dump_count++ % 2)) + { +@@ -344,7 +404,7 @@ + } + #endif + +- if (!mysql_bin_log.is_open()) ++ if (!mysql_bin_log.is_log_open()) + { + errmsg = "Binary log is not open"; + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; +@@ -529,6 +589,12 @@ + } + #endif + ++ /* Update the binlog sending state. */ ++ processlist_show_binlog_state( ++ binlog_state_msg, binlog_state_msg_len, thd, ++ "Send binlog events to slave", ++ log_file_name, pos); ++ + if ((*packet)[EVENT_TYPE_OFFSET+1] == FORMAT_DESCRIPTION_EVENT) + { + binlog_can_be_corrupted= test((*packet)[FLAGS_OFFSET+1] & +@@ -634,6 +700,13 @@ + } + if (!thd->killed) + { ++ /* Update the binlog sending state. */ ++ processlist_show_binlog_state( ++ binlog_state_msg, binlog_state_msg_len, thd, ++ "Has sent all binlog to slave; " ++ "waiting for binlog to be updated", ++ log_file_name, pos); ++ + /* Note that the following call unlocks lock_log */ + mysql_bin_log.wait_for_update(thd, 0); + } +@@ -650,7 +723,12 @@ + + if (read_packet) + { +- thd_proc_info(thd, "Sending binlog event to slave"); ++ // thd_proc_info(thd, "Sending binlog event to slave"); ++ /* Update the binlog sending state. */ ++ processlist_show_binlog_state(binlog_state_msg, ++ binlog_state_msg_len, thd, ++ "Sending binlog event to slave", ++ log_file_name, pos); + if (my_net_write(net, (char*)packet->ptr(), packet->length()) ) + { + errmsg = "Failed on my_net_write()"; +@@ -685,10 +763,21 @@ + } + else + { ++ char old_log_file_name[FN_REFLEN]; + bool loop_breaker = 0; + /* need this to break out of the for loop from switch */ + +- thd_proc_info(thd, "Finished reading one binlog; switching to next binlog"); ++ // thd_proc_info(thd, "Finished reading one binlog; switching to next binlog"); ++ /* Update the binlog sending state. */ ++ processlist_show_binlog_state( ++ binlog_state_msg, binlog_state_msg_len, thd, ++ "Finished reading one binlog; switching to next binlog", ++ log_file_name, pos); ++ ++ /* Keep the old fileename. */ ++ strmake(old_log_file_name, log_file_name, ++ sizeof(old_log_file_name) - 1); ++ + switch (mysql_bin_log.find_next_log(&linfo, 1)) { + case LOG_INFO_EOF: + loop_breaker = (flags & BINLOG_DUMP_NON_BLOCK); +@@ -706,6 +795,16 @@ + + end_io_cache(&log); + (void) my_close(file, MYF(MY_WME)); ++ ++ /* A sanity check that we can not serve the same binlog twice because ++ * the filenames are stored in a .index file. ++ */ ++ if (strcmp(old_log_file_name, log_file_name) >= 0) { ++ errmsg = "Re-serving an already served binlog file."; ++ my_errno = ER_MASTER_FATAL_ERROR_READING_BINLOG; ++ goto err; ++ } ++ + + /* + Call fake_rotate_event() in case the previous log (the one which +@@ -733,6 +832,8 @@ + end_io_cache(&log); + (void)my_close(file, MYF(MY_WME)); + ++ repl_cleanup(flags); ++ + send_eof(thd); + thd_proc_info(thd, "Waiting to finalize termination"); + pthread_mutex_lock(&LOCK_thread_count); +@@ -743,6 +844,7 @@ + err: + thd_proc_info(thd, "Waiting to finalize termination"); + end_io_cache(&log); ++ repl_cleanup(flags); + /* + Exclude iteration through thread list + this is needed for purge_logs() - it will iterate through +@@ -1316,7 +1418,7 @@ + Format_description_log_event *description_event= new + Format_description_log_event(3); /* MySQL 4.0 by default */ + +- if (mysql_bin_log.is_open()) ++ if (mysql_bin_log.is_log_open()) + { + LEX_MASTER_INFO *lex_mi= &thd->lex->mi; + SELECT_LEX_UNIT *unit= &thd->lex->unit; +@@ -1456,7 +1558,7 @@ + DBUG_RETURN(TRUE); + protocol->prepare_for_resend(); + +- if (mysql_bin_log.is_open()) ++ if (mysql_bin_log.is_log_open()) + { + LOG_INFO li; + mysql_bin_log.get_current_log(&li); +@@ -1497,7 +1599,7 @@ + Protocol *protocol= thd->protocol; + DBUG_ENTER("show_binlogs"); + +- if (!mysql_bin_log.is_open()) ++ if (!mysql_bin_log.is_log_open()) + { + my_message(ER_NO_BINARY_LOGGING, ER(ER_NO_BINARY_LOGGING), MYF(0)); + return 1; +@@ -1606,6 +1708,235 @@ + DBUG_RETURN(0); + } + ++ ++/* make_master: Make the current database a primary and starts the ++ * binlog logging for all updates. ++ * ++ * The function handles the following sql commands: ++ * . MAKE MASTER MASTER_LOG_FILE='replication_log', MASTER_SERVER_ID=1, ++ * [WITH BINLOG]; ++ * . MAKE MASTER MASTER_LOG_FILE='replication_log', MASTER_SERVER_ID=1, ++ * INDEX='replication_log.index' [WITH BINLOG]; ++ * . MAKE MASTER REVOKE SESSION; ++ * . MAKE MASTER REVOKE SESSION WITH KILL; ++ * . MAKE MASTER GRANT SESSION; ++ * ++ * Args: ++ * thd - the current thread ++ * binlog_name - binlog's filename ++ * binlog_indexname - binlog index's filename ++ * mi - master info struct containing binlog name ++ * (set when we enable master during runtime) ++ * ++ * Return: ++ * 0 : success ++ * -1 : failure ++ */ ++int make_master(THD* thd, ++ const char *binlog_name, ++ const char *binlog_indexname, ++ const LEX_MASTER_INFO* mi) { ++ int error = 0; ++ ++ DBUG_ENTER("make_master"); ++ /* In two mode, we enable the binlog: ++ * . !mi - LEX is not provided; this is called from startup time ++ * . mi->log_file_name - binlog is specified in the command ++ */ ++ if (!mi || mi->log_file_name) { ++ /* Get the mutex */ ++ VOID(pthread_mutex_lock(&LOCK_failover_master)); ++ ++ /* If the binlog is already opened, we issue an error. We reuse one ++ * existing error, which might not be fully accurate. ++ */ ++ if (mysql_bin_log.is_log_open()) { ++ my_error(ER_MASTER_INFO, MYF(0)); ++ sql_print_error("Replication master log is already open: cannot " ++ "make another master!"); ++ error = -1; ++ } else { ++ if (!mi) { ++ /* This opening happens at mysql startup time. */ ++ if (make_master_open_log(&mysql_bin_log, binlog_name, ++ 0, max_binlog_size) != 0) { ++ error = -1; ++ } ++ } else { ++ /* This opening happens during mysql runtime, which is mostly ++ * requested to do failover. ++ */ ++ ++ error = -1; ++ if (!is_in_failover()) { ++ sql_print_error( ++ "\"make master\" runs only in failover mode. " ++ "Please run \"make master revoke session (with kill)\""); ++ } else if (strlen(mi->log_file_name) == 0) { ++ sql_print_error("Master log filename is not specified correctly."); ++ } else if (!mi->server_id || mi->server_id == MASTER_INFO_SERVER_ID) { ++ sql_print_error("\"make master\": invalid server_id(%d)", ++ mi->server_id); ++ } else { ++ /* Open the new log files and delete all existing ones to avoid ++ * conflicts. ++ */ ++ uint32 old_server_id = server_id; ++ char *binlog_name = NULL; ++ ++ /* Set the global master server id. ++ * We would not change server id for all connection threads. ++ * All non-super sessions should be blocked by revoke sessions. ++ * Super-user sessions are responsible for their own operations. ++ */ ++ server_id = mi->server_id; ++ thd->server_id = mi->server_id; ++ ++ if (!(binlog_name = my_strdup(mi->log_file_name, MYF(0))) || ++ make_master_open_index(&binlog_name, mi->log_index_name) != 0 || ++ make_master_open_log(&mysql_bin_log, binlog_name, ++ 0, max_binlog_size) != 0) { ++ sql_print_error("Open master logfile failed."); ++ thd->server_id = old_server_id; ++ server_id = old_server_id; ++ } else if (!mi->with_old_binlog && ++ mysql_bin_log.reset_logs(thd) != 0) { ++ sql_print_error("Cleanup existing master logfiles failed."); ++ thd->server_id = old_server_id; ++ server_id = old_server_id; ++ } else { ++ error = 0; ++ } ++ } ++ if (error == -1) ++ my_error(ER_MASTER_INFO, MYF(0)); ++ } ++ } ++ ++ if (error == 0) { ++ /* indicates that binlog is enabled now */ ++ using_update_log = 1; ++ } else if (mysql_bin_log.is_open()) { ++ mysql_bin_log.close(LOG_CLOSE_INDEX); ++ } ++ ++ /* Release the mutex */ ++ VOID(pthread_mutex_unlock(&LOCK_failover_master)); ++ } else { ++ /* The following actions are related to session management during ++ * failover operation. We do not want some sessions come in ++ * during failover and make updates. ++ * This is invoked for command: MAKE MASTER GRANT/REVOKE SESSION; ++ */ ++ if (mi->in_failover) { ++ set_in_failover(mi->kill_session); ++ } else { ++ clear_in_failover(); ++ } ++ } ++ ++ DBUG_RETURN(error); ++} ++ ++static int make_master_open_log(MYSQL_LOG *log, ++ const char *opt_name, ++ bool no_auto_events, ++ ulong max_size) { ++ char tmp[FN_REFLEN]; ++ ++ // get rid of extension ++ char *p = fn_ext(opt_name); ++ uint length=(uint) (p-opt_name); ++ strmake(tmp,opt_name,min(length,FN_REFLEN)); ++ opt_name=tmp; ++ ++ return log->open(opt_name, LOG_BIN, NULL, WRITE_CACHE, 0, ++ max_size, 0); ++} ++ ++int make_master_open_index(char **binlog_name, ++ const char *binlog_indexname) { ++ char buf[FN_REFLEN]; ++ const char *ln; ++ DBUG_ENTER("make_master_open_index"); ++ ++ ln= mysql_bin_log.generate_name(*binlog_name, "-bin", 1, buf); ++ if (!(*binlog_name) && !binlog_indexname) { ++ /* ++ User didn't give us info to name the binlog index file. ++ Picking `hostname`-bin.index like did in 4.x, causes replication to ++ fail if the hostname is changed later. So, we would like to instead ++ require a name. But as we don't want to break many existing setups, we ++ only give warning, not error. ++ */ ++ sql_print_warning("No argument was provided to --log-bin, and " ++ "--log-bin-index was not used; so replication " ++ "may break when this MySQL server acts as a " ++ "master and has his hostname changed!! Please " ++ "use '--log-bin=%s' to avoid this problem.", ln); ++ } ++ if (ln == buf) { ++ my_free(*binlog_name, MYF(MY_ALLOW_ZERO_PTR)); ++ *binlog_name = my_strdup(buf, MYF(0)); ++ } ++ if (mysql_bin_log.open_index_file(binlog_indexname, ln) != 0) { ++ DBUG_RETURN(-1); ++ } ++ ++ /* ++ Used to specify which type of lock we need to use for queries of type ++ INSERT ... SELECT. This will change when we have row level logging. ++ */ ++ using_update_log=1; ++ ++ DBUG_RETURN(0); ++} ++ ++/* Set the status indicating that we are in failover and deny all non-super ++ * user access. ++ * ++ * Args: ++ * kill_session - kill all non-super sessions if specified ++ * ++ * Return: ++ * 0 - success ++ * -1 - failure (caused by not killing all sessions) ++ */ ++static int set_in_failover(bool kill_session) { ++ failover_deny_access = 1; ++ ++ if (kill_session) { ++ /* If kill session option is specified, we need to kill all non-super ++ * user sessions. ++ */ ++ THD *kill_thd; ++ ++ uint error=ER_NO_SUCH_THREAD; ++ pthread_mutex_lock(&LOCK_thread_count); // For unlink from list ++ I_List_iterator<THD> it(threads); ++ while ((kill_thd=it++)) { ++ if (!(kill_thd->main_security_ctx.master_access & SUPER_ACL)) { ++ pthread_mutex_lock(&kill_thd->LOCK_delete); // Lock from delete ++ ++ /* ask the thread to die */ ++ kill_thd->awake(THD::KILL_CONNECTION); ++ pthread_mutex_unlock(&kill_thd->LOCK_delete); ++ } ++ } ++ pthread_mutex_unlock(&LOCK_thread_count); ++ } ++ return 0; ++} ++ ++static void clear_in_failover(void) { ++ failover_deny_access = 0; ++} ++ ++bool is_in_failover(void) { ++ return failover_deny_access; ++} ++ ++ + #endif /* HAVE_REPLICATION */ + + +diff -r 66cc9e0a6768 sql/sql_repl.h +--- a/sql/sql_repl.h Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/sql_repl.h Thu Dec 04 21:46:15 2008 -0800 +@@ -38,6 +38,10 @@ + int start_slave(THD* thd, MASTER_INFO* mi, bool net_report); + int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report); + bool change_master(THD* thd, MASTER_INFO* mi); ++int make_master(THD* thd, const char *binlog_name, ++ const char *binlog_indexname, const LEX_MASTER_INFO* mi); ++int make_master_open_index(char **binlog_name, const char *binlog_indexname); ++bool is_in_failover(void); + bool mysql_show_binlog_events(THD* thd); + int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1, + const char* log_file_name2, ulonglong log_pos2); +diff -r 66cc9e0a6768 sql/sql_yacc.yy +--- a/sql/sql_yacc.yy Thu Dec 04 21:37:12 2008 -0800 ++++ b/sql/sql_yacc.yy Thu Dec 04 21:46:15 2008 -0800 +@@ -735,6 +735,7 @@ + %token LOOP_SYM + %token LOW_PRIORITY + %token LT ++%token MAKE_SYM + %token MAKE_SET_SYM + %token MASTER_CONNECT_RETRY_SYM + %token MASTER_HOST_SYM +@@ -1167,7 +1168,7 @@ + query verb_clause create change select do drop insert replace insert2 + insert_values update delete truncate rename + show describe load alter optimize keycache preload flush +- reset purge begin commit rollback savepoint release ++ make reset purge begin commit rollback savepoint release + slave master_def master_defs master_file_def slave_until_opts + repair restore backup analyze check start checksum + field_list field_list_item field_spec kill column_def key_def +@@ -1301,6 +1302,7 @@ + | kill + | load + | lock ++ | make + | optimize + | keycache + | preload +@@ -1428,6 +1430,56 @@ + master_defs + {} + ; ++ ++/* make master */ ++make: ++ MAKE_SYM MASTER_SYM ++ { ++ LEX *lex = Lex; ++ lex->sql_command = SQLCOM_MAKE_MASTER; ++ bzero((char*) &lex->mi, sizeof(lex->mi)); ++ } ++ make_master_defs ++ { ++ } ++ ; ++ ++make_master_defs: ++ MASTER_LOG_FILE_SYM EQ TEXT_STRING ',' MASTER_SERVER_ID_SYM EQ ulong_num ++ { ++ Lex->mi.log_file_name = $3.str; ++ Lex->mi.server_id = $7; ++ } ++ make_master_with_defs {} ++ | MASTER_LOG_FILE_SYM EQ TEXT_STRING ',' MASTER_SERVER_ID_SYM EQ ulong_num ',' INDEX_SYM EQ TEXT_STRING ++ { ++ Lex->mi.log_file_name = $3.str; ++ Lex->mi.server_id = $7; ++ Lex->mi.log_index_name = $11.str; ++ } ++ make_master_with_defs {} ++ | GRANT SESSION_SYM ++ { ++ Lex->mi.in_failover = 0; ++ } ++ | REVOKE SESSION_SYM ++ { ++ Lex->mi.in_failover = 1; ++ } ++ | REVOKE SESSION_SYM WITH KILL_SYM ++ { ++ Lex->mi.in_failover = 1; ++ Lex->mi.kill_session = 1; ++ } ++ ; ++ ++make_master_with_defs: ++ /* empty */ {} ++ | WITH BINLOG_SYM ++ { ++ /* All old binlogs will be kept after "make master" command. */ ++ Lex->mi.with_old_binlog = 1; ++ } + + master_defs: + master_def +@@ -8396,6 +8448,7 @@ + | HANDLER_SYM {} + | HELP_SYM {} + | LANGUAGE_SYM {} ++ | MAKE_SYM {} + | NO_SYM {} + | OPEN_SYM {} + | PREPARE_SYM {} |