From c322ede0da544268324fca152c8ab1463979a359 Mon Sep 17 00:00:00 2001 From: Hemant Dangi Date: Wed, 11 Dec 2024 18:23:44 +0530 Subject: [PATCH] CREATE, DROP, ALTER TABLE causes MDL BF-BF conflict Issue: ------ During DROP TABLE execution, if parent tables are opened and MDL locks for those tables are acquired, then concurrent operations to those table could end up in MDL conflict. For CREATE and ALTER TABLE foreign keys of immediate parent table are getting appended in certiification, but if parent table also has foreign key and if those refrenced tables are opened and MDL locks for those tables are acquired, then concurrent operations to those tables could also end up in MDL conflict. Solution: --------- To avoid MDL BF-BF conflicts, append certification keys for all parent's tables so that potentially conflicting operation are not applied in parallel with CREATE, DROP, ALTER TABLE command. --- .../suite/galera/r/mwb-1789-alter.result | 41 +++++ .../suite/galera/r/mwb-1789-create.result | 40 ++++ .../suite/galera/r/mwb-1789-drop-fk2.result | 43 +++++ .../suite/galera/r/mwb-1789-drop.result | 32 ++++ mysql-test/suite/galera/t/mwb-1789-alter.test | 94 ++++++++++ .../suite/galera/t/mwb-1789-create.test | 93 ++++++++++ .../suite/galera/t/mwb-1789-drop-fk2.test | 99 ++++++++++ mysql-test/suite/galera/t/mwb-1789-drop.test | 85 +++++++++ sql/sql_parse.cc | 6 +- sql/sql_table.cc | 14 +- sql/wsrep_mysqld.cc | 173 ++++++++++++++++-- sql/wsrep_mysqld.h | 9 +- 12 files changed, 706 insertions(+), 23 deletions(-) create mode 100644 mysql-test/suite/galera/r/mwb-1789-alter.result create mode 100644 mysql-test/suite/galera/r/mwb-1789-create.result create mode 100644 mysql-test/suite/galera/r/mwb-1789-drop-fk2.result create mode 100644 mysql-test/suite/galera/r/mwb-1789-drop.result create mode 100644 mysql-test/suite/galera/t/mwb-1789-alter.test create mode 100644 mysql-test/suite/galera/t/mwb-1789-create.test create mode 100644 mysql-test/suite/galera/t/mwb-1789-drop-fk2.test create mode 100644 mysql-test/suite/galera/t/mwb-1789-drop.test diff --git a/mysql-test/suite/galera/r/mwb-1789-alter.result b/mysql-test/suite/galera/r/mwb-1789-alter.result new file mode 100644 index 0000000000000..1b88cf2130354 --- /dev/null +++ b/mysql-test/suite/galera/r/mwb-1789-alter.result @@ -0,0 +1,41 @@ +connection node_2; +connection node_1; +connection node_2; +SET GLOBAL wsrep_slave_threads=2; +CREATE TABLE t1 ( +id INTEGER PRIMARY KEY, +f2 INTEGER +); +CREATE TABLE t2 ( +id INT PRIMARY KEY, +t1_id INT NOT NULL, +f2 INTEGER NOT NULL, +KEY key_t1_id(t1_id), +CONSTRAINT key_t1_id FOREIGN KEY (t1_id) REFERENCES t1 (id) ON UPDATE CASCADE ON DELETE CASCADE +); +CREATE TABLE t3 ( +id INT PRIMARY KEY, +t2_id INT NOT NULL, +f2 INTEGER NOT NULL, +KEY key_t2_id(t2_id) +); +INSERT INTO t1 VALUES (1,0); +INSERT INTO t1 VALUES (2,0); +INSERT INTO t2 VALUES (1,1,1234); +INSERT INTO t2 VALUES (2,2,1234); +connection node_2; +SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi'; +connection node_1; +ALTER TABLE t3 ADD CONSTRAINT key_t2_id FOREIGN KEY (t2_id) +REFERENCES t2 (id) ON UPDATE CASCADE ON DELETE CASCADE; +connection node_2; +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached"; +SET SESSION wsrep_sync_wait = 0; +connection node_1; +UPDATE t1 SET f2 = 1 WHERE id=2; +connection node_2; +SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi'; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; +SET GLOBAL DEBUG_DBUG="RESET"; +SET GLOBAL wsrep_slave_threads=DEFAULT; +DROP TABLE t3, t2, t1; diff --git a/mysql-test/suite/galera/r/mwb-1789-create.result b/mysql-test/suite/galera/r/mwb-1789-create.result new file mode 100644 index 0000000000000..e879456e99562 --- /dev/null +++ b/mysql-test/suite/galera/r/mwb-1789-create.result @@ -0,0 +1,40 @@ +connection node_2; +connection node_1; +connection node_2; +SET GLOBAL wsrep_slave_threads=2; +CREATE TABLE t1 ( +id INTEGER PRIMARY KEY, +f2 INTEGER +); +CREATE TABLE t2 ( +id INT PRIMARY KEY, +t1_id INT NOT NULL, +f2 INTEGER NOT NULL, +KEY key_t1_id(t1_id), +CONSTRAINT key_t1_id FOREIGN KEY (t1_id) REFERENCES t1 (id) ON UPDATE CASCADE ON DELETE CASCADE +); +INSERT INTO t1 VALUES (1,0); +INSERT INTO t1 VALUES (2,0); +INSERT INTO t2 VALUES (1,1,1234); +INSERT INTO t2 VALUES (2,2,1234); +connection node_2; +SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi'; +connection node_1; +CREATE TABLE t3 ( +id INT PRIMARY KEY, +t2_id INT NOT NULL, +f2 INTEGER NOT NULL, +KEY key_t2_id(t2_id), +CONSTRAINT key_t2_id FOREIGN KEY (t2_id) REFERENCES t2 (id) ON UPDATE CASCADE ON DELETE CASCADE +); +connection node_2; +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached"; +SET SESSION wsrep_sync_wait = 0; +connection node_1; +UPDATE t1 SET f2 = 1 WHERE id=2; +connection node_2; +SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi'; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; +SET GLOBAL DEBUG_DBUG="RESET"; +SET GLOBAL wsrep_slave_threads=DEFAULT; +DROP TABLE t3, t2, t1; diff --git a/mysql-test/suite/galera/r/mwb-1789-drop-fk2.result b/mysql-test/suite/galera/r/mwb-1789-drop-fk2.result new file mode 100644 index 0000000000000..bb2ccbe7ccdcf --- /dev/null +++ b/mysql-test/suite/galera/r/mwb-1789-drop-fk2.result @@ -0,0 +1,43 @@ +connection node_2; +connection node_1; +connection node_2; +SET GLOBAL wsrep_slave_threads=2; +CREATE TABLE t1 ( +id INTEGER PRIMARY KEY, +f2 INTEGER +); +CREATE TABLE t2 ( +id INT PRIMARY KEY, +t1_id INT NOT NULL, +f2 INTEGER NOT NULL, +KEY key_t1_id(t1_id), +CONSTRAINT key_t1_id FOREIGN KEY (t1_id) REFERENCES t1 (id) ON UPDATE CASCADE ON DELETE CASCADE +); +CREATE TABLE t3 ( +id INT PRIMARY KEY, +t2_id INT NOT NULL, +f2 INTEGER NOT NULL, +KEY key_t2_id(t2_id), +CONSTRAINT key_t2_id FOREIGN KEY (t2_id) REFERENCES t2 (id) ON UPDATE CASCADE ON DELETE CASCADE +); +INSERT INTO t1 VALUES (1,0); +INSERT INTO t1 VALUES (2,0); +INSERT INTO t2 VALUES (1,1,1234); +INSERT INTO t2 VALUES (2,2,1234); +INSERT INTO t3 VALUES (1,1,1234); +INSERT INTO t3 VALUES (2,2,1234); +connection node_2; +SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi'; +connection node_1; +DROP TABLE t3; +connection node_2; +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached"; +SET SESSION wsrep_sync_wait = 0; +connection node_1; +UPDATE t1 SET f2 = 1 WHERE id=2; +connection node_2; +SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi'; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; +SET GLOBAL DEBUG_DBUG="RESET"; +SET GLOBAL wsrep_slave_threads=DEFAULT; +DROP TABLE t2, t1; diff --git a/mysql-test/suite/galera/r/mwb-1789-drop.result b/mysql-test/suite/galera/r/mwb-1789-drop.result new file mode 100644 index 0000000000000..b09faf14703df --- /dev/null +++ b/mysql-test/suite/galera/r/mwb-1789-drop.result @@ -0,0 +1,32 @@ +connection node_2; +connection node_1; +connection node_2; +SET GLOBAL wsrep_slave_threads=2; +CREATE TABLE t1 ( +id INTEGER PRIMARY KEY, +f2 INTEGER); +CREATE TABLE t2 ( +f1 INT PRIMARY KEY, +t1_id INT NOT NULL, +f2 INTEGER NOT NULL, +KEY key_t1_id(t1_id), +CONSTRAINT key_t1_id FOREIGN KEY (t1_id) REFERENCES t1 (id) ON UPDATE CASCADE ON DELETE CASCADE); +INSERT INTO t1 VALUES (1,0); +INSERT INTO t1 VALUES (2,0); +INSERT INTO t2 VALUES (1,1,1234); +INSERT INTO t2 VALUES (2,2,1234); +connection node_2; +SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi'; +connection node_1; +DROP TABLE t2; +connection node_2; +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached"; +SET SESSION wsrep_sync_wait = 0; +connection node_1; +UPDATE t1 SET f2 = 1 WHERE id=2; +connection node_2; +SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi'; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; +SET GLOBAL DEBUG_DBUG="RESET"; +SET GLOBAL wsrep_slave_threads=DEFAULT; +DROP TABLE t1; diff --git a/mysql-test/suite/galera/t/mwb-1789-alter.test b/mysql-test/suite/galera/t/mwb-1789-alter.test new file mode 100644 index 0000000000000..8ad4a6a9c146a --- /dev/null +++ b/mysql-test/suite/galera/t/mwb-1789-alter.test @@ -0,0 +1,94 @@ +# +# BF-BF conflict on MDL locks between: +# ALTER TABLE t3 (whose parent table are t3 -> t2 -> t1), and +# UPDATE on t1 with t2 referencing t1, and t3 referencing t2. +# + +--source include/galera_cluster.inc +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc + +# +# Setup +# +--connection node_2 +SET GLOBAL wsrep_slave_threads=2; + +CREATE TABLE t1 ( + id INTEGER PRIMARY KEY, + f2 INTEGER +); + +CREATE TABLE t2 ( + id INT PRIMARY KEY, + t1_id INT NOT NULL, + f2 INTEGER NOT NULL, + KEY key_t1_id(t1_id), + CONSTRAINT key_t1_id FOREIGN KEY (t1_id) REFERENCES t1 (id) ON UPDATE CASCADE ON DELETE CASCADE +); + +CREATE TABLE t3 ( + id INT PRIMARY KEY, + t2_id INT NOT NULL, + f2 INTEGER NOT NULL, + KEY key_t2_id(t2_id) +); + +INSERT INTO t1 VALUES (1,0); +INSERT INTO t1 VALUES (2,0); + +INSERT INTO t2 VALUES (1,1,1234); +INSERT INTO t2 VALUES (2,2,1234); + +# +# ALTER TABLE t3 and wait for it to reach node_2 +# +--connection node_2 +SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi'; + +--connection node_1 +ALTER TABLE t3 ADD CONSTRAINT key_t2_id FOREIGN KEY (t2_id) + REFERENCES t2 (id) ON UPDATE CASCADE ON DELETE CASCADE; + +--connection node_2 +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached"; + +SET SESSION wsrep_sync_wait = 0; +--let $expected_apply_waits = `SELECT VARIABLE_VALUE+1 FROM performance_schema.global_status WHERE VARIABLE_NAME = 'wsrep_apply_waits'` + +# +# Issue a UPDATE to table that references t1 +# Notice that we update field f2, not the primary key, +# and not foreign key. Bug does not manifest if we update +# one of those fields (because FK keys appended in those cases). +# +--connection node_1 +UPDATE t1 SET f2 = 1 WHERE id=2; + + +# +# Expect the UPDATE to depend on the ALTER TABLE, +# therefore it should wait for the CREAT TABLE to +# finish before it can be applied. +# If bug is present, expect the wait condition +# to timeout and when the UPDATE applies, it +# will be granted a MDL lock of type SHARED_READ +# for table t1. When resumed, the ALTER TABLE will +# also try to MDL lock t1, causing a BF-BF conflict +# on that MDL lock. +# +--connection node_2 +--let $wait_condition = SELECT VARIABLE_VALUE = $expected_apply_waits FROM performance_schema.global_status WHERE VARIABLE_NAME = 'wsrep_apply_waits'; +--source include/wait_condition.inc +SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi'; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; + + +# +# Cleanup +# +SET GLOBAL DEBUG_DBUG="RESET"; +SET GLOBAL wsrep_slave_threads=DEFAULT; + +DROP TABLE t3, t2, t1; diff --git a/mysql-test/suite/galera/t/mwb-1789-create.test b/mysql-test/suite/galera/t/mwb-1789-create.test new file mode 100644 index 0000000000000..2c95c5a9178a1 --- /dev/null +++ b/mysql-test/suite/galera/t/mwb-1789-create.test @@ -0,0 +1,93 @@ +# +# BF-BF conflict on MDL locks between: +# CREATE TABLE t3 (whose parent table are t3 -> t2 -> t1), and +# UPDATE on t1 with t2 referencing t1, and t3 referencing t2. +# + +--source include/galera_cluster.inc +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc + +# +# Setup +# +--connection node_2 +SET GLOBAL wsrep_slave_threads=2; + +CREATE TABLE t1 ( + id INTEGER PRIMARY KEY, + f2 INTEGER +); + +CREATE TABLE t2 ( + id INT PRIMARY KEY, + t1_id INT NOT NULL, + f2 INTEGER NOT NULL, + KEY key_t1_id(t1_id), + CONSTRAINT key_t1_id FOREIGN KEY (t1_id) REFERENCES t1 (id) ON UPDATE CASCADE ON DELETE CASCADE +); + + +INSERT INTO t1 VALUES (1,0); +INSERT INTO t1 VALUES (2,0); + +INSERT INTO t2 VALUES (1,1,1234); +INSERT INTO t2 VALUES (2,2,1234); + +# +# CREATE TABLE t3 and wait for it to reach node_2 +# +--connection node_2 +SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi'; + +--connection node_1 +CREATE TABLE t3 ( + id INT PRIMARY KEY, + t2_id INT NOT NULL, + f2 INTEGER NOT NULL, + KEY key_t2_id(t2_id), + CONSTRAINT key_t2_id FOREIGN KEY (t2_id) REFERENCES t2 (id) ON UPDATE CASCADE ON DELETE CASCADE +); + +--connection node_2 +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached"; + +SET SESSION wsrep_sync_wait = 0; +--let $expected_apply_waits = `SELECT VARIABLE_VALUE+1 FROM performance_schema.global_status WHERE VARIABLE_NAME = 'wsrep_apply_waits'` + +# +# Issue a UPDATE to table that references t1 +# Notice that we update field f2, not the primary key, +# and not foreign key. Bug does not manifest if we update +# one of those fields (because FK keys appended in those cases). +# +--connection node_1 +UPDATE t1 SET f2 = 1 WHERE id=2; + + +# +# Expect the UPDATE to depend on the CREATE TABLE, +# therefore it should wait for the CREAT TABLE to +# finish before it can be applied. +# If bug is present, expect the wait condition +# to timeout and when the UPDATE applies, it +# will be granted a MDL lock of type SHARED_READ +# for table t1. When resumed, the CREATE TABLE will +# also try to MDL lock t1, causing a BF-BF conflict +# on that MDL lock. +# +--connection node_2 +--let $wait_condition = SELECT VARIABLE_VALUE = $expected_apply_waits FROM performance_schema.global_status WHERE VARIABLE_NAME = 'wsrep_apply_waits'; +--source include/wait_condition.inc +SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi'; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; + + +# +# Cleanup +# +SET GLOBAL DEBUG_DBUG="RESET"; +SET GLOBAL wsrep_slave_threads=DEFAULT; + +DROP TABLE t3, t2, t1; diff --git a/mysql-test/suite/galera/t/mwb-1789-drop-fk2.test b/mysql-test/suite/galera/t/mwb-1789-drop-fk2.test new file mode 100644 index 0000000000000..3b5433ce55e8f --- /dev/null +++ b/mysql-test/suite/galera/t/mwb-1789-drop-fk2.test @@ -0,0 +1,99 @@ +# +# BF-BF conflict on MDL locks between: DROP TABLE t2 and UPDATE on t1 +# with t2 referencing t1 +# + +--source include/galera_cluster.inc +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc + +# +# Setup +# +--connection node_2 +SET GLOBAL wsrep_slave_threads=2; + +CREATE TABLE t1 ( + id INTEGER PRIMARY KEY, + f2 INTEGER +); + +CREATE TABLE t2 ( + id INT PRIMARY KEY, + t1_id INT NOT NULL, + f2 INTEGER NOT NULL, + KEY key_t1_id(t1_id), + CONSTRAINT key_t1_id FOREIGN KEY (t1_id) REFERENCES t1 (id) ON UPDATE CASCADE ON DELETE CASCADE +); + +CREATE TABLE t3 ( + id INT PRIMARY KEY, + t2_id INT NOT NULL, + f2 INTEGER NOT NULL, + KEY key_t2_id(t2_id), + CONSTRAINT key_t2_id FOREIGN KEY (t2_id) REFERENCES t2 (id) ON UPDATE CASCADE ON DELETE CASCADE +); + + +INSERT INTO t1 VALUES (1,0); +INSERT INTO t1 VALUES (2,0); + +INSERT INTO t2 VALUES (1,1,1234); +INSERT INTO t2 VALUES (2,2,1234); + +INSERT INTO t3 VALUES (1,1,1234); +INSERT INTO t3 VALUES (2,2,1234); + +# +# DROP TABLE t2 and wait for it to reach node_2 +# +--connection node_2 +SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi'; + +--connection node_1 +DROP TABLE t3; + +--connection node_2 +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached"; + +SET SESSION wsrep_sync_wait = 0; +--let $expected_apply_waits = `SELECT VARIABLE_VALUE+1 FROM performance_schema.global_status WHERE VARIABLE_NAME = 'wsrep_apply_waits'` + +# +# Issue a UPDATE to table that references t1 +# Notice that we update field f2, not the primary key, +# and not foreign key. Bug does not manifest if we update +# one of those fields (because FK keys appended in those cases). +# +--connection node_1 +UPDATE t1 SET f2 = 1 WHERE id=2; + + +# +# Expect the UPDATE to depend on the DROP, +# therefore it should wait for the DROP to +# finish before it can be applied. +# If bug is present, expect the wait condition +# to timeout and when the UPDATE applies, it +# will be granted a MDL lock of type SHARED_READ +# for table t1. When resumed, the DROP TABLE will +# also try to MDL lock t1, causing a BF-BF conflict +# on that MDL lock. +# +--connection node_2 +--let $wait_condition = SELECT VARIABLE_VALUE = $expected_apply_waits FROM performance_schema.global_status WHERE VARIABLE_NAME = 'wsrep_apply_waits'; +--source include/wait_condition.inc +SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi'; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; + + +# +# Cleanup +# +SET GLOBAL DEBUG_DBUG="RESET"; +# SET DEBUG_SYNC = 'RESET'; + +SET GLOBAL wsrep_slave_threads=DEFAULT; + +DROP TABLE t2, t1; diff --git a/mysql-test/suite/galera/t/mwb-1789-drop.test b/mysql-test/suite/galera/t/mwb-1789-drop.test new file mode 100644 index 0000000000000..eb43d029748f5 --- /dev/null +++ b/mysql-test/suite/galera/t/mwb-1789-drop.test @@ -0,0 +1,85 @@ +# +# BF-BF conflict on MDL locks between: DROP TABLE t2 and UPDATE on t1 +# with t2 referencing t1 +# + +--source include/galera_cluster.inc +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc + +# +# Setup +# +--connection node_2 +SET GLOBAL wsrep_slave_threads=2; + +CREATE TABLE t1 ( + id INTEGER PRIMARY KEY, + f2 INTEGER); + +CREATE TABLE t2 ( + f1 INT PRIMARY KEY, + t1_id INT NOT NULL, + f2 INTEGER NOT NULL, + KEY key_t1_id(t1_id), + CONSTRAINT key_t1_id FOREIGN KEY (t1_id) REFERENCES t1 (id) ON UPDATE CASCADE ON DELETE CASCADE); + +INSERT INTO t1 VALUES (1,0); +INSERT INTO t1 VALUES (2,0); + +INSERT INTO t2 VALUES (1,1,1234); +INSERT INTO t2 VALUES (2,2,1234); + +# +# DROP TABLE t2 and wait for it to reach node_2 +# +--connection node_2 +SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi'; + +--connection node_1 +DROP TABLE t2; + +--connection node_2 +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached"; + +SET SESSION wsrep_sync_wait = 0; +--let $expected_apply_waits = `SELECT VARIABLE_VALUE+1 FROM performance_schema.global_status WHERE VARIABLE_NAME = 'wsrep_apply_waits'` + +# +# Issue a UPDATE to table that references t1 +# Notice that we update field f2, not the primary key, +# and not foreign key. Bug does not manifest if we update +# one of those fields (because FK keys appended in those cases). +# +--connection node_1 +UPDATE t1 SET f2 = 1 WHERE id=2; + + +# +# Expect the UPDATE to depend on the DROP, +# therefore it should wait for the DROP to +# finish before it can be applied. +# If bug is present, expect the wait condition +# to timeout and when the UPDATE applies, it +# will be granted a MDL lock of type SHARED_READ +# for table t1. When resumed, the DROP TABLE will +# also try to MDL lock t1, causing a BF-BF conflict +# on that MDL lock. +# +--connection node_2 +--let $wait_condition = SELECT VARIABLE_VALUE = $expected_apply_waits FROM performance_schema.global_status WHERE VARIABLE_NAME = 'wsrep_apply_waits'; +--source include/wait_condition.inc +SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi'; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; + + +# +# Cleanup +# +SET GLOBAL DEBUG_DBUG="RESET"; +# SET DEBUG_SYNC = 'RESET'; + +SET GLOBAL wsrep_slave_threads=DEFAULT; + +DROP TABLE t1; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index bc5bf04591d7f..d2ff98bbefa79 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5049,10 +5049,10 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt) for (TABLE_LIST *table= all_tables; table; table= table->next_global) { if (!lex->tmp_table() && - (!thd->is_current_stmt_binlog_format_row() || - !is_temporary_table(table))) + (!thd->is_current_stmt_binlog_format_row() || + !is_temporary_table(table))) { - WSREP_TO_ISOLATION_BEGIN(NULL, NULL, all_tables); + WSREP_TO_ISOLATION_BEGIN_DROP(NULL, NULL, all_tables); break; } } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 4580f7ae9829f..43a719ffa49ae 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -12313,12 +12313,16 @@ bool Sql_cmd_create_table_like::execute(THD *thd) wsrep_check_sequence(thd, lex->create_info.seq_create_info, used_engine)) DBUG_RETURN(true); - WSREP_TO_ISOLATION_BEGIN_ALTER(create_table->db.str, create_table->table_name.str, - first_table, &alter_info, NULL, &create_info) + wsrep::key_array keys; + if (!wsrep_append_fk_parent_table(thd, create_table, &keys)) { - WSREP_WARN("CREATE TABLE isolation failure"); - res= true; - goto end_with_restore_list; + WSREP_TO_ISOLATION_BEGIN_ALTER(create_table->db.str, create_table->table_name.str, + first_table, &alter_info, &keys, &create_info) + { + WSREP_WARN("CREATE TABLE isolation failure"); + res= true; + goto end_with_restore_list; + } } } // check_engine will set db_type to NULL if e.g. TEMPORARY is diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 6997e5b777018..a97d8645bf706 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1721,6 +1721,83 @@ static void wsrep_keys_free(wsrep_key_arr_t* key_arr) key_arr->keys_len= 0; } +/*! + * @param thd thread + * @param tables list of tables + * @param keys prepared keys + + * @return 0 if parent table append was successful, non-zero otherwise. +*/ +bool +wsrep_append_fk_parent_single_table(THD* thd, TABLE_LIST* tables, + wsrep::key_array* keys) +{ + DBUG_ENTER("wsrep_append_fk_parent_single_table"); + bool fail= false; + TABLE_LIST *table; + + for (table= tables; table; table= table->next_local) + { + if (is_temporary_table(table)) + { + WSREP_DEBUG("Temporary table %s.%s already opened query=%s", table->db.str, + table->table_name.str, wsrep_thd_query(thd)); + DBUG_RETURN(false); + } + } + + uint counter; + if (open_tables(thd, &tables, &counter, MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)) + { + WSREP_DEBUG("Unable to open table for FK checks for %s", wsrep_thd_query(thd)); + fail= true; + goto exit; + } + + for (table= tables; table; table= table->next_local) + { + if (!is_temporary_table(table) && table->table) + { + FOREIGN_KEY_INFO *f_key_info; + List f_key_list; + + table->table->file->get_foreign_key_list(thd, &f_key_list); + List_iterator_fast it(f_key_list); + while ((f_key_info=it++)) + { + keys->push_back(wsrep_prepare_key_for_toi(f_key_info->referenced_db->str, + f_key_info->referenced_table->str, + wsrep::key::shared)); + + TABLE_LIST *tl_tmp= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); + tl_tmp->init_one_table(f_key_info->referenced_db, + f_key_info->referenced_table, 0, TL_READ); + table->next_local = tl_tmp; + if (wsrep_append_fk_parent_single_table(thd, table->next_local, keys)) + { + fail= true; + goto exit; + } + } + } else { + DBUG_RETURN(false); + } + } + +exit: + if (!fail) + { + mysql_mutex_lock(&thd->LOCK_thd_kill); + if (thd->killed) + { + fail= true; + } + mysql_mutex_unlock(&thd->LOCK_thd_kill); + } + + DBUG_RETURN(fail); +} + /*! * @param thd thread * @param tables list of tables @@ -2083,14 +2160,41 @@ wsrep::key wsrep_prepare_key_for_toi(const char* db, const char* table, return ret; } -wsrep::key_array -wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db, - const Alter_info* alter_info) +void +wsrep_prepare_keys_for_drop_table_fk(THD* thd, const char* db_name, + const char* table_name, + wsrep::key_array &ret) + +{ + ret.push_back(wsrep_prepare_key_for_toi(db_name, table_name, + wsrep::key::exclusive)); + thd->release_transactional_locks(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + TABLE_LIST *table; + TABLE_LIST *tl_tmp= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); + LEX_CSTRING db_name_lex = {db_name, strlen(db_name)}; + LEX_CSTRING table_name_lex = {table_name, strlen(table_name)}; + tl_tmp->init_one_table(&db_name_lex, &table_name_lex, 0, TL_READ); + wsrep_append_fk_parent_single_table(thd, tl_tmp, &ret); + + close_thread_tables(thd); + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + + for (table= tl_tmp; table; table= table->next_local) + { + table->table= NULL; + table->next_global= NULL; + table->mdl_request.ticket= NULL; + } +} + +void +wsrep_prepare_keys_for_alter_add_fk(THD* thd, const char* child_table_db, + List_iterator &key_iterator, + wsrep::key_array &ret) { - wsrep::key_array ret; Key *key; - List_iterator key_iterator(const_cast(alter_info)->key_list); while ((key= key_iterator++)) { if (key->type == Key::FOREIGN_KEY) @@ -2104,39 +2208,77 @@ wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db, } ret.push_back(wsrep_prepare_key_for_toi(db_name, table_name, wsrep::key::exclusive)); + thd->release_transactional_locks(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + TABLE_LIST *table; + TABLE_LIST *tl_tmp= (TABLE_LIST *) thd->alloc(sizeof(TABLE_LIST)); + LEX_CSTRING db_name_lex = {db_name, strlen(db_name)}; + tl_tmp->init_one_table(&db_name_lex, &fk_key->ref_table, 0, TL_READ); + wsrep_append_fk_parent_single_table(thd, tl_tmp, &ret); + + close_thread_tables(thd); + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + + for (table= tl_tmp; table; table= table->next_local) + { + table->table= NULL; + table->next_global= NULL; + table->mdl_request.ticket= NULL; + } } } - return ret; } -wsrep::key_array wsrep_prepare_keys_for_toi(const char *db, +wsrep::key_array wsrep_prepare_keys_for_toi(THD* thd, const char *db, const char *table, const TABLE_LIST *table_list, const Alter_info *alter_info, - const wsrep::key_array *fk_tables) + const wsrep::key_array *fk_tables, + bool is_drop_table_enable= false) { wsrep::key_array ret; + if (db || table) { ret.push_back(wsrep_prepare_key_for_toi(db, table, wsrep::key::exclusive)); } + for (const TABLE_LIST* table= table_list; table; table= table->next_global) { - ret.push_back(wsrep_prepare_key_for_toi(table->db.str, table->table_name.str, + ret.push_back(wsrep_prepare_key_for_toi(table->db.str, + table->table_name.str, wsrep::key::exclusive)); } + if (alter_info) { - wsrep::key_array fk(wsrep_prepare_keys_for_alter_add_fk(table_list->db.str, alter_info)); + wsrep::key_array fk; + List_iterator key_iterator( + const_cast(alter_info)->key_list); + wsrep_prepare_keys_for_alter_add_fk(thd, table_list->db.str, key_iterator, + fk); + if (!fk.empty()) + { + ret.insert(ret.end(), fk.begin(), fk.end()); + } + } + + if (is_drop_table_enable) + { + wsrep::key_array fk; + wsrep_prepare_keys_for_drop_table_fk(thd, table_list->db.str, + table_list->table_name.str, fk); if (!fk.empty()) { ret.insert(ret.end(), fk.begin(), fk.end()); } } + if (fk_tables && !fk_tables->empty()) { ret.insert(ret.end(), fk_tables->begin(), fk_tables->end()); } + return ret; } @@ -2702,7 +2844,8 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, const TABLE_LIST *table_list, const Alter_info *alter_info, const wsrep::key_array *fk_tables, - const HA_CREATE_INFO *create_info) + const HA_CREATE_INFO *create_info, + bool is_drop_table_enable= false) { DBUG_ASSERT(wsrep_OSU_method_get(thd) == WSREP_OSU_TOI); @@ -2733,7 +2876,8 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, struct wsrep_buf buff= { buf, buf_len }; wsrep::key_array key_array= - wsrep_prepare_keys_for_toi(db, table, table_list, alter_info, fk_tables); + wsrep_prepare_keys_for_toi(thd, db, table, table_list, alter_info, + fk_tables, is_drop_table_enable); if (thd->has_read_only_protection()) { @@ -2940,7 +3084,8 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, const TABLE_LIST* table_list, const Alter_info *alter_info, const wsrep::key_array *fk_tables, - const HA_CREATE_INFO *create_info) + const HA_CREATE_INFO *create_info, + bool is_drop_table_enable) { mysql_mutex_lock(&thd->LOCK_thd_kill); const killed_state killed = thd->killed; @@ -3030,7 +3175,7 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, switch (wsrep_OSU_method_get(thd)) { case WSREP_OSU_TOI: ret= wsrep_TOI_begin(thd, db_, table_, table_list, alter_info, fk_tables, - create_info); + create_info, is_drop_table_enable); break; case WSREP_OSU_RSU: ret= wsrep_RSU_begin(thd, db_, table_); diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index a2d0b71fba6b1..9b58133fb8a0f 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -241,6 +241,12 @@ void WSREP_LOG(void (*fun)(const char* fmt, ...), const char* fmt, ...); if (WSREP_ON && WSREP(thd) && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) \ goto wsrep_error_label; +#define WSREP_TO_ISOLATION_BEGIN_DROP(db_, table_, table_list_) \ + if (WSREP_ON && WSREP(thd) && \ + wsrep_to_isolation_begin(thd, db_, table_, \ + table_list_, nullptr, nullptr, nullptr, true))\ + goto wsrep_error_label; + #define WSREP_TO_ISOLATION_BEGIN_CREATE(db_, table_, table_list_, create_info_) \ if (WSREP_ON && WSREP(thd) && \ wsrep_to_isolation_begin(thd, db_, table_, \ @@ -353,7 +359,8 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, const TABLE_LIST* table_list, const Alter_info* alter_info= nullptr, const wsrep::key_array *fk_tables= nullptr, - const HA_CREATE_INFO* create_info= nullptr); + const HA_CREATE_INFO* create_info= nullptr, + bool is_drop_table_enable= false); bool wsrep_should_replicate_ddl(THD* thd, const handlerton *db_type); bool wsrep_should_replicate_ddl_iterate(THD* thd, const TABLE_LIST* table_list);