[Commits] de209ca: MDEV-12179: Per-engine mysql.gtid_slave_pos tables

Sergei Petrunia psergey at askmonty.org
Mon Jun 26 18:29:30 EEST 2017


revision-id: de209ca4a10385a392e3596c80187b88434b6fe5
parent(s): 2a3fe45dd2df047cc0d66e2bcdbadd5005c85a1a
committer: Sergei Petrunia
branch nick: 10.3-mdev12179
timestamp: 2017-06-26 18:29:30 +0300
message:

MDEV-12179: Per-engine mysql.gtid_slave_pos tables

Merge to 10.3
- changed a few LEX_STRING to LEX_CSTRING

---
 mysql-test/extra/rpl_tests/rpl_deadlock.test       |   8 +
 mysql-test/mysql-test-run.pl                       |   1 +
 mysql-test/r/mysqld--help.result                   |   8 +
 .../multi_source/gtid_ignore_duplicates.result     |   5 +
 .../suite/multi_source/gtid_ignore_duplicates.test |   5 +
 .../suite/multi_source/gtid_slave_pos.result       | 155 +++++++
 mysql-test/suite/multi_source/gtid_slave_pos.test  | 173 ++++++++
 mysql-test/suite/rpl/r/rpl_deadlock_innodb.result  |   6 +
 mysql-test/suite/rpl/r/rpl_gtid_crash.result       |   4 +-
 .../suite/rpl/r/rpl_gtid_errorhandling.result      |   8 +-
 mysql-test/suite/rpl/r/rpl_gtid_ignored.result     |   1 +
 mysql-test/suite/rpl/r/rpl_gtid_mdev4484.result    |   8 +-
 mysql-test/suite/rpl/r/rpl_gtid_stop_start.result  |   4 +-
 mysql-test/suite/rpl/r/rpl_gtid_until.result       |   2 +
 mysql-test/suite/rpl/r/rpl_mdev10863.result        |   1 +
 mysql-test/suite/rpl/r/rpl_mdev12179.result        | 265 +++++++++++
 .../rpl/r/show_status_stop_slave_race-7126.result  |   1 +
 mysql-test/suite/rpl/t/rpl_gtid_crash.test         |   4 +-
 mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test |   8 +-
 mysql-test/suite/rpl/t/rpl_gtid_ignored.test       |   1 +
 mysql-test/suite/rpl/t/rpl_gtid_mdev4484.test      |  16 +-
 mysql-test/suite/rpl/t/rpl_gtid_stop_start.test    |  18 +-
 mysql-test/suite/rpl/t/rpl_gtid_until.test         |   3 +
 mysql-test/suite/rpl/t/rpl_mdev10863.test          |   1 +
 mysql-test/suite/rpl/t/rpl_mdev12179.test          | 280 ++++++++++++
 .../rpl/t/show_status_stop_slave_race-7126.test    |   1 +
 .../sys_vars/r/sysvars_server_notembedded.result   |  14 +
 scripts/mysql_system_tables.sql                    |   2 +
 sql/handler.cc                                     |   5 +
 sql/log.h                                          |   1 +
 sql/log_event.cc                                   |  18 +-
 sql/mysqld.cc                                      |  53 ++-
 sql/mysqld.h                                       |   6 +
 sql/rpl_gtid.cc                                    | 274 ++++++++++--
 sql/rpl_gtid.h                                     |  64 ++-
 sql/rpl_mi.cc                                      |  29 +-
 sql/rpl_mi.h                                       |   2 +-
 sql/rpl_parallel.cc                                |   4 +-
 sql/rpl_rli.cc                                     | 483 +++++++++++++++++++--
 sql/rpl_rli.h                                      |   1 +
 sql/set_var.cc                                     | 219 ++++++++++
 sql/set_var.h                                      |   7 +
 sql/slave.cc                                       | 226 +++++++++-
 sql/slave.h                                        |   3 +
 sql/sql_plugin.cc                                  |  30 ++
 sql/sys_vars.cc                                    |  39 ++
 sql/sys_vars.ic                                    | 110 +++++
 .../mysql-test/rpl/r/rpl_deadlock_tokudb.result    |   6 +
 .../mysql-test/tokudb_rpl/r/mdev12179.result       | 265 +++++++++++
 .../tokudb/mysql-test/tokudb_rpl/t/mdev12179.test  | 232 ++++++++++
 50 files changed, 2944 insertions(+), 136 deletions(-)

diff --git a/mysql-test/extra/rpl_tests/rpl_deadlock.test b/mysql-test/extra/rpl_tests/rpl_deadlock.test
index f7a1e71..e9191d5 100644
--- a/mysql-test/extra/rpl_tests/rpl_deadlock.test
+++ b/mysql-test/extra/rpl_tests/rpl_deadlock.test
@@ -46,6 +46,9 @@ BEGIN;
 SELECT * FROM t1 FOR UPDATE;
 # Save variable 'Slave_retried_transactions' before deadlock
 let $slave_retried_transactions= query_get_value(SHOW GLOBAL STATUS LIKE 'Slave_retried_transactions', Value, 1);
+# Run the START SLAVE in a separate connection. Otherwise it terminates
+# the SELECT FOR UPDATE transaction (START SLAVE does implicit COMMIT!).
+connection slave1;
 START SLAVE;
 # Wait until SQL thread blocked: variable 'Slave_retried_transactions' will incremented
 let $status_var= Slave_retried_transactions;
@@ -53,6 +56,7 @@ let $status_var_value= $slave_retried_transactions;
 let $status_type= GLOBAL;
 let $status_var_comparsion= >;
 --source include/wait_for_status_var.inc
+connection slave;
 SELECT COUNT(*) FROM t2;
 COMMIT;
 sync_with_master;
@@ -78,9 +82,11 @@ BEGIN;
 # Hold lock
 SELECT * FROM t1 FOR UPDATE;
 # Wait until slave stopped with error 'Lock wait timeout exceeded'
+connection slave1;
 START SLAVE;
 let $slave_sql_errno= 1205;
 --source include/wait_for_slave_sql_error.inc
+connection slave;
 SELECT COUNT(*) FROM t2;
 COMMIT;
 --source include/start_slave.inc
@@ -109,9 +115,11 @@ BEGIN;
 # Hold lock
 SELECT * FROM t1 FOR UPDATE;
 # Wait until slave stopped with error 'Lock wait timeout exceeded'
+connection slave1;
 START SLAVE;
 let $slave_sql_errno= 1205;
 --source include/wait_for_slave_sql_error.inc
+connection slave;
 SELECT COUNT(*) FROM t2;
 COMMIT;
 --source include/start_slave.inc
diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl
index 3bd8dd3..9e1c72e 100755
--- a/mysql-test/mysql-test-run.pl
+++ b/mysql-test/mysql-test-run.pl
@@ -5225,6 +5225,7 @@ sub server_need_restart {
     if (!My::Options::same($started_opts, $extra_opts) ||
         exists $server->{'restart_opts'})
     {
+      delete $server->{'restart_opts'};
       my $use_dynamic_option_switch= 0;
       if (!$use_dynamic_option_switch)
       {
diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result
index 1fd365e..838ee74 100644
--- a/mysql-test/r/mysqld--help.result
+++ b/mysql-test/r/mysqld--help.result
@@ -257,6 +257,13 @@ The following options may be given as the first argument:
  applied; this means it is the responsibility of the user
  to ensure that GTID sequence numbers are strictly
  increasing.
+ --gtid-pos-auto-engines=name 
+ List of engines for which to automatically create a
+ mysql.gtid_slave_pos_ENGINE table, if a transaction using
+ that engine is replicated. This can be used to avoid
+ introducing cross-engine transactions, if engines are
+ used different from that used by table
+ mysql.gtid_slave_pos
  --gtid-strict-mode  Enforce strict seq_no ordering of events in the binary
  log. Slave stops with an error if it encounters an event
  that would cause it to generate an out-of-order binlog if
@@ -1259,6 +1266,7 @@ getopt-prefix-matching TRUE
 group-concat-max-len 1048576
 gtid-domain-id 0
 gtid-ignore-duplicates FALSE
+gtid-pos-auto-engines 
 gtid-strict-mode FALSE
 help TRUE
 histogram-size 0
diff --git a/mysql-test/suite/multi_source/gtid_ignore_duplicates.result b/mysql-test/suite/multi_source/gtid_ignore_duplicates.result
index 92d0962..96627b4 100644
--- a/mysql-test/suite/multi_source/gtid_ignore_duplicates.result
+++ b/mysql-test/suite/multi_source/gtid_ignore_duplicates.result
@@ -65,6 +65,7 @@ include/wait_for_slave_to_start.inc
 set default_master_connection = '';
 connection server_1;
 ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+CALL mtr.add_suppression("This change will not take full effect until all SQL threads have been restarted");
 CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
 INSERT INTO t1 VALUES (1);
 BEGIN;
@@ -491,17 +492,21 @@ SET GLOBAL slave_parallel_threads= @old_parallel;
 SET GLOBAL gtid_ignore_duplicates= @old_ignore_duplicates;
 connection server_1;
 DROP TABLE t1;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 include/reset_master_slave.inc
 disconnect server_1;
 connection server_2;
 DROP TABLE t1;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 include/reset_master_slave.inc
 disconnect server_2;
 connection server_3;
 DROP TABLE t1;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 include/reset_master_slave.inc
 disconnect server_3;
 connection server_4;
 DROP TABLE t1;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 include/reset_master_slave.inc
 disconnect server_4;
diff --git a/mysql-test/suite/multi_source/gtid_ignore_duplicates.test b/mysql-test/suite/multi_source/gtid_ignore_duplicates.test
index 218d91a..b61da0f 100644
--- a/mysql-test/suite/multi_source/gtid_ignore_duplicates.test
+++ b/mysql-test/suite/multi_source/gtid_ignore_duplicates.test
@@ -86,6 +86,7 @@ set default_master_connection = '';
 
 --connection server_1
 ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+CALL mtr.add_suppression("This change will not take full effect until all SQL threads have been restarted");
 CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB;
 INSERT INTO t1 VALUES (1);
 BEGIN;
@@ -431,20 +432,24 @@ SET GLOBAL gtid_ignore_duplicates= @old_ignore_duplicates;
 
 --connection server_1
 DROP TABLE t1;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 --source include/reset_master_slave.inc
 --disconnect server_1
 
 --connection server_2
 DROP TABLE t1;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 --source include/reset_master_slave.inc
 --disconnect server_2
 
 --connection server_3
 DROP TABLE t1;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 --source include/reset_master_slave.inc
 --disconnect server_3
 
 --connection server_4
 DROP TABLE t1;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 --source include/reset_master_slave.inc
 --disconnect server_4
diff --git a/mysql-test/suite/multi_source/gtid_slave_pos.result b/mysql-test/suite/multi_source/gtid_slave_pos.result
new file mode 100644
index 0000000..d57cfc1
--- /dev/null
+++ b/mysql-test/suite/multi_source/gtid_slave_pos.result
@@ -0,0 +1,155 @@
+connect  slave1,127.0.0.1,root,,,$SERVER_MYPORT_3;
+connect  master1,127.0.0.1,root,,,$SERVER_MYPORT_1;
+connect  master2,127.0.0.1,root,,,$SERVER_MYPORT_2;
+connection slave1;
+CHANGE MASTER 'slave1' TO master_port=MYPORT_1, master_host='127.0.0.1', master_user='root', master_use_gtid=slave_pos;
+CHANGE MASTER 'slave2' TO master_port=MYPORT_2, master_host='127.0.0.1', master_user='root', master_use_gtid=slave_pos;
+set default_master_connection = 'slave1';
+START SLAVE;
+include/wait_for_slave_to_start.inc
+set default_master_connection = 'slave2';
+START SLAVE;
+include/wait_for_slave_to_start.inc
+set default_master_connection = '';
+connection master1;
+SET GLOBAL gtid_domain_id= 1;
+SET SESSION gtid_domain_id= 1;
+CREATE TABLE t3 (a INT PRIMARY KEY, b VARCHAR(10)) ENGINE=InnoDB;
+CREATE TABLE t1 (a INT PRIMARY KEY, b VARCHAR(10));
+INSERT INTO t1 VALUES (1, "initial");
+INSERT INTO t3 VALUES (101, "initial 1");
+include/save_master_gtid.inc
+connection master2;
+SET GLOBAL gtid_domain_id= 2;
+SET SESSION gtid_domain_id= 2;
+CREATE TABLE t2 (a INT PRIMARY KEY, b VARCHAR(10)) ENGINE=InnoDB;
+INSERT INTO t2 VALUES (1, "initial");
+connection slave1;
+include/sync_with_master_gtid.inc
+connection master2;
+include/save_master_gtid.inc
+connection slave1;
+include/sync_with_master_gtid.inc
+*** Add an innodb gtid_slave_pos table. It is not used yet as slaves are already running ***
+SET sql_log_bin=0;
+CREATE TABLE mysql.gtid_slave_pos_innodb LIKE mysql.gtid_slave_pos;
+ALTER TABLE mysql.gtid_slave_pos_innodb ENGINE=InnoDB;
+SET sql_log_bin=0;
+connection master1;
+INSERT INTO t3 VALUES (102, "secondary");
+include/save_master_gtid.inc
+connection slave1;
+include/sync_with_master_gtid.inc
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
+domain_id	max(seq_no)
+1	5
+2	2
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos_innodb GROUP BY domain_id;
+domain_id	max(seq_no)
+*** Restart one slave thread, the other keeps running. Now the new table is used ***
+connection slave1;
+set default_master_connection = 'slave1';
+STOP SLAVE;
+include/wait_for_slave_to_stop.inc
+START SLAVE;
+include/wait_for_slave_to_start.inc
+connection master1;
+INSERT INTO t1 VALUES (2, "followup");
+include/save_master_gtid.inc
+connection slave1;
+include/sync_with_master_gtid.inc
+connection master2;
+INSERT INTO t2 VALUES (2, "secondary2");
+include/save_master_gtid.inc
+connection slave1;
+include/sync_with_master_gtid.inc
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
+domain_id	max(seq_no)
+1	6
+2	2
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos_innodb GROUP BY domain_id;
+domain_id	max(seq_no)
+2	3
+*** Remove a gtid_slave_posXXX table, restart one slave ***
+*** Get a warning that the change is not yet picked up ***
+*** See that updates fail due to trying to use the missing table ***
+connection slave1;
+SET sql_log_bin=0;
+DROP TABLE mysql.gtid_slave_pos_innodb;
+SET sql_log_bin=1;
+set default_master_connection = 'slave2';
+STOP SLAVE;
+include/wait_for_slave_to_stop.inc
+START SLAVE;
+include/wait_for_slave_to_start.inc
+CALL mtr.add_suppression("The table mysql.gtid_slave_pos_innodb was removed.");
+connection master2;
+INSERT INTO t2 VALUES (3, "tertiary 2");
+connection slave1;
+include/wait_for_slave_sql_error.inc [errno=1942]
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
+domain_id	max(seq_no)
+1	6
+2	2
+*** Stop both slaves, see that the drop of mysql.gtid_slave_pos_innodb is now picked up ***
+connection slave1;
+set default_master_connection = 'slave1';
+STOP SLAVE;
+include/wait_for_slave_to_stop.inc
+set default_master_connection = 'slave2';
+STOP SLAVE;
+include/wait_for_slave_to_stop.inc
+set default_master_connection = 'slave1';
+START SLAVE;
+include/wait_for_slave_to_start.inc
+connection master1;
+INSERT INTO t1 VALUES (3, "more stuff");
+include/save_master_gtid.inc
+connection slave1;
+include/sync_with_master_gtid.inc
+set default_master_connection = 'slave2';
+START SLAVE;
+include/wait_for_slave_to_start.inc
+connection master2;
+include/save_master_gtid.inc
+connection slave1;
+include/sync_with_master_gtid.inc
+SELECT * FROM t1 ORDER BY a;
+a	b
+1	initial
+2	followup
+3	more stuff
+SELECT * FROM t2 ORDER BY a;
+a	b
+1	initial
+2	secondary2
+3	tertiary 2
+SELECT * FROM t3 ORDER BY a;
+a	b
+101	initial 1
+102	secondary
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
+domain_id	max(seq_no)
+1	7
+2	4
+connection master1;
+DROP TABLE t1;
+DROP TABLE t3;
+connection master2;
+DROP TABLE t2;
+connection slave1;
+SET GLOBAL gtid_domain_id=0;
+STOP ALL SLAVES;
+Warnings:
+Note	1938	SLAVE 'slave1' stopped
+Note	1938	SLAVE 'slave2' stopped
+include/reset_master_slave.inc
+disconnect slave1;
+connection master1;
+SET GLOBAL gtid_domain_id=0;
+include/reset_master_slave.inc
+disconnect master1;
+connection master2;
+SET GLOBAL gtid_domain_id=0;
+include/reset_master_slave.inc
+disconnect master2;
diff --git a/mysql-test/suite/multi_source/gtid_slave_pos.test b/mysql-test/suite/multi_source/gtid_slave_pos.test
new file mode 100644
index 0000000..c01130f
--- /dev/null
+++ b/mysql-test/suite/multi_source/gtid_slave_pos.test
@@ -0,0 +1,173 @@
+--source include/not_embedded.inc
+--source include/have_innodb.inc
+
+#
+# Test multiple mysql.gtid_slave_posXXX tables with multiple master connections
+#
+
+--connect (slave1,127.0.0.1,root,,,$SERVER_MYPORT_3)
+--connect (master1,127.0.0.1,root,,,$SERVER_MYPORT_1)
+--connect (master2,127.0.0.1,root,,,$SERVER_MYPORT_2)
+
+--connection slave1
+--replace_result $SERVER_MYPORT_1 MYPORT_1
+eval CHANGE MASTER 'slave1' TO master_port=$SERVER_MYPORT_1, master_host='127.0.0.1', master_user='root', master_use_gtid=slave_pos;
+--replace_result $SERVER_MYPORT_2 MYPORT_2
+eval CHANGE MASTER 'slave2' TO master_port=$SERVER_MYPORT_2, master_host='127.0.0.1', master_user='root', master_use_gtid=slave_pos;
+set default_master_connection = 'slave1';
+START SLAVE;
+--source include/wait_for_slave_to_start.inc
+set default_master_connection = 'slave2';
+START SLAVE;
+--source include/wait_for_slave_to_start.inc
+set default_master_connection = '';
+
+
+--connection master1
+SET GLOBAL gtid_domain_id= 1;
+SET SESSION gtid_domain_id= 1;
+CREATE TABLE t3 (a INT PRIMARY KEY, b VARCHAR(10)) ENGINE=InnoDB;
+CREATE TABLE t1 (a INT PRIMARY KEY, b VARCHAR(10));
+INSERT INTO t1 VALUES (1, "initial");
+INSERT INTO t3 VALUES (101, "initial 1");
+--source include/save_master_gtid.inc
+
+--connection master2
+SET GLOBAL gtid_domain_id= 2;
+SET SESSION gtid_domain_id= 2;
+CREATE TABLE t2 (a INT PRIMARY KEY, b VARCHAR(10)) ENGINE=InnoDB;
+INSERT INTO t2 VALUES (1, "initial");
+
+
+--connection slave1
+--source include/sync_with_master_gtid.inc
+
+--connection master2
+--source include/save_master_gtid.inc
+
+--connection slave1
+--source include/sync_with_master_gtid.inc
+
+
+--echo *** Add an innodb gtid_slave_pos table. It is not used yet as slaves are already running ***
+
+SET sql_log_bin=0;
+CREATE TABLE mysql.gtid_slave_pos_innodb LIKE mysql.gtid_slave_pos;
+ALTER TABLE mysql.gtid_slave_pos_innodb ENGINE=InnoDB;
+SET sql_log_bin=0;
+
+--connection master1
+INSERT INTO t3 VALUES (102, "secondary");
+--source include/save_master_gtid.inc
+
+--connection slave1
+--source include/sync_with_master_gtid.inc
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos_innodb GROUP BY domain_id;
+
+--echo *** Restart one slave thread, the other keeps running. Now the new table is used ***
+--connection slave1
+set default_master_connection = 'slave1';
+STOP SLAVE;
+--source include/wait_for_slave_to_stop.inc
+START SLAVE;
+--source include/wait_for_slave_to_start.inc
+
+# Send through a transaction on the slave1 connection, to be sure that it has
+# had time to update the state with the new table.
+--connection master1
+INSERT INTO t1 VALUES (2, "followup");
+--source include/save_master_gtid.inc
+--connection slave1
+--source include/sync_with_master_gtid.inc
+
+--connection master2
+INSERT INTO t2 VALUES (2, "secondary2");
+--source include/save_master_gtid.inc
+
+--connection slave1
+--source include/sync_with_master_gtid.inc
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos_innodb GROUP BY domain_id;
+
+--echo *** Remove a gtid_slave_posXXX table, restart one slave ***
+--echo *** Get a warning that the change is not yet picked up ***
+--echo *** See that updates fail due to trying to use the missing table ***
+--connection slave1
+SET sql_log_bin=0;
+DROP TABLE mysql.gtid_slave_pos_innodb;
+SET sql_log_bin=1;
+set default_master_connection = 'slave2';
+STOP SLAVE;
+--source include/wait_for_slave_to_stop.inc
+START SLAVE;
+--source include/wait_for_slave_to_start.inc
+CALL mtr.add_suppression("The table mysql.gtid_slave_pos_innodb was removed.");
+
+--connection master2
+INSERT INTO t2 VALUES (3, "tertiary 2");
+
+--connection slave1
+--let $slave_sql_errno= 1942
+--source include/wait_for_slave_sql_error.inc
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
+
+--echo *** Stop both slaves, see that the drop of mysql.gtid_slave_pos_innodb is now picked up ***
+--connection slave1
+set default_master_connection = 'slave1';
+STOP SLAVE;
+--source include/wait_for_slave_to_stop.inc
+set default_master_connection = 'slave2';
+STOP SLAVE;
+--source include/wait_for_slave_to_stop.inc
+set default_master_connection = 'slave1';
+START SLAVE;
+--source include/wait_for_slave_to_start.inc
+# Send through a transaction on the slave1 connection, to be sure that it has
+# had time to update the state with the new table.
+--connection master1
+INSERT INTO t1 VALUES (3, "more stuff");
+--source include/save_master_gtid.inc
+--connection slave1
+--source include/sync_with_master_gtid.inc
+set default_master_connection = 'slave2';
+START SLAVE;
+--source include/wait_for_slave_to_start.inc
+
+--connection master2
+--source include/save_master_gtid.inc
+--connection slave1
+--source include/sync_with_master_gtid.inc
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+SELECT * FROM t3 ORDER BY a;
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
+
+
+# Cleanup.
+--connection master1
+DROP TABLE t1;
+DROP TABLE t3;
+
+--connection master2
+DROP TABLE t2;
+
+--connection slave1
+SET GLOBAL gtid_domain_id=0;
+--let $wait_condition= SELECT COUNT(*)=0 FROM information_schema.tables WHERE table_name IN ("t1", "t2", "t3") AND table_schema = "test"
+--source include/wait_condition.inc
+--sorted_result
+STOP ALL SLAVES;
+--source include/reset_master_slave.inc
+--disconnect slave1
+
+
+--connection master1
+SET GLOBAL gtid_domain_id=0;
+--source include/reset_master_slave.inc
+--disconnect master1
+
+--connection master2
+SET GLOBAL gtid_domain_id=0;
+--source include/reset_master_slave.inc
+--disconnect master2
diff --git a/mysql-test/suite/rpl/r/rpl_deadlock_innodb.result b/mysql-test/suite/rpl/r/rpl_deadlock_innodb.result
index 1c9611b..bb8c45a 100644
--- a/mysql-test/suite/rpl/r/rpl_deadlock_innodb.result
+++ b/mysql-test/suite/rpl/r/rpl_deadlock_innodb.result
@@ -39,7 +39,9 @@ connection slave;
 BEGIN;
 SELECT * FROM t1 FOR UPDATE;
 a
+connection slave1;
 START SLAVE;
+connection slave;
 SELECT COUNT(*) FROM t2;
 COUNT(*)
 0
@@ -61,8 +63,10 @@ BEGIN;
 SELECT * FROM t1 FOR UPDATE;
 a
 1
+connection slave1;
 START SLAVE;
 include/wait_for_slave_sql_error.inc [errno=1205]
+connection slave;
 SELECT COUNT(*) FROM t2;
 COUNT(*)
 0
@@ -92,8 +96,10 @@ SELECT * FROM t1 FOR UPDATE;
 a
 1
 1
+connection slave1;
 START SLAVE;
 include/wait_for_slave_sql_error.inc [errno=1205]
+connection slave;
 SELECT COUNT(*) FROM t2;
 COUNT(*)
 0
diff --git a/mysql-test/suite/rpl/r/rpl_gtid_crash.result b/mysql-test/suite/rpl/r/rpl_gtid_crash.result
index 7b6e95b..ed2a2b2 100644
--- a/mysql-test/suite/rpl/r/rpl_gtid_crash.result
+++ b/mysql-test/suite/rpl/r/rpl_gtid_crash.result
@@ -88,16 +88,16 @@ include/save_master_gtid.inc
 connection server_2;
 include/sync_with_master_gtid.inc
 include/stop_slave.inc
-SET GLOBAL debug_dbug="+d,crash_commit_before";
 START SLAVE;
+SET GLOBAL debug_dbug="+d,crash_commit_before";
 connection server_1;
 INSERT INTO t1 VALUES (5);
 include/save_master_gtid.inc
 connection server_2;
 include/sync_with_master_gtid.inc
 include/stop_slave.inc
-SET GLOBAL debug_dbug="+d,crash_commit_after";
 START SLAVE;
+SET GLOBAL debug_dbug="+d,crash_commit_after";
 connection server_1;
 INSERT INTO t1 VALUES (6);
 include/save_master_gtid.inc
diff --git a/mysql-test/suite/rpl/r/rpl_gtid_errorhandling.result b/mysql-test/suite/rpl/r/rpl_gtid_errorhandling.result
index 7c2471b..62a5b9c 100644
--- a/mysql-test/suite/rpl/r/rpl_gtid_errorhandling.result
+++ b/mysql-test/suite/rpl/r/rpl_gtid_errorhandling.result
@@ -12,21 +12,21 @@ connection master;
 INSERT INTO t1 VALUES (1);
 connection slave;
 CALL mtr.add_suppression("Slave: Failed to open mysql.gtid_slave_pos");
-include/wait_for_slave_sql_error.inc [errno=1942]
+include/wait_for_slave_sql_error.inc [errno=1944]
 include/stop_slave.inc
 ALTER TABLE mysql.gtid_slave_pos CHANGE seq_no seq_no BIGINT UNSIGNED NOT NULL;
 ALTER TABLE mysql.gtid_slave_pos DROP PRIMARY KEY;
 ALTER TABLE mysql.gtid_slave_pos ADD PRIMARY KEY (sub_id, domain_id);
 START SLAVE;
-include/wait_for_slave_sql_error.inc [errno=1942]
+include/wait_for_slave_sql_error.inc [errno=1944]
 include/stop_slave.inc
 ALTER TABLE mysql.gtid_slave_pos DROP PRIMARY KEY;
 START SLAVE;
-include/wait_for_slave_sql_error.inc [errno=1942]
+include/wait_for_slave_sql_error.inc [errno=1944]
 include/stop_slave.inc
 ALTER TABLE mysql.gtid_slave_pos ADD PRIMARY KEY (sub_id);
 START SLAVE;
-include/wait_for_slave_sql_error.inc [errno=1942]
+include/wait_for_slave_sql_error.inc [errno=1944]
 include/stop_slave.inc
 ALTER TABLE mysql.gtid_slave_pos DROP PRIMARY KEY;
 ALTER TABLE mysql.gtid_slave_pos ADD PRIMARY KEY (domain_id, sub_id);
diff --git a/mysql-test/suite/rpl/r/rpl_gtid_ignored.result b/mysql-test/suite/rpl/r/rpl_gtid_ignored.result
index 8d5b9be..ac608c3 100644
--- a/mysql-test/suite/rpl/r/rpl_gtid_ignored.result
+++ b/mysql-test/suite/rpl/r/rpl_gtid_ignored.result
@@ -79,6 +79,7 @@ a
 9
 connection server_1;
 DROP TABLE t1;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 SET GLOBAL gtid_strict_mode= @old_gtid_strict_mode;
 SET debug_sync = "reset";
 connection server_2;
diff --git a/mysql-test/suite/rpl/r/rpl_gtid_mdev4484.result b/mysql-test/suite/rpl/r/rpl_gtid_mdev4484.result
index c49207b..aaeb0c8 100644
--- a/mysql-test/suite/rpl/r/rpl_gtid_mdev4484.result
+++ b/mysql-test/suite/rpl/r/rpl_gtid_mdev4484.result
@@ -1,5 +1,12 @@
 include/master-slave.inc
 [connection master]
+connection slave;
+include/stop_slave.inc
+SET sql_log_bin=0;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
+SET sql_log_bin=1;
+include/start_slave.inc
+connection master;
 CREATE TABLE t1 (i int) ENGINE=InnoDB;
 connection slave;
 *** MDEV-4484, incorrect error handling when entries in gtid_slave_pos not found. ***
@@ -13,7 +20,6 @@ SET @old_dbug= @@GLOBAL.debug_dbug;
 SET GLOBAL debug_dbug="+d,gtid_slave_pos_simulate_failed_delete";
 SET sql_log_bin= 0;
 CALL mtr.add_suppression("Can't find file");
-ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 SET sql_log_bin= 1;
 include/start_slave.inc
 connection master;
diff --git a/mysql-test/suite/rpl/r/rpl_gtid_stop_start.result b/mysql-test/suite/rpl/r/rpl_gtid_stop_start.result
index 3f3b5e4..ff84579 100644
--- a/mysql-test/suite/rpl/r/rpl_gtid_stop_start.result
+++ b/mysql-test/suite/rpl/r/rpl_gtid_stop_start.result
@@ -194,7 +194,7 @@ domain_id	COUNT(*)
 *** MDEV-4650: show variables; ERROR 1946 (HY000): Failed to load replication slave GTID position ***
 connection server_2;
 SET sql_log_bin=0;
-RENAME TABLE mysql.gtid_slave_pos TO mysql.gtid_slave_pos_old;
+RENAME TABLE mysql.gtid_slave_pos TO mysql.old_gtid_slave_pos;
 SET sql_log_bin=1;
 SHOW VARIABLES;
 SHOW VARIABLES LIKE 'gtid_slave_pos';
@@ -207,7 +207,7 @@ Level	Code	Message
 Error	1146	Table 'mysql.gtid_slave_pos' doesn't exist
 Error	1946	Failed to load replication slave GTID position from table mysql.gtid_slave_pos
 SET sql_log_bin=0;
-RENAME TABLE mysql.gtid_slave_pos_old TO mysql.gtid_slave_pos;
+RENAME TABLE mysql.old_gtid_slave_pos TO mysql.gtid_slave_pos;
 CALL mtr.add_suppression("Failed to load slave replication state from table mysql.gtid_slave_pos");
 SET sql_log_bin=1;
 SHOW VARIABLES LIKE 'gtid_slave_pos';
diff --git a/mysql-test/suite/rpl/r/rpl_gtid_until.result b/mysql-test/suite/rpl/r/rpl_gtid_until.result
index 886f6cf..2295aad 100644
--- a/mysql-test/suite/rpl/r/rpl_gtid_until.result
+++ b/mysql-test/suite/rpl/r/rpl_gtid_until.result
@@ -10,6 +10,8 @@ SET s= SUBSTR(s FROM 1 FOR LOCATE(",", s) - 1);
 RETURN s;
 END|
 connection server_2;
+include/stop_slave.inc
+include/start_slave.inc
 START SLAVE UNTIL master_gtid_pos = "";
 ERROR HY000: Slave is already running
 include/stop_slave_io.inc
diff --git a/mysql-test/suite/rpl/r/rpl_mdev10863.result b/mysql-test/suite/rpl/r/rpl_mdev10863.result
index 158d4a9..6accd1e 100644
--- a/mysql-test/suite/rpl/r/rpl_mdev10863.result
+++ b/mysql-test/suite/rpl/r/rpl_mdev10863.result
@@ -46,5 +46,6 @@ SET GLOBAL slave_parallel_threads=@old_parallel_threads;
 SET GLOBAL max_relay_log_size= @old_max_relay;
 include/start_slave.inc
 connection server_1;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 DROP TABLE t1;
 include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_mdev12179.result b/mysql-test/suite/rpl/r/rpl_mdev12179.result
new file mode 100644
index 0000000..4005937
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_mdev12179.result
@@ -0,0 +1,265 @@
+include/rpl_init.inc [topology=1->2]
+connection server_2;
+SET GLOBAL gtid_pos_auto_engines="innodb";
+ERROR HY000: This operation cannot be performed as you have a running slave ''; run STOP SLAVE '' first
+include/stop_slave.inc
+CHANGE MASTER TO master_use_gtid=slave_pos;
+SELECT @@gtid_pos_auto_engines;
+@@gtid_pos_auto_engines
+
+SELECT @@SESSION.gtid_pos_auto_engines;
+ERROR HY000: Variable 'gtid_pos_auto_engines' is a GLOBAL variable
+SET GLOBAL gtid_pos_auto_engines= NULL;
+ERROR 42000: Variable 'gtid_pos_auto_engines' can't be set to the value of 'NULL'
+SET GLOBAL gtid_pos_auto_engines="innodb";
+SELECT @@gtid_pos_auto_engines;
+@@gtid_pos_auto_engines
+InnoDB
+SET GLOBAL gtid_pos_auto_engines="myisam,innodb";
+SELECT @@gtid_pos_auto_engines;
+@@gtid_pos_auto_engines
+MyISAM,InnoDB
+SET GLOBAL gtid_pos_auto_engines="innodb,myisam";
+SELECT @@gtid_pos_auto_engines;
+@@gtid_pos_auto_engines
+InnoDB,MyISAM
+SET GLOBAL gtid_pos_auto_engines="innodb,innodb,myisam,innodb,myisam,myisam,innodb";
+SELECT @@gtid_pos_auto_engines;
+@@gtid_pos_auto_engines
+InnoDB,MyISAM
+SET GLOBAL gtid_pos_auto_engines=DEFAULT;
+SELECT @@gtid_pos_auto_engines;
+@@gtid_pos_auto_engines
+
+SET GLOBAL gtid_pos_auto_engines="";
+SELECT @@gtid_pos_auto_engines;
+@@gtid_pos_auto_engines
+
+include/start_slave.inc
+connection server_1;
+CREATE TABLE t1 (a INT PRIMARY KEY);
+INSERT INTO t1 VALUES (1);
+SELECT * FROM t1 ORDER BY a;
+a
+1
+connection server_2;
+SELECT * FROM t1 ORDER BY a;
+a
+1
+include/stop_slave.inc
+SET sql_log_bin=0;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
+CREATE TABLE mysql.gtid_slave_pos_innodb LIKE mysql.gtid_slave_pos;
+ALTER TABLE mysql.gtid_slave_pos_innodb ENGINE=InnoDB;
+INSERT INTO mysql.gtid_slave_pos_innodb SELECT * FROM mysql.gtid_slave_pos;
+TRUNCATE mysql.gtid_slave_pos;
+SET sql_log_bin=1;
+connection server_1;
+INSERT INTO t1 VALUES (2);
+INSERT INTO t1 VALUES (3);
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+include/save_master_gtid.inc
+*** Restart server with --gtid-pos-auto-engines=innodb,myisam ***
+connection server_2;
+include/sync_with_master_gtid.inc
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+*** Verify no new gtid_slave_pos* tables are created ***
+SELECT table_name, engine FROM information_schema.tables
+WHERE table_schema='mysql' AND table_name LIKE 'gtid_slave_pos%'
+ ORDER BY table_name;
+table_name	engine
+gtid_slave_pos	MyISAM
+gtid_slave_pos_innodb	InnoDB
+SELECT @@gtid_pos_auto_engines;
+@@gtid_pos_auto_engines
+InnoDB,MyISAM
+include/stop_slave.inc
+SET sql_log_bin=0;
+INSERT INTO mysql.gtid_slave_pos_innodb SELECT * FROM mysql.gtid_slave_pos;
+DROP TABLE mysql.gtid_slave_pos;
+RENAME TABLE mysql.gtid_slave_pos_innodb TO mysql.gtid_slave_pos;
+SET sql_log_bin=1;
+connection server_1;
+CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (4);
+INSERT INTO t2 VALUES (1);
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+4
+SELECT * FROM t2 ORDER BY a;
+a
+1
+include/save_master_gtid.inc
+*** Restart server with --gtid-pos-auto-engines=myisam,innodb ***
+connection server_2;
+include/sync_with_master_gtid.inc
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+4
+SELECT * FROM t2 ORDER BY a;
+a
+1
+*** Verify that no new gtid_slave_pos* tables are auto-created ***
+SELECT table_name, engine FROM information_schema.tables
+WHERE table_schema='mysql' AND table_name LIKE 'gtid_slave_pos%'
+ ORDER BY table_name;
+table_name	engine
+gtid_slave_pos	InnoDB
+include/stop_slave.inc
+SET sql_log_bin=0;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
+SET sql_log_bin=1;
+connection server_1;
+INSERT INTO t1 VALUES (5);
+INSERT INTO t2 VALUES (2);
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+4
+5
+SELECT * FROM t2 ORDER BY a;
+a
+1
+2
+include/save_master_gtid.inc
+connection server_2;
+include/sync_with_master_gtid.inc
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+4
+5
+SELECT * FROM t2 ORDER BY a;
+a
+1
+2
+*** Verify that mysql.gtid_slave_pos_InnoDB is auto-created ***
+SELECT table_name, engine FROM information_schema.tables
+WHERE table_schema='mysql' AND table_name LIKE 'gtid_slave_pos%'
+ ORDER BY table_name;
+table_name	engine
+gtid_slave_pos	MyISAM
+gtid_slave_pos_InnoDB	InnoDB
+include/stop_slave.inc
+SET sql_log_bin=0;
+INSERT INTO mysql.gtid_slave_pos SELECT * FROM mysql.gtid_slave_pos_InnoDB;
+DROP TABLE mysql.gtid_slave_pos_InnoDB;
+SET sql_log_bin=1;
+connection server_1;
+INSERT INTO t1 VALUES (6);
+INSERT INTO t2 VALUES (3);
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+4
+5
+6
+SELECT * FROM t2 ORDER BY a;
+a
+1
+2
+3
+include/save_master_gtid.inc
+*** Restart server without --gtid-pos-auto-engines ***
+connection server_2;
+include/sync_with_master_gtid.inc
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+4
+5
+6
+SELECT * FROM t2 ORDER BY a;
+a
+1
+2
+3
+*** Verify that no mysql.gtid_slave_pos* table is auto-created ***
+SELECT table_name, engine FROM information_schema.tables
+WHERE table_schema='mysql' AND table_name LIKE 'gtid_slave_pos%'
+ ORDER BY table_name;
+table_name	engine
+gtid_slave_pos	MyISAM
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
+domain_id	max(seq_no)
+0	11
+include/stop_slave.inc
+SET GLOBAL gtid_pos_auto_engines="innodb";
+include/start_slave.inc
+connection server_1;
+INSERT INTO t1 VALUES (7);
+INSERT INTO t2 VALUES (4);
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+4
+5
+6
+7
+SELECT * FROM t2 ORDER BY a;
+a
+1
+2
+3
+4
+include/save_master_gtid.inc
+connection server_2;
+include/sync_with_master_gtid.inc
+SELECT * FROM t1 ORDER BY a;
+a
+1
+2
+3
+4
+5
+6
+7
+SELECT * FROM t2 ORDER BY a;
+a
+1
+2
+3
+4
+*** Verify that mysql.gtid_slave_pos_InnoDB is auto-created ***
+SELECT table_name, engine FROM information_schema.tables
+WHERE table_schema='mysql' AND table_name LIKE 'gtid_slave_pos%'
+ ORDER BY table_name;
+table_name	engine
+gtid_slave_pos	MyISAM
+gtid_slave_pos_InnoDB	InnoDB
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
+domain_id	max(seq_no)
+0	13
+include/stop_slave.inc
+SET GLOBAL gtid_pos_auto_engines="";
+SET sql_log_bin=0;
+DROP TABLE mysql.gtid_slave_pos_InnoDB;
+SET sql_log_bin=1;
+include/start_slave.inc
+connection server_1;
+DROP TABLE t1, t2;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/show_status_stop_slave_race-7126.result b/mysql-test/suite/rpl/r/show_status_stop_slave_race-7126.result
index 64219e3..999d941 100644
--- a/mysql-test/suite/rpl/r/show_status_stop_slave_race-7126.result
+++ b/mysql-test/suite/rpl/r/show_status_stop_slave_race-7126.result
@@ -3,6 +3,7 @@ include/master-slave.inc
 call mtr.add_suppression("Master is configured to log replication events");
 connection slave;
 connection slave;
+include/wait_for_slave_to_stop.inc
 start slave;
 connection master;
 include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_gtid_crash.test b/mysql-test/suite/rpl/t/rpl_gtid_crash.test
index b81cbd3..5cf28b6 100644
--- a/mysql-test/suite/rpl/t/rpl_gtid_crash.test
+++ b/mysql-test/suite/rpl/t/rpl_gtid_crash.test
@@ -161,8 +161,8 @@ EOF
 --write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
 wait
 EOF
-SET GLOBAL debug_dbug="+d,crash_commit_before";
 START SLAVE;
+SET GLOBAL debug_dbug="+d,crash_commit_before";
 
 --connection server_1
 INSERT INTO t1 VALUES (5);
@@ -185,8 +185,8 @@ EOF
 --write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
 wait
 EOF
-SET GLOBAL debug_dbug="+d,crash_commit_after";
 START SLAVE;
+SET GLOBAL debug_dbug="+d,crash_commit_after";
 
 --connection server_1
 INSERT INTO t1 VALUES (6);
diff --git a/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test b/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test
index 05da466..796f689 100644
--- a/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test
+++ b/mysql-test/suite/rpl/t/rpl_gtid_errorhandling.test
@@ -17,7 +17,7 @@ INSERT INTO t1 VALUES (1);
 
 --connection slave
 CALL mtr.add_suppression("Slave: Failed to open mysql.gtid_slave_pos");
---let $slave_sql_errno=1942
+--let $slave_sql_errno=1944
 --source include/wait_for_slave_sql_error.inc
 
 --source include/stop_slave.inc
@@ -25,19 +25,19 @@ ALTER TABLE mysql.gtid_slave_pos CHANGE seq_no seq_no BIGINT UNSIGNED NOT NULL;
 ALTER TABLE mysql.gtid_slave_pos DROP PRIMARY KEY;
 ALTER TABLE mysql.gtid_slave_pos ADD PRIMARY KEY (sub_id, domain_id);
 START SLAVE;
---let $slave_sql_errno=1942
+--let $slave_sql_errno=1944
 --source include/wait_for_slave_sql_error.inc
 
 --source include/stop_slave.inc
 ALTER TABLE mysql.gtid_slave_pos DROP PRIMARY KEY;
 START SLAVE;
---let $slave_sql_errno=1942
+--let $slave_sql_errno=1944
 --source include/wait_for_slave_sql_error.inc
 
 --source include/stop_slave.inc
 ALTER TABLE mysql.gtid_slave_pos ADD PRIMARY KEY (sub_id);
 START SLAVE;
---let $slave_sql_errno=1942
+--let $slave_sql_errno=1944
 --source include/wait_for_slave_sql_error.inc
 
 --source include/stop_slave.inc
diff --git a/mysql-test/suite/rpl/t/rpl_gtid_ignored.test b/mysql-test/suite/rpl/t/rpl_gtid_ignored.test
index cb98be3..6e927bd 100644
--- a/mysql-test/suite/rpl/t/rpl_gtid_ignored.test
+++ b/mysql-test/suite/rpl/t/rpl_gtid_ignored.test
@@ -129,6 +129,7 @@ SELECT * FROM t1 ORDER BY a;
 # Clean up.
 --connection server_1
 DROP TABLE t1;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 SET GLOBAL gtid_strict_mode= @old_gtid_strict_mode;
 SET debug_sync = "reset";
 
diff --git a/mysql-test/suite/rpl/t/rpl_gtid_mdev4484.test b/mysql-test/suite/rpl/t/rpl_gtid_mdev4484.test
index 43634ec..57f7853 100644
--- a/mysql-test/suite/rpl/t/rpl_gtid_mdev4484.test
+++ b/mysql-test/suite/rpl/t/rpl_gtid_mdev4484.test
@@ -2,6 +2,18 @@
 --source include/have_innodb.inc
 --source include/have_debug.inc
 
+--connection slave
+--source include/stop_slave.inc
+# Since we inject an error updating mysql.gtid_slave_pos, we will get different
+# output depending on whether it is InnoDB or MyISAM (roll back or no roll
+# back). So fix it to make sure we are consistent, in case an earlier test case
+# left it as InnoDB.
+SET sql_log_bin=0;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
+SET sql_log_bin=1;
+--source include/start_slave.inc
+
+--connection master
 CREATE TABLE t1 (i int) ENGINE=InnoDB;
 
 --sync_slave_with_master
@@ -20,10 +32,6 @@ SET @old_dbug= @@GLOBAL.debug_dbug;
 SET GLOBAL debug_dbug="+d,gtid_slave_pos_simulate_failed_delete";
 SET sql_log_bin= 0;
 CALL mtr.add_suppression("Can't find file");
-# Since we inject an error updating mysql.gtid_slave_pos, we will get different
-# output depending on whether it is InnoDB or MyISAM (roll back or no roll
-# back). So fix it to make sure we are consistent.
-ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 SET sql_log_bin= 1;
 --source include/start_slave.inc
 
diff --git a/mysql-test/suite/rpl/t/rpl_gtid_stop_start.test b/mysql-test/suite/rpl/t/rpl_gtid_stop_start.test
index 09b3501..309debd 100644
--- a/mysql-test/suite/rpl/t/rpl_gtid_stop_start.test
+++ b/mysql-test/suite/rpl/t/rpl_gtid_stop_start.test
@@ -232,6 +232,20 @@ EOF
 SET sql_log_bin= 0;
 ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 SET sql_log_bin= 1;
+# Do a second restart to get the mysql.gtid_slave_pos table loaded with
+# the right engine.
+--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
+wait
+EOF
+--shutdown_server 30
+--source include/wait_until_disconnected.inc
+
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
+restart:  
+EOF
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
 --source include/start_slave.inc
 
 --connection server_1
@@ -285,7 +299,7 @@ SELECT domain_id, COUNT(*) FROM mysql.gtid_slave_pos GROUP BY domain_id;
 --connection server_2
 SET sql_log_bin=0;
 --let $old_pos= `SELECT @@GLOBAL.gtid_slave_pos`
-RENAME TABLE mysql.gtid_slave_pos TO mysql.gtid_slave_pos_old;
+RENAME TABLE mysql.gtid_slave_pos TO mysql.old_gtid_slave_pos;
 SET sql_log_bin=1;
 
 --write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
@@ -313,7 +327,7 @@ SHOW WARNINGS;
 # Restore things.
 
 SET sql_log_bin=0;
-RENAME TABLE mysql.gtid_slave_pos_old TO mysql.gtid_slave_pos;
+RENAME TABLE mysql.old_gtid_slave_pos TO mysql.gtid_slave_pos;
 CALL mtr.add_suppression("Failed to load slave replication state from table mysql.gtid_slave_pos");
 SET sql_log_bin=1;
 
diff --git a/mysql-test/suite/rpl/t/rpl_gtid_until.test b/mysql-test/suite/rpl/t/rpl_gtid_until.test
index 20d4510..aa05ecf 100644
--- a/mysql-test/suite/rpl/t/rpl_gtid_until.test
+++ b/mysql-test/suite/rpl/t/rpl_gtid_until.test
@@ -19,6 +19,9 @@ delimiter ;|
 
 --connection server_2
 --sync_with_master
+# Restart SQL thread to pick up ALTER TABLE of mysql.gtid_slave_pos.
+--source include/stop_slave.inc
+--source include/start_slave.inc
 
 # Both replication threads must be stopped for UNTIL master_gtid_pos.
 --error ER_SLAVE_WAS_RUNNING
diff --git a/mysql-test/suite/rpl/t/rpl_mdev10863.test b/mysql-test/suite/rpl/t/rpl_mdev10863.test
index 796e770..81cdfd8 100644
--- a/mysql-test/suite/rpl/t/rpl_mdev10863.test
+++ b/mysql-test/suite/rpl/t/rpl_mdev10863.test
@@ -99,6 +99,7 @@ SET GLOBAL max_relay_log_size= @old_max_relay;
 --source include/start_slave.inc
 
 --connection server_1
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
 DROP TABLE t1;
 
 --source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_mdev12179.test b/mysql-test/suite/rpl/t/rpl_mdev12179.test
new file mode 100644
index 0000000..a9113c9
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_mdev12179.test
@@ -0,0 +1,280 @@
+--source include/have_innodb.inc
+--let $rpl_topology=1->2
+--source include/rpl_init.inc
+
+--connection server_2
+--error ER_SLAVE_MUST_STOP
+SET GLOBAL gtid_pos_auto_engines="innodb";
+--source include/stop_slave.inc
+CHANGE MASTER TO master_use_gtid=slave_pos;
+
+# Test the @@gtid_pos_auto_engines sysvar.
+SELECT @@gtid_pos_auto_engines;
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SELECT @@SESSION.gtid_pos_auto_engines;
+--error ER_WRONG_VALUE_FOR_VAR
+SET GLOBAL gtid_pos_auto_engines= NULL;
+SET GLOBAL gtid_pos_auto_engines="innodb";
+SELECT @@gtid_pos_auto_engines;
+SET GLOBAL gtid_pos_auto_engines="myisam,innodb";
+SELECT @@gtid_pos_auto_engines;
+SET GLOBAL gtid_pos_auto_engines="innodb,myisam";
+SELECT @@gtid_pos_auto_engines;
+SET GLOBAL gtid_pos_auto_engines="innodb,innodb,myisam,innodb,myisam,myisam,innodb";
+SELECT @@gtid_pos_auto_engines;
+SET GLOBAL gtid_pos_auto_engines=DEFAULT;
+SELECT @@gtid_pos_auto_engines;
+SET GLOBAL gtid_pos_auto_engines="";
+SELECT @@gtid_pos_auto_engines;
+
+--source include/start_slave.inc
+
+--connection server_1
+CREATE TABLE t1 (a INT PRIMARY KEY);
+INSERT INTO t1 VALUES (1);
+SELECT * FROM t1 ORDER BY a;
+--save_master_pos
+
+--connection server_2
+--sync_with_master
+SELECT * FROM t1 ORDER BY a;
+--source include/stop_slave.inc
+SET sql_log_bin=0;
+# Reset storage engine for mysql.gtid_slave_pos in case an earlier test
+# might have changed it to InnoDB.
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
+CREATE TABLE mysql.gtid_slave_pos_innodb LIKE mysql.gtid_slave_pos;
+ALTER TABLE mysql.gtid_slave_pos_innodb ENGINE=InnoDB;
+INSERT INTO mysql.gtid_slave_pos_innodb SELECT * FROM mysql.gtid_slave_pos;
+TRUNCATE mysql.gtid_slave_pos;
+SET sql_log_bin=1;
+
+# Restart the slave mysqld server, and verify that the GTID position is
+# read correctly from the new mysql.gtid_slave_pos_innodb table.
+
+--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
+wait
+EOF
+--shutdown_server 30
+--source include/wait_until_disconnected.inc
+
+--connection server_1
+INSERT INTO t1 VALUES (2);
+INSERT INTO t1 VALUES (3);
+SELECT * FROM t1 ORDER BY a;
+--source include/save_master_gtid.inc
+
+# Let the slave mysqld server start again.
+# As we are restarting, also take the opportunity to test --gtid-pos-auto-engines
+--echo *** Restart server with --gtid-pos-auto-engines=innodb,myisam ***
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
+restart: --skip-slave-start=0 --gtid-pos-auto-engines=innodb,myisam
+EOF
+
+--connection server_2
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--source include/sync_with_master_gtid.inc
+SELECT * FROM t1 ORDER BY a;
+
+--echo *** Verify no new gtid_slave_pos* tables are created ***
+SELECT table_name, engine FROM information_schema.tables
+ WHERE table_schema='mysql' AND table_name LIKE 'gtid_slave_pos%'
+ ORDER BY table_name;
+
+SELECT @@gtid_pos_auto_engines;
+--source include/stop_slave.inc
+SET sql_log_bin=0;
+INSERT INTO mysql.gtid_slave_pos_innodb SELECT * FROM mysql.gtid_slave_pos;
+DROP TABLE mysql.gtid_slave_pos;
+RENAME TABLE mysql.gtid_slave_pos_innodb TO mysql.gtid_slave_pos;
+SET sql_log_bin=1;
+
+--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
+wait
+EOF
+--shutdown_server 30
+--source include/wait_until_disconnected.inc
+
+--connection server_1
+CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (4);
+INSERT INTO t2 VALUES (1);
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+--source include/save_master_gtid.inc
+
+--echo *** Restart server with --gtid-pos-auto-engines=myisam,innodb ***
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
+restart: --skip-slave-start=0 --gtid-pos-auto-engines=myisam,innodb
+EOF
+
+--connection server_2
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--source include/sync_with_master_gtid.inc
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+
+--echo *** Verify that no new gtid_slave_pos* tables are auto-created ***
+SELECT table_name, engine FROM information_schema.tables
+ WHERE table_schema='mysql' AND table_name LIKE 'gtid_slave_pos%'
+ ORDER BY table_name;
+
+
+--source include/stop_slave.inc
+SET sql_log_bin=0;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
+SET sql_log_bin=1;
+
+--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
+wait
+EOF
+--shutdown_server 30
+--source include/wait_until_disconnected.inc
+
+--connection server_1
+INSERT INTO t1 VALUES (5);
+INSERT INTO t2 VALUES (2);
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+--source include/save_master_gtid.inc
+
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
+--echo *** Restart server with --gtid-pos-auto-engines=innodb ***
+restart: --skip-slave-start=0 --gtid-pos-auto-engines=innodb
+EOF
+
+--connection server_2
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--source include/sync_with_master_gtid.inc
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+
+--echo *** Verify that mysql.gtid_slave_pos_InnoDB is auto-created ***
+# Note, the create happens asynchronously, so wait for it.
+let $wait_condition=
+  SELECT EXISTS (SELECT * FROM information_schema.tables
+                  WHERE table_schema='mysql' AND table_name='gtid_slave_pos_InnoDB');
+--source include/wait_condition.inc
+SELECT table_name, engine FROM information_schema.tables
+ WHERE table_schema='mysql' AND table_name LIKE 'gtid_slave_pos%'
+ ORDER BY table_name;
+
+
+--source include/stop_slave.inc
+SET sql_log_bin=0;
+INSERT INTO mysql.gtid_slave_pos SELECT * FROM mysql.gtid_slave_pos_InnoDB;
+DROP TABLE mysql.gtid_slave_pos_InnoDB;
+SET sql_log_bin=1;
+
+--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
+wait
+EOF
+--shutdown_server 30
+--source include/wait_until_disconnected.inc
+
+--connection server_1
+INSERT INTO t1 VALUES (6);
+INSERT INTO t2 VALUES (3);
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+--source include/save_master_gtid.inc
+
+--echo *** Restart server without --gtid-pos-auto-engines ***
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
+restart: --skip-slave-start=0
+EOF
+
+--connection server_2
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--source include/sync_with_master_gtid.inc
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+
+--echo *** Verify that no mysql.gtid_slave_pos* table is auto-created ***
+SELECT table_name, engine FROM information_schema.tables
+ WHERE table_schema='mysql' AND table_name LIKE 'gtid_slave_pos%'
+ ORDER BY table_name;
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
+
+--source include/stop_slave.inc
+SET GLOBAL gtid_pos_auto_engines="innodb";
+--source include/start_slave.inc
+
+--connection server_1
+INSERT INTO t1 VALUES (7);
+INSERT INTO t2 VALUES (4);
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+--source include/save_master_gtid.inc
+
+--connection server_2
+--source include/sync_with_master_gtid.inc
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+
+--echo *** Verify that mysql.gtid_slave_pos_InnoDB is auto-created ***
+let $wait_condition=
+  SELECT EXISTS (SELECT * FROM information_schema.tables
+                  WHERE table_schema='mysql' AND table_name='gtid_slave_pos_InnoDB');
+--source include/wait_condition.inc
+SELECT table_name, engine FROM information_schema.tables
+ WHERE table_schema='mysql' AND table_name LIKE 'gtid_slave_pos%'
+ ORDER BY table_name;
+SELECT domain_id, max(seq_no) FROM mysql.gtid_slave_pos GROUP BY domain_id;
+
+# Check that the auto-created InnoDB table starts being used without
+# needing slave restart. The auto-create happens asynchronously, so it
+# is non-deterministic when it will start being used. But we can wait
+# for it to happen.
+
+--let $count=300
+--let $done=0
+--let $old_silent= $keep_include_silent
+--let $keep_include_silent= 1
+--disable_query_log
+while (!$done)
+{
+  --connection server_1
+  INSERT INTO t2(a) SELECT 1+MAX(a) FROM t2;
+  --source include/save_master_gtid.inc
+
+  --connection server_2
+  --source include/sync_with_master_gtid.inc
+  --let $done=`SELECT COUNT(*) > 0 FROM mysql.gtid_slave_pos_InnoDB`
+  if (!$done)
+  {
+    dec $count;
+    if (!$count)
+    {
+      SELECT * FROM mysql.gtid_slave_pos_InnoDB;
+      --die Timeout waiting for mysql.gtid_slave_pos_InnoDB to be used
+    }
+    real_sleep 0.1;
+  }
+}
+--enable_query_log
+--let $keep_include_silent=$old_silent
+# Note that at this point, the contents of table t2, as well as the GTID
+# position, is non-deterministic.
+
+
+#--connection server_2
+--source include/stop_slave.inc
+SET GLOBAL gtid_pos_auto_engines="";
+SET sql_log_bin=0;
+DROP TABLE mysql.gtid_slave_pos_InnoDB;
+SET sql_log_bin=1;
+--source include/start_slave.inc
+
+--connection server_1
+DROP TABLE t1, t2;
+
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/show_status_stop_slave_race-7126.test b/mysql-test/suite/rpl/t/show_status_stop_slave_race-7126.test
index 88d7076..06a9e8a 100644
--- a/mysql-test/suite/rpl/t/show_status_stop_slave_race-7126.test
+++ b/mysql-test/suite/rpl/t/show_status_stop_slave_race-7126.test
@@ -15,6 +15,7 @@ call mtr.add_suppression("Master is configured to log replication events");
 # All done.
 
 --connection slave
+--source include/wait_for_slave_to_stop.inc
 start slave;
 
 --connection master
diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
index 13072f5..46ffa2f 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
+++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
@@ -1115,6 +1115,20 @@ NUMERIC_BLOCK_SIZE	NULL
 ENUM_VALUE_LIST	OFF,ON
 READ_ONLY	NO
 COMMAND_LINE_ARGUMENT	OPTIONAL
+VARIABLE_NAME	GTID_POS_AUTO_ENGINES
+SESSION_VALUE	NULL
+GLOBAL_VALUE	
+GLOBAL_VALUE_ORIGIN	COMPILE-TIME
+DEFAULT_VALUE	
+VARIABLE_SCOPE	GLOBAL
+VARIABLE_TYPE	VARCHAR
+VARIABLE_COMMENT	List of engines for which to automatically create a mysql.gtid_slave_pos_ENGINE table, if a transaction using that engine is replicated. This can be used to avoid introducing cross-engine transactions, if engines are used different from that used by table mysql.gtid_slave_pos
+NUMERIC_MIN_VALUE	NULL
+NUMERIC_MAX_VALUE	NULL
+NUMERIC_BLOCK_SIZE	NULL
+ENUM_VALUE_LIST	NULL
+READ_ONLY	NO
+COMMAND_LINE_ARGUMENT	NULL
 VARIABLE_NAME	GTID_SEQ_NO
 SESSION_VALUE	0
 GLOBAL_VALUE	NULL
diff --git a/scripts/mysql_system_tables.sql b/scripts/mysql_system_tables.sql
index 7b61416..60c1ac9 100644
--- a/scripts/mysql_system_tables.sql
+++ b/scripts/mysql_system_tables.sql
@@ -224,6 +224,8 @@ CREATE TABLE IF NOT EXISTS column_stats (db_name varchar(64) NOT NULL, table_nam
 
 CREATE TABLE IF NOT EXISTS index_stats (db_name varchar(64) NOT NULL, table_name varchar(64) NOT NULL, index_name varchar(64) NOT NULL, prefix_arity int(11) unsigned NOT NULL, avg_frequency decimal(12,4) DEFAULT NULL, PRIMARY KEY (db_name,table_name,index_name,prefix_arity) ) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Statistics on Indexes';
 
+-- Note: This definition must be kept in sync with the one used in
+-- build_gtid_pos_create_query() in sql/slave.cc
 SET @cmd= "CREATE TABLE IF NOT EXISTS gtid_slave_pos (
   domain_id INT UNSIGNED NOT NULL,
   sub_id BIGINT UNSIGNED NOT NULL,
diff --git a/sql/handler.cc b/sql/handler.cc
index c74675e..7a1cec3 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1546,6 +1546,7 @@ static int
 commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
 {
   int error= 0;
+  uint count= 0;
   Ha_trx_info *ha_info= trans->ha_list, *ha_info_next;
   DBUG_ENTER("commit_one_phase_2");
   if (is_real_trans)
@@ -1563,6 +1564,8 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
       }
       /* Should this be done only if is_real_trans is set ? */
       status_var_increment(thd->status_var.ha_commit_count);
+      if (is_real_trans && ht != binlog_hton && ha_info->is_trx_read_write())
+        ++count;
       ha_info_next= ha_info->next();
       ha_info->reset(); /* keep it conveniently zero-filled */
     }
@@ -1581,6 +1584,8 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
   {
     thd->has_waiter= false;
     thd->transaction.cleanup();
+    if (count >= 2)
+      statistic_increment(transactions_multi_engine, LOCK_status);
   }
 
   DBUG_RETURN(error);
diff --git a/sql/log.h b/sql/log.h
index eaa63d4..f57693f 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -1096,6 +1096,7 @@ void make_default_log_name(char **out, const char* log_ext, bool once);
 void binlog_reset_cache(THD *thd);
 
 extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log;
+extern handlerton *binlog_hton;
 extern LOGGER logger;
 
 extern const char *log_bin_index;
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 44a2bf7..6aaa9a6 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -5027,6 +5027,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
   int expected_error,actual_error= 0;
   Schema_specification_st db_options;
   uint64 sub_id= 0;
+  void *hton= NULL;
   rpl_gtid gtid;
   Relay_log_info const *rli= rgi->rli;
   Rpl_filter *rpl_filter= rli->mi->rpl_filter;
@@ -5197,7 +5198,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
 
           gtid= rgi->current_gtid;
           if (rpl_global_gtid_slave_state->record_gtid(thd, &gtid, sub_id,
-                                                       true, false))
+                                                       true, false, &hton))
           {
             int errcode= thd->get_stmt_da()->sql_errno();
             if (!is_parallel_retry_error(rgi, errcode))
@@ -5418,7 +5419,7 @@ START SLAVE; . Query: '%s'", expected_error, thd->query());
 
 end:
   if (sub_id && !thd->is_slave_error)
-    rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, rgi);
+    rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, hton, rgi);
 
   /*
     Probably we have set thd->query, thd->db, thd->catalog to point to places
@@ -7901,15 +7902,17 @@ Gtid_list_log_event::do_apply_event(rpl_group_info *rgi)
   int ret;
   if (gl_flags & FLAG_IGN_GTIDS)
   {
+    void *hton= NULL;
     uint32 i;
+
     for (i= 0; i < count; ++i)
     {
       if ((ret= rpl_global_gtid_slave_state->record_gtid(thd, &list[i],
-                                                        sub_id_list[i],
-                                                        false, false)))
+                                                         sub_id_list[i],
+                                                         false, false, &hton)))
         return ret;
       rpl_global_gtid_slave_state->update_state_hash(sub_id_list[i], &list[i],
-                                                    NULL);
+                                                     hton, NULL);
     }
   }
   ret= Log_event::do_apply_event(rgi);
@@ -8390,6 +8393,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
   rpl_gtid gtid;
   uint64 sub_id= 0;
   Relay_log_info const *rli= rgi->rli;
+  void *hton= NULL;
 
   /*
     XID_EVENT works like a COMMIT statement. And it also updates the
@@ -8414,7 +8418,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
 
     gtid= rgi->current_gtid;
     err= rpl_global_gtid_slave_state->record_gtid(thd, &gtid, sub_id, true,
-                                                  false);
+                                                  false, &hton);
     if (err)
     {
       int ec= thd->get_stmt_da()->sql_errno();
@@ -8447,7 +8451,7 @@ int Xid_log_event::do_apply_event(rpl_group_info *rgi)
   thd->mdl_context.release_transactional_locks();
 
   if (!res && sub_id)
-    rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, rgi);
+    rpl_global_gtid_slave_state->update_state_hash(sub_id, &gtid, hton, rgi);
 
   /*
     Increment the global status commit count variable
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index b8e0b44..c66f1c4 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -372,6 +372,8 @@ char *my_bind_addr_str;
 static char *default_collation_name;
 char *default_storage_engine, *default_tmp_storage_engine;
 char *enforced_storage_engine=NULL;
+char *gtid_pos_auto_engines;
+plugin_ref *opt_gtid_pos_auto_plugins;
 static char compiled_default_collation_name[]= MYSQL_DEFAULT_COLLATION_NAME;
 static I_List<CONNECT> thread_cache;
 static bool binlog_format_used= false;
@@ -523,6 +525,9 @@ ulong max_connections, max_connect_errors;
 ulong extra_max_connections;
 uint max_digest_length= 0;
 ulong slave_retried_transactions;
+ulong transactions_multi_engine;
+ulong rpl_transactions_multi_engine;
+ulong transactions_gtid_foreign_engine;
 ulonglong slave_skipped_errors;
 ulong feature_files_opened_with_delayed_keys= 0, feature_check_constraint= 0;
 ulonglong denied_connections;
@@ -4258,6 +4263,7 @@ static int init_common_variables()
   default_storage_engine= const_cast<char *>("MyISAM");
 #endif
   default_tmp_storage_engine= NULL;
+  gtid_pos_auto_engines= const_cast<char *>("");
 
   /*
     Add server status variables to the dynamic list of
@@ -4937,6 +4943,34 @@ static int init_default_storage_engine_impl(const char *opt_name,
   return 0;
 }
 
+
+static int
+init_gtid_pos_auto_engines(void)
+{
+  plugin_ref *plugins;
+
+  /*
+    For the command-line option --gtid_pos_auto_engines, we allow (and ignore)
+    engines that are unknown. This is convenient, since it allows to set
+    default auto-create engines that might not be used by particular users.
+    The option sets a list of storage engines that will have gtid position
+    table auto-created for them if needed. And if the engine is not available,
+    then it will certainly not be needed.
+  */
+  if (gtid_pos_auto_engines)
+    plugins= resolve_engine_list(NULL, gtid_pos_auto_engines,
+                                 strlen(gtid_pos_auto_engines), false, false);
+  else
+    plugins= resolve_engine_list(NULL, "", 0, false, false);
+  if (!plugins)
+    return 1;
+  mysql_mutex_lock(&LOCK_global_system_variables);
+  opt_gtid_pos_auto_plugins= plugins;
+  mysql_mutex_unlock(&LOCK_global_system_variables);
+  return 0;
+}
+
+
 static int init_server_components()
 {
   DBUG_ENTER("init_server_components");
@@ -5374,6 +5408,9 @@ static int init_server_components()
   if (init_default_storage_engine(enforced_storage_engine, enforced_table_plugin))
     unireg_abort(1);
 
+  if (init_gtid_pos_auto_engines())
+    unireg_abort(1);
+
 #ifdef USE_ARIA_FOR_TMP_TABLES
   if (!ha_storage_engine_is_enabled(maria_hton) && !opt_bootstrap)
   {
@@ -7367,6 +7404,14 @@ struct my_option my_long_options[]=
    "Set up signals usable for debugging. Deprecated, use --debug-gdb instead.",
    &opt_debugging, &opt_debugging,
    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+  {"gtid-pos-auto-engines", 0,
+   "List of engines for which to automatically create a "
+   "mysql.gtid_slave_pos_ENGINE table, if a transaction using that engine "
+   "is replicated. This can be used to avoid introducing cross-engine "
+   "transactions, if engines are used different from that used by table "
+   "mysql.gtid_slave_pos",
+   &gtid_pos_auto_engines, 0, 0, GET_STR, REQUIRED_ARG,
+   0, 0, 0, 0, 0, 0 },
 #ifdef HAVE_LARGE_PAGE_OPTION
   {"super-large-pages", 0, "Enable support for super large pages.",
    &opt_super_large_pages, &opt_super_large_pages, 0,
@@ -7764,7 +7809,7 @@ static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff)
   var->type= SHOW_LONGLONG;
   var->value= buff;
 
-  *((longlong *)buff)= any_slave_sql_running();
+  *((longlong *)buff)= any_slave_sql_running(false);
 
   return 0;
 }
@@ -8539,6 +8584,9 @@ SHOW_VAR status_vars[]= {
   {"Threads_connected",        (char*) &connection_count,       SHOW_INT},
   {"Threads_created",	       (char*) &thread_created,		SHOW_LONG_NOFLUSH},
   {"Threads_running",          (char*) &thread_running,         SHOW_INT},
+  {"Transactions_multi_engine", (char*) &transactions_multi_engine, SHOW_LONG},
+  {"Rpl_transactions_multi_engine", (char*) &rpl_transactions_multi_engine, SHOW_LONG},
+  {"Transactions_gtid_foreign_engine", (char*) &transactions_gtid_foreign_engine, SHOW_LONG},
   {"Update_scan",	       (char*) offsetof(STATUS_VAR, update_scan_count), SHOW_LONG_STATUS},
   {"Uptime",                   (char*) &show_starttime,         SHOW_SIMPLE_FUNC},
 #ifdef ENABLED_PROFILING
@@ -8782,6 +8830,9 @@ static int mysql_init_variables(void)
   report_user= report_password = report_host= 0;	/* TO BE DELETED */
   opt_relay_logname= opt_relaylog_index_name= 0;
   slave_retried_transactions= 0;
+  transactions_multi_engine= 0;
+  rpl_transactions_multi_engine= 0;
+  transactions_gtid_foreign_engine= 0;
   log_bin_basename= NULL;
   log_bin_index= NULL;
 
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 38e42dd..6cf5a37 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -19,6 +19,7 @@
 
 #include <my_global.h> /* MYSQL_PLUGIN_IMPORT, FN_REFLEN, FN_EXTLEN */
 #include "sql_basic_types.h"			/* query_id_t */
+#include "sql_plugin.h"
 #include "sql_bitmap.h"                         /* Bitmap */
 #include "my_decimal.h"                         /* my_decimal */
 #include "mysql_com.h"                     /* SERVER_VERSION_LENGTH */
@@ -130,6 +131,9 @@ extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
 extern my_bool opt_slave_compressed_protocol, use_temp_pool;
 extern ulong slave_exec_mode_options, slave_ddl_exec_mode_options;
 extern ulong slave_retried_transactions;
+extern ulong transactions_multi_engine;
+extern ulong rpl_transactions_multi_engine;
+extern ulong transactions_gtid_foreign_engine;
 extern ulong slave_run_triggers_for_rbr;
 extern ulonglong slave_type_conversions_options;
 extern my_bool read_only, opt_readonly;
@@ -153,6 +157,8 @@ extern char *default_tz_name;
 extern Time_zone *default_tz;
 extern char *default_storage_engine, *default_tmp_storage_engine;
 extern char *enforced_storage_engine;
+extern char *gtid_pos_auto_engines;
+extern plugin_ref *opt_gtid_pos_auto_plugins;
 extern bool opt_endinfo, using_udf_functions;
 extern my_bool locked_in_memory;
 extern bool opt_using_transactions;
diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc
index c385434..fb57bab 100644
--- a/sql/rpl_gtid.cc
+++ b/sql/rpl_gtid.cc
@@ -26,6 +26,7 @@
 #include "key.h"
 #include "rpl_gtid.h"
 #include "rpl_rli.h"
+#include "slave.h"
 
 
 const LEX_STRING rpl_gtid_slave_state_table_name=
@@ -33,7 +34,7 @@ const LEX_STRING rpl_gtid_slave_state_table_name=
 
 
 void
-rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid,
+rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid, void *hton,
                                    rpl_group_info *rgi)
 {
   int err;
@@ -45,7 +46,7 @@ rpl_slave_state::update_state_hash(uint64 sub_id, rpl_gtid *gtid,
     it is even committed.
   */
   mysql_mutex_lock(&LOCK_slave_state);
-  err= update(gtid->domain_id, gtid->server_id, sub_id, gtid->seq_no, rgi);
+  err= update(gtid->domain_id, gtid->server_id, sub_id, gtid->seq_no, hton, rgi);
   mysql_mutex_unlock(&LOCK_slave_state);
   if (err)
   {
@@ -74,12 +75,14 @@ rpl_slave_state::record_and_update_gtid(THD *thd, rpl_group_info *rgi)
   if (rgi->gtid_pending)
   {
     uint64 sub_id= rgi->gtid_sub_id;
+    void *hton= NULL;
+
     rgi->gtid_pending= false;
     if (rgi->gtid_ignore_duplicate_state!=rpl_group_info::GTID_DUPLICATE_IGNORE)
     {
-      if (record_gtid(thd, &rgi->current_gtid, sub_id, false, false))
+      if (record_gtid(thd, &rgi->current_gtid, sub_id, false, false, &hton))
         DBUG_RETURN(1);
-      update_state_hash(sub_id, &rgi->current_gtid, rgi);
+      update_state_hash(sub_id, &rgi->current_gtid, hton, rgi);
     }
     rgi->gtid_ignore_duplicate_state= rpl_group_info::GTID_DUPLICATE_NULL;
   }
@@ -243,7 +246,7 @@ rpl_slave_state_free_element(void *arg)
 
 
 rpl_slave_state::rpl_slave_state()
-  : last_sub_id(0), loaded(false)
+  : last_sub_id(0), gtid_pos_tables(0), loaded(false)
 {
   mysql_mutex_init(key_LOCK_slave_state, &LOCK_slave_state,
                    MY_MUTEX_INIT_SLOW);
@@ -255,6 +258,7 @@ rpl_slave_state::rpl_slave_state()
 
 rpl_slave_state::~rpl_slave_state()
 {
+  free_gtid_pos_tables((struct gtid_pos_table *)gtid_pos_tables);
   truncate_hash();
   my_hash_free(&hash);
   delete_dynamic(&gtid_sort_array);
@@ -286,11 +290,12 @@ rpl_slave_state::truncate_hash()
 
 int
 rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id,
-                        uint64 seq_no, rpl_group_info *rgi)
+                        uint64 seq_no, void *hton, rpl_group_info *rgi)
 {
   element *elem= NULL;
   list_element *list_elem= NULL;
 
+  DBUG_ASSERT(hton || !loaded);
   if (!(elem= get_element(domain_id)))
     return 1;
 
@@ -335,6 +340,7 @@ rpl_slave_state::update(uint32 domain_id, uint32 server_id, uint64 sub_id,
   list_elem->server_id= server_id;
   list_elem->sub_id= sub_id;
   list_elem->seq_no= seq_no;
+  list_elem->hton= hton;
 
   elem->add(list_elem);
   if (last_sub_id < sub_id)
@@ -466,6 +472,94 @@ gtid_check_rpl_slave_state_table(TABLE *table)
 
 
 /*
+  Attempt to find a mysql.gtid_slave_posXXX table that has a storage engine
+  that is already in use by the current transaction, if any.
+*/
+void
+rpl_slave_state::select_gtid_pos_table(THD *thd, LEX_STRING *out_tablename)
+{
+  struct gtid_pos_table *list, *table_entry, *default_entry;
+
+  /*
+    See comments on rpl_slave_state::gtid_pos_tables for rules around proper
+    access to the list.
+  */
+  list= (struct gtid_pos_table *)
+    my_atomic_loadptr_explicit(&gtid_pos_tables, MY_MEMORY_ORDER_ACQUIRE);
+
+  Ha_trx_info *ha_info;
+  uint count = 0;
+  for (ha_info= thd->transaction.all.ha_list; ha_info; ha_info= ha_info->next())
+  {
+    void *trx_hton= ha_info->ht();
+    table_entry= list;
+
+    if (!ha_info->is_trx_read_write() || trx_hton == binlog_hton)
+      continue;
+    while (table_entry)
+    {
+      if (table_entry->table_hton == trx_hton)
+      {
+        if (likely(table_entry->state == GTID_POS_AVAILABLE))
+        {
+          *out_tablename= table_entry->table_name;
+          /*
+            Check if this is a cross-engine transaction, so we can correctly
+            maintain the rpl_transactions_multi_engine status variable.
+          */
+          if (count >= 1)
+            statistic_increment(rpl_transactions_multi_engine, LOCK_status);
+          else
+          {
+            for (;;)
+            {
+              ha_info= ha_info->next();
+              if (!ha_info)
+                break;
+              if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton)
+              {
+                statistic_increment(rpl_transactions_multi_engine, LOCK_status);
+                break;
+              }
+            }
+          }
+          return;
+        }
+        /*
+          This engine is marked to automatically create the table.
+          We cannot easily do this here (possibly in the middle of a
+          transaction). But we can request the slave background thread
+          to create it, and in a short while it should become available
+          for following transactions.
+        */
+#ifdef HAVE_REPLICATION
+        slave_background_gtid_pos_create_request(table_entry);
+#endif
+        break;
+      }
+      table_entry= table_entry->next;
+    }
+    ++count;
+  }
+  /*
+    If we cannot find any table whose engine matches an engine that is
+    already active in the transaction, or if there is no current transaction
+    engines available, we return the default gtid_slave_pos table.
+  */
+  default_entry= (struct gtid_pos_table *)
+    my_atomic_loadptr_explicit(&default_gtid_pos_table, MY_MEMORY_ORDER_ACQUIRE);
+  *out_tablename= default_entry->table_name;
+  /* Record in status that we failed to find a suitable gtid_pos table. */
+  if (count > 0)
+  {
+    statistic_increment(transactions_gtid_foreign_engine, LOCK_status);
+    if (count > 1)
+      statistic_increment(rpl_transactions_multi_engine, LOCK_status);
+  }
+}
+
+
+/*
   Write a gtid to the replication slave state table.
 
   Do it as part of the transaction, to get slave crash safety, or as a separate
@@ -481,19 +575,24 @@ gtid_check_rpl_slave_state_table(TABLE *table)
 */
 int
 rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
-                             bool in_transaction, bool in_statement)
+                             bool in_transaction, bool in_statement,
+                             void **out_hton)
 {
   TABLE_LIST tlist;
   int err= 0;
   bool table_opened= false;
   TABLE *table;
-  list_element *elist= 0, *next;
+  list_element *delete_list= 0, *next, *cur, **next_ptr_ptr, **best_ptr_ptr;
+  uint64_t best_sub_id;
   element *elem;
   ulonglong thd_saved_option= thd->variables.option_bits;
   Query_tables_list lex_backup;
   wait_for_commit* suspended_wfc;
+  void *hton= NULL;
+  LEX_STRING gtid_pos_table_name;
   DBUG_ENTER("record_gtid");
 
+  *out_hton= NULL;
   if (unlikely(!loaded))
   {
     /*
@@ -508,6 +607,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
 
   if (!in_statement)
     thd->reset_for_next_command();
+  select_gtid_pos_table(thd, &gtid_pos_table_name);
 
   DBUG_EXECUTE_IF("gtid_inject_record_gtid",
                   {
@@ -538,14 +638,13 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
   */
   suspended_wfc= thd->suspend_subsequent_commits();
   thd->lex->reset_n_backup_query_tables_list(&lex_backup);
-  tlist.init_one_table(STRING_WITH_LEN("mysql"),
-                       rpl_gtid_slave_state_table_name.str,
-                       rpl_gtid_slave_state_table_name.length,
-                       NULL, TL_WRITE);
+  tlist.init_one_table(STRING_WITH_LEN("mysql"), gtid_pos_table_name.str,
+                       gtid_pos_table_name.length, NULL, TL_WRITE);
   if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0)))
     goto end;
   table_opened= true;
   table= tlist.table;
+  hton= table->s->db_type();
 
   if ((err= gtid_check_rpl_slave_state_table(table)))
     goto end;
@@ -581,6 +680,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
     table->file->print_error(err, MYF(0));
     goto end;
   }
+  *out_hton= hton;
 
   if(opt_bin_log &&
      (err= mysql_bin_log.bump_seq_no_counter_if_needed(gtid->domain_id,
@@ -598,36 +698,62 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
     err= 1;
     goto end;
   }
-  if ((elist= elem->grab_list()) != NULL)
+
+  /* Now pull out all GTIDs that were recorded in this engine. */
+  delete_list = NULL;
+  next_ptr_ptr= &elem->list;
+  cur= elem->list;
+  best_sub_id= 0;
+  best_ptr_ptr= NULL;
+  while (cur)
   {
-    /* Delete any old stuff, but keep around the most recent one. */
-    list_element *cur= elist;
-    uint64 best_sub_id= cur->sub_id;
-    list_element **best_ptr_ptr= &elist;
-    while ((next= cur->next))
+    list_element *next= cur->next;
+    if (cur->hton == hton)
     {
-      if (next->sub_id > best_sub_id)
+      /* Belongs to same engine, so move it to the delete list. */
+      cur->next= delete_list;
+      delete_list= cur;
+      if (cur->sub_id > best_sub_id)
       {
-        best_sub_id= next->sub_id;
+        best_sub_id= cur->sub_id;
+        best_ptr_ptr= &delete_list;
+      }
+      else if (best_ptr_ptr == &delete_list)
         best_ptr_ptr= &cur->next;
+    }
+    else
+    {
+      /* Another engine, leave it in the list. */
+      if (cur->sub_id > best_sub_id)
+      {
+        best_sub_id= cur->sub_id;
+        /* Current best is not on the delete list. */
+        best_ptr_ptr= NULL;
       }
-      cur= next;
+      *next_ptr_ptr= cur;
+      next_ptr_ptr= &cur->next;
     }
-    /*
-      Delete the highest sub_id element from the old list, and put it back as
-      the single-element new list.
-    */
+    cur= next;
+  }
+  *next_ptr_ptr= NULL;
+  /*
+    If the highest sub_id element is on the delete list, put it back on the
+    original list, to preserve the highest sub_id element in the table for
+    GTID position recovery.
+  */
+  if (best_ptr_ptr)
+  {
     cur= *best_ptr_ptr;
     *best_ptr_ptr= cur->next;
-    cur->next= NULL;
+    cur->next= elem->list;
     elem->list= cur;
   }
   mysql_mutex_unlock(&LOCK_slave_state);
 
-  if (!elist)
+  if (!delete_list)
     goto end;
 
-  /* Now delete any already committed rows. */
+  /* Now delete any already committed GTIDs. */
   bitmap_set_bit(table->read_set, table->field[0]->field_index);
   bitmap_set_bit(table->read_set, table->field[1]->field_index);
 
@@ -636,7 +762,7 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
     table->file->print_error(err, MYF(0));
     goto end;
   }
-  while (elist)
+  while (delete_list)
   {
     uchar key_buffer[4+8];
 
@@ -646,9 +772,9 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
                       /* `break' does not work inside DBUG_EXECUTE_IF */
                       goto dbug_break; });
 
-    next= elist->next;
+    next= delete_list->next;
 
-    table->field[1]->store(elist->sub_id, true);
+    table->field[1]->store(delete_list->sub_id, true);
     /* domain_id is already set in table->record[0] from write_row() above. */
     key_copy(key_buffer, table->record[0], &table->key_info[0], 0, false);
     if (table->file->ha_index_read_map(table->record[1], key_buffer,
@@ -662,8 +788,8 @@ rpl_slave_state::record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
       not want to endlessly error on the same element in case of table
       corruption or such.
     */
-    my_free(elist);
-    elist= next;
+    my_free(delete_list);
+    delete_list= next;
     if (err)
       break;
   }
@@ -681,13 +807,13 @@ IF_DBUG(dbug_break:, )
     if (err || (err= ha_commit_trans(thd, FALSE)))
     {
       /*
-        If error, we need to put any remaining elist back into the HASH so we
-        can do another delete attempt later.
+        If error, we need to put any remaining delete_list back into the HASH
+        so we can do another delete attempt later.
       */
-      if (elist)
+      if (delete_list)
       {
         mysql_mutex_lock(&LOCK_slave_state);
-        put_back_list(gtid->domain_id, elist);
+        put_back_list(gtid->domain_id, delete_list);
         mysql_mutex_unlock(&LOCK_slave_state);
       }
 
@@ -1077,11 +1203,12 @@ rpl_slave_state::load(THD *thd, const char *state_from_master, size_t len,
   {
     rpl_gtid gtid;
     uint64 sub_id;
+    void *hton= NULL;
 
     if (gtid_parser_helper(&state_from_master, end, &gtid) ||
         !(sub_id= next_sub_id(gtid.domain_id)) ||
-        record_gtid(thd, &gtid, sub_id, false, in_statement) ||
-        update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no, NULL))
+        record_gtid(thd, &gtid, sub_id, false, in_statement, &hton) ||
+        update(gtid.domain_id, gtid.server_id, sub_id, gtid.seq_no, hton, NULL))
       return 1;
     if (state_from_master == end)
       break;
@@ -1115,6 +1242,75 @@ rpl_slave_state::is_empty()
 }
 
 
+void
+rpl_slave_state::free_gtid_pos_tables(struct rpl_slave_state::gtid_pos_table *list)
+{
+  struct gtid_pos_table *cur, *next;
+
+  cur= list;
+  while (cur)
+  {
+    next= cur->next;
+    my_free(cur);
+    cur= next;
+  }
+}
+
+
+/*
+  Replace the list of available mysql.gtid_slave_posXXX tables with a new list.
+  The caller must be holding LOCK_slave_state. Additionally, this function
+  must only be called while all SQL threads are stopped.
+*/
+void
+rpl_slave_state::set_gtid_pos_tables_list(rpl_slave_state::gtid_pos_table *new_list,
+                                          rpl_slave_state::gtid_pos_table *default_entry)
+{
+  gtid_pos_table *old_list;
+
+  mysql_mutex_assert_owner(&LOCK_slave_state);
+  old_list= (struct gtid_pos_table *)gtid_pos_tables;
+  my_atomic_storeptr_explicit(&gtid_pos_tables, new_list, MY_MEMORY_ORDER_RELEASE);
+  my_atomic_storeptr_explicit(&default_gtid_pos_table, default_entry,
+                              MY_MEMORY_ORDER_RELEASE);
+  free_gtid_pos_tables(old_list);
+}
+
+
+void
+rpl_slave_state::add_gtid_pos_table(rpl_slave_state::gtid_pos_table *entry)
+{
+  mysql_mutex_assert_owner(&LOCK_slave_state);
+  entry->next= (struct gtid_pos_table *)gtid_pos_tables;
+  my_atomic_storeptr_explicit(&gtid_pos_tables, entry, MY_MEMORY_ORDER_RELEASE);
+}
+
+
+struct rpl_slave_state::gtid_pos_table *
+rpl_slave_state::alloc_gtid_pos_table(LEX_STRING *table_name, void *hton,
+                                      rpl_slave_state::gtid_pos_table_state state)
+{
+  struct gtid_pos_table *p;
+  char *allocated_str;
+
+  if (!my_multi_malloc(MYF(MY_WME),
+                       &p, sizeof(*p),
+                       &allocated_str, table_name->length+1,
+                       NULL))
+  {
+    my_error(ER_OUTOFMEMORY, MYF(0), (int)(sizeof(*p) + table_name->length+1));
+    return NULL;
+  }
+  memcpy(allocated_str, table_name->str, table_name->length+1); // Also copy '\0'
+  p->next = NULL;
+  p->table_hton= hton;
+  p->table_name.str= allocated_str;
+  p->table_name.length= table_name->length;
+  p->state= state;
+  return p;
+}
+
+
 void rpl_binlog_state::init()
 {
   my_hash_init(&hash, &my_charset_bin, 32, offsetof(element, domain_id),
diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h
index 5dfac7a..4a7661e 100644
--- a/sql/rpl_gtid.h
+++ b/sql/rpl_gtid.h
@@ -112,6 +112,12 @@ struct rpl_slave_state
     uint64 sub_id;
     uint64 seq_no;
     uint32 server_id;
+    /*
+      hton of mysql.gtid_slave_pos* table used to record this GTID.
+      Can be NULL if the gtid table failed to load (eg. missing
+      mysql.gtid_slave_pos table following an upgrade).
+    */
+    void *hton;
   };
 
   /* Elements in the HASH that hold the state for one domain_id. */
@@ -155,6 +161,26 @@ struct rpl_slave_state
     }
   };
 
+  /* Descriptor for mysql.gtid_slave_posXXX table in specific engine. */
+  enum gtid_pos_table_state {
+    GTID_POS_AUTO_CREATE,
+    GTID_POS_CREATE_REQUESTED,
+    GTID_POS_CREATE_IN_PROGRESS,
+    GTID_POS_AVAILABLE
+  };
+  struct gtid_pos_table {
+    struct gtid_pos_table *next;
+    /*
+      Use a void * here, rather than handlerton *, to make explicit that we
+      are not using the value to access any functionality in the engine. It
+      is just used as an opaque value to identify which engine we are using
+      for each GTID row.
+    */
+    void *table_hton;
+    LEX_STRING table_name;
+    uint8 state;
+  };
+
   /* Mapping from domain_id to its element. */
   HASH hash;
   /* Mutex protecting access to the state. */
@@ -163,6 +189,30 @@ struct rpl_slave_state
   DYNAMIC_ARRAY gtid_sort_array;
 
   uint64 last_sub_id;
+  /*
+    List of tables available for durably storing the slave GTID position.
+
+    Accesses to this table is protected by LOCK_slave_state. However for
+    efficiency, there is also a provision for read access to it from a running
+    slave without lock.
+
+    An element can be added at the head of a list by storing the new
+    gtid_pos_tables pointer atomically with release semantics, to ensure that
+    the next pointer of the new element is visible to readers of the new list.
+    Other changes (like deleting or replacing elements) must happen only while
+    all SQL driver threads are stopped. LOCK_slave_state must be held in any
+    case.
+
+    The list can be read without lock by an SQL driver thread or worker thread
+    by reading the gtid_pos_tables pointer atomically with acquire semantics,
+    to ensure that it will see the correct next pointer of a new head element.
+
+    The type is struct gtid_pos_table *, but needs to be void * to allow using
+    my_atomic operations without violating C strict aliasing semantics.
+  */
+  void * volatile gtid_pos_tables;
+  /* The default entry in gtid_pos_tables, mysql.gtid_slave_pos. */
+  void * volatile default_gtid_pos_table;
   bool loaded;
 
   rpl_slave_state();
@@ -171,10 +221,11 @@ struct rpl_slave_state
   void truncate_hash();
   ulong count() const { return hash.records; }
   int update(uint32 domain_id, uint32 server_id, uint64 sub_id,
-             uint64 seq_no, rpl_group_info *rgi);
+             uint64 seq_no, void *hton, rpl_group_info *rgi);
   int truncate_state_table(THD *thd);
