From f1c32705f1800bdcff4f8f8fd441552272fe1ce2 Mon Sep 17 00:00:00 2001 From: "mats@capulet.kindahl.net" <> Date: Thu, 20 Dec 2007 16:07:54 +0100 Subject: [PATCH] BUG#12691 (Exec_master_log_pos corrupted with SQL_SLAVE_SKIP_COUNTER): Complementary patch since LOAD DATA INFILE was not covered in the previous patch. This patch adds a check so that the slave skip counter is not decreased to zero if seeing a BEGIN_LOAD_QUERY_EVENT, APPEND_BLOCK_EVENT, or CREATE_FILE_EVENT since these cannot end a group. The group is terminated by an EXECUTE_LOAD_QUERY_ EVENT or DELETE_FILE_EVENT. --- mysql-test/r/rpl_slave_skip.result | 104 +++++++++++++++++- mysql-test/t/rpl_slave_skip.test | 169 ++++++++++++++++++++++++++++- sql/slave.cc | 5 +- 3 files changed, 271 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/rpl_slave_skip.result b/mysql-test/r/rpl_slave_skip.result index a59ac3eb884..0e06e0951b3 100644 --- a/mysql-test/r/rpl_slave_skip.result +++ b/mysql-test/r/rpl_slave_skip.result @@ -5,8 +5,10 @@ reset slave; drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; start slave; **** On Master **** -CREATE TABLE t1 (a INT, b SET('master','slave')) ENGINE=INNODB; -CREATE TABLE t2 (a INT, b SET('master','slave')) ENGINE=MYISAM; +CREATE TABLE t1 (a INT, b SET('master','slave')) ENGINE=InnoDB; +CREATE TABLE t2 (a INT, b SET('master','slave')) ENGINE=MyISAM; +CREATE TABLE t3 (a CHAR(20), b SET('master','slave')) ENGINE=InnoDB; +CREATE TABLE t4 (a CHAR(20), b SET('master','slave')) ENGINE=MyISAM; ==== Skipping normal transactions ==== **** On Slave **** STOP SLAVE; @@ -139,6 +141,102 @@ a b SELECT * FROM t2 ORDER BY a; a b 5 master,slave +==== Skipping first event of a LOAD DATA for a transactional table ==== +**** On Slave **** +STOP SLAVE; +**** On Master **** +SET AUTOCOMMIT=1; +LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE t3(a) SET b = 'master'; +INSERT INTO t3 VALUES ('Go Rin No Sho', 'master,slave'); +SELECT COUNT(*) FROM t3; +COUNT(*) +71 +**** On Slave **** +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +-- Should only contain records marked 'master,slave' +SELECT * FROM t3 ORDER BY a; +a b +Go Rin No Sho master,slave +**** On Master **** +DELETE FROM t3; +==== Skipping first event of a LOAD DATA for a non-transactional table ==== +**** On Slave **** +STOP SLAVE; +**** On Master **** +SET AUTOCOMMIT=1; +LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE t4(a) SET b = 'master'; +INSERT INTO t4 VALUES ('Go Rin No Sho', 'master,slave'); +SELECT COUNT(*) FROM t4; +COUNT(*) +71 +**** On Slave **** +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +-- Should only contain records marked 'master,slave' +SELECT * FROM t4 ORDER BY a; +a b +Go Rin No Sho master,slave +**** On Master **** +DELETE FROM t4; +==== Try with a big file so that we get an append_block event as well +**** On Slave **** +STOP SLAVE; +**** On Master **** +SET AUTOCOMMIT=1; +SET SQL_LOG_BIN=0; +LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE t4(a) SET b = 'master'; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +SELECT a FROM t4 INTO OUTFILE 'rpl_slave_skip_words.dat'; +SET SQL_LOG_BIN=1; +LOAD DATA INFILE 'rpl_slave_skip_words.dat' INTO TABLE t4(a) SET b = 'master'; +INSERT INTO t4 VALUES ('Go Rin No Sho', 'master,slave'); +SELECT COUNT(*) FROM t4; +COUNT(*) +286721 +**** On Slave **** +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +-- Should only contain records marked 'master,slave' +SELECT * FROM t4 ORDER BY a; +a b +Go Rin No Sho master,slave +**** On Master **** +DELETE FROM t4; +**** On Master **** +CREATE TABLE t5 (a int, b int, c SET('master','slave'), PRIMARY KEY (a,b)) ENGINE=MyISAM; +LOAD DATA INFILE '../std_data_ln/loaddata5.dat' INTO TABLE t5 FIELDS TERMINATED BY '' ENCLOSED BY '' (a,b) SET c='master,slave'; +**** On Slave **** +STOP SLAVE; +**** On Master **** +LOAD DATA INFILE '../std_data_ln/loaddata5.dat' INTO TABLE t5 FIELDS TERMINATED BY '' ENCLOSED BY '' (a,b) SET c=''; +ERROR 23000: Duplicate entry '1-2' for key 1 +INSERT INTO t5 VALUES (42, 42, 'master,slave'); +SELECT * FROM t5; +a b c +1 2 master,slave +3 4 master,slave +5 6 master,slave +42 42 master,slave +**** On Slave **** +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +SELECT * FROM t5; +a b c +1 2 master,slave +3 4 master,slave +5 6 master,slave +42 42 master,slave ==== Cleanup ==== **** On Master **** -DROP TABLE t1, t2; +DROP TABLE t1, t2, t3, t4, t5; diff --git a/mysql-test/t/rpl_slave_skip.test b/mysql-test/t/rpl_slave_skip.test index 04aafc51129..71e951aceea 100644 --- a/mysql-test/t/rpl_slave_skip.test +++ b/mysql-test/t/rpl_slave_skip.test @@ -13,8 +13,10 @@ source include/master-slave.inc; # it back to get the non-transactional change into the table. --echo **** On Master **** -CREATE TABLE t1 (a INT, b SET('master','slave')) ENGINE=INNODB; -CREATE TABLE t2 (a INT, b SET('master','slave')) ENGINE=MYISAM; +CREATE TABLE t1 (a INT, b SET('master','slave')) ENGINE=InnoDB; +CREATE TABLE t2 (a INT, b SET('master','slave')) ENGINE=MyISAM; +CREATE TABLE t3 (a CHAR(20), b SET('master','slave')) ENGINE=InnoDB; +CREATE TABLE t4 (a CHAR(20), b SET('master','slave')) ENGINE=MyISAM; --echo ==== Skipping normal transactions ==== @@ -195,9 +197,170 @@ sync_with_master; SELECT * FROM t1 ORDER BY a; SELECT * FROM t2 ORDER BY a; +--echo ==== Skipping first event of a LOAD DATA for a transactional table ==== + +--echo **** On Slave **** +connection slave; +STOP SLAVE; +source include/wait_for_slave_to_stop.inc; + +--echo **** On Master **** +connection master; +SET AUTOCOMMIT=1; + +LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE t3(a) SET b = 'master'; +INSERT INTO t3 VALUES ('Go Rin No Sho', 'master,slave'); + +save_master_pos; + +SELECT COUNT(*) FROM t3; + +# This will skip a begin event and the first INSERT of the +# transaction, and it should keep skipping until it has reached the +# transaction terminator. + +--echo **** On Slave **** +connection slave; +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +source include/wait_for_slave_to_start.inc; +sync_with_master; +--echo -- Should only contain records marked 'master,slave' +SELECT * FROM t3 ORDER BY a; + +--echo **** On Master **** +connection master; +DELETE FROM t3; +sync_slave_with_master; + +--echo ==== Skipping first event of a LOAD DATA for a non-transactional table ==== + +--echo **** On Slave **** +connection slave; +STOP SLAVE; +source include/wait_for_slave_to_stop.inc; + +--echo **** On Master **** +connection master; +SET AUTOCOMMIT=1; + +LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE t4(a) SET b = 'master'; +INSERT INTO t4 VALUES ('Go Rin No Sho', 'master,slave'); + +save_master_pos; + +SELECT COUNT(*) FROM t4; + +# This will skip a begin event and the first INSERT of the +# transaction, and it should keep skipping until it has reached the +# transaction terminator. + +--echo **** On Slave **** +connection slave; +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +source include/wait_for_slave_to_start.inc; +sync_with_master; +--echo -- Should only contain records marked 'master,slave' +SELECT * FROM t4 ORDER BY a; + +--echo **** On Master **** +connection master; +DELETE FROM t4; +sync_slave_with_master; + +--echo ==== Try with a big file so that we get an append_block event as well + +--echo **** On Slave **** +connection slave; +STOP SLAVE; +source include/wait_for_slave_to_stop.inc; + +--echo **** On Master **** +connection master; +SET AUTOCOMMIT=1; + +# This contain about 70 words, so we double it a few times to get more than 128 KiB +SET SQL_LOG_BIN=0; +LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE t4(a) SET b = 'master'; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +INSERT INTO t4 SELECT * FROM t4; +SELECT a FROM t4 INTO OUTFILE 'rpl_slave_skip_words.dat'; +SET SQL_LOG_BIN=1; + +# Start the real job +LOAD DATA INFILE 'rpl_slave_skip_words.dat' INTO TABLE t4(a) SET b = 'master'; +INSERT INTO t4 VALUES ('Go Rin No Sho', 'master,slave'); + +#SHOW BINLOG EVENTS; + +save_master_pos; + +SELECT COUNT(*) FROM t4; + +# This will skip a begin event and the first INSERT of the +# transaction, and it should keep skipping until it has reached the +# transaction terminator. + +--echo **** On Slave **** +connection slave; +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +source include/wait_for_slave_to_start.inc; +sync_with_master; +--echo -- Should only contain records marked 'master,slave' +SELECT * FROM t4 ORDER BY a; + +--echo **** On Master **** +connection master; +DELETE FROM t4; +sync_slave_with_master; + +# Test to generate a Delete_file log event, and see that it works as well. +--echo **** On Master **** +connection master; +CREATE TABLE t5 (a int, b int, c SET('master','slave'), PRIMARY KEY (a,b)) ENGINE=MyISAM; +LOAD DATA INFILE '../std_data_ln/loaddata5.dat' INTO TABLE t5 FIELDS TERMINATED BY '' ENCLOSED BY '' (a,b) SET c='master,slave'; + +--echo **** On Slave **** +sync_slave_with_master; +STOP SLAVE; +source include/wait_for_slave_to_stop.inc; + +--echo **** On Master **** +connection master; +error ER_DUP_ENTRY; +LOAD DATA INFILE '../std_data_ln/loaddata5.dat' INTO TABLE t5 FIELDS TERMINATED BY '' ENCLOSED BY '' (a,b) SET c=''; +INSERT INTO t5 VALUES (42, 42, 'master,slave'); +save_master_pos; + +#SHOW BINLOG EVENTS; + +SELECT * FROM t5; + +--echo **** On Slave **** +connection slave; +SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1; +START SLAVE; +source include/wait_for_slave_to_start.inc; + +sync_with_master; +SELECT * FROM t5; + +connection slave; + --echo ==== Cleanup ==== --echo **** On Master **** connection master; -DROP TABLE t1, t2; +DROP TABLE t1, t2, t3, t4, t5; sync_slave_with_master; diff --git a/sql/slave.cc b/sql/slave.cc index 1509916fe91..4a65e9aaa85 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3348,7 +3348,10 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) if (rli->slave_skip_counter && !((type_code == INTVAR_EVENT || type_code == RAND_EVENT || - type_code == USER_VAR_EVENT) && + type_code == USER_VAR_EVENT || + type_code == BEGIN_LOAD_QUERY_EVENT || + type_code == APPEND_BLOCK_EVENT || + type_code == CREATE_FILE_EVENT) && rli->slave_skip_counter == 1) && #if MYSQL_VERSION_ID < 50100 /*