summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrandon Nesterenko <brandon.nesterenko@mariadb.com>2022-04-04 07:35:48 -0600
committerBrandon Nesterenko <brandon.nesterenko@mariadb.com>2022-04-18 16:06:51 -0600
commitd924a30eb9c92731548bae8476255fde6642cd86 (patch)
tree6ba425c65b1d2013e9a57bdc29774cb5d37aa1f6
parente4e25d2bacc067417c35750f5f6c44cad10c81de (diff)
downloadmariadb-git-d924a30eb9c92731548bae8476255fde6642cd86.tar.gz
MDEV-25768: ALTER gtid_slave_pos table storage engine should not be allowed when slave is running.bb-10.2-MDEV-25768
Problem: ======== The mysql.gtid_slave_pos table can have its engine modified while the SQL thread is alive. Solution: ======== Use MDL locks to ensure that the table can only have its engine modified when the slave SQL thread is not running. Reviewed By: ============ TODO
-rw-r--r--mysql-test/suite/rpl/r/rpl_alter_gtid_slave_pos_engine.result125
-rw-r--r--mysql-test/suite/rpl/r/rpl_mdev10863.result9
-rw-r--r--mysql-test/suite/rpl/t/rpl_alter_gtid_slave_pos_engine.test221
-rw-r--r--mysql-test/suite/rpl/t/rpl_mdev10863.test13
-rw-r--r--sql/share/errmsg-utf8.txt2
-rw-r--r--sql/slave.cc31
-rw-r--r--sql/sql_rename.cc28
-rw-r--r--sql/sql_table.cc33
8 files changed, 452 insertions, 10 deletions
diff --git a/mysql-test/suite/rpl/r/rpl_alter_gtid_slave_pos_engine.result b/mysql-test/suite/rpl/r/rpl_alter_gtid_slave_pos_engine.result
new file mode 100644
index 00000000000..f258cb3fdfc
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_alter_gtid_slave_pos_engine.result
@@ -0,0 +1,125 @@
+include/master-slave.inc
+[connection master]
+connection slave;
+SET @old_parallel_threads=@@GLOBAL.slave_parallel_threads;
+call mtr.add_suppression("Slave: This operation cannot be performed as you have a running slave with parallel threads.*");
+#
+# Trying to alter gtid_slave_pos table while slave has parallel threads
+# should result in an error
+#
+connection slave;
+include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=1;
+CHANGE MASTER TO master_use_gtid=slave_pos;
+include/start_slave.inc
+# Slave_SQL_Running: Yes
+ALTER TABLE mysql.gtid_slave_pos ENGINE=innodb;
+ERROR HY000: This operation cannot be performed as you have a running slave with parallel threads. Either stop the slave or disable parallelism first.
+#
+# Altering gtid_slave_pos engine while slave SQL thread is stopped should
+# succeed
+#
+include/stop_slave.inc
+# Slave_SQL_Running: No
+ALTER TABLE mysql.gtid_slave_pos ENGINE=innodb;
+include/start_slave.inc
+# Reset gtid_slave_pos engine
+include/stop_slave.inc
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
+include/start_slave.inc
+#
+# Altering gtid_slave_pos engine on serial slave should succeed
+#
+include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=0;
+include/start_slave.inc
+# Slave_SQL_Running: Yes
+ALTER TABLE mysql.gtid_slave_pos ENGINE=innodb;
+include/start_slave.inc
+Warnings:
+Note 1254 Slave is already running
+# Reset gtid_slave_pos engine
+include/stop_slave.inc
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
+include/start_slave.inc
+#
+# Replicating gtid_slave_pos engine change from master to serial slave
+# should succeed
+#
+connection slave;
+include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=0;
+include/start_slave.inc
+connection master;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+include/save_master_gtid.inc
+connection slave;
+include/sync_with_master_gtid.inc
+# Reset gtid_slave_pos engine
+connection master;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
+include/save_master_gtid.inc
+connection slave;
+include/sync_with_master_gtid.inc
+#
+# Replicating gtid_slave_pos engine change from master to parallel slave
+# should fail
+#
+connection slave;
+include/stop_slave.inc
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
+SET GLOBAL slave_parallel_threads=1;
+include/start_slave.inc
+connection master;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+include/save_master_gtid.inc
+connection slave;
+include/wait_for_slave_sql_error.inc [errno=4054]
+# Skip the problem event from the master.
+include/stop_slave.inc
+SET sql_slave_skip_counter=1;
+include/start_slave.inc
+include/sync_with_master_gtid.inc
+#
+# Renaming gtid_slave_pos table while slave has parallel threads should
+# result in an error
+#
+connection slave;
+include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=1;
+include/start_slave.inc
+# Slave_SQL_Running: Yes
+RENAME TABLE mysql.gtid_slave_pos TO mysql.gtid_slave_pos_v2;
+ERROR HY000: This operation cannot be performed as you have a running slave with parallel threads. Either stop the slave or disable parallelism first.
+#
+# Renaming gtid_slave_pos table on serial slave should succeed
+#
+connection slave;
+include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=0;
+include/start_slave.inc
+# Slave_SQL_Running: Yes
+RENAME TABLE mysql.gtid_slave_pos TO mysql.gtid_slave_pos_v2;
+# Revert table name
+RENAME TABLE mysql.gtid_slave_pos_v2 TO mysql.gtid_slave_pos;
+#
+# Renaming gtid_slave_pos table should succeed on stopped slave
+#
+connection slave;
+# Slave_SQL_Running: Yes
+include/stop_slave.inc
+RENAME TABLE mysql.gtid_slave_pos TO mysql.gtid_slave_pos_v2;
+include/start_slave.inc
+# Revert table name
+include/stop_slave.inc
+RENAME TABLE mysql.gtid_slave_pos_v2 TO mysql.gtid_slave_pos;
+include/start_slave.inc
+#
+# Cleanup
+#
+connection slave;
+include/stop_slave.inc
+ALTER TABLE mysql.gtid_slave_pos ENGINE=MyISAM;
+SET GLOBAL slave_parallel_threads=@old_parallel_threads;
+include/start_slave.inc
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/r/rpl_mdev10863.result b/mysql-test/suite/rpl/r/rpl_mdev10863.result
index 158d4a921b7..d7ed88fbcf5 100644
--- a/mysql-test/suite/rpl/r/rpl_mdev10863.result
+++ b/mysql-test/suite/rpl/r/rpl_mdev10863.result
@@ -2,13 +2,18 @@ include/rpl_init.inc [topology=1->2]
connection server_2;
SET @old_parallel_threads=@@GLOBAL.slave_parallel_threads;
include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=0;
+CHANGE MASTER TO master_use_gtid=slave_pos;
+include/start_slave.inc
+connection server_1;
+ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+connection server_2;
+include/stop_slave.inc
SET GLOBAL slave_parallel_threads=10;
SET @old_max_relay= @@GLOBAL.max_relay_log_size;
SET GLOBAL max_relay_log_size = 4096;
-CHANGE MASTER TO master_use_gtid=slave_pos;
include/start_slave.inc
connection server_1;
-ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1 (a int PRIMARY KEY, b VARCHAR(100)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1, "a");
connection server_2;
diff --git a/mysql-test/suite/rpl/t/rpl_alter_gtid_slave_pos_engine.test b/mysql-test/suite/rpl/t/rpl_alter_gtid_slave_pos_engine.test
new file mode 100644
index 00000000000..eca5d9e91a9
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_alter_gtid_slave_pos_engine.test
@@ -0,0 +1,221 @@
+#
+# Purpose:
+# This test ensures that the engine/table_name of the mysql.gtid_slave_pos
+# table can only be changed if the slave is in serial mode (no parallel
+# threads).
+#
+# References:
+# MDEV-25768: ALTER gtid_slave_pos table storage engine should not be allowed
+# when slave is running
+
+--source include/have_innodb.inc
+--source include/master-slave.inc
+
+# Changing gtid_slave_pos table is format independent so just use one for
+# reduced test time
+--source include/have_binlog_format_row.inc
+
+--connection slave
+--let old_slave_pos_engine= query_get_value(SHOW TABLE STATUS FROM mysql LIKE 'gtid_slave_pos', Engine, 1)
+SET @old_parallel_threads=@@GLOBAL.slave_parallel_threads;
+call mtr.add_suppression("Slave: This operation cannot be performed as you have a running slave with parallel threads.*");
+
+
+--echo #
+--echo # Trying to alter gtid_slave_pos table while slave has parallel threads
+--echo # should result in an error
+--echo #
+--connection slave
+--let slave_sql_running= query_get_value(SHOW SLAVE STATUS, Slave_SQL_Running, 1)
+
+--source include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=1;
+CHANGE MASTER TO master_use_gtid=slave_pos;
+--source include/start_slave.inc
+
+--echo # Slave_SQL_Running: $slave_sql_running
+--error 4054
+ALTER TABLE mysql.gtid_slave_pos ENGINE=innodb;
+--let slave_pos_engine= query_get_value(SHOW TABLE STATUS FROM mysql LIKE 'gtid_slave_pos', Engine, 1)
+if ($old_slave_pos_engine != $slave_pos_engine)
+{
+ --die gtid_slave_pos engine should not have changed if slave has active parallel threads
+}
+
+
+--echo #
+--echo # Altering gtid_slave_pos engine while slave SQL thread is stopped should
+--echo # succeed
+--echo #
+--source include/stop_slave.inc
+--let slave_sql_running= query_get_value(SHOW SLAVE STATUS, Slave_SQL_Running, 1)
+--echo # Slave_SQL_Running: $slave_sql_running
+ALTER TABLE mysql.gtid_slave_pos ENGINE=innodb;
+--source include/start_slave.inc
+--let slave_pos_engine= query_get_value(SHOW TABLE STATUS FROM mysql LIKE 'gtid_slave_pos', Engine, 1)
+if ($old_slave_pos_engine == $slave_pos_engine)
+{
+ --die gtid_slave_pos engine should have changed with a stopped slave
+}
+
+--echo # Reset gtid_slave_pos engine
+--source include/stop_slave.inc
+--eval ALTER TABLE mysql.gtid_slave_pos ENGINE=$old_slave_pos_engine
+--source include/start_slave.inc
+
+
+--echo #
+--echo # Altering gtid_slave_pos engine on serial slave should succeed
+--echo #
+--source include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=0;
+--source include/start_slave.inc
+--let slave_sql_running= query_get_value(SHOW SLAVE STATUS, Slave_SQL_Running, 1)
+--echo # Slave_SQL_Running: $slave_sql_running
+ALTER TABLE mysql.gtid_slave_pos ENGINE=innodb;
+--source include/start_slave.inc
+--let slave_pos_engine= query_get_value(SHOW TABLE STATUS FROM mysql LIKE 'gtid_slave_pos', Engine, 1)
+if ($old_slave_pos_engine == $slave_pos_engine)
+{
+ --die gtid_slave_pos engine should have changed on a serial slave
+}
+
+--echo # Reset gtid_slave_pos engine
+--source include/stop_slave.inc
+--eval ALTER TABLE mysql.gtid_slave_pos ENGINE=$old_slave_pos_engine
+--source include/start_slave.inc
+
+
+--echo #
+--echo # Replicating gtid_slave_pos engine change from master to serial slave
+--echo # should succeed
+--echo #
+--connection slave
+--source include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=0;
+--source include/start_slave.inc
+
+--connection master
+ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+--source include/save_master_gtid.inc
+
+--connection slave
+--source include/sync_with_master_gtid.inc
+--let slave_pos_engine= query_get_value(SHOW TABLE STATUS FROM mysql LIKE 'gtid_slave_pos', Engine, 1)
+if ($old_slave_pos_engine == $slave_pos_engine)
+{
+ --die gtid_slave_pos engine change should have replicated from master on serial slave
+}
+
+--echo # Reset gtid_slave_pos engine
+--connection master
+--eval ALTER TABLE mysql.gtid_slave_pos ENGINE=$old_slave_pos_engine
+--source include/save_master_gtid.inc
+--connection slave
+--source include/sync_with_master_gtid.inc
+
+
+--echo #
+--echo # Replicating gtid_slave_pos engine change from master to parallel slave
+--echo # should fail
+--echo #
+--connection slave
+--source include/stop_slave.inc
+--eval ALTER TABLE mysql.gtid_slave_pos ENGINE=$old_slave_pos_engine
+SET GLOBAL slave_parallel_threads=1;
+--source include/start_slave.inc
+
+--connection master
+ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+--source include/save_master_gtid.inc
+
+--connection slave
+--let $slave_sql_errno=4054
+--source include/wait_for_slave_sql_error.inc
+
+--let slave_pos_engine= query_get_value(SHOW TABLE STATUS FROM mysql LIKE 'gtid_slave_pos', Engine, 1)
+if ($old_slave_pos_engine != $slave_pos_engine)
+{
+ --die gtid_slave_pos engine change from master should fail on parallel slave
+}
+
+--echo # Skip the problem event from the master.
+--source include/stop_slave.inc
+#SET GLOBAL gtid_slave_pos= "0-1-3";
+SET sql_slave_skip_counter=1;
+--source include/start_slave.inc
+--source include/sync_with_master_gtid.inc
+
+
+--echo #
+--echo # Renaming gtid_slave_pos table while slave has parallel threads should
+--echo # result in an error
+--echo #
+--connection slave
+--let slave_sql_running= query_get_value(SHOW SLAVE STATUS, Slave_SQL_Running, 1)
+
+--source include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=1;
+--source include/start_slave.inc
+
+--echo # Slave_SQL_Running: $slave_sql_running
+--error 4054
+RENAME TABLE mysql.gtid_slave_pos TO mysql.gtid_slave_pos_v2;
+--let slave_pos_engine= query_get_value(SHOW TABLE STATUS FROM mysql LIKE 'gtid_slave_pos', Engine, 1)
+if (`SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='mysql' AND table_name='gtid_slave_pos_v2'`)
+{
+ --die gtid_slave_pos rename should fail on slave with active parallel threads
+}
+
+
+--echo #
+--echo # Renaming gtid_slave_pos table on serial slave should succeed
+--echo #
+--connection slave
+--let slave_sql_running= query_get_value(SHOW SLAVE STATUS, Slave_SQL_Running, 1)
+
+--source include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=0;
+--source include/start_slave.inc
+
+--echo # Slave_SQL_Running: $slave_sql_running
+RENAME TABLE mysql.gtid_slave_pos TO mysql.gtid_slave_pos_v2;
+--let slave_pos_engine= query_get_value(SHOW TABLE STATUS FROM mysql LIKE 'gtid_slave_pos', Engine, 1)
+if (`SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='mysql' AND table_name='gtid_slave_pos'`)
+{
+ --die gtid_slave_pos rename should succeed for serial slave
+}
+--echo # Revert table name
+RENAME TABLE mysql.gtid_slave_pos_v2 TO mysql.gtid_slave_pos;
+
+
+--echo #
+--echo # Renaming gtid_slave_pos table should succeed on stopped slave
+--echo #
+--connection slave
+--let slave_sql_running= query_get_value(SHOW SLAVE STATUS, Slave_SQL_Running, 1)
+
+--echo # Slave_SQL_Running: $slave_sql_running
+--source include/stop_slave.inc
+RENAME TABLE mysql.gtid_slave_pos TO mysql.gtid_slave_pos_v2;
+--source include/start_slave.inc
+--let slave_pos_engine= query_get_value(SHOW TABLE STATUS FROM mysql LIKE 'gtid_slave_pos', Engine, 1)
+if (`SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='mysql' AND table_name='gtid_slave_pos'`)
+{
+ --die gtid_slave_pos rename should succeed for stopped slave
+}
+--echo # Revert table name
+--source include/stop_slave.inc
+RENAME TABLE mysql.gtid_slave_pos_v2 TO mysql.gtid_slave_pos;
+--source include/start_slave.inc
+
+--echo #
+--echo # Cleanup
+--echo #
+--connection slave
+--source include/stop_slave.inc
+--eval ALTER TABLE mysql.gtid_slave_pos ENGINE=$old_slave_pos_engine
+SET GLOBAL slave_parallel_threads=@old_parallel_threads;
+--source include/start_slave.inc
+
+--source include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_mdev10863.test b/mysql-test/suite/rpl/t/rpl_mdev10863.test
index 796e770672d..a4ce80d1bf5 100644
--- a/mysql-test/suite/rpl/t/rpl_mdev10863.test
+++ b/mysql-test/suite/rpl/t/rpl_mdev10863.test
@@ -7,14 +7,23 @@
--connection server_2
SET @old_parallel_threads=@@GLOBAL.slave_parallel_threads;
--source include/stop_slave.inc
+SET GLOBAL slave_parallel_threads=0;
+CHANGE MASTER TO master_use_gtid=slave_pos;
+--source include/start_slave.inc
+
+--connection server_1
+ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
+--save_master_pos
+
+--connection server_2
+--sync_with_master
+--source include/stop_slave.inc
SET GLOBAL slave_parallel_threads=10;
SET @old_max_relay= @@GLOBAL.max_relay_log_size;
SET GLOBAL max_relay_log_size = 4096;
-CHANGE MASTER TO master_use_gtid=slave_pos;
--source include/start_slave.inc
--connection server_1
-ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
CREATE TABLE t1 (a int PRIMARY KEY, b VARCHAR(100)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1, "a");
--save_master_pos
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 814716b0a97..9919c7f081d 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -7664,6 +7664,8 @@ ER_SLAVE_SAME_ID
eng "A slave with the same server_uuid/server_id as this slave has connected to the master"
ER_FLASHBACK_NOT_SUPPORTED
eng "Flashback does not support %s %s"
+ER_SLAVE_IS_PARALLEL
+ eng "This operation cannot be performed as you have a running slave with parallel threads. Either stop the slave or disable parallelism first."
diff --git a/sql/slave.cc b/sql/slave.cc
index 2ff1a0490e9..7f851b2cecd 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -4801,6 +4801,10 @@ pthread_handler_t handle_slave_sql(void *arg)
const char *errmsg;
rpl_group_info *serial_rgi;
rpl_sql_thread_info sql_info(mi->rpl_filter);
+ MDL_request mdl_req_gtid_slave_state;
+ mdl_req_gtid_slave_state.init(MDL_key::TABLE, "mysql",
+ rpl_gtid_slave_state_table_name.str,
+ MDL_SHARED_WRITE, MDL_EXPLICIT);
// needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff
my_thread_init();
@@ -4840,13 +4844,25 @@ pthread_handler_t handle_slave_sql(void *arg)
pthread_detach_this_thread();
- if (opt_slave_parallel_threads > 0 &&
- rpl_parallel_activate_pool(&global_rpl_thread_pool))
+ if (opt_slave_parallel_threads > 0)
{
- mysql_cond_broadcast(&rli->start_cond);
- rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
- "Failed during parallel slave pool activation");
- goto err_during_init;
+ if (rpl_parallel_activate_pool(&global_rpl_thread_pool))
+ {
+ mysql_cond_broadcast(&rli->start_cond);
+ rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL,
+ "Failed during parallel slave pool activation");
+ goto err_during_init;
+ }
+ /*
+ Prevent the gtid_slave_pos from having its engine altered while the SQL
+ thread is running.
+ */
+ if (thd->mdl_context.acquire_lock(&mdl_req_gtid_slave_state,
+ thd->variables.lock_wait_timeout))
+ {
+ my_error(ER_CANT_LOCK, MYF(0));
+ goto err;
+ }
}
if (init_slave_thread(thd, mi, SLAVE_THD_SQL))
@@ -5300,6 +5316,9 @@ err_during_init:
delete serial_rgi;
mysql_mutex_unlock(&LOCK_thread_count);
+ if (mdl_req_gtid_slave_state.ticket)
+ thd->mdl_context.release_lock(mdl_req_gtid_slave_state.ticket);
+
delete thd;
thread_safe_decrement32(&service_thread_count);
signal_thd_deleted();
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index af4a6ca3ce1..53d539e8be3 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -30,6 +30,8 @@
#include "sql_base.h" // tdc_remove_table, lock_table_names,
#include "sql_handler.h" // mysql_ha_rm_tables
#include "sql_statistics.h"
+#include "rpl_rli.h"
+#include "rpl_gtid.h"
static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,
bool skip_error);
@@ -51,6 +53,10 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
TABLE_LIST *ren_table= 0;
int to_table;
char *rename_log_table[2]= {NULL, NULL};
+#ifdef HAVE_REPLICATION
+ MDL_request mdl_req_gtid_slave_state;
+ mdl_req_gtid_slave_state.ticket= NULL;
+#endif
DBUG_ENTER("mysql_rename_tables");
/*
@@ -140,6 +146,28 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
}
}
+#ifdef HAVE_REPLICATION
+ /*
+ The gtid slave state table (gtid_slave_pos) cannot be renamed if the slave
+ has parallel threads. The table's MDL lock is held when the slave is
+ running with parallel threads.
+ */
+ if (!(strncmp(table_list->db, C_STRING_WITH_LEN("mysql")) ||
+ strncmp(table_list->table_name, rpl_gtid_slave_state_table_name.str,
+ rpl_gtid_slave_state_table_name.length)))
+ {
+ mdl_req_gtid_slave_state.init(MDL_key::TABLE, "mysql",
+ rpl_gtid_slave_state_table_name.str,
+ MDL_SHARED_NO_WRITE, MDL_TRANSACTION);
+ if (!thd->mdl_context.try_acquire_lock(&mdl_req_gtid_slave_state) &&
+ !mdl_req_gtid_slave_state.ticket)
+ {
+ my_error(ER_SLAVE_IS_PARALLEL, MYF(0));
+ goto err;
+ }
+ }
+#endif
+
if (lock_table_names(thd, table_list, 0, thd->variables.lock_wait_timeout,
0))
goto err;
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index a2dc5c97aeb..70e94baa1c8 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -8974,6 +8974,11 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
*/
int table_kind= check_if_log_table(table_list, FALSE, NullS);
+#ifdef HAVE_REPLICATION
+ MDL_request mdl_req_gtid_slave_state;
+ mdl_req_gtid_slave_state.ticket= NULL;
+#endif
+
if (table_kind)
{
/* Disable alter of enabled log tables */
@@ -9213,6 +9218,29 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
DBUG_RETURN(true);
}
+#ifdef HAVE_REPLICATION
+ /*
+ The engine of the gtid slave state table (gtid_slave_pos) cannot be changed
+ if the slave has parallel threads. The table's MDL lock is held when the
+ slave is running with parallel threads.
+ */
+ if ((old_db_type != new_db_type || alter_ctx.is_table_renamed()) &&
+ !(strncmp(alter_ctx.db, C_STRING_WITH_LEN("mysql")) ||
+ strncmp(alter_ctx.table_name, rpl_gtid_slave_state_table_name.str,
+ rpl_gtid_slave_state_table_name.length)))
+ {
+ mdl_req_gtid_slave_state.init(MDL_key::TABLE, "mysql",
+ rpl_gtid_slave_state_table_name.str,
+ MDL_SHARED_NO_WRITE, MDL_EXPLICIT);
+ if (!thd->mdl_context.try_acquire_lock(&mdl_req_gtid_slave_state) &&
+ !mdl_req_gtid_slave_state.ticket)
+ {
+ my_error(ER_SLAVE_IS_PARALLEL, MYF(0));
+ DBUG_RETURN(true);
+ }
+ }
+#endif
+
if (table->s->tmp_table == NO_TMP_TABLE)
mysql_audit_alter_table(thd, table_list);
@@ -9987,6 +10015,11 @@ end_inplace:
mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE);
}
+#ifdef HAVE_REPLICATION
+ if (mdl_req_gtid_slave_state.ticket)
+ thd->mdl_context.release_lock(mdl_req_gtid_slave_state.ticket);
+#endif
+
end_temporary:
my_snprintf(alter_ctx.tmp_name, sizeof(alter_ctx.tmp_name),
ER_THD(thd, ER_INSERT_INFO),