+  void select_gtid_pos_table(THD *thd, LEX_STRING *out_tablename);
   int record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
-                  bool in_transaction, bool in_statement);
+                  bool in_transaction, bool in_statement, void **out_hton);
   uint64 next_sub_id(uint32 domain_id);
   int iterate(int (*cb)(rpl_gtid *, void *), void *data,
               rpl_gtid *extra_gtids, uint32 num_extra,
@@ -188,10 +239,17 @@ struct rpl_slave_state
   element *get_element(uint32 domain_id);
   int put_back_list(uint32 domain_id, list_element *list);
 
-  void update_state_hash(uint64 sub_id, rpl_gtid *gtid, rpl_group_info *rgi);
+  void update_state_hash(uint64 sub_id, rpl_gtid *gtid, void *hton,
+                         rpl_group_info *rgi);
   int record_and_update_gtid(THD *thd, struct rpl_group_info *rgi);
   int check_duplicate_gtid(rpl_gtid *gtid, rpl_group_info *rgi);
   void release_domain_owner(rpl_group_info *rgi);
+  void set_gtid_pos_tables_list(gtid_pos_table *new_list,
+                                gtid_pos_table *default_entry);
+  void add_gtid_pos_table(gtid_pos_table *entry);
+  struct gtid_pos_table *alloc_gtid_pos_table(LEX_STRING *table_name,
+      void *hton, rpl_slave_state::gtid_pos_table_state state);
+  void free_gtid_pos_tables(struct gtid_pos_table *list);
 };
 
 
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index e90557e..43c5ece 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -1557,6 +1557,9 @@ bool give_error_if_slave_running(bool already_locked)
 /**
    any_slave_sql_running()
 
+   @param
+   already_locked  0 if we need to lock, 1 if we have LOCK_active_mi_locked
+
    @return
    0            No Slave SQL thread is running
    #		Number of slave SQL thread running
@@ -1567,26 +1570,28 @@ bool give_error_if_slave_running(bool already_locked)
    hash entries can't be accessed.
 */
 
