summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrei Elkin <andrei.elkin@mariadb.com>2020-03-08 21:52:30 +0200
committerAndrei Elkin <andrei.elkin@mariadb.com>2020-03-15 17:04:29 +0200
commitdc9057184296583af86522c12c27204401819c75 (patch)
treedfa3c5e2ba3b52364d04c0a4c04769f3184eb8cd
parenta467e6755dc28159ee0bef42bea35cffb9b7bacb (diff)
downloadmariadb-git-bb-10.5-mdev_742.tar.gz
MDEV-742: read-only, pure myisam, binlog-*, @@skip_log_bin corner casesbb-10.5-mdev_742
(Pushed to 10.5) Are addressed along the following policies. Prepared read-only, or on non-transactional engines, or binlog-* filter in binlog, or skipped to binlog XA:s remains in the xid cache after disconnect. But their consequent completion with Commit or Rollback differs. 1. The read-only at reconnect marks XID to fail for future completion with ER_XA_RBROLLBACK. 2. `binlog-*` filtered XA when it changes engine data is regarded as loggable even when nothing got cached for binlog. An empty XA-prepare group is recorded. Consequent Commit-or-Rollback succeeds in the Engine(s) as well as recorded into binlog. 3. The same applies to the non-transactional engine XA. 4. @@skip_log_bin=OFF does not record anything at XA-prepare (obviously), but the completion event is recorded into binlog to admit inconsistency with slave. The following actions are taken by the patch. At XA-prepare: when empty binlog cache - don't do anything to binlog if RO, otherwise write empty XA_prepare (assert(binlog-filter case)). At Disconnect: when Prepared && RO (=> no binlogging was done) set Xid_cache_element::error := ER_XA_RBROLLBACK *keep* XID in the cache, and rollback the transaction. At XA-"complete": Discover the error, if any don't binlog the "complete", return the error to the user. RO patch review notes.
-rw-r--r--mysql-test/main/xa_sync.test2
-rw-r--r--mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc2
-rw-r--r--mysql-test/suite/rpl/r/rpl_xa.result178
-rw-r--r--mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result178
-rw-r--r--mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc9
-rw-r--r--mysql-test/suite/rpl/t/rpl_xa-master.opt1
-rw-r--r--mysql-test/suite/rpl/t/rpl_xa.inc283
-rw-r--r--mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine-master.opt1
-rw-r--r--sql/handler.cc23
-rw-r--r--sql/handler.h1
-rw-r--r--sql/log.cc34
-rw-r--r--sql/xa.cc6
12 files changed, 708 insertions, 10 deletions
diff --git a/mysql-test/main/xa_sync.test b/mysql-test/main/xa_sync.test
index 2fe7337501e..ad1243ce6f8 100644
--- a/mysql-test/main/xa_sync.test
+++ b/mysql-test/main/xa_sync.test
@@ -38,7 +38,7 @@ while ($i)
--echo *** Must have 'xatest' in the list
XA RECOVER;
# second time yields no error
- --error 0
+ --error 0,1402
--eval $op
disconnect con2;
diff --git a/mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc b/mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc
index 1f6ce713cc9..4a83aa5c282 100644
--- a/mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc
+++ b/mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc
@@ -23,6 +23,7 @@ XA RECOVER;
--source include/wait_condition.inc
# It will conclude now
+--error 0,1402
--eval $terminate_with 'trx1$type'
--replace_result $conn3_id CONN_ID
@@ -32,4 +33,5 @@ XA RECOVER;
--source include/wait_condition.inc
# It will conclude now
+--error 0,1402
--eval $terminate_with 'trx3$type'
diff --git a/mysql-test/suite/rpl/r/rpl_xa.result b/mysql-test/suite/rpl/r/rpl_xa.result
index 3420f2348e2..ed07edd8fb5 100644
--- a/mysql-test/suite/rpl/r/rpl_xa.result
+++ b/mysql-test/suite/rpl/r/rpl_xa.result
@@ -44,5 +44,181 @@ connection slave;
include/diff_tables.inc [master:t1, slave:t1]
include/diff_tables.inc [master:t2, slave:t2]
connection master;
-drop table t1, t2;
+*** At the start of read-only section gtid list is:
+flush logs;
+show binlog events in 'master-bin.000002' limit 1,1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000002 # Gtid_list 1 # [0-1-11]
+set @query1="select 1";
+set @query2="select count(*) into @s2 from t1";
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'ro_2';
+select count(*) into @s2 from t1;
+xa end 'ro_2';
+xa prepare 'ro_2';;
+disconnect master_ro_2;
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'ro_1';
+select 1;
+1
+1
+xa end 'ro_1';
+xa prepare 'ro_1';;
+disconnect master_ro_1;
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'ro_2';
+select count(*) into @s2 from t1;
+xa end 'ro_2';
+xa prepare 'ro_2';;
+disconnect master_ro_2;
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'ro_1';
+select 1;
+1
+1
+xa end 'ro_1';
+xa prepare 'ro_1';;
+disconnect master_ro_1;
+*** 2 prepared xa:s must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+1 4 0 ro_2
+1 4 0 ro_1
+*** Zero prepared xa:s must be in the list:
+xa recover;
+formatID gtrid_length bqual_length data
+*** At the end of read-only section gtid list has 0 more compare with previous check:
+flush logs;
+show binlog events in 'master-bin.000003' limit 1,1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000003 # Gtid_list 1 # [0-1-11]
+create database test_ign;
+set @@sql_log_bin = 0;
+create table test_ign.t (a int) engine=InnoDB;
+set @@sql_log_bin = 1;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'rw_no_binlog';
+insert into test_ign.t set a=1;
+xa end 'rw_no_binlog';
+xa prepare 'rw_no_binlog';;
+disconnect master_rw_no_binlog;
+*** rw_no_binlog must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+1 12 0 rw_no_binlog
+*** Zero must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+*** At the end of --binlog-ignore-db section gtid list has 2 more:
+flush logs;
+show binlog events in 'master-bin.000004' limit 1,1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000004 # Gtid_list 1 # [0-1-13]
+connection master;
+create table t3 (a int) engine=innodb;
+*** the disconnected prepare case
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+set @@binlog_format=statement;
+xa start 'rw_binlog_only';
+delete from t3;
+xa end 'rw_binlog_only';
+xa prepare 'rw_binlog_only';
+disconnect master_rw_binlog_only;
+connection master;
+*** rw_binlog_only must be in the list:
+xa recover;
+formatID gtrid_length bqual_length data
+1 14 0 rw_binlog_only
+*** Zero must be in the list:
+xa recover;
+formatID gtrid_length bqual_length data
+*** the same connection complete case.
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+set @@binlog_format=statement;
+xa start 'rw_binlog_only';
+delete from t3;
+xa end 'rw_binlog_only';
+xa prepare 'rw_binlog_only';
+*** rw_binlog_only must be in the list:
+xa recover;
+formatID gtrid_length bqual_length data
+1 14 0 rw_binlog_only
+disconnect master_rw_binlog_only;
+*** Zero must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+*** At the end of ineffective in engine section gtid list has 5 more:
+flush logs;
+show binlog events in 'master-bin.000005' limit 1,1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000005 # Gtid_list 1 # [0-1-18]
+create table tm (a int) engine=myisam;
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'rw_myisam';
+insert into tm set a=1;
+xa end 'rw_myisam';
+xa prepare 'rw_myisam';;
+disconnect master_rw_myisam;
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'rw_myisam';
+insert into tm set a=1;
+xa end 'rw_myisam';
+xa prepare 'rw_myisam';;
+disconnect master_rw_myisam;
+*** rw_myisam prepared must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+1 9 0 rw_myisam
+*** Zero prepared xa:s must be in the list:
+xa recover;
+formatID gtrid_length bqual_length data
+*** At the end of MyISAM "xa" section gtid list has 7 more compare with previous check:
+flush logs;
+show binlog events in 'master-bin.000006' limit 1,1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000006 # Gtid_list 1 # [0-1-25]
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+set @@session.sql_log_bin = OFF;
+xa start 'skip_binlog';
+insert into t2 values(1);
+xa end 'skip_binlog';
+xa prepare 'skip_binlog';
+disconnect master_skip_binlog;
+*** skip_binlog must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+1 11 0 skip_binlog
+connection master;
+call mtr.add_suppression("Slave: XAER_NOTA: Unknown XID");
+xa rollback 'skip_binlog';
+*** Zero must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+*** At the end of --binlog-ignore-db section gtid list has 2 more:
+flush logs;
+show binlog events in 'master-bin.000007' limit 1,1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000007 # Gtid_list 1 # [0-1-27]
+include/save_master_gtid.inc
+connection slave;
+include/wait_for_slave_sql_error.inc [errno=1397]
+set @@global.sql_slave_skip_counter= 1;
+include/start_slave.inc
+connection master;
+drop database test_ign;
+drop table t1, t2, t3, tm;
include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result b/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result
index a7ed0f97ea2..3826bf32f32 100644
--- a/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result
+++ b/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result
@@ -53,7 +53,183 @@ connection slave;
include/diff_tables.inc [master:t1, slave:t1]
include/diff_tables.inc [master:t2, slave:t2]
connection master;
-drop table t1, t2;
+*** At the start of read-only section gtid list is:
+flush logs;
+show binlog events in 'master-bin.000002' limit 1,1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000002 # Gtid_list 1 # [0-1-11]
+set @query1="select 1";
+set @query2="select count(*) into @s2 from t1";
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'ro_2';
+select count(*) into @s2 from t1;
+xa end 'ro_2';
+xa prepare 'ro_2';;
+disconnect master_ro_2;
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'ro_1';
+select 1;
+1
+1
+xa end 'ro_1';
+xa prepare 'ro_1';;
+disconnect master_ro_1;
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'ro_2';
+select count(*) into @s2 from t1;
+xa end 'ro_2';
+xa prepare 'ro_2';;
+disconnect master_ro_2;
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'ro_1';
+select 1;
+1
+1
+xa end 'ro_1';
+xa prepare 'ro_1';;
+disconnect master_ro_1;
+*** 2 prepared xa:s must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+1 4 0 ro_2
+1 4 0 ro_1
+*** Zero prepared xa:s must be in the list:
+xa recover;
+formatID gtrid_length bqual_length data
+*** At the end of read-only section gtid list has 0 more compare with previous check:
+flush logs;
+show binlog events in 'master-bin.000003' limit 1,1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000003 # Gtid_list 1 # [0-1-11]
+create database test_ign;
+set @@sql_log_bin = 0;
+create table test_ign.t (a int) engine=InnoDB;
+set @@sql_log_bin = 1;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'rw_no_binlog';
+insert into test_ign.t set a=1;
+xa end 'rw_no_binlog';
+xa prepare 'rw_no_binlog';;
+disconnect master_rw_no_binlog;
+*** rw_no_binlog must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+1 12 0 rw_no_binlog
+*** Zero must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+*** At the end of --binlog-ignore-db section gtid list has 2 more:
+flush logs;
+show binlog events in 'master-bin.000004' limit 1,1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000004 # Gtid_list 1 # [0-1-13]
+connection master;
+create table t3 (a int) engine=innodb;
+*** the disconnected prepare case
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+set @@binlog_format=statement;
+xa start 'rw_binlog_only';
+delete from t3;
+xa end 'rw_binlog_only';
+xa prepare 'rw_binlog_only';
+disconnect master_rw_binlog_only;
+connection master;
+*** rw_binlog_only must be in the list:
+xa recover;
+formatID gtrid_length bqual_length data
+1 14 0 rw_binlog_only
+*** Zero must be in the list:
+xa recover;
+formatID gtrid_length bqual_length data
+*** the same connection complete case.
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+set @@binlog_format=statement;
+xa start 'rw_binlog_only';
+delete from t3;
+xa end 'rw_binlog_only';
+xa prepare 'rw_binlog_only';
+*** rw_binlog_only must be in the list:
+xa recover;
+formatID gtrid_length bqual_length data
+1 14 0 rw_binlog_only
+disconnect master_rw_binlog_only;
+*** Zero must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+*** At the end of ineffective in engine section gtid list has 5 more:
+flush logs;
+show binlog events in 'master-bin.000005' limit 1,1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000005 # Gtid_list 1 # [0-1-18]
+create table tm (a int) engine=myisam;
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'rw_myisam';
+insert into tm set a=1;
+xa end 'rw_myisam';
+xa prepare 'rw_myisam';;
+disconnect master_rw_myisam;
+connection master;
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+xa start 'rw_myisam';
+insert into tm set a=1;
+xa end 'rw_myisam';
+xa prepare 'rw_myisam';;
+disconnect master_rw_myisam;
+*** rw_myisam prepared must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+1 9 0 rw_myisam
+*** Zero prepared xa:s must be in the list:
+xa recover;
+formatID gtrid_length bqual_length data
+*** At the end of MyISAM "xa" section gtid list has 7 more compare with previous check:
+flush logs;
+show binlog events in 'master-bin.000006' limit 1,1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000006 # Gtid_list 1 # [0-1-25]
+connect master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,;
+set @@session.sql_log_bin = OFF;
+xa start 'skip_binlog';
+insert into t2 values(1);
+xa end 'skip_binlog';
+xa prepare 'skip_binlog';
+disconnect master_skip_binlog;
+*** skip_binlog must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+1 11 0 skip_binlog
+connection master;
+call mtr.add_suppression("Slave: XAER_NOTA: Unknown XID");
+xa rollback 'skip_binlog';
+*** Zero must be in the list:
+connection master;
+xa recover;
+formatID gtrid_length bqual_length data
+*** At the end of --binlog-ignore-db section gtid list has 2 more:
+flush logs;
+show binlog events in 'master-bin.000007' limit 1,1;
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000007 # Gtid_list 1 # [0-1-27]
+include/save_master_gtid.inc
+connection slave;
+include/wait_for_slave_sql_error.inc [errno=1397]
+set @@global.sql_slave_skip_counter= 1;
+include/start_slave.inc
+connection master;
+drop database test_ign;
+drop table t1, t2, t3, tm;
connection slave;
include/stop_slave.inc
SET @@global.gtid_pos_auto_engines="";
diff --git a/mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc b/mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc
new file mode 100644
index 00000000000..b823ebf62ee
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_create_xa_prepared.inc
@@ -0,0 +1,9 @@
+# param $xid to name xa and take part in the connection name
+# param $query to execute as the xa body
+# param $db_ign the default database
+
+--connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,)
+--eval xa start '$xid'
+--eval $query
+--eval xa end '$xid'
+--eval xa prepare '$xid';
diff --git a/mysql-test/suite/rpl/t/rpl_xa-master.opt b/mysql-test/suite/rpl/t/rpl_xa-master.opt
new file mode 100644
index 00000000000..6794216dc45
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa-master.opt
@@ -0,0 +1 @@
+--binlog-ignore-db=test_ign
diff --git a/mysql-test/suite/rpl/t/rpl_xa.inc b/mysql-test/suite/rpl/t/rpl_xa.inc
index f1ba4cf8557..571a79c1acc 100644
--- a/mysql-test/suite/rpl/t/rpl_xa.inc
+++ b/mysql-test/suite/rpl/t/rpl_xa.inc
@@ -69,5 +69,286 @@ source include/diff_tables.inc;
let $diff_tables= master:t2, slave:t2;
source include/diff_tables.inc;
+#
+# Read-only XA remains prepared after disconnect and must rollback at XA-complete
+# after recoonect. To the read-only also belongs non-transactional engine XA.
+#
+--connection master
+
+--echo *** At the start of read-only section gtid list is:
+flush logs;
+--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+--source include/show_gtid_list.inc
+
+
+set @query1="select 1";
+set @query2="select count(*) into @s2 from t1";
+--let $ro_cases=2
+--let $db=test
+
+# No disconnect
+--let $p_trx=$ro_cases
+while ($p_trx)
+{
+--connection master
+ --let $xid=ro_$p_trx
+ --let $query=`SELECT @query$p_trx`
+ --source rpl_create_xa_prepared.inc
+ --let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')`
+ --error 0
+ --disable_query_log
+ --disable_result_log
+ --eval xa $complete '$xid'
+ --enable_result_log
+ --enable_query_log
+
+ --disconnect master_$xid
+ --source include/wait_until_disconnected.inc
+
+ --dec $p_trx
+}
+
+
+--let $p_trx=$ro_cases
+# With diconnect
+while ($p_trx)
+{
+--connection master
+ --let $xid=ro_$p_trx
+ --let $query=`SELECT @query$p_trx`
+ --source rpl_create_xa_prepared.inc
+
+ --disconnect master_$xid
+ --source include/wait_until_disconnected.inc
+
+ --dec $p_trx
+}
+
+--echo *** $ro_cases prepared xa:s must be in the list:
+--connection master
+xa recover;
+
+--let $p_trx=$ro_cases
+while ($p_trx)
+{
+ --let $xid=ro_$p_trx
+ --let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')`
+ --disable_query_log
+ --disable_result_log
+ --error ER_XA_RBROLLBACK
+ --eval xa $complete '$xid'
+ --enable_result_log
+ --enable_query_log
+
+ --dec $p_trx
+}
+--echo *** Zero prepared xa:s must be in the list:
+xa recover;
+
+--echo *** At the end of read-only section gtid list has 0 more compare with previous check:
+flush logs;
+--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+--source include/show_gtid_list.inc
+
+
+#
+# XA logging cases while some of XA resources are read-only
+#
+# A1. Binlog filter
+
+
+--let $db=test_ign
+--eval create database $db
+set @@sql_log_bin = 0;
+--eval create table $db.t (a int) engine=InnoDB
+set @@sql_log_bin = 1;
+
+--let $xid=rw_no_binlog
+--let $query=insert into $db.t set a=1
+--source rpl_create_xa_prepared.inc
+--disconnect master_$xid
+--source include/wait_until_disconnected.inc
+
+--echo *** $xid must be in the list:
+--connection master
+xa recover;
+
+--let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')`
+--error 0
+--disable_query_log
+--disable_result_log
+--eval xa $complete '$xid'
+--enable_result_log
+--enable_query_log
+
+--echo *** Zero must be in the list:
+--connection master
+xa recover;
+# restore for the following tests
+--let $db=test
+
+--echo *** At the end of --binlog-ignore-db section gtid list has 2 more:
+flush logs;
+--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+--source include/show_gtid_list.inc
+
+#
+# A2. Opposite to A1, ineffective execution in Engine may create a
+# binlog transaction
+#
+connection master;
+create table t3 (a int) engine=innodb;
+
+--echo *** the disconnected prepare case
+--let $xid=rw_binlog_only
+--let $query=delete from t3
+--connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,)
+ set @@binlog_format=statement;
+ # --source rpl_create_xa_prepared.inc
+ --eval xa start '$xid'
+ --eval $query
+ --eval xa end '$xid'
+ --eval xa prepare '$xid'
+
+ --disconnect master_$xid
+ --source include/wait_until_disconnected.inc
+
+connection master;
+--echo *** $xid must be in the list:
+xa recover;
+
+ --let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')`
+ --disable_query_log
+ --disable_result_log
+ --eval xa $complete '$xid'
+ --enable_result_log
+ --enable_query_log
+
+--echo *** Zero must be in the list:
+xa recover;
+
+--echo *** the same connection complete case.
+connection master;
+ --let $xid=rw_binlog_only
+ --let $query=delete from t3
+--connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,)
+ set @@binlog_format=statement;
+ # --source rpl_create_xa_prepared.inc
+ --eval xa start '$xid'
+ --eval $query
+ --eval xa end '$xid'
+ --eval xa prepare '$xid'
+
+--echo *** $xid must be in the list:
+xa recover;
+
+--disable_query_log
+ --disable_result_log
+ --eval xa $complete '$xid'
+ --enable_result_log
+ --enable_query_log
+--disconnect master_$xid
+--source include/wait_until_disconnected.inc
+
+--echo *** Zero must be in the list:
+--connection master
+xa recover;
+
+--echo *** At the end of ineffective in engine section gtid list has 5 more:
+flush logs;
+--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+--source include/show_gtid_list.inc
+
+#
+# A3 MyISAM "xa" logs empty XA-prepare group, followed by
+# an XA-complete event
+create table tm (a int) engine=myisam;
+
+# No disconnect
+--connection master
+ --let $xid=rw_myisam
+ --let $query=insert into tm set a=1
+ --source rpl_create_xa_prepared.inc
+ --let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')`
+ --error 0
+ --disable_query_log
+ --disable_result_log
+ --eval xa $complete '$xid'
+ --enable_result_log
+ --enable_query_log
+
+ --disconnect master_$xid
+ --source include/wait_until_disconnected.inc
+
+# With diconnect
+--connection master
+ --source rpl_create_xa_prepared.inc
+ --disconnect master_$xid
+ --source include/wait_until_disconnected.inc
+
+--echo *** $xid prepared must be in the list:
+--connection master
+xa recover;
+
+ --let $complete=`select if(floor(rand()*10)%2,'COMMIT','ROLLBACK')`
+ --disable_query_log
+ --disable_result_log
+ --eval xa $complete '$xid'
+ --enable_result_log
+ --enable_query_log
+
+--echo *** Zero prepared xa:s must be in the list:
+xa recover;
+
+--echo *** At the end of MyISAM "xa" section gtid list has 7 more compare with previous check:
+flush logs;
+--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+--source include/show_gtid_list.inc
+
+
+# B. Session binlog disable does not log even empty XA-prepare but XA-complete will be
+# logged despite of that.
+
+--let $db=test
+--let $xid=skip_binlog
+--let $query=insert into t2 values(1)
+--connect (master_$xid, 127.0.0.1,root,,$db,$MASTER_MYPORT,)
+set @@session.sql_log_bin = OFF;
+--eval xa start '$xid'
+ --eval $query
+--eval xa end '$xid'
+--eval xa prepare '$xid'
+
+--disconnect master_$xid
+--source include/wait_until_disconnected.inc
+
+--echo *** $xid must be in the list:
+--connection master
+xa recover;
+
+--connection master
+call mtr.add_suppression("Slave: XAER_NOTA: Unknown XID");
+--eval xa rollback '$xid'
+
+--echo *** Zero must be in the list:
+--connection master
+xa recover;
+
+--echo *** At the end of --binlog-ignore-db section gtid list has 2 more:
+flush logs;
+--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+--source include/show_gtid_list.inc
+--source include/save_master_gtid.inc
+
+#
+# Expected error on slave and manual correction
+#
+--connection slave
+let $slave_sql_errno= 1397; # ER_XAER_NOTA
+source include/wait_for_slave_sql_error.inc;
+set @@global.sql_slave_skip_counter= 1;
+--source include/start_slave.inc
+
connection master;
-drop table t1, t2;
+--eval drop database test_ign
+drop table t1, t2, t3, tm;
diff --git a/mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine-master.opt b/mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine-master.opt
new file mode 100644
index 00000000000..6794216dc45
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine-master.opt
@@ -0,0 +1 @@
+--binlog-ignore-db=test_ign
diff --git a/sql/handler.cc b/sql/handler.cc
index c5b6e05b448..5964f6cd7c5 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1367,6 +1367,29 @@ int ha_prepare(THD *thd)
DBUG_RETURN(error);
}
+/*
+ Like ha_check_and_coalesce_trx_read_only to return counted number of
+ read-write transaction participants limited to two, but works in the 'all'
+ context.
+ Also returns the last found rw ha_info through the 2nd argument.
+*/
+uint ha_count_rw_all(THD *thd, Ha_trx_info **ptr_ha_info)
+{
+ unsigned rw_ha_count= 0;
+
+ for (auto ha_info= thd->transaction.all.ha_list; ha_info;
+ ha_info= ha_info->next())
+ {
+ if (ha_info->is_trx_read_write())
+ {
+ *ptr_ha_info= ha_info;
+ if (++rw_ha_count > 1)
+ break;
+ }
+ }
+ return rw_ha_count;
+}
+
/**
Check if we can skip the two-phase commit.
diff --git a/sql/handler.h b/sql/handler.h
index ebddb659977..22592821164 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -5173,4 +5173,5 @@ void print_keydup_error(TABLE *table, KEY *key, myf errflag);
int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info);
int del_global_table_stat(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table);
+uint ha_count_rw_all(THD *thd, Ha_trx_info **ptr_ha_info);
#endif /* HANDLER_INCLUDED */
diff --git a/sql/log.cc b/sql/log.cc
index 747313f37d3..3e7f3a043c3 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -2040,8 +2040,8 @@ static int binlog_rollback_by_xid(handlerton *hton, XID *xid)
(void) thd->binlog_setup_trx_data();
- DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_ROLLBACK);
-
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_ROLLBACK ||
+ (thd->transaction.xid_state.get_state_code() == XA_ROLLBACK_ONLY));
return binlog_rollback(hton, thd, TRUE);
}
@@ -10009,6 +10009,10 @@ int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
DBUG_RETURN(BINLOG_COOKIE_GET_ERROR_FLAG(cookie));
}
+static bool write_empty_xa_prepare(THD *thd, binlog_cache_mngr *cache_mngr)
+{
+ return binlog_commit_flush_xa_prepare(thd, true, cache_mngr);
+}
int TC_LOG_BINLOG::unlog_xa_prepare(THD *thd, bool all)
{
@@ -10017,10 +10021,28 @@ int TC_LOG_BINLOG::unlog_xa_prepare(THD *thd, bool all)
binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data();
int cookie= 0;
- if (!cache_mngr || !cache_mngr->need_unlog)
- return 0;
- else
- cookie= BINLOG_COOKIE_MAKE(cache_mngr->binlog_id, cache_mngr->delayed_error);
+ if (!cache_mngr->need_unlog)
+ {
+ Ha_trx_info *ha_info;
+ uint rw_count= ha_count_rw_all(thd, &ha_info);
+ bool rc= false;
+
+ if (rw_count > 0)
+ {
+ /* an empty XA-prepare event group is logged */
+#ifndef DBUG_OFF
+ for (ha_info= thd->transaction.all.ha_list; rw_count > 1 && ha_info;
+ ha_info= ha_info->next())
+ DBUG_ASSERT(ha_info->ht() != binlog_hton);
+#endif
+ rc= write_empty_xa_prepare(thd, cache_mngr); // normally gains need_unlog
+ trans_register_ha(thd, true, binlog_hton, 0); // do it for future commmit
+ }
+ if (rw_count == 0 || !cache_mngr->need_unlog)
+ return rc;
+ }
+
+ cookie= BINLOG_COOKIE_MAKE(cache_mngr->binlog_id, cache_mngr->delayed_error);
cache_mngr->need_unlog= false;
return unlog(cookie, 1);
diff --git a/sql/xa.cc b/sql/xa.cc
index 5a9310ad3df..cde6350f38d 100644
--- a/sql/xa.cc
+++ b/sql/xa.cc
@@ -820,6 +820,12 @@ bool trans_xa_detach(THD *thd)
if (thd->transaction.xid_state.xid_cache_element->xa_state != XA_PREPARED)
return xa_trans_force_rollback(thd);
+ else if (!thd->transaction.all.is_trx_read_write())
+ {
+ thd->transaction.xid_state.set_error(ER_XA_RBROLLBACK);
+ ha_rollback_trans(thd, true);
+ }
+
thd->transaction.xid_state.xid_cache_element->acquired_to_recovered();
thd->transaction.xid_state.xid_cache_element= 0;
thd->transaction.cleanup();