revision-id: 7466fe9f82cebb0f64b66dab0b96b84e4ba0a9f1 ()
parent(s): 0a239bae9791c327ecca1e25d9a082466956bc38
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2020-03-07 22:52:46 +0300
message:
A test using mysqlslap
---
03-run-sort-int-mysqlslap.sh | 33 ++++++++++++++++++++++++
filesort-bench1/02-make-clean-sort-int-tables.sh | 11 ++++++++
filesort-bench1/02-make-fill-sort-int-tables.sh | 21 +++++++++++++++
setup-server/setup-mariadb-current.sh | 3 ++-
4 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/03-run-sort-int-mysqlslap.sh b/03-run-sort-int-mysqlslap.sh
new file mode 100644
index 0000000..65d1b1f
--- /dev/null
+++ b/03-run-sort-int-mysqlslap.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+set -e
+
+RES='result-02-sort-int-mysqlslap'
+
+mkdir $RES
+
+# Generate the scripts
+bash filesort-bench1/02-make-fill-sort-int-tables.sh > $RES/fill-sort-int-tables.sql
+bash filesort-bench1/02-make-clean-sort-int-tables.sh > $RES/clean-sort-int-tables.sql
+
+for SERVER in mariadb-10.5 mariadb-10.5-mdev6915-ext ; do
+
+bash prepare-server.sh -m $SERVER
+source $SERVER-vars.sh
+
+$MYSQL $MYSQL_ARGS test < $RES/fill-sort-int-tables.sql | tee $RES/fill-log-$SERVER.txt
+
+for size in 100000 500000 1000000 2000000 4000000 ; do
+
+$MYSQLSLAP $MYSQL_ARGS \
+ --create-schema=test --no-drop \
+ --query="select a, b from t_int_$size order by a limit 100" \
+ --concurrency=30 --iterations=400 | tee -a $RES/result-$SERVER.txt
+
+done
+
+$MYSQL $MYSQL_ARGS test < $RES/clean-sort-int-tables.sql | tee $RES/clean-log-$SERVER.txt
+
+done
+exit 0;
+
diff --git a/filesort-bench1/02-make-clean-sort-int-tables.sh b/filesort-bench1/02-make-clean-sort-int-tables.sh
new file mode 100644
index 0000000..9260bec
--- /dev/null
+++ b/filesort-bench1/02-make-clean-sort-int-tables.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+# Drop the tables
+
+for size in 100000 500000 1000000 2000000 4000000 8000000 16000000 32000000; do
+
+cat <<END
+drop table if exists t_int_$size;
+END
+
+done
diff --git a/filesort-bench1/02-make-fill-sort-int-tables.sh b/filesort-bench1/02-make-fill-sort-int-tables.sh
new file mode 100644
index 0000000..99800af
--- /dev/null
+++ b/filesort-bench1/02-make-fill-sort-int-tables.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Just create the tables
+
+for size in 100000 500000 1000000 2000000 4000000 8000000 16000000 32000000; do
+
+cat <<END
+drop table if exists t_int_$size;
+create table t_int_$size (
+ a int, b int
+) engine=myisam;
+
+insert into t_int_$size
+select
+ floor(rand() * 25), 1234
+from seq_1_to_$size;
+
+analyze table t_int_$size;
+END
+
+done
diff --git a/setup-server/setup-mariadb-current.sh b/setup-server/setup-mariadb-current.sh
index 12b8b55..9f1b3bb 100755
--- a/setup-server/setup-mariadb-current.sh
+++ b/setup-server/setup-mariadb-current.sh
@@ -81,7 +81,8 @@ innodb_buffer_pool_size=8G
EOF
cat > mysql-vars.sh <<EOF
-MYSQL="./mariadb-$BRANCH/client/mysql"
+MYSQL="`pwd`/mariadb-$BRANCH/client/mysql"
+MYSQLSLAP="`pwd`/mariadb-$BRANCH/client/mysqlslap"
MYSQL_SOCKET="--socket=$SOCKETNAME"
MYSQL_USER="-uroot"
MYSQL_ARGS="\$MYSQL_USER \$MYSQL_SOCKET"
1
0
07 Mar '20
revision-id: 0a239bae9791c327ecca1e25d9a082466956bc38 ()
parent(s): e947318405a673ca5c88cd399a9c7b6541c1bd8b
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2020-03-07 21:10:59 +0300
message:
mv 01-run-sort-int-bench.sh 02-run-sort-int-bench.sh
---
01-run-sort-int-bench.sh => 02-run-sort-int-bench.sh | 0
1 file changed, 0 insertions(+), 0 deletions(-)
diff --git a/01-run-sort-int-bench.sh b/02-run-sort-int-bench.sh
similarity index 100%
rename from 01-run-sort-int-bench.sh
rename to 02-run-sort-int-bench.sh
1
0
revision-id: e947318405a673ca5c88cd399a9c7b6541c1bd8b ()
parent(s): e01fbb6ce1b65f068f0e81d8373c86f192c94729
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2020-03-07 17:39:41 +0300
message:
update .gitignore
---
.gitignore | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/.gitignore b/.gitignore
index 1222055..cac097e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,11 @@
mariadb-10.5-mdev6915-ext
+mariadb-10.5-mdev6915-ext-data
+mariadb-10.5
+mariadb-10.5-mdev6915-ext-vars.sh
+my-mariadb-10.5-mdev6915-ext.cnf
+mariadb-10.5-mdev6915-ext-data.clean
+mariadb-10.5-data.clean
+mariadb-10.5-data
+mariadb-10.5-vars.sh
+my-mariadb-10.5.cnf
+mysql-vars.sh
1
0
revision-id: e01fbb6ce1b65f068f0e81d8373c86f192c94729 ()
parent(s): e6ec7389b0d53bd63730b02deead2291d2740de3
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2020-03-07 17:39:03 +0300
message:
First run: 01-run-sort-int-bench.sh
---
01-run-sort-int-bench.sh | 20 ++++++
filesort-bench1/01-make-sort-int-bench.sh | 101 ++++++++++++++++++++++++++++++
2 files changed, 121 insertions(+)
diff --git a/01-run-sort-int-bench.sh b/01-run-sort-int-bench.sh
new file mode 100644
index 0000000..eb3c86d
--- /dev/null
+++ b/01-run-sort-int-bench.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+set -e
+
+RES='result-sort-int'
+
+mkdir $RES
+
+bash filesort-bench1/01-make-sort-int-bench.sh > $RES/sort-int-bench.sql
+
+bash prepare-server.sh -m mariadb-10.5
+source mariadb-10.5-vars.sh
+
+$MYSQL $MYSQL_ARGS test < $RES/sort-int-bench.sql | tee $RES/sort-int-mariadb-10.5.txt
+
+source mariadb-10.5-mdev6915-ext-vars.sh
+bash prepare-server.sh -m mariadb-10.5-mdev6915-ext
+$MYSQL $MYSQL_ARGS test < $RES/sort-int-bench.sql | tee $RES/sort-int-mariadb-10.5-mdev6915-ext.txt
+
+
diff --git a/filesort-bench1/01-make-sort-int-bench.sh b/filesort-bench1/01-make-sort-int-bench.sh
new file mode 100644
index 0000000..1b38adf
--- /dev/null
+++ b/filesort-bench1/01-make-sort-int-bench.sh
@@ -0,0 +1,101 @@
+#!/bin/bash
+
+## Setup the test
+
+cat <<END
+drop table if exists test_runs;
+drop table if exists test_run_queries;
+
+--
+-- Info about test runs
+--
+create table test_runs (
+ test_name varchar(255),
+ test_ts timestamp,
+ test_time_ms bigint,
+ sort_merge_passes varchar(255)
+);
+
+
+-- Individual queries that are ran as part of the test
+create table test_run_queries (
+ test_name varchar(255),
+ test_ts timestamp,
+ test_time_ms bigint,
+ sort_merge_passes int
+);
+END
+
+###
+
+
+for size in 100000 500000 1000000 2000000 4000000 8000000 16000000 32000000; do
+
+# if
+
+cat <<END
+drop table if exists t_int_$size;
+create table t_int_$size (
+ a int, b int
+) engine=myisam;
+
+insert into t_int_$size
+select
+ floor(rand() * 25), 1234
+from seq_1_to_$size;
+
+analyze table t_int_$size;
+END
+
+for i in 1 2 3 4 5 6 7 8 9 10 ; do
+
+### query_start.sql here:
+cat <<END
+select variable_value into @query_start_smp from information_schema.session_status where variable_name like 'sort_merge_passes';
+select current_timestamp(6) into @query_start_time;
+END
+###
+
+### THE QUERY:
+
+TEST_NAME="sort-int-limit-$size"
+
+QUERY="select a, b from t_int_$size order by a limit 100;"
+#select a, count(distinct b) from t_int_$size group by a;
+
+echo $QUERY
+
+### query-end.sql here:
+cat << END
+set @test_name='$TEST_NAME';
+set @query_time_ms= timestampdiff(microsecond, @query_start_time, current_timestamp(6))/1000;
+select variable_value into @query_end_smp from information_schema.session_status where variable_name like 'sort_merge_passes';
+set @query_merge_passes = @query_end_smp - @query_start_smp;
+insert into test_run_queries
+ (test_name, test_ts, test_time_ms, sort_merge_passes)
+ values (@test_name, @query_start_time, @query_time_ms, @query_merge_passes);
+END
+
+
+done
+
+# Summarize results from multiple runs of one query:
+cat <<END
+set @min_time = (select min(test_time_ms) from test_run_queries where test_name=@test_name);
+set @sort_buffers= (select group_concat(distinct sort_merge_passes) from test_run_queries where test_name=@test_name);
+insert into test_runs(test_name, test_ts, test_time_ms, sort_merge_passes) values
+ (@test_name, current_timestamp(6), @min_time, @sort_buffers);
+END
+
+
+done
+
+cat <<END
+select '${QUERY/'/\\'}';
+select test_name,test_time_ms,sort_merge_passes from test_runs;
+select concat(test_name, ',',
+ test_time_ms, ',',
+ sort_merge_passes)
+from test_runs;
+END
+
1
0
revision-id: e6ec7389b0d53bd63730b02deead2291d2740de3 ()
parent(s): 2c13e8046abc4a896a7df3eafb1aeb05011415ff
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2020-03-07 15:50:33 +0300
message:
added .gitignore
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1222055
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+mariadb-10.5-mdev6915-ext
1
0
revision-id: 2c13e8046abc4a896a7df3eafb1aeb05011415ff ()
parent(s):
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2020-03-07 15:49:22 +0300
message:
Initial commit
---
01-setup-trees.sh | 6 ++
prepare-server.sh | 94 +++++++++++++++++++++++++++
setup-server/setup-mariadb-current.sh | 117 ++++++++++++++++++++++++++++++++++
setup-server/setup-os-ubuntu.sh | 24 +++++++
4 files changed, 241 insertions(+)
diff --git a/01-setup-trees.sh b/01-setup-trees.sh
new file mode 100644
index 0000000..b0ecb5a
--- /dev/null
+++ b/01-setup-trees.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+./setup-server/setup-mariadb-current.sh 10.5-mdev6915-ext
+./setup-server/setup-mariadb-current.sh 10.5 0c35e80dc9ff24bcb8e710cb8cb16428c8c9986f
+
+
diff --git a/prepare-server.sh b/prepare-server.sh
new file mode 100755
index 0000000..03c0100
--- /dev/null
+++ b/prepare-server.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+#
+#
+#
+usage () {
+echo "Usage: $0 [-m] server_name"
+echo " -m - Put datadir on /dev/shm"
+}
+
+###
+### Parse options
+###
+
+while getopts ":pmcd" opt; do
+ case ${opt} in
+ m ) USE_RAMDISK=1
+ ;;
+ \? )
+ usage;
+ exit 1
+ ;;
+ esac
+done
+shift $((OPTIND -1))
+
+SERVERNAME=$1
+
+if [ "x${SERVERNAME}y" = "xy" ] ; then
+ usage;
+ exit 1
+fi
+
+if [ ! -d $SERVERNAME ]; then
+ echo "Can't find tree $SERVERNAME."
+ exit 1
+fi
+
+if [[ $USE_RAMDISK ]] ; then
+ echo " Using /dev/shm for data dir"
+fi
+
+#############################################################################
+### Start the server
+killall -9 mysqld
+sleep 5
+
+DATA_DIR=$SERVERNAME-data
+rm -rf $DATA_DIR
+
+if [[ $USE_RAMDISK ]] ; then
+ rm -rf /dev/shm/$DATA_DIR
+ cp -r ${DATA_DIR}.clean /dev/shm/$DATA_DIR
+ ln -s /dev/shm/$DATA_DIR $DATA_DIR
+else
+ cp -r ${DATA_DIR}.clean $DATA_DIR
+fi
+
+#exit 0;
+./$SERVERNAME/sql/mysqld --defaults-file=./my-${SERVERNAME}.cnf &
+
+
+server_attempts=0
+
+while true ; do
+ client_attempts=0
+ while true ; do
+ ./$SERVERNAME/client/mysql --defaults-file=./my-${SERVERNAME}.cnf -uroot -e "create database sbtest"
+
+ if [ $? -eq 0 ]; then
+ break
+ fi
+ sleep 1
+
+ client_attempts=$((client_attempts + 1))
+ if [ $client_attempts -ge 10 ]; then
+ break;
+ fi
+ done
+
+ MYSQLD_PID=`ps -C mysqld --no-header | awk '{print $1}'`
+ if [[ "a${MYSQLD_PID}b" != "ab" ]] ; then
+ break
+ fi
+
+ server_attempts=$((server_attempts + 1))
+ if [ $server_attempts -ge 4 ]; then
+ echo "Failed to launch mysqld"
+ exit 1
+ fi
+done
+
+# Done.
+
diff --git a/setup-server/setup-mariadb-current.sh b/setup-server/setup-mariadb-current.sh
new file mode 100755
index 0000000..12b8b55
--- /dev/null
+++ b/setup-server/setup-mariadb-current.sh
@@ -0,0 +1,117 @@
+#!/bin/bash
+
+set -e
+
+
+if [ "x${1}y" == "xy" ] ; then
+ echo "Usage: $0 BRANCH_NAME [revision]"
+ exit 1
+fi
+
+BRANCH="${1}"
+REVISION="${2}"
+HOMEDIR=`pwd`
+DATADIR="$HOMEDIR/mariadb-$BRANCH-data"
+
+if [ -d $DATADIR ] ; then
+ echo "Data directory $DATADIR exists, will not overwrite it "
+ du -hs $DATADIR
+ exit 1;
+fi
+
+# --single-branch
+git clone --branch $BRANCH https://github.com/MariaDB/server.git mariadb-$BRANCH
+
+
+(
+cd mariadb-$BRANCH
+
+if [ "x${REVISION}y" != "xy" ] ; then
+ git reset --hard $REVISION
+fi
+
+git submodule init
+git submodule update
+cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo \
+ -DWITHOUT_MROONGA:bool=1 -DWITHOUT_TOKUDB:bool=1
+make -j8
+cd ..
+)
+
+(
+ cd mariadb-$BRANCH/mysql-test
+ ./mtr alias
+ cp -r var/install.db $DATADIR
+ cp -r var/install.db $DATADIR.clean
+)
+
+# Guess a reasonable socket name
+source_dir=`pwd`
+socket_name="`basename $source_dir`.sock"
+SOCKETNAME="/tmp/$socket_name"
+
+
+# plugin-load=ha_rocksdb.so
+# default-storage-engine=rocksdb
+# skip-innodb
+# default-tmp-storage-engine=MyISAM
+# skip-slave-start
+# plugin-dir=$HOMEDIR/mariadb-$BRANCH/storage/rocksdb
+# log-bin=pslp
+# binlog-format=row
+
+cat > $HOMEDIR/my-mariadb-$BRANCH.cnf << EOF
+[mysqld]
+
+bind-address=0.0.0.0
+datadir=$DATADIR
+plugin-dir=$HOMEDIR/mariadb-$BRANCH/mysql-test/var/plugins
+
+log-error
+lc_messages_dir=$HOMEDIR/mariadb-$BRANCH/sql/share
+
+tmpdir=/tmp
+port=3341
+socket=$SOCKETNAME
+gdb
+server-id=12
+
+innodb_buffer_pool_size=8G
+
+EOF
+
+cat > mysql-vars.sh <<EOF
+MYSQL="./mariadb-$BRANCH/client/mysql"
+MYSQL_SOCKET="--socket=$SOCKETNAME"
+MYSQL_USER="-uroot"
+MYSQL_ARGS="\$MYSQL_USER \$MYSQL_SOCKET"
+EOF
+
+source mysql-vars.sh
+cp mysql-vars.sh mariadb-$BRANCH-vars.sh
+
+(
+cd $HOMEDIR/mariadb-$BRANCH/sql
+../sql/mysqld --defaults-file=$HOMEDIR/my-mariadb-$BRANCH.cnf &
+)
+
+
+client_attempts=0
+while true ; do
+ echo $MYSQL $MYSQL_ARGS -e "select version()";
+ $MYSQL $MYSQL_ARGS -e "select version()";
+
+ if [ $? -eq 0 ]; then
+ break
+ fi
+ sleep 1
+
+ client_attempts=$((client_attempts + 1))
+ if [ $client_attempts -ge 30 ]; then
+ echo "Failed to start server."
+ exit 1
+ fi
+done
+
+echo "Done setting up MariaDB"
+
diff --git a/setup-server/setup-os-ubuntu.sh b/setup-server/setup-os-ubuntu.sh
new file mode 100755
index 0000000..5206107
--- /dev/null
+++ b/setup-server/setup-os-ubuntu.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+if [ -e /etc/debian_version ] ; then
+ sudo apt-get update
+ sudo apt-get -y install g++ cmake libbz2-dev libaio-dev bison zlib1g-dev libsnappy-dev libboost-all-dev
+ sudo apt-get -y install libgflags-dev libreadline6-dev libncurses5-dev liblz4-dev gdb git libzstd-dev
+
+ sudo apt-get -y install libzstd0
+ sudo apt-get -y install libssl-dev
+
+ sudo apt-get -y install libgnutls28-dev
+
+# percona server:
+ sudo apt-get -y install libcurl4-gnutls-dev
+
+ sudo apt-get -y install sysstat
+
+ sudo ln -s /usr/lib/x86_64-linux-gnu/libz.so /usr/lib/libz.so
+ sudo sh -c "echo 0 > /proc/sys/kernel/yama/ptrace_scope"
+
+# Need unzip to unpack TPC-DS generator
+ sudo apt-get -y install unzip
+fi
+
1
0
[Commits] Fwd: [andrei.elkin@mariadb.com] 30a3ee1b8ce: MDEV-21469: Implement crash-safe logging of the user XA
by andrei.elkinīŧ pp.inet.fi 04 Mar '20
by andrei.elkinīŧ pp.inet.fi 04 Mar '20
04 Mar '20
Kristian,
Fyi, here is the XA replication event recovery part.
It's largely coded by Sujatha Sivakumar. The latest patch resides in bb-10.5-MDEV_21469.
Cheers,
Andrei
revision-id: 30a3ee1b8ce98ea33dfc0595207bf467cdb91def (mariadb-10.5.0-285-g30a3ee1b8ce)
parent(s): 77f4a1938f2a069174e07b3453fa0f99ba171a2e
author: Sujatha
committer: Andrei Elkin
timestamp: 2020-03-04 14:45:01 +0200
message:
MDEV-21469: Implement crash-safe logging of the user XA
Description: Make XA PREPARE, XA COMMIT and XA ROLLBACK statements crash-safe.
Implementation:
In order to ensure consistent replication XA statements like XA PREPARE, XA
COMMIT and XA ROLLBACK are firstly written into the binary log and then to
storage engine. In a case server crashes after writing to binary log but not
in storage engine it will lead to inconsistent state.
In order to make both binary log and engine to be consistent crash recovery
needs to be initiated. During crash recovery binary log needs to be parsed to
identify the transactions which are present only in binary log and not present
in engine. These transaction are resubmitted to engine to make it consistent
along with binary log.
---
.../r/binlog_xa_multi_binlog_crash_recovery.result | 40 +++
.../t/binlog_xa_multi_binlog_crash_recovery.test | 85 +++++
.../suite/rpl/r/rpl_xa_commit_crash_safe.result | 50 +++
.../suite/rpl/r/rpl_xa_event_apply_failure.result | 60 ++++
.../rpl/r/rpl_xa_prepare_commit_prepare.result | 48 +++
.../suite/rpl/r/rpl_xa_prepare_crash_safe.result | 62 ++++
.../rpl/r/rpl_xa_rollback_commit_crash_safe.result | 47 +++
.../suite/rpl/t/rpl_xa_commit_crash_safe.test | 98 ++++++
.../suite/rpl/t/rpl_xa_event_apply_failure.test | 119 +++++++
.../suite/rpl/t/rpl_xa_prepare_commit_prepare.test | 95 ++++++
.../suite/rpl/t/rpl_xa_prepare_crash_safe.test | 117 +++++++
.../rpl/t/rpl_xa_rollback_commit_crash_safe.test | 97 ++++++
sql/handler.cc | 29 +-
sql/handler.h | 12 +-
sql/log.cc | 379 +++++++++++++++++++--
sql/log.h | 8 +-
sql/log_event.cc | 18 +-
sql/log_event.h | 15 +-
sql/mysqld.cc | 4 +-
sql/xa.cc | 4 +
20 files changed, 1353 insertions(+), 34 deletions(-)
diff --git a/mysql-test/suite/binlog/r/binlog_xa_multi_binlog_crash_recovery.result b/mysql-test/suite/binlog/r/binlog_xa_multi_binlog_crash_recovery.result
new file mode 100644
index 00000000000..a09472aa94c
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_xa_multi_binlog_crash_recovery.result
@@ -0,0 +1,40 @@
+RESET MASTER;
+CREATE TABLE t1 (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=Innodb;
+CALL mtr.add_suppression("Found 1 prepared XA transactions");
+connect con1,localhost,root,,;
+SET DEBUG_SYNC= "simulate_hang_after_binlog_prepare SIGNAL con1_ready WAIT_FOR con1_go";
+SET GLOBAL DEBUG_DBUG="d,simulate_crash_after_binlog_prepare";
+XA START 'xa1';
+INSERT INTO t1 SET a=1;
+XA END 'xa1';
+XA PREPARE 'xa1';;
+connection default;
+SET DEBUG_SYNC= "now WAIT_FOR con1_ready";
+FLUSH LOGS;
+FLUSH LOGS;
+FLUSH LOGS;
+show binary logs;
+Log_name File_size
+master-bin.000001 #
+master-bin.000002 #
+master-bin.000003 #
+master-bin.000004 #
+include/show_binlog_events.inc
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000004 # Format_desc # # SERVER_VERSION, BINLOG_VERSION
+master-bin.000004 # Gtid_list # # [#-#-#]
+master-bin.000004 # Binlog_checkpoint # # master-bin.000001
+SET DEBUG_SYNC= "now SIGNAL con1_go";
+connection con1;
+ERROR HY000: Lost connection to MySQL server during query
+connection default;
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 3 0 xa1
+XA COMMIT 'xa1';
+SELECT * FROM t1;
+a b
+1 NULL
+connection default;
+DROP TABLE t1;
+SET debug_sync = 'reset';
diff --git a/mysql-test/suite/binlog/t/binlog_xa_multi_binlog_crash_recovery.test b/mysql-test/suite/binlog/t/binlog_xa_multi_binlog_crash_recovery.test
new file mode 100644
index 00000000000..aa8d3d04fc7
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_xa_multi_binlog_crash_recovery.test
@@ -0,0 +1,85 @@
+# ==== Purpose ====
+#
+# Test verifies that XA crash recovery works fine across multiple binary logs.
+#
+# ==== Implementation ====
+#
+# Steps:
+# 0 - Generate an explicit XA transaction. Using debug simulation hold the
+# execution of XA PREPARE statement after the XA PREPARE is written to
+# the binary log. With this the prepare will not be done in engine.
+# 1 - By executing FLUSH LOGS generate multiple binary logs.
+# 2 - Now make the server to disappear at this point.
+# 3 - Restart the server. During recovery the XA PREPARE from the binary
+# log will be read. It is cross checked with engine. Since it is not
+# present in engine it will be executed once again.
+# 4 - When server is up execute XA RECOVER to check that the XA is
+# prepared in engine as well.
+# 5 - XA COMMIT the transaction and check the validity of the data.
+#
+# ==== References ====
+#
+# MDEV-21469: Implement crash-safe logging of the user XA
+#
+
+--source include/have_innodb.inc
+--source include/have_debug.inc
+--source include/have_debug_sync.inc
+--source include/have_log_bin.inc
+
+RESET MASTER;
+
+CREATE TABLE t1 (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=Innodb;
+CALL mtr.add_suppression("Found 1 prepared XA transactions");
+
+connect(con1,localhost,root,,);
+SET DEBUG_SYNC= "simulate_hang_after_binlog_prepare SIGNAL con1_ready WAIT_FOR con1_go";
+SET GLOBAL DEBUG_DBUG="d,simulate_crash_after_binlog_prepare";
+XA START 'xa1';
+INSERT INTO t1 SET a=1;
+XA END 'xa1';
+--send XA PREPARE 'xa1';
+
+connection default;
+SET DEBUG_SYNC= "now WAIT_FOR con1_ready";
+FLUSH LOGS;
+FLUSH LOGS;
+FLUSH LOGS;
+
+--source include/show_binary_logs.inc
+--let $binlog_file= master-bin.000004
+--let $binlog_start= 4
+--source include/show_binlog_events.inc
+
+--write_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+wait
+EOF
+
+SET DEBUG_SYNC= "now SIGNAL con1_go";
+--source include/wait_until_disconnected.inc
+
+--connection con1
+--error 2013
+--reap
+--source include/wait_until_disconnected.inc
+
+#
+# Server restart
+#
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+restart
+EOF
+
+connection default;
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+XA RECOVER;
+XA COMMIT 'xa1';
+
+SELECT * FROM t1;
+
+# Clean up.
+connection default;
+DROP TABLE t1;
+SET debug_sync = 'reset';
diff --git a/mysql-test/suite/rpl/r/rpl_xa_commit_crash_safe.result b/mysql-test/suite/rpl/r/rpl_xa_commit_crash_safe.result
new file mode 100644
index 00000000000..27d043270ea
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_commit_crash_safe.result
@@ -0,0 +1,50 @@
+include/master-slave.inc
+[connection master]
+connect master2,localhost,root,,;
+connection master;
+CALL mtr.add_suppression("Found 1 prepared XA transactions");
+CREATE TABLE t ( f INT ) ENGINE=INNODB;
+XA START 'xa1';
+INSERT INTO t VALUES (20);
+XA END 'xa1';
+XA PREPARE 'xa1';
+XA COMMIT 'xa1';
+connection slave;
+include/stop_slave.inc
+connection master1;
+XA START 'xa2';
+INSERT INTO t VALUES (40);
+XA END 'xa2';
+XA PREPARE 'xa2';
+SET GLOBAL DEBUG_DBUG="d,simulate_crash_after_binlog_commit";
+XA COMMIT 'xa2';
+ERROR HY000: Lost connection to MySQL server during query
+connection master1;
+connection master;
+connection default;
+connection server_1;
+connection master;
+connection slave;
+include/start_slave.inc
+connection master;
+SELECT * FROM t;
+f
+20
+40
+XA RECOVER;
+formatID gtrid_length bqual_length data
+XA COMMIT 'xa2';
+ERROR XAE04: XAER_NOTA: Unknown XID
+SELECT * FROM t;
+f
+20
+40
+connection slave;
+SELECT * FROM t;
+f
+20
+40
+connection master;
+DROP TABLE t;
+connection slave;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_xa_event_apply_failure.result b/mysql-test/suite/rpl/r/rpl_xa_event_apply_failure.result
new file mode 100644
index 00000000000..547a0aae9a6
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_event_apply_failure.result
@@ -0,0 +1,60 @@
+include/master-slave.inc
+[connection master]
+connect master2,localhost,root,,;
+connection master;
+CALL mtr.add_suppression("Found 1 prepared XA transactions");
+CALL mtr.add_suppression("Failed to execute binlog query event");
+CALL mtr.add_suppression("Recovery: Error .Out of memory..");
+CALL mtr.add_suppression("Crash recovery failed.");
+CALL mtr.add_suppression("Can.t init tc log");
+CALL mtr.add_suppression("Aborting");
+CREATE TABLE t ( f INT ) ENGINE=INNODB;
+XA START 'xa1';
+INSERT INTO t VALUES (20);
+XA END 'xa1';
+XA PREPARE 'xa1';
+XA COMMIT 'xa1';
+connection slave;
+include/stop_slave.inc
+connection master1;
+XA START 'xa2';
+INSERT INTO t VALUES (40);
+XA END 'xa2';
+XA PREPARE 'xa2';
+SET GLOBAL DEBUG_DBUG="d,simulate_crash_after_binlog_commit";
+XA COMMIT 'xa2';
+ERROR HY000: Lost connection to MySQL server during query
+connection master1;
+connection master;
+connection default;
+connection default;
+connection master;
+*** must be no 'xa2' commit seen, as it's still prepared:
+SELECT * FROM t;
+f
+20
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 3 0 xa2
+SET GLOBAL DEBUG_DBUG="";
+SET SQL_LOG_BIN=0;
+XA COMMIT 'xa2';
+SET SQL_LOG_BIN=1;
+connection server_1;
+connection master;
+connection slave;
+include/start_slave.inc
+connection master;
+SELECT * FROM t;
+f
+20
+40
+connection slave;
+SELECT * FROM t;
+f
+20
+40
+connection master;
+DROP TABLE t;
+connection slave;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_xa_prepare_commit_prepare.result b/mysql-test/suite/rpl/r/rpl_xa_prepare_commit_prepare.result
new file mode 100644
index 00000000000..9ba24716639
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_prepare_commit_prepare.result
@@ -0,0 +1,48 @@
+include/master-slave.inc
+[connection master]
+connect master2,localhost,root,,;
+connection master;
+CALL mtr.add_suppression("Found 1 prepared XA transactions");
+CREATE TABLE t ( f INT ) ENGINE=INNODB;
+XA START 'xa1';
+INSERT INTO t VALUES (20);
+XA END 'xa1';
+XA PREPARE 'xa1';
+XA COMMIT 'xa1';
+connection slave;
+include/stop_slave.inc
+connection master1;
+XA START 'xa2';
+INSERT INTO t VALUES (40);
+XA END 'xa2';
+SET GLOBAL DEBUG_DBUG="d,simulate_crash_after_binlog_prepare";
+XA PREPARE 'xa2';
+ERROR HY000: Lost connection to MySQL server during query
+connection master1;
+connection master;
+connection default;
+connection server_1;
+connection master;
+connection slave;
+include/start_slave.inc
+connection master;
+SELECT * FROM t;
+f
+20
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 3 0 xa2
+XA COMMIT 'xa2';
+SELECT * FROM t;
+f
+20
+40
+connection slave;
+SELECT * FROM t;
+f
+20
+40
+connection master;
+DROP TABLE t;
+connection slave;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_xa_prepare_crash_safe.result b/mysql-test/suite/rpl/r/rpl_xa_prepare_crash_safe.result
new file mode 100644
index 00000000000..99baf59a3c1
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_prepare_crash_safe.result
@@ -0,0 +1,62 @@
+include/master-slave.inc
+[connection master]
+connect master2,localhost,root,,;
+connection master;
+CALL mtr.add_suppression("Found 1 prepared XA transactions");
+CALL mtr.add_suppression("Found 2 prepared XA transactions");
+CALL mtr.add_suppression("Found 3 prepared XA transactions");
+CREATE TABLE t ( f INT ) ENGINE=INNODB;
+XA START 'xa1';
+INSERT INTO t VALUES (20);
+XA END 'xa1';
+XA PREPARE 'xa1';
+connection slave;
+include/stop_slave.inc
+connection master1;
+use test;
+xa start 'xa2';
+insert into t values (30);
+xa end 'xa2';
+SET DEBUG_SYNC="simulate_hang_after_binlog_prepare SIGNAL reached WAIT_FOR go";
+xa prepare 'xa2';
+connection master2;
+XA START 'xa3';
+INSERT INTO t VALUES (40);
+XA END 'xa3';
+SET GLOBAL DEBUG_DBUG="d,simulate_crash_after_binlog_prepare";
+XA PREPARE 'xa3';
+ERROR HY000: Lost connection to MySQL server during query
+connection master1;
+ERROR HY000: Lost connection to MySQL server during query
+connection master;
+connection default;
+connection server_1;
+connection master;
+connection slave;
+include/start_slave.inc
+connection master;
+SELECT * FROM t;
+f
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 3 0 xa3
+1 3 0 xa1
+1 3 0 xa2
+XA COMMIT 'xa1';
+XA COMMIT 'xa2';
+XA COMMIT 'xa3';
+SELECT * FROM t;
+f
+20
+30
+40
+connection slave;
+SELECT * FROM t;
+f
+20
+30
+40
+connection master;
+DROP TABLE t;
+connection slave;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_xa_rollback_commit_crash_safe.result b/mysql-test/suite/rpl/r/rpl_xa_rollback_commit_crash_safe.result
new file mode 100644
index 00000000000..bc48c84e1c7
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_rollback_commit_crash_safe.result
@@ -0,0 +1,47 @@
+include/master-slave.inc
+[connection master]
+connect master2,localhost,root,,;
+connection master;
+CALL mtr.add_suppression("Found 1 prepared XA transactions");
+CREATE TABLE t ( f INT ) ENGINE=INNODB;
+XA START 'xa1';
+INSERT INTO t VALUES (20);
+XA END 'xa1';
+XA PREPARE 'xa1';
+XA COMMIT 'xa1';
+connection slave;
+include/stop_slave.inc
+connection master1;
+XA START 'xa2';
+INSERT INTO t VALUES (40);
+XA END 'xa2';
+XA PREPARE 'xa2';
+SET GLOBAL DEBUG_DBUG="d,simulate_crash_after_binlog_rollback";
+XA ROLLBACK 'xa2';
+ERROR HY000: Lost connection to MySQL server during query
+connection master1;
+connection master;
+connection default;
+connection server_1;
+connection master;
+connection slave;
+include/start_slave.inc
+connection master;
+SELECT * FROM t;
+f
+20
+XA RECOVER;
+formatID gtrid_length bqual_length data
+XA ROLLBACK 'xa2';
+ERROR XAE04: XAER_NOTA: Unknown XID
+SELECT * FROM t;
+f
+20
+connection slave;
+SELECT * FROM t;
+f
+20
+connection master;
+DROP TABLE t;
+connection slave;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_xa_commit_crash_safe.test b/mysql-test/suite/rpl/t/rpl_xa_commit_crash_safe.test
new file mode 100644
index 00000000000..b9e3b0d3d0d
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_commit_crash_safe.test
@@ -0,0 +1,98 @@
+# ==== Purpose ====
+#
+# Test verifies that XA COMMIT statements are crash safe.
+#
+# ==== Implementation ====
+#
+# Steps:
+# 0 - Generate 2 explicit XA transactions. 'xa1' and 'xa2'.
+# 'xa1' will be prepared and committed.
+# 1 - For 'xa2' let the XA COMMIT be done in binary log and crash the
+# server so that it is not committed in engine.
+# 2 - Restart the server. The recovery code should successfully recover
+# 'xa2'. The COMMIT should be executed during recovery.
+# 3 - Check the data in table. Both rows should be present in table.
+# 4 - Trying to commit 'xa2' should report unknown 'XA' error as COMMIT is
+# already complete during recovery.
+#
+# ==== References ====
+#
+# MDEV-21469: Implement crash-safe logging of the user XA
+
+
+--source include/have_innodb.inc
+--source include/master-slave.inc
+--source include/have_debug.inc
+
+connect (master2,localhost,root,,);
+--connection master
+CALL mtr.add_suppression("Found 1 prepared XA transactions");
+
+CREATE TABLE t ( f INT ) ENGINE=INNODB;
+XA START 'xa1';
+INSERT INTO t VALUES (20);
+XA END 'xa1';
+XA PREPARE 'xa1';
+XA COMMIT 'xa1';
+--sync_slave_with_master
+--source include/stop_slave.inc
+
+--connection master1
+XA START 'xa2';
+INSERT INTO t VALUES (40);
+XA END 'xa2';
+XA PREPARE 'xa2';
+
+--write_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+wait
+EOF
+
+SET GLOBAL DEBUG_DBUG="d,simulate_crash_after_binlog_commit";
+--error 2013 # CR_SERVER_LOST
+XA COMMIT 'xa2';
+--source include/wait_until_disconnected.inc
+
+--connection master1
+--source include/wait_until_disconnected.inc
+
+--connection master
+--source include/wait_until_disconnected.inc
+
+#
+# Server restart
+#
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+restart
+EOF
+
+connection default;
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+# rpl_end.inc needs to use the connection server_1
+connection server_1;
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--connection master
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--connection slave
+--source include/start_slave.inc
+--sync_with_master
+
+--connection master
+SELECT * FROM t;
+XA RECOVER;
+--error 1397 # ER_XAER_NOTA
+XA COMMIT 'xa2';
+SELECT * FROM t;
+--sync_slave_with_master
+
+SELECT * FROM t;
+
+--connection master
+DROP TABLE t;
+--sync_slave_with_master
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_xa_event_apply_failure.test b/mysql-test/suite/rpl/t/rpl_xa_event_apply_failure.test
new file mode 100644
index 00000000000..71d0de0fc56
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_event_apply_failure.test
@@ -0,0 +1,119 @@
+# ==== Purpose ====
+#
+# Test verifies that if for some reason an event cannot be applied during
+# recovery, appropriate error is reported.
+#
+# ==== Implementation ====
+#
+# Steps:
+# 0 - Generate 2 explicit XA transactions. 'xa1' and 'xa2'.
+# 'xa1' will be prepared and committed.
+# 1 - For 'xa2' let the XA COMMIT be done in binary log and crash the
+# server so that it is not committed in engine.
+# 2 - Restart the server. Using debug simulation point make XA COMMIT 'xa2'
+# execution to fail. The server will resume anyway
+# to leave the error in the errlog (see "Recovery: Error..").
+# 3 - Work around the simulated failure with Commit once again
+# from a connection that turns OFF binlogging.
+# Slave must catch up with the master.
+#
+# ==== References ====
+#
+# MDEV-21469: Implement crash-safe logging of the user XA
+
+
+--source include/have_innodb.inc
+--source include/master-slave.inc
+--source include/have_debug.inc
+
+connect (master2,localhost,root,,);
+--connection master
+CALL mtr.add_suppression("Found 1 prepared XA transactions");
+CALL mtr.add_suppression("Failed to execute binlog query event");
+CALL mtr.add_suppression("Recovery: Error .Out of memory..");
+CALL mtr.add_suppression("Crash recovery failed.");
+CALL mtr.add_suppression("Can.t init tc log");
+CALL mtr.add_suppression("Aborting");
+
+CREATE TABLE t ( f INT ) ENGINE=INNODB;
+XA START 'xa1';
+INSERT INTO t VALUES (20);
+XA END 'xa1';
+XA PREPARE 'xa1';
+XA COMMIT 'xa1';
+--sync_slave_with_master
+--source include/stop_slave.inc
+
+--connection master1
+XA START 'xa2';
+INSERT INTO t VALUES (40);
+XA END 'xa2';
+XA PREPARE 'xa2';
+
+--write_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+wait
+EOF
+
+SET GLOBAL DEBUG_DBUG="d,simulate_crash_after_binlog_commit";
+--error 2013 # CR_SERVER_LOST
+XA COMMIT 'xa2';
+--source include/wait_until_disconnected.inc
+
+--connection master1
+--source include/wait_until_disconnected.inc
+
+--connection master
+--source include/wait_until_disconnected.inc
+
+#
+# Server restart
+#
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+restart: --debug-dbug=d,trans_xa_commit_fail
+EOF
+
+connection default;
+--source include/wait_until_disconnected.inc
+
+connection default;
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--connection master
+--enable_reconnect
+--echo *** must be no 'xa2' commit seen, as it's still prepared:
+SELECT * FROM t;
+XA RECOVER;
+
+# Commit it manually now to work around the extra binlog record
+# by turning binlogging OFF by the connection.
+
+SET GLOBAL DEBUG_DBUG="";
+SET SQL_LOG_BIN=0;
+--error 0
+XA COMMIT 'xa2';
+SET SQL_LOG_BIN=1;
+
+
+# rpl_end.inc needs to use the connection server_1
+connection server_1;
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--connection master
+--source include/wait_until_connected_again.inc
+
+--connection slave
+--source include/start_slave.inc
+--sync_with_master
+
+--connection master
+SELECT * FROM t;
+
+--sync_slave_with_master
+SELECT * FROM t;
+
+--connection master
+DROP TABLE t;
+--sync_slave_with_master
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_xa_prepare_commit_prepare.test b/mysql-test/suite/rpl/t/rpl_xa_prepare_commit_prepare.test
new file mode 100644
index 00000000000..7b987c7f29b
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_prepare_commit_prepare.test
@@ -0,0 +1,95 @@
+# ==== Purpose ====
+#
+# Test verifies that XA PREPARE transactions are crash safe.
+#
+# ==== Implementation ====
+#
+# Steps:
+# 0 - Generate 2 explicit XA transactions. 'xa1' and 'xa2'.
+# 'xa1' will be prepared and committed.
+# 1 - For 'xa2' let the XA PREPARE be done in binary log and crash the
+# server so that it is not prepared in engine.
+# 2 - Restart the server. The recovery code should successfully recover
+# 'xa2'.
+# 3 - When server is up, execute XA RECOVER and verify that 'xa2' is
+# present.
+# 4 - Commit the XA transaction and verify its correctness.
+#
+# ==== References ====
+#
+# MDEV-21469: Implement crash-safe logging of the user XA
+
+--source include/have_innodb.inc
+--source include/master-slave.inc
+--source include/have_debug.inc
+
+connect (master2,localhost,root,,);
+--connection master
+CALL mtr.add_suppression("Found 1 prepared XA transactions");
+
+CREATE TABLE t ( f INT ) ENGINE=INNODB;
+XA START 'xa1';
+INSERT INTO t VALUES (20);
+XA END 'xa1';
+XA PREPARE 'xa1';
+XA COMMIT 'xa1';
+--sync_slave_with_master
+--source include/stop_slave.inc
+
+--connection master1
+XA START 'xa2';
+INSERT INTO t VALUES (40);
+XA END 'xa2';
+
+--write_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+wait
+EOF
+
+SET GLOBAL DEBUG_DBUG="d,simulate_crash_after_binlog_prepare";
+--error 2013 # CR_SERVER_LOST
+XA PREPARE 'xa2';
+--source include/wait_until_disconnected.inc
+
+--connection master1
+--source include/wait_until_disconnected.inc
+
+--connection master
+--source include/wait_until_disconnected.inc
+
+#
+# Server restart
+#
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+restart
+EOF
+
+connection default;
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+# rpl_end.inc needs to use the connection server_1
+connection server_1;
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--connection master
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--connection slave
+--source include/start_slave.inc
+--sync_with_master
+
+--connection master
+SELECT * FROM t;
+XA RECOVER;
+XA COMMIT 'xa2';
+SELECT * FROM t;
+--sync_slave_with_master
+
+SELECT * FROM t;
+
+--connection master
+DROP TABLE t;
+--sync_slave_with_master
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_xa_prepare_crash_safe.test b/mysql-test/suite/rpl/t/rpl_xa_prepare_crash_safe.test
new file mode 100644
index 00000000000..9d2c5cce528
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_prepare_crash_safe.test
@@ -0,0 +1,117 @@
+# ==== Purpose ====
+#
+# Test verifies that XA PREPARE transactions are crash safe.
+#
+# ==== Implementation ====
+#
+# Steps:
+# 0 - Generate 3 explicit XA transactions. 'xa1', 'xa2' and 'xa3'.
+# Using debug simulation hold the execution of second XA PREPARE
+# statement after the XA PREPARE is written to the binary log.
+# With this the prepare will not be done in engine.
+# 1 - For 'xa3' allow the PREPARE statement to be written to binary log and
+# simulate server crash.
+# 2 - Restart the server. The recovery code should successfully recover
+# 'xa2' and 'xa3'.
+# 3 - When server is up, execute XA RECOVER and verify that 'xa2' and 'xa3'
+# are present along with 'xa1'.
+# 4 - Commit all the XA transactions and verify their correctness.
+#
+# ==== References ====
+#
+# MDEV-21469: Implement crash-safe logging of the user XA
+
+
+--source include/have_innodb.inc
+--source include/master-slave.inc
+--source include/have_debug.inc
+
+connect (master2,localhost,root,,);
+--connection master
+CALL mtr.add_suppression("Found 1 prepared XA transactions");
+CALL mtr.add_suppression("Found 2 prepared XA transactions");
+CALL mtr.add_suppression("Found 3 prepared XA transactions");
+
+CREATE TABLE t ( f INT ) ENGINE=INNODB;
+XA START 'xa1';
+INSERT INTO t VALUES (20);
+XA END 'xa1';
+XA PREPARE 'xa1';
+--sync_slave_with_master
+--source include/stop_slave.inc
+
+--connection master1
+use test;
+xa start 'xa2';
+insert into t values (30);
+xa end 'xa2';
+SET DEBUG_SYNC="simulate_hang_after_binlog_prepare SIGNAL reached WAIT_FOR go";
+send xa prepare 'xa2';
+
+--connection master2
+let $wait_condition=
+ SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST
+ WHERE STATE like "debug sync point: simulate_hang_after_binlog_prepare%";
+--source include/wait_condition.inc
+
+XA START 'xa3';
+INSERT INTO t VALUES (40);
+XA END 'xa3';
+
+--write_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+wait
+EOF
+
+SET GLOBAL DEBUG_DBUG="d,simulate_crash_after_binlog_prepare";
+--error 2013 # CR_SERVER_LOST
+XA PREPARE 'xa3';
+--source include/wait_until_disconnected.inc
+
+--connection master1
+--error 2013
+--reap
+--source include/wait_until_disconnected.inc
+
+--connection master
+--source include/wait_until_disconnected.inc
+
+#
+# Server restart
+#
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+restart
+EOF
+
+connection default;
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+# rpl_end.inc needs to use the connection server_1
+connection server_1;
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--connection master
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+
+--connection slave
+--source include/start_slave.inc
+--sync_with_master
+
+--connection master
+SELECT * FROM t;
+XA RECOVER;
+XA COMMIT 'xa1';
+XA COMMIT 'xa2';
+XA COMMIT 'xa3';
+SELECT * FROM t;
+--sync_slave_with_master
+
+SELECT * FROM t;
+
+--connection master
+DROP TABLE t;
+--sync_slave_with_master
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_xa_rollback_commit_crash_safe.test b/mysql-test/suite/rpl/t/rpl_xa_rollback_commit_crash_safe.test
new file mode 100644
index 00000000000..6416602da5e
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_rollback_commit_crash_safe.test
@@ -0,0 +1,97 @@
+# ==== Purpose ====
+#
+# Test verifies that XA COMMIT statements are crash safe.
+#
+# ==== Implementation ====
+#
+# Steps:
+# 0 - Generate 2 explicit XA transactions. 'xa1' and 'xa2'.
+# 'xa1' will be prepared and committed.
+# 1 - For 'xa2' let the XA ROLLBACK be done in binary log and crash the
+# server so that it is not committed in engine.
+# 2 - Restart the server. The recovery code should successfully recover
+# 'xa2'. The ROLLBACK should be executed during recovery.
+# 3 - Check the data in table. Only one row should be present in table.
+# 4 - Trying to rollback 'xa2' should report unknown 'XA' error as rollback
+# is already complete during recovery.
+#
+# ==== References ====
+#
+# MDEV-21469: Implement crash-safe logging of the user XA
+
+--source include/have_innodb.inc
+--source include/master-slave.inc
+--source include/have_debug.inc
+
+connect (master2,localhost,root,,);
+--connection master
+CALL mtr.add_suppression("Found 1 prepared XA transactions");
+
+CREATE TABLE t ( f INT ) ENGINE=INNODB;
+XA START 'xa1';
+INSERT INTO t VALUES (20);
+XA END 'xa1';
+XA PREPARE 'xa1';
+XA COMMIT 'xa1';
+--sync_slave_with_master
+--source include/stop_slave.inc
+
+--connection master1
+XA START 'xa2';
+INSERT INTO t VALUES (40);
+XA END 'xa2';
+XA PREPARE 'xa2';
+
+--write_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+wait
+EOF
+
+SET GLOBAL DEBUG_DBUG="d,simulate_crash_after_binlog_rollback";
+--error 2013 # CR_SERVER_LOST
+XA ROLLBACK 'xa2';
+--source include/wait_until_disconnected.inc
+
+--connection master1
+--source include/wait_until_disconnected.inc
+
+--connection master
+--source include/wait_until_disconnected.inc
+
+#
+# Server restart
+#
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+restart
+EOF
+
+connection default;
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+# rpl_end.inc needs to use the connection server_1
+connection server_1;
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--connection master
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--connection slave
+--source include/start_slave.inc
+--sync_with_master
+
+--connection master
+SELECT * FROM t;
+XA RECOVER;
+--error 1397 # ER_XAER_NOTA
+XA ROLLBACK 'xa2';
+SELECT * FROM t;
+--sync_slave_with_master
+
+SELECT * FROM t;
+
+--connection master
+DROP TABLE t;
+--sync_slave_with_master
+--source include/rpl_end.inc
diff --git a/sql/handler.cc b/sql/handler.cc
index a1719f9b922..c997c52e602 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1290,6 +1290,9 @@ int ha_prepare(THD *thd)
error=1;
break;
}
+ DEBUG_SYNC(thd, "simulate_hang_after_binlog_prepare");
+ DBUG_EXECUTE_IF("simulate_crash_after_binlog_prepare",
+ DBUG_SUICIDE(););
}
else
{
@@ -1795,6 +1798,8 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
++count;
ha_info_next= ha_info->next();
ha_info->reset(); /* keep it conveniently zero-filled */
+ DBUG_EXECUTE_IF("simulate_crash_after_binlog_commit",
+ DBUG_SUICIDE(););
}
trans->ha_list= 0;
trans->no_2pc=0;
@@ -1908,6 +1913,8 @@ int ha_rollback_trans(THD *thd, bool all)
status_var_increment(thd->status_var.ha_rollback_count);
ha_info_next= ha_info->next();
ha_info->reset(); /* keep it conveniently zero-filled */
+ DBUG_EXECUTE_IF("simulate_crash_after_binlog_rollback",
+ DBUG_SUICIDE(););
}
trans->ha_list= 0;
trans->no_2pc=0;
@@ -2107,6 +2114,7 @@ struct xarecover_st
int len, found_foreign_xids, found_my_xids;
XID *list;
HASH *commit_list;
+ HASH *xa_prepared_list;
bool dry_run;
};
@@ -2155,7 +2163,23 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
_db_doprnt_("ignore xid %s", xid_to_str(buf, info->list+i));
});
xid_cache_insert(info->list + i, true);
+ XID *foreign_xid= info->list + i;
info->found_foreign_xids++;
+
+ /*
+ For each foreign xid prepraed in engine, check if it is present in
+ xa_prepared_list sent by binlog.
+ */
+ if (info->xa_prepared_list)
+ {
+ struct xa_recovery_member *member= NULL;
+ if ((member= (xa_recovery_member *)
+ my_hash_search(info->xa_prepared_list, foreign_xid->key(),
+ foreign_xid->key_length())))
+ {
+ member->in_engine_prepare= true;
+ }
+ }
continue;
}
if (IF_WSREP(!(wsrep_emulate_bin_log &&
@@ -2202,12 +2226,13 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
return FALSE;
}
-int ha_recover(HASH *commit_list)
+int ha_recover(HASH *commit_list, HASH *xa_prepared_list)
{
struct xarecover_st info;
DBUG_ENTER("ha_recover");
info.found_foreign_xids= info.found_my_xids= 0;
info.commit_list= commit_list;
+ info.xa_prepared_list= xa_prepared_list;
info.dry_run= (info.commit_list==0 && tc_heuristic_recover==0);
info.list= NULL;
@@ -2254,7 +2279,7 @@ int ha_recover(HASH *commit_list)
info.found_my_xids, opt_tc_log_file);
DBUG_RETURN(1);
}
- if (info.commit_list)
+ if (info.commit_list && !info.found_foreign_xids)
sql_print_information("Crash recovery finished.");
DBUG_RETURN(0);
}
diff --git a/sql/handler.h b/sql/handler.h
index 92c2a61ed0e..4ff08d7bd08 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -521,6 +521,8 @@ enum legacy_db_type
DB_TYPE_FIRST_DYNAMIC=45,
DB_TYPE_DEFAULT=127 // Must be last
};
+
+enum xa_binlog_state {XA_PREPARE=0, XA_COMPLETE};
/*
Better name for DB_TYPE_UNKNOWN. Should be used for engines that do not have
a hard-coded type value here.
@@ -806,7 +808,6 @@ struct st_system_tablename
const char *tablename;
};
-
typedef ulonglong my_xid; // this line is the same as in log_event.h
#define MYSQL_XID_PREFIX "MySQLXid"
#define MYSQL_XID_PREFIX_LEN 8 // must be a multiple of 8
@@ -898,6 +899,13 @@ struct xid_t {
};
typedef struct xid_t XID;
+struct xa_recovery_member
+{
+ XID xid;
+ enum xa_binlog_state state;
+ bool in_engine_prepare;
+};
+
/* for recover() handlerton call */
#define MIN_XID_LIST_SIZE 128
#define MAX_XID_LIST_SIZE (1024*128)
@@ -4996,7 +5004,7 @@ int ha_commit_one_phase(THD *thd, bool all);
int ha_commit_trans(THD *thd, bool all);
int ha_rollback_trans(THD *thd, bool all);
int ha_prepare(THD *thd);
-int ha_recover(HASH *commit_list);
+int ha_recover(HASH *commit_list, HASH *xa_recover_list);
/* transactions: these functions never call handlerton functions directly */
int ha_enable_transaction(THD *thd, bool on);
diff --git a/sql/log.cc b/sql/log.cc
index e13f8fbc88f..ed6dc87b262 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -38,6 +38,7 @@
#include "log_event.h" // Query_log_event
#include "rpl_filter.h"
#include "rpl_rli.h"
+#include "rpl_mi.h"
#include "sql_audit.h"
#include "mysqld.h"
@@ -3406,6 +3407,8 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
index_file_name[0] = 0;
bzero((char*) &index_file, sizeof(index_file));
bzero((char*) &purge_index_file, sizeof(purge_index_file));
+ /* non-zero is a marker to conduct xa recovery and related cleanup */
+ xa_recover_list.records= 0;
}
void MYSQL_BIN_LOG::stop_background_thread()
@@ -3467,6 +3470,11 @@ void MYSQL_BIN_LOG::cleanup()
mysql_cond_destroy(&COND_xid_list);
mysql_cond_destroy(&COND_binlog_background_thread);
mysql_cond_destroy(&COND_binlog_background_thread_end);
+ if (!is_relay_log && xa_recover_list.records)
+ {
+ free_root(&mem_root, MYF(0));
+ my_hash_free(&xa_recover_list);
+ }
}
/*
@@ -8028,7 +8036,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
/* Now we have in queue the list of transactions to be committed in order. */
}
-
+
DBUG_ASSERT(is_open());
if (likely(is_open())) // Should always be true
{
@@ -9717,7 +9725,7 @@ int TC_LOG_MMAP::recover()
goto err2; // OOM
}
- if (ha_recover(&xids))
+ if (ha_recover(&xids, 0))
goto err2;
my_hash_free(&xids);
@@ -9758,7 +9766,7 @@ int TC_LOG::using_heuristic_recover()
return 0;
sql_print_information("Heuristic crash recovery mode");
- if (ha_recover(0))
+ if (ha_recover(0, 0))
sql_print_error("Heuristic crash recovery failed");
sql_print_information("Please restart mysqld without --tc-heuristic-recover");
return 1;
@@ -10217,14 +10225,108 @@ start_binlog_background_thread()
return 0;
}
+/**
+ Auxiliary function for ::recover().
+ @returns a successfully created and inserted @c xa_recovery_member
+ into hash @c hash_arg,
+ or NULL.
+*/
+static xa_recovery_member*
+xa_member_insert(HASH *hash_arg, xid_t *xid_arg, xa_binlog_state state_arg,
+ MEM_ROOT *ptr_mem_root)
+{
+ xa_recovery_member *member= (xa_recovery_member*)
+ alloc_root(ptr_mem_root, sizeof(xa_recovery_member));
+ if (!member)
+ return NULL;
+
+ member->xid.set(xid_arg);
+ member->state= state_arg;
+ member->in_engine_prepare= false;
+ return my_hash_insert(hash_arg, (uchar*) member) ? NULL : member;
+}
+/* Inserts or update an existing hash member with a proper state */
+static bool xa_member_replace(HASH *hash_arg, xid_t *xid_arg, bool is_prepare,
+ MEM_ROOT *ptr_mem_root)
+{
+ if(is_prepare)
+ {
+ if (!(xa_member_insert(hash_arg, xid_arg, XA_PREPARE, ptr_mem_root)))
+ return true;
+ }
+ else
+ {
+ /*
+ Search if XID is already present in recovery_list. If found
+ and the state is 'XA_PREPRAED' mark it as XA_COMPLETE.
+ Effectively, there won't be XA-prepare event group replay.
+ */
+ xa_recovery_member* member;
+ if ((member= (xa_recovery_member *)
+ my_hash_search(hash_arg, xid_arg->key(), xid_arg->key_length())))
+ {
+ if (member->state == XA_PREPARE)
+ member->state= XA_COMPLETE;
+ }
+ else // We found only XA COMMIT during recovery insert to list
+ {
+ if (!(member= xa_member_insert(hash_arg,
+ xid_arg, XA_COMPLETE, ptr_mem_root)))
+ return true;
+ }
+ }
+ return false;
+}
+
+extern "C" uchar *xid_get_var_key(xid_t *entry, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= entry->key_length();
+ return (uchar*) entry->key();
+}
+
+/**
+ Performs recovery based on transaction coordinator log for 2pc. At the
+ time of crash, if the binary log was in active state, then recovery for
+ 'xid's and explicit 'XA' transactions is initiated, otherwise the gtid
+ binlog state is updated. For 'xid' and 'XA' based recovery following steps
+ are performed.
+
+ Look for latest binlog checkpoint file. There can be two cases. The active
+ binary log and the latest binlog checkpoint file can be the same.
+
+ Scan the binary log from the beginning.
+ From GTID_LIST and GTID_EVENTs reconstruct the gtid binlog state.
+ Prepare a list of 'xid's for recovery.
+ Prepare a list of explicit 'XA' transactions for recovery.
+ Recover the 'xid' transactions.
+ The explicit 'XA' transaction recovery is initiated once all the server
+ components are initialized. Please check 'execute_xa_for_recovery()'.
+
+ Called from @c MYSQL_BIN_LOG::do_binlog_recovery()
+
+ @param linfo Store here the found log file name and position to
+ the NEXT log file name in the index file.
+
+ @param last_log_name Name of the last active binary log at the time of
+ crash.
+
+ @param first_log Pointer to IO_CACHE of active binary log
+ @param fdle Format_description_log_event of active binary log
+ @param do_xa Is 2pc recovery needed for 'xid's and explicit XA
+ transactions.
+ @return indicates success or failure of recovery.
+ @retval 0 success
+ @retval 1 failure
+
+*/
int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
IO_CACHE *first_log,
Format_description_log_event *fdle, bool do_xa)
{
Log_event *ev= NULL;
HASH xids;
- MEM_ROOT mem_root;
char binlog_checkpoint_name[FN_REFLEN];
bool binlog_checkpoint_found;
bool first_round;
@@ -10237,9 +10339,17 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
bool last_gtid_valid= false;
#endif
- if (! fdle->is_valid() ||
- (do_xa && my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
- sizeof(my_xid), 0, 0, MYF(0))))
+ binlog_checkpoint_name[0]= 0;
+ if (!fdle->is_valid() ||
+ (do_xa &&
+ (my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3,
+ 0,
+ sizeof(my_xid), 0, 0, MYF(0)) ||
+ my_hash_init(&xa_recover_list,
+ &my_charset_bin,
+ TC_LOG_PAGE_SIZE/3,
+ 0, 0,
+ (my_hash_get_key) xid_get_var_key, 0, MYF(0)))))
goto err1;
if (do_xa)
@@ -10313,21 +10423,29 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
#ifdef HAVE_REPLICATION
case GTID_EVENT:
- if (first_round)
{
Gtid_log_event *gev= (Gtid_log_event *)ev;
-
- /* Update the binlog state with any GTID logged after Gtid_list. */
- last_gtid.domain_id= gev->domain_id;
- last_gtid.server_id= gev->server_id;
- last_gtid.seq_no= gev->seq_no;
- last_gtid_standalone=
- ((gev->flags2 & Gtid_log_event::FL_STANDALONE) ? true : false);
- last_gtid_valid= true;
+ if (first_round)
+ {
+ /* Update the binlog state with any GTID logged after Gtid_list. */
+ last_gtid.domain_id= gev->domain_id;
+ last_gtid.server_id= gev->server_id;
+ last_gtid.seq_no= gev->seq_no;
+ last_gtid_standalone=
+ ((gev->flags2 & Gtid_log_event::FL_STANDALONE) ? true : false);
+ last_gtid_valid= true;
+ }
+ if (do_xa &&
+ (gev->flags2 &
+ (Gtid_log_event::FL_PREPARED_XA |
+ Gtid_log_event::FL_COMPLETED_XA)) &&
+ xa_member_replace(&xa_recover_list, &gev->xid,
+ gev->flags2 & Gtid_log_event::FL_PREPARED_XA,
+ &mem_root))
+ goto err2;
+ break;
}
- break;
#endif
-
case START_ENCRYPTION_EVENT:
{
if (fdle->start_decryption((Start_encryption_log_event*) ev))
@@ -10417,10 +10535,22 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
if (do_xa)
{
- if (ha_recover(&xids))
+ if (ha_recover(&xids, &xa_recover_list))
goto err2;
- free_root(&mem_root, MYF(0));
+ DBUG_ASSERT(!xa_recover_list.records ||
+ (binlog_checkpoint_found && binlog_checkpoint_name[0] != 0));
+
+ if (!xa_recover_list.records)
+ {
+ free_root(&mem_root, MYF(0));
+ my_hash_free(&xa_recover_list);
+ }
+ else
+ {
+ xa_binlog_checkpoint_name= strmake_root(&mem_root, binlog_checkpoint_name,
+ strlen(binlog_checkpoint_name));
+ }
my_hash_free(&xids);
}
return 0;
@@ -10436,6 +10566,7 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
{
free_root(&mem_root, MYF(0));
my_hash_free(&xids);
+ my_hash_free(&xa_recover_list);
}
err1:
sql_print_error("Crash recovery failed. Either correct the problem "
@@ -10445,6 +10576,214 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
return 1;
}
+void MYSQL_BIN_LOG::execute_xa_for_recovery()
+{
+ if (xa_recover_list.records)
+ (void) recover_explicit_xa_prepare();
+ free_root(&mem_root, MYF(0));
+ my_hash_free(&xa_recover_list);
+};
+
+/**
+ Performs recovery of explict XA transactions.
+ 'xa_recover_list' contains the list of XA transactions to be recovered.
+ These events are replayed from the binary log to complete the recovery.
+
+ @return indicates success or failure of recovery.
+ @retval false success
+ @retval true failure
+
+*/
+bool MYSQL_BIN_LOG::recover_explicit_xa_prepare()
+{
+#ifndef HAVE_REPLICATION
+ /* Can't be supported without replication applier built in. */
+ return false;
+#else
+ bool err= true;
+ int error=0;
+ Relay_log_info *rli= NULL;
+ rpl_group_info *rgi;
+ THD *thd= new THD(0); /* Needed by start_slave_threads */
+ thd->thread_stack= (char*) &thd;
+ thd->store_globals();
+ thd->security_ctx->skip_grants();
+ IO_CACHE log;
+ const char *errmsg;
+ File file;
+ bool enable_apply_event= false;
+ Log_event *ev = 0;
+ LOG_INFO linfo;
+ int recover_xa_count= xa_recover_list.records;
+ xa_recovery_member *member= NULL;
+
+ //DBUG_ASSERT(!thd->rli_fake);
+
+ if (!(rli= thd->rli_fake= new Relay_log_info(FALSE, "Recovery")))
+ {
+ my_error(ER_OUTOFMEMORY, MYF(ME_FATAL), 1);
+ goto err2;
+ }
+ rli->sql_driver_thd= thd;
+ static LEX_CSTRING connection_name= { STRING_WITH_LEN("Recovery") };
+ rli->mi= new Master_info(&connection_name, false);
+ if (!(rgi= thd->rgi_fake))
+ rgi= thd->rgi_fake= new rpl_group_info(rli);
+ rgi->thd= thd;
+ thd->system_thread_info.rpl_sql_info=
+ new rpl_sql_thread_info(rli->mi->rpl_filter);
+
+ if (rli && !rli->relay_log.description_event_for_exec)
+ {
+ rli->relay_log.description_event_for_exec=
+ new Format_description_log_event(4);
+ }
+ if (find_log_pos(&linfo, xa_binlog_checkpoint_name, 1))
+ {
+ sql_print_error("Binlog file '%s' not found in binlog index, needed "
+ "for recovery. Aborting.", xa_binlog_checkpoint_name);
+ goto err2;
+ }
+
+ tmp_disable_binlog(thd);
+ thd->variables.pseudo_slave_mode= TRUE;
+ for (;;)
+ {
+ if ((file= open_binlog(&log, linfo.log_file_name, &errmsg)) < 0)
+ {
+ sql_print_error("%s", errmsg);
+ goto err1;
+ }
+ while (recover_xa_count > 0 &&
+ (ev= Log_event::read_log_event(&log,
+ rli->relay_log.description_event_for_exec,
+ opt_master_verify_checksum)))
+ {
+ if (!ev->is_valid())
+ {
+ sql_print_error("Found invalid binlog query event %s"
+ " at %s:%lu; error %d %s", ev->get_type_str(),
+ linfo.log_file_name,
+ (ev->log_pos - ev->data_written));
+ goto err1;
+ }
+ enum Log_event_type typ= ev->get_type_code();
+ ev->thd= thd;
+
+ if (typ == FORMAT_DESCRIPTION_EVENT)
+ enable_apply_event= true;
+
+ if (typ == GTID_EVENT)
+ {
+ Gtid_log_event *gev= (Gtid_log_event *)ev;
+ if (gev->flags2 &
+ (Gtid_log_event::FL_PREPARED_XA | Gtid_log_event::FL_COMPLETED_XA))
+ {
+ if ((member=
+ (xa_recovery_member*) my_hash_search(&xa_recover_list,
+ gev->xid.key(),
+ gev->xid.key_length())))
+ {
+ /* Got XA PREPARE query in binlog but check member->state. If it is
+ marked as XA_PREPARE then this PREPARE has not seen its end
+ COMMIT/ROLLBACK. Check if it exists in engine in prepared state.
+ If so apply.
+ */
+ if (gev->flags2 & Gtid_log_event::FL_PREPARED_XA)
+ {
+ if (member->state == XA_PREPARE)
+ {
+ // XA is prepared in binlog and not present in engine then apply
+ if (member->in_engine_prepare == false)
+ enable_apply_event= true;
+ else
+ --recover_xa_count;
+ }
+ }
+ else if (gev->flags2 & Gtid_log_event::FL_COMPLETED_XA)
+ {
+ if (member->state == XA_COMPLETE &&
+ member->in_engine_prepare == true)
+ enable_apply_event= true;
+ else
+ --recover_xa_count;
+ }
+ }
+ }
+ }
+
+ if (enable_apply_event)
+ {
+ if (typ == XA_PREPARE_LOG_EVENT)
+ thd->transaction.xid_state.set_binlogged();
+ if ((err= ev->apply_event(rgi)))
+ {
+ sql_print_error("Failed to execute binlog query event of type: %s,"
+ " at %s:%lu; error %d %s", ev->get_type_str(),
+ linfo.log_file_name,
+ (ev->log_pos - ev->data_written),
+ thd->get_stmt_da()->sql_errno(),
+ thd->get_stmt_da()->message());
+ delete ev;
+ goto err1;
+ }
+ else if (typ == FORMAT_DESCRIPTION_EVENT)
+ enable_apply_event=false;
+ else if (thd->lex->sql_command == SQLCOM_XA_PREPARE ||
+ thd->lex->sql_command == SQLCOM_XA_COMMIT ||
+ thd->lex->sql_command == SQLCOM_XA_ROLLBACK)
+ {
+ --recover_xa_count;
+ enable_apply_event=false;
+
+ sql_print_information("Binlog event %s at %s:%lu"
+ " successfully applied",
+ typ == XA_PREPARE_LOG_EVENT ?
+ static_cast<XA_prepare_log_event *>(ev)->get_query() :
+ static_cast<Query_log_event *>(ev)->query,
+ linfo.log_file_name, (ev->log_pos - ev->data_written));
+ }
+ }
+ if (typ != FORMAT_DESCRIPTION_EVENT)
+ delete ev;
+ }
+ end_io_cache(&log);
+ mysql_file_close(file, MYF(MY_WME));
+ file= -1;
+ if (unlikely((error= find_next_log(&linfo, 1))))
+ {
+ if (error != LOG_INFO_EOF)
+ sql_print_error("find_log_pos() failed (error: %d)", error);
+ else
+ break;
+ }
+ }
+err1:
+ reenable_binlog(thd);
+ /*
+ There should be no more XA transactions to recover upon successful
+ completion.
+ */
+ if (recover_xa_count > 0)
+ goto err2;
+ sql_print_information("Crash recovery finished.");
+ err= false;
+err2:
+ if (file >= 0)
+ {
+ end_io_cache(&log);
+ mysql_file_close(file, MYF(MY_WME));
+ }
+ thd->variables.pseudo_slave_mode= FALSE;
+ delete rli->mi;
+ delete thd->system_thread_info.rpl_sql_info;
+ rgi->slave_close_thread_tables(thd);
+ thd->reset_globals();
+ delete thd;
+
+ return err;
+#endif /* !HAVE_REPLICATION */
+}
int
MYSQL_BIN_LOG::do_binlog_recovery(const char *opt_name, bool do_xa_recovery)
diff --git a/sql/log.h b/sql/log.h
index 8e70d3c8f4c..9bf3248d4c9 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -63,6 +63,7 @@ class TC_LOG
virtual int unlog(ulong cookie, my_xid xid)=0;
virtual int unlog_xa_prepare(THD *thd, bool all)= 0;
virtual void commit_checkpoint_notify(void *cookie)= 0;
+ virtual void execute_xa_for_recovery() {};
protected:
/*
@@ -708,6 +709,8 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
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);
+ bool recover_explicit_xa_prepare();
+
int do_binlog_recovery(const char *opt_name, bool do_xa_recovery);
#if !defined(MYSQL_CLIENT)
@@ -932,7 +935,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
mysql_mutex_t* get_binlog_end_pos_lock() { return &LOCK_binlog_end_pos; }
int wait_for_update_binlog_end_pos(THD* thd, struct timespec * timeout);
-
+ void execute_xa_for_recovery();
/*
Binlog position of end of the binlog.
Access to this is protected by LOCK_binlog_end_pos
@@ -945,6 +948,9 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
*/
my_off_t binlog_end_pos;
char binlog_end_pos_file[FN_REFLEN];
+ MEM_ROOT mem_root;
+ char *xa_binlog_checkpoint_name;
+ HASH xa_recover_list;
};
class Log_event_handler
diff --git a/sql/log_event.cc b/sql/log_event.cc
index ee44f7f1da4..9adfefceb97 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -18,7 +18,7 @@
#include "mariadb.h"
#include "sql_priv.h"
-
+#include "handler.h"
#ifndef MYSQL_CLIENT
#include "unireg.h"
#include "log_event.h"
@@ -2812,9 +2812,25 @@ XA_prepare_log_event(const char* buf,
buf += sizeof(temp);
memcpy(&temp, buf, sizeof(temp));
m_xid.gtrid_length= uint4korr(&temp);
+ // Todo: validity here and elsewhere checks to be replaced by MDEV-21839 fixes
+ if (m_xid.gtrid_length < 0 || m_xid.gtrid_length > MAXGTRIDSIZE)
+ {
+ m_xid.formatID= -1;
+ return;
+ }
buf += sizeof(temp);
memcpy(&temp, buf, sizeof(temp));
m_xid.bqual_length= uint4korr(&temp);
+ if (m_xid.bqual_length < 0 || m_xid.bqual_length > MAXBQUALSIZE)
+ {
+ m_xid.formatID= -1;
+ return;
+ }
+ if (m_xid.gtrid_length + m_xid.bqual_length > XIDDATASIZE)
+ {
+ m_xid.formatID= -1;
+ return;
+ }
buf += sizeof(temp);
memcpy(m_xid.data, buf, m_xid.gtrid_length + m_xid.bqual_length);
diff --git a/sql/log_event.h b/sql/log_event.h
index a6543b70eb5..595dc9f6c3c 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -3234,6 +3234,7 @@ class XA_prepare_log_event: public Xid_apply_log_event
const Format_description_log_event *description_event);
~XA_prepare_log_event() {}
Log_event_type get_type_code() { return XA_PREPARE_LOG_EVENT; }
+ bool is_valid() const { return m_xid.formatID != -1; }
int get_data_size()
{
return xid_subheader_no_data + m_xid.gtrid_length + m_xid.bqual_length;
@@ -3241,12 +3242,7 @@ class XA_prepare_log_event: public Xid_apply_log_event
#ifdef MYSQL_SERVER
bool write();
-#endif
-
-private:
-#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- char query[sizeof("XA COMMIT ONE PHASE") + 1 + ser_buf_size];
- int do_commit();
+#ifdef HAVE_REPLICATION
const char* get_query()
{
sprintf(query,
@@ -3254,6 +3250,13 @@ class XA_prepare_log_event: public Xid_apply_log_event
m_xid.serialize());
return query;
}
+#endif /* HAVE_REPLICATION */
+#endif /* MYSQL_SERVER */
+
+private:
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ char query[sizeof("XA COMMIT ONE PHASE") + 1 + ser_buf_size];
+ int do_commit();
#endif
};
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index b2f8afca7a6..f669a4ca5d8 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -5189,7 +5189,7 @@ static int init_server_components()
unireg_abort(1);
}
- if (ha_recover(0))
+ if (ha_recover(0, 0))
{
unireg_abort(1);
}
@@ -5606,7 +5606,7 @@ int mysqld_main(int argc, char **argv)
initialize_information_schema_acl();
execute_ddl_log_recovery();
-
+ tc_log->execute_xa_for_recovery();
/*
Change EVENTS_ORIGINAL to EVENTS_OFF (the default value) as there is no
point in using ORIGINAL during startup
diff --git a/sql/xa.cc b/sql/xa.cc
index 786d09c2b39..df7d0229157 100644
--- a/sql/xa.cc
+++ b/sql/xa.cc
@@ -581,6 +581,10 @@ bool trans_xa_commit(THD *thd)
XID_STATE &xid_state= thd->transaction.xid_state;
DBUG_ENTER("trans_xa_commit");
+ DBUG_EXECUTE_IF("trans_xa_commit_fail",
+ {my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ DBUG_RETURN(TRUE);});
+
if (!xid_state.is_explicit_XA() ||
!xid_state.xid_cache_element->xid.eq(thd->lex->xid))
1
0
[Commits] [andrei.elkin@mariadb.com] 25fe744e5c3: MDEV-742 XA PREPAREd transaction survive disconnect/server restart
by andrei.elkinīŧ pp.inet.fi 04 Mar '20
by andrei.elkinīŧ pp.inet.fi 04 Mar '20
04 Mar '20
Howdy, Kristian.
I am forwarding for your attention a patch that HEADs bb-10.5-mdev_742
branch. While it's of some size maybe you'll find some time to review
changes/extension esp done to the pararellel slave.
Looking forward to hear from you!
Thanks.
Andrei
revision-id: 25fe744e5c3d2e8f7d3db7eed37d8e412837457b (mariadb-10.5.0-283-g25fe744e5c3)
parent(s): 36cebe53a3645bf1e665ffdf5b552cabcc1e8e56
author: Andrei Elkin
committer: Andrei Elkin
timestamp: 2020-03-04 15:48:43 +0200
message:
MDEV-742 XA PREPAREd transaction survive disconnect/server restart
Lifted long standing limitation to the XA of rolling it back at the
transaction's
connection close even if the XA is prepared.
Prepared XA-transaction is made to sustain connection close or server
restart.
The patch consists of
- binary logging extension to write prepared XA part of
transaction signified with
its XID in a new XA_prepare_log_event. The concusion part -
with Commit or Rollback decision - is logged separately as
Query_log_event.
That is in the binlog the XA consists of two separate group of
events.
That makes the whole XA possibly interweaving in binlog with
other XA:s or regular transaction but with no harm to
replication and data consistency.
Gtid_log_event receives two more flags to identify which of the
two XA phases of the transaction it represents. With either flag
set also XID info is added to the event.
- engines are made aware of the server policy to keep up user
prepared XA:s so they (Innodb, rocksdb) don't roll them back
anymore at their disconnect methods.
- slave applier is refined to cope with two phase logged XA:s
including parallel modes of execution.
This patch does not address crash-safe logging of the new events which
is being addressed by MDEV-21469, its commit is to be published shortly.
There's a list of fixes to 10.5 that were required by this MDEV, incl
MDEV-21856: XID::formatID is constrained to 4 bytes by requirements of
cross-platform replication and Innodb legacy.
MDEV-21659 XA rollback 'foreign_xid' is allowed inside active XA
MDEV-21766 Forbid XID with empty 'gtrid'
MDEV-21854 xa commit 'xid' one phase for already prepared transaction must always error out
Many thanks to Alexey Botchkov for driving this work initially!
---
mysql-test/include/kill_and_restart_mysqld.inc | 15 +
mysql-test/main/flush_read_lock.result | 76 +-
mysql-test/main/flush_read_lock.test | 112 +-
mysql-test/main/xa.result | 61 +
mysql-test/main/xa.test | 49 +
mysql-test/main/xa_binlog.result | 11 +-
mysql-test/main/xa_binlog.test | 2 +-
mysql-test/main/xa_prepared_binlog_off-master.opt | 1 +
mysql-test/main/xa_prepared_binlog_off.result | 1044 +++++++++++++++++
mysql-test/main/xa_prepared_binlog_off.test | 11 +
mysql-test/main/xa_sync.result | 10 +
mysql-test/main/xa_sync.test | 5 +
.../include/binlog_xa_prepare_connection.inc | 31 +
.../include/binlog_xa_prepare_disconnect.inc | 35 +
.../include/binlog_xa_prepared_do_and_restart.inc | 323 ++++++
.../suite/binlog/r/binlog_xa_checkpoint.result | 33 +
.../suite/binlog/r/binlog_xa_prepared.result | 1176 ++++++++++++++++++++
.../binlog/r/binlog_xa_prepared_disconnect.result | 1176 ++++++++++++++++++++
.../suite/binlog/t/binlog_xa_checkpoint.test | 57 +
mysql-test/suite/binlog/t/binlog_xa_prepared.inc | 102 ++
.../binlog/t/binlog_xa_prepared_disconnect.test | 11 +
.../suite/rpl/include/rpl_xa_mixed_engines.inc | 183 +++
.../suite/rpl/r/rpl_parallel_optimistic_xa.result | 51 +
.../r/rpl_parallel_optimistic_xa_lsu_off.result | 51 +
.../suite/rpl/r/rpl_parallel_xa_same_xid.result | 23 +
mysql-test/suite/rpl/r/rpl_temporary_errors.result | 47 +-
mysql-test/suite/rpl/r/rpl_xa.result | 48 +
mysql-test/suite/rpl/r/rpl_xa_gap_lock.result | 44 +
.../suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result | 64 ++
.../suite/rpl/r/rpl_xa_survive_disconnect.result | 319 ++++++
.../rpl/r/rpl_xa_survive_disconnect_lsu_off.result | 319 ++++++
.../rpl_xa_survive_disconnect_mixed_engines.result | 373 +++++++
.../suite/rpl/t/rpl_parallel_optimistic_xa.test | 235 ++++
.../t/rpl_parallel_optimistic_xa_lsu_off-slave.opt | 1 +
.../rpl/t/rpl_parallel_optimistic_xa_lsu_off.test | 2 +
.../suite/rpl/t/rpl_parallel_xa_same_xid.test | 138 +++
mysql-test/suite/rpl/t/rpl_temporary_errors.test | 82 +-
mysql-test/suite/rpl/t/rpl_xa.inc | 73 ++
mysql-test/suite/rpl/t/rpl_xa.test | 5 +
mysql-test/suite/rpl/t/rpl_xa_gap_lock-slave.opt | 1 +
mysql-test/suite/rpl/t/rpl_xa_gap_lock.test | 137 +++
.../suite/rpl/t/rpl_xa_gtid_pos_auto_engine.test | 29 +
.../suite/rpl/t/rpl_xa_survive_disconnect.test | 294 +++++
.../t/rpl_xa_survive_disconnect_lsu_off-slave.opt | 2 +
.../rpl/t/rpl_xa_survive_disconnect_lsu_off.test | 8 +
.../t/rpl_xa_survive_disconnect_mixed_engines.test | 68 ++
sql/handler.cc | 11 +-
sql/log.cc | 248 ++++-
sql/log.h | 10 +
sql/log_event.cc | 59 +-
sql/log_event.h | 213 +++-
sql/log_event_client.cc | 37 +-
sql/log_event_server.cc | 298 +++--
sql/rpl_parallel.cc | 220 +++-
sql/rpl_parallel.h | 28 +-
sql/rpl_rli.cc | 14 +-
sql/rpl_rli.h | 4 +
sql/slave.cc | 10 +-
sql/sql_repl.cc | 4 +-
sql/xa.cc | 254 ++++-
sql/xa.h | 22 +-
storage/innobase/handler/ha_innodb.cc | 1 +
storage/innobase/trx/trx0trx.cc | 19 +-
storage/rocksdb/ha_rocksdb.cc | 18 +-
storage/rocksdb/mysql-test/rocksdb/r/xa.result | 41 +-
storage/rocksdb/mysql-test/rocksdb/t/xa.test | 43 +-
.../rocksdb/mysql-test/rocksdb_rpl/r/rpl_xa.result | 50 +
.../rocksdb/mysql-test/rocksdb_rpl/t/rpl_xa.inc | 70 ++
.../rocksdb/mysql-test/rocksdb_rpl/t/rpl_xa.test | 6 +
.../tokudb/mysql-test/tokudb_mariadb/r/xa.result | 1 +
storage/tokudb/mysql-test/tokudb_mariadb/t/xa.test | 3 +
71 files changed, 8410 insertions(+), 212 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 0f8c2ce9fb9..be710050139 100644
--- a/mysql-test/main/flush_read_lock.result
+++ b/mysql-test/main/flush_read_lock.result
@@ -1310,6 +1310,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;
@@ -1322,11 +1324,25 @@ connection con1;
flush tables with read lock;
connection default;
xa end 'test1';
-xa prepare 'test1';
+xa prepare 'test1';;
+connection con1;
+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.
unlock tables;
connection default;
+# Reap XA ROLLBACK
xa start 'test1';
insert into t3_trans values (1);
connection con1;
@@ -1334,7 +1350,20 @@ 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.
+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;
@@ -1344,6 +1373,51 @@ 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.
+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.
+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..4283358770c 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 backup 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 backup 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 backup 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;
@@ -1631,6 +1679,64 @@ connection default;
--echo # Reap XA COMMIT.
--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 backup 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 backup 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';
diff --git a/mysql-test/main/xa.result b/mysql-test/main/xa.result
index bd2946247d8..aa48f6d26c7 100644
--- a/mysql-test/main/xa.result
+++ b/mysql-test/main/xa.result
@@ -66,6 +66,9 @@ select * from t1;
a
20
disconnect con1;
+xa rollback 'testb',0x2030405060,11;
+xa recover;
+formatID gtrid_length bqual_length data
connection default;
xa start 'tr1';
insert t1 values (40);
@@ -376,3 +379,61 @@ XA PREPARE 'Đ¯_ŅĐŋaĐģa_c_ŅĐĩĐŊОваĐģĐ°_ŅĐžŅĐŧОСиĐģĐ°_ĐŗĐžĐģОвОК';
XA ROLLBACK 'Đ¯_ŅĐŋaĐģa_c_ŅĐĩĐŊОваĐģĐ°_ŅĐžŅĐŧОСиĐģĐ°_ĐŗĐžĐģОвОК';
SET NAMES default;
DROP TABLE t1;
+# MDEV-7974 related
+# 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) engine=innodb;
+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';
+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 55c41452635..ca09989dfc8 100644
--- a/mysql-test/main/xa.test
+++ b/mysql-test/main/xa.test
@@ -98,6 +98,8 @@ xa start 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz';
select * from t1;
disconnect con1;
--source include/wait_until_count_sessions.inc
+xa rollback 'testb',0x2030405060,11;
+xa recover;
connection default;
@@ -523,3 +525,50 @@ SET NAMES default;
DROP TABLE t1;
--source include/wait_until_count_sessions.inc
+
+--echo # MDEV-7974 related
+--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) engine=innodb;
+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/main/xa_binlog.result b/mysql-test/main/xa_binlog.result
index 619a6e08b20..c45749d500f 100644
--- a/mysql-test/main/xa_binlog.result
+++ b/mysql-test/main/xa_binlog.result
@@ -18,14 +18,17 @@ a
1
2
3
-SHOW BINLOG EVENTS LIMIT 3,9;
+SHOW BINLOG EVENTS LIMIT 3,12;
Log_name Pos Event_type Server_id End_log_pos Info
-master-bin.000001 # Gtid 1 # BEGIN GTID #-#-#
+master-bin.000001 # Gtid 1 # XA START X'786174657374',X'',1 GTID #-#-#
master-bin.000001 # Query 1 # use `test`; INSERT INTO t1 VALUES (1)
-master-bin.000001 # Query 1 # COMMIT
+master-bin.000001 # Query 1 # XA END X'786174657374',X'',1
+master-bin.000001 # XA_prepare 1 # XA PREPARE X'786174657374',X'',1
+master-bin.000001 # Gtid 1 # GTID #-#-#
+master-bin.000001 # Query 1 # XA COMMIT X'786174657374',X'',1
master-bin.000001 # Gtid 1 # BEGIN GTID #-#-#
master-bin.000001 # Query 1 # use `test`; INSERT INTO t1 VALUES (2)
-master-bin.000001 # Query 1 # COMMIT
+master-bin.000001 # Xid 1 # COMMIT /* xid=XX */
master-bin.000001 # Gtid 1 # BEGIN GTID #-#-#
master-bin.000001 # Query 1 # use `test`; INSERT INTO t1 VALUES (3)
master-bin.000001 # Xid 1 # COMMIT /* xid=XX */
diff --git a/mysql-test/main/xa_binlog.test b/mysql-test/main/xa_binlog.test
index ecbf1f4f066..91bca2ac8cb 100644
--- a/mysql-test/main/xa_binlog.test
+++ b/mysql-test/main/xa_binlog.test
@@ -27,6 +27,6 @@ SELECT * FROM t1 ORDER BY a;
--replace_column 2 # 5 #
--replace_regex /xid=[0-9]+/xid=XX/ /GTID [0-9]+-[0-9]+-[0-9]+/GTID #-#-#/
-SHOW BINLOG EVENTS LIMIT 3,9;
+SHOW BINLOG EVENTS LIMIT 3,12;
DROP TABLE t1;
diff --git a/mysql-test/main/xa_prepared_binlog_off-master.opt b/mysql-test/main/xa_prepared_binlog_off-master.opt
new file mode 100644
index 00000000000..789275fa25e
--- /dev/null
+++ b/mysql-test/main/xa_prepared_binlog_off-master.opt
@@ -0,0 +1 @@
+--skip-log-bin
diff --git a/mysql-test/main/xa_prepared_binlog_off.result b/mysql-test/main/xa_prepared_binlog_off.result
new file mode 100644
index 00000000000..ca19f6cdfaf
--- /dev/null
+++ b/mysql-test/main/xa_prepared_binlog_off.result
@@ -0,0 +1,1044 @@
+call mtr.add_suppression("You need to use --log-bin to make --log-slave-updates work.");
+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';
+# 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
+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/main/xa_prepared_binlog_off.test b/mysql-test/main/xa_prepared_binlog_off.test
new file mode 100644
index 00000000000..edbfa7c2825
--- /dev/null
+++ b/mysql-test/main/xa_prepared_binlog_off.test
@@ -0,0 +1,11 @@
+###############################################################################
+# MDEV-7974 (bug#12161 Xa recovery and client disconnection)
+# Testing XA behaviour with binlog turned off.
+###############################################################################
+
+--source include/not_valgrind.inc
+--source include/not_embedded.inc
+
+# Common part with XA binlogging testing
+call mtr.add_suppression("You need to use --log-bin to make --log-slave-updates work.");
+--source suite/binlog/t/binlog_xa_prepared.inc
diff --git a/mysql-test/main/xa_sync.result b/mysql-test/main/xa_sync.result
index 1482ff5cacf..e7dd9b02847 100644
--- a/mysql-test/main/xa_sync.result
+++ b/mysql-test/main/xa_sync.result
@@ -18,6 +18,11 @@ disconnect con1;
SET debug_sync='now SIGNAL go';
connection con2;
ERROR XAE04: XAER_NOTA: Unknown XID
+*** Must have 'xatest' in the list
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 6 0 xatest
+XA COMMIT 'xatest';
disconnect con2;
connection default;
SET debug_sync='RESET';
@@ -37,6 +42,11 @@ disconnect con1;
SET debug_sync='now SIGNAL go';
connection con2;
ERROR XAE04: XAER_NOTA: Unknown XID
+*** Must have 'xatest' in the list
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 6 0 xatest
+XA ROLLBACK 'xatest';
disconnect con2;
connection default;
SET debug_sync='RESET';
diff --git a/mysql-test/main/xa_sync.test b/mysql-test/main/xa_sync.test
index bb95af7c0ba..2fe7337501e 100644
--- a/mysql-test/main/xa_sync.test
+++ b/mysql-test/main/xa_sync.test
@@ -35,6 +35,11 @@ while ($i)
connection con2;
--error ER_XAER_NOTA
reap;
+ --echo *** Must have 'xatest' in the list
+ XA RECOVER;
+ # second time yields no error
+ --error 0
+ --eval $op
disconnect con2;
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..cbd740fdae4
--- /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 suite/binlog/include/binlog_xa_prepare_connection.inc
+
+--let $index = 2
+--source suite/binlog/include/binlog_xa_prepare_connection.inc
+
+--let $index = 3
+--source suite/binlog/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 suite/binlog/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 suite/binlog/include/binlog_xa_prepare_connection.inc
+
+--let $index = 2
+--source suite/binlog/include/binlog_xa_prepare_connection.inc
+
+--let $index = 3
+--source suite/binlog/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 suite/binlog/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 suite/binlog/include/binlog_xa_prepare_connection.inc
+
+--let $index = 2
+--source suite/binlog/include/binlog_xa_prepare_connection.inc
+
+--let $index = 3
+--source suite/binlog/include/binlog_xa_prepare_connection.inc
+--let $conn3_id=`SELECT connection_id()`
+
+--let $terminate_with = XA COMMIT
+--inc $num_trx_prepared
+--source suite/binlog/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_checkpoint.result b/mysql-test/suite/binlog/r/binlog_xa_checkpoint.result
new file mode 100644
index 00000000000..d8a5818674f
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_xa_checkpoint.result
@@ -0,0 +1,33 @@
+RESET MASTER;
+CREATE TABLE t1 (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=Innodb;
+connect con1,localhost,root,,;
+SET DEBUG_SYNC= "at_unlog_xa_prepare SIGNAL con1_ready WAIT_FOR con1_go";
+XA START '1';
+INSERT INTO t1 SET a=1;
+XA END '1';
+XA PREPARE '1';;
+connection default;
+SET DEBUG_SYNC= "now WAIT_FOR con1_ready";
+FLUSH LOGS;
+FLUSH LOGS;
+FLUSH LOGS;
+show binary logs;
+Log_name File_size
+master-bin.000001 #
+master-bin.000002 #
+master-bin.000003 #
+master-bin.000004 #
+include/show_binlog_events.inc
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000004 # Format_desc # # SERVER_VERSION, BINLOG_VERSION
+master-bin.000004 # Gtid_list # # [#-#-#]
+master-bin.000004 # Binlog_checkpoint # # master-bin.000001
+SET DEBUG_SYNC= "now SIGNAL con1_go";
+connection con1;
+*** master-bin.000004 checkpoint must show up now ***
+connection con1;
+XA ROLLBACK '1';
+SET debug_sync = 'reset';
+connection default;
+DROP TABLE t1;
+SET debug_sync = 'reset';
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..9fda8ab3143
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_xa_prepared.result
@@ -0,0 +1,1176 @@
+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';
+# 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
+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 # # XA START X'7472785f30',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=0
+master-bin.000001 # Query # # XA END X'7472785f30',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f30',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f31',X'',1 GTID #-#-#
+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 X'7472785f31',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f31',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f32',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=2
+master-bin.000001 # Query # # XA END X'7472785f32',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f32',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f33',X'',1 GTID #-#-#
+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 X'7472785f33',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f33',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f34',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=4
+master-bin.000001 # Query # # XA END X'7472785f34',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f34',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f35',X'',1 GTID #-#-#
+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 X'7472785f35',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f35',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f36',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=6
+master-bin.000001 # Query # # XA END X'7472785f36',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f36',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f37',X'',1 GTID #-#-#
+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 X'7472785f37',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f37',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f38',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=8
+master-bin.000001 # Query # # XA END X'7472785f38',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f38',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f39',X'',1 GTID #-#-#
+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 X'7472785f39',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f39',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3130',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=10
+master-bin.000001 # Query # # XA END X'7472785f3130',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3130',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3131',X'',1 GTID #-#-#
+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 X'7472785f3131',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3131',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3132',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=12
+master-bin.000001 # Query # # XA END X'7472785f3132',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3132',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3133',X'',1 GTID #-#-#
+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 X'7472785f3133',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3133',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3134',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=14
+master-bin.000001 # Query # # XA END X'7472785f3134',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3134',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3135',X'',1 GTID #-#-#
+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 X'7472785f3135',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3135',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3136',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=16
+master-bin.000001 # Query # # XA END X'7472785f3136',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3136',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3137',X'',1 GTID #-#-#
+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 X'7472785f3137',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3137',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3138',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=18
+master-bin.000001 # Query # # XA END X'7472785f3138',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3138',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3139',X'',1 GTID #-#-#
+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 X'7472785f3139',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3139',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA ROLLBACK X'7472785f30',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA ROLLBACK X'7472785f31',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA ROLLBACK X'7472785f32',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA ROLLBACK X'7472785f33',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA ROLLBACK X'7472785f34',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'7472785f35',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'7472785f36',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'7472785f37',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'7472785f38',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'7472785f39',X'',1
+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/r/binlog_xa_prepared_disconnect.result b/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result
new file mode 100644
index 00000000000..9fda8ab3143
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result
@@ -0,0 +1,1176 @@
+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';
+# 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
+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 # # XA START X'7472785f30',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=0
+master-bin.000001 # Query # # XA END X'7472785f30',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f30',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f31',X'',1 GTID #-#-#
+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 X'7472785f31',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f31',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f32',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=2
+master-bin.000001 # Query # # XA END X'7472785f32',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f32',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f33',X'',1 GTID #-#-#
+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 X'7472785f33',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f33',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f34',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=4
+master-bin.000001 # Query # # XA END X'7472785f34',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f34',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f35',X'',1 GTID #-#-#
+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 X'7472785f35',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f35',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f36',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=6
+master-bin.000001 # Query # # XA END X'7472785f36',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f36',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f37',X'',1 GTID #-#-#
+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 X'7472785f37',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f37',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f38',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=8
+master-bin.000001 # Query # # XA END X'7472785f38',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f38',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f39',X'',1 GTID #-#-#
+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 X'7472785f39',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f39',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3130',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=10
+master-bin.000001 # Query # # XA END X'7472785f3130',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3130',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3131',X'',1 GTID #-#-#
+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 X'7472785f3131',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3131',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3132',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=12
+master-bin.000001 # Query # # XA END X'7472785f3132',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3132',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3133',X'',1 GTID #-#-#
+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 X'7472785f3133',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3133',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3134',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=14
+master-bin.000001 # Query # # XA END X'7472785f3134',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3134',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3135',X'',1 GTID #-#-#
+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 X'7472785f3135',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3135',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3136',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=16
+master-bin.000001 # Query # # XA END X'7472785f3136',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3136',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3137',X'',1 GTID #-#-#
+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 X'7472785f3137',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3137',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3138',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=18
+master-bin.000001 # Query # # XA END X'7472785f3138',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3138',X'',1
+master-bin.000001 # Gtid # # XA START X'7472785f3139',X'',1 GTID #-#-#
+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 X'7472785f3139',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'7472785f3139',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA ROLLBACK X'7472785f30',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA ROLLBACK X'7472785f31',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA ROLLBACK X'7472785f32',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA ROLLBACK X'7472785f33',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA ROLLBACK X'7472785f34',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'7472785f35',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'7472785f36',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'7472785f37',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'7472785f38',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'7472785f39',X'',1
+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_checkpoint.test b/mysql-test/suite/binlog/t/binlog_xa_checkpoint.test
new file mode 100644
index 00000000000..b208d02cf2a
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_xa_checkpoint.test
@@ -0,0 +1,57 @@
+--source include/have_innodb.inc
+--source include/have_debug.inc
+--source include/have_debug_sync.inc
+--source include/have_binlog_format_row.inc
+
+RESET MASTER;
+
+CREATE TABLE t1 (a INT PRIMARY KEY, b MEDIUMTEXT) ENGINE=Innodb;
+
+# Test that
+# 1. XA PREPARE is binlogged before the XA has been prepared in Engine
+# 2. While XA PREPARE already binlogged in an old binlog file which has been rotated,
+# Binlog checkpoint is not generated for the latest log until
+# XA PREPARE returns, e.g OK to the client.
+
+
+# con1 will hang before doing commit checkpoint, blocking RESET MASTER.
+connect(con1,localhost,root,,);
+SET DEBUG_SYNC= "at_unlog_xa_prepare SIGNAL con1_ready WAIT_FOR con1_go";
+XA START '1';
+INSERT INTO t1 SET a=1;
+XA END '1';
+--send XA PREPARE '1';
+
+
+connection default;
+SET DEBUG_SYNC= "now WAIT_FOR con1_ready";
+FLUSH LOGS;
+FLUSH LOGS;
+FLUSH LOGS;
+
+--source include/show_binary_logs.inc
+--let $binlog_file= master-bin.000004
+--let $binlog_start= 4
+--source include/show_binlog_events.inc
+
+SET DEBUG_SYNC= "now SIGNAL con1_go";
+
+connection con1;
+reap;
+--echo *** master-bin.000004 checkpoint must show up now ***
+--source include/wait_for_binlog_checkpoint.inc
+
+# Todo: think about the error code returned, move to an appropriate test, or remove
+# connection default;
+#--error 1399
+# DROP TABLE t1;
+
+connection con1;
+XA ROLLBACK '1';
+SET debug_sync = 'reset';
+
+# Clean up.
+connection default;
+
+DROP TABLE t1;
+SET debug_sync = 'reset';
diff --git a/mysql-test/suite/binlog/t/binlog_xa_prepared.inc b/mysql-test/suite/binlog/t/binlog_xa_prepared.inc
new file mode 100644
index 00000000000..b6306791cf4
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_xa_prepared.inc
@@ -0,0 +1,102 @@
+--source include/have_innodb.inc
+--source include/have_perfschema.inc
+#
+# The test verifies binlogging of XA transaction and state of prepared XA
+# as far as binlog is concerned.
+#
+# 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.
+#
+# 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 suite/binlog/include/binlog_xa_prepared_do_and_restart.inc
+
+--let $how_to_restart=kill_and_restart_mysqld.inc
+--source suite/binlog/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
+}
+
+--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..2a3184030cf
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_xa_prepared_disconnect.test
@@ -0,0 +1,11 @@
+###############################################################################
+# Bug#12161 Xa recovery and client disconnection
+# Testing new server options and binary logging prepared XA transaction.
+###############################################################################
+
+#
+# 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.inc
diff --git a/mysql-test/suite/rpl/include/rpl_xa_mixed_engines.inc b/mysql-test/suite/rpl/include/rpl_xa_mixed_engines.inc
new file mode 100644
index 00000000000..0707a04090a
--- /dev/null
+++ b/mysql-test/suite/rpl/include/rpl_xa_mixed_engines.inc
@@ -0,0 +1,183 @@
+#
+# The test file is invoked from rpl.rpl_xa_survive_disconnect_mixed_engines
+#
+# The test file is orginized as three sections: setup, run and cleanup.
+# The main logics is resided in the run section which generates
+# three types of XA transaction: two kinds of mixed and one on non-transactional
+# table.
+#
+# param $command one of three of: 'setup', 'run' or 'cleanup'
+# param $xa_terminate how to conclude: 'XA COMMIT' or 'XA ROLLBACK'
+# param $one_phase 'one_phase' can be opted with XA COMMIT above
+# param $xa_prepare_opt '1' or empty can be opted to test with and without XA PREPARE
+# param $xid arbitrary name for xa trx, defaults to 'xa_trx'
+# Note '' is merely to underline, not a part of the value.
+#
+
+if ($command == setup)
+{
+ # 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;
+ CREATE TABLE tm (a INT) ENGINE=myisam;
+}
+if (!$xid)
+{
+ --let $xid=xa_trx
+}
+if ($command == run)
+{
+ ## Non-temporary table cases
+ # Non transactional table goes first
+ --eval XA START '$xid'
+ --disable_warnings
+ INSERT INTO tm VALUES (1);
+ INSERT INTO t VALUES (1);
+ --enable_warnings
+ --eval XA END '$xid'
+ if ($xa_prepare_opt)
+ {
+ --eval XA PREPARE '$xid'
+ }
+ --eval $xa_terminate '$xid' $one_phase
+
+ # Transactional table goes first
+ --eval XA START '$xid'
+ --disable_warnings
+ INSERT INTO t VALUES (2);
+ INSERT INTO tm VALUES (2);
+ --enable_warnings
+ --eval XA END '$xid'
+ if ($xa_prepare_opt)
+ {
+ --eval XA PREPARE '$xid'
+ }
+ --eval $xa_terminate '$xid' $one_phase
+
+ # The pure non-transactional table
+ --eval XA START '$xid'
+ --disable_warnings
+ INSERT INTO tm VALUES (3);
+ --enable_warnings
+ --eval XA END '$xid'
+ if ($xa_prepare_opt)
+ {
+ --eval XA PREPARE '$xid'
+ }
+ --eval $xa_terminate '$xid' $one_phase
+
+ ## Temporary tables
+ # create outside xa use at the tail
+ CREATE TEMPORARY TABLE tmp_i LIKE t;
+ CREATE TEMPORARY TABLE tmp_m LIKE tm;
+ --eval XA START '$xid'
+ --disable_warnings
+ INSERT INTO t VALUES (4);
+ INSERT INTO tm VALUES (4);
+ INSERT INTO tmp_i VALUES (4);
+ INSERT INTO tmp_m VALUES (4);
+ INSERT INTO t SELECT * FROM tmp_i;
+ INSERT INTO tm SELECT * FROM tmp_m;
+ --enable_warnings
+ --eval XA END '$xid'
+ if ($xa_prepare_opt)
+ {
+ --eval XA PREPARE '$xid'
+ }
+ --eval $xa_terminate '$xid' $one_phase
+
+ # temporary tables at the head
+ --eval XA START '$xid'
+ --disable_warnings
+ INSERT INTO tmp_i VALUES (5);
+ INSERT INTO tmp_m VALUES (5);
+ INSERT INTO t SELECT * FROM tmp_i;
+ INSERT INTO tm SELECT * FROM tmp_m;
+ INSERT INTO t VALUES (5);
+ INSERT INTO tm VALUES (5);
+ --enable_warnings
+ --eval XA END '$xid'
+ if ($xa_prepare_opt)
+ {
+ --eval XA PREPARE '$xid'
+ }
+ --eval $xa_terminate '$xid' $one_phase
+
+ # create inside xa use at the tail
+ DROP TEMPORARY TABLE tmp_i;
+ DROP TEMPORARY TABLE tmp_m;
+
+ --eval XA START '$xid'
+ --disable_warnings
+ INSERT INTO t VALUES (6);
+ INSERT INTO tm VALUES (6);
+ CREATE TEMPORARY TABLE tmp_i LIKE t;
+ CREATE TEMPORARY TABLE tmp_m LIKE tm;
+ INSERT INTO tmp_i VALUES (6);
+ INSERT INTO tmp_m VALUES (6);
+ INSERT INTO t SELECT * FROM tmp_i;
+ INSERT INTO tm SELECT * FROM tmp_m;
+ --enable_warnings
+ --eval XA END '$xid'
+ if ($xa_prepare_opt)
+ {
+ --eval XA PREPARE '$xid'
+ }
+ --eval $xa_terminate '$xid' $one_phase
+
+ # use at the head
+ DROP TEMPORARY TABLE tmp_i;
+ DROP TEMPORARY TABLE tmp_m;
+ --eval XA START '$xid'
+ --disable_warnings
+ CREATE TEMPORARY TABLE tmp_i LIKE t;
+ CREATE TEMPORARY TABLE tmp_m LIKE tm;
+ INSERT INTO tmp_i VALUES (7);
+ INSERT INTO tmp_m VALUES (7);
+ INSERT INTO t SELECT * FROM tmp_i;
+ INSERT INTO tm SELECT * FROM tmp_m;
+ INSERT INTO t VALUES (7);
+ INSERT INTO tm VALUES (7);
+ --enable_warnings
+ --eval XA END '$xid'
+ if ($xa_prepare_opt)
+ {
+ --eval XA PREPARE '$xid'
+ }
+ --eval $xa_terminate '$xid' $one_phase
+
+ # use at the tail and drop
+ --eval XA START '$xid'
+ --disable_warnings
+ INSERT INTO t VALUES (8);
+ INSERT INTO tm VALUES (8);
+ INSERT INTO tmp_i VALUES (8);
+ INSERT INTO tmp_m VALUES (8);
+ INSERT INTO t SELECT * FROM tmp_i;
+ INSERT INTO tm SELECT * FROM tmp_m;
+ DROP TEMPORARY TABLE tmp_i;
+ DROP TEMPORARY TABLE tmp_m;
+ --enable_warnings
+ --eval XA END '$xid'
+ if ($xa_prepare_opt)
+ {
+ --eval XA PREPARE '$xid'
+ }
+ --eval $xa_terminate '$xid' $one_phase
+
+ ## Ineffective transactional table operation case
+
+ --eval XA START '$xid'
+ UPDATE t SET a = 99 where a = -1;
+ --eval XA END '$xid'
+ if ($xa_prepare_opt)
+ {
+ --eval XA PREPARE '$xid'
+ }
+ --eval $xa_terminate '$xid' $one_phase
+}
+
+if ($command == cleanup)
+{
+ DROP TABLE t, tm;
+}
diff --git a/mysql-test/suite/rpl/r/rpl_parallel_optimistic_xa.result b/mysql-test/suite/rpl/r/rpl_parallel_optimistic_xa.result
new file mode 100644
index 00000000000..4136f1885db
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_parallel_optimistic_xa.result
@@ -0,0 +1,51 @@
+include/master-slave.inc
+[connection master]
+call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction");
+call mtr.add_suppression("WSREP: handlerton rollback failed");
+CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
+connection master;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+connection slave;
+include/stop_slave.inc
+SET @old_parallel_threads = @@GLOBAL.slave_parallel_threads;
+SET @@global.slave_parallel_threads = 7;
+SET @old_parallel_mode = @@GLOBAL.slave_parallel_mode;
+SET @@global.slave_parallel_mode ='optimistic';
+SET @old_gtid_cleanup_batch_size = @@GLOBAL.gtid_cleanup_batch_size;
+SET @@global.gtid_cleanup_batch_size = 1000000;
+CHANGE MASTER TO master_use_gtid=slave_pos;
+connection master;
+CREATE TABLE t0 (a int, b INT) ENGINE=InnoDB;
+CREATE TABLE t1 (a int PRIMARY KEY, b INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1, 0);
+include/save_master_gtid.inc
+connection slave;
+include/start_slave.inc
+include/sync_with_master_gtid.inc
+include/stop_slave.inc
+connection master;
+include/save_master_gtid.inc
+connection slave;
+include/start_slave.inc
+include/sync_with_master_gtid.inc
+include/diff_tables.inc [master:t0, slave:t0]
+include/diff_tables.inc [master:t1, slave:t1]
+connection slave;
+include/stop_slave.inc
+set global log_warnings=default;
+SET GLOBAL slave_parallel_mode=@old_parallel_mode;
+SET GLOBAL slave_parallel_threads=@old_parallel_threads;
+include/start_slave.inc
+connection master;
+DROP VIEW v_processlist;
+DROP TABLE t0, t1;
+include/save_master_gtid.inc
+connection slave;
+include/sync_with_master_gtid.inc
+SELECT COUNT(*) <= 5*@@GLOBAL.gtid_cleanup_batch_size
+FROM mysql.gtid_slave_pos;
+COUNT(*) <= 5*@@GLOBAL.gtid_cleanup_batch_size
+1
+SET GLOBAL gtid_cleanup_batch_size= @old_gtid_cleanup_batch_size;
+connection master;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_parallel_optimistic_xa_lsu_off.result b/mysql-test/suite/rpl/r/rpl_parallel_optimistic_xa_lsu_off.result
new file mode 100644
index 00000000000..4136f1885db
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_parallel_optimistic_xa_lsu_off.result
@@ -0,0 +1,51 @@
+include/master-slave.inc
+[connection master]
+call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction");
+call mtr.add_suppression("WSREP: handlerton rollback failed");
+CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
+connection master;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+connection slave;
+include/stop_slave.inc
+SET @old_parallel_threads = @@GLOBAL.slave_parallel_threads;
+SET @@global.slave_parallel_threads = 7;
+SET @old_parallel_mode = @@GLOBAL.slave_parallel_mode;
+SET @@global.slave_parallel_mode ='optimistic';
+SET @old_gtid_cleanup_batch_size = @@GLOBAL.gtid_cleanup_batch_size;
+SET @@global.gtid_cleanup_batch_size = 1000000;
+CHANGE MASTER TO master_use_gtid=slave_pos;
+connection master;
+CREATE TABLE t0 (a int, b INT) ENGINE=InnoDB;
+CREATE TABLE t1 (a int PRIMARY KEY, b INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1, 0);
+include/save_master_gtid.inc
+connection slave;
+include/start_slave.inc
+include/sync_with_master_gtid.inc
+include/stop_slave.inc
+connection master;
+include/save_master_gtid.inc
+connection slave;
+include/start_slave.inc
+include/sync_with_master_gtid.inc
+include/diff_tables.inc [master:t0, slave:t0]
+include/diff_tables.inc [master:t1, slave:t1]
+connection slave;
+include/stop_slave.inc
+set global log_warnings=default;
+SET GLOBAL slave_parallel_mode=@old_parallel_mode;
+SET GLOBAL slave_parallel_threads=@old_parallel_threads;
+include/start_slave.inc
+connection master;
+DROP VIEW v_processlist;
+DROP TABLE t0, t1;
+include/save_master_gtid.inc
+connection slave;
+include/sync_with_master_gtid.inc
+SELECT COUNT(*) <= 5*@@GLOBAL.gtid_cleanup_batch_size
+FROM mysql.gtid_slave_pos;
+COUNT(*) <= 5*@@GLOBAL.gtid_cleanup_batch_size
+1
+SET GLOBAL gtid_cleanup_batch_size= @old_gtid_cleanup_batch_size;
+connection master;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_parallel_xa_same_xid.result b/mysql-test/suite/rpl/r/rpl_parallel_xa_same_xid.result
new file mode 100644
index 00000000000..03fe5157623
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_parallel_xa_same_xid.result
@@ -0,0 +1,23 @@
+include/master-slave.inc
+[connection master]
+connection slave;
+call mtr.add_suppression("WSREP: handlerton rollback failed");
+include/stop_slave.inc
+ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+SET @old_parallel_threads = @@GLOBAL.slave_parallel_threads;
+SET @old_parallel_mode = @@GLOBAL.slave_parallel_mode;
+SET @@global.slave_parallel_mode ='optimistic';
+include/start_slave.inc
+connection master;
+CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB;
+CREATE TABLE t2 (a INT AUTO_INCREMENT PRIMARY KEY, b INT) ENGINE=InnoDB;
+include/sync_slave_sql_with_master.inc
+include/diff_tables.inc [master:t1, slave:t1]
+connection slave;
+include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=@old_parallel_threads;
+SET GLOBAL slave_parallel_mode=@old_parallel_mode;
+include/start_slave.inc
+connection master;
+DROP TABLE t1, t2;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_temporary_errors.result b/mysql-test/suite/rpl/r/rpl_temporary_errors.result
index 8654fe218dc..c126871e460 100644
--- a/mysql-test/suite/rpl/r/rpl_temporary_errors.result
+++ b/mysql-test/suite/rpl/r/rpl_temporary_errors.result
@@ -3,7 +3,7 @@ include/master-slave.inc
call mtr.add_suppression("Deadlock found");
call mtr.add_suppression("Can't find record in 't.'");
connection master;
-CREATE TABLE t1 (a INT PRIMARY KEY, b INT);
+CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=innodb;
INSERT INTO t1 VALUES (1,1), (2,2), (3,3), (4,4);
connection slave;
SHOW STATUS LIKE 'Slave_retried_transactions';
@@ -11,34 +11,67 @@ Variable_name Value
Slave_retried_transactions 0
set @@global.slave_exec_mode= 'IDEMPOTENT';
UPDATE t1 SET a = 5, b = 47 WHERE a = 1;
-SELECT * FROM t1;
+SELECT * FROM t1 ORDER BY a;
a b
-5 47
2 2
3 3
4 4
+5 47
connection master;
UPDATE t1 SET a = 5, b = 5 WHERE a = 1;
-SELECT * FROM t1;
+SELECT * FROM t1 ORDER BY a;
a b
-5 5
2 2
3 3
4 4
+5 5
connection slave;
set @@global.slave_exec_mode= default;
SHOW STATUS LIKE 'Slave_retried_transactions';
Variable_name Value
Slave_retried_transactions 0
-SELECT * FROM t1;
+SELECT * FROM t1 ORDER BY a;
a b
-5 47
2 2
3 3
4 4
+5 47
include/check_slave_is_running.inc
connection slave;
call mtr.add_suppression("Slave SQL.*Could not execute Update_rows event on table test.t1");
+call mtr.add_suppression("Slave SQL for channel '': worker thread retried transaction");
+call mtr.add_suppression("The slave coordinator and worker threads are stopped");
+connection slave;
+set @save_innodb_lock_wait_timeout=@@global.innodb_lock_wait_timeout;
+set @save_slave_transaction_retries=@@global.slave_transaction_retries;
+set @@global.innodb_lock_wait_timeout=1;
+set @@global.slave_transaction_retries=2;
+include/restart_slave.inc
+connection slave1;
+BEGIN;
+INSERT INTO t1 SET a = 6, b = 7;
+connection master;
+INSERT INTO t1 SET a = 99, b = 99;
+XA START 'xa1';
+INSERT INTO t1 SET a = 6, b = 6;
+XA END 'xa1';
+XA PREPARE 'xa1';
+connection slave;
+include/wait_for_slave_sql_error.inc [errno=1213,1205]
+set @@global.innodb_lock_wait_timeout=1;
+set @@global.slave_transaction_retries=100;
+include/restart_slave.inc
+Warnings:
+Note 1255 Slave already has been stopped
+connection slave1;
+ROLLBACK;
+connection master;
+XA COMMIT 'xa1';
+include/sync_slave_sql_with_master.inc
+connection slave;
+include/assert.inc [XA transaction record must be in the table]
+set @@global.innodb_lock_wait_timeout=@save_innodb_lock_wait_timeout;
+set @@global.slave_transaction_retries= @save_slave_transaction_retries;
connection master;
DROP TABLE t1;
connection slave;
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..3420f2348e2
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa.result
@@ -0,0 +1,48 @@
+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';
+include/save_master_gtid.inc
+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;
+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_gap_lock.result b/mysql-test/suite/rpl/r/rpl_xa_gap_lock.result
new file mode 100644
index 00000000000..cb760abe2d2
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_gap_lock.result
@@ -0,0 +1,44 @@
+include/master-slave.inc
+[connection master]
+connection slave;
+SET @saved_innodb_limit_optimistic_insert_debug = @@GLOBAL.innodb_limit_optimistic_insert_debug;
+SET @@GLOBAL.innodb_limit_optimistic_insert_debug = 2;
+connection master;
+CREATE TABLE t1 (
+c1 INT NOT NULL,
+KEY(c1)
+) ENGINE=InnoDB;
+CREATE TABLE t2 (
+c1 INT NOT NULL,
+FOREIGN KEY(c1) REFERENCES t1(c1)
+) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1), (3), (4);
+connection master1;
+XA START 'XA1';
+INSERT INTO t1 values(2);
+XA END 'XA1';
+connection master;
+XA START 'XA2';
+INSERT INTO t2 values(3);
+XA END 'XA2';
+XA PREPARE 'XA2';
+connection master1;
+XA PREPARE 'XA1';
+XA COMMIT 'XA1';
+connection master;
+XA COMMIT 'XA2';
+include/sync_slave_sql_with_master.inc
+include/stop_slave.inc
+DROP TABLE t2, t1;
+RESET SLAVE;
+RESET MASTER;
+connection master;
+Restore binary log from the master into the slave
+include/diff_tables.inc [master:test.t1, slave:test.t1]
+include/diff_tables.inc [master:test.t2, slave:test.t2]
+DROP TABLE t2, t1;
+connection slave;
+CHANGE MASTER TO MASTER_LOG_FILE='LOG_FILE', MASTER_LOG_POS=LOG_POS;
+SET @@GLOBAL.innodb_limit_optimistic_insert_debug = @saved_innodb_limit_optimistic_insert_debug;
+include/start_slave.inc
+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..a7ed0f97ea2
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_gtid_pos_auto_engine.result
@@ -0,0 +1,64 @@
+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';
+include/save_master_gtid.inc
+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;
+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.result b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect.result
new file mode 100644
index 00000000000..d2ed1e2c235
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect.result
@@ -0,0 +1,319 @@
+include/master-slave.inc
+[connection master]
+connection master;
+call mtr.add_suppression("Found 2 prepared XA transactions");
+CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
+CREATE DATABASE d1;
+CREATE DATABASE d2;
+CREATE TABLE d1.t (a INT) ENGINE=innodb;
+CREATE TABLE d2.t (a INT) ENGINE=innodb;
+connect master_conn1, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@session.binlog_format= statement;
+XA START '1-stmt';
+INSERT INTO d1.t VALUES (1);
+XA END '1-stmt';
+XA PREPARE '1-stmt';
+disconnect master_conn1;
+connection master;
+connect master_conn2, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@session.binlog_format= row;
+XA START '1-row';
+INSERT INTO d2.t VALUES (1);
+XA END '1-row';
+XA PREPARE '1-row';
+disconnect master_conn2;
+connection master;
+XA START '2';
+INSERT INTO d1.t VALUES (2);
+XA END '2';
+XA PREPARE '2';
+XA COMMIT '2';
+XA COMMIT '1-row';
+XA COMMIT '1-stmt';
+include/show_binlog_events.inc
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `mtr`; INSERT INTO test_suppressions (pattern) VALUES ( NAME_CONST('pattern',_latin1'Found 2 prepared XA transactions' COLLATE 'latin1_swedish_ci'))
+master-bin.000001 # Query # # COMMIT
+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 # # GTID #-#-#
+master-bin.000001 # Query # # CREATE DATABASE d1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # CREATE DATABASE d2
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE TABLE d1.t (a INT) ENGINE=innodb
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE TABLE d2.t (a INT) ENGINE=innodb
+master-bin.000001 # Gtid # # XA START X'312d73746d74',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO d1.t VALUES (1)
+master-bin.000001 # Query # # XA END X'312d73746d74',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'312d73746d74',X'',1
+master-bin.000001 # Gtid # # XA START X'312d726f77',X'',1 GTID #-#-#
+master-bin.000001 # Annotate_rows # # INSERT INTO d2.t VALUES (1)
+master-bin.000001 # Table_map # # table_id: # (d2.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END X'312d726f77',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'312d726f77',X'',1
+master-bin.000001 # Gtid # # XA START X'32',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO d1.t VALUES (2)
+master-bin.000001 # Query # # XA END X'32',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'32',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'32',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'312d726f77',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'312d73746d74',X'',1
+include/sync_slave_sql_with_master.inc
+include/stop_slave.inc
+connection master;
+connect master2, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master2;
+SET @@session.binlog_format= statement;
+XA START '3-stmt';
+INSERT INTO d1.t VALUES (3);
+XA END '3-stmt';
+XA PREPARE '3-stmt';
+disconnect master2;
+connect master2, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master2;
+SET @@session.binlog_format= row;
+XA START '3-row';
+INSERT INTO d2.t VALUES (4);
+XA END '3-row';
+XA PREPARE '3-row';
+disconnect master2;
+connection master;
+connect master2, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master2;
+XA START '4';
+SELECT * FROM d1.t;
+a
+1
+2
+XA END '4';
+XA PREPARE '4';
+disconnect master2;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_10';
+INSERT INTO d1.t VALUES (10);
+INSERT INTO d2.t VALUES (10);
+XA END 'bulk_trx_10';
+XA PREPARE 'bulk_trx_10';
+disconnect master_bulk_conn10;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_9';
+INSERT INTO d1.t VALUES (9);
+INSERT INTO d2.t VALUES (9);
+XA END 'bulk_trx_9';
+XA PREPARE 'bulk_trx_9';
+disconnect master_bulk_conn9;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_8';
+INSERT INTO d1.t VALUES (8);
+INSERT INTO d2.t VALUES (8);
+XA END 'bulk_trx_8';
+XA PREPARE 'bulk_trx_8';
+disconnect master_bulk_conn8;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_7';
+INSERT INTO d1.t VALUES (7);
+INSERT INTO d2.t VALUES (7);
+XA END 'bulk_trx_7';
+XA PREPARE 'bulk_trx_7';
+disconnect master_bulk_conn7;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_6';
+INSERT INTO d1.t VALUES (6);
+INSERT INTO d2.t VALUES (6);
+XA END 'bulk_trx_6';
+XA PREPARE 'bulk_trx_6';
+disconnect master_bulk_conn6;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_5';
+INSERT INTO d1.t VALUES (5);
+INSERT INTO d2.t VALUES (5);
+XA END 'bulk_trx_5';
+XA PREPARE 'bulk_trx_5';
+disconnect master_bulk_conn5;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_4';
+INSERT INTO d1.t VALUES (4);
+INSERT INTO d2.t VALUES (4);
+XA END 'bulk_trx_4';
+XA PREPARE 'bulk_trx_4';
+disconnect master_bulk_conn4;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_3';
+INSERT INTO d1.t VALUES (3);
+INSERT INTO d2.t VALUES (3);
+XA END 'bulk_trx_3';
+XA PREPARE 'bulk_trx_3';
+disconnect master_bulk_conn3;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_2';
+INSERT INTO d1.t VALUES (2);
+INSERT INTO d2.t VALUES (2);
+XA END 'bulk_trx_2';
+XA PREPARE 'bulk_trx_2';
+disconnect master_bulk_conn2;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_1';
+INSERT INTO d1.t VALUES (1);
+INSERT INTO d2.t VALUES (1);
+XA END 'bulk_trx_1';
+XA PREPARE 'bulk_trx_1';
+disconnect master_bulk_conn1;
+connection master;
+connection slave;
+include/start_slave.inc
+connection master;
+include/sync_slave_sql_with_master.inc
+include/stop_slave.inc
+connection master;
+XA COMMIT 'bulk_trx_10';
+XA ROLLBACK 'bulk_trx_9';
+XA COMMIT 'bulk_trx_8';
+XA ROLLBACK 'bulk_trx_7';
+XA COMMIT 'bulk_trx_6';
+XA ROLLBACK 'bulk_trx_5';
+XA COMMIT 'bulk_trx_4';
+XA ROLLBACK 'bulk_trx_3';
+XA COMMIT 'bulk_trx_2';
+XA ROLLBACK 'bulk_trx_1';
+include/rpl_restart_server.inc [server_number=1]
+connection slave;
+include/start_slave.inc
+connection master;
+*** '3-stmt','3-row' xa-transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 6 0 3-stmt
+1 5 0 3-row
+XA COMMIT '3-stmt';
+XA ROLLBACK '3-row';
+include/sync_slave_sql_with_master.inc
+connection master;
+connect master_conn2, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START '0123456789012345678901234567890123456789012345678901234567890124','0123456789012345678901234567890123456789012345678901234567890124',4294967292;
+INSERT INTO d1.t VALUES (64);
+XA END '0123456789012345678901234567890123456789012345678901234567890124','0123456789012345678901234567890123456789012345678901234567890124',4294967292;
+XA PREPARE '0123456789012345678901234567890123456789012345678901234567890124','0123456789012345678901234567890123456789012345678901234567890124',4294967292;
+disconnect master_conn2;
+connection master;
+connect master_conn3, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',X'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',0;
+INSERT INTO d1.t VALUES (0);
+XA END X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',X'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',0;
+XA PREPARE X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',X'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',0;
+disconnect master_conn3;
+connection master;
+disconnect master_conn4;
+connection master;
+XA COMMIT '0123456789012345678901234567890123456789012345678901234567890124','0123456789012345678901234567890123456789012345678901234567890124',4294967292;
+XA COMMIT X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',X'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',0;
+XA COMMIT 'RANDOM XID'
+include/sync_slave_sql_with_master.inc
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn10;
+XA START 'one_phase_10';
+INSERT INTO d1.t VALUES (10);
+INSERT INTO d2.t VALUES (10);
+XA END 'one_phase_10';
+XA COMMIT 'one_phase_10' ONE PHASE;
+disconnect master_bulk_conn10;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn9;
+XA START 'one_phase_9';
+INSERT INTO d1.t VALUES (9);
+INSERT INTO d2.t VALUES (9);
+XA END 'one_phase_9';
+XA COMMIT 'one_phase_9' ONE PHASE;
+disconnect master_bulk_conn9;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn8;
+XA START 'one_phase_8';
+INSERT INTO d1.t VALUES (8);
+INSERT INTO d2.t VALUES (8);
+XA END 'one_phase_8';
+XA COMMIT 'one_phase_8' ONE PHASE;
+disconnect master_bulk_conn8;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn7;
+XA START 'one_phase_7';
+INSERT INTO d1.t VALUES (7);
+INSERT INTO d2.t VALUES (7);
+XA END 'one_phase_7';
+XA COMMIT 'one_phase_7' ONE PHASE;
+disconnect master_bulk_conn7;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn6;
+XA START 'one_phase_6';
+INSERT INTO d1.t VALUES (6);
+INSERT INTO d2.t VALUES (6);
+XA END 'one_phase_6';
+XA COMMIT 'one_phase_6' ONE PHASE;
+disconnect master_bulk_conn6;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn5;
+XA START 'one_phase_5';
+INSERT INTO d1.t VALUES (5);
+INSERT INTO d2.t VALUES (5);
+XA END 'one_phase_5';
+XA COMMIT 'one_phase_5' ONE PHASE;
+disconnect master_bulk_conn5;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn4;
+XA START 'one_phase_4';
+INSERT INTO d1.t VALUES (4);
+INSERT INTO d2.t VALUES (4);
+XA END 'one_phase_4';
+XA COMMIT 'one_phase_4' ONE PHASE;
+disconnect master_bulk_conn4;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn3;
+XA START 'one_phase_3';
+INSERT INTO d1.t VALUES (3);
+INSERT INTO d2.t VALUES (3);
+XA END 'one_phase_3';
+XA COMMIT 'one_phase_3' ONE PHASE;
+disconnect master_bulk_conn3;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn2;
+XA START 'one_phase_2';
+INSERT INTO d1.t VALUES (2);
+INSERT INTO d2.t VALUES (2);
+XA END 'one_phase_2';
+XA COMMIT 'one_phase_2' ONE PHASE;
+disconnect master_bulk_conn2;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn1;
+XA START 'one_phase_1';
+INSERT INTO d1.t VALUES (1);
+INSERT INTO d2.t VALUES (1);
+XA END 'one_phase_1';
+XA COMMIT 'one_phase_1' ONE PHASE;
+disconnect master_bulk_conn1;
+connection master;
+include/sync_slave_sql_with_master.inc
+include/diff_tables.inc [master:d1.t, slave:d1.t]
+include/diff_tables.inc [master:d2.t, slave:d2.t]
+connection master;
+DELETE FROM d1.t;
+DELETE FROM d2.t;
+DROP TABLE d1.t, d2.t;
+DROP DATABASE d1;
+DROP DATABASE d2;
+DROP VIEW v_processlist;
+include/sync_slave_sql_with_master.inc
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_lsu_off.result b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_lsu_off.result
new file mode 100644
index 00000000000..d2ed1e2c235
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_lsu_off.result
@@ -0,0 +1,319 @@
+include/master-slave.inc
+[connection master]
+connection master;
+call mtr.add_suppression("Found 2 prepared XA transactions");
+CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
+CREATE DATABASE d1;
+CREATE DATABASE d2;
+CREATE TABLE d1.t (a INT) ENGINE=innodb;
+CREATE TABLE d2.t (a INT) ENGINE=innodb;
+connect master_conn1, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@session.binlog_format= statement;
+XA START '1-stmt';
+INSERT INTO d1.t VALUES (1);
+XA END '1-stmt';
+XA PREPARE '1-stmt';
+disconnect master_conn1;
+connection master;
+connect master_conn2, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@session.binlog_format= row;
+XA START '1-row';
+INSERT INTO d2.t VALUES (1);
+XA END '1-row';
+XA PREPARE '1-row';
+disconnect master_conn2;
+connection master;
+XA START '2';
+INSERT INTO d1.t VALUES (2);
+XA END '2';
+XA PREPARE '2';
+XA COMMIT '2';
+XA COMMIT '1-row';
+XA COMMIT '1-stmt';
+include/show_binlog_events.inc
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `mtr`; INSERT INTO test_suppressions (pattern) VALUES ( NAME_CONST('pattern',_latin1'Found 2 prepared XA transactions' COLLATE 'latin1_swedish_ci'))
+master-bin.000001 # Query # # COMMIT
+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 # # GTID #-#-#
+master-bin.000001 # Query # # CREATE DATABASE d1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # CREATE DATABASE d2
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE TABLE d1.t (a INT) ENGINE=innodb
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE TABLE d2.t (a INT) ENGINE=innodb
+master-bin.000001 # Gtid # # XA START X'312d73746d74',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO d1.t VALUES (1)
+master-bin.000001 # Query # # XA END X'312d73746d74',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'312d73746d74',X'',1
+master-bin.000001 # Gtid # # XA START X'312d726f77',X'',1 GTID #-#-#
+master-bin.000001 # Annotate_rows # # INSERT INTO d2.t VALUES (1)
+master-bin.000001 # Table_map # # table_id: # (d2.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END X'312d726f77',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'312d726f77',X'',1
+master-bin.000001 # Gtid # # XA START X'32',X'',1 GTID #-#-#
+master-bin.000001 # Query # # use `test`; INSERT INTO d1.t VALUES (2)
+master-bin.000001 # Query # # XA END X'32',X'',1
+master-bin.000001 # XA_prepare # # XA PREPARE X'32',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'32',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'312d726f77',X'',1
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # XA COMMIT X'312d73746d74',X'',1
+include/sync_slave_sql_with_master.inc
+include/stop_slave.inc
+connection master;
+connect master2, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master2;
+SET @@session.binlog_format= statement;
+XA START '3-stmt';
+INSERT INTO d1.t VALUES (3);
+XA END '3-stmt';
+XA PREPARE '3-stmt';
+disconnect master2;
+connect master2, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master2;
+SET @@session.binlog_format= row;
+XA START '3-row';
+INSERT INTO d2.t VALUES (4);
+XA END '3-row';
+XA PREPARE '3-row';
+disconnect master2;
+connection master;
+connect master2, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master2;
+XA START '4';
+SELECT * FROM d1.t;
+a
+1
+2
+XA END '4';
+XA PREPARE '4';
+disconnect master2;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_10';
+INSERT INTO d1.t VALUES (10);
+INSERT INTO d2.t VALUES (10);
+XA END 'bulk_trx_10';
+XA PREPARE 'bulk_trx_10';
+disconnect master_bulk_conn10;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_9';
+INSERT INTO d1.t VALUES (9);
+INSERT INTO d2.t VALUES (9);
+XA END 'bulk_trx_9';
+XA PREPARE 'bulk_trx_9';
+disconnect master_bulk_conn9;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_8';
+INSERT INTO d1.t VALUES (8);
+INSERT INTO d2.t VALUES (8);
+XA END 'bulk_trx_8';
+XA PREPARE 'bulk_trx_8';
+disconnect master_bulk_conn8;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_7';
+INSERT INTO d1.t VALUES (7);
+INSERT INTO d2.t VALUES (7);
+XA END 'bulk_trx_7';
+XA PREPARE 'bulk_trx_7';
+disconnect master_bulk_conn7;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_6';
+INSERT INTO d1.t VALUES (6);
+INSERT INTO d2.t VALUES (6);
+XA END 'bulk_trx_6';
+XA PREPARE 'bulk_trx_6';
+disconnect master_bulk_conn6;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_5';
+INSERT INTO d1.t VALUES (5);
+INSERT INTO d2.t VALUES (5);
+XA END 'bulk_trx_5';
+XA PREPARE 'bulk_trx_5';
+disconnect master_bulk_conn5;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_4';
+INSERT INTO d1.t VALUES (4);
+INSERT INTO d2.t VALUES (4);
+XA END 'bulk_trx_4';
+XA PREPARE 'bulk_trx_4';
+disconnect master_bulk_conn4;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_3';
+INSERT INTO d1.t VALUES (3);
+INSERT INTO d2.t VALUES (3);
+XA END 'bulk_trx_3';
+XA PREPARE 'bulk_trx_3';
+disconnect master_bulk_conn3;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_2';
+INSERT INTO d1.t VALUES (2);
+INSERT INTO d2.t VALUES (2);
+XA END 'bulk_trx_2';
+XA PREPARE 'bulk_trx_2';
+disconnect master_bulk_conn2;
+connection master;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'bulk_trx_1';
+INSERT INTO d1.t VALUES (1);
+INSERT INTO d2.t VALUES (1);
+XA END 'bulk_trx_1';
+XA PREPARE 'bulk_trx_1';
+disconnect master_bulk_conn1;
+connection master;
+connection slave;
+include/start_slave.inc
+connection master;
+include/sync_slave_sql_with_master.inc
+include/stop_slave.inc
+connection master;
+XA COMMIT 'bulk_trx_10';
+XA ROLLBACK 'bulk_trx_9';
+XA COMMIT 'bulk_trx_8';
+XA ROLLBACK 'bulk_trx_7';
+XA COMMIT 'bulk_trx_6';
+XA ROLLBACK 'bulk_trx_5';
+XA COMMIT 'bulk_trx_4';
+XA ROLLBACK 'bulk_trx_3';
+XA COMMIT 'bulk_trx_2';
+XA ROLLBACK 'bulk_trx_1';
+include/rpl_restart_server.inc [server_number=1]
+connection slave;
+include/start_slave.inc
+connection master;
+*** '3-stmt','3-row' xa-transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 6 0 3-stmt
+1 5 0 3-row
+XA COMMIT '3-stmt';
+XA ROLLBACK '3-row';
+include/sync_slave_sql_with_master.inc
+connection master;
+connect master_conn2, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START '0123456789012345678901234567890123456789012345678901234567890124','0123456789012345678901234567890123456789012345678901234567890124',4294967292;
+INSERT INTO d1.t VALUES (64);
+XA END '0123456789012345678901234567890123456789012345678901234567890124','0123456789012345678901234567890123456789012345678901234567890124',4294967292;
+XA PREPARE '0123456789012345678901234567890123456789012345678901234567890124','0123456789012345678901234567890123456789012345678901234567890124',4294967292;
+disconnect master_conn2;
+connection master;
+connect master_conn3, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',X'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',0;
+INSERT INTO d1.t VALUES (0);
+XA END X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',X'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',0;
+XA PREPARE X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',X'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',0;
+disconnect master_conn3;
+connection master;
+disconnect master_conn4;
+connection master;
+XA COMMIT '0123456789012345678901234567890123456789012345678901234567890124','0123456789012345678901234567890123456789012345678901234567890124',4294967292;
+XA COMMIT X'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF',X'00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',0;
+XA COMMIT 'RANDOM XID'
+include/sync_slave_sql_with_master.inc
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn10;
+XA START 'one_phase_10';
+INSERT INTO d1.t VALUES (10);
+INSERT INTO d2.t VALUES (10);
+XA END 'one_phase_10';
+XA COMMIT 'one_phase_10' ONE PHASE;
+disconnect master_bulk_conn10;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn9;
+XA START 'one_phase_9';
+INSERT INTO d1.t VALUES (9);
+INSERT INTO d2.t VALUES (9);
+XA END 'one_phase_9';
+XA COMMIT 'one_phase_9' ONE PHASE;
+disconnect master_bulk_conn9;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn8;
+XA START 'one_phase_8';
+INSERT INTO d1.t VALUES (8);
+INSERT INTO d2.t VALUES (8);
+XA END 'one_phase_8';
+XA COMMIT 'one_phase_8' ONE PHASE;
+disconnect master_bulk_conn8;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn7;
+XA START 'one_phase_7';
+INSERT INTO d1.t VALUES (7);
+INSERT INTO d2.t VALUES (7);
+XA END 'one_phase_7';
+XA COMMIT 'one_phase_7' ONE PHASE;
+disconnect master_bulk_conn7;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn6;
+XA START 'one_phase_6';
+INSERT INTO d1.t VALUES (6);
+INSERT INTO d2.t VALUES (6);
+XA END 'one_phase_6';
+XA COMMIT 'one_phase_6' ONE PHASE;
+disconnect master_bulk_conn6;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn5;
+XA START 'one_phase_5';
+INSERT INTO d1.t VALUES (5);
+INSERT INTO d2.t VALUES (5);
+XA END 'one_phase_5';
+XA COMMIT 'one_phase_5' ONE PHASE;
+disconnect master_bulk_conn5;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn4;
+XA START 'one_phase_4';
+INSERT INTO d1.t VALUES (4);
+INSERT INTO d2.t VALUES (4);
+XA END 'one_phase_4';
+XA COMMIT 'one_phase_4' ONE PHASE;
+disconnect master_bulk_conn4;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn3;
+XA START 'one_phase_3';
+INSERT INTO d1.t VALUES (3);
+INSERT INTO d2.t VALUES (3);
+XA END 'one_phase_3';
+XA COMMIT 'one_phase_3' ONE PHASE;
+disconnect master_bulk_conn3;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn2;
+XA START 'one_phase_2';
+INSERT INTO d1.t VALUES (2);
+INSERT INTO d2.t VALUES (2);
+XA END 'one_phase_2';
+XA COMMIT 'one_phase_2' ONE PHASE;
+disconnect master_bulk_conn2;
+connect master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+connection master_bulk_conn1;
+XA START 'one_phase_1';
+INSERT INTO d1.t VALUES (1);
+INSERT INTO d2.t VALUES (1);
+XA END 'one_phase_1';
+XA COMMIT 'one_phase_1' ONE PHASE;
+disconnect master_bulk_conn1;
+connection master;
+include/sync_slave_sql_with_master.inc
+include/diff_tables.inc [master:d1.t, slave:d1.t]
+include/diff_tables.inc [master:d2.t, slave:d2.t]
+connection master;
+DELETE FROM d1.t;
+DELETE FROM d2.t;
+DROP TABLE d1.t, d2.t;
+DROP DATABASE d1;
+DROP DATABASE d2;
+DROP VIEW v_processlist;
+include/sync_slave_sql_with_master.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..09bfffc0da4
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_mixed_engines.result
@@ -0,0 +1,373 @@
+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;
+=== COMMIT ===
+XA START 'xa_trx';
+INSERT INTO tm VALUES (1);
+INSERT INTO t VALUES (1);
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+XA COMMIT 'xa_trx' ;
+XA START 'xa_trx';
+INSERT INTO t VALUES (2);
+INSERT INTO tm VALUES (2);
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+XA COMMIT 'xa_trx' ;
+XA START 'xa_trx';
+INSERT INTO tm VALUES (3);
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+XA COMMIT 'xa_trx' ;
+CREATE TEMPORARY TABLE tmp_i LIKE t;
+CREATE TEMPORARY TABLE tmp_m LIKE tm;
+XA START 'xa_trx';
+INSERT INTO t VALUES (4);
+INSERT INTO tm VALUES (4);
+INSERT INTO tmp_i VALUES (4);
+INSERT INTO tmp_m VALUES (4);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+XA COMMIT 'xa_trx' ;
+XA START 'xa_trx';
+INSERT INTO tmp_i VALUES (5);
+INSERT INTO tmp_m VALUES (5);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+INSERT INTO t VALUES (5);
+INSERT INTO tm VALUES (5);
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+XA COMMIT 'xa_trx' ;
+DROP TEMPORARY TABLE tmp_i;
+DROP TEMPORARY TABLE tmp_m;
+XA START 'xa_trx';
+INSERT INTO t VALUES (6);
+INSERT INTO tm VALUES (6);
+CREATE TEMPORARY TABLE tmp_i LIKE t;
+CREATE TEMPORARY TABLE tmp_m LIKE tm;
+INSERT INTO tmp_i VALUES (6);
+INSERT INTO tmp_m VALUES (6);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+XA COMMIT 'xa_trx' ;
+DROP TEMPORARY TABLE tmp_i;
+DROP TEMPORARY TABLE tmp_m;
+XA START 'xa_trx';
+CREATE TEMPORARY TABLE tmp_i LIKE t;
+CREATE TEMPORARY TABLE tmp_m LIKE tm;
+INSERT INTO tmp_i VALUES (7);
+INSERT INTO tmp_m VALUES (7);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+INSERT INTO t VALUES (7);
+INSERT INTO tm VALUES (7);
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+XA COMMIT 'xa_trx' ;
+XA START 'xa_trx';
+INSERT INTO t VALUES (8);
+INSERT INTO tm VALUES (8);
+INSERT INTO tmp_i VALUES (8);
+INSERT INTO tmp_m VALUES (8);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+DROP TEMPORARY TABLE tmp_i;
+DROP TEMPORARY TABLE tmp_m;
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+XA COMMIT 'xa_trx' ;
+XA START 'xa_trx';
+UPDATE t SET a = 99 where a = -1;
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+XA COMMIT 'xa_trx' ;
+include/sync_slave_sql_with_master.inc
+connection master;
+=== COMMIT ONE PHASE ===
+XA START 'xa_trx';
+INSERT INTO tm VALUES (1);
+INSERT INTO t VALUES (1);
+XA END 'xa_trx';
+XA COMMIT 'xa_trx' ONE PHASE;
+XA START 'xa_trx';
+INSERT INTO t VALUES (2);
+INSERT INTO tm VALUES (2);
+XA END 'xa_trx';
+XA COMMIT 'xa_trx' ONE PHASE;
+XA START 'xa_trx';
+INSERT INTO tm VALUES (3);
+XA END 'xa_trx';
+XA COMMIT 'xa_trx' ONE PHASE;
+CREATE TEMPORARY TABLE tmp_i LIKE t;
+CREATE TEMPORARY TABLE tmp_m LIKE tm;
+XA START 'xa_trx';
+INSERT INTO t VALUES (4);
+INSERT INTO tm VALUES (4);
+INSERT INTO tmp_i VALUES (4);
+INSERT INTO tmp_m VALUES (4);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+XA END 'xa_trx';
+XA COMMIT 'xa_trx' ONE PHASE;
+XA START 'xa_trx';
+INSERT INTO tmp_i VALUES (5);
+INSERT INTO tmp_m VALUES (5);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+INSERT INTO t VALUES (5);
+INSERT INTO tm VALUES (5);
+XA END 'xa_trx';
+XA COMMIT 'xa_trx' ONE PHASE;
+DROP TEMPORARY TABLE tmp_i;
+DROP TEMPORARY TABLE tmp_m;
+XA START 'xa_trx';
+INSERT INTO t VALUES (6);
+INSERT INTO tm VALUES (6);
+CREATE TEMPORARY TABLE tmp_i LIKE t;
+CREATE TEMPORARY TABLE tmp_m LIKE tm;
+INSERT INTO tmp_i VALUES (6);
+INSERT INTO tmp_m VALUES (6);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+XA END 'xa_trx';
+XA COMMIT 'xa_trx' ONE PHASE;
+DROP TEMPORARY TABLE tmp_i;
+DROP TEMPORARY TABLE tmp_m;
+XA START 'xa_trx';
+CREATE TEMPORARY TABLE tmp_i LIKE t;
+CREATE TEMPORARY TABLE tmp_m LIKE tm;
+INSERT INTO tmp_i VALUES (7);
+INSERT INTO tmp_m VALUES (7);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+INSERT INTO t VALUES (7);
+INSERT INTO tm VALUES (7);
+XA END 'xa_trx';
+XA COMMIT 'xa_trx' ONE PHASE;
+XA START 'xa_trx';
+INSERT INTO t VALUES (8);
+INSERT INTO tm VALUES (8);
+INSERT INTO tmp_i VALUES (8);
+INSERT INTO tmp_m VALUES (8);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+DROP TEMPORARY TABLE tmp_i;
+DROP TEMPORARY TABLE tmp_m;
+XA END 'xa_trx';
+XA COMMIT 'xa_trx' ONE PHASE;
+XA START 'xa_trx';
+UPDATE t SET a = 99 where a = -1;
+XA END 'xa_trx';
+XA COMMIT 'xa_trx' ONE PHASE;
+include/sync_slave_sql_with_master.inc
+connection master;
+=== ROLLBACK with PREPARE ===
+XA START 'xa_trx';
+INSERT INTO tm VALUES (1);
+INSERT INTO t VALUES (1);
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+XA START 'xa_trx';
+INSERT INTO t VALUES (2);
+INSERT INTO tm VALUES (2);
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+XA START 'xa_trx';
+INSERT INTO tm VALUES (3);
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+CREATE TEMPORARY TABLE tmp_i LIKE t;
+CREATE TEMPORARY TABLE tmp_m LIKE tm;
+XA START 'xa_trx';
+INSERT INTO t VALUES (4);
+INSERT INTO tm VALUES (4);
+INSERT INTO tmp_i VALUES (4);
+INSERT INTO tmp_m VALUES (4);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+XA START 'xa_trx';
+INSERT INTO tmp_i VALUES (5);
+INSERT INTO tmp_m VALUES (5);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+INSERT INTO t VALUES (5);
+INSERT INTO tm VALUES (5);
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+DROP TEMPORARY TABLE tmp_i;
+DROP TEMPORARY TABLE tmp_m;
+XA START 'xa_trx';
+INSERT INTO t VALUES (6);
+INSERT INTO tm VALUES (6);
+CREATE TEMPORARY TABLE tmp_i LIKE t;
+CREATE TEMPORARY TABLE tmp_m LIKE tm;
+INSERT INTO tmp_i VALUES (6);
+INSERT INTO tmp_m VALUES (6);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+DROP TEMPORARY TABLE tmp_i;
+DROP TEMPORARY TABLE tmp_m;
+XA START 'xa_trx';
+CREATE TEMPORARY TABLE tmp_i LIKE t;
+CREATE TEMPORARY TABLE tmp_m LIKE tm;
+INSERT INTO tmp_i VALUES (7);
+INSERT INTO tmp_m VALUES (7);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+INSERT INTO t VALUES (7);
+INSERT INTO tm VALUES (7);
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+XA START 'xa_trx';
+INSERT INTO t VALUES (8);
+INSERT INTO tm VALUES (8);
+INSERT INTO tmp_i VALUES (8);
+INSERT INTO tmp_m VALUES (8);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+DROP TEMPORARY TABLE tmp_i;
+DROP TEMPORARY TABLE tmp_m;
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+XA START 'xa_trx';
+UPDATE t SET a = 99 where a = -1;
+XA END 'xa_trx';
+XA PREPARE 'xa_trx';
+xa rollback 'xa_trx' ;
+include/sync_slave_sql_with_master.inc
+connection master;
+=== ROLLBACK with no PREPARE ===
+XA START 'xa_trx';
+INSERT INTO tm VALUES (1);
+INSERT INTO t VALUES (1);
+XA END 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+XA START 'xa_trx';
+INSERT INTO t VALUES (2);
+INSERT INTO tm VALUES (2);
+XA END 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+XA START 'xa_trx';
+INSERT INTO tm VALUES (3);
+XA END 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+CREATE TEMPORARY TABLE tmp_i LIKE t;
+CREATE TEMPORARY TABLE tmp_m LIKE tm;
+XA START 'xa_trx';
+INSERT INTO t VALUES (4);
+INSERT INTO tm VALUES (4);
+INSERT INTO tmp_i VALUES (4);
+INSERT INTO tmp_m VALUES (4);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+XA END 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+XA START 'xa_trx';
+INSERT INTO tmp_i VALUES (5);
+INSERT INTO tmp_m VALUES (5);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+INSERT INTO t VALUES (5);
+INSERT INTO tm VALUES (5);
+XA END 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+DROP TEMPORARY TABLE tmp_i;
+DROP TEMPORARY TABLE tmp_m;
+XA START 'xa_trx';
+INSERT INTO t VALUES (6);
+INSERT INTO tm VALUES (6);
+CREATE TEMPORARY TABLE tmp_i LIKE t;
+CREATE TEMPORARY TABLE tmp_m LIKE tm;
+INSERT INTO tmp_i VALUES (6);
+INSERT INTO tmp_m VALUES (6);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+XA END 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+DROP TEMPORARY TABLE tmp_i;
+DROP TEMPORARY TABLE tmp_m;
+XA START 'xa_trx';
+CREATE TEMPORARY TABLE tmp_i LIKE t;
+CREATE TEMPORARY TABLE tmp_m LIKE tm;
+INSERT INTO tmp_i VALUES (7);
+INSERT INTO tmp_m VALUES (7);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+INSERT INTO t VALUES (7);
+INSERT INTO tm VALUES (7);
+XA END 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+XA START 'xa_trx';
+INSERT INTO t VALUES (8);
+INSERT INTO tm VALUES (8);
+INSERT INTO tmp_i VALUES (8);
+INSERT INTO tmp_m VALUES (8);
+INSERT INTO t SELECT * FROM tmp_i;
+INSERT INTO tm SELECT * FROM tmp_m;
+DROP TEMPORARY TABLE tmp_i;
+DROP TEMPORARY TABLE tmp_m;
+XA END 'xa_trx';
+xa rollback 'xa_trx' ;
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+XA START 'xa_trx';
+UPDATE t SET a = 99 where a = -1;
+XA END 'xa_trx';
+xa rollback 'xa_trx' ;
+include/sync_slave_sql_with_master.inc
+include/diff_tables.inc [master:tm, slave:tm]
+connection master;
+DROP TABLE t, tm;
+include/sync_slave_sql_with_master.inc
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa.test b/mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa.test
new file mode 100644
index 00000000000..35c22d1e92e
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa.test
@@ -0,0 +1,235 @@
+# The tests verify concurrent execution of replicated (MDEV-742)
+# XA transactions in the parallel optimistic mode.
+
+--source include/have_innodb.inc
+--source include/have_perfschema.inc
+--source include/master-slave.inc
+
+# Tests' global declarations
+--let $trx = _trx_
+
+call mtr.add_suppression("Deadlock found when trying to get lock; try restarting transaction");
+call mtr.add_suppression("WSREP: handlerton rollback failed");
+#call mtr.add_suppression("Can't find record in 't1'");
+CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
+
+--connection master
+ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+--save_master_pos
+
+# Prepare to restart slave into optimistic parallel mode
+--connection slave
+--sync_with_master
+--source include/stop_slave.inc
+SET @old_parallel_threads = @@GLOBAL.slave_parallel_threads;
+SET @@global.slave_parallel_threads = 7;
+SET @old_parallel_mode = @@GLOBAL.slave_parallel_mode;
+SET @@global.slave_parallel_mode ='optimistic';
+# Run the first part of the test with high batch size and see that
+# old rows remain in the table.
+SET @old_gtid_cleanup_batch_size = @@GLOBAL.gtid_cleanup_batch_size;
+SET @@global.gtid_cleanup_batch_size = 1000000;
+
+CHANGE MASTER TO master_use_gtid=slave_pos;
+
+# LOAD GENERATOR creates XA:s interleaved in binlog when they are from
+# different connections. All the following block XA:s of the same connection
+# update the same data which challenges slave optimistic scheduler's correctness.
+# Slave must eventually apply such load, and correctly (checked).
+
+--connection master
+CREATE TABLE t0 (a int, b INT) ENGINE=InnoDB;
+CREATE TABLE t1 (a int PRIMARY KEY, b INT) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (1, 0);
+
+
+# I. Logging some sequence of XA:s by one connection.
+#
+# The slave applier's task is to successfully execute a series of
+# Prepare and Complete parts of a sequence of XA:s
+
+--let $trx_num = 300
+--let $i = $trx_num
+--let $conn = master
+--disable_query_log
+while($i > 0)
+{
+ # 'decision' to commit 0, or rollback 1
+ --let $decision = `SELECT $i % 2`
+ --eval XA START '$conn$trx$i'
+ --eval UPDATE t1 SET b = 1 - 2 * $decision WHERE a = 1
+ --eval XA END '$conn$trx$i'
+ --let $one_phase = `SELECT IF(floor(rand()*10)%2, "ONE PHASE", 0)`
+ if (!$one_phase)
+ {
+ --eval XA PREPARE '$conn$trx$i'
+ --let $one_phase =
+ }
+
+ --let $term = COMMIT
+ if ($decision)
+ {
+ --let $term = ROLLBACK
+ --let $one_phase =
+ }
+ --eval XA $term '$conn$trx$i' $one_phase
+
+ --dec $i
+}
+--enable_query_log
+--source include/save_master_gtid.inc
+
+--connection slave
+--source include/start_slave.inc
+--source include/sync_with_master_gtid.inc
+--source include/stop_slave.inc
+
+
+# II. Logging XS:s from multiple connections in random interweaving manner:
+#
+# in a loop ($i) per connection
+# arrange an inner ($k) loop where
+# start and prepare an XA;
+# decide whether to terminate it and then continue to loop innerly
+# OR disconnect to break the inner loop;
+# the disconnected one's XA is taken care by 'master' connection
+#
+# Effectively binlog must collect a well mixed XA- prepared and terminated
+# groups for slave to handle.
+
+--connection master
+# Total # of connections
+--let $conn_num=53
+
+--let $i = $conn_num
+--disable_query_log
+while($i > 0)
+{
+ --connect (master_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,)
+--dec $i
+}
+--enable_query_log
+
+--let $i = $conn_num
+while($i > 0)
+{
+ --let $conn_i = conn$i
+ # $i2 indexes the current connection's "own" row
+ --let $i2 = `SELECT $i + 2`
+--disable_query_log
+ --connection master_conn$i
+--enable_query_log
+ --disable_query_log
+ --let $i_conn_id = `SELECT connection_id()`
+
+ --let $decision = 0
+ # the row id of the last connection that committed its XA
+ --let $c_max = 1
+ --let $k = 0
+ while ($decision < 3)
+ {
+ --inc $k
+ --eval XA START '$conn_i$trx$k'
+ # UPDATE depends on previously *committed* transactions
+ --eval UPDATE t1 SET b = b + $k + 1 WHERE a = $c_max
+ if (`SELECT $k % 2 = 1`)
+ {
+ --eval REPLACE INTO t1 VALUES ($i2, $k)
+ }
+ if (`SELECT $k % 2 = 0`)
+ {
+ --eval DELETE FROM t1 WHERE a = $i2
+ }
+ CREATE TEMPORARY TABLE tmp LIKE t0;
+ --eval INSERT INTO tmp SET a=$i, b= $k
+ INSERT INTO t0 SELECT * FROM tmp;
+ DROP TEMPORARY TABLE tmp;
+ --eval XA END '$conn_i$trx$k'
+
+ --let $term = COMMIT
+ --let $decision = `SELECT (floor(rand()*10 % 10) + ($i+$k)) % 4`
+ if ($decision == 1)
+ {
+ --let $term = ROLLBACK
+ }
+ if ($decision < 2)
+ {
+ --eval XA PREPARE '$conn_i$trx$k'
+ --eval XA $term '$conn_i$trx$k'
+ # Iteration counter is taken care *now*
+ }
+ if ($decision == 2)
+ {
+ --eval XA COMMIT '$conn_i$trx$k' ONE PHASE
+ }
+ }
+
+ # $decision = 3
+ --eval XA PREPARE '$conn_i$trx$k'
+ # disconnect now
+ --disconnect master_conn$i
+ --connection master
+
+ --let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $i_conn_id
+ --source include/wait_condition.inc
+
+ --disable_query_log
+ --let $decision = `SELECT ($i+$k) % 2`
+ --let $term = COMMIT
+ if ($decision == 1)
+ {
+ --let $term = ROLLBACK
+ }
+ --eval XA $term '$conn_i$trx$k'
+ --let $c_max = $i2
+
+--dec $i
+}
+--enable_query_log
+--source include/save_master_gtid.inc
+
+--connection slave
+--source include/start_slave.inc
+--source include/sync_with_master_gtid.inc
+
+#
+# Overall consistency check
+#
+--let $diff_tables= master:t0, slave:t0
+--source include/diff_tables.inc
+--let $diff_tables= master:t1, slave:t1
+--source include/diff_tables.inc
+
+
+#
+# Clean up.
+#
+--connection slave
+--source include/stop_slave.inc
+set global log_warnings=default;
+SET GLOBAL slave_parallel_mode=@old_parallel_mode;
+SET GLOBAL slave_parallel_threads=@old_parallel_threads;
+--source include/start_slave.inc
+
+--connection master
+DROP VIEW v_processlist;
+DROP TABLE t0, t1;
+--source include/save_master_gtid.inc
+
+--connection slave
+--source include/sync_with_master_gtid.inc
+# Check that old rows are deleted from mysql.gtid_slave_pos.
+# Deletion is asynchronous, so use wait_condition.inc.
+# Also, there is a small amount of non-determinism in the deletion of old
+# rows, so it is not guaranteed that there can never be more than
+# @@gtid_cleanup_batch_size rows in the table; so allow a bit of slack
+# here.
+let $wait_condition=
+ SELECT COUNT(*) <= 5*@@GLOBAL.gtid_cleanup_batch_size
+ FROM mysql.gtid_slave_pos;
+--source include/wait_condition.inc
+eval $wait_condition;
+SET GLOBAL gtid_cleanup_batch_size= @old_gtid_cleanup_batch_size;
+
+--connection master
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa_lsu_off-slave.opt b/mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa_lsu_off-slave.opt
new file mode 100644
index 00000000000..88cf77fd281
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa_lsu_off-slave.opt
@@ -0,0 +1 @@
+--log-slave-updates=OFF
diff --git a/mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa_lsu_off.test b/mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa_lsu_off.test
new file mode 100644
index 00000000000..f82b522eefe
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_parallel_optimistic_xa_lsu_off.test
@@ -0,0 +1,2 @@
+# --log-slave-updates OFF version of rpl_parallel_optimistic_xa
+--source rpl_parallel_optimistic_xa.test
diff --git a/mysql-test/suite/rpl/t/rpl_parallel_xa_same_xid.test b/mysql-test/suite/rpl/t/rpl_parallel_xa_same_xid.test
new file mode 100644
index 00000000000..888dd2f177b
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_parallel_xa_same_xid.test
@@ -0,0 +1,138 @@
+# The tests verify concurrent execution of replicated (MDEV-742)
+# XA transactions in the parallel optimistic mode.
+# Prove optimistic scheduler handles xid-namesake XA:s.
+# That is despite running in parallel there must be no conflicts
+# caused by multiple transactions' same xid.
+
+--source include/have_binlog_format_mixed_or_row.inc
+--source include/have_innodb.inc
+--source include/have_perfschema.inc
+--source include/master-slave.inc
+
+--let $xid_num = 19
+--let $repeat = 17
+--let $workers = 7
+--connection slave
+call mtr.add_suppression("WSREP: handlerton rollback failed");
+
+--source include/stop_slave.inc
+# a measure against MDEV-20605
+ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+
+SET @old_parallel_threads = @@GLOBAL.slave_parallel_threads;
+--disable_query_log
+--eval SET @@global.slave_parallel_threads = $workers
+--enable_query_log
+SET @old_parallel_mode = @@GLOBAL.slave_parallel_mode;
+SET @@global.slave_parallel_mode ='optimistic';
+--source include/start_slave.inc
+
+--connection master
+CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB;
+
+--let $i = $xid_num
+--let $t = t1
+--disable_query_log
+while ($i)
+{
+--let $k = $repeat
+while ($k)
+{
+--eval XA START 'xid_$i'
+--eval INSERT INTO $t SET a=$i, b=$k
+--eval XA END 'xid_$i'
+--let $one_phase = `SELECT IF(floor(rand()*10)%2, "ONE PHASE", 0)`
+ if (!$one_phase)
+ {
+ --eval XA PREPARE 'xid_$i'
+ --eval XA COMMIT 'xid_$i'
+ }
+ if ($one_phase)
+ {
+ --eval XA COMMIT 'xid_$i' ONE PHASE
+ }
+
+ if (!$one_phase)
+ {
+ --eval XA START 'xid_$i'
+ --eval INSERT INTO $t SET a=$i, b=$k
+ --eval XA END 'xid_$i'
+ --eval XA PREPARE 'xid_$i'
+ --eval XA ROLLBACK 'xid_$i'
+ }
+
+--dec $k
+}
+
+--dec $i
+}
+--enable_query_log
+
+
+
+# Above-like test complicates execution env to create
+# data conflicts as well. They will be resolved by the optmistic
+# scheduler as usual.
+
+CREATE TABLE t2 (a INT AUTO_INCREMENT PRIMARY KEY, b INT) ENGINE=InnoDB;
+
+--let $i = $xid_num
+--let $t = t2
+--disable_query_log
+while ($i)
+{
+--let $k = $repeat
+while ($k)
+{
+--eval XA START 'xid_$i'
+--eval INSERT INTO $t SET a=NULL, b=$k
+--eval UPDATE $t SET b=$k + 1 WHERE a=last_insert_id() % $workers
+--eval XA END 'xid_$i'
+--let $one_phase = `SELECT IF(floor(rand()*10)%2, "ONE PHASE", 0)`
+ if (!$one_phase)
+ {
+ --eval XA PREPARE 'xid_$i'
+ --eval XA COMMIT 'xid_$i'
+ }
+ if ($one_phase)
+ {
+ --eval XA COMMIT 'xid_$i' ONE PHASE
+ }
+
+--eval XA START 'xid_$i'
+--eval UPDATE $t SET b=$k + 1 WHERE a=last_insert_id() % $workers
+--eval DELETE FROM $t WHERE a=last_insert_id()
+--eval XA END 'xid_$i'
+--eval XA PREPARE 'xid_$i'
+--eval XA ROLLBACK 'xid_$i'
+
+--let $do_drop_create = `SELECT IF(floor(rand()*10)%100, 1, 0)`
+if ($do_drop_create)
+{
+ DROP TABLE t1;
+ CREATE TABLE t1 (a INT, b INT) ENGINE=InnoDB;
+}
+--dec $k
+}
+
+--dec $i
+}
+--enable_query_log
+
+--source include/sync_slave_sql_with_master.inc
+--let $diff_tables= master:t1, slave:t1
+--source include/diff_tables.inc
+
+#
+# Clean up.
+#
+--connection slave
+--source include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=@old_parallel_threads;
+SET GLOBAL slave_parallel_mode=@old_parallel_mode;
+--source include/start_slave.inc
+
+--connection master
+DROP TABLE t1, t2;
+
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_temporary_errors.test b/mysql-test/suite/rpl/t/rpl_temporary_errors.test
index 6392fb90b9b..85e16afa270 100644
--- a/mysql-test/suite/rpl/t/rpl_temporary_errors.test
+++ b/mysql-test/suite/rpl/t/rpl_temporary_errors.test
@@ -6,7 +6,7 @@ call mtr.add_suppression("Deadlock found");
call mtr.add_suppression("Can't find record in 't.'");
connection master;
-CREATE TABLE t1 (a INT PRIMARY KEY, b INT);
+CREATE TABLE t1 (a INT PRIMARY KEY, b INT) ENGINE=innodb;
INSERT INTO t1 VALUES (1,1), (2,2), (3,3), (4,4);
sync_slave_with_master;
SHOW STATUS LIKE 'Slave_retried_transactions';
@@ -14,20 +14,94 @@ SHOW STATUS LIKE 'Slave_retried_transactions';
# the following UPDATE t1 to pass the mode is switched temprorarily
set @@global.slave_exec_mode= 'IDEMPOTENT';
UPDATE t1 SET a = 5, b = 47 WHERE a = 1;
-SELECT * FROM t1;
+SELECT * FROM t1 ORDER BY a;
connection master;
UPDATE t1 SET a = 5, b = 5 WHERE a = 1;
-SELECT * FROM t1;
+SELECT * FROM t1 ORDER BY a;
#SHOW BINLOG EVENTS;
sync_slave_with_master;
set @@global.slave_exec_mode= default;
SHOW STATUS LIKE 'Slave_retried_transactions';
-SELECT * FROM t1;
+SELECT * FROM t1 ORDER BY a;
source include/check_slave_is_running.inc;
connection slave;
call mtr.add_suppression("Slave SQL.*Could not execute Update_rows event on table test.t1");
+call mtr.add_suppression("Slave SQL for channel '': worker thread retried transaction");
+call mtr.add_suppression("The slave coordinator and worker threads are stopped");
+#
+# Bug#24764800 REPLICATION FAILING ON SLAVE WITH XAER_RMFAIL ERROR
+#
+# Verify that a temporary failing replicated xa transaction completes
+# upon slave applier restart after previous
+# @@global.slave_transaction_retries number of retries in vain.
+#
+connection slave;
+
+set @save_innodb_lock_wait_timeout=@@global.innodb_lock_wait_timeout;
+set @save_slave_transaction_retries=@@global.slave_transaction_retries;
+
+# Slave applier parameters for the failed retry
+set @@global.innodb_lock_wait_timeout=1;
+set @@global.slave_transaction_retries=2;
+--source include/restart_slave_sql.inc
+
+# Temporary error implement: a record is blocked by slave local trx
+connection slave1;
+BEGIN;
+INSERT INTO t1 SET a = 6, b = 7;
+
+connection master;
+INSERT INTO t1 SET a = 99, b = 99; # slave applier warm up trx
+XA START 'xa1';
+INSERT INTO t1 SET a = 6, b = 6; # this record eventually must be found on slave
+XA END 'xa1';
+XA PREPARE 'xa1';
+
+connection slave;
+# convert_error(ER_LOCK_WAIT_TIMEOUT)
+--let $err_timeout= 1205
+# convert_error(ER_LOCK_DEADLOCK)
+--let $err_deadlock= 1213
+--let $slave_sql_errno=$err_deadlock,$err_timeout
+--let $show_slave_sql_error=
+--source include/wait_for_slave_sql_error.inc
+
+# b. Slave applier parameters for successful retry after restart
+set @@global.innodb_lock_wait_timeout=1;
+set @@global.slave_transaction_retries=100;
+
+--source include/restart_slave_sql.inc
+
+--let $last_retries= query_get_value(SHOW GLOBAL STATUS LIKE 'Slave_retried_transactions', Value, 1)
+--let $status_type=GLOBAL
+--let $status_var=Slave_retried_transactions
+--let $status_var_value=`SELECT 1 + $last_retries`
+--let $$status_var_comparsion= >
+--source include/wait_for_status_var.inc
+
+# Release the record after just one retry
+connection slave1;
+ROLLBACK;
+
+connection master;
+XA COMMIT 'xa1';
+
+--source include/sync_slave_sql_with_master.inc
+
+# Proof of correctness: the committed XA is on the slave
+connection slave;
+--let $assert_text=XA transaction record must be in the table
+--let $assert_cond=count(*)=1 FROM t1 WHERE a=6 AND b=6
+--source include/assert.inc
+
+# Bug#24764800 cleanup:
+set @@global.innodb_lock_wait_timeout=@save_innodb_lock_wait_timeout;
+set @@global.slave_transaction_retries= @save_slave_transaction_retries;
+#
+# Total cleanup:
+#
connection master;
DROP TABLE t1;
--sync_slave_with_master
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..f1ba4cf8557
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa.inc
@@ -0,0 +1,73 @@
+#
+# 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;
+--disable_warnings
+SET pseudo_slave_mode=1;
+--enable_warnings
+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';
+--source include/save_master_gtid.inc
+
+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';
+--disable_warnings
+SET pseudo_slave_mode=0;
+--enable_warnings
+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_gap_lock-slave.opt b/mysql-test/suite/rpl/t/rpl_xa_gap_lock-slave.opt
new file mode 100644
index 00000000000..4602a43ce25
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_gap_lock-slave.opt
@@ -0,0 +1 @@
+--transaction-isolation=READ-COMMITTED
diff --git a/mysql-test/suite/rpl/t/rpl_xa_gap_lock.test b/mysql-test/suite/rpl/t/rpl_xa_gap_lock.test
new file mode 100644
index 00000000000..9c48891b889
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_gap_lock.test
@@ -0,0 +1,137 @@
+# ==== Purpose ====
+#
+# This test will generate two XA transactions on the master in a way that
+# they will block each other on the slave if the transaction isolation level
+# used by the slave applier is more restrictive than the READ COMMITTED one.
+#
+# Consider:
+# E=execute, P=prepare, C=commit;
+# 1=first transaction, 2=second transaction;
+#
+# Master does: E1, E2, P2, P1, C1, C2
+# Slave does: E2, P2, E1, P1, C1, C2
+#
+# The transactions are designed so that, if the applier transaction isolation
+# level is more restrictive than the READ COMMITTED, E1 will be blocked on
+# the slave waiting for gap locks to be released.
+#
+# Step 1
+#
+# The test will verify that the transactions don't block each other because
+# the applier thread automatically changed the isolation level.
+#
+# Step 2
+#
+# The test will verify that applying master's binary log dump in slave doesn't
+# block because mysqlbinlog is informing the isolation level to be used.
+#
+# ==== Related Bugs and Worklogs ====
+#
+# BUG#25040331: INTERLEAVED XA TRANSACTIONS MAY DEADLOCK SLAVE APPLIER WITH
+# REPEATABLE READ
+#
+--source include/have_debug.inc
+--source include/have_innodb.inc
+# The test case only make sense for RBR
+--source include/have_binlog_format_row.inc
+--source include/master-slave.inc
+
+--connection slave
+# To hit the issue, we need to split the data in two pages.
+# This global variable will help us.
+SET @saved_innodb_limit_optimistic_insert_debug = @@GLOBAL.innodb_limit_optimistic_insert_debug;
+SET @@GLOBAL.innodb_limit_optimistic_insert_debug = 2;
+
+#
+# Step 1 - Using async replication
+#
+
+# Let's generate the workload on the master
+--connection master
+CREATE TABLE t1 (
+ c1 INT NOT NULL,
+ KEY(c1)
+) ENGINE=InnoDB;
+
+CREATE TABLE t2 (
+ c1 INT NOT NULL,
+ FOREIGN KEY(c1) REFERENCES t1(c1)
+) ENGINE=InnoDB;
+
+INSERT INTO t1 VALUES (1), (3), (4);
+
+--connection master1
+XA START 'XA1';
+INSERT INTO t1 values(2);
+XA END 'XA1';
+
+# This transaction will reference the gap where XA1
+# was inserted, and will be prepared and committed
+# before XA1, so the slave will prepare it (but will
+# not commit it) before preparing XA1.
+--connection master
+XA START 'XA2';
+INSERT INTO t2 values(3);
+XA END 'XA2';
+
+# The XA2 prepare should be binary logged first
+XA PREPARE 'XA2';
+
+# The XA1 prepare should be binary logged
+# after XA2 prepare and before XA2 commit.
+--connection master1
+XA PREPARE 'XA1';
+
+# The commit order doesn't matter much for the issue being tested.
+XA COMMIT 'XA1';
+--connection master
+XA COMMIT 'XA2';
+
+# Everything is fine if the slave can sync with the master.
+--source include/sync_slave_sql_with_master.inc
+
+#
+# Step 2 - Using mysqlbinlog dump to restore the salve
+#
+--source include/stop_slave.inc
+DROP TABLE t2, t1;
+RESET SLAVE;
+RESET MASTER;
+
+--connection master
+--let $master_data_dir= `SELECT @@datadir`
+--let $master_log_file= query_get_value(SHOW MASTER STATUS, File, 1)
+--let $mysql_server= $MYSQL --defaults-group-suffix=.2
+--echo Restore binary log from the master into the slave
+--exec $MYSQL_BINLOG --force-if-open $master_data_dir/$master_log_file | $mysql_server
+
+--let $diff_tables= master:test.t1, slave:test.t1
+--source include/diff_tables.inc
+--let $diff_tables= master:test.t2, slave:test.t2
+--source include/diff_tables.inc
+
+#
+# Cleanup
+#
+--let $master_file= query_get_value(SHOW MASTER STATUS, File, 1)
+--let $master_pos= query_get_value(SHOW MASTER STATUS, Position, 1)
+DROP TABLE t2, t1;
+
+## When GTID_MODE=OFF, we need to skip already applied transactions
+--connection slave
+#--let $gtid_mode= `SELECT @@GTID_MODE`
+#if ($gtid_mode == OFF)
+#{
+# --disable_query_log
+# --disable_result_log
+# --eval CHANGE MASTER TO MASTER_LOG_FILE='$master_file', MASTER_LOG_POS=$master_pos
+# --enable_result_log
+# --enable_query_log
+#}
+--replace_result $master_file LOG_FILE $master_pos LOG_POS
+--eval CHANGE MASTER TO MASTER_LOG_FILE='$master_file', MASTER_LOG_POS=$master_pos
+
+SET @@GLOBAL.innodb_limit_optimistic_insert_debug = @saved_innodb_limit_optimistic_insert_debug;
+--source include/start_slave.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..b83493762c3
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_gtid_pos_auto_engine.test
@@ -0,0 +1,29 @@
+--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;
+if (`SHOW COUNT(*) WARNINGS`)
+{
+ show tables in mysql like 'gtid_slave_pos%';
+}
+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.test b/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect.test
new file mode 100644
index 00000000000..1c33435473e
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect.test
@@ -0,0 +1,294 @@
+# BUG #12161 Xa recovery and client disconnection
+# the test verifies that
+# a. disconnection does not lose a prepared transaction
+# so it can be committed from another connection
+# c. the prepared transaction is logged
+# d. interleaved prepared transactions are correctly applied on the slave.
+
+#
+# Both replication format are checked through explict
+# set @@binlog_format in the test.
+#
+--source include/have_innodb.inc
+--source include/have_binlog_format_mixed.inc
+#
+# Prepared XA can't get available to an external connection
+# until a 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_perfschema.inc
+--source include/master-slave.inc
+
+--connection master
+call mtr.add_suppression("Found 2 prepared XA transactions");
+CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
+
+CREATE DATABASE d1;
+CREATE DATABASE d2;
+
+CREATE TABLE d1.t (a INT) ENGINE=innodb;
+CREATE TABLE d2.t (a INT) ENGINE=innodb;
+
+connect (master_conn1, 127.0.0.1,root,,test,$MASTER_MYPORT,);
+--let $conn_id=`SELECT connection_id()`
+SET @@session.binlog_format= statement;
+XA START '1-stmt';
+INSERT INTO d1.t VALUES (1);
+XA END '1-stmt';
+XA PREPARE '1-stmt';
+
+--disconnect master_conn1
+
+--connection master
+
+--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
+--source include/wait_condition.inc
+
+connect (master_conn2, 127.0.0.1,root,,test,$MASTER_MYPORT,);
+--let $conn_id=`SELECT connection_id()`
+SET @@session.binlog_format= row;
+XA START '1-row';
+INSERT INTO d2.t VALUES (1);
+XA END '1-row';
+XA PREPARE '1-row';
+
+--disconnect master_conn2
+
+--connection master
+--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
+--source include/wait_condition.inc
+
+XA START '2';
+INSERT INTO d1.t VALUES (2);
+XA END '2';
+XA PREPARE '2';
+XA COMMIT '2';
+
+XA COMMIT '1-row';
+XA COMMIT '1-stmt';
+source include/show_binlog_events.inc;
+
+# the proof: slave is in sync with the table updated by the prepared transactions.
+--source include/sync_slave_sql_with_master.inc
+
+--source include/stop_slave.inc
+
+#
+# Recover with Master server restart
+#
+--connection master
+
+connect (master2, 127.0.0.1,root,,test,$MASTER_MYPORT,);
+--connection master2
+SET @@session.binlog_format= statement;
+XA START '3-stmt';
+INSERT INTO d1.t VALUES (3);
+XA END '3-stmt';
+XA PREPARE '3-stmt';
+--disconnect master2
+
+connect (master2, 127.0.0.1,root,,test,$MASTER_MYPORT,);
+--connection master2
+SET @@session.binlog_format= row;
+XA START '3-row';
+INSERT INTO d2.t VALUES (4);
+XA END '3-row';
+XA PREPARE '3-row';
+--disconnect master2
+
+--connection master
+
+#
+# Testing read-only
+#
+connect (master2, 127.0.0.1,root,,test,$MASTER_MYPORT,);
+--connection master2
+XA START '4';
+SELECT * FROM d1.t;
+XA END '4';
+XA PREPARE '4';
+--disconnect master2
+
+#
+# Logging few disconnected XA:s for replication.
+#
+--let $bulk_trx_num=10
+--let $i = $bulk_trx_num
+
+while($i > 0)
+{
+ --connect (master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,)
+ --let $conn_id=`SELECT connection_id()`
+
+ --eval XA START 'bulk_trx_$i'
+ --eval INSERT INTO d1.t VALUES ($i)
+ --eval INSERT INTO d2.t VALUES ($i)
+ --eval XA END 'bulk_trx_$i'
+ --eval XA PREPARE 'bulk_trx_$i'
+
+ --disconnect master_bulk_conn$i
+
+ --connection master
+ --let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
+ --source include/wait_condition.inc
+
+ --dec $i
+}
+
+#
+# Prove the slave applier is capable to resume the prepared XA:s
+# upon its restart.
+#
+--connection slave
+--source include/start_slave.inc
+--connection master
+--source include/sync_slave_sql_with_master.inc
+--source include/stop_slave.inc
+
+--connection master
+--let $i = $bulk_trx_num
+while($i > 0)
+{
+ --let $command=COMMIT
+ if (`SELECT $i % 2`)
+ {
+ --let $command=ROLLBACK
+ }
+ --eval XA $command 'bulk_trx_$i'
+ --dec $i
+}
+
+--let $rpl_server_number= 1
+--source include/rpl_restart_server.inc
+
+--connection slave
+--source include/start_slave.inc
+
+--connection master
+--echo *** '3-stmt','3-row' xa-transactions must be in the list ***
+XA RECOVER;
+XA COMMIT '3-stmt';
+XA ROLLBACK '3-row';
+
+--source include/sync_slave_sql_with_master.inc
+
+#
+# Testing replication with marginal XID values and in two formats.
+#
+
+--connection master
+--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
+--source include/wait_condition.inc
+
+# Max size XID incl max value of formatID
+connect (master_conn2, 127.0.0.1,root,,test,$MASTER_MYPORT,);
+--let $conn_id=`SELECT connection_id()`
+
+--let $gtrid=0123456789012345678901234567890123456789012345678901234567890124
+--let $bqual=0123456789012345678901234567890123456789012345678901234567890124
+--eval XA START '$gtrid','$bqual',4294967292
+ INSERT INTO d1.t VALUES (64);
+--eval XA END '$gtrid','$bqual',4294967292
+--eval XA PREPARE '$gtrid','$bqual',4294967292
+
+--disconnect master_conn2
+
+--connection master
+--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
+--source include/wait_condition.inc
+
+# Max size XID with non-ascii chars
+connect (master_conn3, 127.0.0.1,root,,test,$MASTER_MYPORT,);
+--let $conn_id=`SELECT connection_id()`
+
+--let $gtrid_hex=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+--let $bqual_hex=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+--eval XA START X'$gtrid_hex',X'$bqual_hex',0
+ INSERT INTO d1.t VALUES (0);
+--eval XA END X'$gtrid_hex',X'$bqual_hex',0
+--eval XA PREPARE X'$gtrid_hex',X'$bqual_hex',0
+
+--disconnect master_conn3
+
+--connection master
+--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
+--source include/wait_condition.inc
+
+# Random XID
+--disable_query_log
+
+connect (master_conn4, 127.0.0.1,root,,test,$MASTER_MYPORT,);
+--let $conn_id=`SELECT connection_id()`
+
+--let $gtridlen=`SELECT 2*(1 + round(rand()*100) % 31)`
+--let $bquallen=`SELECT 2*(1 + round(rand()*100) % 31)`
+--let $gtrid_rand=`SELECT substring(concat(MD5(rand()), MD5(rand())), 1, $gtridlen)`
+--let $bqual_rand=`SELECT substring(concat(MD5(rand()), MD5(rand())), 1, $bquallen)`
+--let $formt_rand=`SELECT floor((rand()*10000000000) % 4294967293)`
+--eval XA START X'$gtrid_rand',X'$bqual_rand',$formt_rand
+ INSERT INTO d1.t VALUES (0);
+--eval XA END X'$gtrid_rand',X'$bqual_rand',$formt_rand
+--eval XA PREPARE X'$gtrid_rand',X'$bqual_rand',$formt_rand
+
+--enable_query_log
+
+--disconnect master_conn4
+
+--connection master
+--let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
+--source include/wait_condition.inc
+
+--eval XA COMMIT '$gtrid','$bqual',4294967292
+--eval XA COMMIT X'$gtrid_hex',X'$bqual_hex',0
+--disable_query_log
+--echo XA COMMIT 'RANDOM XID'
+--eval XA COMMIT X'$gtrid_rand',X'$bqual_rand',$formt_rand
+--enable_query_log
+
+--source include/sync_slave_sql_with_master.inc
+
+#
+# Testing ONE PHASE
+#
+--let $onephase_trx_num=10
+--let $i = $onephase_trx_num
+while($i > 0)
+{
+ --connect (master_bulk_conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,)
+
+ --connection master_bulk_conn$i
+ --eval XA START 'one_phase_$i'
+ --eval INSERT INTO d1.t VALUES ($i)
+ --eval INSERT INTO d2.t VALUES ($i)
+ --eval XA END 'one_phase_$i'
+ --eval XA COMMIT 'one_phase_$i' ONE PHASE
+
+ --disconnect master_bulk_conn$i
+ --dec $i
+}
+--connection master
+--source include/sync_slave_sql_with_master.inc
+
+#
+# Overall consistency check
+#
+--let $diff_tables= master:d1.t, slave:d1.t
+--source include/diff_tables.inc
+--let $diff_tables= master:d2.t, slave:d2.t
+--source include/diff_tables.inc
+#
+# cleanup
+#
+--connection master
+
+DELETE FROM d1.t;
+DELETE FROM d2.t;
+DROP TABLE d1.t, d2.t;
+DROP DATABASE d1;
+DROP DATABASE d2;
+DROP VIEW v_processlist;
+
+--source include/sync_slave_sql_with_master.inc
+
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_lsu_off-slave.opt b/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_lsu_off-slave.opt
new file mode 100644
index 00000000000..94c3650024f
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_lsu_off-slave.opt
@@ -0,0 +1,2 @@
+--log-slave-updates=off
+
diff --git a/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_lsu_off.test b/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_lsu_off.test
new file mode 100644
index 00000000000..df3811df6ae
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_lsu_off.test
@@ -0,0 +1,8 @@
+# ==== Purpose ====
+# 'rpl_xa_survive_disconnect_lsu_off' verifies the same properties as the sourced file
+# in conditions of the slave does not log own updates
+# (lsu in the name stands for log_slave_updates).
+# Specifically this mode aims at proving correct operations on the slave
+# mysql.gtid_executed.
+
+--source ./rpl_xa_survive_disconnect.test
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..f52a9630a87
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_mixed_engines.test
@@ -0,0 +1,68 @@
+# 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.
+# Transactions are terminated according to specfied parameters to
+# a sourced inc-file.
+
+--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");
+
+--let $command=setup
+--source include/rpl_xa_mixed_engines.inc
+
+--echo === COMMIT ===
+--let $command=run
+--let $xa_terminate=XA COMMIT
+--let $xa_prepare_opt=1
+--source include/rpl_xa_mixed_engines.inc
+
+--source include/sync_slave_sql_with_master.inc
+--connection master
+
+--echo === COMMIT ONE PHASE ===
+
+--let $command=run
+--let $xa_terminate=XA COMMIT
+--let $one_phase=ONE PHASE
+--let $xa_prepare_opt=
+--source include/rpl_xa_mixed_engines.inc
+--let $one_phase=
+--source include/sync_slave_sql_with_master.inc
+--connection master
+
+--echo === ROLLBACK with PREPARE ===
+
+--let $command=run
+--let $xa_terminate=xa rollback
+--let $xa_prepare_opt=1
+--source include/rpl_xa_mixed_engines.inc
+
+--source include/sync_slave_sql_with_master.inc
+--connection master
+
+--echo === ROLLBACK with no PREPARE ===
+
+--let $command=run
+--let $xa_terminate=xa rollback
+--let $xa_prepare_opt=
+--source include/rpl_xa_mixed_engines.inc
+--let $xa_rollback_only=
+
+--source include/sync_slave_sql_with_master.inc
+
+--let $diff_tables= master:tm, slave:tm
+--source include/diff_tables.inc
+
+# Cleanup
+
+--connection master
+--let $command=cleanup
+--source include/rpl_xa_mixed_engines.inc
+
+--source include/sync_slave_sql_with_master.inc
+
+--source include/rpl_end.inc
diff --git a/sql/handler.cc b/sql/handler.cc
index 7d61252eea6..c72386c7e99 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1300,6 +1300,14 @@ int ha_prepare(THD *thd)
}
}
+
+ DEBUG_SYNC(thd, "at_unlog_xa_prepare");
+
+ if (tc_log->unlog_xa_prepare(thd, all))
+ {
+ ha_rollback_trans(thd, all);
+ error=1;
+ }
}
DBUG_RETURN(error);
@@ -1853,7 +1861,8 @@ int ha_rollback_trans(THD *thd, bool all)
rollback without signalling following transactions. And in release
builds, we explicitly do the signalling before rolling back.
*/
- DBUG_ASSERT(!(thd->rgi_slave && thd->rgi_slave->did_mark_start_commit));
+ DBUG_ASSERT(!(thd->rgi_slave && thd->rgi_slave->did_mark_start_commit) ||
+ thd->transaction.xid_state.is_explicit_XA());
if (thd->rgi_slave && thd->rgi_slave->did_mark_start_commit)
thd->rgi_slave->unmark_start_commit();
}
diff --git a/sql/log.cc b/sql/log.cc
index 56e83bf2448..e13f8fbc88f 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -91,7 +91,13 @@ static bool binlog_savepoint_rollback_can_release_mdl(handlerton *hton,
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_xa_recover_dummy(handlerton *hton, XID *xid_list, uint len);
+static int binlog_commit_by_xid(handlerton *hton, XID *xid);
+static int binlog_rollback_by_xid(handlerton *hton, XID *xid);
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") };
@@ -1693,6 +1699,10 @@ int binlog_init(void *p)
{
binlog_hton->prepare= binlog_prepare;
binlog_hton->start_consistent_snapshot= binlog_start_consistent_snapshot;
+ binlog_hton->commit_by_xid= binlog_commit_by_xid;
+ binlog_hton->rollback_by_xid= binlog_rollback_by_xid;
+ // recover needs to be set to make xa{commit,rollback}_handlerton effective
+ binlog_hton->recover= binlog_xa_recover_dummy;
}
binlog_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN;
return 0;
@@ -1765,7 +1775,8 @@ binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr,
DBUG_PRINT("enter", ("end_ev: %p", end_ev));
if ((using_stmt && !cache_mngr->stmt_cache.empty()) ||
- (using_trx && !cache_mngr->trx_cache.empty()))
+ (using_trx && !cache_mngr->trx_cache.empty()) ||
+ thd->transaction.xid_state.is_explicit_XA())
{
if (using_stmt && thd->binlog_flush_pending_rows_event(TRUE, FALSE))
DBUG_RETURN(1);
@@ -1837,6 +1848,17 @@ binlog_commit_flush_stmt_cache(THD *thd, bool all,
DBUG_RETURN(binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, FALSE));
}
+
+inline size_t serialize_with_xid(XID *xid, char *buf,
+ const char *query, size_t q_len)
+{
+ memcpy(buf, query, q_len);
+
+ return
+ q_len + strlen(static_cast<event_xid_t*>(xid)->serialize(buf + q_len));
+}
+
+
/**
This function flushes the trx-cache upon commit.
@@ -1850,11 +1872,28 @@ static inline int
binlog_commit_flush_trx_cache(THD *thd, bool all, binlog_cache_mngr *cache_mngr)
{
DBUG_ENTER("binlog_commit_flush_trx_cache");
- Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
- TRUE, TRUE, TRUE, 0);
+
+ const char query[]= "XA COMMIT ";
+ const size_t q_len= sizeof(query) - 1; // do not count trailing 0
+ char buf[q_len + ser_buf_size]= "COMMIT";
+ size_t buflen= sizeof("COMMIT") - 1;
+
+ if (thd->lex->sql_command == SQLCOM_XA_COMMIT &&
+ thd->lex->xa_opt != XA_ONE_PHASE)
+ {
+ DBUG_ASSERT(thd->transaction.xid_state.is_explicit_XA());
+ DBUG_ASSERT(thd->transaction.xid_state.get_state_code() ==
+ XA_PREPARED);
+
+ buflen= serialize_with_xid(thd->transaction.xid_state.get_xid(),
+ buf, query, q_len);
+ }
+ Query_log_event end_evt(thd, buf, buflen, TRUE, TRUE, TRUE, 0);
+
DBUG_RETURN(binlog_flush_cache(thd, cache_mngr, &end_evt, all, FALSE, TRUE));
}
+
/**
This function flushes the trx-cache upon rollback.
@@ -1868,8 +1907,20 @@ static inline int
binlog_rollback_flush_trx_cache(THD *thd, bool all,
binlog_cache_mngr *cache_mngr)
{
- Query_log_event end_evt(thd, STRING_WITH_LEN("ROLLBACK"),
- TRUE, TRUE, TRUE, 0);
+ const char query[]= "XA ROLLBACK ";
+ const size_t q_len= sizeof(query) - 1; // do not count trailing 0
+ char buf[q_len + ser_buf_size]= "ROLLBACK";
+ size_t buflen= sizeof("ROLLBACK") - 1;
+
+ if (thd->transaction.xid_state.is_explicit_XA())
+ {
+ /* for not prepared use plain ROLLBACK */
+ if (thd->transaction.xid_state.get_state_code() == XA_PREPARED)
+ buflen= serialize_with_xid(thd->transaction.xid_state.get_xid(),
+ buf, query, q_len);
+ }
+ Query_log_event end_evt(thd, buf, buflen, TRUE, TRUE, TRUE, 0);
+
return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, FALSE, TRUE));
}
@@ -1887,23 +1938,10 @@ 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
- {
- /*
- 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(xid); // replaced former treatment of ONE-PHASE XA
+
+ Xid_log_event end_evt(thd, xid, TRUE);
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
}
/**
@@ -1959,17 +1997,67 @@ binlog_truncate_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr, bool all)
DBUG_RETURN(error);
}
+
+inline bool is_preparing_xa(THD *thd)
+{
+ return
+ thd->transaction.xid_state.is_explicit_XA() &&
+ thd->lex->sql_command == SQLCOM_XA_PREPARE;
+}
+
+
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()
+ Do nothing unless the transaction is a user XA.
*/
+ return
+ !is_preparing_xa(thd) ? 0 : binlog_commit(NULL, thd, all);
+}
+
+
+static int binlog_xa_recover_dummy(handlerton *hton __attribute__((unused)),
+ XID *xid_list __attribute__((unused)),
+ uint len __attribute__((unused)))
+{
+ /* Does nothing. */
return 0;
}
+
+static int binlog_commit_by_xid(handlerton *hton, XID *xid)
+{
+ THD *thd= current_thd;
+
+ if (thd->transaction.xid_state.is_binlogged())
+ (void) thd->binlog_setup_trx_data();
+
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_COMMIT);
+
+ return binlog_commit(hton, thd, TRUE);
+}
+
+
+static int binlog_rollback_by_xid(handlerton *hton, XID *xid)
+{
+ THD *thd= current_thd;
+
+ if (thd->transaction.xid_state.is_binlogged())
+ (void) thd->binlog_setup_trx_data();
+
+ DBUG_ASSERT(thd->lex->sql_command == SQLCOM_XA_ROLLBACK);
+
+ return binlog_rollback(hton, thd, TRUE);
+}
+
+
+inline bool is_prepared_xa(THD *thd)
+{
+ return thd->transaction.xid_state.is_explicit_XA() &&
+ thd->transaction.xid_state.get_state_code() == XA_PREPARED;
+}
+
+
/*
We flush the cache wrapped in a beging/rollback if:
. aborting a single or multi-statement transaction and;
@@ -1992,7 +2080,55 @@ static bool trans_cannot_safely_rollback(THD *thd, bool all)
thd->wsrep_binlog_format() == BINLOG_FORMAT_MIXED) ||
(trans_has_updated_non_trans_table(thd) &&
ending_single_stmt_trans(thd,all) &&
- thd->wsrep_binlog_format() == BINLOG_FORMAT_MIXED));
+ thd->wsrep_binlog_format() == BINLOG_FORMAT_MIXED) ||
+ is_prepared_xa(thd));
+}
+
+
+/**
+ Specific log flusher invoked through log_xa_prepare().
+*/
+static int binlog_commit_flush_xa_prepare(THD *thd, bool all,
+ binlog_cache_mngr *cache_mngr)
+{
+ XID *xid= thd->transaction.xid_state.get_xid();
+ {
+ // todo assert wsrep_simulate || is_open()
+
+ /*
+ 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 char query[]= "XA END ";
+ const size_t q_len= sizeof(query) - 1; // do not count trailing 0
+ char buf[q_len + ser_buf_size];
+ size_t buflen;
+ binlog_cache_data *cache_data;
+ IO_CACHE *file;
+
+ memcpy(buf, query, q_len);
+ buflen= q_len +
+ strlen(static_cast<event_xid_t*>(xid)->serialize(buf + q_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 (mysql_bin_log.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);
+ /*
+ Memorize the fact of prepare-logging to recall at commit
+ possibly from another session.
+ */
+ 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));
}
@@ -2019,7 +2155,9 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
if (!cache_mngr)
{
- DBUG_ASSERT(WSREP(thd));
+ DBUG_ASSERT(WSREP(thd) ||
+ (thd->transaction.xid_state.is_explicit_XA() &&
+ !thd->transaction.xid_state.is_binlogged()));
DBUG_RETURN(0);
}
@@ -2038,7 +2176,8 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
error= binlog_commit_flush_stmt_cache(thd, all, cache_mngr);
}
- if (cache_mngr->trx_cache.empty())
+ if (cache_mngr->trx_cache.empty() &&
+ !thd->transaction.xid_state.is_binlogged())
{
/*
we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
@@ -2055,8 +2194,11 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
Otherwise, we accumulate the changes.
*/
if (likely(!error) && ending_trans(thd, all))
- error= binlog_commit_flush_trx_cache(thd, all, cache_mngr);
-
+ {
+ error= !is_preparing_xa(thd) ?
+ binlog_commit_flush_trx_cache (thd, all, cache_mngr) :
+ binlog_commit_flush_xa_prepare(thd, all, cache_mngr);
+ }
/*
This is part of the stmt rollback.
*/
@@ -2080,13 +2222,16 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
static int binlog_rollback(handlerton *hton, THD *thd, bool all)
{
DBUG_ENTER("binlog_rollback");
+
int error= 0;
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
if (!cache_mngr)
{
- DBUG_ASSERT(WSREP(thd));
+ DBUG_ASSERT(WSREP(thd) ||
+ (is_prepared_xa(thd) ||
+ !thd->transaction.xid_state.is_binlogged()));
DBUG_RETURN(0);
}
@@ -2101,15 +2246,16 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
*/
if (cache_mngr->stmt_cache.has_incident())
{
- error= mysql_bin_log.write_incident(thd);
+ error |= static_cast<int>(mysql_bin_log.write_incident(thd));
cache_mngr->reset(true, false);
}
else if (!cache_mngr->stmt_cache.empty())
{
- error= binlog_commit_flush_stmt_cache(thd, all, cache_mngr);
+ error |= binlog_commit_flush_stmt_cache(thd, all, cache_mngr);
}
- if (cache_mngr->trx_cache.empty())
+ if (cache_mngr->trx_cache.empty() &&
+ !thd->transaction.xid_state.is_binlogged())
{
/*
we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
@@ -7340,10 +7486,10 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd,
entry.all= all;
entry.using_stmt_cache= using_stmt_cache;
entry.using_trx_cache= using_trx_cache;
- entry.need_unlog= false;
+ entry.need_unlog= is_preparing_xa(thd);
ha_info= all ? thd->transaction.all.ha_list : thd->transaction.stmt.ha_list;
- for (; ha_info; ha_info= ha_info->next())
+ for (; !entry.need_unlog && ha_info; ha_info= ha_info->next())
{
if (ha_info->is_started() && ha_info->ht() != binlog_hton &&
!ha_info->ht()->commit_checkpoint_request)
@@ -7916,7 +8062,9 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
We already checked before that at least one cache is non-empty; if both
are empty we would have skipped calling into here.
*/
- DBUG_ASSERT(!cache_mngr->stmt_cache.empty() || !cache_mngr->trx_cache.empty());
+ DBUG_ASSERT(!cache_mngr->stmt_cache.empty() ||
+ !cache_mngr->trx_cache.empty() ||
+ current->thd->transaction.xid_state.is_explicit_XA());
if (unlikely((current->error= write_transaction_or_stmt(current,
commit_id))))
@@ -7925,7 +8073,7 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader)
strmake_buf(cache_mngr->last_commit_pos_file, log_file_name);
commit_offset= my_b_write_tell(&log_file);
cache_mngr->last_commit_pos_offset= commit_offset;
- if (cache_mngr->using_xa && cache_mngr->xa_xid)
+ if ((cache_mngr->using_xa && cache_mngr->xa_xid) || current->need_unlog)
{
/*
If all storage engines support commit_checkpoint_request(), then we
@@ -8160,7 +8308,8 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry,
binlog_cache_mngr *mngr= entry->cache_mngr;
DBUG_ENTER("MYSQL_BIN_LOG::write_transaction_or_stmt");
- if (write_gtid_event(entry->thd, false, entry->using_trx_cache, commit_id))
+ if (write_gtid_event(entry->thd, is_prepared_xa(entry->thd),
+ entry->using_trx_cache, commit_id))
DBUG_RETURN(ER_ERROR_ON_WRITE);
if (entry->using_stmt_cache && !mngr->stmt_cache.empty() &&
@@ -9862,6 +10011,24 @@ int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
DBUG_RETURN(BINLOG_COOKIE_GET_ERROR_FLAG(cookie));
}
+
+int TC_LOG_BINLOG::unlog_xa_prepare(THD *thd, bool all)
+{
+ DBUG_ASSERT(is_preparing_xa(thd));
+
+ binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data();
+ int cookie= 0;
+
+ if (!cache_mngr || !cache_mngr->need_unlog)
+ return 0;
+ else
+ cookie= BINLOG_COOKIE_MAKE(cache_mngr->binlog_id, cache_mngr->delayed_error);
+ cache_mngr->need_unlog= false;
+
+ return unlog(cookie, 1);
+}
+
+
void
TC_LOG_BINLOG::commit_checkpoint_notify(void *cookie)
{
@@ -10178,6 +10345,7 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
((last_gtid_standalone && !ev->is_part_of_group(typ)) ||
(!last_gtid_standalone &&
(typ == XID_EVENT ||
+ typ == XA_PREPARE_LOG_EVENT ||
(LOG_EVENT_IS_QUERY(typ) &&
(((Query_log_event *)ev)->is_commit() ||
((Query_log_event *)ev)->is_rollback()))))))
diff --git a/sql/log.h b/sql/log.h
index 8684eaba786..8e70d3c8f4c 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -61,6 +61,7 @@ class TC_LOG
bool need_prepare_ordered,
bool need_commit_ordered) = 0;
virtual int unlog(ulong cookie, my_xid xid)=0;
+ virtual int unlog_xa_prepare(THD *thd, bool all)= 0;
virtual void commit_checkpoint_notify(void *cookie)= 0;
protected:
@@ -115,6 +116,10 @@ class TC_LOG_DUMMY: public TC_LOG // use it to disable the logging
return 1;
}
int unlog(ulong cookie, my_xid xid) { return 0; }
+ int unlog_xa_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie) { DBUG_ASSERT(0); };
};
@@ -198,6 +203,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 unlog_xa_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie);
int recover();
@@ -695,6 +704,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_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 unlog_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 4c1c18fffff..ee44f7f1da4 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -1178,6 +1178,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;
@@ -1229,7 +1232,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;
@@ -2066,6 +2068,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;
/*
@@ -2556,7 +2559,7 @@ Binlog_checkpoint_log_event::Binlog_checkpoint_log_event(
Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
const Format_description_log_event *description_event)
- : Log_event(buf, description_event), seq_no(0), commit_id(0)
+ : Log_event(buf, description_event), seq_no(0), commit_id(0), xid_pins_idx(0)
{
uint8 header_size= description_event->common_header_len;
uint8 post_header_len= description_event->post_header_len[GTID_EVENT-1];
@@ -2569,7 +2572,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)
@@ -2577,8 +2580,24 @@ 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 | FL_COMPLETED_XA))
+ {
+ uint32 temp= 0;
+
+ memcpy(&temp, buf, sizeof(temp));
+ xid.formatID= uint4korr(&temp);
+ buf += sizeof(temp);
+
+ xid.gtrid_length= (long) buf[0];
+ xid.bqual_length= (long) buf[1];
+ buf+= 2;
+
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(xid.data, buf, data_length);
+ buf+= data_length;
}
}
@@ -2764,7 +2783,7 @@ Rand_log_event::Rand_log_event(const char* buf,
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 +
@@ -2772,6 +2791,36 @@ Xid_log_event(const char* buf,
memcpy((char*) &xid, buf, sizeof(xid));
}
+/**************************************************************************
+ XA_prepare_log_event methods
+**************************************************************************/
+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= uint4korr(&temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.gtrid_length= uint4korr(&temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.bqual_length= uint4korr(&temp);
+ buf += sizeof(temp);
+ memcpy(m_xid.data, buf, m_xid.gtrid_length + m_xid.bqual_length);
+
+ xid= NULL;
+}
+
/**************************************************************************
User_var_log_event methods
diff --git a/sql/log_event.h b/sql/log_event.h
index 15442bd5a97..a6543b70eb5 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -222,6 +222,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
@@ -664,6 +665,7 @@ enum Log_event_type
/* MySQL 5.7 events, ignored by MariaDB */
TRANSACTION_CONTEXT_EVENT= 36,
VIEW_CHANGE_EVENT= 37,
+ /* not ignored */
XA_PREPARE_LOG_EVENT= 38,
/*
@@ -3022,6 +3024,32 @@ class Rand_log_event: public Log_event
#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);
+ virtual const char* get_query()= 0;
+#endif
+};
+
+
/**
@class Xid_log_event
@@ -3034,18 +3062,22 @@ class Rand_log_event: public Log_event
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), xid(x)
{
if (direct)
cache_type= Log_event::EVENT_NO_CACHE;
}
+ const char* get_query()
+ {
+ return "COMMIT /* implicit, from Xid_log_event */";
+ }
#ifdef HAVE_REPLICATION
void pack_info(Protocol* protocol);
#endif /* HAVE_REPLICATION */
@@ -3061,15 +3093,171 @@ 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. The latter option is only for
+ compatibility with the upstream.
+
+ From the groupping perspective the event finalizes the current
+ "prepare" group that is started with Gtid_log_event similarly to the
+ regular replicated transaction.
+*/
+
+/**
+ 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
+ {MYSQL_,}XID definitions.
+
+ @param buf pointer to a buffer allocated for storing serialized data
+ @param fmt formatID value
+ @param gln gtrid_length value
+ @param bln bqual_length value
+ @param dat data value
+
+ @return the value of the buffer pointer
+*/
+
+inline char *serialize_xid(char *buf, long fmt, long gln, long bln,
+ const char *dat)
+{
+ int i;
+ char *c= buf;
+ /*
+ Build a string consisting of the hex format representation of XID
+ as passed through fmt,gln,bln,dat argument:
+ X'hex11hex12...hex1m',X'hex21hex22...hex2n',11
+ and store it into buf.
+ */
+ c[0]= 'X';
+ c[1]= '\'';
+ c+= 2;
+ for (i= 0; i < gln; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) dat)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) dat)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ c[1]= ',';
+ c[2]= 'X';
+ c[3]= '\'';
+ c+= 4;
+
+ for (; i < gln + bln; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) dat)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) dat)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ sprintf(c+1, ",%lu", fmt);
+
+ return buf;
+}
+
+/*
+ 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 * MYSQL_XIDDATASIZE + 4 * sizeof(long) + 1;
+
+struct event_mysql_xid_t : MYSQL_XID
+{
+ char buf[ser_buf_size];
+ char *serialize()
+ {
+ return serialize_xid(buf, formatID, gtrid_length, bqual_length, data);
+ }
+};
+
+#ifndef MYSQL_CLIENT
+struct event_xid_t : XID
+{
+ char buf[ser_buf_size];
+
+ char *serialize(char *buf_arg)
+ {
+ return serialize_xid(buf_arg, formatID, gtrid_length, bqual_length, data);
+ }
+ char *serialize()
+ {
+ return serialize(buf);
+ }
+};
+#endif
+
+class XA_prepare_log_event: public Xid_apply_log_event
+{
+protected:
+
+ /* Constant contributor to subheader in write() by members of XID struct. */
+ static const int xid_subheader_no_data= 12;
+ event_mysql_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_subheader_no_data + m_xid.gtrid_length + m_xid.bqual_length;
+ }
+
+#ifdef MYSQL_SERVER
+ bool write();
+#endif
+
+private:
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ char query[sizeof("XA COMMIT ONE PHASE") + 1 + ser_buf_size];
+ int do_commit();
+ const char* get_query()
+ {
+ sprintf(query,
+ (one_phase ? "XA COMMIT %s ONE PHASE" : "XA PREPARE %s"),
+ m_xid.serialize());
+ return query;
+ }
+#endif
+};
+
+
/**
@class User_var_log_event
@@ -3382,8 +3570,13 @@ class Gtid_log_event: public Log_event
uint64 seq_no;
uint64 commit_id;
uint32 domain_id;
+#ifdef MYSQL_SERVER
+ event_xid_t xid;
+#else
+ event_mysql_xid_t xid;
+#endif
uchar flags2;
-
+ uint32 xid_pins_idx;
/* Flags2. */
/* FL_STANDALONE is set when there is no terminating COMMIT event. */
@@ -3410,6 +3603,10 @@ class Gtid_log_event: public Log_event
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;
+ /* FL_"COMMITTED or ROLLED-BACK"_XA is set for XA transaction. */
+ static const uchar FL_COMPLETED_XA= 128;
#ifdef MYSQL_SERVER
Gtid_log_event(THD *thd_arg, uint64 seq_no, uint32 domain_id, bool standalone,
diff --git a/sql/log_event_client.cc b/sql/log_event_client.cc
index cae4842355a..0e57b82476b 100644
--- a/sql/log_event_client.cc
+++ b/sql/log_event_client.cc
@@ -3892,11 +3892,44 @@ 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 ");
+ xid.serialize();
+ my_b_write(&cache, (uchar*) xid.buf, strlen(xid.buf));
+ 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:
return 1;
}
+
+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);
+ m_xid.serialize();
+
+ if (!print_event_info->short_form)
+ {
+ print_header(&cache, print_event_info, FALSE);
+ if (my_b_printf(&cache, "\tXID = %s\n", m_xid.buf))
+ goto error;
+ }
+
+ if (my_b_printf(&cache, "XA PREPARE %s\n%s\n",
+ m_xid.buf, print_event_info->delimiter))
+ goto error;
+
+ return cache.flush_data();
+error:
+ return TRUE;
+}
diff --git a/sql/log_event_server.cc b/sql/log_event_server.cc
index 202a41c2837..210d321d2cd 100644
--- a/sql/log_event_server.cc
+++ b/sql/log_event_server.cc
@@ -1487,6 +1487,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:
@@ -3218,6 +3219,26 @@ 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));
+
+ XID_STATE &xid_state= thd->transaction.xid_state;
+ if (is_transactional && xid_state.is_explicit_XA() &&
+ ((xid_state.get_state_code() == XA_IDLE &&
+ thd->lex->sql_command == SQLCOM_XA_PREPARE) ||
+ xid_state.get_state_code() == XA_PREPARED))
+ {
+ DBUG_ASSERT(thd->lex->xa_opt != XA_ONE_PHASE);
+ DBUG_ASSERT(xid_state.get_state_code() == XA_IDLE ||
+ xid_state.is_binlogged());
+
+ flags2|= xid_state.get_state_code() == XA_IDLE ?
+ FL_PREPARED_XA : FL_COMPLETED_XA;
+
+ xid.formatID= xid_state.get_xid()->formatID;
+ xid.gtrid_length= xid_state.get_xid()->gtrid_length;
+ xid.bqual_length= xid_state.get_xid()->bqual_length;
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(xid.data, xid_state.get_xid()->data, data_length);
+ }
}
@@ -3260,7 +3281,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);
@@ -3272,8 +3293,22 @@ Gtid_log_event::write()
write_len= GTID_HEADER_LEN + 2;
}
else
+ write_len= 13;
+
+ if (flags2 & (FL_PREPARED_XA | FL_COMPLETED_XA))
+ {
+ int4store(&buf[write_len], xid.formatID);
+ buf[write_len +4]= (uchar) xid.gtrid_length;
+ buf[write_len +4+1]= (uchar) xid.bqual_length;
+ write_len+= 6;
+ 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+13, GTID_HEADER_LEN-13);
+ bzero(buf+write_len, GTID_HEADER_LEN-write_len);
write_len= GTID_HEADER_LEN;
}
return write_header(write_len) ||
@@ -3316,9 +3351,14 @@ 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+ ser_buf_size+5 /* sprintf */];
char *p;
- p = strmov(buf, (flags2 & FL_STANDALONE ? "GTID " : "BEGIN GTID "));
+ p = strmov(buf, (flags2 & FL_STANDALONE ? "GTID " :
+ flags2 & FL_PREPARED_XA ? "XA START " : "BEGIN GTID "));
+ if (flags2 & FL_PREPARED_XA)
+ {
+ p += sprintf(p, "%s GTID ", xid.serialize());
+ }
p= longlong10_to_str(domain_id, p, 10);
*p++= '-';
p= longlong10_to_str(server_id, p, 10);
@@ -3378,16 +3418,37 @@ Gtid_log_event::do_apply_event(rpl_group_info *rgi)
bits|= (ulonglong)OPTION_RPL_SKIP_PARALLEL;
thd->variables.option_bits= bits;
DBUG_PRINT("info", ("Set OPTION_GTID_BEGIN"));
- thd->set_query_and_id(gtid_begin_string, sizeof(gtid_begin_string)-1,
- &my_charset_bin, next_query_id());
- 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))
+
+ char buf_xa[sizeof("XA START") + 1 + ser_buf_size];
+ if (flags2 & FL_PREPARED_XA)
{
- DBUG_PRINT("error", ("trans_begin() failed"));
- thd->is_slave_error= 1;
+ const char fmt[]= "XA START %s";
+
+ thd->lex->xid= &xid;
+ thd->lex->xa_opt= XA_NONE;
+ sprintf(buf_xa, fmt, xid.serialize());
+ thd->set_query_and_id(buf_xa, static_cast<uint32>(strlen(buf_xa)),
+ &my_charset_bin, next_query_id());
+ thd->lex->sql_command= SQLCOM_XA_START;
+ if (trans_xa_start(thd))
+ {
+ DBUG_PRINT("error", ("trans_xa_start() failed"));
+ thd->is_slave_error= 1;
+ }
+ }
+ else
+ {
+ thd->set_query_and_id(gtid_begin_string, sizeof(gtid_begin_string)-1,
+ &my_charset_bin, next_query_id());
+ thd->lex->sql_command= SQLCOM_BEGIN;
+ if (trans_begin(thd, 0))
+ {
+ DBUG_PRINT("error", ("trans_begin() failed"));
+ thd->is_slave_error= 1;
+ }
}
+ status_var_increment(thd->status_var.com_stat[thd->lex->sql_command]);
thd->update_stats();
if (likely(!thd->is_slave_error))
@@ -3771,46 +3832,58 @@ bool slave_execute_deferred_events(THD *thd)
/**************************************************************************
- Xid_log_event methods
+ Xid_apply_log_event methods
**************************************************************************/
#if defined(HAVE_REPLICATION)
-void Xid_log_event::pack_info(Protocol *protocol)
+
+int Xid_apply_log_event::do_record_gtid(THD *thd, rpl_group_info *rgi,
+ bool in_trans, void **out_hton)
{
- char buf[128], *pos;
- pos= strmov(buf, "COMMIT /* xid=");
- pos= longlong10_to_str(xid, pos, 10);
- pos= strmov(pos, " */");
- protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
-}
-#endif
+ 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);
-bool Xid_log_event::write()
-{
- DBUG_EXECUTE_IF("do_not_write_xid", return 0;);
- return write_header(sizeof(xid)) ||
- write_data((uchar*)&xid, sizeof(xid)) ||
- write_footer();
-}
+ 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;
+}
-#if defined(HAVE_REPLICATION)
-int Xid_log_event::do_apply_event(rpl_group_info *rgi)
+int Xid_apply_log_event::do_apply_event(rpl_group_info *rgi)
{
bool res;
int err;
- rpl_gtid gtid;
uint64 sub_id= 0;
- Relay_log_info const *rli= rgi->rli;
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.
-
+ An instance of this class such as XID_EVENT works like a COMMIT
+ statement. It updates mysql.gtid_slave_pos 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.
+
+ XA_PREPARE_LOG_EVENT also updates the gtid table *but* the update gets
+ committed as separate "autocommit" transaction.
*/
thd->reset_for_next_command();
/*
@@ -3824,57 +3897,50 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
if (rgi->gtid_pending)
{
sub_id= rgi->gtid_sub_id;
- rgi->gtid_pending= false;
-
gtid= rgi->current_gtid;
- err= rpl_global_gtid_slave_state->record_gtid(thd, >id, sub_id, true,
- false, &hton);
- if (unlikely(err))
+
+ if (!thd->transaction.xid_state.is_explicit_XA())
{
- 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;
+ 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;
+ });
}
-
- 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 */");
+ general_log_print(thd, COM_QUERY, get_query());
thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
- res= trans_commit(thd); /* Automatically rolls back on error. */
- thd->mdl_context.release_transactional_locks();
+ res= do_commit();
+ if (!res && rgi->gtid_pending)
+ {
+ DBUG_ASSERT(!thd->transaction.xid_state.is_explicit_XA());
+ 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, >id, hton, rgi);
/*
Increment the global status commit count variable
*/
- status_var_increment(thd->status_var.com_stat[SQLCOM_COMMIT]);
+ enum enum_sql_command cmd= !thd->transaction.xid_state.is_explicit_XA() ?
+ SQLCOM_COMMIT : SQLCOM_XA_PREPARE;
+ status_var_increment(thd->status_var.com_stat[cmd]);
return res;
}
Log_event::enum_skip_reason
-Xid_log_event::do_shall_skip(rpl_group_info *rgi)
+Xid_apply_log_event::do_shall_skip(rpl_group_info *rgi)
{
- DBUG_ENTER("Xid_log_event::do_shall_skip");
+ 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));
@@ -3898,9 +3964,108 @@ Xid_log_event::do_shall_skip(rpl_group_info *rgi)
#endif
DBUG_RETURN(Log_event::do_shall_skip(rgi));
}
+#endif /* HAVE_REPLICATION */
+
+/**************************************************************************
+ Xid_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION)
+void Xid_log_event::pack_info(Protocol *protocol)
+{
+ char buf[128], *pos;
+ pos= strmov(buf, "COMMIT /* xid=");
+ pos= longlong10_to_str(xid, pos, 10);
+ pos= strmov(pos, " */");
+ protocol->store(buf, (uint) (pos-buf), &my_charset_bin);
+}
+
+
+int Xid_log_event::do_commit()
+{
+ bool res;
+ res= trans_commit(thd); /* Automatically rolls back on error. */
+ thd->mdl_context.release_transactional_locks();
+ return res;
+}
+#endif
+
+
+bool Xid_log_event::write()
+{
+ DBUG_EXECUTE_IF("do_not_write_xid", return 0;);
+ return write_header(sizeof(xid)) ||
+ write_data((uchar*)&xid, sizeof(xid)) ||
+ write_footer();
+}
+
+/**************************************************************************
+ XA_prepare_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION)
+void XA_prepare_log_event::pack_info(Protocol *protocol)
+{
+ char query[sizeof("XA COMMIT ONE PHASE") + 1 + ser_buf_size];
+
+ sprintf(query,
+ (one_phase ? "XA COMMIT %s ONE PHASE" : "XA PREPARE %s"),
+ m_xid.serialize());
+
+ protocol->store(query, strlen(query), &my_charset_bin);
+}
+
+
+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 ((res= thd->wait_for_prior_commit()))
+ return res;
+
+ thd->lex->sql_command= SQLCOM_XA_PREPARE;
+ res= trans_xa_prepare(thd);
+ }
+ else
+ {
+ res= trans_xa_commit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+
+ return res;
+}
#endif // HAVE_REPLICATION
+bool XA_prepare_log_event::write()
+{
+ uchar data[1 + 4 + 4 + 4]= {one_phase,};
+ uint8 one_phase_byte= 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_subheader_no_data == sizeof(data) - 1);
+
+ return write_header(sizeof(one_phase_byte) + xid_subheader_no_data +
+ 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();
+}
+
+
/**************************************************************************
User_var_log_event methods
**************************************************************************/
@@ -8303,7 +8468,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/rpl_parallel.cc b/sql/rpl_parallel.cc
index 4313840119e..fc6475a170b 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -27,6 +27,38 @@ struct rpl_parallel_thread_pool global_rpl_thread_pool;
static void signal_error_to_sql_driver_thread(THD *thd, rpl_group_info *rgi,
int err);
+struct XID_cache_insert_element
+{
+ XID *xid;
+ uint32 worker_idx;
+
+ XID_cache_insert_element(XID *xid_arg, uint32 idx_arg):
+ xid(xid_arg), worker_idx(idx_arg) {}
+};
+
+class XID_cache_element_para
+{
+public:
+ XID xid;
+ uint32 worker_idx;
+ Atomic_counter<int32_t> cnt; // of consecutive namesake xa:s queued for exec
+ static void lf_hash_initializer(LF_HASH *hash __attribute__((unused)),
+ XID_cache_element_para *element,
+ XID_cache_insert_element *new_element)
+ {
+ element->xid.set(new_element->xid);
+ element->worker_idx= new_element->worker_idx;
+ element->cnt= 1;
+ }
+ static uchar *key(const XID_cache_element_para *element, size_t *length,
+ my_bool)
+ {
+ *length= element->xid.key_length();
+ return element->xid.key();
+ }
+};
+
+
static int
rpt_handle_event(rpl_parallel_thread::queued_event *qev,
struct rpl_parallel_thread *rpt)
@@ -271,6 +303,33 @@ finish_event_group(rpl_parallel_thread *rpt, uint64 sub_id,
*/
thd->get_stmt_da()->reset_diagnostics_area();
wfc->wakeup_subsequent_commits(rgi->worker_error);
+
+ if (!rgi->current_xid.is_null())
+ {
+ Relay_log_info *rli= rgi->rli;
+ LF_PINS *pins= rli->parallel.rpl_xid_pins[rgi->xid_pins_idx];
+
+ DBUG_ASSERT(rgi->xid_pins_idx > 0 &&
+ rgi->xid_pins_idx <= opt_slave_parallel_threads);
+
+ XID_cache_element_para* el=
+ rli->parallel.xid_cache_search(&rgi->current_xid, pins);
+
+ if (el)
+ {
+ lf_hash_search_unpin(pins); // it's safe unpin now none but us can delete
+ if (el->cnt-- == 1) // comparison aganst old value
+ (void) rli->parallel.xid_cache_delete(el, pins);
+
+ DBUG_ASSERT(el->cnt >= 0);
+ }
+ else
+ {
+ // no record is begign when replication resumes after XA PREPARE.
+ sql_print_warning(ER_THD(rli->sql_driver_thd, ER_XAER_NOTA));
+ }
+ }
+ rgi->current_xid.null();
}
@@ -672,12 +731,14 @@ convert_kill_to_deadlock_error(rpl_group_info *rgi)
static int
is_group_ending(Log_event *ev, Log_event_type event_type)
{
- if (event_type == XID_EVENT)
+ if (event_type == XID_EVENT || event_type == XA_PREPARE_LOG_EVENT)
return 1;
if (event_type == QUERY_EVENT) // COMMIT/ROLLBACK are never compressed
{
Query_log_event *qev = (Query_log_event *)ev;
- if (qev->is_commit())
+ if (qev->is_commit() ||
+ !strncmp(qev->query, STRING_WITH_LEN("XA COMMIT")) ||
+ !strncmp(qev->query, STRING_WITH_LEN("XA ROLLBACK")))
return 1;
if (qev->is_rollback())
return 2;
@@ -1269,6 +1330,12 @@ handle_rpl_parallel_thread(void *arg)
slave_output_error_info(rgi, thd);
signal_error_to_sql_driver_thread(thd, rgi, 1);
}
+ if (static_cast<Gtid_log_event*>(qev->ev)->
+ flags2 & Gtid_log_event::FL_COMPLETED_XA)
+ {
+ rgi->current_xid.set(&static_cast<Gtid_log_event*>(qev->ev)->xid);
+ rgi->xid_pins_idx= static_cast<Gtid_log_event*>(qev->ev)->xid_pins_idx;
+ }
}
group_rgi= rgi;
@@ -2090,24 +2157,71 @@ rpl_parallel_thread_pool::release_thread(rpl_parallel_thread *rpt)
If the flag `reuse' is set, the last worker thread will be returned again,
if it is still available. Otherwise a new worker thread is allocated.
+
+ XA flagged as COMPLETED or PREPARED are handled as the following.
+
+ For the PREPARED one, a record consisting of the xid and a choosen worker's
+ descriptor is inserted into a local rli parallel xid hash.
+ The record also contains a usage counter field to account for
+ "duplicate" xid:s which may arise naturally as the result of the
+ same xid transaction multiple times execution on master. Each emerging
+ PREPARED xa increments the usage counter of the record keyed by xid.
+
+ For the COMPLETED xa, its xid is searched in the local hash for the
+ xa prepared worker. A found record's worker is reused, and when not
+ found (which may be benign) a new worker is allocated by the regular rule.
+
+ While the driver thread is responsible to insert a xid record into
+ the local hash and possibly increment its usage counter, a Worker assigned
+ for that xid decrements the counter at the end of xa's completion and deletes
+ the record when the counter drops to zero.
+ Pins associated with the Worker are passed to it through Gtid_log_event::pins
+ of the passed pointer.
*/
rpl_parallel_thread *
rpl_parallel_entry::choose_thread(rpl_group_info *rgi, bool *did_enter_cond,
- PSI_stage_info *old_stage, bool reuse)
+ PSI_stage_info *old_stage,
+ Gtid_log_event *gtid_ev)
{
uint32 idx;
Relay_log_info *rli= rgi->rli;
rpl_parallel_thread *thr;
+ bool reuse= gtid_ev == NULL;
idx= rpl_thread_idx;
+
if (!reuse)
{
+ if (gtid_ev->flags2 &
+ (Gtid_log_event::FL_COMPLETED_XA | Gtid_log_event::FL_PREPARED_XA))
+ {
+ LF_PINS *pins= rli->parallel.rpl_xid_pins[0];
+ XID_cache_element_para* el= rli->parallel.xid_cache_search(>id_ev->xid,
+ pins);
+
+ if (el)
+ {
+ idx= el->worker_idx;
+ lf_hash_search_unpin(pins);
+ goto idx_assigned;
+ }
+ else
+ {
+ // Further execution will clear out whether it's indeed the error case.
+ // XA completion event may arrive without the prepare one done so.
+ if (gtid_ev->flags2 & Gtid_log_event::FL_COMPLETED_XA)
+ sql_print_warning(ER_THD(rli->sql_driver_thd, ER_XAER_NOTA));
+ }
+ }
++idx;
if (idx >= rpl_thread_max)
idx= 0;
+
+idx_assigned:
rpl_thread_idx= idx;
}
thr= rpl_threads[idx];
+
if (thr)
{
*did_enter_cond= false;
@@ -2177,6 +2291,14 @@ rpl_parallel_entry::choose_thread(rpl_group_info *rgi, bool *did_enter_cond,
rpl_threads[idx]= thr= global_rpl_thread_pool.get_thread(&rpl_threads[idx],
this);
+ if (thr && gtid_ev)
+ {
+ if (gtid_ev->flags2 & Gtid_log_event::FL_PREPARED_XA)
+ (void) rli->parallel.xid_cache_replace(>id_ev->xid, idx);
+ else if (gtid_ev->flags2 & Gtid_log_event::FL_COMPLETED_XA)
+ gtid_ev->xid_pins_idx= idx + 1; // pass pins index to the assigned worker
+ }
+
return thr;
}
@@ -2205,12 +2327,21 @@ rpl_parallel::rpl_parallel() :
}
-void
-rpl_parallel::reset()
+bool
+rpl_parallel::reset(bool is_parallel)
{
my_hash_reset(&domain_hash);
current= NULL;
sql_thread_stopping= false;
+ if (is_parallel)
+ {
+ xid_cache_init();
+ rpl_xid_pins= new LF_PINS*[opt_slave_parallel_threads + 1];
+ for (ulong i= 0; i <= opt_slave_parallel_threads; i++)
+ if (!(rpl_xid_pins[i]= lf_hash_get_pins(&xid_cache_para)))
+ return true;
+ }
+ return false;
}
@@ -2473,6 +2604,76 @@ rpl_parallel::wait_for_workers_idle(THD *thd)
}
+void rpl_parallel::xid_cache_init()
+{
+ lf_hash_init(&xid_cache_para, sizeof(XID_cache_element_para),
+ LF_HASH_UNIQUE, 0, 0,
+ (my_hash_get_key) XID_cache_element_para::key, &my_charset_bin);
+ xid_cache_para.alloc.constructor= NULL;
+ xid_cache_para.alloc.destructor= NULL;
+ xid_cache_para.initializer=
+ (lf_hash_initializer) XID_cache_element_para::lf_hash_initializer;
+}
+
+
+void rpl_parallel::xid_cache_free()
+{
+ lf_hash_destroy(&xid_cache_para);
+}
+
+
+XID_cache_element_para* rpl_parallel::xid_cache_search(XID *xid, LF_PINS* pins)
+{
+ return (XID_cache_element_para*) lf_hash_search(&xid_cache_para, pins,
+ xid->key(),
+ xid->key_length());
+}
+
+/**
+ Insert a first xid-keyed record, or "replace" it with incremented
+ usage counter.
+*/
+void rpl_parallel::xid_cache_replace(XID *xid, uint32 idx)
+{
+ LF_PINS *pins= rpl_xid_pins[idx + 1];
+ XID_cache_element_para* el= xid_cache_search(xid, pins);
+
+ if (el)
+ {
+ if (unlikely(el->cnt++ == 0))
+ {
+ lf_hash_search_unpin(pins);
+ while (xid_cache_search(xid, pins)) // record must be at being deleted
+ (void) LF_BACKOFF();
+ lf_hash_search_unpin(pins);
+ (void) xid_cache_insert(xid, idx);
+ }
+
+ DBUG_ASSERT(el->cnt > 0);
+ }
+ else
+ {
+ (void) xid_cache_insert(xid, idx);
+ }
+ lf_hash_search_unpin(pins);
+}
+
+bool rpl_parallel::xid_cache_insert(XID *xid, uint32 idx)
+{
+ LF_PINS *pins= rpl_xid_pins[idx + 1];
+ XID_cache_insert_element new_element(xid, idx);
+
+ return lf_hash_insert(&xid_cache_para, pins, &new_element);
+}
+
+
+bool rpl_parallel::xid_cache_delete(XID_cache_element_para* el, LF_PINS *pins)
+{
+ return lf_hash_delete(&xid_cache_para, pins,
+ el->xid.key(), el->xid.key_length());
+}
+
+
/*
Handle seeing a GTID during slave restart in GTID mode. If we stopped with
different replication domains having reached different positions in the relay
@@ -2662,7 +2863,7 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
else
{
DBUG_ASSERT(rli->gtid_skip_flag == GTID_SKIP_TRANSACTION);
- if (typ == XID_EVENT ||
+ if (typ == XID_EVENT || typ == XA_PREPARE_LOG_EVENT ||
(typ == QUERY_EVENT && // COMMIT/ROLLBACK are never compressed
(((Query_log_event *)ev)->is_commit() ||
((Query_log_event *)ev)->is_rollback())))
@@ -2673,10 +2874,11 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
}
}
+ Gtid_log_event *gtid_ev= NULL;
if (typ == GTID_EVENT)
{
rpl_gtid gtid;
- Gtid_log_event *gtid_ev= static_cast<Gtid_log_event *>(ev);
+ gtid_ev= static_cast<Gtid_log_event *>(ev);
uint32 domain_id= (rli->mi->using_gtid == Master_info::USE_GTID_NO ||
rli->mi->parallel_mode <= SLAVE_PARALLEL_MINIMAL ?
0 : gtid_ev->domain_id);
@@ -2715,8 +2917,7 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
instead re-use a thread that we queued for previously.
*/
cur_thread=
- e->choose_thread(serial_rgi, &did_enter_cond, &old_stage,
- typ != GTID_EVENT);
+ e->choose_thread(serial_rgi, &did_enter_cond, &old_stage, gtid_ev);
if (!cur_thread)
{
/* This means we were killed. The error is already signalled. */
@@ -2734,7 +2935,6 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
if (typ == GTID_EVENT)
{
- Gtid_log_event *gtid_ev= static_cast<Gtid_log_event *>(ev);
bool new_gco;
enum_slave_parallel_mode mode= rli->mi->parallel_mode;
uchar gtid_flags= gtid_ev->flags2;
diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h
index 4579d0da9bc..b27bc63255e 100644
--- a/sql/rpl_parallel.h
+++ b/sql/rpl_parallel.h
@@ -2,7 +2,7 @@
#define RPL_PARALLEL_H
#include "log_event.h"
-
+#include "lf.h"
struct rpl_parallel;
struct rpl_parallel_entry;
@@ -345,10 +345,14 @@ struct rpl_parallel_entry {
group_commit_orderer *current_gco;
rpl_parallel_thread * choose_thread(rpl_group_info *rgi, bool *did_enter_cond,
- PSI_stage_info *old_stage, bool reuse);
+ PSI_stage_info *old_stage,
+ Gtid_log_event *gtid_ev);
int queue_master_restart(rpl_group_info *rgi,
Format_description_log_event *fdev);
};
+
+class XID_cache_element_para;
+
struct rpl_parallel {
HASH domain_hash;
rpl_parallel_entry *current;
@@ -356,13 +360,31 @@ struct rpl_parallel {
rpl_parallel();
~rpl_parallel();
- void reset();
+ bool reset(bool is_parallel);
rpl_parallel_entry *find(uint32 domain_id);
void wait_for_done(THD *thd, Relay_log_info *rli);
void stop_during_until();
bool workers_idle();
int wait_for_workers_idle(THD *thd);
int do_event(rpl_group_info *serial_rgi, Log_event *ev, ulonglong event_size);
+ void leave(THD *thd, Relay_log_info *rli)
+ {
+ wait_for_done(thd, rli);
+ for (ulong i= 0; i <= opt_slave_parallel_threads; i++)
+ lf_hash_put_pins(rpl_xid_pins[i]);
+ delete[] rpl_xid_pins;
+ xid_cache_free();
+ };
+ // XA related. API follows xa.h naming.
+ LF_HASH xid_cache_para;
+ LF_PINS **rpl_xid_pins;
+
+ void xid_cache_init();
+ void xid_cache_free();
+ bool xid_cache_insert(XID *xid, uint32 idx);
+ void xid_cache_replace(XID *xid, uint32 idx);
+ bool xid_cache_delete(XID_cache_element_para *el, LF_PINS *pins);
+ XID_cache_element_para *xid_cache_search(XID *xid, LF_PINS *pins);
};
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 6d55b06b497..ab035afb276 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -35,7 +35,7 @@
#include "sql_table.h"
static int count_relay_log_space(Relay_log_info* rli);
-
+bool xa_trans_force_rollback(THD *thd);
/**
Current replication state (hash of last GTID executed, per replication
domain).
@@ -2103,13 +2103,15 @@ rpl_group_info::reinit(Relay_log_info *rli)
rpl_group_info::rpl_group_info(Relay_log_info *rli)
: thd(0), wait_commit_sub_id(0),
wait_commit_group_info(0), parallel_entry(0),
- deferred_events(NULL), m_annotate_event(0), is_parallel_exec(false)
+ deferred_events(NULL), m_annotate_event(0), is_parallel_exec(false),
+ xid_pins_idx(0)
{
reinit(rli);
bzero(¤t_gtid, sizeof(current_gtid));
mysql_mutex_init(key_rpl_group_info_sleep_lock, &sleep_lock,
MY_MUTEX_INIT_FAST);
mysql_cond_init(key_rpl_group_info_sleep_cond, &sleep_cond, NULL);
+ current_xid.null();
}
@@ -2230,6 +2232,14 @@ void rpl_group_info::cleanup_context(THD *thd, bool error)
if (unlikely(error))
{
+ /*Todo/fixme: does it still not hold? Sort out and optimize if does not.
+ trans_rollback above does not rollback XA transactions.
+ It could be done only after necessarily closing tables which dictates
+ the following placement.
+ */
+ if (thd->transaction.xid_state.is_explicit_XA())
+ xa_trans_force_rollback(thd);
+
thd->mdl_context.release_transactional_locks();
if (thd == rli->sql_driver_thd)
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index 0e2e42fcb08..0b7d7f6a377 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -808,6 +808,10 @@ struct rpl_group_info
};
uchar killed_for_retry;
+ /* A store to remember xid of being completed XA */
+ XID current_xid;
+ uint32 xid_pins_idx; /* xid pins index to use by XA competing worker */
+
rpl_group_info(Relay_log_info *rli_);
~rpl_group_info();
void reinit(Relay_log_info *rli);
diff --git a/sql/slave.cc b/sql/slave.cc
index 87c1cf6cb77..4b3ad57fe41 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -4230,7 +4230,7 @@ inline void update_state_of_relay_log(Relay_log_info *rli, Log_event *ev)
rli->clear_flag(Relay_log_info::IN_TRANSACTION);
}
}
- if (typ == XID_EVENT)
+ if (typ == XID_EVENT || typ == XA_PREPARE_LOG_EVENT)
rli->clear_flag(Relay_log_info::IN_TRANSACTION);
if (typ == GTID_EVENT &&
!(((Gtid_log_event*) ev)->flags2 & Gtid_log_event::FL_STANDALONE))
@@ -5434,7 +5434,6 @@ pthread_handler_t handle_slave_sql(void *arg)
But the master timestamp is reset by RESET SLAVE & CHANGE MASTER.
*/
rli->clear_error();
- rli->parallel.reset();
//tell the I/O thread to take relay_log_space_limit into account from now on
rli->ignore_log_space_limit= 0;
@@ -5601,7 +5600,9 @@ pthread_handler_t handle_slave_sql(void *arg)
}
#endif /* WITH_WSREP */
/* Read queries from the IO/THREAD until this thread is killed */
-
+ if (rli->parallel.reset(mi->using_parallel()))
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
+ "Error initializing parallel mode");
thd->set_command(COM_SLAVE_SQL);
while (!sql_slave_killed(serial_rgi))
{
@@ -5663,7 +5664,7 @@ pthread_handler_t handle_slave_sql(void *arg)
err:
if (mi->using_parallel())
- rli->parallel.wait_for_done(thd, rli);
+ rli->parallel.leave(thd, rli);
/* Thread stopped. Print the current replication position to the log */
{
@@ -7095,6 +7096,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
buf[EVENT_TYPE_OFFSET])) ||
(!mi->last_queued_gtid_standalone &&
((uchar)buf[EVENT_TYPE_OFFSET] == XID_EVENT ||
+ (uchar)buf[EVENT_TYPE_OFFSET] == XA_PREPARE_LOG_EVENT ||
((uchar)buf[EVENT_TYPE_OFFSET] == QUERY_EVENT && /* QUERY_COMPRESSED_EVENT would never be commmit or rollback */
Query_log_event::peek_is_commit_rollback(buf, event_len,
checksum_alg))))))
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 5bfa29b72c4..fd7fa89f227 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -1651,7 +1651,7 @@ is_until_reached(binlog_send_info *info, ulong *ev_offset,
return false;
break;
case GTID_UNTIL_STOP_AFTER_TRANSACTION:
- if (event_type != XID_EVENT &&
+ if (event_type != XID_EVENT && event_type != XA_PREPARE_LOG_EVENT &&
(event_type != QUERY_EVENT || /* QUERY_COMPRESSED_EVENT would never be commmit or rollback */
!Query_log_event::peek_is_commit_rollback
(info->packet->ptr()+*ev_offset,
@@ -1886,7 +1886,7 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type,
info->gtid_skip_group= GTID_SKIP_NOT;
return NULL;
case GTID_SKIP_TRANSACTION:
- if (event_type == XID_EVENT ||
+ if (event_type == XID_EVENT || event_type == XA_PREPARE_LOG_EVENT ||
(event_type == QUERY_EVENT && /* QUERY_COMPRESSED_EVENT would never be commmit or rollback */
Query_log_event::peek_is_commit_rollback(packet->ptr() + ev_offset,
len - ev_offset,
diff --git a/sql/xa.cc b/sql/xa.cc
index e4cad40318e..da6c9c93157 100644
--- a/sql/xa.cc
+++ b/sql/xa.cc
@@ -20,13 +20,11 @@
#include "sql_class.h"
#include "transaction.h"
+static bool slave_applier_reset_xa_trans(THD *thd);
/***************************************************************************
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;
@@ -78,6 +76,7 @@ class XID_cache_element
uint rm_error;
enum xa_states xa_state;
XID xid;
+ bool binlogged;
bool is_set(int32_t flag)
{ return m_state.load(std::memory_order_relaxed) & flag; }
void set(int32_t flag)
@@ -131,6 +130,7 @@ class XID_cache_element
{
DBUG_ASSERT(!element->is_set(ACQUIRED | RECOVERED));
element->rm_error= 0;
+ element->binlogged= false;
element->xa_state= new_element->xa_state;
element->xid.set(new_element->xid);
new_element->xid_cache_element= element;
@@ -158,6 +158,29 @@ static LF_HASH xid_cache;
static bool xid_cache_inited;
+bool XID_STATE::is_binlogged()
+{
+ return is_explicit_XA() && xid_cache_element->binlogged;
+}
+
+
+void XID_STATE::set_binlogged()
+{
+ if (xid_cache_element)
+ xid_cache_element->binlogged= true;
+}
+
+
+void XID_STATE::unset_binlogged()
+{
+ if (xid_cache_element)
+ xid_cache_element->binlogged= false;
+}
+
+
+enum xa_states XID_STATE::get_state_code() { return xid_cache_element->xa_state; }
+
+
bool THD::fix_xid_hash_pins()
{
if (!xid_hash_pins)
@@ -267,6 +290,7 @@ bool xid_cache_insert(XID *xid)
{
case 0:
new_element.xid_cache_element->set(XID_cache_element::RECOVERED);
+ new_element.xid_cache_element->binlogged= true;
break;
case 1:
res= 0;
@@ -308,7 +332,11 @@ static void xid_cache_delete(THD *thd, XID_cache_element *&element)
void xid_cache_delete(THD *thd, XID_STATE *xid_state)
{
- DBUG_ASSERT(xid_state->is_explicit_XA());
+ DBUG_ASSERT(xid_state->is_explicit_XA() || thd->lex->xa_opt == XA_ONE_PHASE);
+
+ if (!xid_state->is_explicit_XA())
+ return;
+
xid_cache_delete(thd, xid_state->xid_cache_element);
xid_state->xid_cache_element= 0;
}
@@ -380,7 +408,7 @@ static bool xa_trans_rolled_back(XID_cache_element *element)
@return TRUE if the rollback failed, FALSE otherwise.
*/
-static bool xa_trans_force_rollback(THD *thd)
+bool xa_trans_force_rollback(THD *thd)
{
bool rc= false;
@@ -389,8 +417,8 @@ static bool xa_trans_force_rollback(THD *thd)
my_error(ER_XAER_RMERR, MYF(0));
rc= true;
}
-
- thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->variables.option_bits&=
+ ~(OPTION_BEGIN | OPTION_KEEP_LOG | OPTION_GTID_BEGIN);
thd->transaction.all.reset();
thd->server_status&=
~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
@@ -492,6 +520,8 @@ bool trans_xa_end(THD *thd)
bool trans_xa_prepare(THD *thd)
{
+ int res= 1;
+
DBUG_ENTER("trans_xa_prepare");
if (!thd->transaction.xid_state.is_explicit_XA() ||
@@ -499,16 +529,40 @@ bool trans_xa_prepare(THD *thd)
thd->transaction.xid_state.er_xaer_rmfail();
else if (!thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid))
my_error(ER_XAER_NOTA, MYF(0));
- else if (ha_prepare(thd))
+ else
{
- xid_cache_delete(thd, &thd->transaction.xid_state);
- my_error(ER_XA_RBROLLBACK, MYF(0));
+ /*
+ Acquire metadata lock which will ensure that COMMIT is blocked
+ by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
+ progress blocks FTWRL).
+
+ We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
+ */
+ MDL_request mdl_request;
+ mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+ MDL_STATEMENT);
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout) ||
+ ha_prepare(thd))
+ {
+ if (!mdl_request.ticket)
+ ha_rollback_trans(thd, TRUE);
+ thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
+ thd->transaction.all.reset();
+ thd->server_status&=
+ ~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
+ xid_cache_delete(thd, &thd->transaction.xid_state);
+ my_error(ER_XA_RBROLLBACK, MYF(0));
+ }
+ else
+ {
+ thd->transaction.xid_state.xid_cache_element->xa_state= XA_PREPARED;
+ res= thd->variables.pseudo_slave_mode || thd->slave_thread ?
+ slave_applier_reset_xa_trans(thd) : 0;
+ }
}
- else
- thd->transaction.xid_state.xid_cache_element->xa_state= XA_PREPARED;
- DBUG_RETURN(thd->is_error() ||
- thd->transaction.xid_state.xid_cache_element->xa_state != XA_PREPARED);
+ DBUG_RETURN(res);
}
@@ -523,11 +577,13 @@ bool trans_xa_prepare(THD *thd)
bool trans_xa_commit(THD *thd)
{
- bool res= TRUE;
+ bool res= true;
+ XID_STATE &xid_state= thd->transaction.xid_state;
+
DBUG_ENTER("trans_xa_commit");
- if (!thd->transaction.xid_state.is_explicit_XA() ||
- !thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid))
+ if (!xid_state.is_explicit_XA() ||
+ !xid_state.xid_cache_element->xid.eq(thd->lex->xid))
{
if (thd->in_multi_stmt_transaction_mode() || thd->lex->xa_opt != XA_NONE)
{
@@ -543,7 +599,45 @@ bool trans_xa_commit(THD *thd)
if (auto xs= xid_cache_search(thd, thd->lex->xid))
{
res= xa_trans_rolled_back(xs);
+ /*
+ Acquire metadata lock which will ensure that COMMIT is blocked
+ by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
+ progress blocks FTWRL).
+
+ We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
+ */
+ MDL_request mdl_request;
+ mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+ MDL_STATEMENT);
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ {
+ /*
+ We can't rollback an XA transaction on lock failure due to
+ Innodb redo log and bin log update is involved in rollback.
+ Return error to user for a retry.
+ */
+ DBUG_ASSERT(thd->is_error());
+
+ xs->acquired_to_recovered();
+ DBUG_RETURN(true);
+ }
+ DBUG_ASSERT(!xid_state.xid_cache_element);
+
+ DEBUG_SYNC(thd, "at_trans_xa_commit");
+ if (thd->wait_for_prior_commit())
+ {
+ DBUG_ASSERT(thd->is_error());
+
+ xs->acquired_to_recovered();
+ DBUG_RETURN(true);
+ }
+
+ xid_state.xid_cache_element= xs;
ha_commit_or_rollback_by_xid(thd->lex->xid, !res);
+ xid_state.xid_cache_element= 0;
+
+ res= res || thd->is_error();
xid_cache_delete(thd, xs);
}
else
@@ -551,19 +645,20 @@ bool trans_xa_commit(THD *thd)
DBUG_RETURN(res);
}
- if (xa_trans_rolled_back(thd->transaction.xid_state.xid_cache_element))
+ if (xa_trans_rolled_back(xid_state.xid_cache_element))
{
xa_trans_force_rollback(thd);
DBUG_RETURN(thd->is_error());
}
- else if (thd->transaction.xid_state.xid_cache_element->xa_state == XA_IDLE &&
+ else if (xid_state.xid_cache_element->xa_state == XA_IDLE &&
thd->lex->xa_opt == XA_ONE_PHASE)
{
+ xid_cache_delete(thd, &xid_state);
int r= ha_commit_trans(thd, TRUE);
if ((res= MY_TEST(r)))
my_error(r == 1 ? ER_XA_RBROLLBACK : ER_XAER_RMERR, MYF(0));
}
- else if (thd->transaction.xid_state.xid_cache_element->xa_state == XA_PREPARED &&
+ else if (xid_state.xid_cache_element->xa_state == XA_PREPARED &&
thd->lex->xa_opt == XA_NONE)
{
MDL_request mdl_request;
@@ -576,26 +671,30 @@ bool trans_xa_commit(THD *thd)
We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
*/
mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
- MDL_TRANSACTION);
+ MDL_STATEMENT);
if (thd->mdl_context.acquire_lock(&mdl_request,
thd->variables.lock_wait_timeout))
{
- ha_rollback_trans(thd, TRUE);
+ /*
+ We can't rollback an XA transaction on lock failure due to
+ Innodb redo log and bin log update is involved in rollback.
+ Return error to user for a retry.
+ */
my_error(ER_XAER_RMERR, MYF(0));
+ DBUG_RETURN(true);
}
else
{
DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
- res= MY_TEST(ha_commit_one_phase(thd, 1));
- if (res)
+ if ((res= MY_TEST(ha_commit_one_phase(thd, 1))))
my_error(ER_XAER_RMERR, MYF(0));
}
}
else
{
- thd->transaction.xid_state.er_xaer_rmfail();
+ xid_state.er_xaer_rmfail();
DBUG_RETURN(TRUE);
}
@@ -604,7 +703,7 @@ bool trans_xa_commit(THD *thd)
thd->server_status&=
~(SERVER_STATUS_IN_TRANS | SERVER_STATUS_IN_TRANS_READONLY);
DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS"));
- xid_cache_delete(thd, &thd->transaction.xid_state);
+ xid_cache_delete(thd, &xid_state);
trans_track_end_trx(thd);
@@ -623,10 +722,13 @@ bool trans_xa_commit(THD *thd)
bool trans_xa_rollback(THD *thd)
{
+ bool res= false;
+ XID_STATE &xid_state= thd->transaction.xid_state;
+
DBUG_ENTER("trans_xa_rollback");
- if (!thd->transaction.xid_state.is_explicit_XA() ||
- !thd->transaction.xid_state.xid_cache_element->xid.eq(thd->lex->xid))
+ if (!xid_state.is_explicit_XA() ||
+ !xid_state.xid_cache_element->xid.eq(thd->lex->xid))
{
if (thd->in_multi_stmt_transaction_mode())
{
@@ -641,8 +743,36 @@ bool trans_xa_rollback(THD *thd)
if (auto xs= xid_cache_search(thd, thd->lex->xid))
{
+ MDL_request mdl_request;
+ mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+ MDL_STATEMENT);
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ {
+ /*
+ We can't rollback an XA transaction on lock failure due to
+ Innodb redo log and bin log update is involved in rollback.
+ Return error to user for a retry.
+ */
+ DBUG_ASSERT(thd->is_error());
+
+ xs->acquired_to_recovered();
+ DBUG_RETURN(true);
+ }
xa_trans_rolled_back(xs);
+ DBUG_ASSERT(!xid_state.xid_cache_element);
+
+ DEBUG_SYNC(thd, "at_trans_xa_rollback");
+ if (thd->wait_for_prior_commit())
+ {
+ DBUG_ASSERT(thd->is_error());
+ xs->acquired_to_recovered();
+ DBUG_RETURN(true);
+ }
+
+ xid_state.xid_cache_element= xs;
ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
+ xid_state.xid_cache_element= 0;
xid_cache_delete(thd, xs);
}
else
@@ -650,21 +780,35 @@ bool trans_xa_rollback(THD *thd)
DBUG_RETURN(thd->get_stmt_da()->is_error());
}
- if (thd->transaction.xid_state.xid_cache_element->xa_state == XA_ACTIVE)
+ if (xid_state.xid_cache_element->xa_state == XA_ACTIVE)
{
- thd->transaction.xid_state.er_xaer_rmfail();
+ xid_state.er_xaer_rmfail();
DBUG_RETURN(TRUE);
}
- DBUG_RETURN(xa_trans_force_rollback(thd));
+
+ MDL_request mdl_request;
+ mdl_request.init(MDL_key::BACKUP, "", "", MDL_BACKUP_COMMIT,
+ MDL_STATEMENT);
+ if (thd->mdl_context.acquire_lock(&mdl_request,
+ thd->variables.lock_wait_timeout))
+ {
+ /*
+ We can't rollback an XA transaction on lock failure due to
+ Innodb redo log and bin log update is involved in rollback.
+ Return error to user for a retry.
+ */
+ my_error(ER_XAER_RMERR, MYF(0));
+ DBUG_RETURN(true);
+ }
+
+ DBUG_RETURN(res != 0 || xa_trans_force_rollback(thd));
}
bool trans_xa_detach(THD *thd)
{
DBUG_ASSERT(thd->transaction.xid_state.is_explicit_XA());
-#if 1
- return xa_trans_force_rollback(thd);
-#else
+
if (thd->transaction.xid_state.xid_cache_element->xa_state != XA_PREPARED)
return xa_trans_force_rollback(thd);
thd->transaction.xid_state.xid_cache_element->acquired_to_recovered();
@@ -683,7 +827,6 @@ bool trans_xa_detach(THD *thd)
thd->transaction.all.ha_list= 0;
thd->transaction.all.no_2pc= 0;
return false;
-#endif
}
@@ -877,3 +1020,44 @@ bool mysql_xa_recover(THD *thd)
my_eof(thd);
DBUG_RETURN(0);
}
+
+
+/**
+ This is a specific to (pseudo-) slave applier collection of standard cleanup
+ actions to reset XA transaction state sim to @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()
+*/
+
+static bool slave_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"));
+
+ thd->transaction.xid_state.xid_cache_element->acquired_to_recovered();
+ thd->transaction.xid_state.xid_cache_element= 0;
+
+ for (Ha_trx_info *ha_info= thd->transaction.all.ha_list, *ha_info_next;
+ ha_info; ha_info= ha_info_next)
+ {
+ ha_info_next= ha_info->next();
+ ha_info->reset();
+ }
+ thd->transaction.all.ha_list= 0;
+
+ ha_close_connection(thd);
+ thd->transaction.cleanup();
+ thd->transaction.all.reset();
+
+ DBUG_ASSERT(!thd->transaction.all.ha_list);
+ DBUG_ASSERT(!thd->transaction.all.no_2pc);
+
+ thd->has_waiter= false;
+
+ return thd->is_error();
+}
diff --git a/sql/xa.h b/sql/xa.h
index 7cf74efad35..507d07f638f 100644
--- a/sql/xa.h
+++ b/sql/xa.h
@@ -1,3 +1,5 @@
+#ifndef XA_INCLUDED
+#define XA_INCLUDED
/*
Copyright (c) 2000, 2016, Oracle and/or its affiliates.
Copyright (c) 2009, 2019, MariaDB Corporation.
@@ -16,17 +18,31 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
-
class XID_cache_element;
+enum xa_states { XA_ACTIVE= 0, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY };
struct XID_STATE {
XID_cache_element *xid_cache_element;
- bool check_has_uncommitted_xa() const;
bool is_explicit_XA() const { return xid_cache_element != 0; }
+ /*
+ Binary logging status of explicit "user" XA.
+ It is set to TRUE at XA PREPARE if the transaction was written
+ to the binlog.
+ It may be FALSE after preparing when the transaction does not modify
+ transactional tables or binlogging is turned off.
+ In that case a consequent XA COMMIT/ROLLBACK shouldn't be binlogged.
+
+ The recovered transaction after server restart sets it to TRUE always.
+ */
+ bool is_binlogged();
+ bool check_has_uncommitted_xa() const;
void set_error(uint error);
void er_xaer_rmfail() const;
XID *get_xid() const;
+ void set_binlogged();
+ void unset_binlogged();
+ enum xa_states get_state_code();
};
void xid_cache_init(void);
@@ -42,3 +58,5 @@ bool trans_xa_commit(THD *thd);
bool trans_xa_rollback(THD *thd);
bool trans_xa_detach(THD *thd);
bool mysql_xa_recover(THD *thd);
+
+#endif /* XA_INCLUDED */
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index a36cc87a201..8a173001ed5 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -4844,6 +4844,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 c5e48ff65ab..1004198ea0b 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -549,8 +549,10 @@ void trx_disconnect_prepared(trx_t *trx)
trx->read_view.close();
trx->is_recovered= true;
trx->mysql_thd= NULL;
+ trx->mysql_log_file_name = 0;
/* todo/fixme: suggest to do it at innodb prepare */
trx->will_lock= 0;
+ trx_sys.rw_trx_hash.put_pins(trx);
}
/****************************************************************//**
@@ -1390,8 +1392,21 @@ trx_commit_in_memory(
trx->release_locks();
}
- 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::free_connection -> ...
+ 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) {
MONITOR_INC(MONITOR_TRX_RO_COMMIT);
} else {
diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc
index 8488f9ee963..272d2800319 100644
--- a/storage/rocksdb/ha_rocksdb.cc
+++ b/storage/rocksdb/ha_rocksdb.cc
@@ -3121,6 +3121,8 @@ class Rdb_transaction {
s_tx_list.erase(this);
RDB_MUTEX_UNLOCK_CHECK(s_tx_list_mutex);
}
+ virtual bool is_prepared() { return false; };
+ virtual void detach_prepared_tx() {};
};
/*
@@ -3157,7 +3159,16 @@ class Rdb_transaction_impl : public Rdb_transaction {
virtual bool is_writebatch_trx() const override { return false; }
- private:
+ bool is_prepared() {
+ return m_rocksdb_tx && rocksdb::Transaction::PREPARED == m_rocksdb_tx->GetState();
+ }
+
+ void detach_prepared_tx() {
+ DBUG_ASSERT(rocksdb::Transaction::PREPARED == m_rocksdb_tx->GetState());
+ m_rocksdb_tx = nullptr;
+ }
+
+private:
void release_tx(void) {
// We are done with the current active transaction object. Preserve it
// for later reuse.
@@ -3798,7 +3809,8 @@ static int rocksdb_close_connection(handlerton *const hton, THD *const thd) {
"disconnecting",
rc);
}
-
+ if (tx->is_prepared())
+ tx->detach_prepared_tx();
delete tx;
}
return HA_EXIT_SUCCESS;
@@ -5301,7 +5313,7 @@ static int rocksdb_init_func(void *const p) {
#ifdef MARIAROCKS_NOT_YET
rocksdb_hton->update_table_stats = rocksdb_update_table_stats;
#endif // MARIAROCKS_NOT_YET
-
+
/*
Not needed in MariaDB:
rocksdb_hton->flush_logs = rocksdb_flush_wal;
diff --git a/storage/rocksdb/mysql-test/rocksdb/r/xa.result b/storage/rocksdb/mysql-test/rocksdb/r/xa.result
index 12ae2b474b6..8cb6f39bbac 100644
--- a/storage/rocksdb/mysql-test/rocksdb/r/xa.result
+++ b/storage/rocksdb/mysql-test/rocksdb/r/xa.result
@@ -1,6 +1,7 @@
-#
-# MDEV-13155: XA recovery not supported for RocksDB (Just a testcase)
#
+# MDEV-742 fixes
+# MDEV-13155: XA recovery not supported for RocksDB
+# as well.
call mtr.add_suppression("Found .* prepared XA transactions");
connect con1,localhost,root,,test;
DROP TABLE IF EXISTS t1;
@@ -15,19 +16,55 @@ INSERT INTO t1 (a) VALUES (3);
INSERT INTO t1 (a) VALUES (4);
XA END 'xa2';
XA PREPARE 'xa2';
+connect con3,localhost,root,,test;
+XA START 'xa3';
+INSERT INTO t1 (a) VALUES (5);
+INSERT INTO t1 (a) VALUES (6);
+XA END 'xa3';
+XA PREPARE 'xa3';
+disconnect con3;
connection default;
SELECT * FROM t1;
a
+Must be all three XA:s in
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 3 0 xa3
+1 3 0 xa1
+1 3 0 xa2
# restart
connect con3,localhost,root,,test;
XA RECOVER;
formatID gtrid_length bqual_length data
+1 3 0 xa3
1 3 0 xa1
1 3 0 xa2
XA ROLLBACK 'xa1';
XA COMMIT 'xa2';
+XA ROLLBACK 'xa3';
+SELECT a FROM t1;
+a
+3
+4
+connect con4,localhost,root,,test;
+XA START 'xa4';
+INSERT INTO t1 (a) VALUES (7);
+INSERT INTO t1 (a) VALUES (8);
+XA END 'xa4';
+XA PREPARE 'xa4';
+connection default;
+# Now restart through graceful shutdown
+# restart
+connect con5,localhost,root,,test;
+Must have 'xa4'
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 3 0 xa4
+XA COMMIT 'xa4';
SELECT a FROM t1;
a
3
4
+7
+8
DROP TABLE t1;
diff --git a/storage/rocksdb/mysql-test/rocksdb/t/xa.test b/storage/rocksdb/mysql-test/rocksdb/t/xa.test
index f8f381f0580..0c23e71df8c 100644
--- a/storage/rocksdb/mysql-test/rocksdb/t/xa.test
+++ b/storage/rocksdb/mysql-test/rocksdb/t/xa.test
@@ -1,6 +1,7 @@
---echo #
---echo # MDEV-13155: XA recovery not supported for RocksDB (Just a testcase)
--echo #
+--echo # MDEV-742 fixes
+--echo # MDEV-13155: XA recovery not supported for RocksDB
+--echo # as well.
call mtr.add_suppression("Found .* prepared XA transactions");
@@ -22,17 +23,51 @@ INSERT INTO t1 (a) VALUES (3);
INSERT INTO t1 (a) VALUES (4);
XA END 'xa2';
XA PREPARE 'xa2';
-
+
+--connect (con3,localhost,root,,test)
+XA START 'xa3';
+INSERT INTO t1 (a) VALUES (5);
+INSERT INTO t1 (a) VALUES (6);
+XA END 'xa3';
+XA PREPARE 'xa3';
+--disconnect con3
+
--connection default
SELECT * FROM t1;
+--echo Must be all three XA:s in
+XA RECOVER;
+
--let $shutdown_timeout= 0
--source include/restart_mysqld.inc
--connect (con3,localhost,root,,test)
--disable_abort_on_error
-XA RECOVER;
+XA RECOVER; # like above
XA ROLLBACK 'xa1';
XA COMMIT 'xa2';
+XA ROLLBACK 'xa3';
SELECT a FROM t1;
+
+--connect (con4,localhost,root,,test)
+XA START 'xa4';
+INSERT INTO t1 (a) VALUES (7);
+INSERT INTO t1 (a) VALUES (8);
+XA END 'xa4';
+XA PREPARE 'xa4';
+
+--connection default
+--echo # Now restart through graceful shutdown
+--source include/restart_mysqld.inc
+
+
+--connect (con5,localhost,root,,test)
+--disable_abort_on_error
+
+--echo Must have 'xa4'
+XA RECOVER;
+XA COMMIT 'xa4';
+
+SELECT a FROM t1;
+
DROP TABLE t1;
diff --git a/storage/rocksdb/mysql-test/rocksdb_rpl/r/rpl_xa.result b/storage/rocksdb/mysql-test/rocksdb_rpl/r/rpl_xa.result
new file mode 100644
index 00000000000..b4713c68390
--- /dev/null
+++ b/storage/rocksdb/mysql-test/rocksdb_rpl/r/rpl_xa.result
@@ -0,0 +1,50 @@
+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';
+include/save_master_gtid.inc
+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/storage/rocksdb/mysql-test/rocksdb_rpl/t/rpl_xa.inc b/storage/rocksdb/mysql-test/rocksdb_rpl/t/rpl_xa.inc
new file mode 100644
index 00000000000..c1300c1e27a
--- /dev/null
+++ b/storage/rocksdb/mysql-test/rocksdb_rpl/t/rpl_xa.inc
@@ -0,0 +1,70 @@
+#
+# 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';
+--source include/save_master_gtid.inc
+
+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/storage/rocksdb/mysql-test/rocksdb_rpl/t/rpl_xa.test b/storage/rocksdb/mysql-test/rocksdb_rpl/t/rpl_xa.test
new file mode 100644
index 00000000000..7d667aa96d2
--- /dev/null
+++ b/storage/rocksdb/mysql-test/rocksdb_rpl/t/rpl_xa.test
@@ -0,0 +1,6 @@
+source include/have_rocksdb.inc;
+source include/master-slave.inc;
+source include/have_binlog_format_row.inc;
+
+source rpl_xa.inc;
+source include/rpl_end.inc;
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..a6be07963f5 100644
--- a/storage/tokudb/mysql-test/tokudb_mariadb/t/xa.test
+++ b/storage/tokudb/mysql-test/tokudb_mariadb/t/xa.test
@@ -68,6 +68,9 @@ xa start 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz';
select * from t1;
disconnect con1;
+xa recover;
+
connection default;
+xa rollback 'testb',0x2030405060,11;
drop table t1;
1
0
revision-id: cd9571467dd00f97ecbbfa95859886066e9cffe2 (mariadb-10.5.0-245-gcd9571467dd)
parent(s): 4042bb891cbb759b519802bfdc6b3867b2eff5c2
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2020-03-04 15:16:51 +0300
message:
XPand SE: Attempt to get packages built
---
storage/xpand/CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/storage/xpand/CMakeLists.txt b/storage/xpand/CMakeLists.txt
index 78fe16c0525..e975411532a 100644
--- a/storage/xpand/CMakeLists.txt
+++ b/storage/xpand/CMakeLists.txt
@@ -12,7 +12,7 @@ ENDIF()
SET(XPAND_PLUGIN_STATIC "xpand")
SET(XPAND_PLUGIN_DYNAMIC "ha_xpand")
SET(XPAND_SOURCES ha_xpand.cc xpand_connection.cc ha_xpand_pushdown.cc)
-MYSQL_ADD_PLUGIN(xpand ${XPAND_SOURCES} STORAGE_ENGINE)
+MYSQL_ADD_PLUGIN(xpand ${XPAND_SOURCES} STORAGE_ENGINE COMPONENT xpand-engine)
IF(MSVC)
IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
1
0
[Commits] 4042bb891cb: XPand: fix compile error on Winx64: pass ushort to add_command_operand_ushort
by psergey 04 Mar '20
by psergey 04 Mar '20
04 Mar '20
revision-id: 4042bb891cbb759b519802bfdc6b3867b2eff5c2 (mariadb-10.5.0-244-g4042bb891cb)
parent(s): 3b7b3843eb437544735a10bf4a03929430e69dea
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2020-03-04 14:26:12 +0300
message:
XPand: fix compile error on Winx64: pass ushort to add_command_operand_ushort
Also add a safety check
---
storage/xpand/xpand_connection.cc | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/storage/xpand/xpand_connection.cc b/storage/xpand/xpand_connection.cc
index d8425e603e4..28e545fff6d 100644
--- a/storage/xpand/xpand_connection.cc
+++ b/storage/xpand/xpand_connection.cc
@@ -899,7 +899,11 @@ int xpand_connection::scan_next(xpand_connection_cursor *scan,
if ((error_code = begin_command(XPAND_SCAN_NEXT)))
return error_code;
- if ((error_code = add_command_operand_ushort(scan->buffer_size)))
+ // This should not happen as @@xpand_row_buffer has this limit.
+ if (scan->buffer_size > 65535)
+ return HA_ERR_INTERNAL_ERROR;
+
+ if ((error_code = add_command_operand_ushort((ushort)scan->buffer_size)))
return error_code;
if ((error_code = add_command_operand_lcb(scan->scan_refid)))
1
0