-uint any_slave_sql_running()
+uint any_slave_sql_running(bool already_locked)
 {
   uint count= 0;
   HASH *hash;
   DBUG_ENTER("any_slave_sql_running");
 
-  mysql_mutex_lock(&LOCK_active_mi);
+  if (!already_locked)
+    mysql_mutex_lock(&LOCK_active_mi);
   if (unlikely(shutdown_in_progress || !master_info_index))
+    count= 1;
+  else
   {
-    mysql_mutex_unlock(&LOCK_active_mi);
-    DBUG_RETURN(1);
-  }
-  hash= &master_info_index->master_info_hash;
-  for (uint i= 0; i< hash->records; ++i)
-  {
-    Master_info *mi= (Master_info *)my_hash_element(hash, i);
-    if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN)
-      count++;
+    hash= &master_info_index->master_info_hash;
+    for (uint i= 0; i< hash->records; ++i)
+    {
+      Master_info *mi= (Master_info *)my_hash_element(hash, i);
+      if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN)
+        count++;
+    }
   }
-  mysql_mutex_unlock(&LOCK_active_mi);
+  if (!already_locked)
+    mysql_mutex_unlock(&LOCK_active_mi);
   DBUG_RETURN(count);
 }
 
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index ccc1be6..58333ad 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -379,7 +379,7 @@ void create_logfile_name_with_suffix(char *res_file_name, size_t length,
 uchar *get_key_master_info(Master_info *mi, size_t *length,
                            my_bool not_used __attribute__((unused)));
 void free_key_master_info(Master_info *mi);
-uint any_slave_sql_running();
+uint any_slave_sql_running(bool already_locked);
 bool give_error_if_slave_running(bool already_lock);
 
 #endif /* HAVE_REPLICATION */
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index aaa72da..0994bce 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -1466,7 +1466,7 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool,
   */
   if (!new_count && !force)
   {
-    if (any_slave_sql_running())
+    if (any_slave_sql_running(false))
     {
       DBUG_PRINT("warning",
                  ("SQL threads running while trying to reset parallel pool"));
@@ -1621,7 +1621,7 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool,
 int rpl_parallel_resize_pool_if_no_slaves(void)
 {
   /* master_info_index is set to NULL on shutdown */
-  if (opt_slave_parallel_threads > 0 && !any_slave_sql_running())
+  if (opt_slave_parallel_threads > 0 && !any_slave_sql_running(false))
     return rpl_parallel_inactivate_pool(&global_rpl_thread_pool);
   return 0;
 }
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index fda922c..63179f5 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -32,6 +32,8 @@
 #include "slave.h"
 #include <mysql/plugin.h>
 #include <mysql/service_thd_wait.h>
+#include "lock.h"
+#include "sql_table.h"
 
 static int count_relay_log_space(Relay_log_info* rli);
 
@@ -1466,41 +1468,22 @@ Relay_log_info::update_relay_log_state(rpl_gtid *gtid_list, uint32 count)
 
 
 #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-int
-rpl_load_gtid_slave_state(THD *thd)
+struct gtid_pos_element { uint64 sub_id; rpl_gtid gtid; void *hton; };
+
+static int
+scan_one_gtid_slave_pos_table(THD *thd, HASH *hash, DYNAMIC_ARRAY *array,
+                              LEX_STRING *tablename, void **out_hton)
 {
   TABLE_LIST tlist;
   TABLE *table;
   bool table_opened= false;
   bool table_scanned= false;
-  bool array_inited= false;
-  struct local_element { uint64 sub_id; rpl_gtid gtid; };
-  struct local_element tmp_entry, *entry;
-  HASH hash;
-  DYNAMIC_ARRAY array;
+  struct gtid_pos_element tmp_entry, *entry;
   int err= 0;
-  uint32 i;
-  DBUG_ENTER("rpl_load_gtid_slave_state");
-
-  mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
-  bool loaded= rpl_global_gtid_slave_state->loaded;
-  mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
-  if (loaded)
-    DBUG_RETURN(0);
-
-  my_hash_init(&hash, &my_charset_bin, 32,
-               offsetof(local_element, gtid) + offsetof(rpl_gtid, domain_id),
-               sizeof(uint32), NULL, my_free, HASH_UNIQUE);
-  if ((err= my_init_dynamic_array(&array, sizeof(local_element), 0, 0, MYF(0))))
-    goto end;
-  array_inited= true;
 
   thd->reset_for_next_command();
-
-  tlist.init_one_table(STRING_WITH_LEN("mysql"),
-                       rpl_gtid_slave_state_table_name.str,
-                       rpl_gtid_slave_state_table_name.length,
-                       NULL, TL_READ);
+  tlist.init_one_table(STRING_WITH_LEN("mysql"), tablename->str,
+                       tablename->length, NULL, TL_READ);
   if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0)))
     goto end;
   table_opened= true;
@@ -1546,26 +1529,28 @@ rpl_load_gtid_slave_state(THD *thd)
     tmp_entry.gtid.domain_id= domain_id;
     tmp_entry.gtid.server_id= server_id;
     tmp_entry.gtid.seq_no= seq_no;
-    if ((err= insert_dynamic(&array, (uchar *)&tmp_entry)))
+    tmp_entry.hton= table->s->db_type();
+    if ((err= insert_dynamic(array, (uchar *)&tmp_entry)))
     {
       my_error(ER_OUT_OF_RESOURCES, MYF(0));
       goto end;
     }
 
-    if ((rec= my_hash_search(&hash, (const uchar *)&domain_id, 0)))
+    if ((rec= my_hash_search(hash, (const uchar *)&domain_id, 0)))
     {
-      entry= (struct local_element *)rec;
+      entry= (struct gtid_pos_element *)rec;
       if (entry->sub_id >= sub_id)
         continue;
       entry->sub_id= sub_id;
       DBUG_ASSERT(entry->gtid.domain_id == domain_id);
       entry->gtid.server_id= server_id;
       entry->gtid.seq_no= seq_no;
+      entry->hton= table->s->db_type();
     }
     else
     {
-      if (!(entry= (struct local_element *)my_malloc(sizeof(*entry),
-                                                     MYF(MY_WME))))
+      if (!(entry= (struct gtid_pos_element *)my_malloc(sizeof(*entry),
+                                                        MYF(MY_WME))))
       {
         my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*entry));
         err= 1;
@@ -1575,7 +1560,8 @@ rpl_load_gtid_slave_state(THD *thd)
       entry->gtid.domain_id= domain_id;
       entry->gtid.server_id= server_id;
       entry->gtid.seq_no= seq_no;
-      if ((err= my_hash_insert(&hash, (uchar *)entry)))
+      entry->hton= table->s->db_type();
+      if ((err= my_hash_insert(hash, (uchar *)entry)))
       {
         my_free(entry);
         my_error(ER_OUT_OF_RESOURCES, MYF(0));
@@ -1583,6 +1569,249 @@ rpl_load_gtid_slave_state(THD *thd)
       }
     }
   }
