diff --git a/include/wsrep.h b/include/wsrep.h index fde5c5226e732..65cceba1065f7 100644 --- a/include/wsrep.h +++ b/include/wsrep.h @@ -27,10 +27,10 @@ if (WSREP_ON && WSREP(thd) && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) \ goto wsrep_error_label; -#define WSREP_TO_ISOLATION_BEGIN_ALTER(db_, table_, table_list_, alter_info_) \ +#define WSREP_TO_ISOLATION_BEGIN_ALTER(db_, table_, table_list_, alter_info_, fk_tables_) \ if (WSREP(thd) && wsrep_thd_is_local(thd) && \ wsrep_to_isolation_begin(thd, db_, table_, \ - table_list_, alter_info_)) \ + table_list_, alter_info_, fk_tables_)) \ goto wsrep_error_label; #define WSREP_TO_ISOLATION_END \ @@ -46,6 +46,10 @@ if (WSREP(thd) && !thd->lex->no_write_to_binlog \ && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) goto wsrep_error_label; +#define WSREP_TO_ISOLATION_BEGIN_FK_TABLES(db_, table_, table_list_, fk_tables) \ + if (WSREP(thd) && !thd->lex->no_write_to_binlog \ + && wsrep_to_isolation_begin(thd, db_, table_, table_list_, NULL, fk_tables)) goto wsrep_error_label; + #define WSREP_DEBUG(...) \ if (wsrep_debug) WSREP_LOG(sql_print_information, ##__VA_ARGS__) #define WSREP_INFO(...) WSREP_LOG(sql_print_information, ##__VA_ARGS__) @@ -69,6 +73,7 @@ #define WSREP_ERROR(...) #define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_) do { } while(0) #define WSREP_TO_ISOLATION_BEGIN_ALTER(db_, table_, table_list_, alter_info_) +#define WSREP_TO_ISOLATION_BEGIN_FK_TABLES(db_, table_, table_list_, fk_tables_) #define WSREP_TO_ISOLATION_END #define WSREP_TO_ISOLATION_BEGIN_WRTCHK(db_, table_, table_list_) #define WSREP_SYNC_WAIT(thd_, before_) diff --git a/mysql-test/suite/galera/r/galera_ddl_fk_conflict.result b/mysql-test/suite/galera/r/galera_ddl_fk_conflict.result new file mode 100644 index 0000000000000..a39fa6a62192f --- /dev/null +++ b/mysql-test/suite/galera/r/galera_ddl_fk_conflict.result @@ -0,0 +1,216 @@ +connection node_2; +connection node_1; +connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1; +connection node_1a; +SET SESSION wsrep_sync_wait=0; +###################################################################### +# Test for OPTIMIZE +###################################################################### +###################################################################### +# +# Scenario #1: DML working on FK parent table BF aborted by DDL +# over child table +# +###################################################################### +connection node_1; +SET SESSION wsrep_sync_wait=0; +CREATE TABLE p (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p VALUES (1, 'INITIAL VALUE'); +INSERT INTO p VALUES (2, 'INITIAL VALUE'); +CREATE TABLE c (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p(pk)); +INSERT INTO c VALUES (1,1), (2,2); +connection node_1; +SET AUTOCOMMIT=ON; +START TRANSACTION; +UPDATE p SET f2 = 'TO DEADLOCK' WHERE pk = 1; +connection node_2; +SET SESSION wsrep_sync_wait=0; +OPTIMIZE TABLE c ; +Table Op Msg_type Msg_text +test.c optimize note Table does not support optimize, doing recreate + analyze instead +test.c optimize status OK +connection node_1; +COMMIT; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +###################################################################### +# +# Scenario #2: DML working on FK parent table tries to replicate, but +# fails in certification for earlier DDL on child table +# +###################################################################### +connection node_1; +BEGIN; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +OPTIMIZE TABLE c ; +Table Op Msg_type Msg_text +test.c optimize note Table does not support optimize, doing recreate + analyze instead +test.c optimize status OK +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +connection node_1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +DROP TABLE c; +DROP TABLE p; +###################################################################### +# Test for REPAIR +###################################################################### +###################################################################### +# +# Scenario #1: DML working on FK parent table BF aborted by DDL +# over child table +# +###################################################################### +connection node_1; +SET SESSION wsrep_sync_wait=0; +CREATE TABLE p (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p VALUES (1, 'INITIAL VALUE'); +INSERT INTO p VALUES (2, 'INITIAL VALUE'); +CREATE TABLE c (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p(pk)); +INSERT INTO c VALUES (1,1), (2,2); +connection node_1; +SET AUTOCOMMIT=ON; +START TRANSACTION; +UPDATE p SET f2 = 'TO DEADLOCK' WHERE pk = 1; +connection node_2; +SET SESSION wsrep_sync_wait=0; +REPAIR TABLE c ; +Table Op Msg_type Msg_text +test.c repair note The storage engine for the table doesn't support repair +connection node_1; +COMMIT; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +###################################################################### +# +# Scenario #2: DML working on FK parent table tries to replicate, but +# fails in certification for earlier DDL on child table +# +###################################################################### +connection node_1; +BEGIN; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +REPAIR TABLE c ; +Table Op Msg_type Msg_text +test.c repair note The storage engine for the table doesn't support repair +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +connection node_1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +DROP TABLE c; +DROP TABLE p; +###################################################################### +# Test for ALTER ENGINE=INNODB +###################################################################### +###################################################################### +# +# Scenario #1: DML working on FK parent table BF aborted by DDL +# over child table +# +###################################################################### +connection node_1; +SET SESSION wsrep_sync_wait=0; +CREATE TABLE p (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p VALUES (1, 'INITIAL VALUE'); +INSERT INTO p VALUES (2, 'INITIAL VALUE'); +CREATE TABLE c (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p(pk)); +INSERT INTO c VALUES (1,1), (2,2); +connection node_1; +SET AUTOCOMMIT=ON; +START TRANSACTION; +UPDATE p SET f2 = 'TO DEADLOCK' WHERE pk = 1; +connection node_2; +SET SESSION wsrep_sync_wait=0; +ALTER TABLE c ENGINE=INNODB; +connection node_1; +COMMIT; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +###################################################################### +# +# Scenario #2: DML working on FK parent table tries to replicate, but +# fails in certification for earlier DDL on child table +# +###################################################################### +connection node_1; +BEGIN; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +ALTER TABLE c ENGINE=INNODB; +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +connection node_1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +DROP TABLE c; +DROP TABLE p; diff --git a/mysql-test/suite/galera/t/galera_ddl_fk_conflict.inc b/mysql-test/suite/galera/t/galera_ddl_fk_conflict.inc new file mode 100644 index 0000000000000..18271d0b6412a --- /dev/null +++ b/mysql-test/suite/galera/t/galera_ddl_fk_conflict.inc @@ -0,0 +1,114 @@ +# +# Test for MDL BF-BF lock conflict +# There are some DDL statements, which take extensive MDL lock for +# a table referenced by foreign key constraint from the actual affetec table. +# This extensive MDL lock may cause MDL BF-BF confclict situations, if the +# FK parent table is not listed as certification key in the replication write set. +# i.e. if replication allows such DDL to apply in parallel with regular DML operating +# on the FK parent table. +# +# This test has two scenarios, where DML modifies FK parent table in node 1, +# and offending DDL for FK child table is sent from node 2. +# +# param: $table_admin_command +# DDL table command to test, script will build full SQL statement: +# $table_admin_command TABLE c; +# +# param: $table_admin_command_end +# Optional additional SQL syntax to end the SQL statement, if any +# $table_admin_command TABLE c $table_admin_command_end; +# +# scenario 1, can be used to test if a DDL statement causes such MDL locking vulnerability. +# call this test script with some table DDL command in $table_admin_command +# if scenario 1 passes (especially COMMIT does fail for ER_LOCK_DEADLOCK), +# then this particular DDL is vulnerable. scenraio 2 should fail for this DDL +# unless code has not been fixed to append parent table certification keys for it. +# + +--echo ###################################################################### +--echo # Test for $table_admin_command $table_admin_command_end +--echo ###################################################################### + + +--echo ###################################################################### +--echo # +--echo # Scenario #1: DML working on FK parent table BF aborted by DDL +--echo # over child table +--echo # +--echo ###################################################################### + +--connection node_1 +SET SESSION wsrep_sync_wait=0; + +CREATE TABLE p (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p VALUES (1, 'INITIAL VALUE'); +INSERT INTO p VALUES (2, 'INITIAL VALUE'); + +CREATE TABLE c (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p(pk)); +INSERT INTO c VALUES (1,1), (2,2); + +--connection node_1 +SET AUTOCOMMIT=ON; +START TRANSACTION; + +UPDATE p SET f2 = 'TO DEADLOCK' WHERE pk = 1; + +--connection node_2 +SET SESSION wsrep_sync_wait=0; +--eval $table_admin_command TABLE c $table_admin_command_end + +--connection node_1 +--error ER_LOCK_DEADLOCK +COMMIT; + +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; + +--connection node_2 +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; + +--echo ###################################################################### +--echo # +--echo # Scenario #2: DML working on FK parent table tries to replicate, but +--echo # fails in certification for earlier DDL on child table +--echo # +--echo ###################################################################### + +--connection node_1 +BEGIN; + +# Block the applier on node #1 and issue DDL on node 2 +--let $galera_sync_point = apply_monitor_slave_enter_sync +--source include/galera_set_sync_point.inc + +--connection node_2 +--eval $table_admin_command TABLE c $table_admin_command_end + +--connection node_1a +--source include/galera_wait_sync_point.inc +--source include/galera_clear_sync_point.inc +--let $expected_cert_failures = `SELECT VARIABLE_VALUE+1 FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_cert_failures'` + +--connection node_1 +UPDATE p SET f2 = 'TO DEADLOCK' WHERE pk = 1; +--send COMMIT + +--connection node_1a +--let $galera_sync_point = apply_monitor_slave_enter_sync +--source include/galera_signal_sync_point.inc + +--let $wait_condition = SELECT VARIABLE_VALUE = $expected_cert_failures FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_cert_failures' +--source include/wait_condition.inc + +--connection node_1 +--error ER_LOCK_DEADLOCK +--reap + +SELECT 'I deadlocked'; + +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; + +--connection node_2 +SELECT COUNT(*) AS EXPECT_2 FROM p WHERE f2 = 'INITIAL VALUE'; + +DROP TABLE c; +DROP TABLE p; diff --git a/mysql-test/suite/galera/t/galera_ddl_fk_conflict.test b/mysql-test/suite/galera/t/galera_ddl_fk_conflict.test new file mode 100644 index 0000000000000..a07006a8e49ce --- /dev/null +++ b/mysql-test/suite/galera/t/galera_ddl_fk_conflict.test @@ -0,0 +1,25 @@ +# +# MDL BF-BF lock conflict +# + +--source include/galera_cluster.inc +--source include/have_innodb.inc +--source include/have_debug_sync.inc +--source include/galera_have_debug_sync.inc + +--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1 +--connection node_1a +SET SESSION wsrep_sync_wait=0; + +--let $table_admin_command = OPTIMIZE +--source galera_ddl_fk_conflict.inc + +--let $table_admin_command = REPAIR +--source galera_ddl_fk_conflict.inc + +--let $table_admin_command = ALTER +--let $table_admin_command_end = ENGINE=INNODB +--source galera_ddl_fk_conflict.inc + +# CHECK and ANALYZE are not affected + diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 6201411d4aa51..ebd0e9b39aad8 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -32,7 +32,7 @@ #include "strfunc.h" #include "sql_admin.h" #include "sql_statistics.h" - +#include "wsrep_mysqld.h" /* Prepare, run and cleanup for mysql_recreate_table() */ static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list) @@ -419,6 +419,48 @@ static bool open_only_one_table(THD* thd, TABLE_LIST* table, return open_error; } +#ifdef WITH_WSREP + /* + OPTIMIZE, REPAIR and ALTER may take MDL locks not only for the affected table, but + also for the table referenced by foreign key constraint. + This wsrep_toi_replication() function handles TOI replication for OPTIMIZE and REPAIR + so that certification keys for potential FK parent tables are also appended in the + write set. + ALTER TABLE case is handled elsewhere. + */ +static bool wsrep_toi_replication(THD *thd, TABLE_LIST *tables) +{ + if (!WSREP(thd) || !WSREP_CLIENT(thd)) return false; + + LEX *lex= thd->lex; + /* only handle OPTIMIZE and REPAIR here */ + switch (lex->sql_command) + { + case SQLCOM_OPTIMIZE: + case SQLCOM_REPAIR: + break; + default: + return false; + } + + close_thread_tables(thd); + wsrep::key_array keys; + + wsrep_append_fk_parent_table(thd, tables, &keys); + + /* now TOI replication, with no locks held */ + if (keys.empty()) + { + WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, tables); + } else { + WSREP_TO_ISOLATION_BEGIN_FK_TABLES(NULL, NULL, tables, &keys); + } + return false; + + wsrep_error_label: + return true; +} +#endif /* WITH_WSREP */ /* RETURN VALUES @@ -487,6 +529,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, close_thread_tables(thd); for (table= tables; table; table= table->next_local) table->table= NULL; +#ifdef WITH_WSREP + if (wsrep_toi_replication(thd, tables)) + { + WSREP_INFO("wsrep TOI replication of has failed, skipping OPTIMIZE"); + goto err; + } +#endif /* WITH_WSREP */ for (table= tables; table; table= table->next_local) { @@ -1333,10 +1382,10 @@ bool Sql_cmd_analyze_table::execute(THD *thd) m_lex->first_select_lex()->table_list.first= first_table; m_lex->query_tables= first_table; -error: #ifdef WITH_WSREP -wsrep_error_label: -#endif + wsrep_error_label: +#endif /* WITH_WSREP */ +error: DBUG_RETURN(res); } @@ -1375,7 +1424,6 @@ bool Sql_cmd_optimize_table::execute(THD *thd) if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ - WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); res= (specialflag & SPECIAL_NO_NEW_FUNC) ? mysql_recreate_table(thd, first_table, true) : @@ -1394,9 +1442,6 @@ bool Sql_cmd_optimize_table::execute(THD *thd) m_lex->query_tables= first_table; error: -#ifdef WITH_WSREP -wsrep_error_label: -#endif DBUG_RETURN(res); } @@ -1411,7 +1456,6 @@ bool Sql_cmd_repair_table::execute(THD *thd) if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ - WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "repair", TL_WRITE, 1, MY_TEST(m_lex->check_opt.sql_flags & TT_USEFRM), @@ -1430,8 +1474,5 @@ bool Sql_cmd_repair_table::execute(THD *thd) m_lex->query_tables= first_table; error: -#ifdef WITH_WSREP -wsrep_error_label: -#endif DBUG_RETURN(res); } diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index 21c79a046a50f..cefd37811a2b6 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -470,6 +470,22 @@ bool Sql_cmd_alter_table::execute(THD *thd) if (check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX, FALSE)) DBUG_RETURN(TRUE); /* purecov: inspected */ +#ifdef WITH_WSREP + if (WSREP(thd) && WSREP_CLIENT(thd) && + (!thd->is_current_stmt_binlog_format_row() || + !thd->find_temporary_table(first_table))) + { + wsrep::key_array keys; + wsrep_append_fk_parent_table(thd, first_table, &keys); + + WSREP_TO_ISOLATION_BEGIN_ALTER((lex->name.str ? select_lex->db.str : NULL), + (lex->name.str ? lex->name.str : NULL), + first_table, &alter_info, &keys); + + thd->variables.auto_increment_offset = 1; + thd->variables.auto_increment_increment = 1; + } +#endif if (lex->name.str && !test_all_bits(priv, INSERT_ACL | CREATE_ACL)) { @@ -497,20 +513,6 @@ bool Sql_cmd_alter_table::execute(THD *thd) thd->work_part_info= 0; #endif -#ifdef WITH_WSREP - if (WSREP(thd) && - (!thd->is_current_stmt_binlog_format_row() || - !thd->find_temporary_table(first_table))) - { - WSREP_TO_ISOLATION_BEGIN_ALTER((lex->name.str ? select_lex->db.str : NULL), - (lex->name.str ? lex->name.str : NULL), - first_table, &alter_info); - - thd->variables.auto_increment_offset = 1; - thd->variables.auto_increment_increment = 1; - } -#endif - result= mysql_alter_table(thd, &select_lex->db, &lex->name, &create_info, first_table, diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index aaf17012118ca..304da7ec97956 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1181,6 +1181,54 @@ void wsrep_keys_free(wsrep_key_arr_t* key_arr) key_arr->keys_len= 0; } +void +wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* tables, wsrep::key_array* keys) +{ + if (!WSREP(thd) || !WSREP_CLIENT(thd)) return; + TABLE_LIST *table; + + thd->mdl_context.release_transactional_locks(); + uint counter; + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + + bool open_error= + open_tables(thd, &tables, &counter, MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL); + if (unlikely(open_error && (thd->killed || thd->is_error()))) + { + WSREP_WARN("unable to open table for FK checks in OPTIMIZE/REPAIR/ALTER processing"); + } + else + { + for (table= tables; table; table= table->next_local) + { + if (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++)) + { + WSREP_DEBUG("appended fkey %s", f_key_info->referenced_table->str); + keys->push_back(wsrep_prepare_key_for_toi(f_key_info->referenced_db->str, + f_key_info->referenced_table->str, + wsrep::key::shared)); + } + } + } + } + + /* close the table and release MDL locks */ + close_thread_tables(thd); + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + for (table= tables; table; table= table->next_local) + { + table->table= NULL; + table->mdl_request.ticket= NULL; + } +} + /*! * @param db Database string * @param table Table string @@ -1434,7 +1482,8 @@ wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db, wsrep::key_array wsrep_prepare_keys_for_toi(const char* db, const char* table, const TABLE_LIST* table_list, - Alter_info* alter_info) + Alter_info* alter_info, + wsrep::key_array* fk_tables) { wsrep::key_array ret; if (db || table) @@ -1454,8 +1503,13 @@ wsrep::key_array wsrep_prepare_keys_for_toi(const char* db, 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; } + /* * Construct Query_log_Event from thd query and serialize it * into buffer. @@ -1907,7 +1961,7 @@ static void wsrep_TOI_begin_failed(THD* thd, const wsrep_buf_t* /* const err */) */ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, const TABLE_LIST* table_list, - Alter_info* alter_info) + Alter_info* alter_info, wsrep::key_array* fk_tables) { DBUG_ASSERT(thd->variables.wsrep_OSU_method == WSREP_OSU_TOI); @@ -1935,7 +1989,7 @@ 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); + wsrep_prepare_keys_for_toi(db, table, table_list, alter_info, fk_tables); if (thd->has_read_only_protection()) { @@ -2048,7 +2102,7 @@ static void wsrep_RSU_end(THD *thd) int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, const TABLE_LIST* table_list, - Alter_info* alter_info) + Alter_info* alter_info, wsrep::key_array* fk_tables) { /* No isolation for applier or replaying threads. @@ -2100,7 +2154,7 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, { switch (thd->variables.wsrep_OSU_method) { case WSREP_OSU_TOI: - ret= wsrep_TOI_begin(thd, db_, table_, table_list, alter_info); + ret= wsrep_TOI_begin(thd, db_, table_, table_list, alter_info, fk_tables); 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 a717caa836e47..b0050a2ebae2b 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -212,6 +212,7 @@ wsrep_sync_wait_upto (THD* thd, wsrep_gtid_t* upto, int timeout); extern void wsrep_last_committed_id (wsrep_gtid_t* gtid); extern int wsrep_check_opts(); extern void wsrep_prepend_PATH (const char* path); +void wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* table, wsrep::key_array* keys); /* Other global variables */ extern wsrep_seqno_t wsrep_locked_seqno; @@ -357,7 +358,7 @@ struct TABLE_LIST; class Alter_info; int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, const TABLE_LIST* table_list, - Alter_info* alter_info= NULL); + Alter_info* alter_info= NULL, wsrep::key_array *fk_tables=NULL); void wsrep_to_isolation_end(THD *thd); @@ -476,6 +477,9 @@ void wsrep_deinit_server(); */ enum wsrep::streaming_context::fragment_unit wsrep_fragment_unit(ulong unit); +wsrep::key wsrep_prepare_key_for_toi(const char* db, const char* table, + enum wsrep::key::type type); + #else /* !WITH_WSREP */ /* These macros are needed to compile MariaDB without WSREP support