revision-id: 4b71faecf7add3af87ceb84bd738716d4954e828 (mariadb-10.2.22-128-g4b71faecf7a)
parent(s): 5633f83ca42ac6f035cf2c18ae11b3b7639b1f7e
author: Varun Gupta
committer: Varun Gupta
timestamp: 2019-04-03 00:10:45 +0530
message:
MDEV-19129: Build Failure on OSX
Fixing part #1, added flag Wno-error=deprecated-declarations to convert errors to warnings, fixed rest of the warnings
---
cmake/maintainer.cmake | 4 ++++
include/my_global.h | 2 +-
mysys/my_sync.c | 2 ++
plugin/server_audit/server_audit.c | 2 ++
sql/sql_parse.cc | 3 ++-
storage/connect/ha_connect.cc | 7 ++++---
storage/rocksdb/ha_rocksdb.cc | 1 -
storage/spider/spd_table.cc | 2 +-
8 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/cmake/maintainer.cmake b/cmake/maintainer.cmake
index caba370c4ae..f058f0861de 100644
--- a/cmake/maintainer.cmake
+++ b/cmake/maintainer.cmake
@@ -35,6 +35,10 @@ IF(CMAKE_COMPILER_IS_GNUCC AND CMAKE_C_COMPILER_VERSION VERSION_LESS "6.0.0")
SET(MY_WARNING_FLAGS ${MY_WARNING_FLAGS} -Wno-error=maybe-uninitialized)
ENDIF()
+IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "10.0.0")
+ SET(MY_WARNING_FLAGS ${MY_WARNING_FLAGS} -Wno-error=deprecated-declarations)
+ENDIF()
+
IF(MYSQL_MAINTAINER_MODE MATCHES "OFF")
RETURN()
ELSEIF(MYSQL_MAINTAINER_MODE MATCHES "AUTO")
diff --git a/include/my_global.h b/include/my_global.h
index 719464933b8..c56105199dc 100644
--- a/include/my_global.h
+++ b/include/my_global.h
@@ -535,7 +535,7 @@ C_MODE_START
#ifdef HAVE_SIGHANDLER_T
#define sig_return sighandler_t
#else
-typedef void (*sig_return)(void); /* Returns type from signal */
+typedef void (*sig_return)(int); /* Returns type from signal */
#endif
C_MODE_END
#if defined(__GNUC__) && !defined(_lint)
diff --git a/mysys/my_sync.c b/mysys/my_sync.c
index d1e239692f1..61a5fa170a5 100644
--- a/mysys/my_sync.c
+++ b/mysys/my_sync.c
@@ -133,7 +133,9 @@ int my_sync(File fd, myf my_flags)
} /* my_sync */
+#ifdef NEED_EXPLICIT_SYNC_DIR
static const char cur_dir_name[]= {FN_CURLIB, 0};
+#endif
/*
Force directory information to disk.
diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c
index 0995327a86d..0d25d8bec06 100644
--- a/plugin/server_audit/server_audit.c
+++ b/plugin/server_audit/server_audit.c
@@ -2130,6 +2130,7 @@ struct mysql_event_general_v8
static void auditing_v8(MYSQL_THD thd, struct mysql_event_general_v8 *ev_v8)
{
+#ifdef __linux__
#ifdef DBUG_OFF
#ifdef __x86_64__
static const int cmd_off= 4200;
@@ -2151,6 +2152,7 @@ static void auditing_v8(MYSQL_THD thd, struct mysql_event_general_v8 *ev_v8)
static const int db_len_off= 68;
#endif /*x86_64*/
#endif /*DBUG_OFF*/
+#endif
struct mysql_event_general event;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index a80bf004916..57951a3d031 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1296,7 +1296,8 @@ bool do_command(THD *thd)
mysql_mutex_lock(&thd->LOCK_thd_data);
if (thd->wsrep_conflict_state == MUST_ABORT)
{
- DBUG_PRINT("wsrep",("aborted for wsrep rollback: %lu", thd->real_id));
+ DBUG_PRINT("wsrep",("aborted for wsrep rollback: %lu",
+ (unsigned long)thd->real_id));
wsrep_client_rollback(thd);
}
mysql_mutex_unlock(&thd->LOCK_thd_data);
diff --git a/storage/connect/ha_connect.cc b/storage/connect/ha_connect.cc
index 859dc859a09..6724eda4521 100644
--- a/storage/connect/ha_connect.cc
+++ b/storage/connect/ha_connect.cc
@@ -1317,7 +1317,7 @@ char *ha_connect::GetRealString(PCSZ s)
{
char *sv;
- if (IsPartitioned() && s && partname && *partname) {
+ if (IsPartitioned() && s && *partname) {
sv= (char*)PlugSubAlloc(xp->g, NULL, 0);
sprintf(sv, s, partname);
PlugSubAlloc(xp->g, NULL, strlen(sv) + 1);
@@ -3061,6 +3061,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
const char *p;
char *s = (ishav) ? havg : body;
uint j, k, n;
+ const char *str= "1970-01-01 00:00:00";
// Append the value to the filter
switch (args[i]->field_type()) {
@@ -3071,7 +3072,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
strncat(s, res->ptr(), res->length());
if (res->length() < 19)
- strcat(s, "1970-01-01 00:00:00" + res->length());
+ strcat(s, str + res->length());
strcat(s, "'}");
break;
@@ -3101,7 +3102,7 @@ PCFIL ha_connect::CheckCond(PGLOBAL g, PCFIL filp, const Item *cond)
strncat(s, res->ptr(), res->length());
if (res->length() < 19)
- strcat(s, "1970-01-01 00:00:00" + res->length());
+ strcat(s, str + res->length());
strcat(s, "'}");
break;
diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc
index 022511320a6..8364dd118a7 100644
--- a/storage/rocksdb/ha_rocksdb.cc
+++ b/storage/rocksdb/ha_rocksdb.cc
@@ -268,7 +268,6 @@ static void rocksdb_flush_all_memtables() {
namespace // anonymous namespace = not visible outside this source file
{
-const ulong TABLE_HASH_SIZE = 32;
typedef Hash_set<Rdb_table_handler> Rdb_table_set;
struct Rdb_open_tables_map {
diff --git a/storage/spider/spd_table.cc b/storage/spider/spd_table.cc
index b4d69af12c4..70edc8c7c09 100644
--- a/storage/spider/spd_table.cc
+++ b/storage/spider/spd_table.cc
@@ -6308,7 +6308,7 @@ int spider_panic(
int spider_db_init(
void *p
) {
- int error_num, roop_count;
+ int error_num= 0, roop_count;
uint dbton_id = 0;
handlerton *spider_hton = (handlerton *)p;
DBUG_ENTER("spider_db_init");
1
0
[Commits] 11fa88ef67b: MDEV-17362: SIGSEGV in JOIN::optimize_inner or Assertion `fixed == 0' failed in Item_equal::fix_fields, server crashes after 2nd execution of PS
by Oleksandr Byelkin 02 Apr '19
by Oleksandr Byelkin 02 Apr '19
02 Apr '19
revision-id: 11fa88ef67bea74bd13ad688938a03d5b266db64 (mariadb-10.4.3-107-g11fa88ef67b)
parent(s): a90622644c11568c2bf982492d2f47bc164ddfef
author: Oleksandr Byelkin
committer: Oleksandr Byelkin
timestamp: 2019-04-02 15:04:45 +0200
message:
MDEV-17362: SIGSEGV in JOIN::optimize_inner or Assertion `fixed == 0' failed in Item_equal::fix_fields, server crashes after 2nd execution of PS
Move reinitialisation of pushdown variables for every query, because it used now not only for derived tables.
---
mysql-test/main/subselect_innodb.result | 20 ++++++++++++++++++++
mysql-test/main/subselect_innodb.test | 24 ++++++++++++++++++++++++
sql/sql_derived.cc | 5 -----
sql/sql_prepare.cc | 4 ++++
sql/sql_select.cc | 6 ++++--
5 files changed, 52 insertions(+), 7 deletions(-)
diff --git a/mysql-test/main/subselect_innodb.result b/mysql-test/main/subselect_innodb.result
index 0eb40c9be00..518158e3a04 100644
--- a/mysql-test/main/subselect_innodb.result
+++ b/mysql-test/main/subselect_innodb.result
@@ -616,3 +616,23 @@ id select_type table type possible_keys key key_len ref rows filtered Extra
Warnings:
Note 1003 select `test`.`t1`.`f1` AS `f1`,`test`.`t2`.`f2` AS `f2`,`test`.`t3`.`f3` AS `f3` from `test`.`t1` join `test`.`t2` semi join (`test`.`t4`) join `test`.`t3` where `test`.`t4`.`f4` = 1 and `test`.`t1`.`f1` >= `test`.`t2`.`f2`
DROP TABLE t1,t2,t3,t4;
+#
+# MDEV-17362: SIGSEGV in JOIN::optimize_inner or Assertion `fixed == 0'
+# failed in Item_equal::fix_fields, server crashes after 2nd execution
+# of PS
+#
+CREATE TABLE t1 (pk int primary key, i1 int, i2 int, v1 varchar(1), v2 varchar(1), KEY i1 (i1), KEY v2 (v2,i1)) ENGINE=InnoDB ;
+INSERT INTO t1 VALUES (12,1,1,'r','r');
+CREATE TABLE t2 (pk int, i1 int, i2 int, v1 varchar(1), v2 varchar(1)) ENGINE=InnoDB ;
+CREATE TABLE t3 (pk int, i1 int, i2 int, v1 varchar(1), v2 varchar(1)) ENGINE=InnoDB ;
+INSERT INTO t3 VALUES (19,1,9,NULL,NULL),(20,5,6,'r','r');
+prepare stmt from "
+SELECT 1 FROM t1
+WHERE t1.pk = t1.i1
+ AND ((t1.pk,t1.i1) IN (SELECT t3.pk, COUNT(t3.pk) FROM t3
+ WHERE EXISTS (SELECT 1 FROM (t1 JOIN t3 ON (t3.i2 = t1.i1)) WHERE t3.v1 = t1.v1)));";
+execute stmt;
+1
+execute stmt;
+1
+drop table t1,t2,t3;
diff --git a/mysql-test/main/subselect_innodb.test b/mysql-test/main/subselect_innodb.test
index 544bcd994ed..214d692e793 100644
--- a/mysql-test/main/subselect_innodb.test
+++ b/mysql-test/main/subselect_innodb.test
@@ -611,3 +611,27 @@ FROM t1
DROP TABLE t1,t2,t3,t4;
+--echo #
+--echo # MDEV-17362: SIGSEGV in JOIN::optimize_inner or Assertion `fixed == 0'
+--echo # failed in Item_equal::fix_fields, server crashes after 2nd execution
+--echo # of PS
+--echo #
+
+CREATE TABLE t1 (pk int primary key, i1 int, i2 int, v1 varchar(1), v2 varchar(1), KEY i1 (i1), KEY v2 (v2,i1)) ENGINE=InnoDB ;
+INSERT INTO t1 VALUES (12,1,1,'r','r');
+
+CREATE TABLE t2 (pk int, i1 int, i2 int, v1 varchar(1), v2 varchar(1)) ENGINE=InnoDB ;
+
+CREATE TABLE t3 (pk int, i1 int, i2 int, v1 varchar(1), v2 varchar(1)) ENGINE=InnoDB ;
+INSERT INTO t3 VALUES (19,1,9,NULL,NULL),(20,5,6,'r','r');
+
+prepare stmt from "
+SELECT 1 FROM t1
+WHERE t1.pk = t1.i1
+ AND ((t1.pk,t1.i1) IN (SELECT t3.pk, COUNT(t3.pk) FROM t3
+ WHERE EXISTS (SELECT 1 FROM (t1 JOIN t3 ON (t3.i2 = t1.i1)) WHERE t3.v1 = t1.v1)));";
+
+execute stmt;
+execute stmt;
+
+drop table t1,t2,t3;
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 1ad97e4244d..f694707578c 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -1294,11 +1294,6 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
unit->types.empty();
/* for derived tables & PS (which can't be reset by Item_subselect) */
unit->reinit_exec_mechanism();
- for (st_select_lex *sl= unit->first_select(); sl; sl= sl->next_select())
- {
- sl->cond_pushed_into_where= NULL;
- sl->cond_pushed_into_having= NULL;
- }
unit->set_thd(thd);
DBUG_RETURN(FALSE);
}
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 8a722ad3952..c15f3a8c143 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -3026,6 +3026,10 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
for (order= win_spec->order_list->first; order; order= order->next)
order->item= &order->item_ptr;
}
+
+ // Reinit Pushdown
+ sl->cond_pushed_into_where= NULL;
+ sl->cond_pushed_into_having= NULL;
}
if (sl->changed_elements & TOUCHED_SEL_DERIVED)
{
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index e8b092b1b23..197ead809eb 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -13445,8 +13445,10 @@ void JOIN::join_free()
void JOIN::cleanup(bool full)
{
DBUG_ENTER("JOIN::cleanup");
- DBUG_PRINT("enter", ("full %u", (uint) full));
-
+ DBUG_PRINT("enter", ("select: %d (%p) join: %p full: %u",
+ select_lex->select_number, select_lex, this,
+ (uint) full));
+
if (full)
have_query_plan= QEP_DELETED;
1
0
02 Apr '19
revision-id: a90622644c11568c2bf982492d2f47bc164ddfef (mariadb-10.4.3-106-ga90622644c1)
parent(s): 3e1f3d3e2f14a157121b92bd4b76e986f1846454
author: Oleksandr Byelkin
committer: Oleksandr Byelkin
timestamp: 2019-04-02 14:46:36 +0200
message:
Cleanup of derived table interface
---
sql/sql_derived.h | 1 -
sql/sql_view.cc | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/sql/sql_derived.h b/sql/sql_derived.h
index abfdb007072..2454d40ba79 100644
--- a/sql/sql_derived.h
+++ b/sql/sql_derived.h
@@ -22,7 +22,6 @@ struct LEX;
bool mysql_handle_derived(LEX *lex, uint phases);
bool mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases);
-bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived);
bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived);
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 3014065d28b..13b5caba539 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -1200,7 +1200,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
in which case the reinit call wasn't done.
See MDEV-6668 for details.
*/
- mysql_derived_reinit(thd, NULL, table);
+ mysql_handle_single_derived(thd->lex, table, DT_REINIT);
DEBUG_SYNC(thd, "after_cached_view_opened");
DBUG_RETURN(0);
1
0
[Commits] 14e113b91a5: Adjust wsrep, galera, galera_3nodes, galera_sr and galera_3nodes_sr tests
by jan 02 Apr '19
by jan 02 Apr '19
02 Apr '19
revision-id: 14e113b91a58b80062ca8a4c43d918031b0e4cd0 (mariadb-10.4.3-133-g14e113b91a5)
parent(s): 79fe17567a71467116214fc8fad925681c4c87da
author: Jan Lindström
committer: Jan Lindström
timestamp: 2019-04-02 15:43:46 +0300
message:
Adjust wsrep, galera, galera_3nodes, galera_sr and galera_3nodes_sr tests
after commit b5615eff0d00cfb4c60b9d1bf67094da7c2258a6
---
mysql-test/suite/galera_3nodes/t/galera_garbd.test | 1 +
mysql-test/suite/galera_3nodes/t/galera_ipv6_mysqldump.test | 1 +
mysql-test/suite/galera_3nodes/t/galera_safe_to_bootstrap.test | 5 ++++-
mysql-test/suite/galera_3nodes/t/galera_wsrep_schema.test | 1 +
mysql-test/suite/galera_3nodes_sr/r/GCF-832.result | 1 +
mysql-test/suite/galera_3nodes_sr/r/galera_sr_join_slave.result | 1 +
mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_master.result | 1 +
.../suite/galera_3nodes_sr/r/galera_sr_kill_slave_after_apply.result | 1 +
.../r/galera_sr_kill_slave_after_apply_rollback.result | 1 +
.../galera_3nodes_sr/r/galera_sr_kill_slave_before_apply.result | 1 +
mysql-test/suite/galera_sr/r/galera_sr_kill_all_nobootstrap.result | 2 ++
mysql-test/suite/galera_sr/r/galera_sr_kill_all_norecovery.result | 2 ++
mysql-test/suite/galera_sr/r/galera_sr_kill_all_pcrecovery.result | 2 ++
mysql-test/suite/galera_sr/r/galera_sr_kill_slave.result | 1 +
mysql-test/suite/galera_sr/r/galera_sr_mysqldump_sst.result | 1 +
mysql-test/suite/galera_sr/r/galera_sr_shutdown_master.result | 1 +
mysql-test/suite/galera_sr/r/galera_sr_shutdown_slave.result | 1 +
mysql-test/suite/galera_sr/r/mysql-wsrep-features#9.result | 1 +
mysql-test/suite/wsrep/t/wsrep-recover-v25.test | 5 +++++
mysql-test/suite/wsrep/t/wsrep-recover.test | 5 +++++
20 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/mysql-test/suite/galera_3nodes/t/galera_garbd.test b/mysql-test/suite/galera_3nodes/t/galera_garbd.test
index aaa9ecc8083..fa3cba0ab16 100644
--- a/mysql-test/suite/galera_3nodes/t/galera_garbd.test
+++ b/mysql-test/suite/galera_3nodes/t/galera_garbd.test
@@ -99,6 +99,7 @@ DROP TABLE t1;
--echo Restarting node #3 to satisfy MTR's end-of-test checks
--connection node_3
+let $restart_noprint=2;
--source include/start_mysqld.inc
# Restore original auto_increment_offset values.
diff --git a/mysql-test/suite/galera_3nodes/t/galera_ipv6_mysqldump.test b/mysql-test/suite/galera_3nodes/t/galera_ipv6_mysqldump.test
index 4733b686e93..c9c32f23230 100644
--- a/mysql-test/suite/galera_3nodes/t/galera_ipv6_mysqldump.test
+++ b/mysql-test/suite/galera_3nodes/t/galera_ipv6_mysqldump.test
@@ -59,6 +59,7 @@ INSERT INTO t1 VALUES (1);
--connection node_2
--echo Starting server ...
--let $start_mysqld_params = --wsrep_sst_auth=sst: --wsrep_sst_method=mysqldump --wsrep-sst-receive-address=[::1].1:$NODE_MYPORT_2
+let $restart_noprint=2;
--source include/start_mysqld.inc
#--source suite/galera/include/galera_load_provider.inc
diff --git a/mysql-test/suite/galera_3nodes/t/galera_safe_to_bootstrap.test b/mysql-test/suite/galera_3nodes/t/galera_safe_to_bootstrap.test
index 053c0bd8ce9..082cb546e87 100644
--- a/mysql-test/suite/galera_3nodes/t/galera_safe_to_bootstrap.test
+++ b/mysql-test/suite/galera_3nodes/t/galera_safe_to_bootstrap.test
@@ -87,8 +87,8 @@ CREATE TABLE t1 (f1 INTEGER) ENGINE=InnoDB;
--source include/assert_grep.inc
# Restart one node
-
--connection node_2
+let $restart_noprint=2;
--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
--source include/start_mysqld.inc
@@ -153,15 +153,18 @@ SET SESSION wsrep_on = OFF;
--connection node_1
--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+let $restart_noprint=2;
--source include/start_mysqld.inc
--source include/wait_until_connected_again.inc
--connection node_2
--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
+let $restart_noprint=2;
--source include/start_mysqld.inc
--connection node_3
--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.3.expect
+let $restart_noprint=2;
--source include/start_mysqld.inc
--let $wait_condition = SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size';
diff --git a/mysql-test/suite/galera_3nodes/t/galera_wsrep_schema.test b/mysql-test/suite/galera_3nodes/t/galera_wsrep_schema.test
index 5c2cae75bd0..fb3af2cd25d 100644
--- a/mysql-test/suite/galera_3nodes/t/galera_wsrep_schema.test
+++ b/mysql-test/suite/galera_3nodes/t/galera_wsrep_schema.test
@@ -59,6 +59,7 @@ SELECT COUNT(*) = 2 FROM mysql.wsrep_cluster_members;
#disabled SELECT COUNT(*) = 2 FROM mysql.wsrep_cluster_member_history WHERE last_view_id = (SELECT MAX(last_view_id) FROM mysql.wsrep_cluster_member_history);
--connection node_2
+let $restart_noprint=2;
--source include/start_mysqld.inc
--source include/wait_until_connected_again.inc
diff --git a/mysql-test/suite/galera_3nodes_sr/r/GCF-832.result b/mysql-test/suite/galera_3nodes_sr/r/GCF-832.result
index d670b8c24a0..58d02e541af 100644
--- a/mysql-test/suite/galera_3nodes_sr/r/GCF-832.result
+++ b/mysql-test/suite/galera_3nodes_sr/r/GCF-832.result
@@ -15,6 +15,7 @@ START TRANSACTION;
INSERT INTO t1 VALUES ('primary'),('primary'),('primary'),('primary'),('primary');
COMMIT;
ERROR HY000: Lost connection to MySQL server during query
+# restart
connection node_1;
SELECT COUNT(*) = 0 FROM mysql.wsrep_streaming_log;
COUNT(*) = 0
diff --git a/mysql-test/suite/galera_3nodes_sr/r/galera_sr_join_slave.result b/mysql-test/suite/galera_3nodes_sr/r/galera_sr_join_slave.result
index 0260ebec86a..dbdd95f837a 100644
--- a/mysql-test/suite/galera_3nodes_sr/r/galera_sr_join_slave.result
+++ b/mysql-test/suite/galera_3nodes_sr/r/galera_sr_join_slave.result
@@ -17,6 +17,7 @@ INSERT INTO t1 VALUES (3);
INSERT INTO t1 VALUES (4);
INSERT INTO t1 VALUES (5);
connection node_2;
+# restart
connection node_1;
INSERT INTO t1 VALUES (6);
INSERT INTO t1 VALUES (7);
diff --git a/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_master.result b/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_master.result
index 1a179565666..4547d06dcf9 100644
--- a/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_master.result
+++ b/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_master.result
@@ -24,6 +24,7 @@ COUNT(*) = 0
1
INSERT INTO t1 VALUES (1);
connection node_2;
+# restart
SELECT COUNT(*) = 0 FROM mysql.wsrep_streaming_log;
COUNT(*) = 0
1
diff --git a/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_slave_after_apply.result b/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_slave_after_apply.result
index 595bd30d675..db04c24b5b1 100644
--- a/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_slave_after_apply.result
+++ b/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_slave_after_apply.result
@@ -24,6 +24,7 @@ INSERT INTO t1 VALUES (8);
INSERT INTO t1 VALUES (9);
INSERT INTO t1 VALUES (10);
connection node_2;
+# restart
connection node_1;
INSERT INTO t1 VALUES (11);
INSERT INTO t1 VALUES (12);
diff --git a/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_slave_after_apply_rollback.result b/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_slave_after_apply_rollback.result
index 239eb748385..ff9215cf31f 100644
--- a/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_slave_after_apply_rollback.result
+++ b/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_slave_after_apply_rollback.result
@@ -24,6 +24,7 @@ INSERT INTO t1 VALUES (8);
INSERT INTO t1 VALUES (9);
INSERT INTO t1 VALUES (10);
connection node_2;
+# restart
connection node_1;
INSERT INTO t1 VALUES (11);
INSERT INTO t1 VALUES (12);
diff --git a/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_slave_before_apply.result b/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_slave_before_apply.result
index 4a135cd9274..e9dc5518e96 100644
--- a/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_slave_before_apply.result
+++ b/mysql-test/suite/galera_3nodes_sr/r/galera_sr_kill_slave_before_apply.result
@@ -29,6 +29,7 @@ INSERT INTO t1 VALUES (4);
INSERT INTO t1 VALUES (5);
connection node_2;
Killing server ...
+# restart
connection node_1;
COMMIT;
SELECT COUNT(*) = 5 FROM t1;
diff --git a/mysql-test/suite/galera_sr/r/galera_sr_kill_all_nobootstrap.result b/mysql-test/suite/galera_sr/r/galera_sr_kill_all_nobootstrap.result
index a78926a9794..45ca6909f29 100644
--- a/mysql-test/suite/galera_sr/r/galera_sr_kill_all_nobootstrap.result
+++ b/mysql-test/suite/galera_sr/r/galera_sr_kill_all_nobootstrap.result
@@ -14,7 +14,9 @@ Killing server ...
connection node_1;
Killing server ...
connection node_1;
+# restart
connection node_2;
+# restart
connection node_1;
SELECT COUNT(*) = 0 FROM mysql.wsrep_streaming_log;
COUNT(*) = 0
diff --git a/mysql-test/suite/galera_sr/r/galera_sr_kill_all_norecovery.result b/mysql-test/suite/galera_sr/r/galera_sr_kill_all_norecovery.result
index e707f04fd55..7ef86c65915 100644
--- a/mysql-test/suite/galera_sr/r/galera_sr_kill_all_norecovery.result
+++ b/mysql-test/suite/galera_sr/r/galera_sr_kill_all_norecovery.result
@@ -17,7 +17,9 @@ Killing server ...
connection node_1;
SET SESSION wsrep_sync_wait = 0;
Killing server ...
+# restart
connection node_2;
+# restart
connection node_1;
SELECT COUNT(*) `expect 0` FROM mysql.wsrep_streaming_log;
expect 0
diff --git a/mysql-test/suite/galera_sr/r/galera_sr_kill_all_pcrecovery.result b/mysql-test/suite/galera_sr/r/galera_sr_kill_all_pcrecovery.result
index 7525cd6d4b7..7731c19e4da 100644
--- a/mysql-test/suite/galera_sr/r/galera_sr_kill_all_pcrecovery.result
+++ b/mysql-test/suite/galera_sr/r/galera_sr_kill_all_pcrecovery.result
@@ -14,7 +14,9 @@ Killing server ...
connection node_1;
Killing server ...
connection node_1;
+# restart
connection node_2;
+# restart
connection node_1;
SELECT COUNT(*) = 0 FROM mysql.wsrep_streaming_log;
COUNT(*) = 0
diff --git a/mysql-test/suite/galera_sr/r/galera_sr_kill_slave.result b/mysql-test/suite/galera_sr/r/galera_sr_kill_slave.result
index b4e84b1b72a..d14de27d2b7 100644
--- a/mysql-test/suite/galera_sr/r/galera_sr_kill_slave.result
+++ b/mysql-test/suite/galera_sr/r/galera_sr_kill_slave.result
@@ -31,6 +31,7 @@ INSERT INTO t1 VALUES (8);
INSERT INTO t1 VALUES (9);
INSERT INTO t1 VALUES (10);
connection node_2;
+# restart
connection node_1;
INSERT INTO t1 VALUES (11);
INSERT INTO t1 VALUES (12);
diff --git a/mysql-test/suite/galera_sr/r/galera_sr_mysqldump_sst.result b/mysql-test/suite/galera_sr/r/galera_sr_mysqldump_sst.result
index f1b60c7f76a..2e94461f377 100644
--- a/mysql-test/suite/galera_sr/r/galera_sr_mysqldump_sst.result
+++ b/mysql-test/suite/galera_sr/r/galera_sr_mysqldump_sst.result
@@ -24,6 +24,7 @@ Shutting down server ...
connection node_1;
connection node_2;
Starting server ...
+# restart: --wsrep_sst_auth=sst:sst --wsrep_sst_method=mysqldump --wsrep-sst-receive-address=127.0.0.1:16001
connection node_1;
connection node_2;
SELECT COUNT(*) > 0 FROM mysql.wsrep_streaming_log;
diff --git a/mysql-test/suite/galera_sr/r/galera_sr_shutdown_master.result b/mysql-test/suite/galera_sr/r/galera_sr_shutdown_master.result
index eb493f66ed6..051b9d6b7ec 100644
--- a/mysql-test/suite/galera_sr/r/galera_sr_shutdown_master.result
+++ b/mysql-test/suite/galera_sr/r/galera_sr_shutdown_master.result
@@ -18,6 +18,7 @@ SELECT COUNT(*) = 0 FROM t1;
COUNT(*) = 0
1
connection node_2;
+# restart
SELECT COUNT(*) = 0 FROM mysql.wsrep_streaming_log;
COUNT(*) = 0
1
diff --git a/mysql-test/suite/galera_sr/r/galera_sr_shutdown_slave.result b/mysql-test/suite/galera_sr/r/galera_sr_shutdown_slave.result
index 568452b10b0..6218cffcb78 100644
--- a/mysql-test/suite/galera_sr/r/galera_sr_shutdown_slave.result
+++ b/mysql-test/suite/galera_sr/r/galera_sr_shutdown_slave.result
@@ -20,6 +20,7 @@ connection node_1a;
INSERT INTO t1 VALUES (14),(15),(16);
COMMIT;
connection node_2;
+# restart
SELECT COUNT(*) > 0 FROM mysql.wsrep_streaming_log;
COUNT(*) > 0
1
diff --git a/mysql-test/suite/galera_sr/r/mysql-wsrep-features#9.result b/mysql-test/suite/galera_sr/r/mysql-wsrep-features#9.result
index 990ea47f8bc..312c8efc6ef 100644
--- a/mysql-test/suite/galera_sr/r/mysql-wsrep-features#9.result
+++ b/mysql-test/suite/galera_sr/r/mysql-wsrep-features#9.result
@@ -9,6 +9,7 @@ Killing server ...
connection node_1;
ALTER TABLE t1 ADD COLUMN f2 INTEGER;
connection node_2;
+# restart
connection node_2a;
SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='t1';
COUNT(*) = 2
diff --git a/mysql-test/suite/wsrep/t/wsrep-recover-v25.test b/mysql-test/suite/wsrep/t/wsrep-recover-v25.test
index f6a0f7fec97..743e2795c1c 100644
--- a/mysql-test/suite/wsrep/t/wsrep-recover-v25.test
+++ b/mysql-test/suite/wsrep/t/wsrep-recover-v25.test
@@ -37,6 +37,8 @@ if ($log_bin) {
# The expected recovered seqno is 1 corresponding to initial cluster
# configuration change.
#
+let $restart_noprint=2;
+
--source include/kill_mysqld.inc
--source wsrep-recover-step.inc
--echo Expect seqno 1
@@ -53,6 +55,7 @@ if ($log_bin) {
#
CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB;
+let $restart_noprint=2;
--source include/kill_mysqld.inc
--source wsrep-recover-step.inc
--echo Expect seqno 3
@@ -71,6 +74,7 @@ CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB;
#
INSERT INTO t1 VALUES (5);
+let $restart_noprint=2;
--source include/kill_mysqld.inc
--source wsrep-recover-step.inc
--echo Expect seqno 5
@@ -99,6 +103,7 @@ if ($log_bin) {
--send INSERT INTO t1 VALUES (7)
--connection default
+ let $restart_noprint=2;
SET DEBUG_SYNC = "now WAIT_FOR after_prepare_reached";
--source include/kill_mysqld.inc
--source wsrep-recover-step.inc
diff --git a/mysql-test/suite/wsrep/t/wsrep-recover.test b/mysql-test/suite/wsrep/t/wsrep-recover.test
index 7c1d0da3078..75271e45b15 100644
--- a/mysql-test/suite/wsrep/t/wsrep-recover.test
+++ b/mysql-test/suite/wsrep/t/wsrep-recover.test
@@ -36,6 +36,7 @@ if ($log_bin) {
# The expected recovered seqno is 1 corresponding to initial cluster
# configuration change.
#
+let $restart_noprint=2;
--source include/kill_mysqld.inc
--source wsrep-recover-step.inc
--echo Expect seqno 1
@@ -50,6 +51,7 @@ if ($log_bin) {
# The expected recovered seqno is 3 corresponding to two configuration
# change events and CREATE TABLE.
#
+let $restart_noprint=2;
CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB;
--source include/kill_mysqld.inc
@@ -68,6 +70,7 @@ CREATE TABLE t1 (f1 INT PRIMARY KEY) ENGINE=InnoDB;
#
# The expected wsrep_last_committed after the server is restarted is 6.
#
+let $restart_noprint=2;
INSERT INTO t1 VALUES (5);
--source include/kill_mysqld.inc
@@ -114,6 +117,7 @@ SET DEBUG_SYNC = "wsrep_before_commit_order_enter SIGNAL before_commit_order_rea
SET DEBUG_SYNC = "now WAIT_FOR before_commit_order_reached";
--connection default
+let $restart_noprint=2;
--source include/kill_mysqld.inc
--source wsrep-recover-step.inc
--echo Expect seqno 6
@@ -163,6 +167,7 @@ SET DEBUG_SYNC = "now SIGNAL continue_after_certification";
SET DEBUG_SYNC = "now WAIT_FOR before_commit_order_reached_1";
--connection default
+let $restart_noprint=2;
--source include/kill_mysqld.inc
--source wsrep-recover-step.inc
--echo Expect seqno 7
1
0
01 Apr '19
revision-id: 25507440cec73c987976511047032b3ef38b67a5 (fb-prod201801-220-g25507440cec)
parent(s): 60be87fedcce6bfbab7c760b3ca127448b4896eb
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2019-04-01 17:25:02 +0300
message:
Range Locking: better initialization
a part of the "Unified Lock Mangager Interface" change
---
rocksdb | 2 +-
storage/rocksdb/ha_rocksdb.cc | 14 +++++++-------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/rocksdb b/rocksdb
index 190a29d06f4..46d49ea0b7e 160000
--- a/rocksdb
+++ b/rocksdb
@@ -1 +1 @@
-Subproject commit 190a29d06f4e79d2df4cb513944ac34bd133caa0
+Subproject commit 46d49ea0b7e0267b5c360d45d90a64776477b8ed
diff --git a/storage/rocksdb/ha_rocksdb.cc b/storage/rocksdb/ha_rocksdb.cc
index 4071e3ab2a8..6fe877f1f54 100644
--- a/storage/rocksdb/ha_rocksdb.cc
+++ b/storage/rocksdb/ha_rocksdb.cc
@@ -5060,6 +5060,13 @@ static int rocksdb_init_func(void *const p) {
DBUG_RETURN(HA_EXIT_FAILURE);
}
+ tx_db_options.use_range_locking = rocksdb_use_range_locking;
+ if (rocksdb_use_range_locking)
+ {
+ tx_db_options.range_locking_opts.cvt_func= range_endpoint_convert;
+ tx_db_options.range_locking_opts.cmp_func= range_endpoints_compare;
+ }
+
status = rocksdb::TransactionDB::Open(
main_opts, tx_db_options, rocksdb_datadir, cf_descr, &cf_handles, &rdb);
@@ -5069,17 +5076,10 @@ static int rocksdb_init_func(void *const p) {
DBUG_RETURN(HA_EXIT_FAILURE);
}
- //psergey-todo: this implies that TransactionDB::Open() call above did not
- // acquire any locks (if it did, we wont be able switch to another locking
- // system):
- rdb->use_range_locking= rocksdb_use_range_locking; // psergey
-
if (rocksdb_use_range_locking)
{
rocksdb::RangeLockMgrControl *mgr= rdb->get_range_lock_manager();
- mgr->set_endpoint_cmp_functions(range_endpoint_convert,
- range_endpoints_compare);
mgr->set_max_lock_memory(rocksdb_max_lock_memory);
sql_print_information("RocksDB: USING NEW RANGE LOCKING");
sql_print_information("RocksDB: Max lock memory=%lu", rocksdb_max_lock_memory);
1
0
01 Apr '19
revision-id: 46d49ea0b7e0267b5c360d45d90a64776477b8ed (v5.8-1025-g46d49ea0b)
parent(s): 190a29d06f4e79d2df4cb513944ac34bd133caa0
author: Sergei Petrunia
committer: Sergei Petrunia
timestamp: 2019-04-01 17:11:33 +0300
message:
Gap Locking: Unified Lock Manager interface
- Make PessimisticTransactionDB use one lock manager (lock_mgr_) which
is either a point lock manager or a range lock manager.
- A few differences are still "visible" due to lock managers having
different properties.
---
include/rocksdb/utilities/transaction_db.h | 40 +++++----
utilities/transactions/pessimistic_transaction.cc | 6 +-
.../transactions/pessimistic_transaction_db.cc | 95 ++++++++++------------
.../transactions/pessimistic_transaction_db.h | 16 ++--
utilities/transactions/range_locking_test.cc | 9 +-
utilities/transactions/transaction_lock_mgr.cc | 39 ++++-----
utilities/transactions/transaction_lock_mgr.h | 34 +++++---
7 files changed, 123 insertions(+), 116 deletions(-)
diff --git a/include/rocksdb/utilities/transaction_db.h b/include/rocksdb/utilities/transaction_db.h
index fb3c6a88c..06d73bedd 100644
--- a/include/rocksdb/utilities/transaction_db.h
+++ b/include/rocksdb/utilities/transaction_db.h
@@ -33,6 +33,24 @@ enum TxnDBWritePolicy {
const uint32_t kInitialMaxDeadlocks = 5;
+struct RangeLockingOptions {
+ typedef void (*convert_key_to_endpoint_func)(const rocksdb::Slice &key,
+ std::string *endpoint);
+
+ typedef int (*compare_endpoints_func)(const char *a, size_t a_len,
+ const char *b, size_t b_len);
+
+ // TODO: So, functions to compare ranges are here, while
+ // functions to compare rowkeys are in per-column family and are in
+ // rocksdb::ColumnFamilyOptions
+ //
+ // TODO: Can we change this to work in a way that does not expose the endpoints
+ // to the user (like discussed on the meeting?)
+ //
+ convert_key_to_endpoint_func cvt_func;
+ compare_endpoints_func cmp_func;
+};
+
struct TransactionDBOptions {
// Specifies the maximum number of keys that can be locked at the same time
// per column family.
@@ -93,6 +111,13 @@ struct TransactionDBOptions {
// logic in myrocks. This hack of simply not rolling back merge operands works
// for the special way that myrocks uses this operands.
bool rollback_merge_operands = false;
+
+ // If true, range_locking_opts specifies options on range locking (filling
+ // the struct is mandatory)
+ bool use_range_locking = false;
+
+ // Members are valid if use_range_locking= true.
+ RangeLockingOptions range_locking_opts;
};
struct TransactionOptions {
@@ -197,15 +222,6 @@ struct DeadlockPath {
class RangeLockMgrControl {
public:
- typedef void (*convert_key_to_endpoint_func)(const rocksdb::Slice &key,
- std::string *endpoint);
-
- typedef int (*compare_endpoints_func)(const char *a, size_t a_len,
- const char *b, size_t b_len);
-
- virtual void set_endpoint_cmp_functions(convert_key_to_endpoint_func cvt_func,
- compare_endpoints_func cmp_func)=0;
-
virtual int set_max_lock_memory(size_t max_lock_memory) = 0;
virtual uint64_t get_escalation_count() = 0;
@@ -286,15 +302,11 @@ class TransactionDB : public StackableDB {
virtual std::vector<DeadlockPath> GetDeadlockInfoBuffer() = 0;
virtual void SetDeadlockInfoBufferSize(uint32_t target_size) = 0;
-
- // psergey-TODO: any better interface for this?
- bool use_range_locking;
virtual RangeLockMgrControl* get_range_lock_manager() { return nullptr; }
-
protected:
// To Create an TransactionDB, call Open()
// The ownership of db is transferred to the base StackableDB
- explicit TransactionDB(DB* db) : StackableDB(db), use_range_locking(false) {}
+ explicit TransactionDB(DB* db) : StackableDB(db) {}
private:
// No copying allowed
diff --git a/utilities/transactions/pessimistic_transaction.cc b/utilities/transactions/pessimistic_transaction.cc
index e900bc067..339dcf422 100644
--- a/utilities/transactions/pessimistic_transaction.cc
+++ b/utilities/transactions/pessimistic_transaction.cc
@@ -50,7 +50,7 @@ PessimisticTransaction::PessimisticTransaction(
skip_concurrency_control_(false) {
txn_db_impl_ =
static_cast_with_check<PessimisticTransactionDB, TransactionDB>(txn_db);
- do_key_tracking_ = ! txn_db_impl_->use_range_locking;
+ do_key_tracking_ = !txn_db_impl_->get_range_lock_manager();
db_impl_ = static_cast_with_check<DBImpl, DB>(db_);
Initialize(txn_options);
}
@@ -90,7 +90,7 @@ void PessimisticTransaction::Initialize(const TransactionOptions& txn_options) {
}
PessimisticTransaction::~PessimisticTransaction() {
- txn_db_impl_->UnLock(this, &GetTrackedKeys());
+ txn_db_impl_->UnLock(this, &GetTrackedKeys()/*, all_keys_hint=true*/);
if (expiration_time_ > 0) {
txn_db_impl_->RemoveExpirableTransaction(txn_id_);
}
@@ -569,7 +569,7 @@ Status PessimisticTransaction::TryLock(ColumnFamilyHandle* column_family,
// since the snapshot. This must be done after we locked the key.
// If we already have validated an earilier snapshot it must has been
// reflected in tracked_at_seq and ValidateSnapshot will return OK.
- if (s.ok()) {
+ if (s.ok()) { //psergey-todo: this check seems to be meaningless, s.ok()==true always
s = ValidateSnapshot(column_family, key, &tracked_at_seq);
if (!s.ok()) {
diff --git a/utilities/transactions/pessimistic_transaction_db.cc b/utilities/transactions/pessimistic_transaction_db.cc
index 308187e13..270859d1a 100644
--- a/utilities/transactions/pessimistic_transaction_db.cc
+++ b/utilities/transactions/pessimistic_transaction_db.cc
@@ -34,22 +34,35 @@ PessimisticTransactionDB::PessimisticTransactionDB(
DB* db, const TransactionDBOptions& txn_db_options)
: TransactionDB(db),
db_impl_(static_cast_with_check<DBImpl, DB>(db)),
- txn_db_options_(txn_db_options),
- lock_mgr_(this, txn_db_options_.num_stripes, txn_db_options.max_num_locks,
- txn_db_options_.max_num_deadlocks,
- txn_db_options_.custom_mutex_factory
- ? txn_db_options_.custom_mutex_factory
- : std::shared_ptr<TransactionDBMutexFactory>(
- new TransactionDBMutexFactoryImpl())),
- range_lock_mgr_(this,
- txn_db_options_.custom_mutex_factory?
- txn_db_options_.custom_mutex_factory :
- std::shared_ptr<TransactionDBMutexFactory>(
- new TransactionDBMutexFactoryImpl())) {
+ txn_db_options_(txn_db_options) {
+ init_lock_manager();
assert(db_impl_ != nullptr);
info_log_ = db_impl_->GetDBOptions().info_log;
}
+void PessimisticTransactionDB::init_lock_manager() {
+ BaseLockMgr *lock_mgr;
+
+ std::shared_ptr<TransactionDBMutexFactory> mutex_factory =
+ txn_db_options_.custom_mutex_factory?
+ txn_db_options_.custom_mutex_factory :
+ std::shared_ptr<TransactionDBMutexFactory>(
+ new TransactionDBMutexFactoryImpl());
+
+ if (txn_db_options_.use_range_locking) {
+ range_lock_mgr_= new RangeLockMgr(this, txn_db_options_.range_locking_opts,
+ mutex_factory);
+ lock_mgr = range_lock_mgr_;
+ } else {
+ lock_mgr = new TransactionLockMgr(this, txn_db_options_.num_stripes,
+ txn_db_options_.max_num_locks,
+ txn_db_options_.max_num_deadlocks,
+ mutex_factory);
+ range_lock_mgr_ = nullptr;
+ }
+ lock_mgr_ = std::shared_ptr<BaseLockMgr>(lock_mgr);
+}
+
// Support initiliazing PessimisticTransactionDB from a stackable db
//
// PessimisticTransactionDB
@@ -70,18 +83,8 @@ PessimisticTransactionDB::PessimisticTransactionDB(
StackableDB* db, const TransactionDBOptions& txn_db_options)
: TransactionDB(db),
db_impl_(static_cast_with_check<DBImpl, DB>(db->GetRootDB())),
- txn_db_options_(txn_db_options),
- lock_mgr_(this, txn_db_options_.num_stripes, txn_db_options.max_num_locks,
- txn_db_options_.max_num_deadlocks,
- txn_db_options_.custom_mutex_factory
- ? txn_db_options_.custom_mutex_factory
- : std::shared_ptr<TransactionDBMutexFactory>(
- new TransactionDBMutexFactoryImpl())),
- range_lock_mgr_(this,
- txn_db_options_.custom_mutex_factory
- ? txn_db_options_.custom_mutex_factory
- : std::shared_ptr<TransactionDBMutexFactory>(
- new TransactionDBMutexFactoryImpl())) {
+ txn_db_options_(txn_db_options) {
+ init_lock_manager();
assert(db_impl_ != nullptr);
}
@@ -351,7 +354,7 @@ Status TransactionDB::WrapStackableDB(
// allocate a LockMap for it.
void PessimisticTransactionDB::AddColumnFamily(
const ColumnFamilyHandle* handle) {
- lock_mgr_.AddColumnFamily(handle->GetID());
+ lock_mgr_->AddColumnFamily(handle->GetID());
}
Status PessimisticTransactionDB::CreateColumnFamily(
@@ -365,7 +368,7 @@ Status PessimisticTransactionDB::CreateColumnFamily(
s = db_->CreateColumnFamily(options, column_family_name, handle);
if (s.ok()) {
- lock_mgr_.AddColumnFamily((*handle)->GetID());
+ lock_mgr_->AddColumnFamily((*handle)->GetID());
UpdateCFComparatorMap(*handle);
}
@@ -380,8 +383,7 @@ Status PessimisticTransactionDB::DropColumnFamily(
Status s = db_->DropColumnFamily(column_family);
if (s.ok()) {
- //psergey-todo: range_lock_mgr_ ??
- lock_mgr_.RemoveColumnFamily(column_family->GetID());
+ lock_mgr_->RemoveColumnFamily(column_family->GetID());
}
return s;
@@ -391,10 +393,7 @@ Status PessimisticTransactionDB::TryLock(PessimisticTransaction* txn,
uint32_t cfh_id,
const std::string& key,
bool exclusive) {
- if (use_range_locking)
- return range_lock_mgr_.TryLock(txn, cfh_id, key, GetEnv(), exclusive);
- else
- return lock_mgr_.TryLock(txn, cfh_id, key, GetEnv(), exclusive);
+ return lock_mgr_->TryLock(txn, cfh_id, key, GetEnv(), exclusive);
}
Status
@@ -402,9 +401,9 @@ PessimisticTransactionDB::TryRangeLock(PessimisticTransaction *txn,
uint32_t cfh_id,
const Slice& start_endp,
const Slice& end_endp) {
- if (use_range_locking) {
- return range_lock_mgr_.TryRangeLock(txn, cfh_id, start_endp,
- end_endp, /*exclusive=*/false);
+ if (range_lock_mgr_) {
+ return range_lock_mgr_->TryRangeLock(txn, cfh_id, start_endp,
+ end_endp, /*exclusive=*/false);
}
else
return Status::NotSupported();
@@ -413,23 +412,16 @@ PessimisticTransactionDB::TryRangeLock(PessimisticTransaction *txn,
void PessimisticTransactionDB::UnLock(PessimisticTransaction* txn,
const TransactionKeyMap* keys,
bool all_keys_hint) {
- if (use_range_locking)
- {
- if (all_keys_hint)
- range_lock_mgr_.UnLockAll(txn, GetEnv());
- else
- range_lock_mgr_.UnLock(txn, keys, GetEnv());
+ if (all_keys_hint && range_lock_mgr_) {
+ range_lock_mgr_->UnLockAll(txn, GetEnv());
+ return;
}
- else
- lock_mgr_.UnLock(txn, keys, GetEnv());
+ lock_mgr_->UnLock(txn, keys, GetEnv());
}
void PessimisticTransactionDB::UnLock(PessimisticTransaction* txn,
uint32_t cfh_id, const std::string& key) {
- if (use_range_locking)
- range_lock_mgr_.UnLock(txn, cfh_id, key, GetEnv());
- else
- lock_mgr_.UnLock(txn, cfh_id, key, GetEnv());
+ lock_mgr_->UnLock(txn, cfh_id, key, GetEnv());
}
// Used when wrapping DB write operations in a transaction
@@ -628,20 +620,17 @@ void PessimisticTransactionDB::GetAllPreparedTransactions(
TransactionLockMgr::LockStatusData
PessimisticTransactionDB::GetLockStatusData() {
- if (use_range_locking)
- return range_lock_mgr_.GetLockStatusData();
- else
- return lock_mgr_.GetLockStatusData();
+ return lock_mgr_->GetLockStatusData();
}
std::vector<DeadlockPath> PessimisticTransactionDB::GetDeadlockInfoBuffer() {
// TODO: Here, we should get deadlock info from RangeLockMgr if we are using
// it. At the moment, it doesn't provide any deadlock information.
- return lock_mgr_.GetDeadlockInfoBuffer();
+ return lock_mgr_->GetDeadlockInfoBuffer();
}
void PessimisticTransactionDB::SetDeadlockInfoBufferSize(uint32_t target_size) {
- lock_mgr_.Resize(target_size);
+ lock_mgr_->Resize(target_size);
}
void PessimisticTransactionDB::RegisterTransaction(Transaction* txn) {
diff --git a/utilities/transactions/pessimistic_transaction_db.h b/utilities/transactions/pessimistic_transaction_db.h
index 388ed4099..ad6eaf4a9 100644
--- a/utilities/transactions/pessimistic_transaction_db.h
+++ b/utilities/transactions/pessimistic_transaction_db.h
@@ -150,15 +150,21 @@ class PessimisticTransactionDB : public TransactionDB {
friend class TransactionTest_TwoPhaseOutOfOrderDelete_Test;
friend class WriteUnpreparedTransactionTest_RecoveryTest_Test;
friend class WriteUnpreparedTransactionTest_MarkLogWithPrepSection_Test;
- TransactionLockMgr lock_mgr_;
- RangeLockMgr range_lock_mgr_;
-
+ // Lock manager being used. This is either a TransactionLockMgr or a RangeLockMgr
+ std::shared_ptr<BaseLockMgr> lock_mgr_;
+
+ // Non-null if we are using a lock manager that supports range locking.
+ RangeLockMgr *range_lock_mgr_ = nullptr;
+
+ public:
// Return Range Lock Manager if we are actually using it
virtual RangeLockMgrControl* get_range_lock_manager() override {
- return use_range_locking? &range_lock_mgr_ : nullptr;
+ return range_lock_mgr_;
}
-
+ private:
+ void init_lock_manager();
+
// Must be held when adding/dropping column families.
InstrumentedMutex column_family_mutex_;
Transaction* BeginInternalTransaction(const WriteOptions& options);
diff --git a/utilities/transactions/range_locking_test.cc b/utilities/transactions/range_locking_test.cc
index fff4a0e26..447e4c44e 100644
--- a/utilities/transactions/range_locking_test.cc
+++ b/utilities/transactions/range_locking_test.cc
@@ -56,7 +56,6 @@ class RangeLockingTest : public ::testing::Test {
Options options;
TransactionDBOptions txn_db_options;
- bool use_stackable_db_;
RangeLockingTest()
: db(nullptr) {
@@ -65,15 +64,17 @@ class RangeLockingTest : public ::testing::Test {
DestroyDB(dbname, options);
Status s;
+ txn_db_options.use_range_locking = true;
+ txn_db_options.range_locking_opts.cvt_func =
+ range_endpoint_convert_same;
+ txn_db_options.range_locking_opts.cmp_func =
+ range_endpoints_compare_default;
s = TransactionDB::Open(options, txn_db_options, dbname, &db);
assert(s.ok());
db->use_range_locking= true;
rocksdb::RangeLockMgrControl *mgr= db->get_range_lock_manager();
assert(mgr);
-
- mgr->set_endpoint_cmp_functions(range_endpoint_convert_same,
- range_endpoints_compare_default);
// can also: mgr->set_max_lock_memory(rocksdb_max_lock_memory);
}
diff --git a/utilities/transactions/transaction_lock_mgr.cc b/utilities/transactions/transaction_lock_mgr.cc
index 79164a962..f15d9df21 100644
--- a/utilities/transactions/transaction_lock_mgr.cc
+++ b/utilities/transactions/transaction_lock_mgr.cc
@@ -700,7 +700,7 @@ void TransactionLockMgr::UnLock(const PessimisticTransaction* txn,
}
}
-TransactionLockMgr::LockStatusData TransactionLockMgr::GetLockStatusData() {
+BaseLockMgr::LockStatusData TransactionLockMgr::GetLockStatusData() {
LockStatusData data;
// Lock order here is important. The correct order is lock_map_mutex_, then
// for every column family ID in ascending order lock every stripe in
@@ -1002,11 +1002,17 @@ int RangeLockMgr::compare_dbt_endpoints(__toku_db*, void *arg,
RangeLockMgr::RangeLockMgr(TransactionDB* txn_db,
+ const RangeLockingOptions& opts,
std::shared_ptr<TransactionDBMutexFactory> mutex_factory) :
- my_txn_db(txn_db), mutex_factory_(mutex_factory) {
+ my_txn_db_(txn_db), mutex_factory_(mutex_factory) {
+ convert_key_to_endpoint= opts.cvt_func;
+ compare_endpoints= opts.cmp_func;
+
ltm.create(on_create, on_destroy, on_escalate, NULL, mutex_factory_);
- lt= nullptr;
- cmp_initialized_= false;
+
+ cmp_.create(compare_dbt_endpoints, (void*)this, NULL);
+ DICTIONARY_ID dict_id = { .dictid = 1 };
+ lt= ltm.get_lt(dict_id, cmp_, /* on_create_extra*/nullptr);
}
@@ -1048,28 +1054,13 @@ void RangeLockMgr::on_escalate(TXNID txnid, const locktree *lt,
// TODO: same as above: lt->get_manager()->note_mem_used(ranges.buffer->total_memory_size());
}
-void
-RangeLockMgr::set_endpoint_cmp_functions(convert_key_to_endpoint_func cvt_func,
- compare_endpoints_func cmp_func) {
- convert_key_to_endpoint= cvt_func;
- compare_endpoints= cmp_func;
-
- // The rest is like a constructor:
- assert(!lt);
-
- cmp_.create(compare_dbt_endpoints, (void*)this, NULL);
- cmp_initialized_ = true;
- DICTIONARY_ID dict_id = { .dictid = 1 };
- lt= ltm.get_lt(dict_id, cmp_, /* on_create_extra*/nullptr);
-}
RangeLockMgr::~RangeLockMgr() {
if (lt) {
ltm.release_lt(lt);
}
ltm.destroy();
- if (cmp_initialized_)
- cmp_.destroy();
+ cmp_.destroy();
}
uint64_t RangeLockMgr::get_escalation_count() {
@@ -1095,7 +1086,7 @@ uint64_t RangeLockMgr::get_escalation_count() {
struct LOCK_PRINT_CONTEXT {
- TransactionLockMgr::LockStatusData *data;
+ BaseLockMgr::LockStatusData *data;
// this will not be needed when locks are per-column-family:
uint32_t cfh_id;
};
@@ -1123,9 +1114,9 @@ void push_into_lock_status_data(void* param, const DBT *left,
}
-TransactionLockMgr::LockStatusData RangeLockMgr::GetLockStatusData() {
- TransactionLockMgr::LockStatusData data;
- LOCK_PRINT_CONTEXT ctx = {&data, GetColumnFamilyID(my_txn_db->DefaultColumnFamily()) };
+BaseLockMgr::LockStatusData RangeLockMgr::GetLockStatusData() {
+ LockStatusData data;
+ LOCK_PRINT_CONTEXT ctx = {&data, GetColumnFamilyID(my_txn_db_->DefaultColumnFamily()) };
lt->dump_locks((void*)&ctx, push_into_lock_status_data);
return data;
}
diff --git a/utilities/transactions/transaction_lock_mgr.h b/utilities/transactions/transaction_lock_mgr.h
index 9f73e5a07..e3de698f2 100644
--- a/utilities/transactions/transaction_lock_mgr.h
+++ b/utilities/transactions/transaction_lock_mgr.h
@@ -73,11 +73,17 @@ class BaseLockMgr {
void UnLock(PessimisticTransaction* txn, uint32_t column_family_id,
const std::string& key, Env* env)=0;
+ // Resize the deadlock info buffer
+ virtual void Resize(uint32_t)=0;
+ virtual std::vector<DeadlockPath> GetDeadlockInfoBuffer()= 0;
virtual ~BaseLockMgr(){}
+
+ using LockStatusData = std::unordered_multimap<uint32_t, KeyLockInfo>;
+ virtual LockStatusData GetLockStatusData()=0;
};
-class TransactionLockMgr {
+class TransactionLockMgr : public BaseLockMgr {
public:
TransactionLockMgr(TransactionDB* txn_db, size_t default_num_stripes,
int64_t max_num_locks, uint32_t max_num_deadlocks,
@@ -105,10 +111,9 @@ class TransactionLockMgr {
void UnLock(PessimisticTransaction* txn, uint32_t column_family_id,
const std::string& key, Env* env);
- using LockStatusData = std::unordered_multimap<uint32_t, KeyLockInfo>;
- LockStatusData GetLockStatusData();
- std::vector<DeadlockPath> GetDeadlockInfoBuffer();
- void Resize(uint32_t);
+ LockStatusData GetLockStatusData() override;
+ std::vector<DeadlockPath> GetDeadlockInfoBuffer() override;
+ void Resize(uint32_t) override;
private:
PessimisticTransactionDB* txn_db_impl_;
@@ -196,6 +201,12 @@ class RangeLockMgr :
Status TryLock(PessimisticTransaction* txn, uint32_t column_family_id,
const std::string& key, Env* env, bool exclusive) override ;
+ // Resize the deadlock-info buffer, does nothing currently
+ void Resize(uint32_t) override {}
+ std::vector<DeadlockPath> GetDeadlockInfoBuffer() override {
+ return std::vector<DeadlockPath>();
+ };
+
// Get a lock on a range
// (TODO: this allows to acquire exclusive range locks although they are not
// used ATM)
@@ -216,6 +227,7 @@ class RangeLockMgr :
const std::string& key, Env* env) override ;
RangeLockMgr(TransactionDB* txn_db,
+ const RangeLockingOptions& opts,
std::shared_ptr<TransactionDBMutexFactory> mutex_factory);
~RangeLockMgr();
@@ -226,20 +238,16 @@ class RangeLockMgr :
uint64_t get_escalation_count() override;
- TransactionLockMgr::LockStatusData GetLockStatusData();
-
- using RangeLockMgrControl::convert_key_to_endpoint_func;
- using RangeLockMgrControl::compare_endpoints_func;
+ LockStatusData GetLockStatusData() override;
- void set_endpoint_cmp_functions(convert_key_to_endpoint_func cvt_func,
- compare_endpoints_func cmp_func) override;
+ typedef RangeLockingOptions::convert_key_to_endpoint_func convert_key_to_endpoint_func;
+ typedef RangeLockingOptions::compare_endpoints_func compare_endpoints_func;
private:
toku::locktree_manager ltm;
toku::locktree *lt; // only one tree for now
toku::comparator cmp_;
- bool cmp_initialized_;
// Convert rowkey to endpoint (TODO: shouldn't "rowkey=const" translate into
// a pair of [start; end] endpoints in general? They translate into the same
@@ -249,7 +257,7 @@ class RangeLockMgr :
// User-provided endpoint comparison function
compare_endpoints_func compare_endpoints;
- TransactionDB* my_txn_db;
+ TransactionDB* my_txn_db_;
std::shared_ptr<TransactionDBMutexFactory> mutex_factory_;
static int compare_dbt_endpoints(__toku_db*, void *arg, const DBT *a_key, const DBT *b_key);
1
0
[Commits] 20b444a: MDEV-7974 XA PREPARE-d transactions survive disconnect/server restart.
by holyfootï¼ askmonty.org 30 Mar '19
by holyfootï¼ askmonty.org 30 Mar '19
30 Mar '19
revision-id: 20b444ad535c058ce90d8195fbd2261cb6609dc4 (mariadb-10.4.3-79-g20b444a)
parent(s): 09b2d37a3227a559aa157fc4a5da5fd6b2011e40
committer: Alexey Botchkov
timestamp: 2019-03-31 01:47:28 +0400
message:
MDEV-7974 XA PREPARE-d transactions survive disconnect/server restart.
trans_detach() implemented to survive disconnect.
XA_prepare_log_event added to reflect XA PREPARE in the binlog.
---
mysql-test/include/kill_and_restart_mysqld.inc | 15 +
mysql-test/main/xa.result | 10 +
mysql-test/main/xa.test | 3 +
.../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_prepared.result | 1042 +++++++++++++++++
.../binlog/r/binlog_xa_prepared_disconnect.result | 1175 ++++++++++++++++++++
mysql-test/suite/binlog/t/binlog_xa_prepared.test | 106 ++
.../binlog/t/binlog_xa_prepared_disconnect.test | 13 +
.../suite/rpl/include/rpl_connection_master.inc | 2 +
.../suite/rpl/include/rpl_connection_slave.inc | 2 +
mysql-test/suite/rpl/r/rpl_xa.result | 62 ++
.../rpl_xa_survive_disconnect_mixed_engines.result | 46 +
mysql-test/suite/rpl/t/rpl_xa.test | 46 +
.../t/rpl_xa_survive_disconnect_mixed_engines.test | 80 ++
sql/handler.cc | 10 +-
sql/handler.h | 25 +
sql/log.cc | 94 +-
sql/log.h | 10 +
sql/log_event.cc | 501 +++++++--
sql/log_event.h | 119 +-
sql/sql_class.cc | 21 +-
sql/sql_class.h | 52 +-
sql/sql_connect.cc | 1 +
sql/sql_parse.cc | 3 +
sql/transaction.cc | 232 +++-
sql/transaction.h | 2 +
storage/innobase/handler/ha_innodb.cc | 46 +-
storage/innobase/trx/trx0trx.cc | 16 +-
30 files changed, 3990 insertions(+), 133 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 0000000..b67fb73
--- /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/xa.result b/mysql-test/main/xa.result
index f77c0af..a609b68 100644
--- a/mysql-test/main/xa.result
+++ b/mysql-test/main/xa.result
@@ -66,6 +66,7 @@ xa end 'tr1';
xa prepare 'tr1';
xa recover format='SQL';
formatID gtrid_length bqual_length data
+11 5 5 X'7465737462',X'2030405060',11
1 3 0 'tr1'
xa rollback 'tr1';
xa start 'tr1', 'bq';
@@ -75,6 +76,7 @@ xa prepare 'tr1', 'bq';
xa recover format='SQL';
formatID gtrid_length bqual_length data
1 3 2 'tr1','bq'
+11 5 5 X'7465737462',X'2030405060',11
xa rollback 'tr1', 'bq';
xa start 'tr1', 'bq', 3;
insert t1 values (40);
@@ -83,6 +85,7 @@ xa prepare 'tr1', 'bq', 3;
xa recover format='SQL';
formatID gtrid_length bqual_length data
3 3 2 'tr1','bq',3
+11 5 5 X'7465737462',X'2030405060',11
xa rollback 'tr1', 'bq', 3;
xa start 'tr1#$';
insert t1 values (40);
@@ -90,6 +93,7 @@ xa end 'tr1#$';
xa prepare 'tr1#$';
xa recover format='SQL';
formatID gtrid_length bqual_length data
+11 5 5 X'7465737462',X'2030405060',11
1 5 0 X'7472312324'
xa rollback 'tr1#$';
xa start 'tr1#$', 'bq';
@@ -98,6 +102,7 @@ xa end 'tr1#$', 'bq';
xa prepare 'tr1#$', 'bq';
xa recover format='SQL';
formatID gtrid_length bqual_length data
+11 5 5 X'7465737462',X'2030405060',11
1 5 2 X'7472312324',X'6271'
xa rollback 'tr1#$', 'bq';
xa start 'tr1#$', 'bq', 3;
@@ -106,11 +111,13 @@ xa end 'tr1#$', 'bq', 3;
xa prepare 'tr1#$', 'bq', 3;
xa recover format='RAW';
formatID gtrid_length bqual_length data
+11 5 5 testb 0@P`
3 5 2 tr1#$bq
xa recover format='PLAIN';
ERROR HY000: Unknown XA RECOVER format name: 'PLAIN'
xa recover format='SQL';
formatID gtrid_length bqual_length data
+11 5 5 X'7465737462',X'2030405060',11
3 5 2 X'7472312324',X'6271',3
xa rollback 'tr1#$', 'bq', 3;
drop table t1;
@@ -345,3 +352,6 @@ connection default;
XA END 'xid1';
XA ROLLBACK 'xid1';
DROP TABLE t1, t2, t3;
+xa rollback 'testb',0x2030405060,11;
+XA RECOVER;
+formatID gtrid_length bqual_length data
diff --git a/mysql-test/main/xa.test b/mysql-test/main/xa.test
index 176ef6a..7a5cc38 100644
--- a/mysql-test/main/xa.test
+++ b/mysql-test/main/xa.test
@@ -476,5 +476,8 @@ XA END 'xid1';
XA ROLLBACK 'xid1';
DROP TABLE t1, t2, t3;
+xa rollback 'testb',0x2030405060,11;
+XA RECOVER;
+
--source include/wait_until_count_sessions.inc
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 0000000..c0041af
--- /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 0000000..1f6ce71
--- /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 0000000..bfda185
--- /dev/null
+++ b/mysql-test/suite/binlog/include/binlog_xa_prepared_do_and_restart.inc
@@ -0,0 +1,323 @@
+#
+# This file creates various kinds of prepared XA transactions,
+# manipulates their connection state and examines how their prepared
+# status behave while the transaction is disconnected, killed or
+# the server kisses it shutdown.
+# The file can be sourced multiple times
+# param $restart_number (as the number of inclusion) adjusts
+# verification logics.
+#
+# param [in] $conn_number Total number of connection each performing
+# one insert into table.
+# param [in] $commit_number Number of commits from either.
+# side of the server restart.
+# param [in] $rollback_number The same as the above just for rollback.
+# param [in] $term_number Number of transaction that are terminated
+# before server restarts
+# param [in] $killed_number Instead of disconnect make some
+# connections killed when their
+# transactions got prepared.
+# param [in] $server_disconn_number Make some connections disconnected
+# by shutdown rather than actively
+# param [in] $post_restart_conn_number Number a "warmup" connection
+# after server restart, they all commit
+# param [out] restart_number Counter to be incremented at the end of the test
+#
+
+# The test consists of three sections:
+# I. Corner cases check
+# II. Regular case check
+# III. Post server-restart verification
+
+
+#
+# I. Corner cases of
+#
+# A. XA with an update to a temp table
+# B. XA with SELECT
+# C. XA empty
+# Demonstrate their XA status upon prepare and how they react on disconnect and
+# shutdown.
+# In each of A,B,C three prepared transactions are set up.
+# trx1 is for disconnection, trx2 for shutdown, trx3 for being killed.
+# The A case additionally contains some XA prohibited state transaction check.
+#
+# D. Prove that not prepared XA remains to be cleared out by disconnection.
+#
+
+#
+# A. The temp table only prepared XA recovers only formally to
+# let post recovery XA COMMIT or XA ROLLBACK with no effect.
+
+--let $type = tmp
+--let $index = 1
+--let $sql_init1 = SET @@sql_log_bin = OFF
+--let $sql_init2 = CREATE TEMPORARY TABLE tmp$index (a int) ENGINE=innodb
+--let $sql_doit = INSERT INTO tmp$index SET a=$index
+--source include/binlog_xa_prepare_connection.inc
+
+--let $index = 2
+--source include/binlog_xa_prepare_connection.inc
+
+--let $index = 3
+--source include/binlog_xa_prepare_connection.inc
+--let $conn3_id=`SELECT connection_id()`
+
+#
+# Various prohibited XA state changes to test here:
+#
+
+--connection default
+# Stealing is not allowed
+--error ER_XAER_NOTA
+--eval XA COMMIT 'trx1$type'
+--error ER_XAER_NOTA
+--eval XA ROLLBACK 'trx1$type'
+
+# Before disconnect: creating a duplicate is not allowed
+--error ER_XAER_DUPID
+--eval XA START 'trx1$type'
+
+# Manipulate now the prepared transactions.
+# Two to terminate, one to leave out.
+--let $terminate_with = XA COMMIT
+--let $num_trx_prepared = $index
+--source include/binlog_xa_prepare_disconnect.inc
+
+#
+# B. "Read-only" (select) prepared XA recovers only formally to
+# let post recovery XA COMMIT or XA ROLLBACK with no effect.
+#
+--let $type=ro
+--let $index = 1
+--let $sql_init1 =
+--let $sql_init2 =
+--let $sql_doit = SELECT * from t ORDER BY a
+--source include/binlog_xa_prepare_connection.inc
+
+--let $index = 2
+--source include/binlog_xa_prepare_connection.inc
+
+--let $index = 3
+--source include/binlog_xa_prepare_connection.inc
+--let $conn3_id=`SELECT connection_id()`
+
+--let $terminate_with = XA ROLLBACK
+# two three above section prepared transaction were terminated.
+--inc $num_trx_prepared
+--source include/binlog_xa_prepare_disconnect.inc
+
+#
+# C. Empty prepared XA recovers only formally to
+# let post recovery XA COMMIT or XA ROLLBACK with no effect.
+#
+--let $type=empty
+--let $index = 1
+--let $sql_init1 =
+--let $sql_init2 =
+--let $sql_doit =
+--source include/binlog_xa_prepare_connection.inc
+
+--let $index = 2
+--source include/binlog_xa_prepare_connection.inc
+
+--let $index = 3
+--source include/binlog_xa_prepare_connection.inc
+--let $conn3_id=`SELECT connection_id()`
+
+--let $terminate_with = XA COMMIT
+--inc $num_trx_prepared
+--source include/binlog_xa_prepare_disconnect.inc
+
+#
+# D. Not prepared XA disconnects to be cleared out,
+# no effect on data left as well.
+# Few more prohibited XA state transactions is checked out.
+#
+--let $type=unprepared
+--let $prev_count=`SELECT count(*) from t`
+
+--connect(conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,)
+--eval XA START 'trx1$type'
+INSERT INTO t set a=0;
+--eval XA END 'trx1$type'
+
+--error ER_XAER_RMFAIL
+INSERT INTO t set a=0;
+--error ER_XAER_RMFAIL
+--eval XA START 'trx1$type'
+--error ER_XAER_RMFAIL
+--eval XA START 'trx1$type'
+
+--disconnect conn1$type
+
+--connection default
+# No such transactions
+--error ER_XAER_NOTA
+--eval XA COMMIT 'trx1$type'
+if (`SELECT count(*) > $prev_count from t`)
+{
+ --echo *** Unexpected commit to the table. ***
+ --die
+}
+
+#
+# II. Regular case.
+#
+# Prepared transactions get disconnected in three ways:
+# actively, being killed and by the server shutdown.
+#
+--let $i=0
+while ($i < $conn_number)
+{
+ --connect (conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,)
+ --let $conn_id=`SELECT connection_id()`
+ --disable_reconnect
+ SET @@binlog_format = STATEMENT;
+ if (`SELECT $i % 2`)
+ {
+ SET @@binlog_format = ROW;
+ }
+ --eval XA START 'trx_$i'
+ --eval INSERT INTO t SET a=$i
+ --eval XA END 'trx_$i'
+ --eval XA PREPARE 'trx_$i'
+
+ --let $disc_via_kill=`SELECT $conn_number - $i <= $killed_number`
+ if (!$disc_via_kill)
+ {
+ --let $disc_via_shutdown=`SELECT $conn_number - $i <= $killed_number + $server_disconn_number`
+ if (!$disc_via_shutdown)
+ {
+ --disconnect conn$i
+ }
+ }
+ if ($disc_via_kill)
+ {
+ --connection default
+ --replace_result $conn_id CONN_ID
+ --eval KILL CONNECTION $conn_id
+ }
+
+ if (!$disc_via_shutdown)
+ {
+ --connection default
+ --let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
+ --source include/wait_condition.inc
+ }
+ --inc $i
+}
+
+# [0, $rollback_number - 1] are rolled back now
+--connection default
+
+--let $i=0
+while ($i < $rollback_number)
+{
+ --eval XA ROLLBACK 'trx_$i'
+
+ --inc $i
+}
+
+# [$rollback_number, $rollback_number + $commit_number - 1] get committed
+while ($i < $term_number)
+{
+ --eval XA COMMIT 'trx_$i'
+
+ --inc $i
+}
+
+--source include/$how_to_restart
+
+#
+# III. Post server-restart verification.
+# It concludes survived XA:s with a number of commits and rollbacks
+# as configured in the 1st part to check expected results in the end.
+# Cleanup section consists of explicit disconnect (for killed, or
+# not disconnected before shutdown).
+#
+
+# New XA can be prepared and committed
+--let $k = 0
+while ($k < $post_restart_conn_number)
+{
+ --connect (conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,)
+ --let $conn_id=`SELECT connection_id()`
+ --eval XA START 'new_trx_$k'
+ --eval INSERT INTO t SET a=$k
+ --eval XA END 'new_trx_$k'
+ --eval XA PREPARE 'new_trx_$k'
+
+ --disconnect conn_restart_$k
+
+ --connection default
+ --let $wait_condition= SELECT count(*) = 0 FROM v_processlist WHERE PROCESSLIST_ID = $conn_id
+ --source include/wait_condition.inc
+
+ --inc $k
+}
+
+--connection default
+--let $k = 0
+while ($k < $post_restart_conn_number)
+{
+ --eval XA COMMIT 'new_trx_$k'
+ --inc $k
+}
+
+#
+# Symmetrically to the pre-restart, the resurrected trx:s are committed
+# [$term_number, $term_number + $commit_number - 1]
+# and the rest is rolled back.
+#
+--let $i = $term_number
+
+while ($i < `SELECT $term_number + $commit_number`)
+{
+ # Expected to fail
+ --error ER_XAER_DUPID
+ --eval XA START 'trx_$i'
+ --eval XA COMMIT 'trx_$i'
+ --inc $i
+}
+
+while ($i < $conn_number)
+{
+ # Expected to fail
+ --error ER_XAER_DUPID
+ --eval XA START 'trx_$i'
+ --eval XA ROLLBACK 'trx_$i'
+ --inc $i
+}
+
+#
+# Verification of correct results of recovered XA transaction handling:
+#
+SELECT * FROM t;
+
+--let $type=tmp
+--disconnect conn2$type
+--disconnect conn3$type
+--let $type=ro
+--disconnect conn2$type
+--disconnect conn3$type
+--let $type=empty
+--disconnect conn2$type
+--disconnect conn3$type
+
+--let $i= $conn_number
+--let $k= 0
+--let $expl_disconn_number = `SELECT $killed_number + $server_disconn_number`
+while ($k < $expl_disconn_number)
+{
+ --connection default
+ --error ER_XAER_NOTA
+ --eval XA ROLLBACK 'trx_$i'
+
+ --dec $i
+ --disconnect conn$i
+
+ --inc $k
+}
+
+--inc $restart_number
diff --git a/mysql-test/suite/binlog/r/binlog_xa_prepared.result b/mysql-test/suite/binlog/r/binlog_xa_prepared.result
new file mode 100644
index 0000000..9635e02
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_xa_prepared.result
@@ -0,0 +1,1042 @@
+connection default;
+CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
+call mtr.add_suppression("Found 10 prepared XA transactions");
+CREATE TABLE t (a INT) ENGINE=innodb;
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx1tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx1tmp';
+XA PREPARE 'trx1tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx2tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx2tmp';
+XA PREPARE 'trx2tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx3tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx3tmp';
+XA PREPARE 'trx3tmp';
+connection default;
+XA COMMIT 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA ROLLBACK 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA START 'trx1tmp';
+ERROR XAE08: XAER_DUPID: The XID already exists
+connection default;
+*** 3 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1tmp;
+disconnect conn1tmp;
+connection default;
+XA COMMIT 'trx1tmp';
+KILL connection CONN_ID;
+XA COMMIT 'trx3tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1ro';
+SELECT * from t ORDER BY a;
+a
+XA END 'trx1ro';
+XA PREPARE 'trx1ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2ro';
+SELECT * from t ORDER BY a;
+a
+XA END 'trx2ro';
+XA PREPARE 'trx2ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3ro';
+SELECT * from t ORDER BY a;
+a
+XA END 'trx3ro';
+XA PREPARE 'trx3ro';
+connection default;
+*** 4 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1ro;
+disconnect conn1ro;
+connection default;
+XA ROLLBACK 'trx1ro';
+KILL connection CONN_ID;
+XA ROLLBACK 'trx3ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1empty';
+XA END 'trx1empty';
+XA PREPARE 'trx1empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2empty';
+XA END 'trx2empty';
+XA PREPARE 'trx2empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3empty';
+XA END 'trx3empty';
+XA PREPARE 'trx3empty';
+connection default;
+*** 5 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1empty;
+disconnect conn1empty;
+connection default;
+XA COMMIT 'trx1empty';
+KILL connection CONN_ID;
+XA COMMIT 'trx3empty';
+connect conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1unprepared';
+INSERT INTO t set a=0;
+XA END 'trx1unprepared';
+INSERT INTO t set a=0;
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+disconnect conn1unprepared;
+connection default;
+XA COMMIT 'trx1unprepared';
+ERROR XAE04: XAER_NOTA: Unknown XID
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_0';
+INSERT INTO t SET a=0;
+XA END 'trx_0';
+XA PREPARE 'trx_0';
+disconnect conn0;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_1';
+INSERT INTO t SET a=1;
+XA END 'trx_1';
+XA PREPARE 'trx_1';
+disconnect conn1;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_2';
+INSERT INTO t SET a=2;
+XA END 'trx_2';
+XA PREPARE 'trx_2';
+disconnect conn2;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_3';
+INSERT INTO t SET a=3;
+XA END 'trx_3';
+XA PREPARE 'trx_3';
+disconnect conn3;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_4';
+INSERT INTO t SET a=4;
+XA END 'trx_4';
+XA PREPARE 'trx_4';
+disconnect conn4;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_5';
+INSERT INTO t SET a=5;
+XA END 'trx_5';
+XA PREPARE 'trx_5';
+disconnect conn5;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_6';
+INSERT INTO t SET a=6;
+XA END 'trx_6';
+XA PREPARE 'trx_6';
+disconnect conn6;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_7';
+INSERT INTO t SET a=7;
+XA END 'trx_7';
+XA PREPARE 'trx_7';
+disconnect conn7;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_8';
+INSERT INTO t SET a=8;
+XA END 'trx_8';
+XA PREPARE 'trx_8';
+disconnect conn8;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_9';
+INSERT INTO t SET a=9;
+XA END 'trx_9';
+XA PREPARE 'trx_9';
+disconnect conn9;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_10';
+INSERT INTO t SET a=10;
+XA END 'trx_10';
+XA PREPARE 'trx_10';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_11';
+INSERT INTO t SET a=11;
+XA END 'trx_11';
+XA PREPARE 'trx_11';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_12';
+INSERT INTO t SET a=12;
+XA END 'trx_12';
+XA PREPARE 'trx_12';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_13';
+INSERT INTO t SET a=13;
+XA END 'trx_13';
+XA PREPARE 'trx_13';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_14';
+INSERT INTO t SET a=14;
+XA END 'trx_14';
+XA PREPARE 'trx_14';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_15';
+INSERT INTO t SET a=15;
+XA END 'trx_15';
+XA PREPARE 'trx_15';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_16';
+INSERT INTO t SET a=16;
+XA END 'trx_16';
+XA PREPARE 'trx_16';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_17';
+INSERT INTO t SET a=17;
+XA END 'trx_17';
+XA PREPARE 'trx_17';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_18';
+INSERT INTO t SET a=18;
+XA END 'trx_18';
+XA PREPARE 'trx_18';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_19';
+INSERT INTO t SET a=19;
+XA END 'trx_19';
+XA PREPARE 'trx_19';
+connection default;
+KILL CONNECTION CONN_ID;
+connection default;
+XA ROLLBACK 'trx_0';
+XA ROLLBACK 'trx_1';
+XA ROLLBACK 'trx_2';
+XA ROLLBACK 'trx_3';
+XA ROLLBACK 'trx_4';
+XA COMMIT 'trx_5';
+XA COMMIT 'trx_6';
+XA COMMIT 'trx_7';
+XA COMMIT 'trx_8';
+XA COMMIT 'trx_9';
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_0';
+INSERT INTO t SET a=0;
+XA END 'new_trx_0';
+XA PREPARE 'new_trx_0';
+disconnect conn_restart_0;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_1';
+INSERT INTO t SET a=1;
+XA END 'new_trx_1';
+XA PREPARE 'new_trx_1';
+disconnect conn_restart_1;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_2';
+INSERT INTO t SET a=2;
+XA END 'new_trx_2';
+XA PREPARE 'new_trx_2';
+disconnect conn_restart_2;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_3';
+INSERT INTO t SET a=3;
+XA END 'new_trx_3';
+XA PREPARE 'new_trx_3';
+disconnect conn_restart_3;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_4';
+INSERT INTO t SET a=4;
+XA END 'new_trx_4';
+XA PREPARE 'new_trx_4';
+disconnect conn_restart_4;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_5';
+INSERT INTO t SET a=5;
+XA END 'new_trx_5';
+XA PREPARE 'new_trx_5';
+disconnect conn_restart_5;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_6';
+INSERT INTO t SET a=6;
+XA END 'new_trx_6';
+XA PREPARE 'new_trx_6';
+disconnect conn_restart_6;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_7';
+INSERT INTO t SET a=7;
+XA END 'new_trx_7';
+XA PREPARE 'new_trx_7';
+disconnect conn_restart_7;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_8';
+INSERT INTO t SET a=8;
+XA END 'new_trx_8';
+XA PREPARE 'new_trx_8';
+disconnect conn_restart_8;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_9';
+INSERT INTO t SET a=9;
+XA END 'new_trx_9';
+XA PREPARE 'new_trx_9';
+disconnect conn_restart_9;
+connection default;
+connection default;
+XA COMMIT 'new_trx_0';
+XA COMMIT 'new_trx_1';
+XA COMMIT 'new_trx_2';
+XA COMMIT 'new_trx_3';
+XA COMMIT 'new_trx_4';
+XA COMMIT 'new_trx_5';
+XA COMMIT 'new_trx_6';
+XA COMMIT 'new_trx_7';
+XA COMMIT 'new_trx_8';
+XA COMMIT 'new_trx_9';
+XA START 'trx_10';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_10';
+XA START 'trx_11';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_11';
+XA START 'trx_12';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_12';
+XA START 'trx_13';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_13';
+XA START 'trx_14';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_14';
+XA START 'trx_15';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_15';
+XA START 'trx_16';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_16';
+XA START 'trx_17';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_17';
+XA START 'trx_18';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_18';
+XA START 'trx_19';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_19';
+SELECT * FROM t;
+a
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+disconnect conn2tmp;
+disconnect conn3tmp;
+disconnect conn2ro;
+disconnect conn3ro;
+disconnect conn2empty;
+disconnect conn3empty;
+connection default;
+XA ROLLBACK 'trx_20';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn19;
+connection default;
+XA ROLLBACK 'trx_19';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn18;
+connection default;
+XA ROLLBACK 'trx_18';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn17;
+connection default;
+XA ROLLBACK 'trx_17';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn16;
+connection default;
+XA ROLLBACK 'trx_16';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn15;
+connection default;
+XA ROLLBACK 'trx_15';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn14;
+connection default;
+XA ROLLBACK 'trx_14';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn13;
+connection default;
+XA ROLLBACK 'trx_13';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn12;
+connection default;
+XA ROLLBACK 'trx_12';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn11;
+connection default;
+XA ROLLBACK 'trx_11';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn10;
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx1tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx1tmp';
+XA PREPARE 'trx1tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx2tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx2tmp';
+XA PREPARE 'trx2tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx3tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx3tmp';
+XA PREPARE 'trx3tmp';
+connection default;
+XA COMMIT 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA ROLLBACK 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA START 'trx1tmp';
+ERROR XAE08: XAER_DUPID: The XID already exists
+connection default;
+*** 3 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1tmp;
+disconnect conn1tmp;
+connection default;
+XA COMMIT 'trx1tmp';
+KILL connection CONN_ID;
+XA COMMIT 'trx3tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1ro';
+SELECT * from t ORDER BY a;
+a
+0
+1
+2
+3
+4
+5
+5
+6
+6
+7
+7
+8
+8
+9
+9
+10
+11
+12
+13
+14
+XA END 'trx1ro';
+XA PREPARE 'trx1ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2ro';
+SELECT * from t ORDER BY a;
+a
+0
+1
+2
+3
+4
+5
+5
+6
+6
+7
+7
+8
+8
+9
+9
+10
+11
+12
+13
+14
+XA END 'trx2ro';
+XA PREPARE 'trx2ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3ro';
+SELECT * from t ORDER BY a;
+a
+0
+1
+2
+3
+4
+5
+5
+6
+6
+7
+7
+8
+8
+9
+9
+10
+11
+12
+13
+14
+XA END 'trx3ro';
+XA PREPARE 'trx3ro';
+connection default;
+*** 4 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1ro;
+disconnect conn1ro;
+connection default;
+XA ROLLBACK 'trx1ro';
+KILL connection CONN_ID;
+XA ROLLBACK 'trx3ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1empty';
+XA END 'trx1empty';
+XA PREPARE 'trx1empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2empty';
+XA END 'trx2empty';
+XA PREPARE 'trx2empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3empty';
+XA END 'trx3empty';
+XA PREPARE 'trx3empty';
+connection default;
+*** 5 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1empty;
+disconnect conn1empty;
+connection default;
+XA COMMIT 'trx1empty';
+KILL connection CONN_ID;
+XA COMMIT 'trx3empty';
+connect conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1unprepared';
+INSERT INTO t set a=0;
+XA END 'trx1unprepared';
+INSERT INTO t set a=0;
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+disconnect conn1unprepared;
+connection default;
+XA COMMIT 'trx1unprepared';
+ERROR XAE04: XAER_NOTA: Unknown XID
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_0';
+INSERT INTO t SET a=0;
+XA END 'trx_0';
+XA PREPARE 'trx_0';
+disconnect conn0;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_1';
+INSERT INTO t SET a=1;
+XA END 'trx_1';
+XA PREPARE 'trx_1';
+disconnect conn1;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_2';
+INSERT INTO t SET a=2;
+XA END 'trx_2';
+XA PREPARE 'trx_2';
+disconnect conn2;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_3';
+INSERT INTO t SET a=3;
+XA END 'trx_3';
+XA PREPARE 'trx_3';
+disconnect conn3;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_4';
+INSERT INTO t SET a=4;
+XA END 'trx_4';
+XA PREPARE 'trx_4';
+disconnect conn4;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_5';
+INSERT INTO t SET a=5;
+XA END 'trx_5';
+XA PREPARE 'trx_5';
+disconnect conn5;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_6';
+INSERT INTO t SET a=6;
+XA END 'trx_6';
+XA PREPARE 'trx_6';
+disconnect conn6;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_7';
+INSERT INTO t SET a=7;
+XA END 'trx_7';
+XA PREPARE 'trx_7';
+disconnect conn7;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_8';
+INSERT INTO t SET a=8;
+XA END 'trx_8';
+XA PREPARE 'trx_8';
+disconnect conn8;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_9';
+INSERT INTO t SET a=9;
+XA END 'trx_9';
+XA PREPARE 'trx_9';
+disconnect conn9;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_10';
+INSERT INTO t SET a=10;
+XA END 'trx_10';
+XA PREPARE 'trx_10';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_11';
+INSERT INTO t SET a=11;
+XA END 'trx_11';
+XA PREPARE 'trx_11';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_12';
+INSERT INTO t SET a=12;
+XA END 'trx_12';
+XA PREPARE 'trx_12';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_13';
+INSERT INTO t SET a=13;
+XA END 'trx_13';
+XA PREPARE 'trx_13';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_14';
+INSERT INTO t SET a=14;
+XA END 'trx_14';
+XA PREPARE 'trx_14';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_15';
+INSERT INTO t SET a=15;
+XA END 'trx_15';
+XA PREPARE 'trx_15';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_16';
+INSERT INTO t SET a=16;
+XA END 'trx_16';
+XA PREPARE 'trx_16';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_17';
+INSERT INTO t SET a=17;
+XA END 'trx_17';
+XA PREPARE 'trx_17';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_18';
+INSERT INTO t SET a=18;
+XA END 'trx_18';
+XA PREPARE 'trx_18';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_19';
+INSERT INTO t SET a=19;
+XA END 'trx_19';
+XA PREPARE 'trx_19';
+connection default;
+KILL CONNECTION CONN_ID;
+connection default;
+XA ROLLBACK 'trx_0';
+XA ROLLBACK 'trx_1';
+XA ROLLBACK 'trx_2';
+XA ROLLBACK 'trx_3';
+XA ROLLBACK 'trx_4';
+XA COMMIT 'trx_5';
+XA COMMIT 'trx_6';
+XA COMMIT 'trx_7';
+XA COMMIT 'trx_8';
+XA COMMIT 'trx_9';
+# Kill and restart
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_0';
+INSERT INTO t SET a=0;
+XA END 'new_trx_0';
+XA PREPARE 'new_trx_0';
+disconnect conn_restart_0;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_1';
+INSERT INTO t SET a=1;
+XA END 'new_trx_1';
+XA PREPARE 'new_trx_1';
+disconnect conn_restart_1;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_2';
+INSERT INTO t SET a=2;
+XA END 'new_trx_2';
+XA PREPARE 'new_trx_2';
+disconnect conn_restart_2;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_3';
+INSERT INTO t SET a=3;
+XA END 'new_trx_3';
+XA PREPARE 'new_trx_3';
+disconnect conn_restart_3;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_4';
+INSERT INTO t SET a=4;
+XA END 'new_trx_4';
+XA PREPARE 'new_trx_4';
+disconnect conn_restart_4;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_5';
+INSERT INTO t SET a=5;
+XA END 'new_trx_5';
+XA PREPARE 'new_trx_5';
+disconnect conn_restart_5;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_6';
+INSERT INTO t SET a=6;
+XA END 'new_trx_6';
+XA PREPARE 'new_trx_6';
+disconnect conn_restart_6;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_7';
+INSERT INTO t SET a=7;
+XA END 'new_trx_7';
+XA PREPARE 'new_trx_7';
+disconnect conn_restart_7;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_8';
+INSERT INTO t SET a=8;
+XA END 'new_trx_8';
+XA PREPARE 'new_trx_8';
+disconnect conn_restart_8;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_9';
+INSERT INTO t SET a=9;
+XA END 'new_trx_9';
+XA PREPARE 'new_trx_9';
+disconnect conn_restart_9;
+connection default;
+connection default;
+XA COMMIT 'new_trx_0';
+XA COMMIT 'new_trx_1';
+XA COMMIT 'new_trx_2';
+XA COMMIT 'new_trx_3';
+XA COMMIT 'new_trx_4';
+XA COMMIT 'new_trx_5';
+XA COMMIT 'new_trx_6';
+XA COMMIT 'new_trx_7';
+XA COMMIT 'new_trx_8';
+XA COMMIT 'new_trx_9';
+XA START 'trx_10';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_10';
+XA START 'trx_11';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_11';
+XA START 'trx_12';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_12';
+XA START 'trx_13';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_13';
+XA START 'trx_14';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_14';
+XA START 'trx_15';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_15';
+XA START 'trx_16';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_16';
+XA START 'trx_17';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_17';
+XA START 'trx_18';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_18';
+XA START 'trx_19';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_19';
+SELECT * FROM t;
+a
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+disconnect conn2tmp;
+disconnect conn3tmp;
+disconnect conn2ro;
+disconnect conn3ro;
+disconnect conn2empty;
+disconnect conn3empty;
+connection default;
+XA ROLLBACK 'trx_20';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn19;
+connection default;
+XA ROLLBACK 'trx_19';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn18;
+connection default;
+XA ROLLBACK 'trx_18';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn17;
+connection default;
+XA ROLLBACK 'trx_17';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn16;
+connection default;
+XA ROLLBACK 'trx_16';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn15;
+connection default;
+XA ROLLBACK 'trx_15';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn14;
+connection default;
+XA ROLLBACK 'trx_14';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn13;
+connection default;
+XA ROLLBACK 'trx_13';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn12;
+connection default;
+XA ROLLBACK 'trx_12';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn11;
+connection default;
+XA ROLLBACK 'trx_11';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn10;
+connection default;
+XA START 'one_phase_trx_0';
+INSERT INTO t SET a=0;
+XA END 'one_phase_trx_0';
+XA COMMIT 'one_phase_trx_0' ONE PHASE;
+XA START 'one_phase_trx_1';
+INSERT INTO t SET a=1;
+XA END 'one_phase_trx_1';
+XA COMMIT 'one_phase_trx_1' ONE PHASE;
+XA START 'one_phase_trx_2';
+INSERT INTO t SET a=2;
+XA END 'one_phase_trx_2';
+XA COMMIT 'one_phase_trx_2' ONE PHASE;
+XA START 'one_phase_trx_3';
+INSERT INTO t SET a=3;
+XA END 'one_phase_trx_3';
+XA COMMIT 'one_phase_trx_3' ONE PHASE;
+XA START 'one_phase_trx_4';
+INSERT INTO t SET a=4;
+XA END 'one_phase_trx_4';
+XA COMMIT 'one_phase_trx_4' ONE PHASE;
+SELECT SUM(a) FROM t;
+SUM(a)
+290
+DROP TABLE t;
+DROP VIEW v_processlist;
+All transactions must be completed, to empty-list the following:
+XA RECOVER;
+formatID gtrid_length bqual_length data
diff --git a/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result b/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result
new file mode 100644
index 0000000..67eb089
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_xa_prepared_disconnect.result
@@ -0,0 +1,1175 @@
+connection default;
+RESET MASTER;
+CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
+call mtr.add_suppression("Found 10 prepared XA transactions");
+CREATE TABLE t (a INT) ENGINE=innodb;
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx1tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx1tmp';
+XA PREPARE 'trx1tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx2tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx2tmp';
+XA PREPARE 'trx2tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx3tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx3tmp';
+XA PREPARE 'trx3tmp';
+connection default;
+XA COMMIT 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA ROLLBACK 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA START 'trx1tmp';
+ERROR XAE08: XAER_DUPID: The XID already exists
+connection default;
+*** 3 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1tmp;
+disconnect conn1tmp;
+connection default;
+XA COMMIT 'trx1tmp';
+KILL connection CONN_ID;
+XA COMMIT 'trx3tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1ro';
+SELECT * from t ORDER BY a;
+a
+XA END 'trx1ro';
+XA PREPARE 'trx1ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2ro';
+SELECT * from t ORDER BY a;
+a
+XA END 'trx2ro';
+XA PREPARE 'trx2ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3ro';
+SELECT * from t ORDER BY a;
+a
+XA END 'trx3ro';
+XA PREPARE 'trx3ro';
+connection default;
+*** 4 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1ro;
+disconnect conn1ro;
+connection default;
+XA ROLLBACK 'trx1ro';
+KILL connection CONN_ID;
+XA ROLLBACK 'trx3ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1empty';
+XA END 'trx1empty';
+XA PREPARE 'trx1empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2empty';
+XA END 'trx2empty';
+XA PREPARE 'trx2empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3empty';
+XA END 'trx3empty';
+XA PREPARE 'trx3empty';
+connection default;
+*** 5 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1empty;
+disconnect conn1empty;
+connection default;
+XA COMMIT 'trx1empty';
+KILL connection CONN_ID;
+XA COMMIT 'trx3empty';
+connect conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1unprepared';
+INSERT INTO t set a=0;
+XA END 'trx1unprepared';
+INSERT INTO t set a=0;
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+disconnect conn1unprepared;
+connection default;
+XA COMMIT 'trx1unprepared';
+ERROR XAE04: XAER_NOTA: Unknown XID
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_0';
+INSERT INTO t SET a=0;
+XA END 'trx_0';
+XA PREPARE 'trx_0';
+disconnect conn0;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_1';
+INSERT INTO t SET a=1;
+XA END 'trx_1';
+XA PREPARE 'trx_1';
+disconnect conn1;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_2';
+INSERT INTO t SET a=2;
+XA END 'trx_2';
+XA PREPARE 'trx_2';
+disconnect conn2;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_3';
+INSERT INTO t SET a=3;
+XA END 'trx_3';
+XA PREPARE 'trx_3';
+disconnect conn3;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_4';
+INSERT INTO t SET a=4;
+XA END 'trx_4';
+XA PREPARE 'trx_4';
+disconnect conn4;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_5';
+INSERT INTO t SET a=5;
+XA END 'trx_5';
+XA PREPARE 'trx_5';
+disconnect conn5;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_6';
+INSERT INTO t SET a=6;
+XA END 'trx_6';
+XA PREPARE 'trx_6';
+disconnect conn6;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_7';
+INSERT INTO t SET a=7;
+XA END 'trx_7';
+XA PREPARE 'trx_7';
+disconnect conn7;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_8';
+INSERT INTO t SET a=8;
+XA END 'trx_8';
+XA PREPARE 'trx_8';
+disconnect conn8;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_9';
+INSERT INTO t SET a=9;
+XA END 'trx_9';
+XA PREPARE 'trx_9';
+disconnect conn9;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_10';
+INSERT INTO t SET a=10;
+XA END 'trx_10';
+XA PREPARE 'trx_10';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_11';
+INSERT INTO t SET a=11;
+XA END 'trx_11';
+XA PREPARE 'trx_11';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_12';
+INSERT INTO t SET a=12;
+XA END 'trx_12';
+XA PREPARE 'trx_12';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_13';
+INSERT INTO t SET a=13;
+XA END 'trx_13';
+XA PREPARE 'trx_13';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_14';
+INSERT INTO t SET a=14;
+XA END 'trx_14';
+XA PREPARE 'trx_14';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_15';
+INSERT INTO t SET a=15;
+XA END 'trx_15';
+XA PREPARE 'trx_15';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_16';
+INSERT INTO t SET a=16;
+XA END 'trx_16';
+XA PREPARE 'trx_16';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_17';
+INSERT INTO t SET a=17;
+XA END 'trx_17';
+XA PREPARE 'trx_17';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_18';
+INSERT INTO t SET a=18;
+XA END 'trx_18';
+XA PREPARE 'trx_18';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_19';
+INSERT INTO t SET a=19;
+XA END 'trx_19';
+XA PREPARE 'trx_19';
+connection default;
+KILL CONNECTION CONN_ID;
+connection default;
+XA ROLLBACK 'trx_0';
+XA ROLLBACK 'trx_1';
+XA ROLLBACK 'trx_2';
+XA ROLLBACK 'trx_3';
+XA ROLLBACK 'trx_4';
+XA COMMIT 'trx_5';
+XA COMMIT 'trx_6';
+XA COMMIT 'trx_7';
+XA COMMIT 'trx_8';
+XA COMMIT 'trx_9';
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_0';
+INSERT INTO t SET a=0;
+XA END 'new_trx_0';
+XA PREPARE 'new_trx_0';
+disconnect conn_restart_0;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_1';
+INSERT INTO t SET a=1;
+XA END 'new_trx_1';
+XA PREPARE 'new_trx_1';
+disconnect conn_restart_1;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_2';
+INSERT INTO t SET a=2;
+XA END 'new_trx_2';
+XA PREPARE 'new_trx_2';
+disconnect conn_restart_2;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_3';
+INSERT INTO t SET a=3;
+XA END 'new_trx_3';
+XA PREPARE 'new_trx_3';
+disconnect conn_restart_3;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_4';
+INSERT INTO t SET a=4;
+XA END 'new_trx_4';
+XA PREPARE 'new_trx_4';
+disconnect conn_restart_4;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_5';
+INSERT INTO t SET a=5;
+XA END 'new_trx_5';
+XA PREPARE 'new_trx_5';
+disconnect conn_restart_5;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_6';
+INSERT INTO t SET a=6;
+XA END 'new_trx_6';
+XA PREPARE 'new_trx_6';
+disconnect conn_restart_6;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_7';
+INSERT INTO t SET a=7;
+XA END 'new_trx_7';
+XA PREPARE 'new_trx_7';
+disconnect conn_restart_7;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_8';
+INSERT INTO t SET a=8;
+XA END 'new_trx_8';
+XA PREPARE 'new_trx_8';
+disconnect conn_restart_8;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_9';
+INSERT INTO t SET a=9;
+XA END 'new_trx_9';
+XA PREPARE 'new_trx_9';
+disconnect conn_restart_9;
+connection default;
+connection default;
+XA COMMIT 'new_trx_0';
+XA COMMIT 'new_trx_1';
+XA COMMIT 'new_trx_2';
+XA COMMIT 'new_trx_3';
+XA COMMIT 'new_trx_4';
+XA COMMIT 'new_trx_5';
+XA COMMIT 'new_trx_6';
+XA COMMIT 'new_trx_7';
+XA COMMIT 'new_trx_8';
+XA COMMIT 'new_trx_9';
+XA START 'trx_10';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_10';
+XA START 'trx_11';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_11';
+XA START 'trx_12';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_12';
+XA START 'trx_13';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_13';
+XA START 'trx_14';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_14';
+XA START 'trx_15';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_15';
+XA START 'trx_16';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_16';
+XA START 'trx_17';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_17';
+XA START 'trx_18';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_18';
+XA START 'trx_19';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_19';
+SELECT * FROM t;
+a
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+disconnect conn2tmp;
+disconnect conn3tmp;
+disconnect conn2ro;
+disconnect conn3ro;
+disconnect conn2empty;
+disconnect conn3empty;
+connection default;
+XA ROLLBACK 'trx_20';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn19;
+connection default;
+XA ROLLBACK 'trx_19';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn18;
+connection default;
+XA ROLLBACK 'trx_18';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn17;
+connection default;
+XA ROLLBACK 'trx_17';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn16;
+connection default;
+XA ROLLBACK 'trx_16';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn15;
+connection default;
+XA ROLLBACK 'trx_15';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn14;
+connection default;
+XA ROLLBACK 'trx_14';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn13;
+connection default;
+XA ROLLBACK 'trx_13';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn12;
+connection default;
+XA ROLLBACK 'trx_12';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn11;
+connection default;
+XA ROLLBACK 'trx_11';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn10;
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx1tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx1tmp';
+XA PREPARE 'trx1tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx2tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx2tmp';
+XA PREPARE 'trx2tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@sql_log_bin = OFF;
+CREATE TEMPORARY TABLE tmp1 (a int) ENGINE=innodb;
+XA START 'trx3tmp';
+INSERT INTO tmp1 SET a=1;
+XA END 'trx3tmp';
+XA PREPARE 'trx3tmp';
+connection default;
+XA COMMIT 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA ROLLBACK 'trx1tmp';
+ERROR XAE04: XAER_NOTA: Unknown XID
+XA START 'trx1tmp';
+ERROR XAE08: XAER_DUPID: The XID already exists
+connection default;
+*** 3 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1tmp;
+disconnect conn1tmp;
+connection default;
+XA COMMIT 'trx1tmp';
+KILL connection CONN_ID;
+XA COMMIT 'trx3tmp';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1ro';
+SELECT * from t ORDER BY a;
+a
+0
+1
+2
+3
+4
+5
+5
+6
+6
+7
+7
+8
+8
+9
+9
+10
+11
+12
+13
+14
+XA END 'trx1ro';
+XA PREPARE 'trx1ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2ro';
+SELECT * from t ORDER BY a;
+a
+0
+1
+2
+3
+4
+5
+5
+6
+6
+7
+7
+8
+8
+9
+9
+10
+11
+12
+13
+14
+XA END 'trx2ro';
+XA PREPARE 'trx2ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3ro';
+SELECT * from t ORDER BY a;
+a
+0
+1
+2
+3
+4
+5
+5
+6
+6
+7
+7
+8
+8
+9
+9
+10
+11
+12
+13
+14
+XA END 'trx3ro';
+XA PREPARE 'trx3ro';
+connection default;
+*** 4 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1ro;
+disconnect conn1ro;
+connection default;
+XA ROLLBACK 'trx1ro';
+KILL connection CONN_ID;
+XA ROLLBACK 'trx3ro';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1empty';
+XA END 'trx1empty';
+XA PREPARE 'trx1empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx2empty';
+XA END 'trx2empty';
+XA PREPARE 'trx2empty';
+connect conn$index$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx3empty';
+XA END 'trx3empty';
+XA PREPARE 'trx3empty';
+connection default;
+*** 5 prepared transactions must be in the list ***
+XA RECOVER;
+formatID gtrid_length bqual_length data
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+1 LEN1 LEN2 TRX_N
+connection conn1empty;
+disconnect conn1empty;
+connection default;
+XA COMMIT 'trx1empty';
+KILL connection CONN_ID;
+XA COMMIT 'trx3empty';
+connect conn1$type, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'trx1unprepared';
+INSERT INTO t set a=0;
+XA END 'trx1unprepared';
+INSERT INTO t set a=0;
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+XA START 'trx1unprepared';
+ERROR XAE07: XAER_RMFAIL: The command cannot be executed when global transaction is in the IDLE state
+disconnect conn1unprepared;
+connection default;
+XA COMMIT 'trx1unprepared';
+ERROR XAE04: XAER_NOTA: Unknown XID
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_0';
+INSERT INTO t SET a=0;
+XA END 'trx_0';
+XA PREPARE 'trx_0';
+disconnect conn0;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_1';
+INSERT INTO t SET a=1;
+XA END 'trx_1';
+XA PREPARE 'trx_1';
+disconnect conn1;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_2';
+INSERT INTO t SET a=2;
+XA END 'trx_2';
+XA PREPARE 'trx_2';
+disconnect conn2;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_3';
+INSERT INTO t SET a=3;
+XA END 'trx_3';
+XA PREPARE 'trx_3';
+disconnect conn3;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_4';
+INSERT INTO t SET a=4;
+XA END 'trx_4';
+XA PREPARE 'trx_4';
+disconnect conn4;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_5';
+INSERT INTO t SET a=5;
+XA END 'trx_5';
+XA PREPARE 'trx_5';
+disconnect conn5;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_6';
+INSERT INTO t SET a=6;
+XA END 'trx_6';
+XA PREPARE 'trx_6';
+disconnect conn6;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_7';
+INSERT INTO t SET a=7;
+XA END 'trx_7';
+XA PREPARE 'trx_7';
+disconnect conn7;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_8';
+INSERT INTO t SET a=8;
+XA END 'trx_8';
+XA PREPARE 'trx_8';
+disconnect conn8;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_9';
+INSERT INTO t SET a=9;
+XA END 'trx_9';
+XA PREPARE 'trx_9';
+disconnect conn9;
+connection default;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_10';
+INSERT INTO t SET a=10;
+XA END 'trx_10';
+XA PREPARE 'trx_10';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_11';
+INSERT INTO t SET a=11;
+XA END 'trx_11';
+XA PREPARE 'trx_11';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_12';
+INSERT INTO t SET a=12;
+XA END 'trx_12';
+XA PREPARE 'trx_12';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_13';
+INSERT INTO t SET a=13;
+XA END 'trx_13';
+XA PREPARE 'trx_13';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_14';
+INSERT INTO t SET a=14;
+XA END 'trx_14';
+XA PREPARE 'trx_14';
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_15';
+INSERT INTO t SET a=15;
+XA END 'trx_15';
+XA PREPARE 'trx_15';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_16';
+INSERT INTO t SET a=16;
+XA END 'trx_16';
+XA PREPARE 'trx_16';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_17';
+INSERT INTO t SET a=17;
+XA END 'trx_17';
+XA PREPARE 'trx_17';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+XA START 'trx_18';
+INSERT INTO t SET a=18;
+XA END 'trx_18';
+XA PREPARE 'trx_18';
+connection default;
+KILL CONNECTION CONN_ID;
+connect conn$i, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+SET @@binlog_format = STATEMENT;
+SET @@binlog_format = ROW;
+XA START 'trx_19';
+INSERT INTO t SET a=19;
+XA END 'trx_19';
+XA PREPARE 'trx_19';
+connection default;
+KILL CONNECTION CONN_ID;
+connection default;
+XA ROLLBACK 'trx_0';
+XA ROLLBACK 'trx_1';
+XA ROLLBACK 'trx_2';
+XA ROLLBACK 'trx_3';
+XA ROLLBACK 'trx_4';
+XA COMMIT 'trx_5';
+XA COMMIT 'trx_6';
+XA COMMIT 'trx_7';
+XA COMMIT 'trx_8';
+XA COMMIT 'trx_9';
+# Kill and restart
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_0';
+INSERT INTO t SET a=0;
+XA END 'new_trx_0';
+XA PREPARE 'new_trx_0';
+disconnect conn_restart_0;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_1';
+INSERT INTO t SET a=1;
+XA END 'new_trx_1';
+XA PREPARE 'new_trx_1';
+disconnect conn_restart_1;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_2';
+INSERT INTO t SET a=2;
+XA END 'new_trx_2';
+XA PREPARE 'new_trx_2';
+disconnect conn_restart_2;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_3';
+INSERT INTO t SET a=3;
+XA END 'new_trx_3';
+XA PREPARE 'new_trx_3';
+disconnect conn_restart_3;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_4';
+INSERT INTO t SET a=4;
+XA END 'new_trx_4';
+XA PREPARE 'new_trx_4';
+disconnect conn_restart_4;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_5';
+INSERT INTO t SET a=5;
+XA END 'new_trx_5';
+XA PREPARE 'new_trx_5';
+disconnect conn_restart_5;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_6';
+INSERT INTO t SET a=6;
+XA END 'new_trx_6';
+XA PREPARE 'new_trx_6';
+disconnect conn_restart_6;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_7';
+INSERT INTO t SET a=7;
+XA END 'new_trx_7';
+XA PREPARE 'new_trx_7';
+disconnect conn_restart_7;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_8';
+INSERT INTO t SET a=8;
+XA END 'new_trx_8';
+XA PREPARE 'new_trx_8';
+disconnect conn_restart_8;
+connection default;
+connect conn_restart_$k, 127.0.0.1,root,,test,$MASTER_MYPORT,;
+XA START 'new_trx_9';
+INSERT INTO t SET a=9;
+XA END 'new_trx_9';
+XA PREPARE 'new_trx_9';
+disconnect conn_restart_9;
+connection default;
+connection default;
+XA COMMIT 'new_trx_0';
+XA COMMIT 'new_trx_1';
+XA COMMIT 'new_trx_2';
+XA COMMIT 'new_trx_3';
+XA COMMIT 'new_trx_4';
+XA COMMIT 'new_trx_5';
+XA COMMIT 'new_trx_6';
+XA COMMIT 'new_trx_7';
+XA COMMIT 'new_trx_8';
+XA COMMIT 'new_trx_9';
+XA START 'trx_10';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_10';
+XA START 'trx_11';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_11';
+XA START 'trx_12';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_12';
+XA START 'trx_13';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_13';
+XA START 'trx_14';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA COMMIT 'trx_14';
+XA START 'trx_15';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_15';
+XA START 'trx_16';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_16';
+XA START 'trx_17';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_17';
+XA START 'trx_18';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_18';
+XA START 'trx_19';
+ERROR XAE08: XAER_DUPID: The XID already exists
+XA ROLLBACK 'trx_19';
+SELECT * FROM t;
+a
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+disconnect conn2tmp;
+disconnect conn3tmp;
+disconnect conn2ro;
+disconnect conn3ro;
+disconnect conn2empty;
+disconnect conn3empty;
+connection default;
+XA ROLLBACK 'trx_20';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn19;
+connection default;
+XA ROLLBACK 'trx_19';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn18;
+connection default;
+XA ROLLBACK 'trx_18';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn17;
+connection default;
+XA ROLLBACK 'trx_17';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn16;
+connection default;
+XA ROLLBACK 'trx_16';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn15;
+connection default;
+XA ROLLBACK 'trx_15';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn14;
+connection default;
+XA ROLLBACK 'trx_14';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn13;
+connection default;
+XA ROLLBACK 'trx_13';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn12;
+connection default;
+XA ROLLBACK 'trx_12';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn11;
+connection default;
+XA ROLLBACK 'trx_11';
+ERROR XAE04: XAER_NOTA: Unknown XID
+disconnect conn10;
+connection default;
+XA START 'one_phase_trx_0';
+INSERT INTO t SET a=0;
+XA END 'one_phase_trx_0';
+XA COMMIT 'one_phase_trx_0' ONE PHASE;
+XA START 'one_phase_trx_1';
+INSERT INTO t SET a=1;
+XA END 'one_phase_trx_1';
+XA COMMIT 'one_phase_trx_1' ONE PHASE;
+XA START 'one_phase_trx_2';
+INSERT INTO t SET a=2;
+XA END 'one_phase_trx_2';
+XA COMMIT 'one_phase_trx_2' ONE PHASE;
+XA START 'one_phase_trx_3';
+INSERT INTO t SET a=3;
+XA END 'one_phase_trx_3';
+XA COMMIT 'one_phase_trx_3' ONE PHASE;
+XA START 'one_phase_trx_4';
+INSERT INTO t SET a=4;
+XA END 'one_phase_trx_4';
+XA COMMIT 'one_phase_trx_4' ONE PHASE;
+SELECT SUM(a) FROM t;
+SUM(a)
+290
+DROP TABLE t;
+DROP VIEW v_processlist;
+include/show_binlog_events.inc
+Log_name Pos Event_type Server_id End_log_pos Info
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v_processlist` AS SELECT * FROM performance_schema.threads where type = 'FOREGROUND'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-#
+master-bin.000001 # Query # # use `mtr`; INSERT INTO test_suppressions (pattern) VALUES ( NAME_CONST('pattern',_latin1'Found 10 prepared XA transactions' COLLATE 'latin1_swedish_ci'))
+master-bin.000001 # Query # # COMMIT
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; CREATE TABLE t (a INT) ENGINE=innodb
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_0
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=0
+master-bin.000001 # Query # # XA END 'trx_0'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_0'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_1
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=1
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_1'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_1'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_2
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=2
+master-bin.000001 # Query # # XA END 'trx_2'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_2'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_3
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=3
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_3'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_3'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_4
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=4
+master-bin.000001 # Query # # XA END 'trx_4'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_4'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_5
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=5
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_5'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_5'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_6
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=6
+master-bin.000001 # Query # # XA END 'trx_6'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_6'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_7
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=7
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_7'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_7'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_8
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=8
+master-bin.000001 # Query # # XA END 'trx_8'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_8'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_9
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=9
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_9'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_9'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_10
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=10
+master-bin.000001 # Query # # XA END 'trx_10'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_10'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_11
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=11
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_11'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_11'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_12
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=12
+master-bin.000001 # Query # # XA END 'trx_12'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_12'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_13
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=13
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_13'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_13'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_14
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=14
+master-bin.000001 # Query # # XA END 'trx_14'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_14'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_15
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=15
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_15'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_15'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_16
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=16
+master-bin.000001 # Query # # XA END 'trx_16'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_16'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_17
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=17
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_17'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_17'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_18
+master-bin.000001 # Query # # use `test`; INSERT INTO t SET a=18
+master-bin.000001 # Query # # XA END 'trx_18'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_18'
+master-bin.000001 # Gtid # # BEGIN GTID #-#-# XID :trx_19
+master-bin.000001 # Annotate_rows # # INSERT INTO t SET a=19
+master-bin.000001 # Table_map # # table_id: # (test.t)
+master-bin.000001 # Write_rows_v1 # # table_id: # flags: STMT_END_F
+master-bin.000001 # Query # # XA END 'trx_19'
+master-bin.000001 # XA_prepare # # XA PREPARE 'trx_19'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA ROLLBACK 'trx_0'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA ROLLBACK 'trx_1'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA ROLLBACK 'trx_2'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA ROLLBACK 'trx_3'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA ROLLBACK 'trx_4'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA COMMIT 'trx_5'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA COMMIT 'trx_6'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA COMMIT 'trx_7'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA COMMIT 'trx_8'
+master-bin.000001 # Gtid # # GTID #-#-#
+master-bin.000001 # Query # # use `test`; XA COMMIT 'trx_9'
+master-bin.000001 # Stop # #
+All transactions must be completed, to empty-list the following:
+XA RECOVER;
+formatID gtrid_length bqual_length data
+XA RECOVER;
+formatID gtrid_length bqual_length data
diff --git a/mysql-test/suite/binlog/t/binlog_xa_prepared.test b/mysql-test/suite/binlog/t/binlog_xa_prepared.test
new file mode 100644
index 0000000..212ecf9
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_xa_prepared.test
@@ -0,0 +1,106 @@
+#
+# The test verifies prepared XA transaction behaviour.
+#
+# The prepared XA transactions can be disconnected from the client,
+# discovered from another connection and commited or rolled back
+# later. They also survive the server restart. The test runs two
+# loops each consisting of prepared XA:s generation, their
+# manipulation and a server restart followed with survived XA:s
+# completion.
+#
+
+#
+# Prepared XA can't get available to an external connection
+# until connection that either leaves actively or is killed
+# has completed a necessary part of its cleanup.
+# Selecting from P_S.threads provides a method to learn that.
+#
+--source include/have_innodb.inc
+--source include/have_perfschema.inc
+
+# Total number of connection each performing one insert into table
+--let $conn_number=20
+# Number of rollbacks and commits from either side of the server restart
+--let $rollback_number=5
+--let $commit_number=5
+# Number of transactions that are terminated before server restarts
+--let $term_number=`SELECT $rollback_number + $commit_number`
+# Instead of disconnect make some connections killed when their
+# transactions got prepared.
+--let $killed_number=5
+# make some connections disconnected by shutdown rather than actively
+--let $server_disconn_number=5
+--let $prepared_at_server_restart = `SELECT $conn_number - $term_number`
+# number a "warmup" connection after server restart, they all commit
+--let $post_restart_conn_number=10
+
+# Counter to be used in GTID consistency check.
+# It's incremented per each non-XA transaction commit.
+# Local to this file variable to control one-phase commit loop
+--let $one_phase_number = 5
+
+--connection default
+
+# Remove possibly preceeding binlogs and clear initialization time
+# GTID executed info. In the following all transactions are counted
+# to conduct verification at the end of the test.
+if (`SELECT @@global.log_bin`)
+{
+ RESET MASTER;
+}
+
+# Disconected and follower threads need synchronization
+CREATE VIEW v_processlist as SELECT * FROM performance_schema.threads where type = 'FOREGROUND';
+
+--eval call mtr.add_suppression("Found $prepared_at_server_restart prepared XA transactions")
+
+CREATE TABLE t (a INT) ENGINE=innodb;
+
+# Counter is incremented at the end of post restart to
+# reflect number of loops done in correctness computation.
+--let $restart_number = 0
+--let $how_to_restart=restart_mysqld.inc
+--source include/binlog_xa_prepared_do_and_restart.inc
+
+--let $how_to_restart=kill_and_restart_mysqld.inc
+--source include/binlog_xa_prepared_do_and_restart.inc
+
+--connection default
+
+# Few xs that commit in one phase, not subject to the server restart
+# nor reconnect.
+# This piece of test is related to mysqlbinlog recovery examine below.
+--let $k = 0
+while ($k < $one_phase_number)
+{
+ --eval XA START 'one_phase_trx_$k'
+ --eval INSERT INTO t SET a=$k
+ --eval XA END 'one_phase_trx_$k'
+ --eval XA COMMIT 'one_phase_trx_$k' ONE PHASE
+
+ --inc $k
+}
+
+SELECT SUM(a) FROM t;
+DROP TABLE t;
+DROP VIEW v_processlist;
+
+let $outfile= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.sql;
+if (`SELECT @@global.log_bin`)
+{
+ # Recording proper samples of binlogged prepared XA:s
+ --source include/show_binlog_events.inc
+ --exec $MYSQL_BINLOG -R --to-last-log master-bin.000001 > $outfile
+ # --cat_file $outfile
+}
+
+--echo All transactions must be completed, to empty-list the following:
+XA RECOVER;
+
+
+if (`SELECT @@global.log_bin`)
+{
+ --exec $MYSQL test < $outfile
+ --remove_file $outfile
+ XA RECOVER;
+}
diff --git a/mysql-test/suite/binlog/t/binlog_xa_prepared_disconnect.test b/mysql-test/suite/binlog/t/binlog_xa_prepared_disconnect.test
new file mode 100644
index 0000000..3301cf7
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_xa_prepared_disconnect.test
@@ -0,0 +1,13 @@
+###############################################################################
+# Bug#12161 Xa recovery and client disconnection
+# Testing new server options and binary logging prepared XA transaction.
+###############################################################################
+--source include/have_log_bin.inc
+
+#
+# MIXED mode is chosen because formats are varied inside the sourced tests.
+#
+--source include/have_binlog_format_mixed.inc
+
+--source suite/binlog/t/binlog_xa_prepared.test
+
diff --git a/mysql-test/suite/rpl/include/rpl_connection_master.inc b/mysql-test/suite/rpl/include/rpl_connection_master.inc
new file mode 100644
index 0000000..fa09cc8
--- /dev/null
+++ b/mysql-test/suite/rpl/include/rpl_connection_master.inc
@@ -0,0 +1,2 @@
+let $rpl_connection_name= master;
+source include/rpl_connection.inc;
diff --git a/mysql-test/suite/rpl/include/rpl_connection_slave.inc b/mysql-test/suite/rpl/include/rpl_connection_slave.inc
new file mode 100644
index 0000000..8dcfb3b
--- /dev/null
+++ b/mysql-test/suite/rpl/include/rpl_connection_slave.inc
@@ -0,0 +1,2 @@
+let $rpl_connection_name= slave;
+source include/rpl_connection.inc;
diff --git a/mysql-test/suite/rpl/r/rpl_xa.result b/mysql-test/suite/rpl/r/rpl_xa.result
new file mode 100644
index 0000000..d406877
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa.result
@@ -0,0 +1,62 @@
+include/master-slave.inc
+[connection master]
+create table t1 (a int, b int) engine=InnoDB;
+xa start 't';
+insert into t1 values(1, 2);
+xa end 't';
+xa prepare 't';
+xa commit 't';
+select * from t1;
+a b
+1 2
+connection slave;
+select * from t1;
+a b
+1 2
+connection master;
+xa start 't';
+insert into t1 values(3, 4);
+xa end 't';
+xa prepare 't';
+xa rollback 't';
+select * from t1;
+a b
+1 2
+connection slave;
+select * from t1;
+a b
+1 2
+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';
+xa commit 't';
+xa commit 's';
+SET pseudo_slave_mode=0;
+Warnings:
+Warning 1231 Slave applier execution mode not active, statement ineffective.
+select * from t1;
+a b
+1 2
+5 6
+select * from t2;
+a
+0
+connection slave;
+select * from t1;
+a b
+1 2
+5 6
+select * from t2;
+a
+0
+connection master;
+drop table t1, t2;
+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 0000000..34f118e
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_xa_survive_disconnect_mixed_engines.result
@@ -0,0 +1,46 @@
+include/master-slave.inc
+[connection master]
+connection master;
+CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
+SET @@session.binlog_direct_non_transactional_updates := if(floor(rand()*10)%2,'ON','OFF');
+CREATE TABLE t (a INT) ENGINE=innodb;
+CREATE TABLE tm (a INT) ENGINE=myisam;
+XA START '1';
+INSERT INTO tm VALUES (1);
+INSERT INTO t VALUES (1);
+XA END '1';
+XA PREPARE '1';
+XA COMMIT '1';
+XA START '2';
+INSERT INTO t VALUES (2);
+INSERT INTO tm VALUES (2);
+INSERT INTO t VALUES (2);
+XA END '2';
+XA PREPARE '2';
+XA COMMIT '2';
+XA START '3';
+INSERT INTO tm VALUES (3);
+INSERT INTO t VALUES (3);
+XA END '3';
+XA PREPARE '3';
+XA ROLLBACK '3';
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+XA START '4';
+INSERT INTO t VALUES (4);
+INSERT INTO tm VALUES (4);
+INSERT INTO t VALUES (4);
+XA END '4';
+XA PREPARE '4';
+XA ROLLBACK '4';
+Warnings:
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+Warning 1196 Some non-transactional changed tables couldn't be rolled back
+include/sync_slave_sql_with_master.inc
+include/diff_tables.inc [master:tm, slave:tm]
+connection master;
+DELETE FROM t;
+DROP TABLE t, tm;
+include/sync_slave_sql_with_master.inc
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_xa.test b/mysql-test/suite/rpl/t/rpl_xa.test
new file mode 100644
index 0000000..fda84b3
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa.test
@@ -0,0 +1,46 @@
+source include/have_innodb.inc;
+source include/master-slave.inc;
+
+create table t1 (a int, b int) engine=InnoDB;
+xa start 't';
+insert into t1 values(1, 2);
+xa end 't';
+xa prepare 't';
+xa commit 't';
+select * from t1;
+sync_slave_with_master;
+select * from t1;
+connection master;
+
+xa start 't';
+insert into t1 values(3, 4);
+xa end 't';
+xa prepare 't';
+xa rollback 't';
+select * from t1;
+sync_slave_with_master;
+select * from 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';
+xa commit 't';
+xa commit 's';
+SET pseudo_slave_mode=0;
+select * from t1;
+select * from t2;
+sync_slave_with_master;
+select * from t1;
+select * from t2;
+
+connection master;
+drop table t1, t2;
+source include/rpl_end.inc;
diff --git a/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_mixed_engines.test b/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_mixed_engines.test
new file mode 100644
index 0000000..1c197dc
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_xa_survive_disconnect_mixed_engines.test
@@ -0,0 +1,80 @@
+# BUG#12161 Xa recovery and client disconnection
+#
+# The test verifies correct XA transaction two phase logging and its applying
+# in a case the transaction updates transactional and non-transactional tables.
+
+--source include/have_innodb.inc
+--source include/master-slave.inc
+
+--connection master
+CALL mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
+
+# Test randomizes the following variable's value:
+SET @@session.binlog_direct_non_transactional_updates := if(floor(rand()*10)%2,'ON','OFF');
+CREATE TABLE t (a INT) ENGINE=innodb;
+
+# memorize for the following show
+--let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1)
+--let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1)
+
+CREATE TABLE tm (a INT) ENGINE=myisam;
+
+# case A: COMMIT
+
+# Non transactional table goes first
+XA START '1';
+INSERT INTO tm VALUES (1);
+INSERT INTO t VALUES (1);
+XA END '1';
+XA PREPARE '1';
+XA COMMIT '1';
+
+# Transactional table goes first
+XA START '2';
+INSERT INTO t VALUES (2);
+--disable_warnings
+INSERT INTO tm VALUES (2);
+--enable_warnings
+INSERT INTO t VALUES (2);
+XA END '2';
+XA PREPARE '2';
+XA COMMIT '2';
+
+# case B: ROLLBACK
+
+# Non transactional table goes first
+XA START '3';
+--disable_warnings
+INSERT INTO tm VALUES (3);
+--enable_warnings
+INSERT INTO t VALUES (3);
+XA END '3';
+XA PREPARE '3';
+XA ROLLBACK '3';
+
+# Transactional table goes first
+XA START '4';
+INSERT INTO t VALUES (4);
+--disable_warnings
+INSERT INTO tm VALUES (4);
+--enable_warnings
+INSERT INTO t VALUES (4);
+XA END '4';
+XA PREPARE '4';
+XA ROLLBACK '4';
+
+--source include/sync_slave_sql_with_master.inc
+
+--let $diff_tables= master:tm, slave:tm
+--source include/diff_tables.inc
+
+# cleanup
+
+--connection master
+
+DELETE FROM t;
+DROP TABLE t, tm;
+
+--source include/sync_slave_sql_with_master.inc
+
+--source include/rpl_end.inc
diff --git a/sql/handler.cc b/sql/handler.cc
index 0746f8a..45d21ea 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1247,6 +1247,7 @@ int ha_prepare(THD *thd)
Ha_trx_info *ha_info= trans->ha_list;
DBUG_ENTER("ha_prepare");
+ thd->transaction.xid_state.registered_for_binlog= false;
if (ha_info)
{
for (; ha_info; ha_info= ha_info->next())
@@ -1270,6 +1271,13 @@ int ha_prepare(THD *thd)
}
}
+
+ if (thd->transaction.xid_state.registered_for_binlog &&
+ unlikely(tc_log->log_xa_prepare(thd, all)))
+ {
+ ha_rollback_trans(thd, all);
+ error=1;
+ }
}
DBUG_RETURN(error);
@@ -2110,7 +2118,7 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
char buf[XIDDATASIZE*4+6]; // see xid_to_str
DBUG_PRINT("info", ("ignore xid %s", xid_to_str(buf, info->list+i)));
#endif
- xid_cache_insert(info->list+i, XA_PREPARED);
+ xid_cache_insert(info->list+i, XA_PREPARED, TRUE);
info->found_foreign_xids++;
continue;
}
diff --git a/sql/handler.h b/sql/handler.h
index e80b963..b04a83e 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -828,6 +828,16 @@ struct xid_t {
long gtrid_length;
long bqual_length;
char data[XIDDATASIZE]; // not \0-terminated !
+ /*
+ The size of the string containing serialized Xid representation
+ is computed as a sum of
+ eight as the number of formatting symbols (X'',X'',)
+ plus 2 x XIDDATASIZE (2 due to hex format),
+ plus space for decimal digits of XID::formatID,
+ plus one for 0x0.
+ */
+ static const uint ser_buf_size=
+ 8 + 2 * XIDDATASIZE + 4 * sizeof(long) + 1;
xid_t() {} /* Remove gcc warning */
bool eq(struct xid_t *xid)
@@ -1661,6 +1671,21 @@ struct handlerton
/* backup */
void (*prepare_for_backup)(void);
void (*end_backup)(void);
+ /**
+ @param[in,out] thd pointer to THD
+ @param[in] new_trx_arg pointer to replacement transaction
+ @param[out] ptr_trx_arg double pointer to being replaced transaction
+
+ Associated with THD engine's native transaction is replaced
+ with @c new_trx_arg. The old value is returned through a buffer if non-null
+ pointer is provided with @c ptr_trx_arg.
+ The method is adapted by XA start and XA prepare handlers to
+ handle XA transaction that is logged as two parts by slave applier.
+
+ This interface concerns engines that are aware of XA transaction.
+ */
+ void (*replace_native_transaction_in_thd)(THD *thd, void *new_trx_arg,
+ void **ptr_trx_arg);
};
diff --git a/sql/log.cc b/sql/log.cc
index 2b1d286..1273ccd 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -92,6 +92,9 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all);
static int binlog_rollback(handlerton *hton, THD *thd, bool all);
static int binlog_prepare(handlerton *hton, THD *thd, bool all);
static int binlog_start_consistent_snapshot(handlerton *hton, THD *thd);
+static int binlog_flush_cache(THD *thd, binlog_cache_mngr *cache_mngr,
+ Log_event *end_ev, bool all, bool using_stmt,
+ bool using_trx);
static const LEX_CSTRING write_error_msg=
{ STRING_WITH_LEN("error writing to the binary log") };
@@ -1887,23 +1890,16 @@ static inline int
binlog_commit_flush_xid_caches(THD *thd, binlog_cache_mngr *cache_mngr,
bool all, my_xid xid)
{
- if (xid)
- {
- Xid_log_event end_evt(thd, xid, TRUE);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
- }
- else
+ /* Mask XA COMMIT ... ONE PHASE as plain BEGIN ... COMMIT */
+ if (!xid)
{
- /*
- Empty xid occurs in XA COMMIT ... ONE PHASE.
- In this case, we do not have a MySQL xid for the transaction, and the
- external XA transaction coordinator will have to handle recovery if
- needed. So we end the transaction with a plain COMMIT query event.
- */
- Query_log_event end_evt(thd, STRING_WITH_LEN("COMMIT"),
- TRUE, TRUE, TRUE, 0);
- return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+ DBUG_ASSERT(thd->transaction.xid_state.xa_state == XA_IDLE &&
+ thd->lex->xa_opt == XA_ONE_PHASE);
+ xid= thd->query_id;
}
+
+ Xid_log_event end_evt(thd, xid, TRUE);
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
}
/**
@@ -1962,14 +1958,37 @@ binlog_truncate_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr, bool all)
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()
+ Mark the XA for binlogging.
+ Transactions with no binlog handler registered like readonly ones,
+ should not go to the binlog.
+ Real work is done in MYSQL_BIN_LOG::log_xa_prepare()
*/
+ thd->transaction.xid_state.registered_for_binlog= true;
return 0;
}
+
+static int serialize_xid(XID *xid, char *buf)
+{
+ size_t size;
+ buf[0]= '\'';
+ memcpy(buf+1, xid->data, xid->gtrid_length);
+ size= xid->gtrid_length + 2;
+ buf[size-1]= '\'';
+ if (xid->bqual_length == 0 && xid->formatID == 1)
+ return size;
+
+ memcpy(buf+size, ", '", 3);
+ memcpy(buf+size+3, xid->data+xid->gtrid_length, xid->bqual_length);
+ size+= 3 + xid->bqual_length;
+ buf[size]= '\'';
+ size++;
+ if (xid->formatID != 1)
+ size+= sprintf(buf+size, ", %ld", xid->formatID);
+ return size;
+}
+
+
/*
We flush the cache wrapped in a beging/rollback if:
. aborting a single or multi-statement transaction and;
@@ -9866,6 +9885,43 @@ int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
DBUG_RETURN(BINLOG_COOKIE_GET_ERROR_FLAG(cookie));
}
+
+int TC_LOG_BINLOG::log_xa_prepare(THD *thd, bool all)
+{
+ binlog_cache_mngr *cache_mngr= thd->binlog_setup_trx_data();
+ XID *xid= &thd->transaction.xid_state.xid;
+ {
+ /*
+ Log the XA END event first.
+ We don't do that in trans_xa_end() as XA COMMIT ONE PHASE
+ is logged as simple BEGIN/COMMIT so the XA END should
+ not get to the log.
+ */
+ const size_t xc_len= sizeof("XA END ") - 1; // do not count trailing 0
+ char buf[xc_len + xid_t::ser_buf_size];
+ size_t buflen;
+ binlog_cache_data *cache_data;
+ IO_CACHE *file;
+
+ memcpy(buf, "XA END ", xc_len);
+ buflen= xc_len + serialize_xid(xid, buf+xc_len);
+ cache_data= cache_mngr->get_binlog_cache_data(true);
+ file= &cache_data->cache_log;
+ thd->lex->sql_command= SQLCOM_XA_END;
+ Query_log_event xa_end(thd, buf, buflen, true, false, true, 0);
+ if (write_event(&xa_end, cache_data, file))
+ return 1;
+ thd->lex->sql_command= SQLCOM_XA_PREPARE;
+ }
+
+ cache_mngr->using_xa= FALSE;
+ XA_prepare_log_event end_evt(thd, xid, FALSE);
+ if (thd->variables.option_bits & OPTION_BIN_LOG)
+ thd->transaction.xid_state.set_binlogged();
+ return (binlog_flush_cache(thd, cache_mngr, &end_evt, all, TRUE, TRUE));
+}
+
+
void
TC_LOG_BINLOG::commit_checkpoint_notify(void *cookie)
{
diff --git a/sql/log.h b/sql/log.h
index 2d4cc7a..f94cb11 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -60,6 +60,7 @@ class TC_LOG
bool need_prepare_ordered,
bool need_commit_ordered) = 0;
virtual int unlog(ulong cookie, my_xid xid)=0;
+ virtual int log_xa_prepare(THD *thd, bool all)= 0;
virtual void commit_checkpoint_notify(void *cookie)= 0;
protected:
@@ -114,6 +115,10 @@ 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 log_xa_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie) { DBUG_ASSERT(0); };
};
@@ -197,6 +202,10 @@ class TC_LOG_MMAP: public TC_LOG
int log_and_order(THD *thd, my_xid xid, bool all,
bool need_prepare_ordered, bool need_commit_ordered);
int unlog(ulong cookie, my_xid xid);
+ int log_xa_prepare(THD *thd, bool all)
+ {
+ return 0;
+ }
void commit_checkpoint_notify(void *cookie);
int recover();
@@ -685,6 +694,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 log_xa_prepare(THD *thd, bool all);
void commit_checkpoint_notify(void *cookie);
int recover(LOG_INFO *linfo, const char *last_log_name, IO_CACHE *first_log,
Format_description_log_event *fdle, bool do_xa);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 70f0e6c..4179145 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -2139,6 +2139,9 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case XID_EVENT:
ev = new Xid_log_event(buf, fdle);
break;
+ case XA_PREPARE_LOG_EVENT:
+ ev = new XA_prepare_log_event(buf, fdle);
+ break;
case RAND_EVENT:
ev = new Rand_log_event(buf, fdle);
break;
@@ -2190,7 +2193,6 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
case PREVIOUS_GTIDS_LOG_EVENT:
case TRANSACTION_CONTEXT_EVENT:
case VIEW_CHANGE_EVENT:
- case XA_PREPARE_LOG_EVENT:
ev= new Ignorable_log_event(buf, fdle,
get_type_str((Log_event_type) event_type));
break;
@@ -4433,6 +4435,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, size_t que
case SQLCOM_RELEASE_SAVEPOINT:
case SQLCOM_ROLLBACK_TO_SAVEPOINT:
case SQLCOM_SAVEPOINT:
+ case SQLCOM_XA_END:
use_cache= trx_cache= TRUE;
break;
default:
@@ -6268,6 +6271,7 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
post_header_len[USER_VAR_EVENT-1]= USER_VAR_HEADER_LEN;
post_header_len[FORMAT_DESCRIPTION_EVENT-1]= FORMAT_DESCRIPTION_HEADER_LEN;
post_header_len[XID_EVENT-1]= XID_HEADER_LEN;
+ post_header_len[XA_PREPARE_LOG_EVENT-1]= XA_PREPARE_HEADER_LEN;
post_header_len[BEGIN_LOAD_QUERY_EVENT-1]= BEGIN_LOAD_QUERY_HEADER_LEN;
post_header_len[EXECUTE_LOAD_QUERY_EVENT-1]= EXECUTE_LOAD_QUERY_HEADER_LEN;
/*
@@ -7920,7 +7924,7 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
buf+= 8;
domain_id= uint4korr(buf);
buf+= 4;
- flags2= *buf;
+ flags2= *(buf++);
if (flags2 & FL_GROUP_COMMIT_ID)
{
if (event_len < (uint)header_size + GTID_HEADER_LEN + 2)
@@ -7928,8 +7932,22 @@ Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
seq_no= 0; // So is_valid() returns false
return;
}
- ++buf;
commit_id= uint8korr(buf);
+ buf+= 8;
+ }
+ if (flags2 & FL_PREPARED_XA)
+ {
+ xid.formatID= (long) buf[0];
+ xid.gtrid_length= (long) buf[1];
+ xid.bqual_length= (long) buf[2];
+
+ buf+= 3;
+ if (xid.formatID != -1)
+ {
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(xid.data, buf, data_length);
+ buf+= data_length;
+ }
}
}
@@ -7960,6 +7978,12 @@ Gtid_log_event::Gtid_log_event(THD *thd_arg, uint64 seq_no_arg,
/* Preserve any DDL or WAITED flag in the slave's binlog. */
if (thd_arg->rgi_slave)
flags2|= (thd_arg->rgi_slave->gtid_ev_flags2 & (FL_DDL|FL_WAITED));
+ if (thd->transaction.xid_state.xa_state == XA_IDLE &&
+ thd->lex->xa_opt != XA_ONE_PHASE)
+ {
+ flags2|= FL_PREPARED_XA;
+ xid= thd->transaction.xid_state.xid;
+ }
}
@@ -8002,7 +8026,7 @@ Gtid_log_event::peek(const char *event_start, size_t event_len,
bool
Gtid_log_event::write()
{
- uchar buf[GTID_HEADER_LEN+2];
+ uchar buf[GTID_HEADER_LEN+2+sizeof(XID)];
size_t write_len;
int8store(buf, seq_no);
@@ -8014,8 +8038,25 @@ Gtid_log_event::write()
write_len= GTID_HEADER_LEN + 2;
}
else
+ write_len= 13;
+
+ if (flags2 & FL_PREPARED_XA)
{
- bzero(buf+13, GTID_HEADER_LEN-13);
+ buf[write_len]= (uchar) ((char) xid.formatID);
+ buf[write_len+1]= (uchar) xid.gtrid_length;
+ buf[write_len+2]= (uchar) xid.bqual_length;
+ write_len+= 3;
+ if (xid.formatID != -1)
+ {
+ long data_length= xid.bqual_length + xid.gtrid_length;
+ memcpy(buf+write_len, xid.data, data_length);
+ write_len+= data_length;
+ }
+ }
+
+ if (write_len < GTID_HEADER_LEN)
+ {
+ bzero(buf+write_len, GTID_HEADER_LEN-write_len);
write_len= GTID_HEADER_LEN;
}
return write_header(write_len) ||
@@ -8058,7 +8099,7 @@ Gtid_log_event::make_compatible_event(String *packet, bool *need_dummy_event,
void
Gtid_log_event::pack_info(Protocol *protocol)
{
- char buf[6+5+10+1+10+1+20+1+4+20+1];
+ char buf[6+5+10+1+10+1+20+1+4+20+1+5+128];
char *p;
p = strmov(buf, (flags2 & FL_STANDALONE ? "GTID " : "BEGIN GTID "));
p= longlong10_to_str(domain_id, p, 10);
@@ -8072,6 +8113,12 @@ Gtid_log_event::pack_info(Protocol *protocol)
p= longlong10_to_str(commit_id, p, 10);
}
+ if (flags2 & FL_PREPARED_XA)
+ {
+ p= strmov(p, " XID :");
+ p= strnmov(p, xid.data, xid.bqual_length + xid.gtrid_length);
+ }
+
protocol->store(buf, p-buf, &my_charset_bin);
}
@@ -8125,10 +8172,23 @@ Gtid_log_event::do_apply_event(rpl_group_info *rgi)
thd->lex->sql_command= SQLCOM_BEGIN;
thd->is_slave_error= 0;
status_var_increment(thd->status_var.com_stat[thd->lex->sql_command]);
- if (trans_begin(thd, 0))
+ if (flags2 & FL_PREPARED_XA)
{
- DBUG_PRINT("error", ("trans_begin() failed"));
- thd->is_slave_error= 1;
+ thd->lex->xid= &xid;
+ thd->lex->xa_opt= XA_NONE;
+ if (trans_xa_start(thd))
+ {
+ DBUG_PRINT("error", ("trans_xa_start() failed"));
+ thd->is_slave_error= 1;
+ }
+ }
+ else
+ {
+ if (trans_begin(thd, 0))
+ {
+ DBUG_PRINT("error", ("trans_begin() failed"));
+ thd->is_slave_error= 1;
+ }
}
thd->update_stats();
@@ -8248,9 +8308,29 @@ Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
buf, print_event_info->delimiter))
goto err;
}
- if (!(flags2 & FL_STANDALONE))
- if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n", print_event_info->delimiter))
+ if ((flags2 & FL_PREPARED_XA) && !is_flashback)
+ {
+ my_b_write_string(&cache, "XA START '");
+ my_b_write(&cache, (uchar *) xid.data, xid.gtrid_length);
+ my_b_write_string(&cache, "'");
+ if (xid.bqual_length > 0 || xid.formatID != 1)
+ {
+ my_b_write_string(&cache, ", '");
+ my_b_write(&cache, (uchar *) xid.data+xid.gtrid_length, xid.bqual_length);
+ my_b_write_string(&cache, "'");
+ if (xid.formatID != 1)
+ if (my_b_printf(&cache, ", %d", xid.formatID))
+ goto err;
+ }
+ if (my_b_printf(&cache, "%s\n", print_event_info->delimiter))
+ goto err;
+ }
+ else if (!(flags2 & FL_STANDALONE))
+ {
+ if (my_b_printf(&cache, is_flashback ? "COMMIT\n%s\n" : "BEGIN\n%s\n",
+ print_event_info->delimiter))
goto err;
+ }
return cache.flush_data();
err:
@@ -8871,80 +8951,20 @@ bool slave_execute_deferred_events(THD *thd)
/**************************************************************************
- Xid_log_event methods
+ Xid_apply_log_event methods
**************************************************************************/
#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-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);
-}
-#endif
-
-/**
- @note
- It's ok not to use int8store here,
- as long as xid_t::set(ulonglong) and
- xid_t::get_my_xid doesn't do it either.
- We don't care about actual values of xids as long as
- identical numbers compare identically
-*/
-
-Xid_log_event::
-Xid_log_event(const char* buf,
- const Format_description_log_event *description_event)
- :Log_event(buf, description_event)
-{
- /* The Post-Header is empty. The Variable Data part begins immediately. */
- buf+= description_event->common_header_len +
- description_event->post_header_len[XID_EVENT-1];
- memcpy((char*) &xid, buf, sizeof(xid));
-}
-
-
-#ifndef MYSQL_CLIENT
-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();
-}
-#endif
-
-#ifdef MYSQL_CLIENT
-bool Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+int Xid_apply_log_event::record_gtid(const rpl_gtid *gtid, uint64 sub_id,
+ void **out_hton)
{
- Write_on_release_cache cache(&print_event_info->head_cache, file,
- Write_on_release_cache::FLUSH_F, this);
-
- if (!print_event_info->short_form)
- {
- char buf[64];
- longlong10_to_str(xid, buf, 10);
-
- if (print_header(&cache, print_event_info, FALSE) ||
- my_b_printf(&cache, "\tXid = %s\n", buf))
- goto err;
- }
- if (my_b_printf(&cache, is_flashback ? "BEGIN%s\n" : "COMMIT%s\n",
- print_event_info->delimiter))
- goto err;
-
- return cache.flush_data();
-err:
- return 1;
+ return rpl_global_gtid_slave_state->record_gtid(thd, gtid, sub_id, true,
+ false, out_hton);
}
-#endif /* MYSQL_CLIENT */
-#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
-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;
@@ -8975,8 +8995,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
rgi->gtid_pending= false;
gtid= rgi->current_gtid;
- err= rpl_global_gtid_slave_state->record_gtid(thd, >id, sub_id, true,
- false, &hton);
+ err= record_gtid(>id, sub_id, &hton);
if (unlikely(err))
{
int ec= thd->get_stmt_da()->sql_errno();
@@ -9005,8 +9024,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
general_log_print(thd, COM_QUERY,
"COMMIT /* implicit, from Xid_log_event */");
thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
- res= trans_commit(thd); /* Automatically rolls back on error. */
- thd->mdl_context.release_transactional_locks();
+ res= do_commit();
if (likely(!res) && sub_id)
rpl_global_gtid_slave_state->update_state_hash(sub_id, >id, hton, rgi);
@@ -9020,9 +9038,9 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
}
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));
@@ -9050,6 +9068,322 @@ Xid_log_event::do_shall_skip(rpl_group_info *rgi)
/**************************************************************************
+ Xid_log_event methods
+**************************************************************************/
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+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);
+}
+#endif
+
+/**
+ @note
+ It's ok not to use int8store here,
+ as long as xid_t::set(ulonglong) and
+ xid_t::get_my_xid doesn't do it either.
+ We don't care about actual values of xids as long as
+ identical numbers compare identically
+*/
+
+Xid_log_event::
+Xid_log_event(const char* buf,
+ const Format_description_log_event *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 +
+ description_event->post_header_len[XID_EVENT-1];
+ memcpy((char*) &xid, buf, sizeof(xid));
+}
+
+
+#ifndef MYSQL_CLIENT
+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();
+}
+#endif
+
+
+#ifdef MYSQL_CLIENT
+bool Xid_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);
+
+ if (!print_event_info->short_form)
+ {
+ char buf[64];
+ longlong10_to_str(xid, buf, 10);
+
+ if (print_header(&cache, print_event_info, FALSE) ||
+ my_b_printf(&cache, "\tXid = %s\n", buf))
+ goto err;
+ }
+ if (my_b_printf(&cache, is_flashback ? "BEGIN%s\n" : "COMMIT%s\n",
+ print_event_info->delimiter))
+ goto err;
+
+ return cache.flush_data();
+err:
+ return 1;
+}
+#endif /* MYSQL_CLIENT */
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+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 /* !MYSQL_CLIENT */
+
+
+#ifdef TODO7974
+/**
+ Function serializes XID which is characterized by by four last arguments
+ of the function.
+ Serialized XID is presented in valid hex format and is returned to
+ the caller in a buffer pointed by the first argument.
+ The buffer size provived by the caller must be not less than
+ 8 + 2 * XIDDATASIZE + 4 * sizeof(XID::formatID) + 1, see
+ XID::serialize_xid() that is a caller and plugin.h for XID declaration.
+
+ @param buf pointer to a buffer allocated for storing serialized data
+
+ @return the value of the buffer pointer
+*/
+
+char *XA_prepare_log_event::event_xid_t::serialize(char *buf) const
+{
+ int i;
+ char *c= buf;
+ /*
+ Build a string like following pattern:
+ X'hex11hex12...hex1m',X'hex21hex22...hex2n',11
+ and store it into buf.
+ Here hex1i and hex2k are hexadecimals representing XID's internal
+ raw bytes (1 <= i <= m, 1 <= k <= n), and `m' and `n' even numbers
+ half of which corresponding to the lengths of XID's components.
+ */
+ c[0]= 'X';
+ c[1]= '\'';
+ c+= 2;
+ for (i= 0; i < gtrid_length; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) data)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) data)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ c[1]= ',';
+ c[2]= 'X';
+ c[3]= '\'';
+ c+= 4;
+
+ for (; i < gtrid_length + bqual_length; i++)
+ {
+ c[0]=_dig_vec_lower[((uchar*) data)[i] >> 4];
+ c[1]=_dig_vec_lower[((uchar*) data)[i] & 0x0f];
+ c+= 2;
+ }
+ c[0]= '\'';
+ sprintf(c+1, ",%lu", formatID);
+
+ return buf;
+}
+#endif /*TODO7974*/
+
+char *XA_prepare_log_event::event_xid_t::serialize(char *buf) const
+{
+ char *c= buf;
+
+ c[0]= '\'';
+ memcpy(c+1, data, gtrid_length);
+ c[gtrid_length+1]= '\'';
+ c+= gtrid_length + 2;
+
+ if (bqual_length)
+ {
+ c[0]= ',';
+ c[1]= '\'';
+ memcpy(c+2, data+gtrid_length, bqual_length);
+ c[bqual_length+2]= '\'';
+ c+= bqual_length+3;
+ }
+
+ if (formatID != 1)
+ sprintf(c, ",%lu", formatID);
+ else
+ c[0]=0;
+
+ return buf;
+}
+
+
+/**************************************************************************
+ XA_prepare_log_event methods
+**************************************************************************/
+/**
+ @note
+ It's ok not to use int8store here,
+ as long as xid_t::set(ulonglong) and
+ xid_t::get_n_xid doesn't do it either.
+ We don't care about actual values of xids as long as
+ identical numbers compare identically
+*/
+
+XA_prepare_log_event::
+XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event)
+ :Xid_apply_log_event(buf, description_event)
+{
+ uint32 temp= 0;
+ uint8 temp_byte;
+
+ buf+= description_event->common_header_len +
+ description_event->post_header_len[XA_PREPARE_LOG_EVENT-1];
+ memcpy(&temp_byte, buf, 1);
+ one_phase= (bool) temp_byte;
+ buf += sizeof(temp_byte);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.formatID= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.gtrid_length= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(&temp, buf, sizeof(temp));
+ m_xid.bqual_length= le32toh(temp);
+ buf += sizeof(temp);
+ memcpy(m_xid.data, buf, m_xid.gtrid_length + m_xid.bqual_length);
+
+ xid= NULL;
+}
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+void XA_prepare_log_event::pack_info(Protocol *protocol)
+{
+ char buf[ser_buf_size];
+ char query[sizeof("XA COMMIT ONE PHASE") + 1 + sizeof(buf)];
+
+ /* RHS of the following assert is unknown to client sources */
+ compile_time_assert(ser_buf_size == XID::ser_buf_size);
+ m_xid.serialize(buf);
+ sprintf(query,
+ (one_phase ? "XA COMMIT %s ONE PHASE" : "XA PREPARE %s"),
+ buf);
+
+ protocol->store(query, strlen(query), &my_charset_bin);
+}
+#endif
+
+
+#ifndef MYSQL_CLIENT
+bool XA_prepare_log_event::write()
+{
+ uchar data[1 + 4 + 4 + 4];
+ uint8 one_phase_byte= one_phase;
+
+ data[0]= one_phase;
+ int4store(data+1, static_cast<XID*>(xid)->formatID);
+ int4store(data+(1+4), static_cast<XID*>(xid)->gtrid_length);
+ int4store(data+(1+4+4), static_cast<XID*>(xid)->bqual_length);
+
+ DBUG_ASSERT(xid_bufs_size == sizeof(data) - 1);
+
+ return write_header(sizeof(one_phase_byte) + xid_bufs_size +
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_data(data, sizeof(data)) ||
+ write_data((uchar*) static_cast<XID*>(xid)->data,
+ static_cast<XID*>(xid)->gtrid_length +
+ static_cast<XID*>(xid)->bqual_length) ||
+ write_footer();
+}
+#endif
+
+
+#ifdef MYSQL_CLIENT
+bool XA_prepare_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
+{
+ Write_on_release_cache cache(&print_event_info->head_cache, file,
+ Write_on_release_cache::FLUSH_F, this);
+ char buf[ser_buf_size];
+
+ m_xid.serialize(buf);
+
+ if (!print_event_info->short_form)
+ {
+ print_header(&cache, print_event_info, FALSE);
+ if (my_b_printf(&cache, "\tXID = %s\n", buf))
+ goto error;
+ }
+
+ if (my_b_printf(&cache, "XA PREPARE %s\n%s\n",
+ buf, print_event_info->delimiter))
+ goto error;
+
+ return cache.flush_data();
+error:
+ return TRUE;
+}
+#endif /* MYSQL_CLIENT */
+
+
+#if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT)
+int XA_prepare_log_event::record_gtid(const rpl_gtid *gtid, uint64 sub_id,
+ void **out_hton)
+{
+ int err;
+ xa_states c_state= thd->transaction.xid_state.xa_state;
+ thd->transaction.xid_state.xa_state= XA_ACTIVE;
+ err= rpl_global_gtid_slave_state->record_gtid(thd, gtid, sub_id, true,
+ false, out_hton);
+ thd->transaction.xid_state.xa_state= c_state;
+ return err;
+}
+
+
+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)
+ {
+ thd->lex->sql_command= SQLCOM_XA_PREPARE;
+ if ((res= trans_xa_prepare(thd)) == 0)
+ res= applier_reset_xa_trans(thd);
+ }
+ else
+ {
+ res= trans_xa_commit(thd);
+ thd->mdl_context.release_transactional_locks();
+ }
+
+ return res;
+}
+#endif /* !MYSQL_CLIENT */
+
+
+/**************************************************************************
User_var_log_event methods
**************************************************************************/
@@ -15069,7 +15403,6 @@ bool event_that_should_be_ignored(const char *buf)
event_type == PREVIOUS_GTIDS_LOG_EVENT ||
event_type == TRANSACTION_CONTEXT_EVENT ||
event_type == VIEW_CHANGE_EVENT ||
- event_type == XA_PREPARE_LOG_EVENT ||
(uint2korr(buf + FLAGS_OFFSET) & LOG_EVENT_IGNORABLE_F))
return 1;
return 0;
diff --git a/sql/log_event.h b/sql/log_event.h
index 339db75..76e42c9 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -217,6 +217,7 @@ class String;
#define GTID_HEADER_LEN 19
#define GTID_LIST_HEADER_LEN 4
#define START_ENCRYPTION_HEADER_LEN 0
+#define XA_PREPARE_HEADER_LEN 0
/*
Max number of possible extra bytes in a replication event compared to a
@@ -3017,6 +3018,30 @@ 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 record_gtid(const rpl_gtid *gtid, uint64 sub_id, void **out_hton);
+ virtual int do_apply_event(rpl_group_info *rgi);
+ enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+#endif
+};
+
+
/**
@class Xid_log_event
@@ -3029,14 +3054,14 @@ 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)
{
if (direct)
cache_type= Log_event::EVENT_NO_CACHE;
@@ -3056,15 +3081,88 @@ class Xid_log_event: public Log_event
#ifdef MYSQL_SERVER
bool write();
#endif
- bool is_valid() const { return 1; }
private:
#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
- virtual int do_apply_event(rpl_group_info *rgi);
- enum_skip_reason do_shall_skip(rpl_group_info *rgi);
+ int do_commit();
#endif
};
+
+/**
+ @class XA_prepare_log_event
+
+ Similar to Xid_log_event except that
+ - it is specific to XA transaction
+ - it carries out the prepare logics rather than the final committing
+ when @c one_phase member is off.
+ From the groupping perspective the event finalizes the current "prepare" group
+ started with XA START Query-log-event.
+ When @c one_phase is false Commit of Rollback for XA transaction are
+ logged separately to the prepare-group events so being a groups of
+ their own.
+ The XA COMMIT ONE PHASE normally is logged as simple BEGIN ... COMMIT,
+ so one_phase is always OFF. Still it is read and handled to be consistent
+ with other servers.
+*/
+
+class XA_prepare_log_event: public Xid_apply_log_event
+{
+protected:
+ /* The event_xid_t members were copied from handler.h */
+ struct event_xid_t
+ {
+ long formatID;
+ long gtrid_length;
+ long bqual_length;
+ char data[MYSQL_XIDDATASIZE]; // not \0-terminated !
+ char *serialize(char *buf) const;
+ };
+
+ /* size of serialization buffer is explained in $MYSQL/sql/xa.h. */
+ static const uint ser_buf_size=
+ 8 + 2 * MYSQL_XIDDATASIZE + 4 * sizeof(long) + 1;
+
+ /* Total size of buffers to hold serialized members of XID struct */
+ static const int xid_bufs_size= 12;
+ event_xid_t m_xid;
+ void *xid;
+ bool one_phase;
+
+public:
+#ifdef MYSQL_SERVER
+ XA_prepare_log_event(THD* thd_arg, XID *xid_arg, bool one_phase_arg):
+ Xid_apply_log_event(thd_arg), xid(xid_arg), one_phase(one_phase_arg)
+ {
+ cache_type= Log_event::EVENT_NO_CACHE;
+ }
+#ifdef HAVE_REPLICATION
+ void pack_info(Protocol* protocol);
+#endif /* HAVE_REPLICATION */
+#else
+ bool print(FILE* file, PRINT_EVENT_INFO* print_event_info);
+#endif
+ XA_prepare_log_event(const char* buf,
+ const Format_description_log_event *description_event);
+ ~XA_prepare_log_event() {}
+ Log_event_type get_type_code() { return XA_PREPARE_LOG_EVENT; }
+ int get_data_size()
+ {
+ return xid_bufs_size + m_xid.gtrid_length + m_xid.bqual_length;
+ }
+
+#ifdef MYSQL_SERVER
+ bool write();
+#endif
+
+private:
+#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION)
+ int record_gtid(const rpl_gtid *gtid, uint64 sub_id, void **out_hton);
+ int do_commit();
+#endif
+};
+
+
/**
@class User_var_log_event
@@ -3377,6 +3475,11 @@ class Gtid_log_event: public Log_event
uint64 seq_no;
uint64 commit_id;
uint32 domain_id;
+#ifdef MYSQL_SERVER
+ XID xid;
+#else
+ struct st_mysql_xid xid;
+#endif
uchar flags2;
/* Flags2. */
@@ -3405,6 +3508,8 @@ 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;
#ifdef MYSQL_SERVER
Gtid_log_event(THD *thd_arg, uint64 seq_no, uint32 domain_id, bool standalone,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index d05cd4d..e81a22b 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1500,12 +1500,19 @@ void THD::cleanup(void)
DBUG_ASSERT(cleanup_done == 0);
set_killed(KILL_CONNECTION);
-#ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE
if (transaction.xid_state.xa_state == XA_PREPARED)
{
-#error xid_state in the cache should be replaced by the allocated value
+ trans_xa_detach(this);
+ transaction.xid_state.xa_state= XA_NOTR;
+ transaction.xid_state.rm_error= 0;
+ }
+ else
+ {
+ transaction.xid_state.xa_state= XA_NOTR;
+ transaction.xid_state.rm_error= 0;
+ trans_rollback(this);
+ xid_cache_delete(this, &transaction.xid_state);
}
-#endif
#ifdef WITH_WSREP
if (wsrep_cs().state() != wsrep::client_state::s_none)
{
@@ -1520,11 +1527,6 @@ void THD::cleanup(void)
delete_dynamic(&user_var_events);
close_temporary_tables();
- transaction.xid_state.xa_state= XA_NOTR;
- transaction.xid_state.rm_error= 0;
- trans_rollback(this);
- xid_cache_delete(this, &transaction.xid_state);
-
DBUG_ASSERT(open_tables == NULL);
/*
If the thread was in the middle of an ongoing transaction (rolled
@@ -5803,7 +5805,7 @@ XID_STATE *xid_cache_search(THD *thd, XID *xid)
}
-bool xid_cache_insert(XID *xid, enum xa_states xa_state)
+bool xid_cache_insert(XID *xid, enum xa_states xa_state, bool is_binlogged)
{
XID_STATE *xs;
LF_PINS *pins;
@@ -5817,6 +5819,7 @@ bool xid_cache_insert(XID *xid, enum xa_states xa_state)
xs->xa_state=xa_state;
xs->xid.set(xid);
xs->rm_error=0;
+ xs->is_binlogged= is_binlogged;
if ((res= lf_hash_insert(&xid_cache, pins, xs)))
my_free(xs);
diff --git a/sql/sql_class.h b/sql/sql_class.h
index bbe9433..bc87a5a 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1284,6 +1284,19 @@ typedef struct st_xid_state {
/* Error reported by the Resource Manager (RM) to the Transaction Manager. */
uint rm_error;
XID_cache_element *xid_cache_element;
+ /*
+ Binary logging status.
+ It is set to TRUE at XA PREPARE if the transaction was written
+ to the binlog.
+ Naturally FALSE means the transaction was not written to
+ the binlog. Happens if the trnasaction did not modify anything
+ or binlogging was turned off. In that case we shouldn't binlog
+ the consequent XA COMMIT/ROLLBACK.
+ The recovered transaction after server restart sets it to TRUE always.
+ That can cause inconsistencies (shoud be fixed?).
+ */
+ bool is_binlogged;
+ bool registered_for_binlog;
/**
Check that XA transaction has an uncommitted work. Report an error
@@ -1307,12 +1320,22 @@ typedef struct st_xid_state {
}
return false;
}
+
+ void reset()
+ {
+ xid.null();
+ is_binlogged= false;
+ }
+ void set_binlogged()
+ { is_binlogged= true; }
+ void unset_binlogged()
+ { is_binlogged= false; }
} XID_STATE;
void xid_cache_init(void);
void xid_cache_free(void);
XID_STATE *xid_cache_search(THD *thd, XID *xid);
-bool xid_cache_insert(XID *xid, enum xa_states xa_state);
+bool xid_cache_insert(XID *xid, enum xa_states xa_state, bool is_binlogged);
bool xid_cache_insert(THD *thd, XID_STATE *xid_state);
void xid_cache_delete(THD *thd, XID_STATE *xid_state);
int xid_cache_iterate(THD *thd, my_hash_walk_action action, void *argument);
@@ -1956,6 +1979,17 @@ struct Ha_data
Lifetime: one user connection.
*/
void *ha_ptr;
+ /**
+ A memorizer to engine specific "native" transaction object to provide
+ storage engine detach-re-attach facility.
+ The server level transaction object can dissociate from storage engine
+ transactions. The released "native" transaction reference
+ can be hold in the member until it is reconciled later.
+ Lifetime: Depends on caller of @c hton::replace_native_transaction_in_thd.
+ For instance in the case of slave server applier handling XA transaction
+ it is from XA START to XA PREPARE.
+ */
+ void *ha_ptr_backup;
/**
0: Life time: one statement within a transaction. If @@autocommit is
on, also represents the entire transaction.
@@ -1972,7 +2006,7 @@ struct Ha_data
non-NULL: engine is bound to this thread, engine shutdown forbidden
*/
plugin_ref lock;
- Ha_data() :ha_ptr(NULL) {}
+ Ha_data() :ha_ptr(NULL), ha_ptr_backup(NULL) {}
};
/**
@@ -2653,7 +2687,7 @@ class THD: public THD_count, /* this must be first */
then.
*/
if (!xid_state.rm_error)
- xid_state.xid.null();
+ xid_state.reset();
free_root(&mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_VOID_RETURN;
}
@@ -6568,6 +6602,18 @@ inline bool add_group_to_list(THD *thd, Item *item, bool asc)
return thd->lex->current_select->add_group_to_list(thd, item, asc);
}
+/**
+ @param THD thread context
+ @param hton pointer to handlerton
+ @return address of the placeholder of handlerton's specific transaction
+ object (data)
+*/
+
+inline void **thd_ha_data_backup(const THD *thd, const struct handlerton *hton)
+{
+ return (void **) &thd->ha_data[hton->slot].ha_ptr_backup;
+}
+
inline Item *and_conds(THD *thd, Item *a, Item *b)
{
if (!b) return a;
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 5adf5c7..e82a2ee 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -1407,6 +1407,7 @@ void do_handle_one_connection(CONNECT *connect)
end_thread:
close_connection(thd);
+ thd->get_stmt_da()->reset_diagnostics_area();
if (thd->userstat_running)
update_global_user_stats(thd, create_user, time(NULL));
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 91f43fd..91fcdcf 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -6274,6 +6274,9 @@ mysql_execute_command(THD *thd)
{
bool rollback_failed= trans_xa_rollback(thd);
thd->mdl_context.release_transactional_locks();
+
+ DBUG_EXECUTE_IF("crash_after_xa_rollback", DBUG_SUICIDE(););
+
if (rollback_failed)
{
WSREP_DEBUG("XA rollback failed, MDL released: %lld",
diff --git a/sql/transaction.cc b/sql/transaction.cc
index 4d61d2a..c7b4b80 100644
--- a/sql/transaction.cc
+++ b/sql/transaction.cc
@@ -780,6 +780,164 @@ bool trans_release_savepoint(THD *thd, LEX_CSTRING name)
/**
+ Detach the current XA transaction;
+
+ @param thd Current thread
+
+ @retval FALSE Success
+ @retval TRUE Failure
+*/
+
+bool trans_xa_detach(THD *thd)
+{
+ XID_STATE *xid_s= &thd->transaction.xid_state;
+ Ha_trx_info *ha_info, *ha_info_next;
+
+ DBUG_ENTER("trans_xa_detach");
+
+// #7974 DBUG_ASSERT(xid_s->xa_state == XA_PREPARED &&
+// xid_cache_search(thd, &xid_s->xid));
+
+ xid_cache_delete(thd, xid_s);
+ if (xid_cache_insert(&xid_s->xid, XA_PREPARED, xid_s->is_binlogged))
+ DBUG_RETURN(TRUE);
+
+ for (ha_info= thd->transaction.all.ha_list;
+ ha_info;
+ ha_info= ha_info_next)
+ {
+ ha_info_next= ha_info->next();
+ ha_info->reset(); /* keep it conveniently zero-filled */
+ }
+
+ thd->transaction.all.ha_list= 0;
+ thd->transaction.all.no_2pc= 0;
+
+ DBUG_RETURN(FALSE);
+}
+
+
+void attach_native_trx(THD *thd);
+/**
+ This is a specific to "slave" applier collection of standard cleanup
+ actions to reset XA transaction states at the end of XA prepare rather than
+ to do it at the transaction commit, see @c ha_commit_one_phase.
+ THD of the slave applier is dissociated from a transaction object in engine
+ that continues to exist there.
+
+ @param THD current thread
+ @return the value of is_error()
+*/
+
+bool applier_reset_xa_trans(THD *thd)
+{
+ XID_STATE *xid_s= &thd->transaction.xid_state;
+
+ 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"));
+ xid_cache_delete(thd, xid_s);
+ if (xid_cache_insert(&xid_s->xid, XA_PREPARED, xid_s->is_binlogged))
+ return true;
+
+ attach_native_trx(thd);
+ thd->transaction.cleanup();
+ thd->transaction.xid_state.xa_state= XA_NOTR;
+ thd->mdl_context.release_transactional_locks();
+
+ return thd->is_error();
+#ifdef p7974
+ Transaction_ctx *trn_ctx= thd->get_transaction();
+ XID_STATE *xid_state= trn_ctx->xid_state();
+ /*
+ In the following the server transaction state gets reset for
+ a slave applier thread similarly to xa_commit logics
+ except commit does not run.
+ */
+ thd->variables.option_bits&= ~OPTION_BEGIN;
+ trn_ctx->reset_unsafe_rollback_flags(Transaction_ctx::STMT);
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ /* Server transaction ctx is detached from THD */
+ transaction_cache_detach(trn_ctx);
+ xid_state->reset();
+ /*
+ The current engine transactions is detached from THD, and
+ previously saved is restored.
+ */
+ attach_native_trx(thd);
+ trn_ctx->set_ha_trx_info(Transaction_ctx::SESSION, NULL);
+ trn_ctx->set_no_2pc(Transaction_ctx::SESSION, false);
+ trn_ctx->cleanup();
+#ifdef HAVE_PSI_TRANSACTION_INTERFACE
+ MYSQL_COMMIT_TRANSACTION(thd->m_transaction_psi);
+ thd->m_transaction_psi= NULL;
+#endif
+
+#endif /*p7974*/
+ return thd->is_error();
+}
+
+
+/**
+ The function detaches existing storage engines transaction
+ context from thd. Backup area to save it is provided to low level
+ storage engine function.
+
+ is invoked by plugin_foreach() after
+ trans_xa_start() for each storage engine.
+
+ @param[in,out] thd Thread context
+ @param plugin Reference to handlerton
+
+ @return FALSE on success, TRUE otherwise.
+*/
+
+my_bool detach_native_trx(THD *thd, plugin_ref plugin, void *unused)
+{
+ handlerton *hton= plugin_hton(plugin);
+ if (hton->replace_native_transaction_in_thd)
+ hton->replace_native_transaction_in_thd(thd, NULL,
+ thd_ha_data_backup(thd, hton));
+
+ return FALSE;
+
+}
+
+/**
+ The function restores previously saved storage engine transaction context.
+
+ @param thd Thread context
+*/
+void attach_native_trx(THD *thd)
+{
+ Ha_trx_info *ha_info= thd->transaction.all.ha_list;
+ Ha_trx_info *ha_info_next;
+
+ if (ha_info)
+ {
+ for (; ha_info; ha_info= ha_info_next)
+ {
+ handlerton *hton= ha_info->ht();
+ if (hton->replace_native_transaction_in_thd)
+ {
+ /* restore the saved original engine transaction's link with thd */
+ void **trx_backup= thd_ha_data_backup(thd, hton);
+
+ hton->
+ replace_native_transaction_in_thd(thd, *trx_backup, NULL);
+ *trx_backup= NULL;
+ }
+ ha_info_next= ha_info->next();
+ ha_info->reset();
+ }
+ }
+ thd->transaction.all.ha_list= 0;
+ thd->transaction.all.no_2pc= 0;
+}
+
+
+/**
Starts an XA transaction with the given xid value.
@param thd Current thread
@@ -823,9 +981,19 @@ bool trans_xa_start(THD *thd)
trans_rollback(thd);
DBUG_RETURN(true);
}
+
+ if (thd->variables.pseudo_slave_mode || thd->slave_thread)
+ {
+ /*
+ In case of slave thread applier or processing binlog by client,
+ detach the "native" thd's trx in favor of dynamically created.
+ */
+ plugin_foreach(thd, detach_native_trx,
+ MYSQL_STORAGE_ENGINE_PLUGIN, NULL);
+ }
+
DBUG_RETURN(FALSE);
}
-
DBUG_RETURN(TRUE);
}
@@ -870,6 +1038,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.xa_state != XA_IDLE)
@@ -884,10 +1054,14 @@ bool trans_xa_prepare(THD *thd)
my_error(ER_XA_RBROLLBACK, MYF(0));
}
else
+ {
+ res= 0;
thd->transaction.xid_state.xa_state= XA_PREPARED;
+ if (thd->variables.pseudo_slave_mode)
+ res= applier_reset_xa_trans(thd);
+ }
- DBUG_RETURN(thd->is_error() ||
- thd->transaction.xid_state.xa_state != XA_PREPARED);
+ DBUG_RETURN(res);
}
@@ -918,10 +1092,25 @@ bool trans_xa_commit(THD *thd)
res= !xs;
if (res)
my_error(ER_XAER_NOTA, MYF(0));
+ else if (thd->in_multi_stmt_transaction_mode())
+ {
+ my_error(ER_XAER_RMFAIL, MYF(0),
+ xa_state_names[thd->transaction.xid_state.xa_state]);
+ res= TRUE;
+ }
else
{
res= xa_trans_rolled_back(xs);
ha_commit_or_rollback_by_xid(thd->lex->xid, !res);
+ if((WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) &&
+ xs->is_binlogged)
+ {
+ res= thd->binlog_query(THD::THD::STMT_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ FALSE, FALSE, FALSE, 0);
+ }
+ else
+ res= 0;
xid_cache_delete(thd, xs);
}
DBUG_RETURN(res);
@@ -962,8 +1151,17 @@ bool trans_xa_commit(THD *thd)
{
DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
- res= MY_TEST(ha_commit_one_phase(thd, 1));
- if (res)
+ if((WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) &&
+ thd->transaction.xid_state.is_binlogged)
+ {
+ res= thd->binlog_query(THD::THD::STMT_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ FALSE, FALSE, FALSE, 0);
+ }
+ else
+ res= 0;
+
+ if (res || (res= MY_TEST(ha_commit_one_phase(thd, 1))))
my_error(ER_XAER_RMERR, MYF(0));
}
}
@@ -1016,19 +1214,37 @@ bool trans_xa_rollback(THD *thd)
else
{
xa_trans_rolled_back(xs);
- ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
+ if (ha_commit_or_rollback_by_xid(thd->lex->xid, 0) == 0 &&
+ xs->is_binlogged &&
+ (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()))
+ thd->binlog_query(THD::THD::STMT_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ FALSE, FALSE, FALSE, 0);
xid_cache_delete(thd, xs);
}
DBUG_RETURN(thd->get_stmt_da()->is_error());
}
- if (xa_state != XA_IDLE && xa_state != XA_PREPARED && xa_state != XA_ROLLBACK_ONLY)
+ if (xa_state != XA_IDLE && xa_state != XA_PREPARED &&
+ xa_state != XA_ROLLBACK_ONLY)
{
my_error(ER_XAER_RMFAIL, MYF(0), xa_state_names[xa_state]);
DBUG_RETURN(TRUE);
}
- res= xa_trans_force_rollback(thd);
+ if(xa_state == XA_PREPARED && thd->transaction.xid_state.is_binlogged &&
+ (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()))
+ {
+ res= thd->binlog_query(THD::THD::STMT_QUERY_TYPE,
+ thd->query(), thd->query_length(),
+ FALSE, FALSE, FALSE, 0);
+ }
+ else
+ res= 0;
+
+ res= res || xa_trans_force_rollback(thd);
+ if (res || (res= MY_TEST(xa_trans_force_rollback(thd))))
+ my_error(ER_XAER_RMERR, MYF(0));
thd->variables.option_bits&= ~(OPTION_BEGIN | OPTION_KEEP_LOG);
thd->transaction.all.reset();
diff --git a/sql/transaction.h b/sql/transaction.h
index 7e34693..1109a61 100644
--- a/sql/transaction.h
+++ b/sql/transaction.h
@@ -42,6 +42,8 @@ bool trans_xa_end(THD *thd);
bool trans_xa_prepare(THD *thd);
bool trans_xa_commit(THD *thd);
bool trans_xa_rollback(THD *thd);
+bool trans_xa_detach(THD *thd);
+bool applier_reset_xa_trans(THD *thd);
void trans_reset_one_shot_chistics(THD *thd);
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 4a98109..5ec469c 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -1835,7 +1835,7 @@ thd_innodb_tmpdir(
/** Obtain the InnoDB transaction of a MySQL thread.
@param[in,out] thd thread handle
@return reference to transaction pointer */
-static trx_t* thd_to_trx(THD* thd)
+static trx_t*& thd_to_trx(THD* thd)
{
return *reinterpret_cast<trx_t**>(thd_ha_data(thd, innodb_hton_ptr));
}
@@ -3642,6 +3642,46 @@ static ulonglong innodb_prepare_commit_versioned(THD* thd, ulonglong *trx_id)
return 0;
}
+/** InnoDB transaction object that is currently associated with THD is
+replaced with that of the 2nd argument. The previous value is
+returned through the 3rd argument's buffer, unless it's NULL. When
+the buffer is not provided (value NULL) that should mean the caller
+restores previously saved association so the current trx has to be
+additionally freed from all association with MYSQL.
+
+@param[in,out] thd MySQL thread handle
+@param[in] new_trx_arg replacement trx_t
+@param[in,out] ptr_trx_arg pointer to a buffer to store old trx_t */
+static
+void
+innodb_replace_trx_in_thd(
+ THD* thd,
+ void* new_trx_arg,
+ void** ptr_trx_arg)
+{
+ trx_t*& trx = thd_to_trx(thd);
+
+ ut_ad(new_trx_arg == NULL
+ || (((trx_t*) new_trx_arg)->mysql_thd == thd
+ && !((trx_t*) new_trx_arg)->is_recovered));
+
+ if (ptr_trx_arg) {
+ *ptr_trx_arg = trx;
+
+ ut_ad(trx == NULL
+ || (trx->mysql_thd == thd && !trx->is_recovered));
+
+ } else if (trx->state == TRX_STATE_NOT_STARTED) {
+ ut_ad(thd == trx->mysql_thd);
+ trx->read_view.close();
+ } else {
+ ut_ad(thd == trx->mysql_thd);
+ ut_ad(trx_state_eq(trx, TRX_STATE_PREPARED));
+ trx_disconnect_prepared(trx);
+ }
+ trx = static_cast<trx_t*>(new_trx_arg);
+}
+
/** Initialize and normalize innodb_buffer_pool_size. */
static void innodb_buffer_pool_size_init()
{
@@ -4209,6 +4249,9 @@ static int innodb_init(void* p)
innobase_hton->prepare_commit_versioned
= innodb_prepare_commit_versioned;
+ innobase_hton->replace_native_transaction_in_thd =
+ innodb_replace_trx_in_thd;
+
innodb_remember_check_sysvar_funcs();
compile_time_assert(DATA_MYSQL_TRUE_VARCHAR == MYSQL_TYPE_VARCHAR);
@@ -5129,6 +5172,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 42b5d05..c7a6a21 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -1337,7 +1337,21 @@ trx_commit_in_memory(
transactions now that it no longer holds any user locks. */
ut_ad(trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY));
- DEBUG_SYNC_C("after_trx_committed_in_memory");
+#ifndef DBUG_OFF
+ const bool debug_sync = trx->mysql_thd &&
+ trx->has_logged_persistent();
+ /* In case of this function is called from a stack executing
+ THD::release_resources -> ...
+ innobase_connection_close() ->
+ trx_rollback_for_mysql... -> .
+ mysql's thd does not seem to have
+ thd->debug_sync_control defined any longer. However the stack
+ is possible only with a prepared trx not updating any data.
+ */
+ if (debug_sync) {
+ DEBUG_SYNC_C("after_trx_committed_in_memory");
+ }
+#endif
if (trx->read_only || trx->rsegs.m_redo.rseg == NULL) {
MONITOR_INC(MONITOR_TRX_RO_COMMIT);
1
0
revision-id: bd41f7ab21b467634ee59fa199451b4ffcb1767d (mariadb-10.1.38-93-gbd41f7ab21b)
parent(s): 8409f451737516e653b078cc529cbb8439a9e3d1
author: Jan Lindström
committer: Jan Lindström
timestamp: 2019-03-30 12:35:06 +0200
message:
Retest
1
0
29 Mar '19
revision-id: 8409f451737516e653b078cc529cbb8439a9e3d1 (mariadb-10.1.38-92-g8409f451737)
parent(s): d0116e10a5da52503a89a413e481996ce3f65e63
author: Jan Lindström
committer: Jan Lindström
timestamp: 2019-03-29 13:20:53 +0200
message:
MDEV-18464: Port kill_one_trx fixes from 10.4 to 10.1
Pushed the decision for innodb transaction and system
locking down to lock_trx_handle_wait() level. With this,
we can avoid releasing these mutexes for executions
where these mutexes were acquired upfront.
To know who initiated abort a new abort_initiator
field is added to THD. This field is protected using
LOCK_thd_data mutex and we add setter and getter
for this field. Both wsrep thread and replication
thread starting a thread abort will mark InnoDB
as abort initiator. If user kills a query no
abort initiator is set. Thus, we can identify
safely in InnoDB case has this thread already
acquired lock sys and trx mutexes or not. For
wsrep and replication abort we have acquired them
and for external user kill we have not.
This patch will also fix BF aborting of native threads, e.g.
threads which have declared wsrep_on=OFF. Earlier, we have
used, for innodb trx locks, was_chosen_as_deadlock_victim
flag, for marking inodb transactions, which are victims for
wsrep BF abort. With native threads (wsrep_on==OFF), re-using
was_chosen_as_deadlock_victim flag may lead to inteference
with real deadlock.
---
sql/sql_class.cc | 32 ++++++++++++-
sql/sql_class.h | 5 ++
storage/innobase/handler/ha_innodb.cc | 61 ++++++++++++------------
storage/innobase/include/ha_prototypes.h | 11 +++++
storage/innobase/include/lock0lock.h | 19 ++++++--
storage/innobase/include/trx0trx.h | 13 +-----
storage/innobase/lock/lock0lock.cc | 80 +++++++++++++++++++++++++-------
storage/innobase/row/row0sel.cc | 24 +++++++++-
storage/innobase/trx/trx0roll.cc | 2 +-
storage/innobase/trx/trx0trx.cc | 7 +--
storage/xtradb/handler/ha_innodb.cc | 59 +++++++++++------------
storage/xtradb/include/ha_prototypes.h | 13 +++++-
storage/xtradb/include/lock0lock.h | 19 ++++++--
storage/xtradb/include/trx0trx.h | 13 +-----
storage/xtradb/lock/lock0lock.cc | 80 +++++++++++++++++++++++++-------
storage/xtradb/row/row0sel.cc | 26 ++++++++++-
storage/xtradb/trx/trx0roll.cc | 2 +-
storage/xtradb/trx/trx0trx.cc | 7 +--
18 files changed, 325 insertions(+), 148 deletions(-)
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 639c7c1784a..288c380a323 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -622,6 +622,35 @@ void thd_set_ha_data(THD *thd, const struct handlerton *hton,
}
+/**
+ Provides setter for thread abort initiator. Caller should
+ already own LOCK_thd_data mutex.
+
+ @param thd THD object
+ @param hton handlerton or NULL
+*/
+extern "C"
+void thd_set_abort_initiator(THD *thd, const struct handlerton *hton)
+{
+ mysql_mutex_assert_owner(&thd->LOCK_thd_data);
+ thd->abort_initiator= hton;
+}
+
+
+/**
+ Provides getter for thread abort initiator. Caller should
+ already own LOCK_thd_data mutex.
+
+ @param thd THD object
+ @retun handlerton object or NULL
+*/
+extern "C"
+const struct handlerton* thd_get_abort_initiator(THD* thd)
+{
+ mysql_mutex_assert_owner(&thd->LOCK_thd_data);
+ return thd->abort_initiator;
+}
+
/**
Allow storage engine to wakeup commits waiting in THD::wait_for_prior_commit.
@see thd_wakeup_subsequent_commits() definition in plugin.h
@@ -4526,7 +4555,7 @@ thd_need_wait_for(const MYSQL_THD thd)
not harmful, but could lead to unnecessary kill and retry, so best avoided).
*/
extern "C" void
-thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd)
+thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd, const handlerton* abort_initiator=NULL)
{
rpl_group_info *rgi;
rpl_group_info *other_rgi;
@@ -4562,6 +4591,7 @@ thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd)
*/
other_rgi->killed_for_retry= rpl_group_info::RETRY_KILL_KILLED;
mysql_mutex_lock(&other_thd->LOCK_thd_data);
+ other_thd->abort_initiator= abort_initiator;
other_thd->awake(KILL_CONNECTION);
mysql_mutex_unlock(&other_thd->LOCK_thd_data);
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index de52cec0f38..c94c9465ac5 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -4141,6 +4141,11 @@ class THD :public Statement,
/* Handling of timeouts for commands */
thr_timer_t query_timer;
+
+ /* Thread abort initiator. Used on thd::awake() to identify the
+ storage engine initing the thread abort or NULL. */
+ const struct handlerton* abort_initiator;
+
public:
void set_query_timer()
{
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index cd605b6b791..a06dfec8868 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -1862,6 +1862,29 @@ thd_to_trx(
{
return(*(trx_t**) thd_ha_data(thd, innodb_hton_ptr));
}
+
+/* Setter and getter for thread abort initiator. */
+extern "C" void thd_set_abort_initiator(MYSQL_THD thd, const struct handlerton *hton);
+extern "C" const struct handlerton* thd_get_abort_initiator(MYSQL_THD thd);
+
+extern "C" void thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd, const struct handlerton* hton);
+
+/* Check if thread abort initiator is InnoDB.
+@param thd MYSQL thread
+@return true if abort initiator is InnoDB */
+bool innodb_is_abort_initiator(THD* thd)
+{
+ return (thd_get_abort_initiator(thd) == innodb_hton_ptr);
+}
+
+/* Helper to report replication waits.
+@param thd MYSQL thread
+@param thd MYSQL victim thread */
+void innodb_report_wait_for(THD * thd, THD* other_thd)
+{
+ thd_report_wait_for(thd, other_thd, innodb_hton_ptr);
+}
+
#ifdef WITH_WSREP
ulonglong
thd_to_trx_id(
@@ -4929,8 +4952,6 @@ static void innobase_kill_query(handlerton*, THD* thd, enum thd_kill_levels)
/* if victim has been signaled by BF thread and/or aborting
is already progressing, following query aborting is not necessary
any more.
- Also, BF thread should own trx mutex for the victim, which would
- conflict with trx_mutex_enter() below
*/
DBUG_VOID_RETURN;
}
@@ -4939,34 +4960,8 @@ static void innobase_kill_query(handlerton*, THD* thd, enum thd_kill_levels)
if (trx_t* trx = thd_to_trx(thd)) {
ut_ad(trx->mysql_thd == thd);
- switch (trx->abort_type) {
-#ifdef WITH_WSREP
- case TRX_WSREP_ABORT:
- break;
-#endif
- case TRX_SERVER_ABORT:
- if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
- lock_mutex_enter();
- }
- /* fall through */
- case TRX_REPLICATION_ABORT:
- trx_mutex_enter(trx);
- }
/* Cancel a pending lock request if there are any */
- lock_trx_handle_wait(trx);
- switch (trx->abort_type) {
-#ifdef WITH_WSREP
- case TRX_WSREP_ABORT:
- break;
-#endif
- case TRX_SERVER_ABORT:
- if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
- lock_mutex_exit();
- }
- /* fall through */
- case TRX_REPLICATION_ABORT:
- trx_mutex_exit(trx);
- }
+ lock_trx_handle_wait_for_mysql(trx);
}
DBUG_VOID_RETURN;
@@ -18683,6 +18678,11 @@ wsrep_innobase_kill_one_trx(
wsrep_thd_ws_handle(thd)->trx_id);
wsrep_thd_LOCK(thd);
+
+ /* We mark thd as a victim and InnoDB as abort initiator, this thread
+ is already marked as BF victim. */
+ thd_set_abort_initiator(thd, innodb_hton_ptr);
+
DBUG_EXECUTE_IF("sync.wsrep_after_BF_victim_lock",
{
const char act[]=
@@ -18866,7 +18866,6 @@ wsrep_abort_transaction(
my_bool signal)
{
DBUG_ENTER("wsrep_innobase_abort_thd");
-
trx_t* victim_trx = thd_to_trx(victim_thd);
trx_t* bf_trx = (bf_thd) ? thd_to_trx(bf_thd) : NULL;
@@ -18878,12 +18877,10 @@ wsrep_abort_transaction(
if (victim_trx) {
lock_mutex_enter();
trx_mutex_enter(victim_trx);
- victim_trx->abort_type = TRX_WSREP_ABORT;
int rcode = wsrep_innobase_kill_one_trx(bf_thd, bf_trx,
victim_trx, signal);
trx_mutex_exit(victim_trx);
lock_mutex_exit();
- victim_trx->abort_type = TRX_SERVER_ABORT;
wsrep_srv_conc_cancel_wait(victim_trx);
DBUG_RETURN(rcode);
} else {
diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h
index be423c8bd01..dd7dd2bc8a2 100644
--- a/storage/innobase/include/ha_prototypes.h
+++ b/storage/innobase/include/ha_prototypes.h
@@ -1,6 +1,7 @@
/*****************************************************************************
Copyright (c) 2006, 2016, Oracle and/or its affiliates. All Rights Reserved.
+Copyright (c) 2017, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -665,4 +666,14 @@ normalize_table_name_low(
ibool set_lower_case); /*!< in: TRUE if we want to set
name to lower case */
+/* Check if thread abort initiator is InnoDB.
+@param thd MYSQL thread
+@return true if abort initiator is InnoDB */
+bool innodb_is_abort_initiator(THD* thd);
+
+/* Helper to report replication waits.
+@param thd MYSQL thread
+@param thd MYSQL victim thread */
+void innodb_report_wait_for(THD * thd, THD* other_thd);
+
#endif /* HA_INNODB_PROTOTYPES_H */
diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h
index b4259cd4851..e3883888ab0 100644
--- a/storage/innobase/include/lock0lock.h
+++ b/storage/innobase/include/lock0lock.h
@@ -821,13 +821,22 @@ lock_unlock_table_autoinc(
/*********************************************************************//**
Check whether the transaction has already been rolled back because it
was selected as a deadlock victim, or if it has to wait then cancel
-the wait lock.
+the wait lock. This function should be called only when holding
+lock sys mutex and trx mutex.
+
+@param trx transaction object
+@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
+dberr_t lock_trx_handle_wait(trx_t* trx)
+ MY_ATTRIBUTE((nonnull));
+
+/*********************************************************************//**
+Handle lock waits for MySQL interface.
+See detailed description on lock0lock.cc
+
+@param trx transaction object
@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
UNIV_INTERN
-dberr_t
-lock_trx_handle_wait(
-/*=================*/
- trx_t* trx) /*!< in/out: trx lock state */
+dberr_t lock_trx_handle_wait_for_mysql(trx_t* trx)
MY_ATTRIBUTE((nonnull));
/*********************************************************************//**
Get the number of locks on a table.
diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h
index fe16b8272b8..2a71e92fc58 100644
--- a/storage/innobase/include/trx0trx.h
+++ b/storage/innobase/include/trx0trx.h
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2015, 2018, MariaDB Corporation.
+Copyright (c) 2015, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -623,7 +623,6 @@ struct trx_lock_t {
lock_sys->mutex. Otherwise, this may
only be modified by the thread that is
serving the running transaction. */
-
mem_heap_t* lock_heap; /*!< memory heap for trx_locks;
protected by lock_sys->mutex */
@@ -695,14 +694,6 @@ lock_rec_convert_impl_to_expl()) will access transactions associated
to other connections. The locks of transactions are protected by
lock_sys->mutex and sometimes by trx->mutex. */
-enum trx_abort_t {
- TRX_SERVER_ABORT = 0,
-#ifdef WITH_WSREP
- TRX_WSREP_ABORT,
-#endif
- TRX_REPLICATION_ABORT
-};
-
struct trx_t{
ulint magic_n;
@@ -880,8 +871,6 @@ struct trx_t{
/*------------------------------*/
THD* mysql_thd; /*!< MySQL thread handle corresponding
to this trx, or NULL */
- trx_abort_t abort_type; /*!< Transaction abort type*/
-
const char* mysql_log_file_name;
/*!< if MySQL binlog is used, this field
contains a pointer to the latest file
diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc
index f06fcd6c4d8..00580a80b62 100644
--- a/storage/innobase/lock/lock0lock.cc
+++ b/storage/innobase/lock/lock0lock.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2014, 2018, MariaDB Corporation.
+Copyright (c) 2014, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -402,7 +402,6 @@ lock_grant(
lock_t* lock, /*!< in/out: waiting lock request */
bool owns_trx_mutex); /*!< in: whether lock->trx->mutex is owned */
-extern "C" void thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd);
extern "C" int thd_need_wait_for(const MYSQL_THD thd);
extern "C"
int thd_need_ordering_with(const MYSQL_THD thd, const MYSQL_THD other_thd);
@@ -1793,10 +1792,8 @@ wsrep_kill_victim(
}
}
- lock->trx->abort_type = TRX_WSREP_ABORT;
wsrep_innobase_kill_one_trx(trx->mysql_thd,
(const trx_t*) trx, lock->trx, TRUE);
- lock->trx->abort_type = TRX_SERVER_ABORT;
}
}
}
@@ -4782,12 +4779,10 @@ lock_report_waiters_to_mysql(
if (w_trx->id != victim_trx_id) {
/* If thd_report_wait_for() decides to kill the
transaction, then we will get a call back into
- innobase_kill_query. We mark this by setting
- current_lock_mutex_owner, so we can avoid trying
- to recursively take lock_sys->mutex. */
- w_trx->abort_type = TRX_REPLICATION_ABORT;
- thd_report_wait_for(mysql_thd, w_trx->mysql_thd);
- w_trx->abort_type = TRX_SERVER_ABORT;
+ innobase_kill_query.*/
+ trx_mutex_enter(w_trx);
+ innodb_report_wait_for(mysql_thd, w_trx->mysql_thd);
+ trx_mutex_exit(w_trx);
}
++i;
}
@@ -7970,13 +7965,12 @@ lock_trx_release_locks(
/*********************************************************************//**
Check whether the transaction has already been rolled back because it
was selected as a deadlock victim, or if it has to wait then cancel
-the wait lock.
+the wait lock. This function should be called only when holding
+lock sys mutex and trx mutex.
+
+@param trx transaction object
@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
-UNIV_INTERN
-dberr_t
-lock_trx_handle_wait(
-/*=================*/
- trx_t* trx) /*!< in/out: trx lock state */
+dberr_t lock_trx_handle_wait(trx_t* trx)
{
ut_ad(lock_mutex_own());
ut_ad(trx_mutex_own(trx));
@@ -7993,6 +7987,60 @@ lock_trx_handle_wait(
return DB_LOCK_WAIT;
}
+/*********************************************************************//**
+Handle lock waits for MySQL interface.
+
+This function should called only from handler API i.e. ha_innodb.cc.
+Call trace: THD::awake() (we hold LOCK_thd_data) -> ha_kill_query()
+-> hton->kill_query() -> innobase_kill_query() ->
+
+There is 3 possible cases:
+
+(1) wsrep high priority thread aborting lock holder i.e. victim thread
+wsrep_abort_transaction() (takes lock sys mutex and trx mutex for
+victim) -> marks abort initiator as InnoDB -> wsrep_thd_awake()
+-> thd->awake(KILL_QUERY) (while holding LOCK_thd_data).
+thd->abort_initiator is protected by LOCK_thd_data and we are
+holding it so it can't change during this function.
+Thus, we hold lock sys, trx and LOCK_thd_data mutexes.
+
+(2) replication thread aborting lock holder
+lock_report_waiters_to_mysql() (we hold lock sys mutes and take trx
+mutex to victim) -> innodb_report_wait_for() -> thd_report_wait_for()
+we mark abort initiator as provided hton-> thd->awake(KILL_CONNECTION)
+(while holding LOCK_thd_data).
+thd->abort_initiator is protected by LOCK_thd_data and we are
+holding it so it can't change during this function.
+Thus, we hold lock sys, trx and LOCK_thd_data mutexes.
+
+(3) User action KILL [HARD | SOFT] [CONNECTION | QUERY [ID] ]
+[thread_id | USER user_name | query_id]
+thd->awake() (while holding LOCK_thd_data) no other mutexes.
+Thus, thd->abort_initiator is protected by LOCK_thd_data and
+we are holding it so it can't change during this function.
+We need to take lock sys mutex and trx mutex.
+
+@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
+UNIV_INTERN
+dberr_t lock_trx_handle_wait_for_mysql(trx_t* trx)
+{
+ dberr_t err= DB_SUCCESS;
+
+ if (trx->mysql_thd && innodb_is_abort_initiator(trx->mysql_thd)) {
+ err = lock_trx_handle_wait(trx);
+ } else {
+ ut_ad(!lock_mutex_own());
+ ut_ad(!trx_mutex_own(trx));
+ lock_mutex_enter();
+ trx_mutex_enter(trx);
+ err = lock_trx_handle_wait(trx);
+ lock_mutex_exit();
+ trx_mutex_exit(trx);
+ }
+
+ return err;
+}
+
/*********************************************************************//**
Get the number of locks on a table.
@return number of locks */
diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc
index 06bf4cc30c0..223aa5b3c39 100644
--- a/storage/innobase/row/row0sel.cc
+++ b/storage/innobase/row/row0sel.cc
@@ -4744,7 +4744,29 @@ row_search_for_mysql(
/* Check whether it was a deadlock or not, if not
a deadlock and the transaction had to wait then
- release the lock it is waiting on. */
+ release the lock it is waiting on.
+
+ There is two possible cases here:
+
+ (1) wsrep high priority thread or replication
+ thread selects this thread as a victim. Both
+ will take lock sys mutex and trx mutex before
+ entering thread abort. Thus, either this
+ thread will check deadlock below or this
+ thread will be aborted and then we find it
+ out below. Thus concurrent access in
+ this case is protected by lock sys and trx
+ mutex.
+
+ (2) User action kill. Here killing thread either
+ takes lock sys mutex and trx mutex before
+ aborting this thread and we will find it out
+ when they are released or this thread
+ acquires them fist and we check deadlock
+ and then killing thread can proceed. Thus
+ concurrent access in this case is also
+ protected by lock sys and trx mutex.
+ */
lock_mutex_enter();
trx_mutex_enter(trx);
diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc
index 3fd71aff23a..fa2bced5ce0 100644
--- a/storage/innobase/trx/trx0roll.cc
+++ b/storage/innobase/trx/trx0roll.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2016, 2018, MariaDB Corporation.
+Copyright (c) 2016, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc
index f36aabba8b4..5359e51c17c 100644
--- a/storage/innobase/trx/trx0trx.cc
+++ b/storage/innobase/trx/trx0trx.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2015, 2018, MariaDB Corporation.
+Copyright (c) 2015, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -1339,11 +1339,6 @@ trx_commit_in_memory(
ut_ad(!trx->in_ro_trx_list);
ut_ad(!trx->in_rw_trx_list);
-#ifdef WITH_WSREP
- if (trx->mysql_thd && wsrep_on(trx->mysql_thd)) {
- trx->lock.was_chosen_as_deadlock_victim = FALSE;
- }
-#endif
trx->dict_operation = TRX_DICT_OP_NONE;
trx->error_state = DB_SUCCESS;
diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc
index a0df23b60d4..0d072bcb6ad 100644
--- a/storage/xtradb/handler/ha_innodb.cc
+++ b/storage/xtradb/handler/ha_innodb.cc
@@ -2171,6 +2171,28 @@ thd_to_trx(
return(*(trx_t**) thd_ha_data(thd, innodb_hton_ptr));
}
+/* Setter and getter for thread abort initiator. */
+extern "C" void thd_set_abort_initiator(MYSQL_THD thd, const struct handlerton *hton);
+extern "C" const struct handlerton* thd_get_abort_initiator(MYSQL_THD thd);
+
+extern "C" void thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd, const struct handlerton* hton);
+
+/* Check if thread abort initiator is InnoDB.
+@param thd MYSQL thread
+@return true if abort initiator is InnoDB */
+bool innodb_is_abort_initiator(THD* thd)
+{
+ return (thd_get_abort_initiator(thd) == innodb_hton_ptr);
+}
+
+/* Helper to report replication waits.
+@param thd MYSQL thread
+@param thd MYSQL victim thread */
+void innodb_report_wait_for(THD * thd, THD* other_thd)
+{
+ thd_report_wait_for(thd, other_thd, innodb_hton_ptr);
+}
+
#ifdef WITH_WSREP
ulonglong
thd_to_trx_id(
@@ -5534,8 +5556,6 @@ static void innobase_kill_query(handlerton*, THD* thd, enum thd_kill_levels)
/* if victim has been signaled by BF thread and/or aborting
is already progressing, following query aborting is not necessary
any more.
- Also, BF thread should own trx mutex for the victim, which would
- conflict with trx_mutex_enter() below
*/
DBUG_VOID_RETURN;
}
@@ -5543,34 +5563,8 @@ static void innobase_kill_query(handlerton*, THD* thd, enum thd_kill_levels)
if (trx_t* trx = thd_to_trx(thd)) {
ut_ad(trx->mysql_thd == thd);
- switch (trx->abort_type) {
-#ifdef WITH_WSREP
- case TRX_WSREP_ABORT:
- break;
-#endif
- case TRX_SERVER_ABORT:
- if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
- lock_mutex_enter();
- }
- /* fall through */
- case TRX_REPLICATION_ABORT:
- trx_mutex_enter(trx);
- }
/* Cancel a pending lock request if there are any */
- lock_trx_handle_wait(trx);
- switch (trx->abort_type) {
-#ifdef WITH_WSREP
- case TRX_WSREP_ABORT:
- break;
-#endif
- case TRX_SERVER_ABORT:
- if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
- lock_mutex_exit();
- }
- /* fall through */
- case TRX_REPLICATION_ABORT:
- trx_mutex_exit(trx);
- }
+ lock_trx_handle_wait_for_mysql(trx);
}
DBUG_VOID_RETURN;
@@ -19723,6 +19717,11 @@ wsrep_innobase_kill_one_trx(
(thd && wsrep_thd_query(thd)) ? wsrep_thd_query(thd) : "void");
wsrep_thd_LOCK(thd);
+
+ /* We mark thd as a victim and InnoDB as abort initiator, this thread
+ is already marked as BF victim. */
+ thd_set_abort_initiator(thd, innodb_hton_ptr);
+
DBUG_EXECUTE_IF("sync.wsrep_after_BF_victim_lock",
{
const char act[]=
@@ -19911,12 +19910,10 @@ wsrep_abort_transaction(handlerton* hton, THD *bf_thd, THD *victim_thd,
if (victim_trx) {
lock_mutex_enter();
trx_mutex_enter(victim_trx);
- victim_trx->abort_type = TRX_WSREP_ABORT;
int rcode = wsrep_innobase_kill_one_trx(bf_thd, bf_trx,
victim_trx, signal);
trx_mutex_exit(victim_trx);
lock_mutex_exit();
- victim_trx->abort_type = TRX_SERVER_ABORT;
wsrep_srv_conc_cancel_wait(victim_trx);
DBUG_RETURN(rcode);
} else {
diff --git a/storage/xtradb/include/ha_prototypes.h b/storage/xtradb/include/ha_prototypes.h
index 3f3c4f28ced..1b5cad16f34 100644
--- a/storage/xtradb/include/ha_prototypes.h
+++ b/storage/xtradb/include/ha_prototypes.h
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 2006, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2017, MariaDB Corporation.
+Copyright (c) 2017, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -688,4 +688,15 @@ normalize_table_name_low(
const char* name, /*!< in: table name string */
ibool set_lower_case); /*!< in: TRUE if we want to set
name to lower case */
+
+/* Check if thread abort initiator is InnoDB.
+@param thd MYSQL thread
+@return true if abort initiator is InnoDB */
+bool innodb_is_abort_initiator(THD* thd);
+
+/* Helper to report replication waits.
+@param thd MYSQL thread
+@param thd MYSQL victim thread */
+void innodb_report_wait_for(THD * thd, THD* other_thd);
+
#endif /* HA_INNODB_PROTOTYPES_H */
diff --git a/storage/xtradb/include/lock0lock.h b/storage/xtradb/include/lock0lock.h
index 5aff67db0ee..9a68e1ff614 100644
--- a/storage/xtradb/include/lock0lock.h
+++ b/storage/xtradb/include/lock0lock.h
@@ -835,13 +835,22 @@ lock_unlock_table_autoinc(
/*********************************************************************//**
Check whether the transaction has already been rolled back because it
was selected as a deadlock victim, or if it has to wait then cancel
-the wait lock.
+the wait lock. This function should be called only when holding
+lock sys mutex and trx mutex.
+
+@param trx transaction object
+@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
+dberr_t lock_trx_handle_wait(trx_t* trx)
+ MY_ATTRIBUTE((nonnull));
+
+/*********************************************************************//**
+Handle lock waits for MySQL interface.
+See detailed description on lock0lock.cc
+
+@param trx transaction object
@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
UNIV_INTERN
-dberr_t
-lock_trx_handle_wait(
-/*=================*/
- trx_t* trx) /*!< in/out: trx lock state */
+dberr_t lock_trx_handle_wait_for_mysql(trx_t* trx)
MY_ATTRIBUTE((nonnull));
/*********************************************************************//**
Get the number of locks on a table.
diff --git a/storage/xtradb/include/trx0trx.h b/storage/xtradb/include/trx0trx.h
index 77afde4c35c..e8b2b31a169 100644
--- a/storage/xtradb/include/trx0trx.h
+++ b/storage/xtradb/include/trx0trx.h
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2015, 2018, MariaDB Corporation.
+Copyright (c) 2015, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -672,7 +672,6 @@ struct trx_lock_t {
lock_sys->mutex. Otherwise, this may
only be modified by the thread that is
serving the running transaction. */
-
mem_heap_t* lock_heap; /*!< memory heap for trx_locks;
protected by lock_sys->mutex */
@@ -744,14 +743,6 @@ lock_rec_convert_impl_to_expl()) will access transactions associated
to other connections. The locks of transactions are protected by
lock_sys->mutex and sometimes by trx->mutex. */
-enum trx_abort_t {
- TRX_SERVER_ABORT = 0,
-#ifdef WITH_WSREP
- TRX_WSREP_ABORT,
-#endif
- TRX_REPLICATION_ABORT
-};
-
struct trx_t{
ulint magic_n;
@@ -930,8 +921,6 @@ struct trx_t{
/*------------------------------*/
THD* mysql_thd; /*!< MySQL thread handle corresponding
to this trx, or NULL */
- trx_abort_t abort_type; /*!< Transaction abort type */
-
const char* mysql_log_file_name;
/*!< if MySQL binlog is used, this field
contains a pointer to the latest file
diff --git a/storage/xtradb/lock/lock0lock.cc b/storage/xtradb/lock/lock0lock.cc
index 9daa2cc906f..183305a49d0 100644
--- a/storage/xtradb/lock/lock0lock.cc
+++ b/storage/xtradb/lock/lock0lock.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2014, 2018, MariaDB Corporation.
+Copyright (c) 2014, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -402,7 +402,6 @@ lock_grant(
lock_t* lock, /*!< in/out: waiting lock request */
bool owns_trx_mutex); /*!< in: whether lock->trx->mutex is owned */
-extern "C" void thd_report_wait_for(MYSQL_THD thd, MYSQL_THD other_thd);
extern "C" int thd_need_wait_for(const MYSQL_THD thd);
extern "C"
int thd_need_ordering_with(const MYSQL_THD thd, const MYSQL_THD other_thd);
@@ -1804,10 +1803,8 @@ wsrep_kill_victim(
}
}
- lock->trx->abort_type = TRX_WSREP_ABORT;
wsrep_innobase_kill_one_trx(trx->mysql_thd,
(const trx_t*) trx, lock->trx, TRUE);
- lock->trx->abort_type = TRX_SERVER_ABORT;
}
}
}
@@ -4821,12 +4818,10 @@ lock_report_waiters_to_mysql(
if (w_trx->id != victim_trx_id) {
/* If thd_report_wait_for() decides to kill the
transaction, then we will get a call back into
- innobase_kill_query. We mark this by setting
- current_lock_mutex_owner, so we can avoid trying
- to recursively take lock_sys->mutex. */
- w_trx->abort_type = TRX_REPLICATION_ABORT;
- thd_report_wait_for(mysql_thd, w_trx->mysql_thd);
- w_trx->abort_type = TRX_SERVER_ABORT;
+ innobase_kill_query.*/
+ trx_mutex_enter(w_trx);
+ innodb_report_wait_for(mysql_thd, w_trx->mysql_thd);
+ trx_mutex_exit(w_trx);
}
++i;
}
@@ -8080,13 +8075,12 @@ lock_trx_release_locks(
/*********************************************************************//**
Check whether the transaction has already been rolled back because it
was selected as a deadlock victim, or if it has to wait then cancel
-the wait lock.
+the wait lock. This function should be called only when holding
+lock sys mutex and trx mutex.
+
+@param trx transaction object
@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
-UNIV_INTERN
-dberr_t
-lock_trx_handle_wait(
-/*=================*/
- trx_t* trx) /*!< in/out: trx lock state */
+dberr_t lock_trx_handle_wait(trx_t* trx)
{
ut_ad(lock_mutex_own());
ut_ad(trx_mutex_own(trx));
@@ -8103,6 +8097,60 @@ lock_trx_handle_wait(
return DB_LOCK_WAIT;
}
+/*********************************************************************//**
+Handle lock waits for MySQL interface.
+
+This function should called only from handler API i.e. ha_innodb.cc.
+Call trace: THD::awake() (we hold LOCK_thd_data) -> ha_kill_query()
+-> hton->kill_query() -> innobase_kill_query() ->
+
+There is 3 possible cases:
+
+(1) wsrep high priority thread aborting lock holder i.e. victim thread
+wsrep_abort_transaction() (takes lock sys mutex and trx mutex for
+victim) -> marks abort initiator as InnoDB -> wsrep_thd_awake()
+-> thd->awake(KILL_QUERY) (while holding LOCK_thd_data).
+thd->abort_initiator is protected by LOCK_thd_data and we are
+holding it so it can't change during this function.
+Thus, we hold lock sys, trx and LOCK_thd_data mutexes.
+
+(2) replication thread aborting lock holder
+lock_report_waiters_to_mysql() (we hold lock sys mutes and take trx
+mutex to victim) -> innodb_report_wait_for() -> thd_report_wait_for()
+we mark abort initiator as provided hton-> thd->awake(KILL_CONNECTION)
+(while holding LOCK_thd_data).
+thd->abort_initiator is protected by LOCK_thd_data and we are
+holding it so it can't change during this function.
+Thus, we hold lock sys, trx and LOCK_thd_data mutexes.
+
+(3) User action KILL [HARD | SOFT] [CONNECTION | QUERY [ID] ]
+[thread_id | USER user_name | query_id]
+thd->awake() (while holding LOCK_thd_data) no other mutexes.
+Thus, thd->abort_initiator is protected by LOCK_thd_data and
+we are holding it so it can't change during this function.
+We need to take lock sys mutex and trx mutex.
+
+@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
+UNIV_INTERN
+dberr_t lock_trx_handle_wait_for_mysql(trx_t* trx)
+{
+ dberr_t err= DB_SUCCESS;
+
+ if (trx->mysql_thd && innodb_is_abort_initiator(trx->mysql_thd)) {
+ err = lock_trx_handle_wait(trx);
+ } else {
+ ut_ad(!lock_mutex_own());
+ ut_ad(!trx_mutex_own(trx));
+ lock_mutex_enter();
+ trx_mutex_enter(trx);
+ err = lock_trx_handle_wait(trx);
+ lock_mutex_exit();
+ trx_mutex_exit(trx);
+ }
+
+ return err;
+}
+
/*********************************************************************//**
Get the number of locks on a table.
@return number of locks */
diff --git a/storage/xtradb/row/row0sel.cc b/storage/xtradb/row/row0sel.cc
index b6b5d107885..6bc9098491d 100644
--- a/storage/xtradb/row/row0sel.cc
+++ b/storage/xtradb/row/row0sel.cc
@@ -2,7 +2,7 @@
Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2008, Google Inc.
-Copyright (c) 2015, 2018, MariaDB Corporation.
+Copyright (c) 2015, 2019, MariaDB Corporation.
Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
@@ -4753,7 +4753,29 @@ row_search_for_mysql(
/* Check whether it was a deadlock or not, if not
a deadlock and the transaction had to wait then
- release the lock it is waiting on. */
+ release the lock it is waiting on.
+
+ There is two possible cases here:
+
+ (1) wsrep high priority thread or replication
+ thread selects this thread as a victim. Both
+ will take lock sys mutex and trx mutex before
+ entering thread abort. Thus, either this
+ thread will check deadlock below or this
+ thread will be aborted and then we find it
+ out below. Thus concurrent access in
+ this case is protected by lock sys and trx
+ mutex.
+
+ (2) User action kill. Here killing thread either
+ takes lock sys mutex and trx mutex before
+ aborting this thread and we will find it out
+ when they are released or this thread
+ acquires them fist and we check deadlock
+ and then killing thread can proceed. Thus
+ concurrent access in this case is also
+ protected by lock sys and trx mutex.
+ */
lock_mutex_enter();
trx_mutex_enter(trx);
diff --git a/storage/xtradb/trx/trx0roll.cc b/storage/xtradb/trx/trx0roll.cc
index 56b7120fa34..66a16ec9541 100644
--- a/storage/xtradb/trx/trx0roll.cc
+++ b/storage/xtradb/trx/trx0roll.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2016, 2018, MariaDB Corporation.
+Copyright (c) 2016, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
diff --git a/storage/xtradb/trx/trx0trx.cc b/storage/xtradb/trx/trx0trx.cc
index 17cba81daf3..9fe947c8318 100644
--- a/storage/xtradb/trx/trx0trx.cc
+++ b/storage/xtradb/trx/trx0trx.cc
@@ -1,7 +1,7 @@
/*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved.
-Copyright (c) 2015, 2018, MariaDB Corporation.
+Copyright (c) 2015, 2019, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -1563,11 +1563,6 @@ trx_commit_in_memory(
ut_ad(!trx->in_ro_trx_list);
ut_ad(!trx->in_rw_trx_list);
-#ifdef WITH_WSREP
- if (trx->mysql_thd && wsrep_on(trx->mysql_thd)) {
- trx->lock.was_chosen_as_deadlock_victim = FALSE;
- }
-#endif
trx->dict_operation = TRX_DICT_OP_NONE;
trx->error_state = DB_SUCCESS;
1
0
[Commits] e27caaf2294: MDEV-18590: galera.versioning_trx_id: Test failure: mysqltest: Result content mismatch
by jan 29 Mar '19
by jan 29 Mar '19
29 Mar '19
revision-id: e27caaf22944db11fd12a35f0f548f3a5d1ed8ba (mariadb-10.4.3-106-ge27caaf2294)
parent(s): 3e1f3d3e2f14a157121b92bd4b76e986f1846454
author: Jan Lindström
committer: Jan Lindström
timestamp: 2019-03-29 10:56:45 +0200
message:
MDEV-18590: galera.versioning_trx_id: Test failure: mysqltest: Result content mismatch
Add wsrep sync waits and if expected result is not reached print
out current contents.
---
mysql-test/suite/galera/r/versioning_trx_id.result | 6 ++++--
mysql-test/suite/galera/t/versioning_trx_id.test | 14 +++++++++++++-
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/mysql-test/suite/galera/r/versioning_trx_id.result b/mysql-test/suite/galera/r/versioning_trx_id.result
index 232cca202c0..ec3dc09e97b 100644
--- a/mysql-test/suite/galera/r/versioning_trx_id.result
+++ b/mysql-test/suite/galera/r/versioning_trx_id.result
@@ -5,6 +5,7 @@ connection node_1;
create table t1 (a int, s bigint unsigned as row start, e bigint unsigned as row end, period for system_time(s,e)) engine=InnoDB with system versioning;
insert into t1 (a) values (1),(2);
connection node_2;
+set session wsrep_sync_wait=15;
insert into t1 (a) values (3),(4);
select a from t1;
a
@@ -19,6 +20,7 @@ select count(*) from mysql.transaction_registry where begin_timestamp>=commit_ti
count(*)
0
connection node_3;
+set session wsrep_sync_wait=15;
insert into t1 (a) values (5),(6);
select a from t1;
a
@@ -35,6 +37,7 @@ select count(*) from mysql.transaction_registry where begin_timestamp>=commit_ti
count(*)
0
connection node_1;
+set session wsrep_sync_wait=15;
select a from t1;
a
1
@@ -50,5 +53,4 @@ select count(*) from mysql.transaction_registry where begin_timestamp>=commit_ti
count(*)
0
drop table t1;
-disconnect node_2;
-disconnect node_1;
+disconnect node_3;
diff --git a/mysql-test/suite/galera/t/versioning_trx_id.test b/mysql-test/suite/galera/t/versioning_trx_id.test
index 175ead265a5..a99d7891ea0 100644
--- a/mysql-test/suite/galera/t/versioning_trx_id.test
+++ b/mysql-test/suite/galera/t/versioning_trx_id.test
@@ -7,22 +7,34 @@ create table t1 (a int, s bigint unsigned as row start, e bigint unsigned as row
insert into t1 (a) values (1),(2);
--connection node_2
+set session wsrep_sync_wait=15;
insert into t1 (a) values (3),(4);
select a from t1;
select count(*) from mysql.transaction_registry where begin_timestamp='0-0-0';
+if (`SELECT count(*) from mysql.transaction_registry where begin_timestamp>=commit_timestamp`) {
+ select * from mysql.transaction_registry;
+}
select count(*) from mysql.transaction_registry where begin_timestamp>=commit_timestamp;
--connection node_3
+set session wsrep_sync_wait=15;
insert into t1 (a) values (5),(6);
select a from t1;
select count(*) from mysql.transaction_registry where begin_timestamp='0-0-0';
+if (`SELECT count(*) from mysql.transaction_registry where begin_timestamp>=commit_timestamp`) {
+ select * from mysql.transaction_registry;
+}
select count(*) from mysql.transaction_registry where begin_timestamp>=commit_timestamp;
--connection node_1
+set session wsrep_sync_wait=15;
select a from t1;
select count(*) from mysql.transaction_registry where begin_timestamp='0-0-0';
+if (`SELECT count(*) from mysql.transaction_registry where begin_timestamp>=commit_timestamp`) {
+ select * from mysql.transaction_registry;
+}
select count(*) from mysql.transaction_registry where begin_timestamp>=commit_timestamp;
drop table t1;
---source include/galera_end.inc
+--disconnect node_3
1
0