+  err= 0;                                       /* Clear HA_ERR_END_OF_FILE */
+
+end:
+  if (table_scanned)
+  {
+    table->file->ha_index_or_rnd_end();
+    ha_commit_trans(thd, FALSE);
+    ha_commit_trans(thd, TRUE);
+  }
+  if (table_opened)
+  {
+    *out_hton= table->s->db_type();
+    close_thread_tables(thd);
+    thd->mdl_context.release_transactional_locks();
+  }
+  return err;
+}
+
+
+/*
+  Look for all tables mysql.gtid_slave_pos*. Read all rows from each such
+  table found into ARRAY. For each domain id, put the row with highest sub_id
+  into HASH.
+*/
+static int
+scan_all_gtid_slave_pos_table(THD *thd, int (*cb)(THD *, LEX_STRING *, void *),
+                              void *cb_data)
+{
+  static LEX_STRING mysql_db_name= {C_STRING_WITH_LEN("mysql")};
+  char path[FN_REFLEN];
+  MY_DIR *dirp;
+
+  thd->reset_for_next_command();
+  if (lock_schema_name(thd, mysql_db_name.str))
+    return 1;
+
+  build_table_filename(path, sizeof(path) - 1, mysql_db_name.str, "", "", 0);
+  if (!(dirp= my_dir(path, MYF(MY_DONT_SORT))))
+  {
+    my_error(ER_FILE_NOT_FOUND, MYF(0), path, my_errno);
+    close_thread_tables(thd);
+    thd->mdl_context.release_transactional_locks();
+    return 1;
+  }
+  else
+  {
+    size_t i;
+    Dynamic_array<LEX_STRING*> files(dirp->number_of_files);
+    Discovered_table_list tl(thd, &files);
+    int err;
+
+    err= ha_discover_table_names(thd, &mysql_db_name, dirp, &tl, false);
+    my_dirend(dirp);
+    close_thread_tables(thd);
+    thd->mdl_context.release_transactional_locks();
+    if (err)
+      return err;
+
+    for (i = 0; i < files.elements(); ++i)
+    {
+      if (strncmp(files.at(i)->str,
+                  rpl_gtid_slave_state_table_name.str,
+                  rpl_gtid_slave_state_table_name.length) == 0)
+      {
+        if ((err= (*cb)(thd, files.at(i), cb_data)))
+          return err;
+      }
+    }
+  }
+
+  return 0;
+}
+
+
+struct load_gtid_state_cb_data {
+  HASH *hash;
+  DYNAMIC_ARRAY *array;
+  struct rpl_slave_state::gtid_pos_table *table_list;
+  struct rpl_slave_state::gtid_pos_table *default_entry;
+};
+
+static int
+process_gtid_pos_table(THD *thd, LEX_STRING *table_name, void *hton,
+                       struct load_gtid_state_cb_data *data)
+{
+  struct rpl_slave_state::gtid_pos_table *p, *entry, **next_ptr;
+  bool is_default=
+    (strcmp(table_name->str, rpl_gtid_slave_state_table_name.str) == 0);
+
+  /*
+    Ignore tables with duplicate storage engine, with a warning.
+    Prefer the default mysql.gtid_slave_pos over another table
+    mysql.gtid_slave_posXXX with the same storage engine.
+  */
+  next_ptr= &data->table_list;
+  entry= data->table_list;
+  while (entry)
+  {
+    if (entry->table_hton == hton)
+    {
+      static const char *warning_msg= "Ignoring redundant table mysql.%s "
+        "since mysql.%s has the same storage engine";
+      if (!is_default)
+      {
+        /* Ignore the redundant table. */
+        sql_print_warning(warning_msg, table_name->str, entry->table_name);
+        return 0;
+      }
+      else
+      {
+        sql_print_warning(warning_msg, entry->table_name, table_name->str);
+        /* Delete the redundant table, and proceed to add this one instead. */
+        *next_ptr= entry->next;
+        my_free(entry);
+        break;
+      }
+    }
+    next_ptr= &entry->next;
+    entry= entry->next;
+  }
+
+  p= rpl_global_gtid_slave_state->alloc_gtid_pos_table(table_name,
+      hton, rpl_slave_state::GTID_POS_AVAILABLE);
+  if (!p)
+    return 1;
+  p->next= data->table_list;
+  data->table_list= p;
+  if (is_default)
+    data->default_entry= p;
+  return 0;
+}
+
+
+/*
+  Put tables corresponding to @@gtid_pos_auto_engines at the end of the list,
+  marked to be auto-created if needed.
+*/
+static int
+gtid_pos_auto_create_tables(rpl_slave_state::gtid_pos_table **list_ptr)
+{
+  plugin_ref *auto_engines;
+  int err= 0;
+  mysql_mutex_lock(&LOCK_global_system_variables);
+  for (auto_engines= opt_gtid_pos_auto_plugins;
+       !err && auto_engines && *auto_engines;
+       ++auto_engines)
+  {
+    void *hton= plugin_hton(*auto_engines);
+    char buf[FN_REFLEN+1];
+    LEX_STRING table_name;
+    char *p;
+    rpl_slave_state::gtid_pos_table *entry, **next_ptr;
+
+    /* See if this engine is already in the list. */
+    next_ptr= list_ptr;
+    entry= *list_ptr;
+    while (entry)
+    {
+      if (entry->table_hton == hton)
+        break;
+      next_ptr= &entry->next;
+      entry= entry->next;
+    }
+    if (entry)
+      continue;
+
+    /* Add an auto-create entry for this engine at end of list. */
+    p= strmake(buf, rpl_gtid_slave_state_table_name.str, FN_REFLEN);
+    p= strmake(p, "_", FN_REFLEN - (p - buf));
+    p= strmake(p, plugin_name(*auto_engines)->str, FN_REFLEN - (p - buf));
+    table_name.str= buf;
+    table_name.length= p - buf;
+    entry= rpl_global_gtid_slave_state->alloc_gtid_pos_table
+      (&table_name, hton, rpl_slave_state::GTID_POS_AUTO_CREATE);
+    if (!entry)
+    {
+      err= 1;
+      break;
+    }
+    *next_ptr= entry;
+  }
+  mysql_mutex_unlock(&LOCK_global_system_variables);
+  return err;
+}
+
+
+static int
+load_gtid_state_cb(THD *thd, LEX_STRING *table_name, void *arg)
+{
+  int err;
+  load_gtid_state_cb_data *data= static_cast<load_gtid_state_cb_data *>(arg);
+  void *hton;
+
+  if ((err= scan_one_gtid_slave_pos_table(thd, data->hash, data->array,
+                                          table_name, &hton)))
+    return err;
+  return process_gtid_pos_table(thd, table_name, hton, data);
+}
+
+
+int
+rpl_load_gtid_slave_state(THD *thd)
+{
+  bool array_inited= false;
+  struct gtid_pos_element tmp_entry, *entry;
+  HASH hash;
+  DYNAMIC_ARRAY array;
+  int err= 0;
+  uint32 i;
+  load_gtid_state_cb_data cb_data;
+  DBUG_ENTER("rpl_load_gtid_slave_state");
+
+  mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+  bool loaded= rpl_global_gtid_slave_state->loaded;
+  mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+  if (loaded)
+    DBUG_RETURN(0);
+
+  cb_data.table_list= NULL;
+  cb_data.default_entry= NULL;
+  my_hash_init(&hash, &my_charset_bin, 32,
+               offsetof(gtid_pos_element, gtid) + offsetof(rpl_gtid, domain_id),
+               sizeof(uint32), NULL, my_free, HASH_UNIQUE);
+  if ((err= my_init_dynamic_array(&array, sizeof(gtid_pos_element), 0, 0, MYF(0))))
+    goto end;
+  array_inited= true;
+
+  cb_data.hash = &hash;
+  cb_data.array = &array;
+  if ((err= scan_all_gtid_slave_pos_table(thd, load_gtid_state_cb, &cb_data)))
+    goto end;
+
+  if (!cb_data.default_entry)
+  {
+    /*
+      If the mysql.gtid_slave_pos table does not exist, but at least one other
+      table is available, arbitrarily pick the first in the list to use as
+      default.
+    */
+    cb_data.default_entry= cb_data.table_list;
+  }
+  if ((err= gtid_pos_auto_create_tables(&cb_data.table_list)))
+    goto end;
 
   mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
   if (rpl_global_gtid_slave_state->loaded)
@@ -1591,14 +1820,24 @@ rpl_load_gtid_slave_state(THD *thd)
     goto end;
   }
 
