# This is a BitKeeper generated diff -Nru style patch.
#
# ChangeSet
#   2007/12/20 16:07:54+01:00 mats@capulet.kindahl.net 
#   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
#   2007/12/20 16:07:51+01:00 mats@capulet.kindahl.net +101 -3
#   Result change.
# 
# mysql-test/t/rpl_slave_skip.test
#   2007/12/20 16:07:51+01:00 mats@capulet.kindahl.net +166 -3
#   Adding tests to test that the first event of a LOAD DATA INFILE
#   can be skipped safely for both transactional and non-transactional
#   tables. Also include a case that will generate a DELETE_FILE event
#   last in the group, and this should be properly skipped as well.
# 
# sql/slave.cc
#   2007/12/20 16:07:52+01:00 mats@capulet.kindahl.net +4 -1
#   Not decrementing slave skip counter to zero when seeing a
#   BEGIN_LOAD_QUERY_EVENT, APPEND_BLOCK_EVENT, or CREATE_FILE_EVENT
#   since these cannot end a group.
# 
diff -Nru a/mysql-test/r/rpl_slave_skip.result b/mysql-test/r/rpl_slave_skip.result
--- a/mysql-test/r/rpl_slave_skip.result	2007-12-20 16:08:11 +01:00
+++ b/mysql-test/r/rpl_slave_skip.result	2007-12-20 16:08:11 +01:00
@@ -5,8 +5,10 @@
 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 @@
 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 -Nru a/mysql-test/t/rpl_slave_skip.test b/mysql-test/t/rpl_slave_skip.test
--- a/mysql-test/t/rpl_slave_skip.test	2007-12-20 16:08:11 +01:00
+++ b/mysql-test/t/rpl_slave_skip.test	2007-12-20 16:08:11 +01:00
@@ -13,8 +13,10 @@
 # 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 @@
 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 -Nru a/sql/slave.cc b/sql/slave.cc
--- a/sql/slave.cc	2007-12-20 16:08:11 +01:00
+++ b/sql/slave.cc	2007-12-20 16:08:11 +01:00
@@ -3348,7 +3348,10 @@
       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
           /*


