Below is the list of changes that have just been committed into a local
5.0 repository of sven. When sven does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-12-21 20:30:23+01:00, sven@riska.(none) +10 -0
BUG#26395: if crash during autocommit update to transactional table on master, slave fails
Now, every transaction (including autocommit transactions) start with
a BEGIN and end with a COMMIT/ROLLBACK in the binlog.
Added a test case, and updated lots of test case result files.
mysql-test/r/mix_innodb_myisam_binlog.result@stripped, 2007-12-21 20:30:22+01:00, sven@riska.(none) +44 -37
Updated result file
mysql-test/r/multi_update.result@stripped, 2007-12-21 20:30:22+01:00, sven@riska.(none) +2 -2
Updated result file
mysql-test/r/rpl_transaction.result@stripped, 2007-12-21 20:30:22+01:00, sven@riska.(none) +4 -4
New result file for new test case.
mysql-test/r/rpl_transaction.result@stripped, 2007-12-21 20:07:12+01:00, sven@riska.(none) +95 -0
BitKeeper file /home/sven/bk/b26395-autocommit-xa/5.0-rpl/mysql-test/r/rpl_transaction.result
mysql-test/r/rpl_transaction.result@stripped, 2007-12-21 20:07:12+01:00, sven@riska.(none) +0 -0
mysql-test/r/sp_trans_log.result@stripped, 2007-12-21 20:30:22+01:00, sven@riska.(none) +1 -0
Updated result file
mysql-test/r/variables-big.result@stripped, 2007-12-21 20:30:22+01:00, sven@riska.(none) +9 -5
Updated result file
mysql-test/t/rpl_transaction-master.opt@stripped, 2007-12-21 20:25:03+01:00, sven@riska.(none) +1 -0
BitKeeper file /home/sven/bk/b26395-autocommit-xa/5.0-rpl/mysql-test/t/rpl_transaction-master.opt
mysql-test/t/rpl_transaction-master.opt@stripped, 2007-12-21 20:25:03+01:00, sven@riska.(none) +0 -0
mysql-test/t/rpl_transaction-slave.opt@stripped, 2007-12-21 20:25:03+01:00, sven@riska.(none) +1 -0
BitKeeper file /home/sven/bk/b26395-autocommit-xa/5.0-rpl/mysql-test/t/rpl_transaction-slave.opt
mysql-test/t/rpl_transaction-slave.opt@stripped, 2007-12-21 20:25:03+01:00, sven@riska.(none) +0 -0
mysql-test/t/rpl_transaction.test@stripped, 2007-12-21 20:30:22+01:00, sven@riska.(none) +3 -1
New test case.
mysql-test/t/rpl_transaction.test@stripped, 2007-12-21 20:07:12+01:00, sven@riska.(none) +104 -0
BitKeeper file /home/sven/bk/b26395-autocommit-xa/5.0-rpl/mysql-test/t/rpl_transaction.test
mysql-test/t/rpl_transaction.test@stripped, 2007-12-21 20:07:12+01:00, sven@riska.(none) +0 -0
sql/log.cc@stripped, 2007-12-21 20:30:22+01:00, sven@riska.(none) +71 -29
- Always write BEGIN and COMMIT around statements, even in autocommit
mode.
- Added comments for binlog_commit and binlog_rollback.
sql/log_event.cc@stripped, 2007-12-21 20:30:22+01:00, sven@riska.(none) +1 -0
Added debug trigger to avoid writing xid events to the binlog.
diff -Nrup a/mysql-test/r/mix_innodb_myisam_binlog.result b/mysql-test/r/mix_innodb_myisam_binlog.result
--- a/mysql-test/r/mix_innodb_myisam_binlog.result 2007-12-15 12:50:21 +01:00
+++ b/mysql-test/r/mix_innodb_myisam_binlog.result 2007-12-21 20:30:22 +01:00
@@ -100,9 +100,10 @@ insert into t1 values(9);
insert into t2 select * from t1;
show binlog events from 98;
Log_name Pos Event_type Server_id End_log_pos Info
-master-bin.000001 98 Query 1 # use `test`; insert into t1 values(9)
-master-bin.000001 185 Xid 1 # COMMIT /* XID */
-master-bin.000001 212 Query 1 # use `test`; insert into t2 select * from t1
+master-bin.000001 98 Query 1 # use `test`; BEGIN
+master-bin.000001 166 Query 1 # use `test`; insert into t1 values(9)
+master-bin.000001 253 Xid 1 # COMMIT /* XID */
+master-bin.000001 280 Query 1 # use `test`; insert into t2 select * from t1
delete from t1;
delete from t2;
reset master;
@@ -111,19 +112,21 @@ begin;
insert into t2 select * from t1;
show binlog events from 98;
Log_name Pos Event_type Server_id End_log_pos Info
-master-bin.000001 98 Query 1 # use `test`; insert into t1 values(10)
-master-bin.000001 186 Xid 1 # COMMIT /* XID */
-master-bin.000001 213 Query 1 # use `test`; insert into t2 select * from t1
+master-bin.000001 98 Query 1 # use `test`; BEGIN
+master-bin.000001 166 Query 1 # use `test`; insert into t1 values(10)
+master-bin.000001 254 Xid 1 # COMMIT /* XID */
+master-bin.000001 281 Query 1 # use `test`; insert into t2 select * from t1
insert into t1 values(11);
commit;
show binlog events from 98;
Log_name Pos Event_type Server_id End_log_pos Info
-master-bin.000001 98 Query 1 # use `test`; insert into t1 values(10)
-master-bin.000001 186 Xid 1 # COMMIT /* XID */
-master-bin.000001 213 Query 1 # use `test`; insert into t2 select * from t1
-master-bin.000001 307 Query 1 # use `test`; BEGIN
-master-bin.000001 375 Query 1 # use `test`; insert into t1 values(11)
-master-bin.000001 463 Xid 1 # COMMIT /* XID */
+master-bin.000001 98 Query 1 # use `test`; BEGIN
+master-bin.000001 166 Query 1 # use `test`; insert into t1 values(10)
+master-bin.000001 254 Xid 1 # COMMIT /* XID */
+master-bin.000001 281 Query 1 # use `test`; insert into t2 select * from t1
+master-bin.000001 375 Query 1 # use `test`; BEGIN
+master-bin.000001 443 Query 1 # use `test`; insert into t1 values(11)
+master-bin.000001 531 Xid 1 # COMMIT /* XID */
alter table t2 engine=INNODB;
delete from t1;
delete from t2;
@@ -235,25 +238,29 @@ master-bin.000001 98 Query 1 # use `test
master-bin.000001 166 Query 1 # use `test`; insert into t1 values(16)
master-bin.000001 254 Query 1 # use `test`; insert into t1 values(18)
master-bin.000001 342 Xid 1 # COMMIT /* XID */
-master-bin.000001 369 Query 1 # use `test`; delete from t1
-master-bin.000001 446 Xid 1 # COMMIT /* XID */
-master-bin.000001 473 Query 1 # use `test`; delete from t2
-master-bin.000001 550 Xid 1 # COMMIT /* XID */
-master-bin.000001 577 Query 1 # use `test`; alter table t2 type=MyISAM
-master-bin.000001 666 Query 1 # use `test`; insert into t1 values (1)
-master-bin.000001 754 Xid 1 # COMMIT /* XID */
-master-bin.000001 781 Query 1 # use `test`; insert into t2 values (20)
-master-bin.000001 870 Query 1 # use `test`; drop table t1,t2
-master-bin.000001 949 Query 1 # use `test`; create temporary table ti (a int) engine=innodb
-master-bin.000001 1059 Query 1 # use `test`; insert into ti values(1)
-master-bin.000001 1146 Xid 1 # COMMIT /* XID */
-master-bin.000001 1173 Query 1 # use `test`; create temporary table t1 (a int) engine=myisam
-master-bin.000001 1283 Query 1 # use `test`; insert t1 values (1)
-master-bin.000001 1366 Query 1 # use `test`; create table t0 (n int)
-master-bin.000001 1452 Query 1 # use `test`; insert t0 select * from t1
-master-bin.000001 1541 Query 1 # use `test`; insert into t0 select GET_LOCK("lock1",null)
-master-bin.000001 1648 Query 1 # use `test`; create table t2 (n int) engine=innodb
-master-bin.000001 1748 Query 1 # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `test`.`t1`,`test`.`ti`
+master-bin.000001 369 Query 1 # use `test`; BEGIN
+master-bin.000001 437 Query 1 # use `test`; delete from t1
+master-bin.000001 514 Xid 1 # COMMIT /* XID */
+master-bin.000001 541 Query 1 # use `test`; BEGIN
+master-bin.000001 609 Query 1 # use `test`; delete from t2
+master-bin.000001 686 Xid 1 # COMMIT /* XID */
+master-bin.000001 713 Query 1 # use `test`; alter table t2 type=MyISAM
+master-bin.000001 802 Query 1 # use `test`; BEGIN
+master-bin.000001 870 Query 1 # use `test`; insert into t1 values (1)
+master-bin.000001 958 Xid 1 # COMMIT /* XID */
+master-bin.000001 985 Query 1 # use `test`; insert into t2 values (20)
+master-bin.000001 1074 Query 1 # use `test`; drop table t1,t2
+master-bin.000001 1153 Query 1 # use `test`; create temporary table ti (a int) engine=innodb
+master-bin.000001 1263 Query 1 # use `test`; BEGIN
+master-bin.000001 1331 Query 1 # use `test`; insert into ti values(1)
+master-bin.000001 1418 Xid 1 # COMMIT /* XID */
+master-bin.000001 1445 Query 1 # use `test`; create temporary table t1 (a int) engine=myisam
+master-bin.000001 1555 Query 1 # use `test`; insert t1 values (1)
+master-bin.000001 1638 Query 1 # use `test`; create table t0 (n int)
+master-bin.000001 1724 Query 1 # use `test`; insert t0 select * from t1
+master-bin.000001 1813 Query 1 # use `test`; insert into t0 select GET_LOCK("lock1",null)
+master-bin.000001 1920 Query 1 # use `test`; create table t2 (n int) engine=innodb
+master-bin.000001 2020 Query 1 # use `test`; DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `test`.`t1`,`test`.`ti`
do release_lock("lock1");
drop table t0,t2;
reset master;
@@ -402,7 +409,7 @@ insert into t2 values (bug27417(1));
ERROR 23000: Duplicate entry '1' for key 1
show master status /* the offset must denote there is the query */;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000001 267
+master-bin.000001 335
select count(*) from t1 /* must be 1 */;
count(*)
1
@@ -414,7 +421,7 @@ insert into t2 select bug27417(1) union
ERROR 23000: Duplicate entry '2' for key 1
show master status /* the offset must denote there is the query */;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000001 290
+master-bin.000001 358
select count(*) from t1 /* must be 2 */;
count(*)
2
@@ -438,7 +445,7 @@ UPDATE t4,t3 SET t4.a=t3.a + bug27417(1)
ERROR 23000: Duplicate entry '2' for key 1
show master status /* the offset must denote there is the query */;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000001 301
+master-bin.000001 369
select count(*) from t1 /* must be 4 */;
count(*)
4
@@ -466,7 +473,7 @@ delete from t2;
ERROR 23000: Duplicate entry '1' for key 1
show master status /* the offset must denote there is the query */;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000001 246
+master-bin.000001 314
select count(*) from t1 /* must be 1 */;
count(*)
1
@@ -483,7 +490,7 @@ delete t2.* from t2,t5 where t2.a=t5.a +
ERROR 23000: Duplicate entry '1' for key 1
show master status /* the offset must denote there is the query */;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000001 274
+master-bin.000001 342
select count(*) from t1 /* must be 1 */;
count(*)
1
@@ -501,7 +508,7 @@ count(*)
2
show master status /* the offset must denote there is the query */;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000001 376
+master-bin.000001 444
drop trigger trg_del_t2;
drop table t1,t2,t3,t4,t5;
drop function bug27417;
diff -Nrup a/mysql-test/r/multi_update.result b/mysql-test/r/multi_update.result
--- a/mysql-test/r/multi_update.result 2007-10-13 14:49:37 +02:00
+++ b/mysql-test/r/multi_update.result 2007-12-21 20:30:22 +01:00
@@ -545,7 +545,7 @@ a b
4 4
show master status /* there must be the UPDATE query event */;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000001 260
+master-bin.000001 328
delete from t1;
delete from t2;
insert into t1 values (1,2),(3,4),(4,4);
@@ -555,7 +555,7 @@ UPDATE t2,t1 SET t2.a=t2.b where t2.a=t
ERROR 23000: Duplicate entry '4' for key 1
show master status /* there must be the UPDATE query event */;
File Position Binlog_Do_DB Binlog_Ignore_DB
-master-bin.000001 275
+master-bin.000001 343
drop table t1, t2;
drop table if exists t1, t2, t3;
CREATE TABLE t1 (a int, PRIMARY KEY (a));
diff -Nrup a/mysql-test/r/rpl_transaction.result b/mysql-test/r/rpl_transaction.result
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/mysql-test/r/rpl_transaction.result 2007-12-21 20:30:22 +01:00
@@ -0,0 +1,95 @@
+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;
+CREATE TABLE tmyisam (a int) ENGINE = MYISAM;
+CREATE TABLE tinnodb (a int) ENGINE = INNODB;
+SHOW CREATE TABLE tmyisam;
+Table Create Table
+tmyisam CREATE TABLE `tmyisam` (
+ `a` int(11) default NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+SHOW CREATE TABLE tinnodb;
+Table Create Table
+tinnodb CREATE TABLE `tinnodb` (
+ `a` int(11) default NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1
+==== Test 1: Non-XA Engines ====
+--- on master ---
+SET AUTOCOMMIT = 1;
+INSERT INTO tmyisam VALUES (1);
+BEGIN;
+INSERT INTO tmyisam VALUES (2);
+INSERT INTO tmyisam VALUES (3);
+COMMIT;
+BEGIN;
+INSERT INTO tmyisam VALUES (5);
+INSERT INTO tmyisam VALUES (6);
+ROLLBACK;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+SELECT * FROM tmyisam ORDER BY a;
+a
+1
+2
+3
+5
+6
+--- on slave ---
+SELECT * FROM tmyisam ORDER BY a;
+a
+1
+2
+3
+5
+6
+==== Test 2: Master crash before writing XID event on XA engine ====
+--- on master ---
+INSERT INTO tinnodb VALUES (1);
+SELECT * FROM tinnodb ORDER BY a;
+a
+1
+--- on slave ---
+STOP SLAVE;
+SHOW SLAVE STATUS;
+Slave_IO_State
+Master_Host 127.0.0.1
+Master_User root
+Master_Port #
+Connect_Retry 1
+Master_Log_File master-bin.000001
+Read_Master_Log_Pos #
+Relay_Log_File #
+Relay_Log_Pos #
+Relay_Master_Log_File master-bin.000001
+Slave_IO_Running No
+Slave_SQL_Running No
+Replicate_Do_DB
+Replicate_Ignore_DB
+Replicate_Do_Table
+Replicate_Ignore_Table #
+Replicate_Wild_Do_Table
+Replicate_Wild_Ignore_Table
+Last_Errno 0
+Last_Error
+Skip_Counter 0
+Exec_Master_Log_Pos #
+Relay_Log_Space #
+Until_Condition None
+Until_Log_File
+Until_Log_Pos 0
+Master_SSL_Allowed No
+Master_SSL_CA_File
+Master_SSL_CA_Path
+Master_SSL_Cert
+Master_SSL_Cipher
+Master_SSL_Key
+Seconds_Behind_Master #
+SELECT * FROM tinnodb ORDER BY a;
+a
+DROP TABLE tmyisam;
+DROP TABLE tinnodb;
+DROP TABLE tmyisam;
+DROP TABLE tinnodb;
diff -Nrup a/mysql-test/r/sp_trans_log.result b/mysql-test/r/sp_trans_log.result
--- a/mysql-test/r/sp_trans_log.result 2007-08-22 14:43:13 +02:00
+++ b/mysql-test/r/sp_trans_log.result 2007-12-21 20:30:22 +01:00
@@ -16,6 +16,7 @@ show binlog events from 98 /* with fixes
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Query 1 # #
master-bin.000001 # Query 1 # #
+master-bin.000001 # Query 1 # #
select count(*),@a from t1 /* must be 1,1 */|
count(*) @a
1 1
diff -Nrup a/mysql-test/r/variables-big.result b/mysql-test/r/variables-big.result
--- a/mysql-test/r/variables-big.result 2007-04-13 00:03:24 +02:00
+++ b/mysql-test/r/variables-big.result 2007-12-21 20:30:22 +01:00
@@ -1,20 +1,24 @@
set session transaction_prealloc_size=1024*1024*1024*1;
show processlist;
Id User Host db Command Time State Info
-1 root localhost test Query 0 NULL show processlist
+6 root localhost test Query 0 NULL show processlist
set session transaction_prealloc_size=1024*1024*1024*2;
show processlist;
Id User Host db Command Time State Info
-1 root localhost test Query 2 NULL show processlist
+6 root localhost test Query 1 NULL show processlist
set session transaction_prealloc_size=1024*1024*1024*3;
show processlist;
Id User Host db Command Time State Info
-1 root localhost test Query 0 NULL show processlist
+6 root localhost test Query 0 NULL show processlist
set session transaction_prealloc_size=1024*1024*1024*4;
+Warnings:
+Warning 1292 Truncated incorrect transaction_prealloc_size value: '4294967296'
show processlist;
Id User Host db Command Time State Info
-1 root localhost test Query 0 NULL show processlist
+6 root localhost test Query 0 NULL show processlist
set session transaction_prealloc_size=1024*1024*1024*5;
+Warnings:
+Warning 1292 Truncated incorrect transaction_prealloc_size value: '5368709120'
show processlist;
Id User Host db Command Time State Info
-1 root localhost test Query 0 NULL show processlist
+6 root localhost test Query 0 NULL show processlist
diff -Nrup a/mysql-test/t/rpl_transaction-master.opt b/mysql-test/t/rpl_transaction-master.opt
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/rpl_transaction-master.opt 2007-12-21 20:25:03 +01:00
@@ -0,0 +1 @@
+--innodb --debug=d,do_not_write_xid
diff -Nrup a/mysql-test/t/rpl_transaction-slave.opt b/mysql-test/t/rpl_transaction-slave.opt
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/rpl_transaction-slave.opt 2007-12-21 20:25:03 +01:00
@@ -0,0 +1 @@
+--innodb
diff -Nrup a/mysql-test/t/rpl_transaction.test b/mysql-test/t/rpl_transaction.test
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/mysql-test/t/rpl_transaction.test 2007-12-21 20:30:22 +01:00
@@ -0,0 +1,106 @@
+# Tests that transactions are replicated correctly, with various
+# combinations of non-transactional and transactional non-XA tables.
+# Also tests that an XA transaction where the master crashes just
+# before writing the XID log event is executed correctly. See below
+# for implementation details.
+
+# Note: this test should not exist in 5.1 or higher. It has been
+# replaced by rpl_ndb_transaction.test, which tests a superset of what
+# this test tests.
+
+source include/have_innodb.inc;
+source include/master-slave.inc;
+
+
+CREATE TABLE tmyisam (a int) ENGINE = MYISAM;
+CREATE TABLE tinnodb (a int) ENGINE = INNODB;
+
+SHOW CREATE TABLE tmyisam;
+SHOW CREATE TABLE tinnodb;
+
+
+--echo ==== Test 1: Non-XA Engines ====
+# Test that everything works fine with non-XA engines. We just try
+# all ways to do transactions involving ndb and/or myisam, with
+# rollback or commit.
+
+--echo --- on master ---
+
+SET AUTOCOMMIT = 1;
+
+INSERT INTO tmyisam VALUES (1);
+
+BEGIN;
+INSERT INTO tmyisam VALUES (2);
+INSERT INTO tmyisam VALUES (3);
+COMMIT;
+
+BEGIN;
+INSERT INTO tmyisam VALUES (5);
+INSERT INTO tmyisam VALUES (6);
+--warning 1196
+ROLLBACK;
+
+SELECT * FROM tmyisam ORDER BY a;
+
+--echo --- on slave ---
+--sync_slave_with_master
+SELECT * FROM tmyisam ORDER BY a;
+
+
+--echo ==== Test 2: Master crash before writing XID event on XA engine ====
+# We now want to test the following scenario, to verify that BUG#26395
+# has been fixed:
+
+# "master and slave have a transactional table that uses XA. Master
+# has AUTOCOMMIT on and executes a statement (in this case an
+# INSERT). Master crashes just before writing the XID event."
+
+# In this scenario, master will roll back, so slave should not execute
+# the statement, and slave should roll back later when master is
+# restarted.
+
+# However, we the master to be alive so that we are sure it replicates
+# the statement to the slave. So in the test case, we must therefore
+# not crash the master. Instead, we fake the crash by just not writing
+# the XID event to the binlog. This is done by the
+# --debug=d,do_not_write_xid flag in the .opt file.
+
+# So, unlike if the master had crashed, the master *will* execute the
+# statement. But the slave should not execute it. Hence, after the
+# first test is executed, the expected result on master is a table
+# with one row, and on slave a table with no rows.
+
+# To simulate the slave correctly, we wait until everything up to the
+# XID is replicated. We cannot sync_slave_with_master, because that
+# would wait for the transaction to end. Instead, we wait for
+# "sufficiently long time". Then we stop the slave.
+
+# Note: since this puts the master binlog in an inconsistent state,
+# this should be the last test of the file.
+
+--echo --- on master ---
+--connection master
+
+INSERT INTO tinnodb VALUES (1);
+SELECT * FROM tinnodb ORDER BY a;
+
+--echo --- on slave ---
+--connection slave
+--sleep 3
+STOP SLAVE;
+source include/wait_for_slave_to_stop.inc;
+--replace_column 4 # 7 # 8 # 9 # 16 # 22 # 23 # 33 #
+query_vertical SHOW SLAVE STATUS;
+# the following statement should show that nothing has been replicated
+SELECT * FROM tinnodb ORDER BY a;
+
+
+# clean up
+connection master;
+DROP TABLE tmyisam;
+DROP TABLE tinnodb;
+
+connection slave;
+DROP TABLE tmyisam;
+DROP TABLE tinnodb;
diff -Nrup a/sql/log.cc b/sql/log.cc
--- a/sql/log.cc 2007-11-05 16:20:08 +01:00
+++ b/sql/log.cc 2007-12-21 20:30:22 +01:00
@@ -122,6 +122,20 @@ static int binlog_prepare(THD *thd, bool
return 0;
}
+/**
+ This function is called once after each statement.
+
+ It has the responsibility to flush the transaction cache to the
+ binlog file on commits.
+
+ @param thd The client thread that executes the transaction.
+ @param all true if this is the last statement before a COMMIT
+ statement; false if either this is a statement in a
+ transaction but not the last, or if this is a statement
+ not inside a BEGIN block and autocommit is on.
+
+ @see handlerton::commit
+*/
static int binlog_commit(THD *thd, bool all)
{
IO_CACHE *trans_log= (IO_CACHE*)thd->ha_data[binlog_hton.slot];
@@ -134,7 +148,15 @@ static int binlog_commit(THD *thd, bool
// we're here because trans_log was flushed in MYSQL_LOG::log_xid()
DBUG_RETURN(0);
}
- if (all)
+ /*
+ Write commit event if at least one of the following holds:
+ - the user sends an explicit COMMIT; or
+ - the autocommit flag is on, and we are not inside a BEGIN.
+ However, if the user has not sent an explicit COMMIT, and we are
+ either inside a BEGIN or run with autocommit off, then this is not
+ the end of a transaction and we should not write a commit event.
+ */
+ if (all || !(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))
{
Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE);
qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE)
@@ -144,6 +166,22 @@ static int binlog_commit(THD *thd, bool
DBUG_RETURN(binlog_end_trans(thd, trans_log, &invisible_commit));
}
+/**
+ This function is called when a transaction involving a transactional
+ table is rolled back.
+
+ It has the responsibility to flush the transaction cache to the
+ binlog file. However, if the transaction does not involve
+ non-transactional tables, nothing needs to be logged.
+
+ @param thd The client thread that executes the transaction.
+ @param all true if this is the last statement before a COMMIT
+ statement; false if either this is a statement in a
+ transaction but not the last, or if this is a statement
+ not inside a BEGIN block and autocommit is on.
+
+ @see handlerton::rollback
+*/
static int binlog_rollback(THD *thd, bool all)
{
int error=0;
@@ -1817,9 +1855,11 @@ uint MYSQL_LOG::next_file_id()
IMPLEMENTATION
- To support transaction over replication, we wrap the transaction
with BEGIN/COMMIT or BEGIN/ROLLBACK in the binary log.
- We want to write a BEGIN/ROLLBACK block when a non-transactional table
- was updated in a transaction which was rolled back. This is to ensure
- that the same updates are run on the slave.
+ If a transaction that only involves transactional tables is
+ rolled back, we do not binlog it. However, we write a
+ BEGIN/ROLLBACK block when a non-transactional table was updated
+ in a transaction which was rolled back. This is to ensure that
+ the same updates are run on the slave.
*/
bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event)
@@ -1837,32 +1877,34 @@ bool MYSQL_LOG::write(THD *thd, IO_CACHE
byte header[LOG_EVENT_HEADER_LEN];
/*
- Log "BEGIN" at the beginning of the transaction.
- which may contain more than 1 SQL statement.
+ Log "BEGIN" at the beginning of every transaction. Here, a
+ transaction is either a BEGIN..COMMIT block or a single
+ statement in autocommit mode.
*/
- if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))
- {
- Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, FALSE);
- /*
- 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, 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;
- /*
- Now this Query_log_event has artificial log_pos 0. It must be adjusted
- to reflect the real position in the log. Not doing it would confuse the
- slave: it would prevent this one from knowing where he is in the
- master's binlog, which would result in wrong positions being shown to
- the user, MASTER_POS_WAIT undue waiting etc.
- */
- if (qinfo.write(&log_file))
- goto err;
- }
+ Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, FALSE);
+ /*
+ 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, 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;
+ /*
+ Now this Query_log_event has artificial log_pos 0. It must be
+ adjusted to reflect the real position in the log. Not doing it
+ would confuse the slave: it would prevent this one from
+ knowing where he is in the master's binlog, which would result
+ in wrong positions being shown to the user, MASTER_POS_WAIT
+ undue waiting etc.
+ */
+ if (qinfo.write(&log_file))
+ goto err;
+
/* Read from the file used to cache the queries .*/
if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
goto err;
diff -Nrup a/sql/log_event.cc b/sql/log_event.cc
--- a/sql/log_event.cc 2007-12-15 12:50:21 +01:00
+++ b/sql/log_event.cc 2007-12-21 20:30:22 +01:00
@@ -3793,6 +3793,7 @@ Xid_log_event(const char* buf,
#ifndef MYSQL_CLIENT
bool Xid_log_event::write(IO_CACHE* file)
{
+ DBUG_EXECUTE_IF("do_not_write_xid", return 0;);
return write_header(file, sizeof(xid)) ||
my_b_safe_write(file, (byte*) &xid, sizeof(xid));
}