+  if (!cb_data.table_list)
+  {
+    my_error(ER_NO_SUCH_TABLE, MYF(0), "mysql",
+             rpl_gtid_slave_state_table_name.str);
+    mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+    err= 1;
+    goto end;
+  }
+
   for (i= 0; i < array.elements; ++i)
   {
     get_dynamic(&array, (uchar *)&tmp_entry, i);
     if ((err= rpl_global_gtid_slave_state->update(tmp_entry.gtid.domain_id,
-                                                 tmp_entry.gtid.server_id,
-                                                 tmp_entry.sub_id,
-                                                 tmp_entry.gtid.seq_no,
-                                                 NULL)))
+                                                  tmp_entry.gtid.server_id,
+                                                  tmp_entry.sub_id,
+                                                  tmp_entry.gtid.seq_no,
+                                                  tmp_entry.hton,
+                                                  NULL)))
     {
       mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
       my_error(ER_OUT_OF_RESOURCES, MYF(0));
@@ -1608,7 +1847,7 @@ rpl_load_gtid_slave_state(THD *thd)
 
   for (i= 0; i < hash.records; ++i)
   {
-    entry= (struct local_element *)my_hash_element(&hash, i);
+    entry= (struct gtid_pos_element *)my_hash_element(&hash, i);
     if (opt_bin_log &&
         mysql_bin_log.bump_seq_no_counter_if_needed(entry->gtid.domain_id,
                                                     entry->gtid.seq_no))
@@ -1619,27 +1858,175 @@ rpl_load_gtid_slave_state(THD *thd)
     }
   }
 
+  rpl_global_gtid_slave_state->set_gtid_pos_tables_list(cb_data.table_list,
+                                                        cb_data.default_entry);
+  cb_data.table_list= NULL;
   rpl_global_gtid_slave_state->loaded= true;
   mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
 
-  err= 0;                                       /* Clear HA_ERR_END_OF_FILE */
+end:
+  if (array_inited)
+    delete_dynamic(&array);
+  my_hash_free(&hash);
+  if (cb_data.table_list)
+    rpl_global_gtid_slave_state->free_gtid_pos_tables(cb_data.table_list);
+  DBUG_RETURN(err);
+}
+
+
+static int
+find_gtid_pos_tables_cb(THD *thd, LEX_STRING *table_name, void *arg)
+{
+  load_gtid_state_cb_data *data= static_cast<load_gtid_state_cb_data *>(arg);
+  TABLE_LIST tlist;
+  TABLE *table= NULL;
+  int err;
+
+  thd->reset_for_next_command();
+  tlist.init_one_table(STRING_WITH_LEN("mysql"), table_name->str,
+                       table_name->length, NULL, TL_READ);
+  if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0)))
+    goto end;
+  table= tlist.table;
+
+  if ((err= gtid_check_rpl_slave_state_table(table)))
+    goto end;
+  err= process_gtid_pos_table(thd, table_name, table->s->db_type(), data);
 
 end:
-  if (table_scanned)
+  if (table)
   {
-    table->file->ha_index_or_rnd_end();
     ha_commit_trans(thd, FALSE);
     ha_commit_trans(thd, TRUE);
-  }
-  if (table_opened)
-  {
     close_thread_tables(thd);
     thd->mdl_context.release_transactional_locks();
   }
-  if (array_inited)
-    delete_dynamic(&array);
-  my_hash_free(&hash);
-  DBUG_RETURN(err);
+
+  return err;
+}
+
+
+/*
+  Re-compute the list of available mysql.gtid_slave_posXXX tables.
+
+  This is done at START SLAVE to pick up any newly created tables without
+  requiring server restart.
+*/
+int
+find_gtid_slave_pos_tables(THD *thd)
+{
+  int err= 0;
+  load_gtid_state_cb_data cb_data;
+  uint num_running;
+
+  mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+  bool loaded= rpl_global_gtid_slave_state->loaded;
+  mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+  if (!loaded)
+    return 0;
+
+  cb_data.table_list= NULL;
+  cb_data.default_entry= NULL;
+  if ((err= scan_all_gtid_slave_pos_table(thd, find_gtid_pos_tables_cb, &cb_data)))
+    goto end;
+
+  if (!cb_data.table_list)
+  {
+    my_error(ER_NO_SUCH_TABLE, MYF(0), "mysql",
+             rpl_gtid_slave_state_table_name.str);
+    err= 1;
+    goto end;
+  }
+  if (!cb_data.default_entry)
+  {
+    /*
+      If the mysql.gtid_slave_pos table does not exist, but at least one other
+      table is available, arbitrarily pick the first in the list to use as
+      default.
+    */
+    cb_data.default_entry= cb_data.table_list;
+  }
+  if ((err= gtid_pos_auto_create_tables(&cb_data.table_list)))
+    goto end;
+
+  mysql_mutex_lock(&LOCK_active_mi);
+  num_running= any_slave_sql_running(true);
+  mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+  if (num_running <= 1)
+  {
+    /*
+      If no slave is running now, the count will be 1, since this SQL thread
+      which is starting is included in the count. In this case, we can safely
+      replace the list, no-one can be trying to read it without lock.
+    */
+    DBUG_ASSERT(num_running == 1);
+    rpl_global_gtid_slave_state->set_gtid_pos_tables_list(cb_data.table_list,
+                                                          cb_data.default_entry);
+    cb_data.table_list= NULL;
+  }
+  else
+  {
+    /*
+      If there are SQL threads running, we cannot safely remove the old list.
+      However we can add new entries, and warn about any tables that
+      disappeared, but may still be visible to running SQL threads.
+    */
+    rpl_slave_state::gtid_pos_table *old_entry, *new_entry, **next_ptr_ptr;
+
+    old_entry= (rpl_slave_state::gtid_pos_table *)
+      rpl_global_gtid_slave_state->gtid_pos_tables;
+    while (old_entry)
+    {
+      new_entry= cb_data.table_list;
+      while (new_entry)
+      {
+        if (new_entry->table_hton == old_entry->table_hton)
+          break;
+        new_entry= new_entry->next;
+      }
+      if (!new_entry)
+        sql_print_warning("The table mysql.%s was removed. "
+                          "This change will not take full effect "
+                          "until all SQL threads have been restarted",
+                          old_entry->table_name.str);
+      old_entry= old_entry->next;
+    }
+    next_ptr_ptr= &cb_data.table_list;
+    new_entry= cb_data.table_list;
+    while (new_entry)
+    {
+      /* Check if we already have a table with this storage engine. */
+      old_entry= (rpl_slave_state::gtid_pos_table *)
+        rpl_global_gtid_slave_state->gtid_pos_tables;
+      while (old_entry)
+      {
+        if (new_entry->table_hton == old_entry->table_hton)
+          break;
+        old_entry= old_entry->next;
+      }
+      if (old_entry)
+      {
+        /* This new_entry is already available in the list. */
+        next_ptr_ptr= &new_entry->next;
+        new_entry= new_entry->next;
+      }
+      else
+      {
+        /* Move this new_entry to the list. */
+        rpl_slave_state::gtid_pos_table *next= new_entry->next;
+        rpl_global_gtid_slave_state->add_gtid_pos_table(new_entry);
+        *next_ptr_ptr= next;
+        new_entry= next;
+      }
+    }
+  }
+  mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+  mysql_mutex_unlock(&LOCK_active_mi);
+
+end:
+  if (cb_data.table_list)
+    rpl_global_gtid_slave_state->free_gtid_pos_tables(cb_data.table_list);
+  return err;
 }
 
 
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index 448fc23..e293d681 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -959,6 +959,7 @@ extern struct rpl_slave_state *rpl_global_gtid_slave_state;
 extern gtid_waiting rpl_global_gtid_waiting;
 
 int rpl_load_gtid_slave_state(THD *thd);
+int find_gtid_slave_pos_tables(THD *thd);
 int event_group_new_gtid(rpl_group_info *rgi, Gtid_log_event *gev);
 void delete_or_keep_event_post_apply(rpl_group_info *rgi,
                                      Log_event_type typ, Log_event *ev);
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 15f6bbd..5558779 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -1293,3 +1293,222 @@ enum sys_var::where get_sys_var_value_origin(void *ptr)
   return sys_var::CONFIG;
 }
 
+
+/*
+  Find the next item in string of comma-separated items.
+  END_POS points at the end of the string.
+  ITEM_START and ITEM_END return the limits of the next item.
+  Returns true while items are available, false at the end.
+*/
+static bool
+engine_list_next_item(const char **pos, const char *end_pos,
+                      const char **item_start, const char **item_end)
+{
+  if (*pos >= end_pos)
+    return false;
+  *item_start= *pos;
+  while (*pos < end_pos && **pos != ',')
+    ++*pos;
+  *item_end= *pos;
+  ++*pos;
+  return true;
+}
+
+
+static bool
+resolve_engine_list_item(THD *thd, plugin_ref *list, uint32 *idx,
+                         const char *pos, const char *pos_end,
+                         bool error_on_unknown_engine, bool temp_copy)
+{
+  LEX_STRING item_str;
+  plugin_ref ref;
+  uint32_t i;
+  THD *thd_or_null = (temp_copy ? thd : NULL);
+
+  item_str.str= const_cast<char*>(pos);
+  item_str.length= pos_end-pos;
+  ref= ha_resolve_by_name(thd_or_null, &item_str, false);
+  if (!ref)
+  {
+    if (error_on_unknown_engine)
+    {
+      ErrConvString err(pos, pos_end-pos, system_charset_info);
+      my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), err.ptr());
+      return true;
+    }
+    return false;
+  }
+  /* Ignore duplicates, like --plugin-load does. */
+  for (i= 0; i < *idx; ++i)
+  {
+    if (plugin_hton(list[i]) == plugin_hton(ref))
+    {
+      if (!temp_copy)
+        plugin_unlock(NULL, ref);
+      return false;
+    }
+  }
+  list[*idx]= ref;
+  ++*idx;
+  return false;
+}
+
+
+/*
+  Helper for class Sys_var_pluginlist.
+  Resolve a comma-separated list of storage engine names to a null-terminated
+  array of plugin_ref.
+
+  If TEMP_COPY is true, a THD must be given as well. In this case, the
+  allocated memory and locked plugins are registered in the THD and will
+  be freed / unlocked automatically. If TEMP_COPY is true, THD can be
+  passed as NULL, and resources must be freed explicitly later with
+  free_engine_list().
+*/
+plugin_ref *
+resolve_engine_list(THD *thd, const char *str_arg, size_t str_arg_len,
+                    bool error_on_unknown_engine, bool temp_copy)
+{
+  uint32 count, idx;
+  const char *pos, *item_start, *item_end;
+  const char *str_arg_end= str_arg + str_arg_len;
+  plugin_ref *res;
+
+  count= 0;
+  pos= str_arg;
+  for (;;)
+  {
+    if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end))
+      break;
+    ++count;
+  }
+
+  if (temp_copy)
+    res= (plugin_ref *)thd->calloc((count+1)*sizeof(*res));
+  else
+    res= (plugin_ref *)my_malloc((count+1)*sizeof(*res), MYF(MY_ZEROFILL|MY_WME));
+  if (!res)
+  {
+    my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*res)));
+    goto err;
+  }
+
+  idx= 0;
+  pos= str_arg;
+  for (;;)
+  {
+    if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end))
+      break;
+    DBUG_ASSERT(idx < count);
+    if (idx >= count)
+      break;
+    if (resolve_engine_list_item(thd, res, &idx, item_start, item_end,
+                                 error_on_unknown_engine, temp_copy))
+      goto err;
+  }
+
+  return res;
+
+err:
+  if (!temp_copy)
+    free_engine_list(res);
+  return NULL;
+}
+
+
+void
+free_engine_list(plugin_ref *list)
+{
+  plugin_ref *p;
+
+  if (!list)
+    return;
+  for (p= list; *p; ++p)
+    plugin_unlock(NULL, *p);
+  my_free(list);
+}
+
+
+plugin_ref *
+copy_engine_list(plugin_ref *list)
+{
+  plugin_ref *p;
+  uint32 count, i;
+
+  for (p= list, count= 0; *p; ++p, ++count)
+    ;
+  p= (plugin_ref *)my_malloc((count+1)*sizeof(*p), MYF(0));
+  if (!p)
+  {
+    my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p)));
+    return NULL;
+  }
+  for (i= 0; i < count; ++i)
+    p[i]= my_plugin_lock(NULL, list[i]);
+  p[i] = NULL;
+  return p;
+}
+
+
+/*
+  Create a temporary copy of an engine list. The memory will be freed
+  (and the plugins unlocked) automatically, on the passed THD.
+*/
+plugin_ref *
+temp_copy_engine_list(THD *thd, plugin_ref *list)
+{
+  plugin_ref *p;
+  uint32 count, i;
+
+  for (p= list, count= 0; *p; ++p, ++count)
+    ;
+  p= (plugin_ref *)thd->alloc((count+1)*sizeof(*p));
+  if (!p)
+  {
+    my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p)));
+    return NULL;
+  }
+  for (i= 0; i < count; ++i)
+    p[i]= my_plugin_lock(thd, list[i]);
+  p[i] = NULL;
+  return p;
+}
+
+
+char *
+pretty_print_engine_list(THD *thd, plugin_ref *list)
+{
+  plugin_ref *p;
+  size_t size;
+  char *buf, *pos;
+
+  if (!list)
+    return thd->strmake("", 0);
+
+  size= 0;
+  for (p= list; *p; ++p)
+    size+= plugin_name(*p)->length + 1;
+  buf= static_cast<char *>(thd->alloc(size));
+  if (!buf)
+    return NULL;
+  pos= buf;
+  for (p= list; *p; ++p)
+  {
+    LEX_STRING *name;
+    size_t remain;
+
+    remain= buf + size - pos;
+    DBUG_ASSERT(remain > 0);
+    if (remain <= 1)
+      break;
+    if (pos != buf)
+    {
+      pos= strmake(pos, ",", remain-1);
+      --remain;
+    }
+    name= plugin_name(*p);
+    pos= strmake(pos, name->str, MY_MIN(name->length, remain-1));
+  }
+  *pos= '\0';
+  return buf;
+}
diff --git a/sql/set_var.h b/sql/set_var.h
index 17d1ff9..8d39854 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -286,6 +286,7 @@ class set_var :public set_var_base
     longlong longlong_value;            ///< for signed integer
     double double_value;                ///< for Sys_var_double
     plugin_ref plugin;                  ///< for Sys_var_plugin
