From 5dd1abae7d2f9e56d1c3f54077057bd92387a600 Mon Sep 17 00:00:00 2001 From: He Zhenxing Date: Sun, 31 May 2009 11:26:58 +0800 Subject: [PATCH] BUG#43263 BEGIN skipped in some replicate-do-db cases BEGIN/COMMIT/ROLLBACK was subject to replication db rules, and caused the boundary of a transaction not recognized correctly when these queries were ignored by the rules. Fixed the problem by skipping replication db rules for these statements. sql/log_event.cc: Skip checking replication db rules for BEGIN/COMMIT/ROLLBACK statements --- mysql-test/r/binlog.result | 10 +- mysql-test/r/mix_innodb_myisam_binlog.result | 46 +++---- mysql-test/r/rpl_begin_commit_rollback.result | 109 +++++++++++++++ .../t/rpl_begin_commit_rollback-slave.opt | 1 + mysql-test/t/rpl_begin_commit_rollback.test | 124 ++++++++++++++++++ sql/log.cc | 6 +- sql/log_event.cc | 5 +- 7 files changed, 269 insertions(+), 32 deletions(-) create mode 100644 mysql-test/r/rpl_begin_commit_rollback.result create mode 100644 mysql-test/t/rpl_begin_commit_rollback-slave.opt create mode 100644 mysql-test/t/rpl_begin_commit_rollback.test diff --git a/mysql-test/r/binlog.result b/mysql-test/r/binlog.result index baab30ebbdd..c885c4ce130 100644 --- a/mysql-test/r/binlog.result +++ b/mysql-test/r/binlog.result @@ -12,10 +12,10 @@ show binlog events from 98; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # use `test`; create table t1 (a int) engine=innodb master-bin.000001 # Query 1 # use `test`; create table t2 (a int) engine=innodb -master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # BEGIN master-bin.000001 # Query 1 # use `test`; insert t1 values (5) master-bin.000001 # Xid 1 # COMMIT /* XID */ -master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # BEGIN master-bin.000001 # Query 1 # use `test`; insert t2 values (5) master-bin.000001 # Xid 1 # COMMIT /* XID */ drop table t1,t2; @@ -27,7 +27,7 @@ drop table t1; show binlog events in 'master-bin.000001' from 98; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # use `test`; create table t1 (n int) engine=innodb -master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # BEGIN master-bin.000001 # Query 1 # use `test`; insert into t1 values(100 + 4) master-bin.000001 # Query 1 # use `test`; insert into t1 values(99 + 4) master-bin.000001 # Query 1 # use `test`; insert into t1 values(98 + 4) @@ -147,7 +147,7 @@ show binlog events from 0; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 4 Format_desc 1 98 Server version, Binlog ver: 4 master-bin.000001 98 Query 1 197 use `test`; create table t1(n int) engine=innodb -master-bin.000001 197 Query 1 265 use `test`; BEGIN +master-bin.000001 197 Query 1 265 BEGIN master-bin.000001 265 Query 1 353 use `test`; insert into t1 values (1) master-bin.000001 353 Query 1 441 use `test`; insert into t1 values (2) master-bin.000001 441 Query 1 529 use `test`; insert into t1 values (3) @@ -161,7 +161,7 @@ show binlog events from 0; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 4 Format_desc 1 98 Server version, Binlog ver: 4 master-bin.000001 98 Query 1 198 use `test`; create table t1 (a int) engine=innodb -master-bin.000001 198 Query 1 266 use `test`; BEGIN +master-bin.000001 198 Query 1 266 BEGIN master-bin.000001 266 Query 1 357 use `test`; insert into t1 values( 400 ) master-bin.000001 357 Query 1 448 use `test`; insert into t1 values( 399 ) master-bin.000001 448 Query 1 539 use `test`; insert into t1 values( 398 ) diff --git a/mysql-test/r/mix_innodb_myisam_binlog.result b/mysql-test/r/mix_innodb_myisam_binlog.result index eda3ff41c89..fa994363287 100644 --- a/mysql-test/r/mix_innodb_myisam_binlog.result +++ b/mysql-test/r/mix_innodb_myisam_binlog.result @@ -8,7 +8,7 @@ insert into t2 select * from t1; commit; show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values(1) master-bin.000001 # Query # # use `test`; insert into t2 select * from t1 master-bin.000001 # Xid # # COMMIT /* XID */ @@ -23,10 +23,10 @@ Warnings: Warning 1196 Some non-transactional changed tables couldn't be rolled back show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values(2) master-bin.000001 # Query # # use `test`; insert into t2 select * from t1 -master-bin.000001 # Query # # use `test`; ROLLBACK +master-bin.000001 # Query # # ROLLBACK delete from t1; delete from t2; reset master; @@ -41,7 +41,7 @@ Warning 1196 Some non-transactional changed tables couldn't be rolled back commit; show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values(3) master-bin.000001 # Query # # use `test`; savepoint my_savepoint master-bin.000001 # Query # # use `test`; insert into t1 values(4) @@ -67,7 +67,7 @@ a 7 show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values(5) master-bin.000001 # Query # # use `test`; savepoint my_savepoint master-bin.000001 # Query # # use `test`; insert into t1 values(6) @@ -89,10 +89,10 @@ get_lock("a",10) 1 show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values(8) master-bin.000001 # Query # # use `test`; insert into t2 select * from t1 -master-bin.000001 # Query # # use `test`; ROLLBACK +master-bin.000001 # Query # # ROLLBACK delete from t1; delete from t2; reset master; @@ -100,7 +100,7 @@ insert into t1 values(9); insert into t2 select * from t1; show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values(9) master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; insert into t2 select * from t1 @@ -112,7 +112,7 @@ begin; insert into t2 select * from t1; show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values(10) master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; insert into t2 select * from t1 @@ -120,11 +120,11 @@ insert into t1 values(11); commit; show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values(10) master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; insert into t2 select * from t1 -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values(11) master-bin.000001 # Xid # # COMMIT /* XID */ alter table t2 engine=INNODB; @@ -137,7 +137,7 @@ insert into t2 select * from t1; commit; show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values(12) master-bin.000001 # Query # # use `test`; insert into t2 select * from t1 master-bin.000001 # Xid # # COMMIT /* XID */ @@ -162,7 +162,7 @@ rollback to savepoint my_savepoint; commit; show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values(14) master-bin.000001 # Xid # # COMMIT /* XID */ delete from t1; @@ -182,7 +182,7 @@ a 18 show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values(16) master-bin.000001 # Query # # use `test`; insert into t1 values(18) master-bin.000001 # Xid # # COMMIT /* XID */ @@ -234,24 +234,24 @@ get_lock("lock1",60) 1 show binlog events from ; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values(16) master-bin.000001 # Query # # use `test`; insert into t1 values(18) master-bin.000001 # Xid # # COMMIT /* XID */ -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; delete from t1 master-bin.000001 # Xid # # COMMIT /* XID */ -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; delete from t2 master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; alter table t2 type=MyISAM -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into t1 values (1) master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; insert into t2 values (20) master-bin.000001 # Query # # use `test`; drop table t1,t2 master-bin.000001 # Query # # use `test`; create temporary table ti (a int) engine=innodb -master-bin.000001 # Query # # use `test`; BEGIN +master-bin.000001 # Query # # BEGIN master-bin.000001 # Query # # use `test`; insert into ti values(1) master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Query # # use `test`; create temporary table t1 (a int) engine=myisam @@ -310,11 +310,11 @@ File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 507 show binlog events from 98; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # BEGIN master-bin.000001 # Query 1 # use `test`; insert into ti values (1) master-bin.000001 # Query 1 # use `test`; insert into ti values (2) master-bin.000001 # Query 1 # use `test`; insert into tt select * from ti -master-bin.000001 # Query 1 # use `test`; ROLLBACK +master-bin.000001 # Query 1 # ROLLBACK select count(*) from ti /* zero */; count(*) 0 @@ -342,11 +342,11 @@ File Position Binlog_Do_DB Binlog_Ignore_DB master-bin.000001 581 show binlog events from 98; Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Query 1 # use `test`; BEGIN +master-bin.000001 # Query 1 # BEGIN master-bin.000001 # Query 1 # use `test`; insert into ti values (1) master-bin.000001 # Query 1 # use `test`; insert into ti values (2) /* to make the dup error in the following */ master-bin.000001 # Query 1 # use `test`; insert into tt select * from ti /* one affected and error */ -master-bin.000001 # Query 1 # use `test`; ROLLBACK +master-bin.000001 # Query 1 # ROLLBACK select count(*) from ti /* zero */; count(*) 0 diff --git a/mysql-test/r/rpl_begin_commit_rollback.result b/mysql-test/r/rpl_begin_commit_rollback.result new file mode 100644 index 00000000000..23736804784 --- /dev/null +++ b/mysql-test/r/rpl_begin_commit_rollback.result @@ -0,0 +1,109 @@ +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 DATABASE IF EXISTS db1; +CREATE DATABASE db1; +use db1; +CREATE TABLE db1.t1 (a INT) ENGINE=InnoDB; +CREATE TABLE db1.t2 (s CHAR(255)) ENGINE=MyISAM; +include/stop_slave.inc +[on master] +CREATE PROCEDURE db1.p1 () +BEGIN +INSERT INTO t1 VALUES (1); +INSERT INTO t1 VALUES (2); +INSERT INTO t1 VALUES (3); +INSERT INTO t1 VALUES (4); +INSERT INTO t1 VALUES (5); +END// +CREATE PROCEDURE db1.p2 () +BEGIN +INSERT INTO t1 VALUES (6); +INSERT INTO t1 VALUES (7); +INSERT INTO t1 VALUES (8); +INSERT INTO t1 VALUES (9); +INSERT INTO t1 VALUES (10); +INSERT INTO t2 VALUES ('executed db1.p2()'); +END// +INSERT INTO db1.t2 VALUES ('before call db1.p1()'); +use test; +BEGIN; +CALL db1.p1(); +COMMIT; +INSERT INTO db1.t2 VALUES ('after call db1.p1()'); +SELECT * FROM db1.t1; +a +1 +2 +3 +4 +5 +SELECT * FROM db1.t2; +s +before call db1.p1() +after call db1.p1() +[on slave] +start slave until master_log_file='master-bin.000001', master_log_pos=MASTER_POS; +# +# If we got non-zero here, then we're suffering BUG#43263 +# +SELECT 0 as 'Must be 0'; +Must be 0 +0 +SELECT * from db1.t1; +a +1 +2 +3 +4 +5 +SELECT * from db1.t2; +s +before call db1.p1() +[on master] +INSERT INTO db1.t2 VALUES ('before call db1.p2()'); +BEGIN; +CALL db1.p2(); +ROLLBACK; +INSERT INTO db1.t2 VALUES ('after call db1.p2()'); +SELECT * FROM db1.t1; +a +1 +2 +3 +4 +5 +SELECT * FROM db1.t2; +s +before call db1.p1() +after call db1.p1() +before call db1.p2() +executed db1.p2() +after call db1.p2() +[on slave] +start slave until master_log_file='master-bin.000001', master_log_pos=MASTER_POS; +# +# If we got non-zero here, then we're suffering BUG#43263 +# +SELECT 0 as 'Must be 0'; +Must be 0 +0 +SELECT * from db1.t1; +a +1 +2 +3 +4 +5 +SELECT * from db1.t2; +s +before call db1.p1() +executed db1.p2() +# +# Clean up +# +DROP DATABASE db1; +DROP DATABASE db1; diff --git a/mysql-test/t/rpl_begin_commit_rollback-slave.opt b/mysql-test/t/rpl_begin_commit_rollback-slave.opt new file mode 100644 index 00000000000..b4abda5893f --- /dev/null +++ b/mysql-test/t/rpl_begin_commit_rollback-slave.opt @@ -0,0 +1 @@ +--innodb --replicate-do-db=db1 diff --git a/mysql-test/t/rpl_begin_commit_rollback.test b/mysql-test/t/rpl_begin_commit_rollback.test new file mode 100644 index 00000000000..060cf3c21fc --- /dev/null +++ b/mysql-test/t/rpl_begin_commit_rollback.test @@ -0,0 +1,124 @@ +source include/master-slave.inc; +source include/have_innodb.inc; + +disable_warnings; +DROP DATABASE IF EXISTS db1; +enable_warnings; + +CREATE DATABASE db1; + +use db1; + +CREATE TABLE db1.t1 (a INT) ENGINE=InnoDB; +CREATE TABLE db1.t2 (s CHAR(255)) ENGINE=MyISAM; + +sync_slave_with_master; +source include/stop_slave.inc; +connection master; +echo [on master]; + +DELIMITER //; +CREATE PROCEDURE db1.p1 () +BEGIN + INSERT INTO t1 VALUES (1); + INSERT INTO t1 VALUES (2); + INSERT INTO t1 VALUES (3); + INSERT INTO t1 VALUES (4); + INSERT INTO t1 VALUES (5); +END// + +CREATE PROCEDURE db1.p2 () +BEGIN + INSERT INTO t1 VALUES (6); + INSERT INTO t1 VALUES (7); + INSERT INTO t1 VALUES (8); + INSERT INTO t1 VALUES (9); + INSERT INTO t1 VALUES (10); + INSERT INTO t2 VALUES ('executed db1.p2()'); +END// +DELIMITER ;// + +INSERT INTO db1.t2 VALUES ('before call db1.p1()'); + +# Note: the master_log_pos is set to be the position of the BEGIN + 1, +# so before fix of BUG#43263 if the BEGIN is ignored, then all the +# INSERTS in p1 will be replicated in AUTOCOMMIT=1 mode and the slave +# SQL thread will stop right before the first INSERT. After fix of +# BUG#43263, BEGIN will not be ignored by the replication db rules, +# and then the whole transaction will be executed before slave SQL +# stop. +let $master_pos= query_get_value(SHOW MASTER STATUS, Position, 1); +let $master_pos= `SELECT $master_pos + 1`; + +use test; +BEGIN; +CALL db1.p1(); +COMMIT; + +# The position where the following START SLAVE UNTIL will stop at +let $master_end_trans_pos= query_get_value(SHOW MASTER STATUS, Position, 1); + +INSERT INTO db1.t2 VALUES ('after call db1.p1()'); +SELECT * FROM db1.t1; +SELECT * FROM db1.t2; + +connection slave; +echo [on slave]; + +replace_result $master_pos MASTER_POS; +eval start slave until master_log_file='master-bin.000001', master_log_pos=$master_pos; +source include/wait_for_slave_sql_to_stop.inc; +let $slave_sql_stop_pos= query_get_value(SHOW SLAVE STATUS, Exec_Master_Log_Pos, 1); +let $result= query_get_value(SELECT $slave_sql_stop_pos - $master_end_trans_pos as result, result, 1); + +--echo # +--echo # If we got non-zero here, then we're suffering BUG#43263 +--echo # +eval SELECT $result as 'Must be 0'; +SELECT * from db1.t1; +SELECT * from db1.t2; + +connection master; +echo [on master]; + +INSERT INTO db1.t2 VALUES ('before call db1.p2()'); + +# See comments above. +let $master_pos= query_get_value(SHOW MASTER STATUS, Position, 1); +let $master_pos= `SELECT $master_pos + 1`; + +BEGIN; +CALL db1.p2(); +disable_warnings; +ROLLBACK; +enable_warnings; +let $master_end_trans_pos= query_get_value(SHOW MASTER STATUS, Position, 1); + +INSERT INTO db1.t2 VALUES ('after call db1.p2()'); +SELECT * FROM db1.t1; +SELECT * FROM db1.t2; + +connection slave; +echo [on slave]; + +replace_result $master_pos MASTER_POS; +eval start slave until master_log_file='master-bin.000001', master_log_pos=$master_pos; +source include/wait_for_slave_sql_to_stop.inc; + +let $slave_sql_stop_pos= query_get_value(SHOW SLAVE STATUS, Exec_Master_Log_Pos, 1); +let $result= query_get_value(SELECT $slave_sql_stop_pos - $master_end_trans_pos as result, result, 1); + +--echo # +--echo # If we got non-zero here, then we're suffering BUG#43263 +--echo # +eval SELECT $result as 'Must be 0'; +SELECT * from db1.t1; +SELECT * from db1.t2; + +--echo # +--echo # Clean up +--echo # +connection master; +DROP DATABASE db1; +connection slave; +DROP DATABASE db1; diff --git a/sql/log.cc b/sql/log.cc index b16303ee232..ec8f5fe820b 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -159,7 +159,7 @@ static int binlog_commit(THD *thd, bool all) if (all || !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) { Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), - TRUE, FALSE, THD::KILLED_NO_VALUE); + TRUE, TRUE, THD::KILLED_NO_VALUE); qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE) DBUG_RETURN(binlog_end_trans(thd, trans_log, &qev)); } @@ -204,7 +204,7 @@ static int binlog_rollback(THD *thd, bool all) if (unlikely(thd->transaction.all.modified_non_trans_table)) { Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), - TRUE, FALSE, THD::KILLED_NO_VALUE); + TRUE, TRUE, THD::KILLED_NO_VALUE); qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE) error= binlog_end_trans(thd, trans_log, &qev); } @@ -2094,7 +2094,7 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) statement in autocommit mode. */ Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), - TRUE, FALSE, THD::KILLED_NO_VALUE); + TRUE, TRUE, THD::KILLED_NO_VALUE); /* Imagine this is rollback due to net timeout, after all statements of the transaction succeeded. Then we want a diff --git a/sql/log_event.cc b/sql/log_event.cc index 3912308cafa..a440f010fa5 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1953,7 +1953,10 @@ int Query_log_event::exec_event(struct st_relay_log_info* rli, ::exec_event(), then the companion SET also have so we don't need to reset_one_shot_variables(). */ - if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) + if (!strncmp(query_arg, "BEGIN", q_len_arg) || + !strncmp(query_arg, "COMMIT", q_len_arg) || + !strncmp(query_arg, "ROLLBACK", q_len_arg) || + db_ok(thd->db, replicate_do_db, replicate_ignore_db)) { thd->set_time((time_t)when); thd->query_length= q_len_arg;