From 4c5d18b1505fe4fbbfd51268df0784d9c3faf983 Mon Sep 17 00:00:00 2001 From: "guilhem@mysql.com" <> Date: Sat, 18 Feb 2006 17:19:16 +0100 Subject: [PATCH] Fix for BUG#16559 "Replication Problems with Non transactional tables inside an interrupted trans.": problem was: when a connection disconnects having an open transaction affecting MyISAM and InnoDB, the ROLLBACK event stored in the binary log contained a non-zero error code (1053 because of the disconnection), so when slave applied the transaction, slave complained that its ROLLBACK succeeded (error_code=0) while master's had 1053, so slave stopped. But internally generated binlog events such as this ROLLBACK should always have 0 as error code, as is true in 4.1 and was accidentally broken in 5.0, so that there is no false alarm. --- mysql-test/r/mix_innodb_myisam_binlog.result | 23 ++++++++++++++ mysql-test/t/mix_innodb_myisam_binlog.test | 33 +++++++++++++++++++- sql/log.cc | 6 +++- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/mix_innodb_myisam_binlog.result b/mysql-test/r/mix_innodb_myisam_binlog.result index c84bd65e748..2273b8c8756 100644 --- a/mysql-test/r/mix_innodb_myisam_binlog.result +++ b/mysql-test/r/mix_innodb_myisam_binlog.result @@ -256,3 +256,26 @@ master-bin.000001 1648 Query 1 # use `test`; create table t2 (n int) engine=inno master-bin.000001 1748 Query 1 # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `test`.`t1`,`test`.`ti` do release_lock("lock1"); drop table t0,t2; +reset master; +create table t1 (a int) engine=innodb; +create table t2 (a int) engine=myisam; +select get_lock("a",10); +get_lock("a",10) +1 +begin; +insert into t1 values(8); +insert into t2 select * from t1; +select get_lock("a",10); +get_lock("a",10) +1 +select +(@a:=load_file("MYSQL_TEST_DIR/var/tmp/mix_innodb_myisam_binlog.output")) +is not null; +(@a:=load_file("MYSQL_TEST_DIR/var/tmp/mix_innodb_myisam_binlog.output")) +is not null +1 +select +@a like "%#%error_code=0%ROLLBACK;%ROLLBACK /* added by mysqlbinlog */;%", +@a not like "%#%error_code=%error_code=%"; +@a like "%#%error_code=0%ROLLBACK;%ROLLBACK /* added by mysqlbinlog */;%" @a not like "%#%error_code=%error_code=%" +1 1 diff --git a/mysql-test/t/mix_innodb_myisam_binlog.test b/mysql-test/t/mix_innodb_myisam_binlog.test index 658584b625e..7ba7b634b22 100644 --- a/mysql-test/t/mix_innodb_myisam_binlog.test +++ b/mysql-test/t/mix_innodb_myisam_binlog.test @@ -259,5 +259,36 @@ show binlog events from 98; do release_lock("lock1"); drop table t0,t2; - # End of 4.1 tests + +# Test for BUG#16559 (ROLLBACK should always have a zero error code in +# binlog). Has to be here and not earlier, as the SELECTs influence +# XIDs differently between normal and ps-protocol (and SHOW BINLOG +# EVENTS above read XIDs). + +connect (con4,localhost,root,,); +connection con3; +reset master; +create table t1 (a int) engine=innodb; +create table t2 (a int) engine=myisam; +select get_lock("a",10); +begin; +insert into t1 values(8); +insert into t2 select * from t1; +disconnect con3; + +connection con4; +select get_lock("a",10); # wait for rollback to finish + +# we check that the error code of the "ROLLBACK" event is 0 and not +# ER_SERVER_SHUTDOWN (i.e. disconnection just rolls back transaction +# and does not make slave to stop) +--exec $MYSQL_BINLOG --start-position=547 $MYSQL_TEST_DIR/var/log/master-bin.000001 > var/tmp/mix_innodb_myisam_binlog.output +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +eval select +(@a:=load_file("$MYSQL_TEST_DIR/var/tmp/mix_innodb_myisam_binlog.output")) +is not null; +--replace_result $MYSQL_TEST_DIR MYSQL_TEST_DIR +eval select +@a like "%#%error_code=0%ROLLBACK;%ROLLBACK /* added by mysqlbinlog */;%", +@a not like "%#%error_code=%error_code=%"; diff --git a/sql/log.cc b/sql/log.cc index 6c37cb04c61..85e8c4dae2f 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -132,6 +132,7 @@ static int binlog_commit(THD *thd, bool all) DBUG_RETURN(0); } Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE); + qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE) DBUG_RETURN(binlog_end_trans(thd, trans_log, &qev)); } @@ -156,6 +157,7 @@ static int binlog_rollback(THD *thd, bool all) if (unlikely(thd->options & OPTION_STATUS_NO_TRANS_UPDATE)) { Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE); + qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE) error= binlog_end_trans(thd, trans_log, &qev); } else @@ -1826,7 +1828,9 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event) Imagine this is rollback due to net timeout, after all statements of the transaction succeeded. Then we want a zero-error code in BEGIN. In other words, if there was a really serious error code it's already - in the statement's events. + in the statement's events, there is no need to put it also in this + internally generated event, and as this event is generated late it + would lead to false alarms. This is safer than thd->clear_error() against kills at shutdown. */ qinfo.error_code= 0;