+    plugin_ref *plugins;                ///< for Sys_var_pluginlist
     Time_zone *time_zone;               ///< for Sys_var_tz
     LEX_STRING string_value;            ///< for Sys_var_charptr and others
     const void *ptr;                    ///< for Sys_var_struct
@@ -424,6 +425,12 @@ int sys_var_init();
 uint sys_var_elements();
 int sys_var_add_options(DYNAMIC_ARRAY *long_options, int parse_flags);
 void sys_var_end(void);
+plugin_ref *resolve_engine_list(THD *thd, const char *str_arg, size_t str_arg_len,
+                                bool error_on_unknown_engine, bool temp_copy);
+void free_engine_list(plugin_ref *list);
+plugin_ref *copy_engine_list(plugin_ref *list);
+plugin_ref *temp_copy_engine_list(THD *thd, plugin_ref *list);
+char *pretty_print_engine_list(THD *thd, plugin_ref *list);
 
 #endif
 
diff --git a/sql/slave.cc b/sql/slave.cc
index a7f0f00..67785be 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -60,6 +60,7 @@
 #include "rpl_tblmap.h"
 #include "debug_sync.h"
 #include "rpl_parallel.h"
+#include "sql_show.h"
 
 #define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
 
@@ -279,15 +280,180 @@ static void init_slave_psi_keys(void)
 #endif /* HAVE_PSI_INTERFACE */
 
 
+/*
+  Note: This definition needs to be kept in sync with the one in
+  mysql_system_tables.sql which is used by mysql_create_db.
+*/
+static const char gtid_pos_table_definition1[]=
+  "CREATE TABLE ";
+static const char gtid_pos_table_definition2[]=
+  " (domain_id INT UNSIGNED NOT NULL, "
+  "sub_id BIGINT UNSIGNED NOT NULL, "
+  "server_id INT UNSIGNED NOT NULL, "
+  "seq_no BIGINT UNSIGNED NOT NULL, "
+  "PRIMARY KEY (domain_id, sub_id)) CHARSET=latin1 "
+  "COMMENT='Replication slave GTID position' "
+  "ENGINE=";
+
+/*
+  Build a query string
+    CREATE TABLE mysql.gtid_slave_pos_<engine> ... ENGINE=<engine>
+*/
+static bool
+build_gtid_pos_create_query(THD *thd, String *query,
+                            LEX_STRING *table_name,
+                            LEX_STRING *engine_name)
+{
+  bool err= false;
+  err|= query->append(gtid_pos_table_definition1);
+  err|= append_identifier(thd, query, table_name->str, table_name->length);
+  err|= query->append(gtid_pos_table_definition2);
+  err|= append_identifier(thd, query, engine_name->str, engine_name->length);
+  return err;
+}
+
+
+static int
+gtid_pos_table_creation(THD *thd, plugin_ref engine, LEX_STRING *table_name)
+{
+  int err;
+  StringBuffer<sizeof(gtid_pos_table_definition1) +
+               sizeof(gtid_pos_table_definition1) +
+               2*FN_REFLEN> query;
+
+  if (build_gtid_pos_create_query(thd, &query, table_name, plugin_name(engine)))
+  {
+    my_error(ER_OUT_OF_RESOURCES, MYF(0));
+    return 1;
+  }
+
+  thd->set_db("mysql", 5);
+  thd->clear_error();
+  ulonglong thd_saved_option= thd->variables.option_bits;
+  /* This query shuold not be binlogged. */
+  thd->variables.option_bits&= ~(ulonglong)OPTION_BIN_LOG;
+  thd->set_query_and_id(query.c_ptr(), query.length(), thd->charset(),
+                        next_query_id());
+  Parser_state parser_state;
+  err= parser_state.init(thd, thd->query(), thd->query_length());
+  if (err)
+    goto end;
+  mysql_parse(thd, thd->query(), thd->query_length(), &parser_state,
+              FALSE, FALSE);
+  if (thd->is_error())
+    err= 1;
+end:
+  thd->variables.option_bits= thd_saved_option;
+  thd->reset_query();
+  return err;
+}
+
+
+static void
+handle_gtid_pos_auto_create_request(THD *thd, void *hton)
+{
+  int err;
+  plugin_ref engine= NULL, *auto_engines;
+  rpl_slave_state::gtid_pos_table *entry;
+  StringBuffer<FN_REFLEN> loc_table_name;
+  LEX_STRING table_name;
+
+  /*
+    Check that the plugin is still in @@gtid_pos_auto_engines, and lock
+    it.
+  */
+  mysql_mutex_lock(&LOCK_global_system_variables);
+  engine= NULL;
+  for (auto_engines= opt_gtid_pos_auto_plugins;
+       auto_engines && *auto_engines;
+       ++auto_engines)
+  {
+    if (plugin_hton(*auto_engines) == hton)
+    {
+      engine= my_plugin_lock(NULL, *auto_engines);
+      break;
+    }
+  }
+  mysql_mutex_unlock(&LOCK_global_system_variables);
+  if (!engine)
+  {
+    /* The engine is gone from @@gtid_pos_auto_engines, so no action. */
+    goto end;
+  }
+
+  /* Find the entry for the table to auto-create. */
+  mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+  entry= (rpl_slave_state::gtid_pos_table *)
+    rpl_global_gtid_slave_state->gtid_pos_tables;
+  while (entry)
+  {
+    if (entry->table_hton == hton &&
+        entry->state == rpl_slave_state::GTID_POS_CREATE_REQUESTED)
+      break;
+    entry= entry->next;
+  }
+  if (entry)
+  {
+    entry->state = rpl_slave_state::GTID_POS_CREATE_IN_PROGRESS;
+    err= loc_table_name.append(entry->table_name.str, entry->table_name.length);
+  }
+  mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+  if (!entry)
+    goto end;
+  if (err)
+  {
+    sql_print_error("Out of memory while trying to auto-create GTID position table");
+    goto end;
+  }
+  table_name.str= loc_table_name.c_ptr_safe();
+  table_name.length= loc_table_name.length();
+
+  err= gtid_pos_table_creation(thd, engine, &table_name);
+  if (err)
+  {
+    sql_print_error("Error auto-creating GTID position table `mysql.%s`: %s Error_code: %d",
+                    table_name.str, thd->get_stmt_da()->message(),
+                    thd->get_stmt_da()->sql_errno());
+    thd->clear_error();
+    goto end;
+  }
+
+  /* Now enable the entry for the auto-created table. */
+  mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+  entry= (rpl_slave_state::gtid_pos_table *)
+    rpl_global_gtid_slave_state->gtid_pos_tables;
+  while (entry)
+  {
+    if (entry->table_hton == hton &&
+        entry->state == rpl_slave_state::GTID_POS_CREATE_IN_PROGRESS)
+    {
+      entry->state= rpl_slave_state::GTID_POS_AVAILABLE;
+      break;
+    }
+    entry= entry->next;
+  }
+  mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+
+end:
+  if (engine)
+    plugin_unlock(NULL, engine);
+}
+
+
 static bool slave_background_thread_running;
 static bool slave_background_thread_stop;
 static bool slave_background_thread_gtid_loaded;
 
-struct slave_background_kill_t {
+static struct slave_background_kill_t {
   slave_background_kill_t *next;
   THD *to_kill;
 } *slave_background_kill_list;
 
+static struct slave_background_gtid_pos_create_t {
+  slave_background_gtid_pos_create_t *next;
+  void *hton;
+} *slave_background_gtid_pos_create_list;
+
 
 pthread_handler_t
 handle_slave_background(void *arg __attribute__((unused)))
@@ -321,6 +487,7 @@ handle_slave_background(void *arg __attribute__((unused)))
   do
   {
     slave_background_kill_t *kill_list;
+    slave_background_gtid_pos_create_t *create_list;
 
     thd->ENTER_COND(&COND_slave_background, &LOCK_slave_background,
                     &stage_slave_background_wait_request,
@@ -329,12 +496,14 @@ handle_slave_background(void *arg __attribute__((unused)))
     {
       stop= abort_loop || thd->killed || slave_background_thread_stop;
       kill_list= slave_background_kill_list;
-      if (stop || kill_list)
+      create_list= slave_background_gtid_pos_create_list;
+      if (stop || kill_list || create_list)
         break;
       mysql_cond_wait(&COND_slave_background, &LOCK_slave_background);
     }
 
     slave_background_kill_list= NULL;
+    slave_background_gtid_pos_create_list= NULL;
     thd->EXIT_COND(&old_stage);
 
     while (kill_list)
@@ -353,6 +522,16 @@ handle_slave_background(void *arg __attribute__((unused)))
       mysql_mutex_unlock(&to_kill->LOCK_wakeup_ready);
       my_free(p);
     }
+
+    while (create_list)
+    {
+      slave_background_gtid_pos_create_t *next= create_list->next;
+      void *hton= create_list->hton;
+      handle_gtid_pos_auto_create_request(thd, hton);
+      my_free(create_list);
+      create_list= next;
+    }
+
     mysql_mutex_lock(&LOCK_slave_background);
   } while (!stop);
 
@@ -392,6 +571,41 @@ slave_background_kill_request(THD *to_kill)
 
 
 /*
+  This function must only be called from a slave SQL thread (or worker thread),
+  to ensure that the table_entry will not go away before we can lock the
+  LOCK_slave_state.
+*/
+void
+slave_background_gtid_pos_create_request(
+        rpl_slave_state::gtid_pos_table *table_entry)
+{
+  slave_background_gtid_pos_create_t *p;
+
+  if (table_entry->state != rpl_slave_state::GTID_POS_AUTO_CREATE)
+    return;
+  p= (slave_background_gtid_pos_create_t *)my_malloc(sizeof(*p), MYF(MY_WME));
+  if (!p)
+    return;
+  mysql_mutex_lock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+  if (table_entry->state != rpl_slave_state::GTID_POS_AUTO_CREATE)
+  {
+    my_free(p);
+    mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+    return;
+  }
+  table_entry->state= rpl_slave_state::GTID_POS_CREATE_REQUESTED;
+  mysql_mutex_unlock(&rpl_global_gtid_slave_state->LOCK_slave_state);
+
+  p->hton= table_entry->table_hton;
+  mysql_mutex_lock(&LOCK_slave_background);
+  p->next= slave_background_gtid_pos_create_list;
+  slave_background_gtid_pos_create_list= p;
+  mysql_cond_signal(&COND_slave_background);
+  mysql_mutex_unlock(&LOCK_slave_background);
+}
+
+
+/*
   Start the slave background thread.
 
   This thread is currently used for two purposes:
@@ -5065,6 +5279,14 @@ pthread_handler_t handle_slave_sql(void *arg)
     if (mi->using_gtid != Master_info::USE_GTID_NO || opt_gtid_strict_mode)
       goto err;
   }
+  /* Re-load the set of mysql.gtid_slave_posXXX tables available. */
+  if (find_gtid_slave_pos_tables(thd))
+  {
+    rli->report(ERROR_LEVEL, thd->get_stmt_da()->sql_errno(), NULL,
+                "Error processing replication GTID position tables: %s",
+                thd->get_stmt_da()->message());
+    goto err;
+  }
 
   /* execute init_slave variable */
   if (opt_init_slave.length)
diff --git a/sql/slave.h b/sql/slave.h
index 431e684..c856a698 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -48,6 +48,7 @@
 #include "my_list.h"
 #include "rpl_filter.h"
 #include "rpl_tblmap.h"
+#include "rpl_gtid.h"
 
 #define SLAVE_NET_TIMEOUT  60
 
@@ -268,6 +269,8 @@ void slave_output_error_info(rpl_group_info *rgi, THD *thd);
 pthread_handler_t handle_slave_sql(void *arg);
 bool net_request_file(NET* net, const char* fname);
 void slave_background_kill_request(THD *to_kill);
+void slave_background_gtid_pos_create_request
+        (rpl_slave_state::gtid_pos_table *table_entry);
 
 extern bool volatile abort_loop;
 extern Master_info *active_mi; /* active_mi for multi-master */
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 92b9c94..c73229a 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -941,6 +941,10 @@ SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type)
 }
 
 
+/*
+  If LEX is passed non-NULL, an automatic unlock of the plugin will happen
+  in the LEX destructor.
+*/
 static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc)
 {
   st_plugin_int *pi= plugin_ref_to_int(rc);
@@ -984,6 +988,16 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc)
 }
 
 
+/*
+  Notes on lifetime:
+
+  If THD is passed as non-NULL (and with a non-NULL thd->lex), an entry is made
+  in the thd->lex which will cause an automatic unlock of the plugin in the LEX
+  destructor. In this case, no manual unlock must be done.
+
+  Otherwise, when passing a NULL THD, the caller must arrange that plugin
+  unlock happens later.
+*/
 plugin_ref plugin_lock(THD *thd, plugin_ref ptr)
 {
   LEX *lex= thd ? thd->lex : 0;
@@ -1020,6 +1034,16 @@ plugin_ref plugin_lock(THD *thd, plugin_ref ptr)
 }
 
 
+/*
+  Notes on lifetime:
+
+  If THD is passed as non-NULL (and with a non-NULL thd->lex), an entry is made
+  in the thd->lex which will cause an automatic unlock of the plugin in the LEX
+  destructor. In this case, no manual unlock must be done.
+
+  Otherwise, when passing a NULL THD, the caller must arrange that plugin
+  unlock happens later.
+*/
 plugin_ref plugin_lock_by_name(THD *thd, const LEX_CSTRING *name, int type)
 {
   LEX *lex= thd ? thd->lex : 0;
@@ -1935,6 +1959,12 @@ void plugin_shutdown(void)
 
   if (initialized)
   {
+    if (opt_gtid_pos_auto_plugins)
+    {
+      free_engine_list(opt_gtid_pos_auto_plugins);
+      opt_gtid_pos_auto_plugins= NULL;
+    }
+
     mysql_mutex_lock(&LOCK_plugin);
 
     reap_needed= true;
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 2866509..de054f3 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -3556,6 +3556,45 @@ static Sys_var_plugin Sys_enforce_storage_engine(
        NO_CMD_LINE, MYSQL_STORAGE_ENGINE_PLUGIN,
        DEFAULT(&enforced_storage_engine), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_has_super));
 
+
+#ifdef HAVE_REPLICATION
+/*
+  Check
+   1. Value for gtid_pos_auto_engines is not NULL.
+   2. No slave SQL thread is running.
+*/
+static bool
+check_gtid_pos_auto_engines(sys_var *self, THD *thd, set_var *var)
+{
+  bool running;
+  bool err= false;
+
+  DBUG_ASSERT(var->type == OPT_GLOBAL);
+  if (var->value && var->value->is_null())
+    err= true;
+  else
+  {
+    running= give_error_if_slave_running(false);
+    if (running)
+      err= true;
+  }
+  return err;
+}
+
+
+static Sys_var_pluginlist Sys_gtid_pos_auto_engines(
+       "gtid_pos_auto_engines",
+       "List of engines for which to automatically create a "
+       "mysql.gtid_slave_pos_ENGINE table, if a transaction using that engine "
+       "is replicated. This can be used to avoid introducing cross-engine "
+       "transactions, if engines are used different from that used by table "
+       "mysql.gtid_slave_pos",
+       GLOBAL_VAR(opt_gtid_pos_auto_plugins), NO_CMD_LINE,
+       DEFAULT(&gtid_pos_auto_engines),
+       NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_gtid_pos_auto_engines));
+#endif
+
+
 #if defined(ENABLED_DEBUG_SYNC)
 /*
   Variable can be set for the session only.
diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic
index f9acfb3..9b05530 100644
--- a/sql/sys_vars.ic
+++ b/sql/sys_vars.ic
@@ -1535,6 +1535,116 @@ class Sys_var_plugin: public sys_var
   { return valptr(thd, get_default(thd)); }
 };
 
+/**
+  Class for variables that containg a list of plugins.
+  Currently this is used only for @@gtid_pos_auto_create_engines
+
+  Backing store: plugin_ref
+
+  @note
+  Currently this is only used for storage engine type plugins, and thus only
+  storage engine type plugin is implemented. It could be extended to other
+  plugin types later if needed, similar to Sys_var_plugin.
+
+  These variables don't support command-line equivalents, any such
+  command-line options should be added manually to my_long_options in mysqld.cc
+
+  Note on lifetimes of resources allocated: We allocate a zero-terminated array
+  of plugin_ref*, and lock the contained plugins. The list in the global
+  variable must be freed (with free_engine_list()). However, the way Sys_var
+  works, there is no place to explicitly free other lists, like the one
+  returned from get_default().
+
+  Therefore, the code needs to work with temporary lists, which are
+  registered in the THD to be automatically freed (and plugins similarly
+  automatically unlocked). This is why do_check() allocates a temporary
+  list, from which do_update() then makes a permanent copy.
+*/
+class Sys_var_pluginlist: public sys_var
+{
+  int plugin_type;
+public:
+  Sys_var_pluginlist(const char *name_arg,
+          const char *comment, int flag_args, ptrdiff_t off, size_t size,
+          CMD_LINE getopt,
+          char **def_val, PolyLock *lock=0,
+          enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
+          on_check_function on_check_func=0,
+          on_update_function on_update_func=0,
+          const char *substitute=0)
+    : sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
+              getopt.arg_type, SHOW_CHAR, (intptr)def_val,
+              lock, binlog_status_arg, on_check_func, on_update_func,
+              substitute)
+  {
+    option.var_type|= GET_STR;
+    SYSVAR_ASSERT(size == sizeof(plugin_ref));
+    SYSVAR_ASSERT(getopt.id < 0); // force NO_CMD_LINE
+  }
+  bool do_check(THD *thd, set_var *var)
+  {
+    char buff[STRING_BUFFER_USUAL_SIZE];
+    String str(buff,sizeof(buff), system_charset_info), *res;
+    plugin_ref *plugins;
+
+    if (!(res=var->value->val_str(&str)))
+      plugins= resolve_engine_list(thd, "", 0, true, true);
+    else
+      plugins= resolve_engine_list(thd, res->ptr(), res->length(), true, true);
+    if (!plugins)
+      return true;
+    var->save_result.plugins= plugins;
+    return false;
+  }
+  void do_update(plugin_ref **valptr, plugin_ref* newval)
+  {
+    plugin_ref *oldval= *valptr;
+    *valptr= copy_engine_list(newval);
+    free_engine_list(oldval);
+  }
+  bool session_update(THD *thd, set_var *var)
+  {
+    do_update((plugin_ref**)session_var_ptr(thd),
+              var->save_result.plugins);
+    return false;
+  }
+  bool global_update(THD *thd, set_var *var)
+  {
+    do_update((plugin_ref**)global_var_ptr(),
+              var->save_result.plugins);
+    return false;
+  }
+  void session_save_default(THD *thd, set_var *var)
+  {
+    plugin_ref* plugins= global_var(plugin_ref *);
+    var->save_result.plugins= plugins ? temp_copy_engine_list(thd, plugins) : 0;
+  }
+  plugin_ref *get_default(THD *thd)
+  {
+    char *default_value= *reinterpret_cast<char**>(option.def_value);
+    if (!default_value)
+      return 0;
+    return resolve_engine_list(thd, default_value, strlen(default_value),
+                               false, true);
+  }
+
+  void global_save_default(THD *thd, set_var *var)
+  {
+    var->save_result.plugins= get_default(thd);
+  }
+
+  uchar *valptr(THD *thd, plugin_ref *plugins)
+  {
+    return (uchar*)pretty_print_engine_list(thd, plugins);
+  }
+  uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+  { return valptr(thd, session_var(thd, plugin_ref*)); }
+  uchar *global_value_ptr(THD *thd, const LEX_STRING *base)
+  { return valptr(thd, global_var(plugin_ref*)); }
+  uchar *default_value_ptr(THD *thd)
+  { return valptr(thd, get_default(thd)); }
+};
+
 #if defined(ENABLED_DEBUG_SYNC)
 
 #include "debug_sync.h"
