summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/wsrep.h9
-rw-r--r--mysql-test/suite/galera/r/galera_ddl_fk_conflict.result216
-rw-r--r--mysql-test/suite/galera/t/galera_ddl_fk_conflict.inc114
-rw-r--r--mysql-test/suite/galera/t/galera_ddl_fk_conflict.test25
-rw-r--r--sql/sql_admin.cc65
-rw-r--r--sql/sql_alter.cc30
-rw-r--r--sql/wsrep_mysqld.cc64
-rw-r--r--sql/wsrep_mysqld.h6
8 files changed, 495 insertions, 34 deletions
diff --git a/include/wsrep.h b/include/wsrep.h
index fde5c5226e7..65cceba1065 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 00000000000..a39fa6a6219
--- /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 00000000000..18271d0b641
--- /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 00000000000..a07006a8e49
--- /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 6201411d4aa..ebd0e9b39aa 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 @@ dbug_err:
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 21c79a046a5..cefd37811a2 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 aaf17012118..304da7ec979 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<FOREIGN_KEY_INFO> f_key_list;
+
+ table->table->file->get_foreign_key_list(thd, &f_key_list);
+ List_iterator_fast<FOREIGN_KEY_INFO> 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 @@ fail:
*/
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 a717caa836e..b0050a2ebae 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