diff --git a/mysql-test/suite/galera/r/galera_multi_level_fk_ddl_insert.result b/mysql-test/suite/galera/r/galera_multi_level_fk_ddl_insert.result new file mode 100644 index 0000000000000..aa120e87cb9af --- /dev/null +++ b/mysql-test/suite/galera/r/galera_multi_level_fk_ddl_insert.result @@ -0,0 +1,364 @@ +connection node_2; +connection node_1; +# +# 1. BF-BF conflict on MDL locks between: DROP TABLE t4 and INSERT t1 +# with foreign key references as below: +# - t1<-t2<-t3<-t4 +# +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 +); +CREATE TABLE t4 ( +id INT PRIMARY KEY, +t3_id INT NOT NULL, +f2 INTEGER NOT NULL, +KEY key_t3_id(t3_id), +CONSTRAINT key_t3_id FOREIGN KEY (t3_id) REFERENCES t3 (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); +INSERT INTO t4 VALUES (1,1,1234); +INSERT INTO t4 VALUES (2,2,1234); +connection node_2; +SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi'; +connection node_1; +DROP TABLE t4; +connection node_2; +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached"; +SET SESSION wsrep_sync_wait = 0; +connection node_1; +SET GLOBAL DEBUG_DBUG = '+d,wsrep_print_foreign_keys_table'; +START TRANSACTION; +INSERT INTO t1 VALUES (3,0); +COMMIT; +connection node_2; +SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi'; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL DEBUG_DBUG = ""; +SET GLOBAL wsrep_slave_threads=DEFAULT; +connection node_1; +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL DEBUG_DBUG = ""; +SET GLOBAL wsrep_slave_threads=DEFAULT; +connection node_1; +include/assert_grep.inc [Foreign key referenced table found: 2 tables] +include/assert_grep.inc [Foreign key referenced table found: test.t2] +include/assert_grep.inc [Foreign key referenced table found: test.t3] +connection node_2; +select * from t1; +id f2 +1 0 +2 0 +3 0 +select * from t2; +id t1_id f2 +1 1 1234 +2 2 1234 +select * from t3; +id t2_id f2 +1 1 1234 +2 2 1234 +select * from t4; +ERROR 42S02: Table 'test.t4' doesn't exist +connection node_1; +select * from t1; +id f2 +1 0 +2 0 +3 0 +select * from t2; +id t1_id f2 +1 1 1234 +2 2 1234 +select * from t3; +id t2_id f2 +1 1 1234 +2 2 1234 +select * from t4; +ERROR 42S02: Table 'test.t4' doesn't exist +DROP TABLE t3, t2, t1; +# +# 2. BF-BF conflict on MDL locks between: +# ALTER TABLE t4 (whose parent table are t4 -> t3 -> t2 -> t1), and +# INSERT on t1. +# +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 +); +CREATE TABLE t4 ( +id INT PRIMARY KEY, +t3_id INT NOT NULL, +f2 INTEGER NOT NULL, +KEY key_t3_id(t3_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); +INSERT INTO t3 VALUES (1,1,1234); +INSERT INTO t3 VALUES (2,2,1234); +INSERT INTO t4 VALUES (1,1,1234); +INSERT INTO t4 VALUES (2,2,1234); +connection node_2; +SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi'; +connection node_1; +ALTER TABLE t4 ADD CONSTRAINT key_t3_id FOREIGN KEY (t3_id) REFERENCES t3 (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; +SET GLOBAL DEBUG_DBUG = '+d,wsrep_print_foreign_keys_table'; +START TRANSACTION; +INSERT INTO t1 VALUES (3,0); +COMMIT; +connection node_2; +SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi'; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL DEBUG_DBUG = ""; +SET GLOBAL wsrep_slave_threads=DEFAULT; +connection node_1; +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL DEBUG_DBUG = ""; +SET GLOBAL wsrep_slave_threads=DEFAULT; +connection node_1; +include/assert_grep.inc [Foreign key referenced table found: 3 tables] +include/assert_grep.inc [Foreign key referenced table found: test.t2] +include/assert_grep.inc [Foreign key referenced table found: test.t3] +include/assert_grep.inc [Foreign key referenced table found: test.t4] +connection node_2; +select * from t1; +id f2 +1 0 +2 0 +3 0 +select * from t2; +id t1_id f2 +1 1 1234 +2 2 1234 +select * from t3; +id t2_id f2 +1 1 1234 +2 2 1234 +connection node_1; +select * from t1; +id f2 +1 0 +2 0 +3 0 +select * from t2; +id t1_id f2 +1 1 1234 +2 2 1234 +select * from t3; +id t2_id f2 +1 1 1234 +2 2 1234 +DROP TABLE t4, t3, t2, t1; +# +# 3. BF-BF conflict on MDL locks between: +# CREATE TABLE t3 (whose parent table are t4 -> t3 -> t2 -> t1), and +# INSERT on t1. +# +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; +CREATE TABLE t4 (id INT PRIMARY KEY, t3_id INT NOT NULL, f2 INTEGER NOT NULL, KEY key_t3_id(t3_id), CONSTRAINT key_t3_id FOREIGN KEY (t3_id) REFERENCES t3 (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; +SET GLOBAL DEBUG_DBUG = '+d,wsrep_print_foreign_keys_table'; +START TRANSACTION; +INSERT INTO t1 VALUES (3,0); +COMMIT; +connection node_2; +SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi'; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL DEBUG_DBUG = ""; +SET GLOBAL wsrep_slave_threads=DEFAULT; +connection node_1; +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL DEBUG_DBUG = ""; +SET GLOBAL wsrep_slave_threads=DEFAULT; +connection node_1; +include/assert_grep.inc [Foreign key referenced table found: 2 tables] +include/assert_grep.inc [Foreign key referenced table found: test.t2] +include/assert_grep.inc [Foreign key referenced table found: test.t3] +include/assert_grep.inc [Foreign key referenced table found: test.t4] +connection node_2; +select * from t1; +id f2 +1 0 +2 0 +3 0 +select * from t2; +id t1_id f2 +1 1 1234 +2 2 1234 +select * from t3; +id t2_id f2 +1 1 1234 +2 2 1234 +connection node_1; +select * from t1; +id f2 +1 0 +2 0 +3 0 +select * from t2; +id t1_id f2 +1 1 1234 +2 2 1234 +select * from t3; +id t2_id f2 +1 1 1234 +2 2 1234 +DROP TABLE t4, t3, t2, t1; +# +# 4. BF-BF conflict on MDL locks between: +# OPTIMIZE TABLE t2 (whose parent table are t2 -> t1), and +# INSERT t1. +# +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; +OPTIMIZE TABLE t3; +Table Op Msg_type Msg_text +test.t3 optimize note Table does not support optimize, doing recreate + analyze instead +test.t3 optimize status OK +connection node_2; +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached"; +SET SESSION wsrep_sync_wait = 0; +connection node_1; +SET GLOBAL DEBUG_DBUG = '+d,wsrep_print_foreign_keys_table'; +START TRANSACTION; +INSERT INTO t1 VALUES (3,0); +COMMIT; +connection node_2; +SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi'; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL DEBUG_DBUG = ""; +SET GLOBAL wsrep_slave_threads=DEFAULT; +connection node_1; +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL DEBUG_DBUG = ""; +SET GLOBAL wsrep_slave_threads=DEFAULT; +connection node_1; +include/assert_grep.inc [Foreign key referenced table found: 1 tables] +include/assert_grep.inc [Foreign key referenced table found: test.t2] +include/assert_grep.inc [Foreign key referenced table found: test.t3] +connection node_2; +select * from t1; +id f2 +1 0 +2 0 +3 0 +select * from t2; +id t1_id f2 +1 1 1234 +2 2 1234 +connection node_1; +select * from t1; +id f2 +1 0 +2 0 +3 0 +select * from t2; +id t1_id f2 +1 1 1234 +2 2 1234 +DROP TABLE t3, t2, t1; diff --git a/mysql-test/suite/galera/t/galera_multi_level_fk_ddl_insert.test b/mysql-test/suite/galera/t/galera_multi_level_fk_ddl_insert.test new file mode 100644 index 0000000000000..78e5054a7cd11 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_multi_level_fk_ddl_insert.test @@ -0,0 +1,454 @@ +# +# BF-BF conflict on MDL locks between DDL and insert query +# when multi-level foreign key like t3 -> t2 -> t1 +# are present. +# +# If bug is present, expect the wait condition +# to timeout and when the INSERT applies, it +# will be granted a MDL lock of type SHARED_READ +# for table t1. When resumed, the DROP/ALTER/CREATE/OPTIMIZE +# TABLE will also try to MDL lock t1, causing a BF-BF conflict +# on that MDL lock. + +--source include/galera_cluster.inc +--source include/have_innodb.inc +--source include/have_debug.inc +--source include/have_debug_sync.inc + +--echo # +--echo # 1. BF-BF conflict on MDL locks between: DROP TABLE t4 and INSERT t1 +--echo # with foreign key references as below: +--echo # - t1<-t2<-t3<-t4 +--echo # + +# +# 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 +); + +CREATE TABLE t4 ( + id INT PRIMARY KEY, + t3_id INT NOT NULL, + f2 INTEGER NOT NULL, + KEY key_t3_id(t3_id), + CONSTRAINT key_t3_id FOREIGN KEY (t3_id) REFERENCES t3 (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); + +INSERT INTO t4 VALUES (1,1,1234); +INSERT INTO t4 VALUES (2,2,1234); + + +--let $fk_parent_query = DROP TABLE t4 +--let $fk_child_query = INSERT INTO t1 VALUES (3,0) +--let $fk_mdl_lock_num = 3 +--source galera_multi_level_foreign_key.inc + + +# +# Verify Foreign key for referenced table added. +# +--connection node_1 +--let assert_text= Foreign key referenced table found: 2 tables +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 2 +--let assert_select= Foreign key referenced table found: +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + +--let assert_text= Foreign key referenced table found: test.t2 +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 1 +--let assert_select= Foreign key referenced table found: test.t2 +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + +--let assert_text= Foreign key referenced table found: test.t3 +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 1 +--let assert_select= Foreign key referenced table found: test.t3 +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + + +# +# Verify insert and drop table has succeded. +# +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 3 FROM test.t1 +--source include/wait_condition.inc + +select * from t1; +select * from t2; +select * from t3; +--error ER_NO_SUCH_TABLE +select * from t4; + +--connection node_1 +select * from t1; +select * from t2; +select * from t3; +--error ER_NO_SUCH_TABLE +select * from t4; + + +# +# Cleanup +# +DROP TABLE t3, t2, t1; + + +--echo # +--echo # 2. BF-BF conflict on MDL locks between: +--echo # ALTER TABLE t4 (whose parent table are t4 -> t3 -> t2 -> t1), and +--echo # INSERT on t1. +--echo # + +# +# 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 +); + +CREATE TABLE t4 ( + id INT PRIMARY KEY, + t3_id INT NOT NULL, + f2 INTEGER NOT NULL, + KEY key_t3_id(t3_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); + +INSERT INTO t3 VALUES (1,1,1234); +INSERT INTO t3 VALUES (2,2,1234); + +INSERT INTO t4 VALUES (1,1,1234); +INSERT INTO t4 VALUES (2,2,1234); + + +# +# ALTER TABLE t3 and wait for it to reach node_2 +# +--let $fk_parent_query = ALTER TABLE t4 ADD CONSTRAINT key_t3_id FOREIGN KEY (t3_id) REFERENCES t3 (id) ON UPDATE CASCADE ON DELETE CASCADE +# +# Issue a INSERT to table that references t1 +# +--let $fk_child_query = INSERT INTO t1 VALUES (3,0) +--let $fk_mdl_lock_num = 4 +--source galera_multi_level_foreign_key.inc + + +# +# Verify Foreign key for referenced table added. +# +--connection node_1 +--let assert_text= Foreign key referenced table found: 3 tables +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 5 +--let assert_select= Foreign key referenced table found: +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + +--let assert_text= Foreign key referenced table found: test.t2 +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 2 +--let assert_select= Foreign key referenced table found: test.t2 +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + +--let assert_text= Foreign key referenced table found: test.t3 +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 2 +--let assert_select= Foreign key referenced table found: test.t3 +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + +--let assert_text= Foreign key referenced table found: test.t4 +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 1 +--let assert_select= Foreign key referenced table found: test.t4 +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + + +# +# Verify insert has succeded. +# +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 3 FROM test.t1 +--source include/wait_condition.inc + +select * from t1; +select * from t2; +select * from t3; + +--connection node_1 +select * from t1; +select * from t2; +select * from t3; + + +# +# Cleanup +# +DROP TABLE t4, t3, t2, t1; + + +--echo # +--echo # 3. BF-BF conflict on MDL locks between: +--echo # CREATE TABLE t3 (whose parent table are t4 -> t3 -> t2 -> t1), and +--echo # INSERT on t1. +--echo # + +# +# 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); + + +--let $fk_parent_query = CREATE TABLE t4 (id INT PRIMARY KEY, t3_id INT NOT NULL, f2 INTEGER NOT NULL, KEY key_t3_id(t3_id), CONSTRAINT key_t3_id FOREIGN KEY (t3_id) REFERENCES t3 (id) ON UPDATE CASCADE ON DELETE CASCADE) +--let $fk_child_query = INSERT INTO t1 VALUES (3,0) +--let $fk_mdl_lock_num = 4 +--source galera_multi_level_foreign_key.inc + + +# +# Verify Foreign key for referenced table added. +# +--connection node_1 +--let assert_text= Foreign key referenced table found: 2 tables +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 8 +--let assert_select= Foreign key referenced table found: +--let $assert_only_after = CURRENT_TEST: +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + +--let assert_text= Foreign key referenced table found: test.t2 +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 3 +--let assert_select= Foreign key referenced table found: test.t2 +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + +--let assert_text= Foreign key referenced table found: test.t3 +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 3 +--let assert_select= Foreign key referenced table found: test.t3 +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + +--let assert_text= Foreign key referenced table found: test.t4 +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 2 +--let assert_select= Foreign key referenced table found: test.t4 +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + + +# +# Verify insert has succeded. +# +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 3 FROM test.t1 +--source include/wait_condition.inc + +select * from t1; +select * from t2; +select * from t3; + +--connection node_1 +select * from t1; +select * from t2; +select * from t3; + + +# +# Cleanup +# +DROP TABLE t4, t3, t2, t1; + + +--echo # +--echo # 4. BF-BF conflict on MDL locks between: +--echo # OPTIMIZE TABLE t2 (whose parent table are t2 -> t1), and +--echo # INSERT t1. +--echo # + +# +# 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); + + +--let $fk_parent_query = OPTIMIZE TABLE t3 +--let $fk_child_query = INSERT INTO t1 VALUES (3,0) +--let $fk_mdl_lock_num = 3 +--source galera_multi_level_foreign_key.inc + + +# +# Verify Foreign key for referenced table added. +# +--connection node_1 +--let assert_text= Foreign key referenced table found: 1 tables +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 10 +--let assert_select= Foreign key referenced table found: +--let $assert_only_after = CURRENT_TEST: +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + +--let assert_text= Foreign key referenced table found: test.t2 +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 4 +--let assert_select= Foreign key referenced table found: test.t2 +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + +--let assert_text= Foreign key referenced table found: test.t3 +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 4 +--let assert_select= Foreign key referenced table found: test.t3 +--let assert_only_after= CURRENT_TEST: galera.galera_multi_level_fk_ddl_insert +--source include/assert_grep.inc + + +# +# Verify insert has succeded. +# +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 3 FROM test.t1 +--source include/wait_condition.inc + +select * from t1; +select * from t2; + +--connection node_1 +select * from t1; +select * from t2; + + +# +# Cleanup +# +DROP TABLE t3, t2, t1; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1a4fa315c3c96..18db6ece4bb0b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3678,6 +3678,15 @@ bool extend_table_list(THD *thd, TABLE_LIST *tables, (tables->updating && tables->lock_type >= TL_FIRST_WRITE) || thd->lex->default_used; +#ifdef WITH_WSREP + if (WSREP(thd) && !thd->wsrep_applier && + wsrep_is_active(thd) && + (sql_command_flags[thd->lex->sql_command] & CF_INSERTS_DATA) && + tables->lock_type == TL_READ) { + maybe_need_prelocking= true; + } +#endif + if (thd->locked_tables_mode <= LTM_LOCK_TABLES && ! has_prelocking_list && maybe_need_prelocking) { @@ -4805,6 +4814,7 @@ prepare_fk_prelocking_list(THD *thd, Query_tables_list *prelocking_ctx, Query_arena *arena, backup; TABLE *table= table_list->table; bool error= FALSE; + bool override_fk_ignore_table= FALSE; if (!table->file->referenced_by_foreign_key()) DBUG_RETURN(FALSE); @@ -4821,6 +4831,41 @@ prepare_fk_prelocking_list(THD *thd, Query_tables_list *prelocking_ctx, *need_prelocking= TRUE; +#ifdef WITH_WSREP + /* + MDL is enough for read-only FK checks, we don't need the table, + but on galera applier node lock_type is set to TL_FIRST_WRITE and not + TL_READ, which is due to Write_rows_log_event event logged for INSERT + is used to record insert, update and delete + (Write_rows_log_event::get_trg_event_map()), and therefore all child + tables will be opened and MDL locks will be taken on applier node, while + opening of multiple child tables is ignored on write node by setting + open_strategy= OPEN_STUB in init_one_table_for_prelocking(). + This difference in write and applier node can result in MDL deadlock. + + Tables with foreign keys: t1<-t2<-t3<-t4 + Conflicting transactions: INSERT t1 and DROP TABLE t4 + + Wsrep certification keys taken on write node: + - for INSERT t1: t1 and t2 + - for DROP TABLE t4: t4 + + On applier node MDL deadlock happened between two transaction because + MDL locks for INSERT t1 were taken on t1, t2, t3 and t4, which conflicted + with MDL lock on t4 taken by DROP TABLE t4. + + The Wsrep certification keys does helps in resolving in transactions + getting MDL deadlock. But to generate Wsrep certification keys it needs + to open and take MDL locks on all child tables. So that conflicting + transactions can be prioritize and scheduled. + */ + if (WSREP(thd) && !thd->wsrep_applier && + wsrep_is_active(thd) && + (sql_command_flags[thd->lex->sql_command] & CF_INSERTS_DATA)) { + override_fk_ignore_table= TRUE; + } +#endif // WITH_WSREP + while ((fk= fk_list_it++)) { // FK_OPTION_RESTRICT and FK_OPTION_NO_ACTION only need read access @@ -4844,13 +4889,15 @@ prepare_fk_prelocking_list(THD *thd, Query_tables_list *prelocking_ctx, TABLE_LIST::PRELOCK_FK, table_list->belong_to_view, op, &prelocking_ctx->query_tables_last, - table_list->for_insert_data); + table_list->for_insert_data, + override_fk_ignore_table); #ifdef WITH_WSREP /* Append table level shared key for the referenced/foreign table for: - statement that updates existing rows (UPDATE, multi-update) - statement that deletes existing rows (DELETE, DELETE_MULTI) + - statement that inserts new rows (INSERT, REPLACE, LOAD, ALTER TABLE) This is done to avoid potential MDL conflicts with concurrent DDLs. */ if (wsrep_foreign_key_append(thd, fk)) @@ -4895,8 +4942,10 @@ bool DML_prelocking_strategy::handle_table(THD *thd, TABLE *table= table_list->table; /* We rely on a caller to check that table is going to be changed. */ DBUG_ASSERT(table_list->lock_type >= TL_FIRST_WRITE || + ((sql_command_flags[thd->lex->sql_command] & CF_INSERTS_DATA) + && table_list->lock_type == TL_READ) || thd->lex->default_used); - + if (table_list->trg_event_map) { if (table->triggers) diff --git a/sql/table.h b/sql/table.h index b68f93f5e5c05..c28fe39593365 100644 --- a/sql/table.h +++ b/sql/table.h @@ -2319,7 +2319,8 @@ struct TABLE_LIST TABLE_LIST *belong_to_view_arg, uint8 trg_event_map_arg, TABLE_LIST ***last_ptr, - my_bool insert_data) + my_bool insert_data, + my_bool override_fk_ignore_table= FALSE) { init_one_table(db_arg, table_name_arg, alias_arg, lock_type_arg); @@ -2331,7 +2332,8 @@ struct TABLE_LIST belong_to_view= belong_to_view_arg; trg_event_map= trg_event_map_arg; /* MDL is enough for read-only FK checks, we don't need the table */ - if (prelocking_type == PRELOCK_FK && lock_type < TL_FIRST_WRITE) + if (prelocking_type == PRELOCK_FK && lock_type < TL_FIRST_WRITE && + !override_fk_ignore_table) open_strategy= OPEN_STUB; **last_ptr= this; diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 010cab56614ce..edd5cc62a9e18 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -4059,7 +4059,7 @@ bool wsrep_foreign_key_append(THD *thd, FOREIGN_KEY_INFO *fk) if (WSREP(thd) && !thd->wsrep_applier && wsrep_is_active(thd) && (sql_command_flags[thd->lex->sql_command] & - (CF_UPDATES_DATA | CF_DELETES_DATA))) + (CF_UPDATES_DATA | CF_DELETES_DATA | CF_INSERTS_DATA))) { wsrep::key key(wsrep::key::shared); key.append_key_part(fk->foreign_db->str, fk->foreign_db->length);