diff --git a/storage/tokudb/mysql-test/rpl/r/rpl_deadlock_tokudb.result b/storage/tokudb/mysql-test/rpl/r/rpl_deadlock_tokudb.result
index 2348fd0..1698dc6 100644
--- a/storage/tokudb/mysql-test/rpl/r/rpl_deadlock_tokudb.result
+++ b/storage/tokudb/mysql-test/rpl/r/rpl_deadlock_tokudb.result
@@ -39,7 +39,9 @@ connection slave;
 BEGIN;
 SELECT * FROM t1 FOR UPDATE;
 a
+connection slave1;
 START SLAVE;
+connection slave;
 SELECT COUNT(*) FROM t2;
 COUNT(*)
 0
@@ -61,8 +63,10 @@ BEGIN;
 SELECT * FROM t1 FOR UPDATE;
 a
 1
+connection slave1;
 START SLAVE;
 include/wait_for_slave_sql_error.inc [errno=1205]
+connection slave;
 SELECT COUNT(*) FROM t2;
 COUNT(*)
 0
@@ -92,8 +96,10 @@ SELECT * FROM t1 FOR UPDATE;
 a
 1
 1
+connection slave1;
 START SLAVE;
 include/wait_for_slave_sql_error.inc [errno=1205]
+connection slave;
 SELECT COUNT(*) FROM t2;
 COUNT(*)
 0
diff --git a/storage/tokudb/mysql-test/tokudb_rpl/r/mdev12179.result b/storage/tokudb/mysql-test/tokudb_rpl/r/mdev12179.result
new file mode 100644
index 0000000..d4532ee
--- /dev/null
+++ b/storage/tokudb/mysql-test/tokudb_rpl/r/mdev12179.result
@@ -0,0 +1,265 @@
+include/master-slave.inc
+[connection master]
+connection server_2;
+include/stop_slave.inc
+CHANGE MASTER TO master_use_gtid=slave_pos;
+SET sql_log_bin=0;
+CREATE TABLE mysql.gtid_slave_pos_innodb LIKE mysql.gtid_slave_pos;
+ALTER TABLE mysql.gtid_slave_pos_innodb ENGINE=InnoDB;
+CREATE TABLE mysql.gtid_slave_pos_tokudb LIKE mysql.gtid_slave_pos;
+ALTER TABLE mysql.gtid_slave_pos_tokudb ENGINE=TokuDB;
+CREATE TABLE mysql.gtid_slave_pos_myisam_redundant LIKE mysql.gtid_slave_pos;
+CREATE TABLE mysql.gtid_slave_pos_innodb_redundant LIKE mysql.gtid_slave_pos;
+ALTER TABLE mysql.gtid_slave_pos_innodb_redundant ENGINE=InnoDB;
+call mtr.add_suppression("Ignoring redundant table.*since.*has the same storage engine");
+include/start_slave.inc
+connection server_1;
+CREATE TABLE t1 (a INT PRIMARY KEY);
+CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB;
+CREATE TABLE t3 (a INT PRIMARY KEY) ENGINE=TokuDB;
+INSERT INTO t1 VALUES (1);
+INSERT INTO t2 VALUES (1);
+INSERT INTO t3 VALUES (1);
+SELECT * FROM t1 ORDER BY a;
+a
+1
+SELECT * FROM t2 ORDER BY a;
+a
+1
+SELECT * FROM t3 ORDER BY a;
+a
+1
+connection server_2;
+SELECT * FROM t1 ORDER BY a;
+a
+1
+SELECT * FROM t2 ORDER BY a;
+a
+1
+SELECT * FROM t3 ORDER BY a;
+a
+1
+SELECT * FROM mysql.gtid_slave_pos ORDER BY sub_id;
+domain_id	sub_id	server_id	seq_no
+0	3	1	3
+0	4	1	4
+SELECT * FROM ( SELECT * FROM mysql.gtid_slave_pos_innodb
+UNION ALL SELECT * FROM mysql.gtid_slave_pos_innodb_redundant) inner_select
+ORDER BY sub_id;
+domain_id	sub_id	server_id	seq_no
+0	5	1	5
+SELECT * FROM mysql.gtid_slave_pos_tokudb ORDER BY sub_id;
+domain_id	sub_id	server_id	seq_no
+0	6	1	6
+connection server_2;
+FLUSH NO_WRITE_TO_BINLOG STATUS;
+SET sql_log_bin=0;
+SHOW STATUS LIKE "Transactions_multi_engine";
+Variable_name	Value
+Transactions_multi_engine	0
+INSERT INTO t1 VALUES (100);
+SHOW STATUS LIKE "Transactions_multi_engine";
+Variable_name	Value
+Transactions_multi_engine	0
+INSERT INTO t2 VALUES (101);
+SHOW STATUS LIKE "Transactions_multi_engine";
+Variable_name	Value
+Transactions_multi_engine	0
+INSERT INTO t3 VALUES (101);
+SHOW STATUS LIKE "Transactions_multi_engine";
+Variable_name	Value
+Transactions_multi_engine	0
+BEGIN;
+INSERT INTO t3 VALUES (102);
+INSERT INTO t2 VALUES (103);
+COMMIT;
+SHOW STATUS LIKE "Transactions_multi_engine";
+Variable_name	Value
+Transactions_multi_engine	1
+BEGIN;
+INSERT INTO t2 VALUES (104);
+INSERT INTO t3 VALUES (105);
+COMMIT;
+SHOW STATUS LIKE "Transactions_multi_engine";
+Variable_name	Value
+Transactions_multi_engine	2
+UPDATE t2, t3 SET t2.a=106, t3.a=107 WHERE t2.a=104 AND t3.a=105;
+SHOW STATUS LIKE "Transactions_multi_engine";
+Variable_name	Value
+Transactions_multi_engine	3
+SET sql_log_bin=1;
+INSERT INTO t1 VALUES (200);
+SHOW STATUS LIKE "Transactions_multi_engine";
+Variable_name	Value
+Transactions_multi_engine	3
+INSERT INTO t2 VALUES (201);
+SHOW STATUS LIKE "Transactions_multi_engine";
+Variable_name	Value
+Transactions_multi_engine	3
+INSERT INTO t3 VALUES (201);
+SHOW STATUS LIKE "Transactions_multi_engine";
+Variable_name	Value
+Transactions_multi_engine	3
+BEGIN;
+INSERT INTO t3 VALUES (202);
+INSERT INTO t2 VALUES (203);
+COMMIT;
+SHOW STATUS LIKE "Transactions_multi_engine";
+Variable_name	Value
+Transactions_multi_engine	4
+BEGIN;
+INSERT INTO t2 VALUES (204);
+INSERT INTO t3 VALUES (205);
+COMMIT;
+SHOW STATUS LIKE "Transactions_multi_engine";
+Variable_name	Value
+Transactions_multi_engine	5
+UPDATE t2, t3 SET t2.a=206, t3.a=207 WHERE t2.a=204 AND t3.a=205;
+SHOW STATUS LIKE "Transactions_multi_engine";
+Variable_name	Value
+Transactions_multi_engine	6
+DELETE FROM t1 WHERE a >= 100;
+DELETE FROM t2 WHERE a >= 100;
+DELETE FROM t3 WHERE a >= 100;
+connection server_2;
+include/stop_slave.inc
+SET sql_log_bin=0;
+DROP TABLE mysql.gtid_slave_pos_tokudb;
+DROP TABLE mysql.gtid_slave_pos_myisam_redundant;
+DROP TABLE mysql.gtid_slave_pos_innodb_redundant;
+SET sql_log_bin=1;
+FLUSH NO_WRITE_TO_BINLOG STATUS;
+include/start_slave.inc
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	0
+Transactions_gtid_foreign_engine	0
+Transactions_multi_engine	0
+connection server_1;
+INSERT INTO t1 VALUES (100);
+connection server_2;
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	0
+Transactions_gtid_foreign_engine	0
+Transactions_multi_engine	0
+connection server_1;
+INSERT INTO t2 VALUES (101);
+connection server_2;
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	0
+Transactions_gtid_foreign_engine	0
+Transactions_multi_engine	0
+connection server_1;
+INSERT INTO t3 VALUES (101);
+connection server_2;
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	0
+Transactions_gtid_foreign_engine	1
+Transactions_multi_engine	0
+connection server_1;
+BEGIN;
+INSERT INTO t3 VALUES (102);
+INSERT INTO t2 VALUES (103);
+COMMIT;
+connection server_2;
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	1
+Transactions_gtid_foreign_engine	1
+Transactions_multi_engine	1
+connection server_1;
+BEGIN;
+INSERT INTO t2 VALUES (104);
+INSERT INTO t3 VALUES (105);
+COMMIT;
+connection server_2;
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	2
+Transactions_gtid_foreign_engine	1
+Transactions_multi_engine	2
+connection server_1;
+UPDATE t2, t3 SET t2.a=106, t3.a=107 WHERE t2.a=104 AND t3.a=105;
+connection server_2;
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	3
+Transactions_gtid_foreign_engine	1
+Transactions_multi_engine	3
+connection server_2;
+connection server_2;
+SHOW VARIABLES LIKE 'log_bin';
+Variable_name	Value
+log_bin	OFF
+include/start_slave.inc
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	0
+Transactions_gtid_foreign_engine	0
+Transactions_multi_engine	0
+connection server_1;
+INSERT INTO t1 VALUES (200);
+connection server_2;
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	0
+Transactions_gtid_foreign_engine	0
+Transactions_multi_engine	0
+connection server_1;
+INSERT INTO t2 VALUES (201);
+connection server_2;
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	0
+Transactions_gtid_foreign_engine	0
+Transactions_multi_engine	0
+connection server_1;
+INSERT INTO t3 VALUES (201);
+connection server_2;
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	0
+Transactions_gtid_foreign_engine	1
+Transactions_multi_engine	0
+connection server_1;
+BEGIN;
+INSERT INTO t3 VALUES (202);
+INSERT INTO t2 VALUES (203);
+COMMIT;
+connection server_2;
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	1
+Transactions_gtid_foreign_engine	1
+Transactions_multi_engine	1
+connection server_1;
+BEGIN;
+INSERT INTO t2 VALUES (204);
+INSERT INTO t3 VALUES (205);
+COMMIT;
+connection server_2;
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	2
+Transactions_gtid_foreign_engine	1
+Transactions_multi_engine	2
+connection server_1;
+UPDATE t2, t3 SET t2.a=206, t3.a=207 WHERE t2.a=204 AND t3.a=205;
+connection server_2;
+SHOW STATUS LIKE "%transactions%engine";
+Variable_name	Value
+Rpl_transactions_multi_engine	3
+Transactions_gtid_foreign_engine	1
+Transactions_multi_engine	3
+connection server_2;
+SET sql_log_bin=0;
+DROP TABLE mysql.gtid_slave_pos_innodb;
+SET sql_log_bin=1;
+connection server_1;
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
+include/rpl_end.inc
diff --git a/storage/tokudb/mysql-test/tokudb_rpl/t/mdev12179.test b/storage/tokudb/mysql-test/tokudb_rpl/t/mdev12179.test
new file mode 100644
index 0000000..ceb119c
--- /dev/null
+++ b/storage/tokudb/mysql-test/tokudb_rpl/t/mdev12179.test
@@ -0,0 +1,232 @@
+--source include/have_tokudb.inc
+--source include/have_innodb.inc
+--source include/master-slave.inc
+
+--connection server_2
+--source include/stop_slave.inc
+CHANGE MASTER TO master_use_gtid=slave_pos;
+SET sql_log_bin=0;
+CREATE TABLE mysql.gtid_slave_pos_innodb LIKE mysql.gtid_slave_pos;
+ALTER TABLE mysql.gtid_slave_pos_innodb ENGINE=InnoDB;
+CREATE TABLE mysql.gtid_slave_pos_tokudb LIKE mysql.gtid_slave_pos;
+ALTER TABLE mysql.gtid_slave_pos_tokudb ENGINE=TokuDB;
+CREATE TABLE mysql.gtid_slave_pos_myisam_redundant LIKE mysql.gtid_slave_pos;
+CREATE TABLE mysql.gtid_slave_pos_innodb_redundant LIKE mysql.gtid_slave_pos;
+ALTER TABLE mysql.gtid_slave_pos_innodb_redundant ENGINE=InnoDB;
+call mtr.add_suppression("Ignoring redundant table.*since.*has the same storage engine");
+--source include/start_slave.inc
+
+--connection server_1
+CREATE TABLE t1 (a INT PRIMARY KEY);
+CREATE TABLE t2 (a INT PRIMARY KEY) ENGINE=InnoDB;
+CREATE TABLE t3 (a INT PRIMARY KEY) ENGINE=TokuDB;
+INSERT INTO t1 VALUES (1);
+INSERT INTO t2 VALUES (1);
+INSERT INTO t3 VALUES (1);
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+SELECT * FROM t3 ORDER BY a;
+--save_master_pos
+
+--connection server_2
+--sync_with_master
+SELECT * FROM t1 ORDER BY a;
+SELECT * FROM t2 ORDER BY a;
+SELECT * FROM t3 ORDER BY a;
+SELECT * FROM mysql.gtid_slave_pos ORDER BY sub_id;
+SELECT * FROM ( SELECT * FROM mysql.gtid_slave_pos_innodb
+      UNION ALL SELECT * FROM mysql.gtid_slave_pos_innodb_redundant) inner_select
+ ORDER BY sub_id;
+SELECT * FROM mysql.gtid_slave_pos_tokudb ORDER BY sub_id;
+
+
+# Test status variable Transactions_multi_engine.
+--connection server_2
+FLUSH NO_WRITE_TO_BINLOG STATUS;
+SET sql_log_bin=0;
+SHOW STATUS LIKE "Transactions_multi_engine";
+INSERT INTO t1 VALUES (100);
+SHOW STATUS LIKE "Transactions_multi_engine";
+INSERT INTO t2 VALUES (101);
+SHOW STATUS LIKE "Transactions_multi_engine";
+INSERT INTO t3 VALUES (101);
+SHOW STATUS LIKE "Transactions_multi_engine";
+BEGIN;
+INSERT INTO t3 VALUES (102);
+INSERT INTO t2 VALUES (103);
+COMMIT;
+SHOW STATUS LIKE "Transactions_multi_engine";
+BEGIN;
+INSERT INTO t2 VALUES (104);
+INSERT INTO t3 VALUES (105);
+COMMIT;
+SHOW STATUS LIKE "Transactions_multi_engine";
+UPDATE t2, t3 SET t2.a=106, t3.a=107 WHERE t2.a=104 AND t3.a=105;
+SHOW STATUS LIKE "Transactions_multi_engine";
+# Try again with binlog enabled.
+SET sql_log_bin=1;
+INSERT INTO t1 VALUES (200);
+SHOW STATUS LIKE "Transactions_multi_engine";
+INSERT INTO t2 VALUES (201);
+SHOW STATUS LIKE "Transactions_multi_engine";
+INSERT INTO t3 VALUES (201);
+SHOW STATUS LIKE "Transactions_multi_engine";
+BEGIN;
+INSERT INTO t3 VALUES (202);
+INSERT INTO t2 VALUES (203);
+COMMIT;
+SHOW STATUS LIKE "Transactions_multi_engine";
+BEGIN;
+INSERT INTO t2 VALUES (204);
+INSERT INTO t3 VALUES (205);
+COMMIT;
+SHOW STATUS LIKE "Transactions_multi_engine";
+UPDATE t2, t3 SET t2.a=206, t3.a=207 WHERE t2.a=204 AND t3.a=205;
+SHOW STATUS LIKE "Transactions_multi_engine";
+
+DELETE FROM t1 WHERE a >= 100;
+DELETE FROM t2 WHERE a >= 100;
+DELETE FROM t3 WHERE a >= 100;
+
+
+# Test status variables Rpl_transactions_multi_engine and Transactions_gtid_foreign_engine.
+# Have mysql.gtid_slave_pos* for myisam and innodb but not tokudb.
+--connection server_2
+--source include/stop_slave.inc
+SET sql_log_bin=0;
+DROP TABLE mysql.gtid_slave_pos_tokudb;
+DROP TABLE mysql.gtid_slave_pos_myisam_redundant;
+DROP TABLE mysql.gtid_slave_pos_innodb_redundant;
+SET sql_log_bin=1;
+FLUSH NO_WRITE_TO_BINLOG STATUS;
+--source include/start_slave.inc
+SHOW STATUS LIKE "%transactions%engine";
+
+--connection server_1
+INSERT INTO t1 VALUES (100);
+--save_master_pos
+--connection server_2
+--sync_with_master
+SHOW STATUS LIKE "%transactions%engine";
+
+--connection server_1
+INSERT INTO t2 VALUES (101);
+--save_master_pos
+--connection server_2
+--sync_with_master
+SHOW STATUS LIKE "%transactions%engine";
+
+--connection server_1
+INSERT INTO t3 VALUES (101);
+--save_master_pos
+--connection server_2
+--sync_with_master
+SHOW STATUS LIKE "%transactions%engine";
+
+--connection server_1
+BEGIN;
+INSERT INTO t3 VALUES (102);
+INSERT INTO t2 VALUES (103);
+COMMIT;
+--save_master_pos
+--connection server_2
+--sync_with_master
+SHOW STATUS LIKE "%transactions%engine";
+
+--connection server_1
+BEGIN;
+INSERT INTO t2 VALUES (104);
+INSERT INTO t3 VALUES (105);
+COMMIT;
+--save_master_pos
+--connection server_2
+--sync_with_master
+SHOW STATUS LIKE "%transactions%engine";
+
+--connection server_1
+UPDATE t2, t3 SET t2.a=106, t3.a=107 WHERE t2.a=104 AND t3.a=105;
+--save_master_pos
+--connection server_2
+--sync_with_master
+SHOW STATUS LIKE "%transactions%engine";
+
+# Now the same thing, but without binlogging on the slave.
+--connection server_2
+--write_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
+wait
+EOF
+--shutdown_server 30
+--source include/wait_until_disconnected.inc
+
+# Restart without binary log.
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.2.expect
+restart: --skip-log-bin
+EOF
+
+--connection server_2
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+SHOW VARIABLES LIKE 'log_bin';
+--source include/start_slave.inc
+SHOW STATUS LIKE "%transactions%engine";
+
+--connection server_1
+INSERT INTO t1 VALUES (200);
+--save_master_pos
+--connection server_2
+--sync_with_master
+SHOW STATUS LIKE "%transactions%engine";
+
+--connection server_1
+INSERT INTO t2 VALUES (201);
+--save_master_pos
+--connection server_2
+--sync_with_master
+SHOW STATUS LIKE "%transactions%engine";
+
+--connection server_1
+INSERT INTO t3 VALUES (201);
+--save_master_pos
+--connection server_2
+--sync_with_master
+SHOW STATUS LIKE "%transactions%engine";
+
+--connection server_1
+BEGIN;
+INSERT INTO t3 VALUES (202);
+INSERT INTO t2 VALUES (203);
+COMMIT;
+--save_master_pos
+--connection server_2
+--sync_with_master
+SHOW STATUS LIKE "%transactions%engine";
+
+--connection server_1
+BEGIN;
+INSERT INTO t2 VALUES (204);
+INSERT INTO t3 VALUES (205);
+COMMIT;
+--save_master_pos
+--connection server_2
+--sync_with_master
+SHOW STATUS LIKE "%transactions%engine";
+
+--connection server_1
+UPDATE t2, t3 SET t2.a=206, t3.a=207 WHERE t2.a=204 AND t3.a=205;
+--save_master_pos
+--connection server_2
+--sync_with_master
+SHOW STATUS LIKE "%transactions%engine";
+
+
+--connection server_2
+SET sql_log_bin=0;
+DROP TABLE mysql.gtid_slave_pos_innodb;
+SET sql_log_bin=1;
+
+--connection server_1
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE t3;
+
+--source include/rpl_end.inc



More information about the commits mailing list