summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Botchkov <holyfoot@askmonty.org>2019-03-31 01:47:28 +0400
committerAndrei Elkin <andrei.elkin@mariadb.com>2019-05-15 19:06:24 +0300
commit0de38e461f8864a916837f4e9808ffbd33d9b741 (patch)
tree589d317e95f037295b50ad680266a31ebfa2341e
parent5cf4022340d73bfcb1650c139468d303355a5afe (diff)
downloadmariadb-git-0de38e461f8864a916837f4e9808ffbd33d9b741.tar.gz
MDEV-7974 XA PREPARE-d transactions survive disconnect/server restart.
trans_detach() implemented to survive disconnect. XA_prepare_log_event added to reflect XA PREPARE in the binlog. MDEV-7974 review/contribution: gtid record of XA-prepare should no be merged into the replicated trx. This patch redirects it into the default mysql.gtid_slave_pos table. A test is added. MDEV-7974 review/contribution: part2 (sources code) gtid record of XA-prepare should no be merged into the replicated trx... MDEV-7974 XA trnasactions. XID_STATE::registered_for_binlog is replaced with rw_trans flag in ha_prepare(). MDEV-7974 XA transactions. FLUSH TABLES WITH READ LOCK now blocks the XA COMMIT/ROLLBACK/PREPARE statements as they write to the binlog/Innodb redo. MDEV-7974 XA transactions. tokudb_maria.xa test fixed. MDEV-7974: record_gtid for XA prepared is fixed to exec the gtid statement after the xa trx got prepared and logged.
-rw-r--r--mysql-test/include/kill_and_restart_mysqld.inc15
-rw-r--r--mysql-test/main/flush_read_lock.result143
-rw-r--r--mysql-test/main/flush_read_lock.test112
-rw-r--r--mysql-test/main/xa.result70
-rw-r--r--mysql-test/main/xa.test49
-rw-r--r--mysql-test/suite/binlog/include/binlog_xa_prepare_connection.inc31
-rw-r--r--mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc35
-rw-r--r--mysql-test/suite/binlog/include/binlog_xa_prepared_do_and_restart.inc323
-rw-r--r--mysql-test/suite/binlog/r/binlog_xa_prepared.result1042
-rw-r--r--mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result1175
-rw-r--r--mysql-test/suite/binlog/t/binlog_xa_prepared.test106
-rw-r--r--mysql-test/suite/binlog/t/binlog_xa_prepared_disconnect.test13
-rw-r--r--mysql-test/suite/rpl/include/rpl_connection_master.inc2
-rw-r--r--mysql-test/suite/rpl/include/rpl_connection_slave.inc2
-rw-r--r--mysql-test/suite/rpl/r/rpl_xa.result49
-rw-r--r--mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result65
-rw-r--r--mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_mixed_engines.result46
-rw-r--r--mysql-test/suite/rpl/t/rpl_xa.inc69
-rw-r--r--mysql-test/suite/rpl/t/rpl_xa.test5
-rw-r--r--mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine.test25
-rw-r--r--mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_mixed_engines.test80
-rw-r--r--sql/handler.cc17
-rw-r--r--sql/handler.h25
-rw-r--r--sql/log.cc87
-rw-r--r--sql/log.h10
-rw-r--r--sql/log_event.cc523
-rw-r--r--sql/log_event.h119
-rw-r--r--sql/sql_class.h25
-rw-r--r--sql/sql_connect.cc1
-rw-r--r--sql/sql_parse.cc3
-rw-r--r--sql/transaction.cc121
-rw-r--r--sql/xa.cc131
-rw-r--r--sql/xa.h151
-rw-r--r--storage/innobase/handler/ha_innodb.cc46
-rw-r--r--storage/innobase/trx/trx0trx.cc16
-rw-r--r--storage/tokudb/mysql-test/tokudb_mariadb/r/xa.result1
-rw-r--r--storage/tokudb/mysql-test/tokudb_mariadb/t/xa.test1
37 files changed, 4480 insertions, 254 deletions
diff --git a/mysql-test/include/kill_and_restart_mysqld.inc b/mysql-test/include/kill_and_restart_mysqld.inc
new file mode 100644
index 00000000000..b67fb7350b4
--- /dev/null
+++ b/mysql-test/include/kill_and_restart_mysqld.inc
@@ -0,0 +1,15 @@
+if (!$restart_parameters)
+{
+ let $restart_parameters = restart;
+}
+
+--let $_server_id= `SELECT @@server_id`
+--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect
+
+--echo # Kill and $restart_parameters
+--exec echo "$restart_parameters" > $_expect_file_name
+--shutdown_server 0
+--source include/wait_until_disconnected.inc
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+--disable_reconnect
diff --git a/mysql-test/main/flush_read_lock.result b/mysql-test/main/flush_read_lock.result
index db92344cfbd..185cacdeca3 100644
--- a/mysql-test/main/flush_read_lock.result
+++ b/mysql-test/main/flush_read_lock.result
@@ -1308,6 +1308,8 @@ unlock tables;
# Check that XA non-COMMIT statements are not and COMMIT is
# blocked by active FTWRL in another connection
#
+# XA COMMIT, XA ROLLBACK and XA PREPARE does take COMMIT lock to ensure
+# that nothing is written to bin log and redo log under FTWRL mode.
connection con1;
flush tables with read lock;
connection default;
@@ -1320,11 +1322,51 @@ connection con1;
flush tables with read lock;
connection default;
xa end 'test1';
-xa prepare 'test1';
-xa rollback 'test1';
+xa prepare 'test1';;
+connection con1;
+Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
+where state = "Waiting for commit lock" and
+info = "xa prepare 'test1'"
+Id User Host db Command Time State Info Progress
+3 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+4 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+1 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+2 system user NULL Daemon NULL InnoDB purge coordinator NULL 0.000
+5 system user NULL Daemon NULL InnoDB shutdown handler NULL 0.000
+9 root localhost test Query 30 Waiting for backup lock xa prepare 'test1' 0.000
+10 root localhost test Query 0 Init show full processlist 0.000
+11 root localhost test Sleep 30 NULL 0.000
+12 root localhost test Sleep 63 NULL 0.000
+unlock tables;
+# Switching to connection 'default'.
+connection default;
+# Reap XA PREPARE.
+# Switching to connection 'con1'.
connection con1;
+flush tables with read lock;
+# Switching to connection 'default'.
+connection default;
+# Send XA ROLLBACK 'test1'
+xa rollback 'test1';
+# Switching to connection 'con1'.
+connection con1;
+# Wait until XA ROLLBACK is blocked.
+Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
+where state = "Waiting for commit lock" and
+info = "xa rollback 'test1'"
+Id User Host db Command Time State Info Progress
+3 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+4 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+1 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+2 system user NULL Daemon NULL InnoDB purge coordinator NULL 0.000
+5 system user NULL Daemon NULL InnoDB shutdown handler NULL 0.000
+9 root localhost test Query 30 Waiting for backup lock xa rollback 'test1' 0.000
+10 root localhost test Query 0 Init show full processlist 0.000
+11 root localhost test Sleep 61 NULL 0.000
+12 root localhost test Sleep 94 NULL 0.000
unlock tables;
connection default;
+# Reap XA ROLLBACK
xa start 'test1';
insert into t3_trans values (1);
connection con1;
@@ -1332,7 +1374,33 @@ flush tables with read lock;
connection default;
connection default;
xa end 'test1';
+# Send XA PREPARE 'test1'
xa prepare 'test1';
+# Switching to connection 'con1'.
+connection con1;
+# Wait until XA PREPARE is blocked.
+Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
+where state = "Waiting for commit lock" and
+info = "xa prepare 'test1'"
+Id User Host db Command Time State Info Progress
+3 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+4 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+1 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+2 system user NULL Daemon NULL InnoDB purge coordinator NULL 0.000
+5 system user NULL Daemon NULL InnoDB shutdown handler NULL 0.000
+9 root localhost test Query 31 Waiting for backup lock xa prepare 'test1' 0.000
+10 root localhost test Query 0 Init show full processlist 0.000
+11 root localhost test Sleep 92 NULL 0.000
+12 root localhost test Sleep 125 NULL 0.000
+unlock tables;
+# Switching to connection 'default'.
+connection default;
+# Reap XA PREPARE.
+# Switching to connection 'con1'.
+connection con1;
+flush tables with read lock;
+# Switching to connection 'default'.
+connection default;
# Send:
xa commit 'test1';;
connection con1;
@@ -1342,6 +1410,77 @@ connection default;
# Reap XA COMMIT.
delete from t3_trans;
#
+# Check that XA COMMIT / ROLLBACK for prepared transaction from a
+# disconnected session is blocked by active FTWRL in another connection.
+#
+# Create temporary connection for XA transaction.
+connect con_tmp,localhost,root,,;
+xa start 'test1';
+insert into t3_trans values (1);
+xa end 'test1';
+xa prepare 'test1';
+# Disconnect temporary connection
+disconnect con_tmp;
+# Create temporary connection for XA transaction.
+connect con_tmp,localhost,root,,;
+xa start 'test2';
+insert into t3_trans values (2);
+xa end 'test2';
+xa prepare 'test2';
+# Disconnect temporary connection
+disconnect con_tmp;
+# Switching to connection 'con1'.
+connection con1;
+flush tables with read lock;
+# Switching to connection 'default'.
+connection default;
+# Send XA ROLLBACK 'test1'
+xa rollback 'test1';
+# Switching to connection 'con1'.
+connection con1;
+# Wait until XA ROLLBACK is blocked.
+Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
+where state = "Waiting for commit lock" and
+info = "xa rollback 'test1'"
+Id User Host db Command Time State Info Progress
+3 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+4 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+1 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+2 system user NULL Daemon NULL InnoDB purge coordinator NULL 0.000
+5 system user NULL Daemon NULL InnoDB shutdown handler NULL 0.000
+9 root localhost test Query 30 Waiting for backup lock xa rollback 'test1' 0.000
+10 root localhost test Query 0 Init show full processlist 0.000
+11 root localhost test Sleep 123 NULL 0.000
+12 root localhost test Sleep 157 NULL 0.000
+unlock tables;
+flush tables with read lock;
+# Switching to connection 'default'.
+connection default;
+# Reap XA ROLLBACK
+# Send XA COMMIT
+xa commit 'test2';;
+# Switching to connection 'con1'.
+connection con1;
+# Wait until XA COMMIT is blocked.
+Timeout in wait_condition.inc for select count(*) = 1 from information_schema.processlist
+where state = "Waiting for commit lock" and
+info = "xa commit 'test2'"
+Id User Host db Command Time State Info Progress
+3 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+4 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+1 system user NULL Daemon NULL InnoDB purge worker NULL 0.000
+2 system user NULL Daemon NULL InnoDB purge coordinator NULL 0.000
+5 system user NULL Daemon NULL InnoDB shutdown handler NULL 0.000
+9 root localhost test Query 31 Waiting for backup lock xa commit 'test2' 0.000
+10 root localhost test Query 0 Init show full processlist 0.000
+11 root localhost test Sleep 154 NULL 0.000
+12 root localhost test Sleep 188 NULL 0.000
+unlock tables;
+# Switching to connection 'default'.
+connection default;
+# Reap XA COMMIT.
+delete from t3_trans;
+#
# Check that XA COMMIT blocks FTWRL in another connection.
xa start 'test1';
insert into t3_trans values (1);
diff --git a/mysql-test/main/flush_read_lock.test b/mysql-test/main/flush_read_lock.test
index 80512deac4e..31d6dba66d7 100644
--- a/mysql-test/main/flush_read_lock.test
+++ b/mysql-test/main/flush_read_lock.test
@@ -1592,6 +1592,8 @@ unlock tables;
--echo # Check that XA non-COMMIT statements are not and COMMIT is
--echo # blocked by active FTWRL in another connection
--echo #
+--echo # XA COMMIT, XA ROLLBACK and XA PREPARE does take COMMIT lock to ensure
+--echo # that nothing is written to bin log and redo log under FTWRL mode.
connection $con_aux1;
flush tables with read lock;
connection default;
@@ -1604,11 +1606,37 @@ connection $con_aux1;
flush tables with read lock;
connection default;
xa end 'test1';
-xa prepare 'test1';
-xa rollback 'test1';
+--send xa prepare 'test1';
connection $con_aux1;
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "xa prepare 'test1'";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap XA PREPARE.
+--reap
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Send XA ROLLBACK 'test1'
+--send xa rollback 'test1'
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until XA ROLLBACK is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "xa rollback 'test1'";
+--source include/wait_condition.inc
unlock tables;
connection default;
+--echo # Reap XA ROLLBACK
+--reap
xa start 'test1';
insert into t3_trans values (1);
connection $con_aux1;
@@ -1616,7 +1644,27 @@ flush tables with read lock;
connection default;
connection default;
xa end 'test1';
-xa prepare 'test1';
+--echo # Send XA PREPARE 'test1'
+--send xa prepare 'test1'
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until XA PREPARE is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "xa prepare 'test1'";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap XA PREPARE.
+--reap
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+
--echo # Send:
--send xa commit 'test1';
connection $con_aux1;
@@ -1632,6 +1680,64 @@ connection default;
--reap
delete from t3_trans;
--echo #
+--echo # Check that XA COMMIT / ROLLBACK for prepared transaction from a
+--echo # disconnected session is blocked by active FTWRL in another connection.
+--echo #
+--echo # Create temporary connection for XA transaction.
+connect (con_tmp,localhost,root,,);
+xa start 'test1';
+insert into t3_trans values (1);
+xa end 'test1';
+xa prepare 'test1';
+--echo # Disconnect temporary connection
+disconnect con_tmp;
+--echo # Create temporary connection for XA transaction.
+connect (con_tmp,localhost,root,,);
+xa start 'test2';
+insert into t3_trans values (2);
+xa end 'test2';
+xa prepare 'test2';
+--echo # Disconnect temporary connection
+disconnect con_tmp;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Send XA ROLLBACK 'test1'
+--send xa rollback 'test1'
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until XA ROLLBACK is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "xa rollback 'test1'";
+--source include/wait_condition.inc
+unlock tables;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap XA ROLLBACK
+--reap
+--echo # Send XA COMMIT
+--send xa commit 'test2';
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until XA COMMIT is blocked.
+let $wait_condition=
+ select count(*) = 1 from information_schema.processlist
+ where state = "Waiting for commit lock" and
+ info = "xa commit 'test2'";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap XA COMMIT.
+--reap
+delete from t3_trans;
+
+--echo #
--echo # Check that XA COMMIT blocks FTWRL in another connection.
xa start 'test1';
insert into t3_trans values (1);
diff --git a/mysql-test/main/xa.result b/mysql-test/main/xa.result
index f77c0afdec5..62e9c9b669e 100644
--- a/mysql-test/main/xa.result
+++ b/mysql-test/main/xa.result
@@ -66,6 +66,7 @@ xa end 'tr1';
xa prepare 'tr1';
xa recover format='SQL';
formatID gtrid_length bqual_length data
+11 5 5 X'7465737462',X'2030405060',11
1 3 0 'tr1'
xa rollback 'tr1';
xa start 'tr1', 'bq';
@@ -75,6 +76,7 @@ xa prepare 'tr1', 'bq';
xa recover format='SQL';
formatID gtrid_length bqual_length data
1 3 2 'tr1','bq'
+11 5 5 X'7465737462',X'2030405060',11
xa rollback 'tr1', 'bq';
xa start 'tr1', 'bq', 3;
insert t1 values (40);
@@ -83,6 +85,7 @@ xa prepare 'tr1', 'bq', 3;
xa recover format='SQL';
formatID gtrid_length bqual_length data
3 3 2 'tr1','bq',3
+11 5 5 X'7465737462',X'2030405060',11
xa rollback 'tr1', 'bq', 3;
xa start 'tr1#$';
insert t1 values (40);
@@ -90,6 +93,7 @@ xa end 'tr1#$';
xa prepare 'tr1#$';
xa recover format='SQL';
formatID gtrid_length bqual_length data
+11 5 5 X'7465737462',X'2030405060',11
1 5 0 X'7472312324'
xa rollback 'tr1#$';
xa start 'tr1#$', 'bq';
@@ -98,6 +102,7 @@ xa end 'tr1#$', 'bq';
xa prepare 'tr1#$', 'bq';
xa recover format='SQL';
formatID gtrid_length bqual_length data
+11 5 5 X'7465737462',X'2030405060',11
1 5 2 X'7472312324',X'6271'
xa rollback 'tr1#$', 'bq';
xa start 'tr1#$', 'bq', 3;
@@ -106,11 +111,13 @@ xa end 'tr1#$', 'bq', 3;
xa prepare 'tr1#$', 'bq', 3;
xa recover format='RAW';
formatID gtrid_length bqual_length data
+11 5 5 testb 0@P`
3 5 2 tr1#$bq
xa recover format='PLAIN';
ERROR HY000: Unknown XA RECOVER format name: 'PLAIN'
xa recover format='SQL';
formatID gtrid_length bqual_length data
+11 5 5 X'7465737462',X'2030405060',11
3 5 2 X'7472312324',X'6271',3
xa rollback 'tr1#$', 'bq', 3;
drop table t1;
@@ -345,3 +352,66 @@ connection default;
XA END 'xid1';
XA ROLLBACK 'xid1';
DROP TABLE t1, t2, t3;
+xa rollback 'testb',0x2030405060,11;
+XA RECOVER;
+formatID gtrid_length bqual_length data
+# Check XA state when lock_wait_timeout happens
+# More tests added to flush_read_lock.test
+connect con_tmp,localhost,root,,;
+set session lock_wait_timeout=1;
+create table asd (a int);
+xa start 'test1';
+insert into asd values(1);
+xa end 'test1';
+connection default;
+flush table with read lock;
+connection con_tmp;
+# PREPARE error will do auto rollback.
+xa prepare 'test1';
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+show errors;
+Level Code Message
+Error 1205 Lock wait timeout exceeded; try restarting transaction
+Error 1402 XA_RBROLLBACK: Transaction branch was rolled back
+connection default;
+unlock tables;
+connection con_tmp;
+xa start 'test1';
+insert into asd values(1);
+xa end 'test1';
+xa prepare 'test1';
+connection default;
+flush tables with read lock;
+connection con_tmp;
+# LOCK error during ROLLBACK will not alter transaction state.
+xa rollback 'test1';
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+show errors;
+Level Code Message
+Error 1205 Lock wait timeout exceeded; try restarting transaction
+Error 1401 XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency
+xa recover;
+formatID gtrid_length bqual_length data
+1 5 0 test1
+# LOCK error during COMMIT will not alter transaction state.
+xa commit 'test1';
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+show errors;
+Level Code Message
+Error 1205 Lock wait timeout exceeded; try restarting transaction
+Error 1401 XAER_RMERR: Fatal error occurred in the transaction branch - check your data for consistency
+xa recover;
+formatID gtrid_length bqual_length data
+1 5 0 test1
+connection default;
+unlock tables;
+connection con_tmp;
+xa rollback 'test1';
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+xa recover;
+formatID gtrid_length bqual_length data
+drop table asd;
+disconnect con_tmp;
+connection default;
diff --git a/mysql-test/main/xa.test b/mysql-test/main/xa.test
index 176ef6aa760..6f56779ec17 100644
--- a/mysql-test/main/xa.test
+++ b/mysql-test/main/xa.test
@@ -468,7 +468,6 @@ REPLACE INTO t2 SELECT * FROM t2;
INSERT INTO t3 VALUES (1);
XA BEGIN 'xid3';
-
#Cleanup
--disconnect con2
--connection default
@@ -476,5 +475,53 @@ XA END 'xid1';
XA ROLLBACK 'xid1';
DROP TABLE t1, t2, t3;
+xa rollback 'testb',0x2030405060,11;
+XA RECOVER;
+
--source include/wait_until_count_sessions.inc
+--echo # Check XA state when lock_wait_timeout happens
+--echo # More tests added to flush_read_lock.test
+connect (con_tmp,localhost,root,,);
+set session lock_wait_timeout=1;
+create table asd (a int);
+xa start 'test1';
+insert into asd values(1);
+xa end 'test1';
+connection default;
+flush table with read lock;
+connection con_tmp;
+--echo # PREPARE error will do auto rollback.
+--ERROR ER_LOCK_WAIT_TIMEOUT
+xa prepare 'test1';
+show errors;
+connection default;
+unlock tables;
+
+connection con_tmp;
+xa start 'test1';
+insert into asd values(1);
+xa end 'test1';
+xa prepare 'test1';
+connection default;
+flush tables with read lock;
+connection con_tmp;
+--echo # LOCK error during ROLLBACK will not alter transaction state.
+--ERROR ER_LOCK_WAIT_TIMEOUT
+xa rollback 'test1';
+show errors;
+xa recover;
+--echo # LOCK error during COMMIT will not alter transaction state.
+--ERROR ER_LOCK_WAIT_TIMEOUT
+xa commit 'test1';
+show errors;
+xa recover;
+connection default;
+unlock tables;
+connection con_tmp;
+xa rollback 'test1';
+xa recover;
+drop table asd;
+disconnect con_tmp;
+--source include/wait_until_disconnected.inc
+connection default;
diff --git a/mysql-test/suite/binlog/include/binlog_xa_prepare_connection.inc b/mysql-test/suite/binlog/include/binlog_xa_prepare_connection.inc
new file mode 100644
index 00000000000..c0041af1e7f
--- /dev/null
+++ b/mysql-test/suite/binlog/include/binlog_xa_prepare_connection.inc
@@ -0,0 +1,31 @@
+#
+# This file initiate connections to run XA transactions up to
+# their prepare.
+# Connection name, transaction name and its content depends on
+# supplied parameters.
+#
+# param $type type of transaction
+# param $index index identifies the connection with those of type $type
+# param $sql_init1 a query to execute once connection is established
+# param $sql_init2 a query to execute once connection is established
+# param $sql_doit a query to execute inside transaction
+# Note, the query may depend on tables created by caller
+#
+
+--connect (conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,)
+if ($sql_init1)
+{
+ --eval $sql_init1
+}
+if ($sql_init2)
+{
+ --eval $sql_init2
+}
+
+--eval XA START 'trx$index$type'
+if ($sql_doit)
+{
+ --eval $sql_doit
+}
+--eval XA END 'trx$index$type'
+--eval XA PREPARE 'trx$index$type'
diff --git a/mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc b/mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc
new file mode 100644
index 00000000000..1f6ce713cc9
--- /dev/null
+++ b/mysql-test/suite/binlog/include/binlog_xa_prepare_disconnect.inc
@@ -0,0 +1,35 @@
+#
+# This file disconnects two connections. One actively and one through
+# kill. It is included by binlog_xa_prepared_do_and_restart.
+#
+# param $type type of transaction
+# param $terminate_with how to conclude actively disconnecte:
+# XA COMMIT or XA ROLLBACK
+# param $conn3_id connection id of the being killed.
+# param $num_trx_prepared number of transactions prepared so far
+#
+--connection default
+
+--echo *** $num_trx_prepared prepared transactions must be in the list ***
+--replace_column 2 LEN1 3 LEN2 4 TRX_N
+XA RECOVER;
+
+--connection conn1$type
+--let $conn1_id=`SELECT connection_id()`
+--disconnect conn1$type
+
+--connection default
+--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn1_id
+--source include/wait_condition.inc
+
+# It will conclude now
+--eval $terminate_with 'trx1$type'
+
+--replace_result $conn3_id CONN_ID
+--eval KILL connection $conn3_id
+
+--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn3_id
+--source include/wait_condition.inc
+
+# It will conclude now
+--eval $terminate_with 'trx3$type'
diff --git a/mysql-test/suite/binlog/include/binlog_xa_prepared_do_and_restart.inc b/mysql-test/suite/binlog/include/binlog_xa_prepared_do_and_restart.inc
new file mode 100644
index 00000000000..bfda185603b
--- /dev/null
+++ b/mysql-test/suite/binlog/include/binlog_xa_prepared_do_and_restart.inc
@@ -0,0 +1,323 @@
+#
+# This file creates various kinds of prepared XA transactions,
+# manipulates their connection state and examines how their prepared
+# status behave while the transaction is disconnected, killed or
+# the server kisses it shutdown.
+# The file can be sourced multiple times
+# param $restart_number (as the number of inclusion) adjusts
+# verification logics.
+#
+# param [in] $conn_number Total number of connection each performing
+# one insert into table.
+# param [in] $commit_number Number of commits from either.
+# side of the server restart.
+# param [in] $rollback_number The same as the above just for rollback.
+# param [in] $term_number Number of transaction that are terminated
+# before server restarts
+# param [in] $killed_number Instead of disconnect make some
+# connections killed when their
+# transactions got prepared.
+# param [in] $server_disconn_number Make some connections disconnected
+# by shutdown rather than actively
+# param [in] $post_restart_conn_number Number a "warmup" connection
+# after server restart, they all commit
+# param [out] restart_number Counter to be incremented at the end of the test
+#
+
+# The test consists of three sections:
+# I. Corner cases check
+# II. Regular case check
+# III. Post server-restart verification
+
+
+#
+# I. Corner cases of
+#
+# A. XA with an update to a temp table
+# B. XA with SELECT
+# C. XA empty
+# Demonstrate their XA status upon prepare and how they react on disconnect and
+# shutdown.
+# In each of A,B,C three prepared transactions are set up.
+# trx1 is for disconnection, trx2 for shutdown, trx3 for being killed.
+# The A case additionally contains some XA prohibited state transaction check.
+#
+# D. Prove that not prepared XA remains to be cleared out by disconnection.
+#
+
+#
+# A. The temp table only prepared XA recovers only formally to
+# let post recovery XA COMMIT or XA ROLLBACK with no effect.
+
+--let $type = tmp
+--let $index = 1
+--let $sql_init1 = SET @@sql_log_bin = OFF
+--let $sql_init2 = CREATE TEMPORARY TABLE tmp$index (a int) ENGINE=innodb
+--let $sql_doit = INSERT INTO tmp$index SET a=$index
+--source include/binlog_xa_prepare_connection.inc
+
+--let $index = 2
+--source include/binlog_xa_prepare_connection.inc
+
+--let $index = 3
+--source include/binlog_xa_prepare_connection.inc
+--let $conn3_id=`SELECT connection_id()`
+
+#
+# Various prohibited XA state changes to test here:
+#
+
+--connection default
+# Stealing is not allowed
+--error ER_XAER_NOTA
+--eval XA COMMIT 'trx1$type'
+--error ER_XAER_NOTA
+--eval XA ROLLBACK 'trx1$type'
+
+# Before disconnect: creating a duplicate is not allowed
+--error ER_XAER_DUPID
+--eval XA START 'trx1$type'
+
+# Manipulate now the prepared transactions.
+# Two to terminate, one to leave out.
+--let $terminate_with = XA COMMIT
+--let $num_trx_prepared = $index
+--source include/binlog_xa_prepare_disconnect.inc
+
+#
+# B. "Read-only" (select) prepared XA recovers only formally to
+# let post recovery XA COMMIT or XA ROLLBACK with no effect.
+#
+--let $type=ro
+--let $index = 1
+--let $sql_init1 =
+--let $sql_init2 =
+--let $sql_doit = SELECT * from t ORDER BY a
+--source include/binlog_xa_prepare_connection.inc
+
+--let $index = 2
+--source include/binlog_xa_prepare_connection.inc
+
+--let $index = 3
+--source include/binlog_xa_prepare_connection.inc
+--let $conn3_id=`SELECT connection_id()`
+
+--let $terminate_with = XA ROLLBACK
+# two three above section prepared transaction were terminated.
+--inc $num_trx_prepared
+--source include/binlog_xa_prepare_disconnect.inc
+
+#
+# C. Empty prepared XA recovers only formally to
+# let post recovery XA COMMIT or XA ROLLBACK with no effect.
+#
+--let $type=empty
+--let $index = 1
+--let $sql_init1 =
+--let $sql_init2 =
+--let $sql_doit =
+--source include/binlog_xa_prepare_connection.inc
+
+--let $index = 2
+--source include/binlog_xa_prepare_connection.inc
+
+--let $index = 3
+--source include/binlog_xa_prepare_connection.inc
+--let $conn3_id=`SELECT connection_id()`
+
+--let $terminate_with = XA COMMIT
+--inc $num_trx_prepared
+--source include/binlog_xa_prepare_disconnect.inc
+
+#
+# D. Not prepared XA disconnects to be cleared out,
+# no effect on data left as well.
+# Few more prohibited XA state transactions is checked out.
+#
+--let $type=unprepared
+--let $prev_count=`SELECT count(*) from t`
+
+--connect(conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,)
+--eval XA START 'trx1$type'
+INSERT INTO t set a=0;
+--eval XA END 'trx1$type'
+
+--error ER_XAER_RMFAIL
+INSERT INTO t set a=0;
+--error ER_XAER_RMFAIL
+--eval XA START 'trx1$type'
+--error ER_XAER_RMFAIL
+--eval XA START 'trx1$type'
+
+--disconnect conn1$type
+
+--connection default
+# No such transactions
+--error ER_XAER_NOTA
+--eval XA COMMIT 'trx1$type'
+if (`SELECT count(*) > $prev_count from t`)
+{
+ --echo *** Unexpected commit to the table. ***
+ --die
+}
+
+#
+# II. Regular case.
+#
+# Prepared transactions get disconnected in three ways:
+# actively, being killed and by the server shutdown.
+#
+--let $i=0
+while ($i < $conn_number)
+{
+ --connect (conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,)
+ --let $conn_id=`SELECT connection_id()`
+ --disable_reconnect
+ SET @@binlog_format = STATEMENT;
+ if (`SELECT $i % 2`)
+ {
+ SET @@binlog_format = ROW;
+ }
+ --eval XA START 'trx_$i'
+ --eval INSERT INTO t SET a=$i
+ --eval XA END 'trx_$i'
+ --eval XA PREPARE 'trx_$i'
+
+ --let $disc_via_kill=`SELECT $conn_number - $i <= $killed_number`
+ if (!$disc_via_kill)
+ {
+ --let $disc_via_shutdown=`SELECT $conn_number - $i <= $killed_number + $server_disconn_number`
+ if (!$disc_via_shutdown)
+ {
+ --disconnect conn$i
+ }
+ }
+ if ($disc_via_kill)
+ {
+ --connection default
+ --replace_result $conn_id CONN_ID
+ --eval KILL CONNECTION $conn_id
+ }
+
+ if (!$disc_via_shutdown)
+ {
+ --connection default
+ --let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
+ --source include/wait_condition.inc
+ }
+ --inc $i
+}
+
+# [0, $rollback_number - 1] are rolled back now
+--connection default
+
+--let $i=0
+while ($i < $rollback_number)
+{
+ --eval XA ROLLBACK 'trx_$i'
+
+ --inc $i
+}
+
+# [$rollback_number, $rollback_number + $commit_number - 1] get committed
+while ($i < $term_number)
+{
+ --eval XA COMMIT 'trx_$i'
+
+ --inc $i
+}
+
+--source include/$how_to_restart
+
+#
+# III. Post server-restart verification.
+# It concludes survived XA:s with a number of commits and rollbacks
+# as configured in the 1st part to check expected results in the end.
+# Cleanup section consists of explicit disconnect (for killed, or
+# not disconnected before shutdown).
+#
+
+# New XA can be prepared and committed
+--let $k = 0
+while ($k < $post_restart_conn_number)
+{
+ --connect (conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,)
+ --let $conn_id=`SELECT connection_id()`
+ --eval XA START 'new_trx_$k'
+ --eval INSERT INTO t SET a=$k
+ --eval XA END 'new_trx_$k'
+ --eval XA PREPARE 'new_trx_$k'
+
+ --disconnect conn_restart_$k
+
+ --connection default
+ --let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
+ --source include/wait_condition.inc
+
+ --inc $k
+}
+
+--connection default
+--let $k = 0
+while ($k < $post_restart_conn_number)
+{
+ --eval XA COMMIT 'new_trx_$k'
+ --inc $k
+}
+
+#
+# Symmetrically to the pre-restart, the resurrected trx:s are committed
+# [$term_number, $term_number + $commit_number - 1]
+# and the rest is rolled back.
+#
+--let $i = $term_number
+
+while ($i < `SELECT $term_number + $commit_number`)
+{
+ # Expected to fail
+ --error ER_XAER_DUPID
+ --eval XA START 'trx_$i'
+ --eval XA COMMIT 'trx_$i'
+ --inc $i
+}
+
+while ($i < $conn_number)
+{
+ # Expected to fail
+ --error ER_XAER_DUPID
+ --eval XA START 'trx_$i'
+ --eval XA ROLLBACK 'trx_$i'
+ --inc $i
+}
+
+#
+# Verification of correct results of recovered XA transaction handling:
+#
+SELECT * FROM t;
+
+--let $type=tmp
+--disconnect conn2$type
+--disconnect conn3$type
+--let $type=ro
+--disconnect conn2$type
+--disconnect conn3$type
+--let $type=empty
+--disconnect conn2$type
+--disconnect conn3$type
+
+--let $i= $conn_number
+--let $k= 0
+--let $expl_disconn_number = `SELECT $killed_number + $server_disconn_number`
+while ($k < $expl_disconn_number)
+{
+ --connection default
+ --error ER_XAER_NOTA
+ --eval XA ROLLBACK 'trx_$i'
+
+ --dec $i
+ --disconnect conn$i
+
+ --inc $k
+}
+
+--inc $restart_number
diff --git a/mysql-test/suite/binlog/r/binlog_xa_prepared.result b/mysql-test/suite/binlog/r/binlog_xa_prepared.result
new file mode 100644
index 00000000000..9635e02081f
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_xa_prepared.result
@@ -0,0 +1,1042 @@
+connection default;
+CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
+call mtr.add_suppression("Found 10 prepared XA transactions");
+CREATE TABLE t (a INT) ENGINE=innodb;
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx1tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx1tmp';
+XA PREPARE 'trx1tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx2tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx2tmp';
+XA PREPARE 'trx2tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx3tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx3tmp';
+XA PREPARE 'trx3tmp';
+connection default;
+XA COMMIT 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA ROLLBACK 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA START 'trx1tmp';
+ERROR XAE08: XAER_DUPID: The XID already exists
+connection default;
+*** 3 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1tmp;
+disconnect conn1tmp;
+connection default;
+XA COMMIT 'trx1tmp';
+KILL connection CONN_ID;
+XA COMMIT 'trx3tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1ro';
+SELECT * from t ORDER BY a;
+a
+XA END 'trx1ro';
+XA PREPARE 'trx1ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2ro';
+SELECT * from t ORDER BY a;
+a
+XA END 'trx2ro';
+XA PREPARE 'trx2ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3ro';
+SELECT * from t ORDER BY a;
+a
+XA END 'trx3ro';
+XA PREPARE 'trx3ro';
+connection default;
+*** 4 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1ro;
+disconnect conn1ro;
+connection default;
+XA ROLLBACK 'trx1ro';
+KILL connection CONN_ID;
+XA ROLLBACK 'trx3ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1empty';
+XA END 'trx1empty';
+XA PREPARE 'trx1empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2empty';
+XA END 'trx2empty';
+XA PREPARE 'trx2empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3empty';
+XA END 'trx3empty';
+XA PREPARE 'trx3empty';
+connection default;
+*** 5 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1empty;
+disconnect conn1empty;
+connection default;
+XA COMMIT 'trx1empty';
+KILL connection CONN_ID;
+XA COMMIT 'trx3empty';
+connect conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1unprepared';
+INSERT INTO t set a=0;
+XA END 'trx1unprepared';
+INSERT INTO t set a=0;
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+disconnect conn1unprepared;
+connection default;
+XA COMMIT 'trx1unprepared';
+ERROR XAE04: XAER_NOTA: Unknown XID
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_0';
+INSERT INTO t SET a=0;
+XA END 'trx_0';
+XA PREPARE 'trx_0';
+disconnect conn0;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_1';
+INSERT INTO t SET a=1;
+XA END 'trx_1';
+XA PREPARE 'trx_1';
+disconnect conn1;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_2';
+INSERT INTO t SET a=2;
+XA END 'trx_2';
+XA PREPARE 'trx_2';
+disconnect conn2;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_3';
+INSERT INTO t SET a=3;
+XA END 'trx_3';
+XA PREPARE 'trx_3';
+disconnect conn3;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_4';
+INSERT INTO t SET a=4;
+XA END 'trx_4';
+XA PREPARE 'trx_4';
+disconnect conn4;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_5';
+INSERT INTO t SET a=5;
+XA END 'trx_5';
+XA PREPARE 'trx_5';
+disconnect conn5;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_6';
+INSERT INTO t SET a=6;
+XA END 'trx_6';
+XA PREPARE 'trx_6';
+disconnect conn6;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_7';
+INSERT INTO t SET a=7;
+XA END 'trx_7';
+XA PREPARE 'trx_7';
+disconnect conn7;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_8';
+INSERT INTO t SET a=8;
+XA END 'trx_8';
+XA PREPARE 'trx_8';
+disconnect conn8;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_9';
+INSERT INTO t SET a=9;
+XA END 'trx_9';
+XA PREPARE 'trx_9';
+disconnect conn9;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_10';
+INSERT INTO t SET a=10;
+XA END 'trx_10';
+XA PREPARE 'trx_10';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_11';
+INSERT INTO t SET a=11;
+XA END 'trx_11';
+XA PREPARE 'trx_11';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_12';
+INSERT INTO t SET a=12;
+XA END 'trx_12';
+XA PREPARE 'trx_12';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_13';
+INSERT INTO t SET a=13;
+XA END 'trx_13';
+XA PREPARE 'trx_13';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_14';
+INSERT INTO t SET a=14;
+XA END 'trx_14';
+XA PREPARE 'trx_14';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_15';
+INSERT INTO t SET a=15;
+XA END 'trx_15';
+XA PREPARE 'trx_15';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_16';
+INSERT INTO t SET a=16;
+XA END 'trx_16';
+XA PREPARE 'trx_16';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_17';
+INSERT INTO t SET a=17;
+XA END 'trx_17';
+XA PREPARE 'trx_17';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_18';
+INSERT INTO t SET a=18;
+XA END 'trx_18';
+XA PREPARE 'trx_18';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_19';
+INSERT INTO t SET a=19;
+XA END 'trx_19';
+XA PREPARE 'trx_19';
+connection default;
+KILL CONNECTION CONN_ID;
+connection default;
+XA ROLLBACK 'trx_0';
+XA ROLLBACK 'trx_1';
+XA ROLLBACK 'trx_2';
+XA ROLLBACK 'trx_3';
+XA ROLLBACK 'trx_4';
+XA COMMIT 'trx_5';
+XA COMMIT 'trx_6';
+XA COMMIT 'trx_7';
+XA COMMIT 'trx_8';
+XA COMMIT 'trx_9';
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_0';
+INSERT INTO t SET a=0;
+XA END 'new_trx_0';
+XA PREPARE 'new_trx_0';
+disconnect conn_restart_0;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_1';
+INSERT INTO t SET a=1;
+XA END 'new_trx_1';
+XA PREPARE 'new_trx_1';
+disconnect conn_restart_1;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_2';
+INSERT INTO t SET a=2;
+XA END 'new_trx_2';
+XA PREPARE 'new_trx_2';
+disconnect conn_restart_2;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_3';
+INSERT INTO t SET a=3;
+XA END 'new_trx_3';
+XA PREPARE 'new_trx_3';
+disconnect conn_restart_3;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_4';
+INSERT INTO t SET a=4;
+XA END 'new_trx_4';
+XA PREPARE 'new_trx_4';
+disconnect conn_restart_4;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_5';
+INSERT INTO t SET a=5;
+XA END 'new_trx_5';
+XA PREPARE 'new_trx_5';
+disconnect conn_restart_5;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_6';
+INSERT INTO t SET a=6;
+XA END 'new_trx_6';
+XA PREPARE 'new_trx_6';
+disconnect conn_restart_6;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_7';
+INSERT INTO t SET a=7;
+XA END 'new_trx_7';
+XA PREPARE 'new_trx_7';
+disconnect conn_restart_7;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_8';
+INSERT INTO t SET a=8;
+XA END 'new_trx_8';
+XA PREPARE 'new_trx_8';
+disconnect conn_restart_8;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_9';
+INSERT INTO t SET a=9;
+XA END 'new_trx_9';
+XA PREPARE 'new_trx_9';
+disconnect conn_restart_9;
+connection default;
+connection default;
+XA COMMIT 'new_trx_0';
+XA COMMIT 'new_trx_1';
+XA COMMIT 'new_trx_2';
+XA COMMIT 'new_trx_3';
+XA COMMIT 'new_trx_4';
+XA COMMIT 'new_trx_5';
+XA COMMIT 'new_trx_6';
+XA COMMIT 'new_trx_7';
+XA COMMIT 'new_trx_8';
+XA COMMIT 'new_trx_9';
+XA START 'trx_10';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_10';
+XA START 'trx_11';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_11';
+XA START 'trx_12';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_12';
+XA START 'trx_13';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_13';
+XA START 'trx_14';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_14';
+XA START 'trx_15';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_15';
+XA START 'trx_16';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_16';
+XA START 'trx_17';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_17';
+XA START 'trx_18';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_18';
+XA START 'trx_19';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_19';
+SELECT * FROM t;
+a
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+disconnect conn2tmp;
+disconnect conn3tmp;
+disconnect conn2ro;
+disconnect conn3ro;
+disconnect conn2empty;
+disconnect conn3empty;
+connection default;
+XA ROLLBACK 'trx_20';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn19;
+connection default;
+XA ROLLBACK 'trx_19';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn18;
+connection default;
+XA ROLLBACK 'trx_18';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn17;
+connection default;
+XA ROLLBACK 'trx_17';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn16;
+connection default;
+XA ROLLBACK 'trx_16';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn15;
+connection default;
+XA ROLLBACK 'trx_15';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn14;
+connection default;
+XA ROLLBACK 'trx_14';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn13;
+connection default;
+XA ROLLBACK 'trx_13';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn12;
+connection default;
+XA ROLLBACK 'trx_12';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn11;
+connection default;
+XA ROLLBACK 'trx_11';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn10;
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx1tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx1tmp';
+XA PREPARE 'trx1tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx2tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx2tmp';
+XA PREPARE 'trx2tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx3tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx3tmp';
+XA PREPARE 'trx3tmp';
+connection default;
+XA COMMIT 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA ROLLBACK 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA START 'trx1tmp';
+ERROR XAE08: XAER_DUPID: The XID already exists
+connection default;
+*** 3 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1tmp;
+disconnect conn1tmp;
+connection default;
+XA COMMIT 'trx1tmp';
+KILL connection CONN_ID;
+XA COMMIT 'trx3tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1ro';
+SELECT * from t ORDER BY a;
+a
+0
+1
+2
+3
+4
+5
+5
+6
+6
+7
+7
+8
+8
+9
+9
+10
+11
+12
+13
+14
+XA END 'trx1ro';
+XA PREPARE 'trx1ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2ro';
+SELECT * from t ORDER BY a;
+a
+0
+1
+2
+3
+4
+5
+5
+6
+6
+7
+7
+8
+8
+9
+9
+10
+11
+12
+13
+14
+XA END 'trx2ro';
+XA PREPARE 'trx2ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3ro';
+SELECT * from t ORDER BY a;
+a
+0
+1
+2
+3
+4
+5
+5
+6
+6
+7
+7
+8
+8
+9
+9
+10
+11
+12
+13
+14
+XA END 'trx3ro';
+XA PREPARE 'trx3ro';
+connection default;
+*** 4 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1ro;
+disconnect conn1ro;
+connection default;
+XA ROLLBACK 'trx1ro';
+KILL connection CONN_ID;
+XA ROLLBACK 'trx3ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1empty';
+XA END 'trx1empty';
+XA PREPARE 'trx1empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2empty';
+XA END 'trx2empty';
+XA PREPARE 'trx2empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3empty';
+XA END 'trx3empty';
+XA PREPARE 'trx3empty';
+connection default;
+*** 5 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1empty;
+disconnect conn1empty;
+connection default;
+XA COMMIT 'trx1empty';
+KILL connection CONN_ID;
+XA COMMIT 'trx3empty';
+connect conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1unprepared';
+INSERT INTO t set a=0;
+XA END 'trx1unprepared';
+INSERT INTO t set a=0;
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+disconnect conn1unprepared;
+connection default;
+XA COMMIT 'trx1unprepared';
+ERROR XAE04: XAER_NOTA: Unknown XID
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_0';
+INSERT INTO t SET a=0;
+XA END 'trx_0';
+XA PREPARE 'trx_0';
+disconnect conn0;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_1';
+INSERT INTO t SET a=1;
+XA END 'trx_1';
+XA PREPARE 'trx_1';
+disconnect conn1;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_2';
+INSERT INTO t SET a=2;
+XA END 'trx_2';
+XA PREPARE 'trx_2';
+disconnect conn2;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_3';
+INSERT INTO t SET a=3;
+XA END 'trx_3';
+XA PREPARE 'trx_3';
+disconnect conn3;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_4';
+INSERT INTO t SET a=4;
+XA END 'trx_4';
+XA PREPARE 'trx_4';
+disconnect conn4;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_5';
+INSERT INTO t SET a=5;
+XA END 'trx_5';
+XA PREPARE 'trx_5';
+disconnect conn5;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_6';
+INSERT INTO t SET a=6;
+XA END 'trx_6';
+XA PREPARE 'trx_6';
+disconnect conn6;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_7';
+INSERT INTO t SET a=7;
+XA END 'trx_7';
+XA PREPARE 'trx_7';
+disconnect conn7;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_8';
+INSERT INTO t SET a=8;
+XA END 'trx_8';
+XA PREPARE 'trx_8';
+disconnect conn8;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_9';
+INSERT INTO t SET a=9;
+XA END 'trx_9';
+XA PREPARE 'trx_9';
+disconnect conn9;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_10';
+INSERT INTO t SET a=10;
+XA END 'trx_10';
+XA PREPARE 'trx_10';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_11';
+INSERT INTO t SET a=11;
+XA END 'trx_11';
+XA PREPARE 'trx_11';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_12';
+INSERT INTO t SET a=12;
+XA END 'trx_12';
+XA PREPARE 'trx_12';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_13';
+INSERT INTO t SET a=13;
+XA END 'trx_13';
+XA PREPARE 'trx_13';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_14';
+INSERT INTO t SET a=14;
+XA END 'trx_14';
+XA PREPARE 'trx_14';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_15';
+INSERT INTO t SET a=15;
+XA END 'trx_15';
+XA PREPARE 'trx_15';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_16';
+INSERT INTO t SET a=16;
+XA END 'trx_16';
+XA PREPARE 'trx_16';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_17';
+INSERT INTO t SET a=17;
+XA END 'trx_17';
+XA PREPARE 'trx_17';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_18';
+INSERT INTO t SET a=18;
+XA END 'trx_18';
+XA PREPARE 'trx_18';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_19';
+INSERT INTO t SET a=19;
+XA END 'trx_19';
+XA PREPARE 'trx_19';
+connection default;
+KILL CONNECTION CONN_ID;
+connection default;
+XA ROLLBACK 'trx_0';
+XA ROLLBACK 'trx_1';
+XA ROLLBACK 'trx_2';
+XA ROLLBACK 'trx_3';
+XA ROLLBACK 'trx_4';
+XA COMMIT 'trx_5';
+XA COMMIT 'trx_6';
+XA COMMIT 'trx_7';
+XA COMMIT 'trx_8';
+XA COMMIT 'trx_9';
+# Kill and restart
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_0';
+INSERT INTO t SET a=0;
+XA END 'new_trx_0';
+XA PREPARE 'new_trx_0';
+disconnect conn_restart_0;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_1';
+INSERT INTO t SET a=1;
+XA END 'new_trx_1';
+XA PREPARE 'new_trx_1';
+disconnect conn_restart_1;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_2';
+INSERT INTO t SET a=2;
+XA END 'new_trx_2';
+XA PREPARE 'new_trx_2';
+disconnect conn_restart_2;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_3';
+INSERT INTO t SET a=3;
+XA END 'new_trx_3';
+XA PREPARE 'new_trx_3';
+disconnect conn_restart_3;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_4';
+INSERT INTO t SET a=4;
+XA END 'new_trx_4';
+XA PREPARE 'new_trx_4';
+disconnect conn_restart_4;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_5';
+INSERT INTO t SET a=5;
+XA END 'new_trx_5';
+XA PREPARE 'new_trx_5';
+disconnect conn_restart_5;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_6';
+INSERT INTO t SET a=6;
+XA END 'new_trx_6';
+XA PREPARE 'new_trx_6';
+disconnect conn_restart_6;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_7';
+INSERT INTO t SET a=7;
+XA END 'new_trx_7';
+XA PREPARE 'new_trx_7';
+disconnect conn_restart_7;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_8';
+INSERT INTO t SET a=8;
+XA END 'new_trx_8';
+XA PREPARE 'new_trx_8';
+disconnect conn_restart_8;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_9';
+INSERT INTO t SET a=9;
+XA END 'new_trx_9';
+XA PREPARE 'new_trx_9';
+disconnect conn_restart_9;
+connection default;
+connection default;
+XA COMMIT 'new_trx_0';
+XA COMMIT 'new_trx_1';
+XA COMMIT 'new_trx_2';
+XA COMMIT 'new_trx_3';
+XA COMMIT 'new_trx_4';
+XA COMMIT 'new_trx_5';
+XA COMMIT 'new_trx_6';
+XA COMMIT 'new_trx_7';
+XA COMMIT 'new_trx_8';
+XA COMMIT 'new_trx_9';
+XA START 'trx_10';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_10';
+XA START 'trx_11';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_11';
+XA START 'trx_12';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_12';
+XA START 'trx_13';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_13';
+XA START 'trx_14';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_14';
+XA START 'trx_15';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_15';
+XA START 'trx_16';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_16';
+XA START 'trx_17';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_17';
+XA START 'trx_18';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_18';
+XA START 'trx_19';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_19';
+SELECT * FROM t;
+a
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+disconnect conn2tmp;
+disconnect conn3tmp;
+disconnect conn2ro;
+disconnect conn3ro;
+disconnect conn2empty;
+disconnect conn3empty;
+connection default;
+XA ROLLBACK 'trx_20';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn19;
+connection default;
+XA ROLLBACK 'trx_19';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn18;
+connection default;
+XA ROLLBACK 'trx_18';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn17;
+connection default;
+XA ROLLBACK 'trx_17';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn16;
+connection default;
+XA ROLLBACK 'trx_16';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn15;
+connection default;
+XA ROLLBACK 'trx_15';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn14;
+connection default;
+XA ROLLBACK 'trx_14';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn13;
+connection default;
+XA ROLLBACK 'trx_13';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn12;
+connection default;
+XA ROLLBACK 'trx_12';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn11;
+connection default;
+XA ROLLBACK 'trx_11';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn10;
+connection default;
+XA START 'one_phase_trx_0';
+INSERT INTO t SET a=0;
+XA END 'one_phase_trx_0';
+XA COMMIT 'one_phase_trx_0' ONE PHASE;
+XA START 'one_phase_trx_1';
+INSERT INTO t SET a=1;
+XA END 'one_phase_trx_1';
+XA COMMIT 'one_phase_trx_1' ONE PHASE;
+XA START 'one_phase_trx_2';
+INSERT INTO t SET a=2;
+XA END 'one_phase_trx_2';
+XA COMMIT 'one_phase_trx_2' ONE PHASE;
+XA START 'one_phase_trx_3';
+INSERT INTO t SET a=3;
+XA END 'one_phase_trx_3';
+XA COMMIT 'one_phase_trx_3' ONE PHASE;
+XA START 'one_phase_trx_4';
+INSERT INTO t SET a=4;
+XA END 'one_phase_trx_4';
+XA COMMIT 'one_phase_trx_4' ONE PHASE;
+SELECT SUM(a) FROM t;
+SUM(a)
+290
+DROP TABLE t;
+DROP VIEW v_processlist;
+All transactions must be completed, to empty-list the following:
+XA RECOVER;
+formatID gtrid_length bqual_length data
diff --git a/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result b/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result
new file mode 100644
index 00000000000..67eb0896696
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result
@@ -0,0 +1,1175 @@
+connection default;
+RESET MASTER;
+CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
+call mtr.add_suppression("Found 10 prepared XA transactions");
+CREATE TABLE t (a INT) ENGINE=innodb;
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx1tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx1tmp';
+XA PREPARE 'trx1tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx2tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx2tmp';
+XA PREPARE 'trx2tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx3tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx3tmp';
+XA PREPARE 'trx3tmp';
+connection default;
+XA COMMIT 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA ROLLBACK 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA START 'trx1tmp';
+ERROR XAE08: XAER_DUPID: The XID already exists
+connection default;
+*** 3 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1tmp;
+disconnect conn1tmp;
+connection default;
+XA COMMIT 'trx1tmp';
+KILL connection CONN_ID;
+XA COMMIT 'trx3tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1ro';
+SELECT * from t ORDER BY a;
+a
+XA END 'trx1ro';
+XA PREPARE 'trx1ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2ro';
+SELECT * from t ORDER BY a;
+a
+XA END 'trx2ro';
+XA PREPARE 'trx2ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3ro';
+SELECT * from t ORDER BY a;
+a
+XA END 'trx3ro';
+XA PREPARE 'trx3ro';
+connection default;
+*** 4 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1ro;
+disconnect conn1ro;
+connection default;
+XA ROLLBACK 'trx1ro';
+KILL connection CONN_ID;
+XA ROLLBACK 'trx3ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1empty';
+XA END 'trx1empty';
+XA PREPARE 'trx1empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2empty';
+XA END 'trx2empty';
+XA PREPARE 'trx2empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3empty';
+XA END 'trx3empty';
+XA PREPARE 'trx3empty';
+connection default;
+*** 5 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1empty;
+disconnect conn1empty;
+connection default;
+XA COMMIT 'trx1empty';
+KILL connection CONN_ID;
+XA COMMIT 'trx3empty';
+connect conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1unprepared';
+INSERT INTO t set a=0;
+XA END 'trx1unprepared';
+INSERT INTO t set a=0;
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+disconnect conn1unprepared;
+connection default;
+XA COMMIT 'trx1unprepared';
+ERROR XAE04: XAER_NOTA: Unknown XID
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_0';
+INSERT INTO t SET a=0;
+XA END 'trx_0';
+XA PREPARE 'trx_0';
+disconnect conn0;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_1';
+INSERT INTO t SET a=1;
+XA END 'trx_1';
+XA PREPARE 'trx_1';
+disconnect conn1;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_2';
+INSERT INTO t SET a=2;
+XA END 'trx_2';
+XA PREPARE 'trx_2';
+disconnect conn2;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_3';
+INSERT INTO t SET a=3;
+XA END 'trx_3';
+XA PREPARE 'trx_3';
+disconnect conn3;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_4';
+INSERT INTO t SET a=4;
+XA END 'trx_4';
+XA PREPARE 'trx_4';
+disconnect conn4;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_5';
+INSERT INTO t SET a=5;
+XA END 'trx_5';
+XA PREPARE 'trx_5';
+disconnect conn5;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_6';
+INSERT INTO t SET a=6;
+XA END 'trx_6';
+XA PREPARE 'trx_6';
+disconnect conn6;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_7';
+INSERT INTO t SET a=7;
+XA END 'trx_7';
+XA PREPARE 'trx_7';
+disconnect conn7;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_8';
+INSERT INTO t SET a=8;
+XA END 'trx_8';
+XA PREPARE 'trx_8';
+disconnect conn8;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_9';
+INSERT INTO t SET a=9;
+XA END 'trx_9';
+XA PREPARE 'trx_9';
+disconnect conn9;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_10';
+INSERT INTO t SET a=10;
+XA END 'trx_10';
+XA PREPARE 'trx_10';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_11';
+INSERT INTO t SET a=11;
+XA END 'trx_11';
+XA PREPARE 'trx_11';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_12';
+INSERT INTO t SET a=12;
+XA END 'trx_12';
+XA PREPARE 'trx_12';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_13';
+INSERT INTO t SET a=13;
+XA END 'trx_13';
+XA PREPARE 'trx_13';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_14';
+INSERT INTO t SET a=14;
+XA END 'trx_14';
+XA PREPARE 'trx_14';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_15';
+INSERT INTO t SET a=15;
+XA END 'trx_15';
+XA PREPARE 'trx_15';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_16';
+INSERT INTO t SET a=16;
+XA END 'trx_16';
+XA PREPARE 'trx_16';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_17';
+INSERT INTO t SET a=17;
+XA END 'trx_17';
+XA PREPARE 'trx_17';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_18';
+INSERT INTO t SET a=18;
+XA END 'trx_18';
+XA PREPARE 'trx_18';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_19';
+INSERT INTO t SET a=19;
+XA END 'trx_19';
+XA PREPARE 'trx_19';
+connection default;
+KILL CONNECTION CONN_ID;
+connection default;
+XA ROLLBACK 'trx_0';
+XA ROLLBACK 'trx_1';
+XA ROLLBACK 'trx_2';
+XA ROLLBACK 'trx_3';
+XA ROLLBACK 'trx_4';
+XA COMMIT 'trx_5';
+XA COMMIT 'trx_6';
+XA COMMIT 'trx_7';
+XA COMMIT 'trx_8';
+XA COMMIT 'trx_9';
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_0';
+INSERT INTO t SET a=0;
+XA END 'new_trx_0';
+XA PREPARE 'new_trx_0';
+disconnect conn_restart_0;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_1';
+INSERT INTO t SET a=1;
+XA END 'new_trx_1';
+XA PREPARE 'new_trx_1';
+disconnect conn_restart_1;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_2';
+INSERT INTO t SET a=2;
+XA END 'new_trx_2';
+XA PREPARE 'new_trx_2';
+disconnect conn_restart_2;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_3';
+INSERT INTO t SET a=3;
+XA END 'new_trx_3';
+XA PREPARE 'new_trx_3';
+disconnect conn_restart_3;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_4';
+INSERT INTO t SET a=4;
+XA END 'new_trx_4';
+XA PREPARE 'new_trx_4';
+disconnect conn_restart_4;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_5';
+INSERT INTO t SET a=5;
+XA END 'new_trx_5';
+XA PREPARE 'new_trx_5';
+disconnect conn_restart_5;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_6';
+INSERT INTO t SET a=6;
+XA END 'new_trx_6';
+XA PREPARE 'new_trx_6';
+disconnect conn_restart_6;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_7';
+INSERT INTO t SET a=7;
+XA END 'new_trx_7';
+XA PREPARE 'new_trx_7';
+disconnect conn_restart_7;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_8';
+INSERT INTO t SET a=8;
+XA END 'new_trx_8';
+XA PREPARE 'new_trx_8';
+disconnect conn_restart_8;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_9';
+INSERT INTO t SET a=9;
+XA END 'new_trx_9';
+XA PREPARE 'new_trx_9';
+disconnect conn_restart_9;
+connection default;
+connection default;
+XA COMMIT 'new_trx_0';
+XA COMMIT 'new_trx_1';
+XA COMMIT 'new_trx_2';
+XA COMMIT 'new_trx_3';
+XA COMMIT 'new_trx_4';
+XA COMMIT 'new_trx_5';
+XA COMMIT 'new_trx_6';
+XA COMMIT 'new_trx_7';
+XA COMMIT 'new_trx_8';
+XA COMMIT 'new_trx_9';
+XA START 'trx_10';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_10';
+XA START 'trx_11';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_11';
+XA START 'trx_12';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_12';
+XA START 'trx_13';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_13';
+XA START 'trx_14';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_14';
+XA START 'trx_15';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_15';
+XA START 'trx_16';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_16';
+XA START 'trx_17';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_17';
+XA START 'trx_18';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_18';
+XA START 'trx_19';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_19';
+SELECT * FROM t;
+a
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+disconnect conn2tmp;
+disconnect conn3tmp;
+disconnect conn2ro;
+disconnect conn3ro;
+disconnect conn2empty;
+disconnect conn3empty;
+connection default;
+XA ROLLBACK 'trx_20';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn19;
+connection default;
+XA ROLLBACK 'trx_19';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn18;
+connection default;
+XA ROLLBACK 'trx_18';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn17;
+connection default;
+XA ROLLBACK 'trx_17';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn16;
+connection default;
+XA ROLLBACK 'trx_16';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn15;
+connection default;
+XA ROLLBACK 'trx_15';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn14;
+connection default;
+XA ROLLBACK 'trx_14';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn13;
+connection default;
+XA ROLLBACK 'trx_13';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn12;
+connection default;
+XA ROLLBACK 'trx_12';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn11;
+connection default;
+XA ROLLBACK 'trx_11';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn10;
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx1tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx1tmp';
+XA PREPARE 'trx1tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx2tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx2tmp';
+XA PREPARE 'trx2tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx3tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx3tmp';
+XA PREPARE 'trx3tmp';
+connection default;
+XA COMMIT 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA ROLLBACK 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA START 'trx1tmp';
+ERROR XAE08: XAER_DUPID: The XID already exists
+connection default;
+*** 3 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1tmp;
+disconnect conn1tmp;
+connection default;
+XA COMMIT 'trx1tmp';
+KILL connection CONN_ID;
+XA COMMIT 'trx3tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1ro';
+SELECT * from t ORDER BY a;
+a
+0
+1
+2
+3
+4
+5
+5
+6
+6
+7
+7
+8
+8
+9
+9
+10
+11
+12
+13
+14
+XA END 'trx1ro';
+XA PREPARE 'trx1ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2ro';
+SELECT * from t ORDER BY a;
+a
+0
+1
+2
+3
+4
+5
+5
+6
+6
+7
+7
+8
+8
+9
+9
+10
+11
+12
+13
+14
+XA END 'trx2ro';
+XA PREPARE 'trx2ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3ro';
+SELECT * from t ORDER BY a;
+a
+0
+1
+2
+3
+4
+5
+5
+6
+6
+7
+7
+8
+8
+9
+9
+10
+11
+12
+13
+14
+XA END 'trx3ro';
+XA PREPARE 'trx3ro';
+connection default;
+*** 4 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1ro;
+disconnect conn1ro;
+connection default;
+XA ROLLBACK 'trx1ro';
+KILL connection CONN_ID;
+XA ROLLBACK 'trx3ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1empty';
+XA END 'trx1empty';
+XA PREPARE 'trx1empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2empty';
+XA END 'trx2empty';
+XA PREPARE 'trx2empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3empty';
+XA END 'trx3empty';
+XA PREPARE 'trx3empty';
+connection default;
+*** 5 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1empty;
+disconnect conn1empty;
+connection default;
+XA COMMIT 'trx1empty';
+KILL connection CONN_ID;
+XA COMMIT 'trx3empty';
+connect conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1unprepared';
+INSERT INTO t set a=0;
+XA END 'trx1unprepared';
+INSERT INTO t set a=0;
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+disconnect conn1unprepared;
+connection default;
+XA COMMIT 'trx1unprepared';
+ERROR XAE04: XAER_NOTA: Unknown XID
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_0';
+INSERT INTO t SET a=0;
+XA END 'trx_0';
+XA PREPARE 'trx_0';
+disconnect conn0;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_1';
+INSERT INTO t SET a=1;
+XA END 'trx_1';
+XA PREPARE 'trx_1';
+disconnect conn1;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_2';
+INSERT INTO t SET a=2;
+XA END 'trx_2';
+XA PREPARE 'trx_2';
+disconnect conn2;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_3';
+INSERT INTO t SET a=3;
+XA END 'trx_3';
+XA PREPARE 'trx_3';
+disconnect conn3;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_4';
+INSERT INTO t SET a=4;
+XA END 'trx_4';
+XA PREPARE 'trx_4';
+disconnect conn4;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_5';
+INSERT INTO t SET a=5;
+XA END 'trx_5';
+XA PREPARE 'trx_5';
+disconnect conn5;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_6';
+INSERT INTO t SET a=6;
+XA END 'trx_6';
+XA PREPARE 'trx_6';
+disconnect conn6;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_7';
+INSERT INTO t SET a=7;
+XA END 'trx_7';
+XA PREPARE 'trx_7';
+disconnect conn7;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_8';
+INSERT INTO t SET a=8;
+XA END 'trx_8';
+XA PREPARE 'trx_8';
+disconnect conn8;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_9';
+INSERT INTO t SET a=9;
+XA END 'trx_9';
+XA PREPARE 'trx_9';
+disconnect conn9;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_10';
+INSERT INTO t SET a=10;
+XA END 'trx_10';
+XA PREPARE 'trx_10';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_11';
+INSERT INTO t SET a=11;
+XA END 'trx_11';
+XA PREPARE 'trx_11';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_12';
+INSERT INTO t SET a=12;
+XA END 'trx_12';
+XA PREPARE 'trx_12';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_13';
+INSERT INTO t SET a=13;
+XA END 'trx_13';
+XA PREPARE 'trx_13';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_14';
+INSERT INTO t SET a=14;
+XA END 'trx_14';
+XA PREPARE 'trx_14';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_15';
+INSERT INTO t SET a=15;
+XA END 'trx_15';
+XA PREPARE 'trx_15';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_16';
+INSERT INTO t SET a=16;
+XA END 'trx_16';
+XA PREPARE 'trx_16';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_17';
+INSERT INTO t SET a=17;
+XA END 'trx_17';
+XA PREPARE 'trx_17';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_18';
+INSERT INTO t SET a=18;
+XA END 'trx_18';
+XA PREPARE 'trx_18';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_19';
+INSERT INTO t SET a=19;
+XA END 'trx_19';
+XA PREPARE 'trx_19';
+connection default;
+KILL CONNECTION CONN_ID;
+connection default;
+XA ROLLBACK 'trx_0';
+XA ROLLBACK 'trx_1';
+XA ROLLBACK 'trx_2';
+XA ROLLBACK 'trx_3';
+XA ROLLBACK 'trx_4';
+XA COMMIT 'trx_5';
+XA COMMIT 'trx_6';
+XA COMMIT 'trx_7';
+XA COMMIT 'trx_8';
+XA COMMIT 'trx_9';
+# Kill and restart
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_0';
+INSERT INTO t SET a=0;
+XA END 'new_trx_0';
+XA PREPARE 'new_trx_0';
+disconnect conn_restart_0;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_1';
+INSERT INTO t SET a=1;
+XA END 'new_trx_1';
+XA PREPARE 'new_trx_1';
+disconnect conn_restart_1;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_2';
+INSERT INTO t SET a=2;
+XA END 'new_trx_2';
+XA PREPARE 'new_trx_2';
+disconnect conn_restart_2;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_3';
+INSERT INTO t SET a=3;
+XA END 'new_trx_3';
+XA PREPARE 'new_trx_3';
+disconnect conn_restart_3;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_4';
+INSERT INTO t SET a=4;
+XA END 'new_trx_4';
+XA PREPARE 'new_trx_4';
+disconnect conn_restart_4;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_5';
+INSERT INTO t SET a=5;
+XA END 'new_trx_5';
+XA PREPARE 'new_trx_5';
+disconnect conn_restart_5;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_6';
+INSERT INTO t SET a=6;
+XA END 'new_trx_6';
+XA PREPARE 'new_trx_6';
+disconnect conn_restart_6;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_7';
+INSERT INTO t SET a=7;
+XA END 'new_trx_7';
+XA PREPARE 'new_trx_7';
+disconnect conn_restart_7;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_8';
+INSERT INTO t SET a=8;
+XA END 'new_trx_8';
+XA PREPARE 'new_trx_8';
+disconnect conn_restart_8;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_9';
+INSERT INTO t SET a=9;
+XA END 'new_trx_9';
+XA PREPARE 'new_trx_9';
+disconnect conn_restart_9;
+connection default;
+connection default;
+XA COMMIT 'new_trx_0';
+XA COMMIT 'new_trx_1';
+XA COMMIT 'new_trx_2';
+XA COMMIT 'new_trx_3';
+XA COMMIT 'new_trx_4';
+XA COMMIT 'new_trx_5';
+XA COMMIT 'new_trx_6';
+XA COMMIT 'new_trx_7';
+XA COMMIT 'new_trx_8';
+XA COMMIT 'new_trx_9';
+XA START 'trx_10';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_10';
+XA START 'trx_11';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_11';
+XA START 'trx_12';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_12';
+XA START 'trx_13';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_13';
+XA START 'trx_14';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_14';
+XA START 'trx_15';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_15';
+XA START 'trx_16';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_16';
+XA START 'trx_17';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_17';
+XA START 'trx_18';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_18';
+XA START 'trx_19';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_19';
+SELECT * FROM t;
+a
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+disconnect conn2tmp;
+disconnect conn3tmp;
+disconnect conn2ro;
+disconnect conn3ro;
+disconnect conn2empty;
+disconnect conn3empty;
+connection default;
+XA ROLLBACK 'trx_20';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn19;
+connection default;
+XA ROLLBACK 'trx_19';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn18;
+connection default;
+XA ROLLBACK 'trx_18';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn17;
+connection default;
+XA ROLLBACK 'trx_17';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn16;
+connection default;
+XA ROLLBACK 'trx_16';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn15;
+connection default;
+XA ROLLBACK 'trx_15';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn14;
+connection default;
+XA ROLLBACK 'trx_14';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn13;
+connection default;
+XA ROLLBACK 'trx_13';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn12;
+connection default;
+XA ROLLBACK 'trx_12';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn11;
+connection default;
+XA ROLLBACK 'trx_11';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn10;
+connection default;
+XA START 'one_phase_trx_0';
+INSERT INTO t SET a=0;
+XA END 'one_phase_trx_0';
+XA COMMIT 'one_phase_trx_0' ONE PHASE;
+XA START 'one_phase_trx_1';
+INSERT INTO t SET a=1;
+XA END 'one_phase_trx_1';
+XA COMMIT 'one_phase_trx_1' ONE PHASE;
+XA START 'one_phase_trx_2';
+INSERT INTO t SET a=2;
+XA END 'one_phase_trx_2';
+XA COMMIT 'one_phase_trx_2' ONE PHASE;
+XA START 'one_phase_trx_3';
+INSERT INTO t SET a=3;
+XA END 'one_phase_trx_3';
+XA COMMIT 'one_phase_trx_3' ONE PHASE;
+XA START 'one_phase_trx_4';
+INSERT INTO t SET a=4;
+XA END 'one_phase_trx_4';
+XA COMMIT 'one_phase_trx_4' ONE PHASE;
+SELECT SUM(a) FROM t;
+SUM(a)
+290
+DROP TABLE t;
+DROP VIEW v_processlist;
+include/show_binlog_events.inc
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v_processlist` AS SELECT * FROM performance_schema.threads where type = 'FOREGROUND'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `mtr`; INSERT INTO test_suppressions (pattern) VALUES ( NAME_CONST('pattern',_latin1'Found 10 prepared XA transactions' COLLATE 'latin1_swedish_ci'))
+master-bin.000001 # Query # # COMMIT
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE TABLE t (a INT) ENGINE=innodb
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_0
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=0
+master-bin.000001 # Query # # XA END 'trx_0'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_0'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_1
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=1
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_1'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_1'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_2
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=2
+master-bin.000001 # Query # # XA END 'trx_2'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_2'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_3
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=3
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_3'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_3'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_4
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=4
+master-bin.000001 # Query # # XA END 'trx_4'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_4'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_5
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=5
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_5'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_5'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_6
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=6
+master-bin.000001 # Query # # XA END 'trx_6'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_6'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_7
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=7
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_7'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_7'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_8
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=8
+master-bin.000001 # Query # # XA END 'trx_8'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_8'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_9
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=9
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_9'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_9'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_10
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=10
+master-bin.000001 # Query # # XA END 'trx_10'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_10'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_11
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=11
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_11'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_11'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_12
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=12
+master-bin.000001 # Query # # XA END 'trx_12'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_12'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_13
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=13
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_13'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_13'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_14
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=14
+master-bin.000001 # Query # # XA END 'trx_14'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_14'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_15
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=15
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_15'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_15'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_16
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=16
+master-bin.000001 # Query # # XA END 'trx_16'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_16'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_17
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=17
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_17'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_17'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_18
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=18
+master-bin.000001 # Query # # XA END 'trx_18'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_18'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_19
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=19
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_19'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_19'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA ROLLBACK 'trx_0'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA ROLLBACK 'trx_1'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA ROLLBACK 'trx_2'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA ROLLBACK 'trx_3'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA ROLLBACK 'trx_4'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA COMMIT 'trx_5'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA COMMIT 'trx_6'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA COMMIT 'trx_7'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA COMMIT 'trx_8'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA COMMIT 'trx_9'
+master-bin.000001 # Stop # #
+All transactions must be completed, to empty-list the following:
+XA RECOVER;
+formatID gtrid_length bqual_length data
+XA RECOVER;
+formatID gtrid_length bqual_length data
diff --git a/mysql-test/suite/binlog/t/binlog_xa_prepared.test b/mysql-test/suite/binlog/t/binlog_xa_prepared.test
new file mode 100644
index 00000000000..212ecf91648
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_xa_prepared.test
@@ -0,0 +1,106 @@
+#
+# The test verifies prepared XA transaction behaviour.
+#
+# The prepared XA transactions can be disconnected from the client,
+# discovered from another connection and commited or rolled back
+# later. They also survive the server restart. The test runs two
+# loops each consisting of prepared XA:s generation, their
+# manipulation and a server restart followed with survived XA:s
+# completion.
+#
+
+#
+# Prepared XA can't get available to an external connection
+# until connection that either leaves actively or is killed
+# has completed a necessary part of its cleanup.
+# Selecting from P_S.threads provides a method to learn that.
+#
+--source include/have_innodb.inc
+--source include/have_perfschema.inc
+
+# Total number of connection each performing one insert into table
+--let $conn_number=20
+# Number of rollbacks and commits from either side of the server restart
+--let $rollback_number=5
+--let $commit_number=5
+# Number of transactions that are terminated before server restarts
+--let $term_number=`SELECT $rollback_number + $commit_number`
+# Instead of disconnect make some connections killed when their
+# transactions got prepared.
+--let $killed_number=5
+# make some connections disconnected by shutdown rather than actively
+--let $server_disconn_number=5
+--let $prepared_at_server_restart = `SELECT $conn_number - $term_number`
+# number a "warmup" connection after server restart, they all commit
+--let $post_restart_conn_number=10
+
+# Counter to be used in GTID consistency check.
+# It's incremented per each non-XA transaction commit.
+# Local to this file variable to control one-phase commit loop
+--let $one_phase_number = 5
+
+--connection default
+
+# Remove possibly preceeding binlogs and clear initialization time
+# GTID executed info. In the following all transactions are counted
+# to conduct verification at the end of the test.
+if (`SELECT @@global.log_bin`)
+{
+ RESET MASTER;
+}
+
+# Disconected and follower threads need synchronization
+CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
+
+--eval call mtr.add_suppression("Found $prepared_at_server_restart prepared XA transactions")
+
+CREATE TABLE t (a INT) ENGINE=innodb;
+
+# Counter is incremented at the end of post restart to
+# reflect number of loops done in correctness computation.
+--let $restart_number = 0
+--let $how_to_restart=restart_mysqld.inc
+--source include/binlog_xa_prepared_do_and_restart.inc
+
+--let $how_to_restart=kill_and_restart_mysqld.inc
+--source include/binlog_xa_prepared_do_and_restart.inc
+
+--connection default
+
+# Few xs that commit in one phase, not subject to the server restart
+# nor reconnect.
+# This piece of test is related to mysqlbinlog recovery examine below.
+--let $k = 0
+while ($k < $one_phase_number)
+{
+ --eval XA START 'one_phase_trx_$k'
+ --eval INSERT INTO t SET a=$k
+ --eval XA END 'one_phase_trx_$k'
+ --eval XA COMMIT 'one_phase_trx_$k' ONE PHASE
+
+ --inc $k
+}
+
+SELECT SUM(a) FROM t;
+DROP TABLE t;
+DROP VIEW v_processlist;
+
+let $outfile= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql;
+if (`SELECT @@global.log_bin`)
+{
+ # Recording proper samples of binlogged prepared XA:s
+ --source include/show_binlog_events.inc
+ --exec $MYSQL_BINLOG -R --to-last-log master-bin.000001 > $outfile
+ # --cat_file $outfile
+}
+
+--echo All transactions must be completed, to empty-list the following:
+XA RECOVER;
+
+
+if (`SELECT @@global.log_bin`)
+{
+ --exec $MYSQL test < $outfile
+ --remove_file $outfile
+ XA RECOVER;
+}
diff --git a/mysql-test/suite/binlog/t/binlog_xa_prepared_disconnect.test b/mysql-test/suite/binlog/t/binlog_xa_prepared_disconnect.test
new file mode 100644
index 00000000000..3301cf71142
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_xa_prepared_disconnect.test
@@ -0,0 +1,13 @@
+###############################################################################
+# Bug#12161 Xa recovery and client disconnection
+# Testing new server options and binary logging prepared XA transaction.
+###############################################################################
+--source include/have_log_bin.inc
+
+#
+# MIXED mode is chosen because formats are varied inside the sourced tests.
+#
+--source include/have_binlog_format_mixed.inc
+
+--source suite/binlog/t/binlog_xa_prepared.test
+
diff --git a/mysql-test/suite/rpl/include/rpl_connection_master.inc b/mysql-test/suite/rpl/include/rpl_connection_master.inc
new file mode 100644
index 00000000000..fa09cc8a610
--- /dev/null
+++ b/mysql-test/suite/rpl/include/rpl_connection_master.inc
@@ -0,0 +1,2 @@
+let $rpl_connection_name= master;
+source include/rpl_connection.inc;
diff --git a/mysql-test/suite/rpl/include/rpl_connection_slave.inc b/mysql-test/suite/rpl/include/rpl_connection_slave.inc
new file mode 100644
index 00000000000..8dcfb3b611b
--- /dev/null
+++ b/mysql-test/suite/rpl/include/rpl_connection_slave.inc
@@ -0,0 +1,2 @@
+let $rpl_connection_name= slave;
+source include/rpl_connection.inc;
diff --git a/mysql-test/suite/rpl/r/rpl_xa.result b/mysql-test/suite/rpl/r/rpl_xa.result
new file mode 100644
index 00000000000..704480fc0e3
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa.result
@@ -0,0 +1,49 @@
+include/master-slave.inc
+[connection master]
+connection master;
+create table t1 (a int, b int) engine=InnoDB;
+insert into t1 values(0, 0);
+xa start 't';
+insert into t1 values(1, 2);
+xa end 't';
+xa prepare 't';
+xa commit 't';
+connection slave;
+include/diff_tables.inc [master:t1, slave:t1]
+connection master;
+xa start 't';
+insert into t1 values(3, 4);
+xa end 't';
+xa prepare 't';
+xa rollback 't';
+connection slave;
+include/diff_tables.inc [master:t1, slave:t1]
+connection master;
+SET pseudo_slave_mode=1;
+create table t2 (a int) engine=InnoDB;
+xa start 't';
+insert into t1 values (5, 6);
+xa end 't';
+xa prepare 't';
+xa start 's';
+insert into t2 values (0);
+xa end 's';
+xa prepare 's';
+connection slave;
+include/sync_with_master_gtid.inc
+xa recover;
+formatID gtrid_length bqual_length data
+1 1 0 t
+1 1 0 s
+connection master;
+xa commit 't';
+xa commit 's';
+SET pseudo_slave_mode=0;
+Warnings:
+Warning 1231 Slave applier execution mode not active, statement ineffective.
+connection slave;
+include/diff_tables.inc [master:t1, slave:t1]
+include/diff_tables.inc [master:t2, slave:t2]
+connection master;
+drop table t1, t2;
+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
new file mode 100644
index 00000000000..e8b4b054f1e
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result
@@ -0,0 +1,65 @@
+include/master-slave.inc
+[connection master]
+connection slave;
+call mtr.add_suppression("The automatically created table.*name may not be entirely in lowercase");
+include/stop_slave.inc
+CHANGE MASTER TO master_use_gtid=slave_pos;
+SET @@global.gtid_pos_auto_engines="innodb";
+include/start_slave.inc
+connection master;
+create table t1 (a int, b int) engine=InnoDB;
+insert into t1 values(0, 0);
+xa start 't';
+insert into t1 values(1, 2);
+xa end 't';
+xa prepare 't';
+xa commit 't';
+connection slave;
+include/diff_tables.inc [master:t1, slave:t1]
+connection master;
+xa start 't';
+insert into t1 values(3, 4);
+xa end 't';
+xa prepare 't';
+xa rollback 't';
+connection slave;
+include/diff_tables.inc [master:t1, slave:t1]
+connection master;
+SET pseudo_slave_mode=1;
+create table t2 (a int) engine=InnoDB;
+xa start 't';
+insert into t1 values (5, 6);
+xa end 't';
+xa prepare 't';
+xa start 's';
+insert into t2 values (0);
+xa end 's';
+xa prepare 's';
+connection slave;
+include/sync_with_master_gtid.inc
+SELECT @@global.gtid_slave_pos = CONCAT(domain_id,"-",server_id,"-",seq_no) FROM mysql.gtid_slave_pos WHERE seq_no = (SELECT DISTINCT max(seq_no) FROM mysql.gtid_slave_pos);
+@@global.gtid_slave_pos = CONCAT(domain_id,"-",server_id,"-",seq_no)
+1
+xa recover;
+formatID gtrid_length bqual_length data
+1 1 0 t
+1 1 0 s
+connection master;
+xa commit 't';
+xa commit 's';
+SET pseudo_slave_mode=0;
+Warnings:
+Warning 1231 Slave applier execution mode not active, statement ineffective.
+connection slave;
+include/diff_tables.inc [master:t1, slave:t1]
+include/diff_tables.inc [master:t2, slave:t2]
+connection master;
+drop table t1, t2;
+connection slave;
+include/stop_slave.inc
+SET @@global.gtid_pos_auto_engines="";
+SET @@session.sql_log_bin=0;
+DROP TABLE mysql.gtid_slave_pos_InnoDB;
+SET @@session.sql_log_bin=1;
+include/start_slave.inc
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_mixed_engines.result b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_mixed_engines.result
new file mode 100644
index 00000000000..34f118e5de9
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_mixed_engines.result
@@ -0,0 +1,46 @@
+include/master-slave.inc
+[connection master]
+connection master;
+CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
+SET @@session.binlog_direct_non_transactional_updates := if(floor(rand()*10)%2,'ON','OFF');
+CREATE TABLE t (a INT) ENGINE=innodb;
+CREATE TABLE tm (a INT) ENGINE=myisam;
+XA START '1';
+INSERT INTO tm VALUES (1);
+INSERT INTO t VALUES (1);
+XA END '1';
+XA PREPARE '1';
+XA COMMIT '1';
+XA START '2';
+INSERT INTO t VALUES (2);
+INSERT INTO tm VALUES (2);
+INSERT INTO t VALUES (2);
+XA END '2';
+XA PREPARE '2';
+XA COMMIT '2';
+XA START '3';
+INSERT INTO tm VALUES (3);
+INSERT INTO t VALUES (3);
+XA END '3';
+XA PREPARE '3';
+XA ROLLBACK '3';
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+XA START '4';
+INSERT INTO t VALUES (4);
+INSERT INTO tm VALUES (4);
+INSERT INTO t VALUES (4);
+XA END '4';
+XA PREPARE '4';
+XA ROLLBACK '4';
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+include/sync_slave_sql_with_master.inc
+include/diff_tables.inc [master:tm, slave:tm]
+connection master;
+DELETE FROM t;
+DROP TABLE t, tm;
+include/sync_slave_sql_with_master.inc
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_xa.inc b/mysql-test/suite/rpl/t/rpl_xa.inc
new file mode 100644
index 00000000000..69d9f902f8f
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa.inc
@@ -0,0 +1,69 @@
+#
+# This "body" file checks general properties of XA transaction replication
+# as of MDEV-7974.
+# Parameters:
+# --let rpl_xa_check= SELECT ...
+#
+connection master;
+create table t1 (a int, b int) engine=InnoDB;
+insert into t1 values(0, 0);
+xa start 't';
+insert into t1 values(1, 2);
+xa end 't';
+xa prepare 't';
+xa commit 't';
+
+sync_slave_with_master;
+let $diff_tables= master:t1, slave:t1;
+source include/diff_tables.inc;
+
+connection master;
+
+xa start 't';
+insert into t1 values(3, 4);
+xa end 't';
+xa prepare 't';
+xa rollback 't';
+
+sync_slave_with_master;
+let $diff_tables= master:t1, slave:t1;
+source include/diff_tables.inc;
+
+connection master;
+SET pseudo_slave_mode=1;
+create table t2 (a int) engine=InnoDB;
+xa start 't';
+insert into t1 values (5, 6);
+xa end 't';
+xa prepare 't';
+xa start 's';
+insert into t2 values (0);
+xa end 's';
+xa prepare 's';
+
+connection slave;
+source include/sync_with_master_gtid.inc;
+if ($rpl_xa_check)
+{
+ --eval $rpl_xa_check
+ if ($rpl_xa_verbose)
+ {
+ --eval SELECT $rpl_xa_check_lhs
+ --eval SELECT $rpl_xa_check_rhs
+ }
+}
+xa recover;
+
+connection master;
+xa commit 't';
+xa commit 's';
+SET pseudo_slave_mode=0;
+
+sync_slave_with_master;
+let $diff_tables= master:t1, slave:t1;
+source include/diff_tables.inc;
+let $diff_tables= master:t2, slave:t2;
+source include/diff_tables.inc;
+
+connection master;
+drop table t1, t2;
diff --git a/mysql-test/suite/rpl/t/rpl_xa.test b/mysql-test/suite/rpl/t/rpl_xa.test
new file mode 100644
index 00000000000..05a1abe59ae
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa.test
@@ -0,0 +1,5 @@
+source include/have_innodb.inc;
+source include/master-slave.inc;
+
+source rpl_xa.inc;
+source include/rpl_end.inc;
diff --git a/mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine.test b/mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine.test
new file mode 100644
index 00000000000..803e29d53ba
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine.test
@@ -0,0 +1,25 @@
+--source include/have_innodb.inc
+--source include/master-slave.inc
+
+--connection slave
+call mtr.add_suppression("The automatically created table.*name may not be entirely in lowercase");
+
+--source include/stop_slave.inc
+CHANGE MASTER TO master_use_gtid=slave_pos;
+
+SET @@global.gtid_pos_auto_engines="innodb";
+--source include/start_slave.inc
+--let $rpl_xa_check_lhs= @@global.gtid_slave_pos
+--let $rpl_xa_check_rhs= CONCAT(domain_id,"-",server_id,"-",seq_no) FROM mysql.gtid_slave_pos WHERE seq_no = (SELECT DISTINCT max(seq_no) FROM mysql.gtid_slave_pos)
+--let $rpl_xa_check=SELECT $rpl_xa_check_lhs = $rpl_xa_check_rhs
+--source rpl_xa.inc
+
+--connection slave
+--source include/stop_slave.inc
+SET @@global.gtid_pos_auto_engines="";
+SET @@session.sql_log_bin=0;
+DROP TABLE mysql.gtid_slave_pos_InnoDB;
+SET @@session.sql_log_bin=1;
+--source include/start_slave.inc
+
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_mixed_engines.test b/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_mixed_engines.test
new file mode 100644
index 00000000000..1c197dcee76
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_mixed_engines.test
@@ -0,0 +1,80 @@
+# BUG#12161 Xa recovery and client disconnection
+#
+# The test verifies correct XA transaction two phase logging and its applying
+# in a case the transaction updates transactional and non-transactional tables.
+
+--source include/have_innodb.inc
+--source include/master-slave.inc
+
+--connection master
+CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
+
+# Test randomizes the following variable's value:
+SET @@session.binlog_direct_non_transactional_updates := if(floor(rand()*10)%2,'ON','OFF');
+CREATE TABLE t (a INT) ENGINE=innodb;
+
+# memorize for the following show
+--let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1)
+--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+
+CREATE TABLE tm (a INT) ENGINE=myisam;
+
+# case A: COMMIT
+
+# Non transactional table goes first
+XA START '1';
+INSERT INTO tm VALUES (1);
+INSERT INTO t VALUES (1);
+XA END '1';
+XA PREPARE '1';
+XA COMMIT '1';
+
+# Transactional table goes first
+XA START '2';
+INSERT INTO t VALUES (2);
+--disable_warnings
+INSERT INTO tm VALUES (2);
+--enable_warnings
+INSERT INTO t VALUES (2);
+XA END '2';
+XA PREPARE '2';
+XA COMMIT '2';
+
+# case B: ROLLBACK
+
+# Non transactional table goes first
+XA START '3';
+--disable_warnings
+INSERT INTO tm VALUES (3);
+--enable_warnings
+INSERT INTO t VALUES (3);
+XA END '3';
+XA PREPARE '3';
+XA ROLLBACK '3';
+
+# Transactional table goes first
+XA START '4';
+INSERT INTO t VALUES (4);
+--disable_warnings
+INSERT INTO tm VALUES (4);
+--enable_warnings
+INSERT INTO t VALUES (4);
+XA END '4';
+XA PREPARE '4';
+XA ROLLBACK '4';
+
+--source include/sync_slave_sql_with_master.inc
+
+--let $diff_tables= master:tm, slave:tm
+--source include/diff_tables.inc
+
+# cleanup
+
+--connection master
+
+DELETE FROM t;
+DROP TABLE t, tm;
+
+--source include/sync_slave_sql_with_master.inc
+
+--source include/rpl_end.inc
diff --git a/sql/handler.cc b/sql/handler.cc
index 12a05aba90d..370273f026d 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -113,6 +113,10 @@ TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"",
static TYPELIB known_extensions= {0,"known_exts", NULL, NULL};
uint known_extensions_id= 0;
+static
+uint
+ha_check_and_coalesce_trx_read_only(THD *thd, Ha_trx_info *ha_list,
+ bool all);
static int commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans,
bool is_real_trans);
@@ -1273,6 +1277,17 @@ int ha_prepare(THD *thd)
}
}
+
+ uint rw_ha_count=
+ ha_check_and_coalesce_trx_read_only(thd,trans->ha_list, all);
+ bool rw_trans=
+ (rw_ha_count > (thd->is_current_stmt_binlog_disabled()?0U:1U));
+
+ if (rw_trans && tc_log->log_xa_prepare(thd, all))
+ {
+ ha_rollback_trans(thd, all);
+ error=1;
+ }
}
DBUG_RETURN(error);
@@ -2115,6 +2130,8 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
char buf[XIDDATASIZE*4+6]; // see xid_to_str
DBUG_PRINT("info", ("ignore xid %s", xid_to_str(buf, info->list+i)));
#endif
+ // Todo MDEV-7974: remove comments upon merge
+ // xid_cache_insert(info->list+i, XA_PREPARED, TRUE);
xid_cache_insert(info->list + i);
info->found_foreign_xids++;
continue;
diff --git a/sql/handler.h b/sql/handler.h
index fb6862e4ce1..16cc7c70c0f 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -831,6 +831,16 @@ struct xid_t {
long gtrid_length;
long bqual_length;
char data[XIDDATASIZE]; // not \0-terminated !
+ /*
+ The size of the string containing serialized Xid representation
+ is computed as a sum of
+ eight as the number of formatting symbols (X'',X'',)
+ plus 2 x XIDDATASIZE (2 due to hex format),
+ plus space for decimal digits of XID::formatID,
+ plus one for 0x0.
+ */
+ static const uint ser_buf_size=
+ 8 + 2 * XIDDATASIZE + 4 * sizeof(long) + 1;
xid_t() {} /* Remove gcc warning */
bool eq(struct xid_t *xid)
@@ -1655,6 +1665,21 @@ struct handlerton
/* backup */
void (*prepare_for_backup)(void);
void (*end_backup)(void);
+ /**
+ @param[in,out] thd pointer to THD
+ @param[in] new_trx_arg pointer to replacement transaction
+ @param[out] ptr_trx_arg double pointer to being replaced transaction
+
+ Associated with THD engine's native transaction is replaced
+ with @c new_trx_arg. The old value is returned through a buffer if non-null
+ pointer is provided with @c ptr_trx_arg.
+ The method is adapted by XA start and XA prepare handlers to
+ handle XA transaction that is logged as two parts by slave applier.
+
+ This interface concerns engines that are aware of XA transaction.
+ */
+ void (*replace_native_transaction_in_thd)(THD *thd, void *new_trx_arg,
+ void **ptr_trx_arg);
};
diff --git a/sql/log.cc b/sql/log.cc
index aa63736b796..d6cee2bc39e 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -92,6 +92,9 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all);
static int binlog_rollback(handlerton *hton, THD *thd, bool all);
static int binlog_prepare(handlerton *hton, THD *thd, bool all);
static int binlog_start_consistent_snapshot(handlerton *hton, THD *thd);
+static int binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr,
+ Log_event *end_ev, bool all, bool using_stmt,
+ bool using_trx);
static const LEX_CSTRING write_error_msg=
{ STRING_WITH_LEN("error writing to the binary log") };
@@ -1887,23 +1890,16 @@ static inline int
binlog_commit_flush_xid_caches(THD *thd, binlog_cache_mngr *cache_mngr,
bool all, my_xid xid)
{
- if (xid)
- {
- Xid_log_event end_evt(thd, xid, TRUE);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
- }
- else
+ /* Mask XA COMMIT ... ONE PHASE as plain BEGIN ... COMMIT */
+ if (!xid)
{
- /*
- Empty xid occurs in XA COMMIT ... ONE PHASE.
- In this case, we do not have a MySQL xid for the transaction, and the
- external XA transaction coordinator will have to handle recovery if
- needed. So we end the transaction with a plain COMMIT query event.
- */
- Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
- TRUE, TRUE, TRUE, 0);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+ DBUG_ASSERT(thd->transaction.xid_state.xid_cache_element->xa_state == XA_IDLE &&
+ thd->lex->xa_opt == XA_ONE_PHASE);
+ xid= thd->query_id;
}
+
+ Xid_log_event end_evt(thd, xid, TRUE);
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
}
/**
@@ -1965,11 +1961,33 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all)
do nothing.
just pretend we can do 2pc, so that MySQL won't
switch to 1pc.
- real work will be done in MYSQL_BIN_LOG::log_and_order()
+ real work is done in MYSQL_BIN_LOG::log_xa_prepare()
*/
return 0;
}
+
+static int serialize_xid(XID *xid, char *buf)
+{
+ size_t size;
+ buf[0]= '\'';
+ memcpy(buf+1, xid->data, xid->gtrid_length);
+ size= xid->gtrid_length + 2;
+ buf[size-1]= '\'';
+ if (xid->bqual_length == 0 && xid->formatID == 1)
+ return size;
+
+ memcpy(buf+size, ", '", 3);
+ memcpy(buf+size+3, xid->data+xid->gtrid_length, xid->bqual_length);
+ size+= 3 + xid->bqual_length;
+ buf[size]= '\'';
+ size++;
+ if (xid->formatID != 1)
+ size+= sprintf(buf+size, ", %ld", xid->formatID);
+ return size;
+}
+
+
/*
We flush the cache wrapped in a beging/rollback if:
. aborting a single or multi-statement transaction and;
@@ -9918,6 +9936,43 @@ int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
DBUG_RETURN(BINLOG_COOKIE_GET_ERROR_FLAG(cookie));
}
+
+int TC_LOG_BINLOG::log_xa_prepare(THD *thd, bool all)
+{
+ binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data();
+ XID *xid= &thd->transaction.xid_state.xid_cache_element->xid;
+ {
+ /*
+ Log the XA END event first.
+ We don't do that in trans_xa_end() as XA COMMIT ONE PHASE
+ is logged as simple BEGIN/COMMIT so the XA END should
+ not get to the log.
+ */
+ const size_t xc_len= sizeof("XA END ") - 1; // do not count trailing 0
+ char buf[xc_len + xid_t::ser_buf_size];
+ size_t buflen;
+ binlog_cache_data *cache_data;
+ IO_CACHE *file;
+
+ memcpy(buf, "XA END ", xc_len);
+ buflen= xc_len + serialize_xid(xid, buf+xc_len);
+ cache_data= cache_mngr->get_binlog_cache_data(true);
+ file= &cache_data->cache_log;
+ thd->lex->sql_command= SQLCOM_XA_END;
+ Query_log_event xa_end(thd, buf, buflen, true, false, true, 0);
+ if (write_event(&xa_end, cache_data, file))
+ return 1;
+ thd->lex->sql_command= SQLCOM_XA_PREPARE;
+ }
+
+ cache_mngr->using_xa= FALSE;
+ XA_prepare_log_event end_evt(thd, xid, FALSE);
+ if (thd->variables.option_bits & OPTION_BIN_LOG)
+ thd->transaction.xid_state.set_binlogged();
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+}
+
+
void
TC_LOG_BINLOG::commit_checkpoint_notify(void *cookie)
{
diff --git a/sql/log.h b/sql/log.h
index 4cdbf300fb9..d1a53e9934a 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -60,6 +60,7 @@ class TC_LOG
bool need_prepare_ordered,
bool need_commit_ordered) = 0;
virtual int unlog(ulong cookie, my_xid xid)=0;
+ virtual int log_xa_prepare(THD *thd, bool all)= 0;
virtual void commit_checkpoint_notify(void *cookie)= 0;
protected:
@@ -114,6 +115,10 @@ public:
return 1;
}
int unlog(ulong cookie, my_xid xid) { return 0; }
+ int log_xa_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie) { DBUG_ASSERT(0); };
};
@@ -197,6 +202,10 @@ class TC_LOG_MMAP: public TC_LOG
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
+ int log_xa_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie);
int recover();
@@ -693,6 +702,7 @@ public:
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
+ int log_xa_prepare(THD *thd, bool all);
void commit_checkpoint_notify(void *cookie);
int recover(LOG_INFO *linfo, const char *last_log_name, IO_CACHE *first_log,
Format_description_log_event *fdle, bool do_xa);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 1246330f7bb..57ec9ec2eb7 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -2139,6 +2139,9 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case XID_EVENT:
ev = new Xid_log_event(buf, fdle);
break;
+ case XA_PREPARE_LOG_EVENT:
+ ev = new XA_prepare_log_event(buf, fdle);
+ break;
case RAND_EVENT:
ev = new Rand_log_event(buf, fdle);
break;
@@ -2190,7 +2193,6 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case PREVIOUS_GTIDS_LOG_EVENT:
case TRANSACTION_CONTEXT_EVENT:
case VIEW_CHANGE_EVENT:
- case XA_PREPARE_LOG_EVENT:
ev= new Ignorable_log_event(buf, fdle,
get_type_str((Log_event_type) event_type));
break;
@@ -4433,6 +4435,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, size_t que
case SQLCOM_RELEASE_SAVEPOINT:
case SQLCOM_ROLLBACK_TO_SAVEPOINT:
case SQLCOM_SAVEPOINT:
+ case SQLCOM_XA_END:
use_cache= trx_cache= TRUE;
break;
default:
@@ -6268,6 +6271,7 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
post_header_len[USER_VAR_EVENT-1]= USER_VAR_HEADER_LEN;
post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIPTION_HEADER_LEN;
post_header_len[XID_EVENT-1]= XID_HEADER_LEN;
+ post_header_len[XA_PREPARE_LOG_EVENT-1]= XA_PREPARE_HEADER_LEN;
post_header_len[BEGIN_LOAD_QUERY_EVENT-1]= BEGIN_LOAD_QUERY_HEADER_LEN;
post_header_len[EXECUTE_LOAD_QUERY_EVENT-1]= EXECUTE_LOAD_QUERY_HEADER_LEN;
/*
@@ -7920,7 +7924,7 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
buf+= 8;
domain_id= uint4korr(buf);
buf+= 4;
- flags2= *buf;
+ flags2= *(buf++);
if (flags2 & FL_GROUP_COMMIT_ID)
{
if (event_len < (uint)header_size + GTID_HEADER_LEN + 2)
@@ -7928,8 +7932,22 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
seq_no= 0; // So is_valid() returns false
return;
}
- ++buf;
commit_id= uint8korr(buf);
+ buf+= 8;
+ }
+ if (flags2 & FL_PREPARED_XA)
+ {
+ xid.formatID= (long) buf[0];
+ xid.gtrid_length= (long) buf[1];
+ xid.bqual_length= (long) buf[2];
+
+ buf+= 3;
+ if (xid.formatID != -1)
+ {
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(xid.data, buf, data_length);
+ buf+= data_length;
+ }
}
}
@@ -7960,6 +7978,12 @@ Gtid_log_event::Gtid_log_event(THD *thd_arg, uint64 seq_no_arg,
/* Preserve any DDL or WAITED flag in the slave's binlog. */
if (thd_arg->rgi_slave)
flags2|= (thd_arg->rgi_slave->gtid_ev_flags2 & (FL_DDL|FL_WAITED));
+ if (thd->transaction.xid_state.xid_cache_element->xa_state == XA_IDLE &&
+ thd->lex->xa_opt != XA_ONE_PHASE)
+ {
+ flags2|= FL_PREPARED_XA;
+ xid= thd->transaction.xid_state.xid_cache_element->xid;
+ }
}
@@ -8002,7 +8026,7 @@ Gtid_log_event::peek(const char *event_start, size_t event_len,
bool
Gtid_log_event::write()
{
- uchar buf[GTID_HEADER_LEN+2];
+ uchar buf[GTID_HEADER_LEN+2+sizeof(XID)];
size_t write_len;
int8store(buf, seq_no);
@@ -8014,8 +8038,25 @@ Gtid_log_event::write()
write_len= GTID_HEADER_LEN + 2;
}
else
+ write_len= 13;
+
+ if (flags2 & FL_PREPARED_XA)
{
- bzero(buf+13, GTID_HEADER_LEN-13);
+ buf[write_len]= (uchar) ((char) xid.formatID);
+ buf[write_len+1]= (uchar) xid.gtrid_length;
+ buf[write_len+2]= (uchar) xid.bqual_length;
+ write_len+= 3;
+ if (xid.formatID != -1)
+ {
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(buf+write_len, xid.data, data_length);
+ write_len+= data_length;
+ }
+ }
+
+ if (write_len < GTID_HEADER_LEN)
+ {
+ bzero(buf+write_len, GTID_HEADER_LEN-write_len);
write_len= GTID_HEADER_LEN;
}
return write_header(write_len) ||
@@ -8058,7 +8099,7 @@ Gtid_log_event::make_compatible_event(String *packet, bool *need_dummy_event,
void
Gtid_log_event::pack_info(Protocol *protocol)
{
- char buf[6+5+10+1+10+1+20+1+4+20+1];
+ char buf[6+5+10+1+10+1+20+1+4+20+1+5+128];
char *p;
p = strmov(buf, (flags2 & FL_STANDALONE ? "GTID " : "BEGIN GTID "));
p= longlong10_to_str(domain_id, p, 10);
@@ -8072,6 +8113,12 @@ Gtid_log_event::pack_info(Protocol *protocol)
p= longlong10_to_str(commit_id, p, 10);
}
+ if (flags2 & FL_PREPARED_XA)
+ {
+ p= strmov(p, " XID :");
+ p= strnmov(p, xid.data, xid.bqual_length + xid.gtrid_length);
+ }
+
protocol->store(buf, p-buf, &my_charset_bin);
}
@@ -8125,10 +8172,23 @@ Gtid_log_event::do_apply_event(rpl_group_info *rgi)
thd->lex->sql_command= SQLCOM_BEGIN;
thd->is_slave_error= 0;
status_var_increment(thd->status_var.com_stat[thd->lex->sql_command]);
- if (trans_begin(thd, 0))
+ if (flags2 & FL_PREPARED_XA)
{
- DBUG_PRINT("error", ("trans_begin() failed"));
- thd->is_slave_error= 1;
+ thd->lex->xid= &xid;
+ thd->lex->xa_opt= XA_NONE;
+ if (trans_xa_start(thd))
+ {
+ DBUG_PRINT("error", ("trans_xa_start() failed"));
+ thd->is_slave_error= 1;
+ }
+ }
+ else
+ {
+ if (trans_begin(thd, 0))
+ {
+ DBUG_PRINT("error", ("trans_begin() failed"));
+ thd->is_slave_error= 1;
+ }
}
thd->update_stats();
@@ -8248,9 +8308,29 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
buf, print_event_info->delimiter))
goto err;
}
- if (!(flags2 & FL_STANDALONE))
- if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n", print_event_info->delimiter))
+ if ((flags2 & FL_PREPARED_XA) && !is_flashback)
+ {
+ my_b_write_string(&cache, "XA START '");
+ my_b_write(&cache, (uchar *) xid.data, xid.gtrid_length);
+ my_b_write_string(&cache, "'");
+ if (xid.bqual_length > 0 || xid.formatID != 1)
+ {
+ my_b_write_string(&cache, ", '");
+ my_b_write(&cache, (uchar *) xid.data+xid.gtrid_length, xid.bqual_length);
+ my_b_write_string(&cache, "'");
+ if (xid.formatID != 1)
+ if (my_b_printf(&cache, ", %d", xid.formatID))
+ goto err;
+ }
+ if (my_b_printf(&cache, "%s\n", print_event_info->delimiter))
goto err;
+ }
+ else if (!(flags2 & FL_STANDALONE))
+ {
+ if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n",
+ print_event_info->delimiter))
+ goto err;
+ }
return cache.flush_data();
err:
@@ -8871,6 +8951,142 @@ bool slave_execute_deferred_events(THD *thd)
/**************************************************************************
+ Xid_apply_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+
+int Xid_apply_log_event::do_record_gtid(THD *thd, rpl_group_info *rgi,
+ bool in_trans, void **out_hton)
+{
+ int err= 0;
+ Relay_log_info const *rli= rgi->rli;
+
+ rgi->gtid_pending= false;
+ err= rpl_global_gtid_slave_state->record_gtid(thd, &rgi->current_gtid,
+ rgi->gtid_sub_id,
+ in_trans, false, out_hton);
+
+ if (unlikely(err))
+ {
+ int ec= thd->get_stmt_da()->sql_errno();
+ /*
+ Do not report an error if this is really a kill due to a deadlock.
+ In this case, the transaction will be re-tried instead.
+ */
+ if (!is_parallel_retry_error(rgi, ec))
+ rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE, rgi->gtid_info(),
+ "Error during XID COMMIT: failed to update GTID state in "
+ "%s.%s: %d: %s",
+ "mysql", rpl_gtid_slave_state_table_name.str, ec,
+ thd->get_stmt_da()->message());
+ thd->is_slave_error= 1;
+ }
+
+ return err;
+}
+
+int Xid_apply_log_event::do_apply_event(rpl_group_info *rgi)
+{
+ bool res;
+ int err;
+ uint64 sub_id= 0;
+ void *hton= NULL;
+ rpl_gtid gtid;
+
+ /*
+ XID_EVENT works like a COMMIT statement. And it also updates the
+ mysql.gtid_slave_pos table with the GTID of the current transaction.
+
+ Therefore, it acts much like a normal SQL statement, so we need to do
+ THD::reset_for_next_command() as if starting a new statement.
+ */
+ thd->reset_for_next_command();
+ /*
+ Record any GTID in the same transaction, so slave state is transactionally
+ consistent.
+ */
+#ifdef WITH_WSREP
+ thd->wsrep_affected_rows= 0;
+#endif
+
+ if (rgi->gtid_pending)
+ {
+ sub_id= rgi->gtid_sub_id;
+ gtid= rgi->current_gtid;
+
+ if (thd->transaction.xid_state.xid_cache_element->xa_state != XA_IDLE)
+ {
+ // TODO: DBUG_ASSERT(thd->transaction.xid_state.xid_cache_element->xa_state == XA_NOTR);
+
+ if ((err= do_record_gtid(thd, rgi, true /* in_trans */, &hton)))
+ return err;
+
+ DBUG_EXECUTE_IF("gtid_fail_after_record_gtid",
+ {
+ my_error(ER_ERROR_DURING_COMMIT, MYF(0),
+ HA_ERR_WRONG_COMMAND);
+ thd->is_slave_error= 1;
+ return 1;
+ });
+ }
+ }
+ /* For a slave Xid_log_event is COMMIT */
+ general_log_print(thd, COM_QUERY,
+ "COMMIT /* implicit, from Xid_log_event */");
+ thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
+ res= do_commit();
+
+ if (rgi->gtid_pending)
+ {
+ // TODO: DBUG_ASSERT(thd->transaction.xid_state.xid_cache_element->xa_state == XA_NOTR);
+
+ if ((err= do_record_gtid(thd, rgi, false, &hton)))
+ return err;
+ }
+ if (likely(!res) && sub_id)
+ rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid,
+ hton, rgi);
+
+ /*
+ Increment the global status commit count variable
+ */
+ status_var_increment(thd->status_var.com_stat[SQLCOM_COMMIT]);
+
+ return res;
+}
+
+Log_event::enum_skip_reason
+Xid_apply_log_event::do_shall_skip(rpl_group_info *rgi)
+{
+ DBUG_ENTER("Xid_apply_log_event::do_shall_skip");
+ if (rgi->rli->slave_skip_counter > 0)
+ {
+ DBUG_ASSERT(!rgi->rli->get_flag(Relay_log_info::IN_TRANSACTION));
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN);
+ DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
+ }
+#ifdef WITH_WSREP
+ else if (wsrep_mysql_replication_bundle && WSREP_ON &&
+ opt_slave_domain_parallel_threads == 0)
+ {
+ if (++thd->wsrep_mysql_replicated < (int)wsrep_mysql_replication_bundle)
+ {
+ WSREP_DEBUG("skipping wsrep commit %d", thd->wsrep_mysql_replicated);
+ DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE);
+ }
+ else
+ {
+ thd->wsrep_mysql_replicated = 0;
+ }
+ }
+#endif
+ DBUG_RETURN(Log_event::do_shall_skip(rgi));
+}
+#endif /* !MYSQL_CLIENT */
+
+
+/**************************************************************************
Xid_log_event methods
**************************************************************************/
@@ -8897,7 +9113,7 @@ void Xid_log_event::pack_info(Protocol *protocol)
Xid_log_event::
Xid_log_event(const char* buf,
const Format_description_log_event *description_event)
- :Log_event(buf, description_event)
+ :Xid_apply_log_event(buf, description_event)
{
/* The Post-Header is empty. The Variable Data part begins immediately. */
buf+= description_event->common_header_len +
@@ -8944,107 +9160,231 @@ err:
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-int Xid_log_event::do_apply_event(rpl_group_info *rgi)
+int Xid_log_event::do_commit()
{
bool res;
- int err;
- rpl_gtid gtid;
- uint64 sub_id= 0;
- Relay_log_info const *rli= rgi->rli;
- void *hton= NULL;
+ res= trans_commit(thd); /* Automatically rolls back on error. */
+ thd->mdl_context.release_transactional_locks();
+ return res;
+}
+#endif /* !MYSQL_CLIENT */
- /*
- XID_EVENT works like a COMMIT statement. And it also updates the
- mysql.gtid_slave_pos table with the GTID of the current transaction.
- Therefore, it acts much like a normal SQL statement, so we need to do
- THD::reset_for_next_command() as if starting a new statement.
- */
- thd->reset_for_next_command();
+#ifdef TODO7974
+/**
+ Function serializes XID which is characterized by by four last arguments
+ of the function.
+ Serialized XID is presented in valid hex format and is returned to
+ the caller in a buffer pointed by the first argument.
+ The buffer size provived by the caller must be not less than
+ 8 + 2 * XIDDATASIZE + 4 * sizeof(XID::formatID) + 1, see
+ XID::serialize_xid() that is a caller and plugin.h for XID declaration.
+
+ @param buf pointer to a buffer allocated for storing serialized data
+
+ @return the value of the buffer pointer
+*/
+
+char *XA_prepare_log_event::event_xid_t::serialize(char *buf) const
+{
+ int i;
+ char *c= buf;
/*
- Record any GTID in the same transaction, so slave state is transactionally
- consistent.
+ Build a string like following pattern:
+ X'hex11hex12...hex1m',X'hex21hex22...hex2n',11
+ and store it into buf.
+ Here hex1i and hex2k are hexadecimals representing XID's internal
+ raw bytes (1 <= i <= m, 1 <= k <= n), and `m' and `n' even numbers
+ half of which corresponding to the lengths of XID's components.
*/
-#ifdef WITH_WSREP
- thd->wsrep_affected_rows= 0;
-#endif
+ c[0]= 'X';
+ c[1]= '\'';
+ c+= 2;
+ for (i= 0; i < gtrid_length; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) data)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) data)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ c[1]= ',';
+ c[2]= 'X';
+ c[3]= '\'';
+ c+= 4;
- if (rgi->gtid_pending)
+ for (; i < gtrid_length + bqual_length; i++)
{
- sub_id= rgi->gtid_sub_id;
- rgi->gtid_pending= false;
+ c[0]=_dig_vec_lower[((uchar*) data)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) data)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ sprintf(c+1, ",%lu", formatID);
- gtid= rgi->current_gtid;
- err= rpl_global_gtid_slave_state->record_gtid(thd, &gtid, sub_id, true,
- false, &hton);
- if (unlikely(err))
- {
- int ec= thd->get_stmt_da()->sql_errno();
- /*
- Do not report an error if this is really a kill due to a deadlock.
- In this case, the transaction will be re-tried instead.
- */
- if (!is_parallel_retry_error(rgi, ec))
- rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE, rgi->gtid_info(),
- "Error during XID COMMIT: failed to update GTID state in "
- "%s.%s: %d: %s",
- "mysql", rpl_gtid_slave_state_table_name.str, ec,
- thd->get_stmt_da()->message());
- thd->is_slave_error= 1;
- return err;
- }
+ return buf;
+}
+#endif /*TODO7974*/
- DBUG_EXECUTE_IF("gtid_fail_after_record_gtid",
- { my_error(ER_ERROR_DURING_COMMIT, MYF(0), HA_ERR_WRONG_COMMAND);
- thd->is_slave_error= 1;
- return 1;
- });
+char *XA_prepare_log_event::event_xid_t::serialize(char *buf) const
+{
+ char *c= buf;
+
+ c[0]= '\'';
+ memcpy(c+1, data, gtrid_length);
+ c[gtrid_length+1]= '\'';
+ c+= gtrid_length + 2;
+
+ if (bqual_length)
+ {
+ c[0]= ',';
+ c[1]= '\'';
+ memcpy(c+2, data+gtrid_length, bqual_length);
+ c[bqual_length+2]= '\'';
+ c+= bqual_length+3;
}
- /* For a slave Xid_log_event is COMMIT */
- general_log_print(thd, COM_QUERY,
- "COMMIT /* implicit, from Xid_log_event */");
- thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
- res= trans_commit(thd); /* Automatically rolls back on error. */
- thd->mdl_context.release_transactional_locks();
+ if (formatID != 1)
+ sprintf(c, ",%lu", formatID);
+ else
+ c[0]=0;
- if (likely(!res) && sub_id)
- rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, hton, rgi);
+ return buf;
+}
- /*
- Increment the global status commit count variable
- */
- status_var_increment(thd->status_var.com_stat[SQLCOM_COMMIT]);
- return res;
+/**************************************************************************
+ XA_prepare_log_event methods
+**************************************************************************/
+/**
+ @note
+ It's ok not to use int8store here,
+ as long as xid_t::set(ulonglong) and
+ xid_t::get_n_xid doesn't do it either.
+ We don't care about actual values of xids as long as
+ identical numbers compare identically
+*/
+
+XA_prepare_log_event::
+XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event)
+ :Xid_apply_log_event(buf, description_event)
+{
+ uint32 temp= 0;
+ uint8 temp_byte;
+
+ buf+= description_event->common_header_len +
+ description_event->post_header_len[XA_PREPARE_LOG_EVENT-1];
+ memcpy(&temp_byte, buf, 1);
+ one_phase= (bool) temp_byte;
+ buf += sizeof(temp_byte);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.formatID= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.gtrid_length= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.bqual_length= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(m_xid.data, buf, m_xid.gtrid_length + m_xid.bqual_length);
+
+ xid= NULL;
}
-Log_event::enum_skip_reason
-Xid_log_event::do_shall_skip(rpl_group_info *rgi)
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+void XA_prepare_log_event::pack_info(Protocol *protocol)
{
- DBUG_ENTER("Xid_log_event::do_shall_skip");
- if (rgi->rli->slave_skip_counter > 0)
+ char buf[ser_buf_size];
+ char query[sizeof("XA COMMIT ONE PHASE") + 1 + sizeof(buf)];
+
+ /* RHS of the following assert is unknown to client sources */
+ compile_time_assert(ser_buf_size == XID::ser_buf_size);
+ m_xid.serialize(buf);
+ sprintf(query,
+ (one_phase ? "XA COMMIT %s ONE PHASE" : "XA PREPARE %s"),
+ buf);
+
+ protocol->store(query, strlen(query), &my_charset_bin);
+}
+#endif
+
+
+#ifndef MYSQL_CLIENT
+bool XA_prepare_log_event::write()
+{
+ uchar data[1 + 4 + 4 + 4];
+ uint8 one_phase_byte= one_phase;
+
+ data[0]= one_phase;
+ int4store(data+1, static_cast<XID*>(xid)->formatID);
+ int4store(data+(1+4), static_cast<XID*>(xid)->gtrid_length);
+ int4store(data+(1+4+4), static_cast<XID*>(xid)->bqual_length);
+
+ DBUG_ASSERT(xid_bufs_size == sizeof(data) - 1);
+
+ return write_header(sizeof(one_phase_byte) + xid_bufs_size +
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_data(data, sizeof(data)) ||
+ write_data((uchar*) static_cast<XID*>(xid)->data,
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_footer();
+}
+#endif
+
+
+#ifdef MYSQL_CLIENT
+bool XA_prepare_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+{
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F, this);
+ char buf[ser_buf_size];
+
+ m_xid.serialize(buf);
+
+ if (!print_event_info->short_form)
{
- DBUG_ASSERT(!rgi->rli->get_flag(Relay_log_info::IN_TRANSACTION));
- thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_GTID_BEGIN);
- DBUG_RETURN(Log_event::EVENT_SKIP_COUNT);
+ print_header(&cache, print_event_info, FALSE);
+ if (my_b_printf(&cache, "\tXID = %s\n", buf))
+ goto error;
}
-#ifdef WITH_WSREP
- else if (wsrep_mysql_replication_bundle && WSREP_ON &&
- opt_slave_domain_parallel_threads == 0)
+
+ if (my_b_printf(&cache, "XA PREPARE %s\n%s\n",
+ buf, print_event_info->delimiter))
+ goto error;
+
+ return cache.flush_data();
+error:
+ return TRUE;
+}
+#endif /* MYSQL_CLIENT */
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+int XA_prepare_log_event::do_commit()
+{
+ int res;
+ xid_t xid;
+ xid.set(m_xid.formatID,
+ m_xid.data, m_xid.gtrid_length,
+ m_xid.data + m_xid.gtrid_length, m_xid.bqual_length);
+
+ thd->lex->xid= &xid;
+ if (!one_phase)
{
- if (++thd->wsrep_mysql_replicated < (int)wsrep_mysql_replication_bundle)
- {
- WSREP_DEBUG("skipping wsrep commit %d", thd->wsrep_mysql_replicated);
- DBUG_RETURN(Log_event::EVENT_SKIP_IGNORE);
- }
- else
- {
- thd->wsrep_mysql_replicated = 0;
- }
+ thd->lex->sql_command= SQLCOM_XA_PREPARE;
+ if ((res= trans_xa_prepare(thd)) == 0)
+ res= applier_reset_xa_trans(thd);
}
-#endif
- DBUG_RETURN(Log_event::do_shall_skip(rgi));
+ else
+ {
+ res= trans_xa_commit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+
+ return res;
}
#endif /* !MYSQL_CLIENT */
@@ -15076,7 +15416,6 @@ bool event_that_should_be_ignored(const char *buf)
event_type == PREVIOUS_GTIDS_LOG_EVENT ||
event_type == TRANSACTION_CONTEXT_EVENT ||
event_type == VIEW_CHANGE_EVENT ||
- event_type == XA_PREPARE_LOG_EVENT ||
(uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F))
return 1;
return 0;
diff --git a/sql/log_event.h b/sql/log_event.h
index 339db756aab..89507d40581 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -217,6 +217,7 @@ class String;
#define GTID_HEADER_LEN 19
#define GTID_LIST_HEADER_LEN 4
#define START_ENCRYPTION_HEADER_LEN 0
+#define XA_PREPARE_HEADER_LEN 0
/*
Max number of possible extra bytes in a replication event compared to a
@@ -3017,6 +3018,31 @@ private:
#endif
};
+
+class Xid_apply_log_event: public Log_event
+{
+public:
+#ifdef MYSQL_SERVER
+ Xid_apply_log_event(THD* thd_arg):
+ Log_event(thd_arg, 0, TRUE) {}
+#endif
+ Xid_apply_log_event(const char* buf,
+ const Format_description_log_event *description_event):
+ Log_event(buf, description_event) {}
+
+ ~Xid_apply_log_event() {}
+ bool is_valid() const { return 1; }
+private:
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ virtual int do_commit()= 0;
+ virtual int do_apply_event(rpl_group_info *rgi);
+ int do_record_gtid(THD *thd, rpl_group_info *rgi, bool in_trans,
+ void **out_hton);
+ enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+#endif
+};
+
+
/**
@class Xid_log_event
@@ -3029,14 +3055,14 @@ private:
typedef ulonglong my_xid; // this line is the same as in handler.h
#endif
-class Xid_log_event: public Log_event
+class Xid_log_event: public Xid_apply_log_event
{
- public:
- my_xid xid;
+public:
+ my_xid xid;
#ifdef MYSQL_SERVER
Xid_log_event(THD* thd_arg, my_xid x, bool direct):
- Log_event(thd_arg, 0, TRUE), xid(x)
+ Xid_apply_log_event(thd_arg)
{
if (direct)
cache_type= Log_event::EVENT_NO_CACHE;
@@ -3056,15 +3082,87 @@ class Xid_log_event: public Log_event
#ifdef MYSQL_SERVER
bool write();
#endif
- bool is_valid() const { return 1; }
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(rpl_group_info *rgi);
- enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+ int do_commit();
#endif
};
+
+/**
+ @class XA_prepare_log_event
+
+ Similar to Xid_log_event except that
+ - it is specific to XA transaction
+ - it carries out the prepare logics rather than the final committing
+ when @c one_phase member is off.
+ From the groupping perspective the event finalizes the current "prepare" group
+ started with XA START Query-log-event.
+ When @c one_phase is false Commit of Rollback for XA transaction are
+ logged separately to the prepare-group events so being a groups of
+ their own.
+ The XA COMMIT ONE PHASE normally is logged as simple BEGIN ... COMMIT,
+ so one_phase is always OFF. Still it is read and handled to be consistent
+ with other servers.
+*/
+
+class XA_prepare_log_event: public Xid_apply_log_event
+{
+protected:
+ /* The event_xid_t members were copied from handler.h */
+ struct event_xid_t
+ {
+ long formatID;
+ long gtrid_length;
+ long bqual_length;
+ char data[MYSQL_XIDDATASIZE]; // not \0-terminated !
+ char *serialize(char *buf) const;
+ };
+
+ /* size of serialization buffer is explained in $MYSQL/sql/xa.h. */
+ static const uint ser_buf_size=
+ 8 + 2 * MYSQL_XIDDATASIZE + 4 * sizeof(long) + 1;
+
+ /* Total size of buffers to hold serialized members of XID struct */
+ static const int xid_bufs_size= 12;
+ event_xid_t m_xid;
+ void *xid;
+ bool one_phase;
+
+public:
+#ifdef MYSQL_SERVER
+ XA_prepare_log_event(THD* thd_arg, XID *xid_arg, bool one_phase_arg):
+ Xid_apply_log_event(thd_arg), xid(xid_arg), one_phase(one_phase_arg)
+ {
+ cache_type= Log_event::EVENT_NO_CACHE;
+ }
+#ifdef HAVE_REPLICATION
+ void pack_info(Protocol* protocol);
+#endif /* HAVE_REPLICATION */
+#else
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+#endif
+ XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event);
+ ~XA_prepare_log_event() {}
+ Log_event_type get_type_code() { return XA_PREPARE_LOG_EVENT; }
+ int get_data_size()
+ {
+ return xid_bufs_size + m_xid.gtrid_length + m_xid.bqual_length;
+ }
+
+#ifdef MYSQL_SERVER
+ bool write();
+#endif
+
+private:
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ int do_commit();
+#endif
+};
+
+
/**
@class User_var_log_event
@@ -3377,6 +3475,11 @@ public:
uint64 seq_no;
uint64 commit_id;
uint32 domain_id;
+#ifdef MYSQL_SERVER
+ XID xid;
+#else
+ struct st_mysql_xid xid;
+#endif
uchar flags2;
/* Flags2. */
@@ -3405,6 +3508,8 @@ public:
static const uchar FL_WAITED= 16;
/* FL_DDL is set for event group containing DDL. */
static const uchar FL_DDL= 32;
+ /* FL_PREPARED_XA is set for XA transaction. */
+ static const uchar FL_PREPARED_XA= 64;
#ifdef MYSQL_SERVER
Gtid_log_event(THD *thd_arg, uint64 seq_no, uint32 domain_id, bool standalone,
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 1266e1777ce..9c71aba7247 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1916,6 +1916,17 @@ struct Ha_data
Lifetime: one user connection.
*/
void *ha_ptr;
+ /**
+ A memorizer to engine specific "native" transaction object to provide
+ storage engine detach-re-attach facility.
+ The server level transaction object can dissociate from storage engine
+ transactions. The released "native" transaction reference
+ can be hold in the member until it is reconciled later.
+ Lifetime: Depends on caller of @c hton::replace_native_transaction_in_thd.
+ For instance in the case of slave server applier handling XA transaction
+ it is from XA START to XA PREPARE.
+ */
+ void *ha_ptr_backup;
/**
0: Life time: one statement within a transaction. If @@autocommit is
on, also represents the entire transaction.
@@ -1932,7 +1943,7 @@ struct Ha_data
non-NULL: engine is bound to this thread, engine shutdown forbidden
*/
plugin_ref lock;
- Ha_data() :ha_ptr(NULL) {}
+ Ha_data() :ha_ptr(NULL), ha_ptr_backup(NULL) {}
};
/**
@@ -6515,6 +6526,18 @@ inline bool add_group_to_list(THD *thd, Item *item, bool asc)
return thd->lex->current_select->add_group_to_list(thd, item, asc);
}
+/**
+ @param THD thread context
+ @param hton pointer to handlerton
+ @return address of the placeholder of handlerton's specific transaction
+ object (data)
+*/
+
+inline void **thd_ha_data_backup(const THD *thd, const struct handlerton *hton)
+{
+ return (void **) &thd->ha_data[hton->slot].ha_ptr_backup;
+}
+
inline Item *and_conds(THD *thd, Item *a, Item *b)
{
if (!b) return a;
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 1d1057cf6c2..8c91b11f340 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -1411,6 +1411,7 @@ void do_handle_one_connection(CONNECT *connect)
end_thread:
close_connection(thd);
+ thd->get_stmt_da()->reset_diagnostics_area();
if (thd->userstat_running)
update_global_user_stats(thd, create_user, time(NULL));
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index e61a9675e65..05c04563cef 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -6222,6 +6222,9 @@ end_with_restore_list:
{
bool rollback_failed= trans_xa_rollback(thd);
thd->mdl_context.release_transactional_locks();
+
+ DBUG_EXECUTE_IF("crash_after_xa_rollback", DBUG_SUICIDE(););
+
if (rollback_failed)
{
WSREP_DEBUG("XA rollback failed, MDL released: %lld",
diff --git a/sql/transaction.cc b/sql/transaction.cc
index 2887ae763df..ef560fbecbf 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -700,3 +700,124 @@ bool trans_release_savepoint(THD *thd, LEX_CSTRING name)
DBUG_RETURN(MY_TEST(res));
}
+
+
+void attach_native_trx(THD *thd);
+/**
+ This is a specific to "slave" applier collection of standard cleanup
+ actions to reset XA transaction states at the end of XA prepare rather than
+ to do it at the transaction commit, see @c ha_commit_one_phase.
+ THD of the slave applier is dissociated from a transaction object in engine
+ that continues to exist there.
+
+ @param THD current thread
+ @return the value of is_error()
+*/
+
+bool applier_reset_xa_trans(THD *thd)
+{
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
+ //TODO: review
+ //xid_cache_delete(thd, xid_s);
+ //if (xid_cache_insert(&xid_s->xid_cache_element->xid, XA_PREPARED, xid_s->is_binlogged))
+ // return true;
+ thd->transaction.xid_state.xid_cache_element->acquired_to_recovered();
+ thd->transaction.xid_state.xid_cache_element= 0;
+
+ attach_native_trx(thd);
+ thd->transaction.cleanup();
+ //TODO: thd->transaction.xid_state.xid_cache_element->xa_state= XA_NOTR;
+ thd->mdl_context.release_transactional_locks();
+
+ return thd->is_error();
+#ifdef p7974
+ Transaction_ctx *trn_ctx= thd->get_transaction();
+ XID_STATE *xid_state= trn_ctx->xid_state();
+ /*
+ In the following the server transaction state gets reset for
+ a slave applier thread similarly to xa_commit logics
+ except commit does not run.
+ */
+ thd->variables.option_bits&= ~OPTION_BEGIN;
+ trn_ctx->reset_unsafe_rollback_flags(Transaction_ctx::STMT);
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ /* Server transaction ctx is detached from THD */
+ transaction_cache_detach(trn_ctx);
+ xid_state->reset();
+ /*
+ The current engine transactions is detached from THD, and
+ previously saved is restored.
+ */
+ attach_native_trx(thd);
+ trn_ctx->set_ha_trx_info(Transaction_ctx::SESSION, NULL);
+ trn_ctx->set_no_2pc(Transaction_ctx::SESSION, false);
+ trn_ctx->cleanup();
+#ifdef HAVE_PSI_TRANSACTION_INTERFACE
+ MYSQL_COMMIT_TRANSACTION(thd->m_transaction_psi);
+ thd->m_transaction_psi= NULL;
+#endif
+
+#endif /*p7974*/
+ return thd->is_error();
+}
+
+
+/**
+ The function detaches existing storage engines transaction
+ context from thd. Backup area to save it is provided to low level
+ storage engine function.
+
+ is invoked by plugin_foreach() after
+ trans_xa_start() for each storage engine.
+
+ @param[in,out] thd Thread context
+ @param plugin Reference to handlerton
+
+ @return FALSE on success, TRUE otherwise.
+*/
+
+my_bool detach_native_trx(THD *thd, plugin_ref plugin, void *unused)
+{
+ handlerton *hton= plugin_hton(plugin);
+ if (hton->replace_native_transaction_in_thd)
+ hton->replace_native_transaction_in_thd(thd, NULL,
+ thd_ha_data_backup(thd, hton));
+
+ return FALSE;
+
+}
+
+/**
+ The function restores previously saved storage engine transaction context.
+
+ @param thd Thread context
+*/
+void attach_native_trx(THD *thd)
+{
+ Ha_trx_info *ha_info= thd->transaction.all.ha_list;
+ Ha_trx_info *ha_info_next;
+
+ if (ha_info)
+ {
+ for (; ha_info; ha_info= ha_info_next)
+ {
+ handlerton *hton= ha_info->ht();
+ if (hton->replace_native_transaction_in_thd)
+ {
+ /* restore the saved original engine transaction's link with thd */
+ void **trx_backup= thd_ha_data_backup(thd, hton);
+
+ hton->
+ replace_native_transaction_in_thd(thd, *trx_backup, NULL);
+ *trx_backup= NULL;
+ }
+ ha_info_next= ha_info->next();
+ ha_info->reset();
+ }
+ }
+ thd->transaction.all.ha_list= 0;
+ thd->transaction.all.no_2pc= 0;
+}
diff --git a/sql/xa.cc b/sql/xa.cc
index c4b983aa4f5..2aa798fae23 100644
--- a/sql/xa.cc
+++ b/sql/xa.cc
@@ -19,140 +19,11 @@
#include "mariadb.h"
#include "sql_class.h"
#include "transaction.h"
-
+#include "xa.h"
/***************************************************************************
Handling of XA id cacheing
***************************************************************************/
-enum xa_states { XA_ACTIVE= 0, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY };
-
-
-struct XID_cache_insert_element
-{
- enum xa_states xa_state;
- XID *xid;
- XID_cache_element *xid_cache_element;
-
- XID_cache_insert_element(enum xa_states xa_state_arg, XID *xid_arg):
- xa_state(xa_state_arg), xid(xid_arg) {}
-};
-
-
-class XID_cache_element
-{
- /*
- m_state is used to prevent elements from being deleted while XA RECOVER
- iterates xid cache and to prevent recovered elments from being acquired by
- multiple threads.
-
- bits 1..29 are reference counter
- bit 30 is RECOVERED flag
- bit 31 is ACQUIRED flag (thread owns this xid)
- bit 32 is unused
-
- Newly allocated and deleted elements have m_state set to 0.
-
- On lock() m_state is atomically incremented. It also creates load-ACQUIRE
- memory barrier to make sure m_state is actually updated before furhter
- memory accesses. Attempting to lock an element that has neither ACQUIRED
- nor RECOVERED flag set returns failure and further accesses to element
- memory are forbidden.
-
- On unlock() m_state is decremented. It also creates store-RELEASE memory
- barrier to make sure m_state is actually updated after preceding memory
- accesses.
-
- ACQUIRED flag is set when thread registers it's xid or when thread acquires
- recovered xid.
-
- RECOVERED flag is set for elements found during crash recovery.
-
- ACQUIRED and RECOVERED flags are cleared before element is deleted from
- hash in a spin loop, after last reference is released.
- */
- std::atomic<int32_t> m_state;
-public:
- static const int32 ACQUIRED= 1 << 30;
- static const int32 RECOVERED= 1 << 29;
- /* Error reported by the Resource Manager (RM) to the Transaction Manager. */
- uint rm_error;
- enum xa_states xa_state;
- XID xid;
- bool is_set(int32_t flag)
- { return m_state.load(std::memory_order_relaxed) & flag; }
- void set(int32_t flag)
- {
- DBUG_ASSERT(!is_set(ACQUIRED | RECOVERED));
- m_state.fetch_add(flag, std::memory_order_relaxed);
- }
- bool lock()
- {
- int32_t old= m_state.fetch_add(1, std::memory_order_acquire);
- if (old & (ACQUIRED | RECOVERED))
- return true;
- unlock();
- return false;
- }
- void unlock()
- { m_state.fetch_sub(1, std::memory_order_release); }
- void mark_uninitialized()
- {
- int32_t old= ACQUIRED;
- while (!m_state.compare_exchange_weak(old, 0,
- std::memory_order_relaxed,
- std::memory_order_relaxed))
- {
- old&= ACQUIRED | RECOVERED;
- (void) LF_BACKOFF();
- }
- }
- void acquired_to_recovered()
- {
- m_state.fetch_or(RECOVERED, std::memory_order_relaxed);
- m_state.fetch_and(~ACQUIRED, std::memory_order_release);
- }
- bool acquire_recovered()
- {
- int32_t old= RECOVERED;
- while (!m_state.compare_exchange_weak(old, ACQUIRED | RECOVERED,
- std::memory_order_acquire,
- std::memory_order_relaxed))
- {
- if (!(old & RECOVERED) || (old & ACQUIRED))
- return false;
- old= RECOVERED;
- (void) LF_BACKOFF();
- }
- return true;
- }
- static void lf_hash_initializer(LF_HASH *hash __attribute__((unused)),
- XID_cache_element *element,
- XID_cache_insert_element *new_element)
- {
- DBUG_ASSERT(!element->is_set(ACQUIRED | RECOVERED));
- element->rm_error= 0;
- element->xa_state= new_element->xa_state;
- element->xid.set(new_element->xid);
- new_element->xid_cache_element= element;
- }
- static void lf_alloc_constructor(uchar *ptr)
- {
- XID_cache_element *element= (XID_cache_element*) (ptr + LF_HASH_OVERHEAD);
- element->m_state= 0;
- }
- static void lf_alloc_destructor(uchar *ptr)
- {
- XID_cache_element *element= (XID_cache_element*) (ptr + LF_HASH_OVERHEAD);
- DBUG_ASSERT(!element->is_set(ACQUIRED));
- }
- static uchar *key(const XID_cache_element *element, size_t *length,
- my_bool not_used __attribute__((unused)))
- {
- *length= element->xid.key_length();
- return element->xid.key();
- }
-};
-
static LF_HASH xid_cache;
static bool xid_cache_inited;
diff --git a/sql/xa.h b/sql/xa.h
index 7cf74efad35..85acb6f4958 100644
--- a/sql/xa.h
+++ b/sql/xa.h
@@ -16,17 +16,167 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+enum xa_states { XA_ACTIVE= 0, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY };
class XID_cache_element;
+struct XID_cache_insert_element
+{
+ enum xa_states xa_state;
+ XID *xid;
+ XID_cache_element *xid_cache_element;
+
+ XID_cache_insert_element(enum xa_states xa_state_arg, XID *xid_arg):
+ xa_state(xa_state_arg), xid(xid_arg) {}
+};
+
+
+class XID_cache_element
+{
+ /*
+ m_state is used to prevent elements from being deleted while XA RECOVER
+ iterates xid cache and to prevent recovered elments from being acquired by
+ multiple threads.
+
+ bits 1..29 are reference counter
+ bit 30 is RECOVERED flag
+ bit 31 is ACQUIRED flag (thread owns this xid)
+ bit 32 is unused
+
+ Newly allocated and deleted elements have m_state set to 0.
+
+ On lock() m_state is atomically incremented. It also creates load-ACQUIRE
+ memory barrier to make sure m_state is actually updated before furhter
+ memory accesses. Attempting to lock an element that has neither ACQUIRED
+ nor RECOVERED flag set returns failure and further accesses to element
+ memory are forbidden.
+
+ On unlock() m_state is decremented. It also creates store-RELEASE memory
+ barrier to make sure m_state is actually updated after preceding memory
+ accesses.
+
+ ACQUIRED flag is set when thread registers it's xid or when thread acquires
+ recovered xid.
+
+ RECOVERED flag is set for elements found during crash recovery.
+
+ ACQUIRED and RECOVERED flags are cleared before element is deleted from
+ hash in a spin loop, after last reference is released.
+ */
+ std::atomic<int32_t> m_state;
+public:
+ static const int32 ACQUIRED= 1 << 30;
+ static const int32 RECOVERED= 1 << 29;
+ /* Error reported by the Resource Manager (RM) to the Transaction Manager. */
+ uint rm_error;
+ enum xa_states xa_state;
+ XID xid;
+ bool is_set(int32_t flag)
+ { return m_state.load(std::memory_order_relaxed) & flag; }
+ void set(int32_t flag)
+ {
+ DBUG_ASSERT(!is_set(ACQUIRED | RECOVERED));
+ m_state.fetch_add(flag, std::memory_order_relaxed);
+ }
+ bool lock()
+ {
+ int32_t old= m_state.fetch_add(1, std::memory_order_acquire);
+ if (old & (ACQUIRED | RECOVERED))
+ return true;
+ unlock();
+ return false;
+ }
+ void unlock()
+ { m_state.fetch_sub(1, std::memory_order_release); }
+ void mark_uninitialized()
+ {
+ int32_t old= ACQUIRED;
+ while (!m_state.compare_exchange_weak(old, 0,
+ std::memory_order_relaxed,
+ std::memory_order_relaxed))
+ {
+ old&= ACQUIRED | RECOVERED;
+ (void) LF_BACKOFF();
+ }
+ }
+ void acquired_to_recovered()
+ {
+ m_state.fetch_or(RECOVERED, std::memory_order_relaxed);
+ m_state.fetch_and(~ACQUIRED, std::memory_order_release);
+ }
+ bool acquire_recovered()
+ {
+ int32_t old= RECOVERED;
+ while (!m_state.compare_exchange_weak(old, ACQUIRED | RECOVERED,
+ std::memory_order_acquire,
+ std::memory_order_relaxed))
+ {
+ if (!(old & RECOVERED) || (old & ACQUIRED))
+ return false;
+ old= RECOVERED;
+ (void) LF_BACKOFF();
+ }
+ return true;
+ }
+ static void lf_hash_initializer(LF_HASH *hash __attribute__((unused)),
+ XID_cache_element *element,
+ XID_cache_insert_element *new_element)
+ {
+ DBUG_ASSERT(!element->is_set(ACQUIRED | RECOVERED));
+ element->rm_error= 0;
+ element->xa_state= new_element->xa_state;
+ element->xid.set(new_element->xid);
+ new_element->xid_cache_element= element;
+ }
+ static void lf_alloc_constructor(uchar *ptr)
+ {
+ XID_cache_element *element= (XID_cache_element*) (ptr + LF_HASH_OVERHEAD);
+ element->m_state= 0;
+ }
+ static void lf_alloc_destructor(uchar *ptr)
+ {
+ XID_cache_element *element= (XID_cache_element*) (ptr + LF_HASH_OVERHEAD);
+ DBUG_ASSERT(!element->is_set(ACQUIRED));
+ }
+ static uchar *key(const XID_cache_element *element, size_t *length,
+ my_bool not_used __attribute__((unused)))
+ {
+ *length= element->xid.key_length();
+ return element->xid.key();
+ }
+};
+
+
struct XID_STATE {
XID_cache_element *xid_cache_element;
+ /*
+ Binary logging status.
+ It is set to TRUE at XA PREPARE if the transaction was written
+ to the binlog.
+ Naturally FALSE means the transaction was not written to
+ the binlog. Happens if the trnasaction did not modify anything
+ or binlogging was turned off. In that case we shouldn't binlog
+ the consequent XA COMMIT/ROLLBACK.
+ The recovered transaction after server restart sets it to TRUE always.
+ That can cause inconsistencies (shoud be fixed?).
+ */
+ bool is_binlogged;
bool check_has_uncommitted_xa() const;
bool is_explicit_XA() const { return xid_cache_element != 0; }
void set_error(uint error);
void er_xaer_rmfail() const;
XID *get_xid() const;
+ void reset()
+ {
+ //TODO: what's an equivalent
+ //xid.null();
+ is_binlogged= false;
+ }
+ void set_binlogged()
+ { is_binlogged= true; }
+ void unset_binlogged()
+ { is_binlogged= false; }
};
void xid_cache_init(void);
@@ -42,3 +192,4 @@ bool trans_xa_commit(THD *thd);
bool trans_xa_rollback(THD *thd);
bool trans_xa_detach(THD *thd);
bool mysql_xa_recover(THD *thd);
+bool applier_reset_xa_trans(THD *thd);
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 04fdb0ff3b2..4891b49cd63 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -1826,7 +1826,7 @@ thd_innodb_tmpdir(
/** Obtain the InnoDB transaction of a MySQL thread.
@param[in,out] thd thread handle
@return reference to transaction pointer */
-static trx_t* thd_to_trx(THD* thd)
+static trx_t*& thd_to_trx(THD* thd)
{
return *reinterpret_cast<trx_t**>(thd_ha_data(thd, innodb_hton_ptr));
}
@@ -3632,6 +3632,46 @@ static ulonglong innodb_prepare_commit_versioned(THD* thd, ulonglong *trx_id)
return 0;
}
+/** InnoDB transaction object that is currently associated with THD is
+replaced with that of the 2nd argument. The previous value is
+returned through the 3rd argument's buffer, unless it's NULL. When
+the buffer is not provided (value NULL) that should mean the caller
+restores previously saved association so the current trx has to be
+additionally freed from all association with MYSQL.
+
+@param[in,out] thd MySQL thread handle
+@param[in] new_trx_arg replacement trx_t
+@param[in,out] ptr_trx_arg pointer to a buffer to store old trx_t */
+static
+void
+innodb_replace_trx_in_thd(
+ THD* thd,
+ void* new_trx_arg,
+ void** ptr_trx_arg)
+{
+ trx_t*& trx = thd_to_trx(thd);
+
+ ut_ad(new_trx_arg == NULL
+ || (((trx_t*) new_trx_arg)->mysql_thd == thd
+ && !((trx_t*) new_trx_arg)->is_recovered));
+
+ if (ptr_trx_arg) {
+ *ptr_trx_arg = trx;
+
+ ut_ad(trx == NULL
+ || (trx->mysql_thd == thd && !trx->is_recovered));
+
+ } else if (trx->state == TRX_STATE_NOT_STARTED) {
+ ut_ad(thd == trx->mysql_thd);
+ trx->read_view.close();
+ } else {
+ ut_ad(thd == trx->mysql_thd);
+ ut_ad(trx_state_eq(trx, TRX_STATE_PREPARED));
+ trx_disconnect_prepared(trx);
+ }
+ trx = static_cast<trx_t*>(new_trx_arg);
+}
+
/** Initialize and normalize innodb_buffer_pool_size. */
static void innodb_buffer_pool_size_init()
{
@@ -4193,6 +4233,9 @@ static int innodb_init(void* p)
innobase_hton->prepare_commit_versioned
= innodb_prepare_commit_versioned;
+ innobase_hton->replace_native_transaction_in_thd =
+ innodb_replace_trx_in_thd;
+
innodb_remember_check_sysvar_funcs();
compile_time_assert(DATA_MYSQL_TRUE_VARCHAR == MYSQL_TYPE_VARCHAR);
@@ -5113,6 +5156,7 @@ innobase_close_connection(
if (trx->has_logged_persistent()) {
trx_disconnect_prepared(trx);
} else {
+ trx_rollback_for_mysql(trx);
trx_deregister_from_2pc(trx);
goto rollback_and_free;
}
diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc
index 7ef6b88a9a9..287fc030bb1 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -1342,7 +1342,21 @@ trx_commit_in_memory(
transactions now that it no longer holds any user locks. */
ut_ad(trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY));
- DEBUG_SYNC_C("after_trx_committed_in_memory");
+#ifndef DBUG_OFF
+ const bool debug_sync = trx->mysql_thd &&
+ trx->has_logged_persistent();
+ /* In case of this function is called from a stack executing
+ THD::release_resources -> ...
+ innobase_connection_close() ->
+ trx_rollback_for_mysql... -> .
+ mysql's thd does not seem to have
+ thd->debug_sync_control defined any longer. However the stack
+ is possible only with a prepared trx not updating any data.
+ */
+ if (debug_sync) {
+ DEBUG_SYNC_C("after_trx_committed_in_memory");
+ }
+#endif
if (trx->read_only || trx->rsegs.m_redo.rseg == NULL) {
MONITOR_INC(MONITOR_TRX_RO_COMMIT);
diff --git a/storage/tokudb/mysql-test/tokudb_mariadb/r/xa.result b/storage/tokudb/mysql-test/tokudb_mariadb/r/xa.result
index 4724a0af926..34233b6fd8d 100644
--- a/storage/tokudb/mysql-test/tokudb_mariadb/r/xa.result
+++ b/storage/tokudb/mysql-test/tokudb_mariadb/r/xa.result
@@ -65,4 +65,5 @@ a
20
disconnect con1;
connection default;
+xa rollback 'testb',0x2030405060,11;
drop table t1;
diff --git a/storage/tokudb/mysql-test/tokudb_mariadb/t/xa.test b/storage/tokudb/mysql-test/tokudb_mariadb/t/xa.test
index dc5520a39b8..625abbbc65e 100644
--- a/storage/tokudb/mysql-test/tokudb_mariadb/t/xa.test
+++ b/storage/tokudb/mysql-test/tokudb_mariadb/t/xa.test
@@ -69,5 +69,6 @@ select * from t1;
disconnect con1;
connection default;
+xa rollback 'testb',0x2030405060,11;
drop table t1;