diff --git a/include/mysql/service_wsrep.h b/include/mysql/service_wsrep.h index a1897d76dc169..a1110413575d4 100644 --- a/include/mysql/service_wsrep.h +++ b/include/mysql/service_wsrep.h @@ -123,6 +123,7 @@ extern struct wsrep_service_st { #define wsrep_thd_is_local(T) wsrep_service->wsrep_thd_is_local_func(T) #define wsrep_thd_self_abort(T) wsrep_service->wsrep_thd_self_abort_func(T) #define wsrep_thd_append_key(T,W,N,K) wsrep_service->wsrep_thd_append_key_func(T,W,N,K) +#define wsrep_thd_append_table_key(T,D,B,K) wsrep_service->wsrep_thd_append_table_key_func(T,D,B,K) #define wsrep_thd_client_state_str(T) wsrep_service->wsrep_thd_client_state_str_func(T) #define wsrep_thd_client_mode_str(T) wsrep_service->wsrep_thd_client_mode_str_func(T) #define wsrep_thd_transaction_state_str(T) wsrep_service->wsrep_thd_transaction_state_str_func(T) @@ -228,6 +229,11 @@ extern "C" int wsrep_thd_append_key(MYSQL_THD thd, int n_keys, enum Wsrep_service_key_type); +extern "C" int wsrep_thd_append_table_key(MYSQL_THD thd, + const char* db, + const char* table, + enum Wsrep_service_key_type); + extern const char* wsrep_sr_table_name_full; extern "C" const char* wsrep_get_sr_table_name(); diff --git a/mysql-test/suite/galera/r/MDEV-35018.result b/mysql-test/suite/galera/r/MDEV-35018.result new file mode 100644 index 0000000000000..2afa7c4d39723 --- /dev/null +++ b/mysql-test/suite/galera/r/MDEV-35018.result @@ -0,0 +1,38 @@ +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; +1 +connection node_1; +UPDATE t1 SET f2 = 1 WHERE id=2; +connection node_2; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; +SET SESSION wsrep_sync_wait = DEFAULT; +SELECT * FROM t1; +id f2 +1 0 +2 1 +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL DEBUG_DBUG = ''; +SET GLOBAL wsrep_slave_threads = DEFAULT; +DROP TABLE t1; diff --git a/mysql-test/suite/galera/r/galera_ddl_fk_conflict.result b/mysql-test/suite/galera/r/galera_ddl_fk_conflict.result index 03e84f9facde4..904c5eb1a826a 100644 --- a/mysql-test/suite/galera/r/galera_ddl_fk_conflict.result +++ b/mysql-test/suite/galera/r/galera_ddl_fk_conflict.result @@ -7,7 +7,7 @@ connect node_1b, 127.0.0.1, root, , test, $NODE_MYPORT_1; connection node_1b; SET SESSION wsrep_sync_wait=0; ###################################################################### -# Test for ALTER ENGINE=INNODB +# Test for ALTER TABLE ENGINE=INNODB ###################################################################### ###################################################################### # @@ -49,6 +49,9 @@ EXPECT_1 SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; EXPECT_2 2 +connection node_1; +CREATE TABLE IF NOT EXISTS c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT IGNORE INTO c1 VALUES (1,1); ###################################################################### # # Scenario #2: DML working on FK parent table tries to replicate, but @@ -87,6 +90,9 @@ EXPECT_1 SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; EXPECT_2 2 +connection node_1; +CREATE TABLE IF NOT EXISTS c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT IGNORE INTO c1 VALUES (1,1); ###################################################################### # # Scenario #3: 2 DMLs working on two FK parent tables try to replicate, @@ -137,10 +143,10 @@ EXPECT_1 SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; EXPECT_2 2 -DROP TABLE c1, c2; +DROP TABLE IF EXISTS c1, c2; DROP TABLE p1, p2; ###################################################################### -# Test for TRUNCATE +# Test for TRUNCATE TABLE ###################################################################### ###################################################################### # @@ -182,6 +188,9 @@ EXPECT_1 SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; EXPECT_2 2 +connection node_1; +CREATE TABLE IF NOT EXISTS c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT IGNORE INTO c1 VALUES (1,1); ###################################################################### # # Scenario #2: DML working on FK parent table tries to replicate, but @@ -220,6 +229,9 @@ EXPECT_1 SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; EXPECT_2 2 +connection node_1; +CREATE TABLE IF NOT EXISTS c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT IGNORE INTO c1 VALUES (1,1); ###################################################################### # # Scenario #3: 2 DMLs working on two FK parent tables try to replicate, @@ -270,5 +282,592 @@ EXPECT_1 SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; EXPECT_2 2 -DROP TABLE c1, c2; +DROP TABLE IF EXISTS c1, c2; DROP TABLE p1, p2; +###################################################################### +# Test for DROP TABLE +###################################################################### +###################################################################### +# +# 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 p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); +CREATE TABLE p2 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p2 VALUES (1, 'INITIAL VALUE'); +INSERT INTO p2 VALUES (2, 'INITIAL VALUE'); +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); +CREATE TABLE c2 (pk INTEGER PRIMARY KEY, fk1 INTEGER, fk2 INTEGER, FOREIGN KEY (fk1) REFERENCES p1(pk), FOREIGN KEY (fk2) REFERENCES p2(pk)); +INSERT INTO c2 VALUES (1,1,1), (2,1,2); +connection node_1; +SET AUTOCOMMIT=ON; +START TRANSACTION; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +connection node_2; +SET SESSION wsrep_sync_wait=0; +DROP TABLE c1 ; +connection node_1; +COMMIT; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_1; +CREATE TABLE IF NOT EXISTS c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT IGNORE INTO c1 VALUES (1,1); +###################################################################### +# +# 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; +DROP TABLE c1 ; +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 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_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_1; +CREATE TABLE IF NOT EXISTS c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT IGNORE INTO c1 VALUES (1,1); +###################################################################### +# +# Scenario #3: 2 DMLs working on two FK parent tables try to replicate, +# but fails in certification for earlier DDL on child table +# which is child to both FK parents +# +###################################################################### +connection node_1; +BEGIN; +connection node_1b; +BEGIN; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +DROP TABLE c2 ; +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1b; +UPDATE p2 SET f2 = 'TO DEADLOCK' WHERE pk = 2; +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 +connection node_1b; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +DROP TABLE IF EXISTS c1, c2; +Warnings: +Note 1051 Unknown table 'test.c2' +DROP TABLE p1, p2; +###################################################################### +# Test for DROP TABLE +###################################################################### +connection node_1; +SET SESSION wsrep_sync_wait=0; +CREATE TABLE p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); +###################################################################### +# +# Scenario #4: DML working on FK parent table tries to replicate, but +# fails in certification for earlier DDL on child table +# and another temporary table. TMP table should be skipped +# but FK child table should be replicated with proper keys +# +###################################################################### +connection node_1; +BEGIN; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +CREATE TEMPORARY TABLE tmp1 (i int); +CREATE TEMPORARY TABLE tmp2 (i int); +DROP TABLE tmp1, c1, tmp2 ; +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +INSERT INTO p1 VALUES (10, 'TO DEADLOCK'); +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_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +DROP TABLE IF EXISTS c1; +Warnings: +Note 1051 Unknown table 'test.c1' +DROP TABLE p1; +DROP TABLE IF EXISTS tmp1, tmp2; +Warnings: +Note 1051 Unknown table 'test.tmp1,test.tmp2' +###################################################################### +# Test for DROP TABLE IF EXISTS +###################################################################### +###################################################################### +# +# 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 p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); +CREATE TABLE p2 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p2 VALUES (1, 'INITIAL VALUE'); +INSERT INTO p2 VALUES (2, 'INITIAL VALUE'); +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); +CREATE TABLE c2 (pk INTEGER PRIMARY KEY, fk1 INTEGER, fk2 INTEGER, FOREIGN KEY (fk1) REFERENCES p1(pk), FOREIGN KEY (fk2) REFERENCES p2(pk)); +INSERT INTO c2 VALUES (1,1,1), (2,1,2); +connection node_1; +SET AUTOCOMMIT=ON; +START TRANSACTION; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +connection node_2; +SET SESSION wsrep_sync_wait=0; +DROP TABLE IF EXISTS c1 ; +connection node_1; +COMMIT; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_1; +CREATE TABLE IF NOT EXISTS c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT IGNORE INTO c1 VALUES (1,1); +###################################################################### +# +# 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; +DROP TABLE IF EXISTS c1 ; +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 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_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_1; +CREATE TABLE IF NOT EXISTS c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT IGNORE INTO c1 VALUES (1,1); +###################################################################### +# +# Scenario #3: 2 DMLs working on two FK parent tables try to replicate, +# but fails in certification for earlier DDL on child table +# which is child to both FK parents +# +###################################################################### +connection node_1; +BEGIN; +connection node_1b; +BEGIN; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +DROP TABLE IF EXISTS c2 ; +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1b; +UPDATE p2 SET f2 = 'TO DEADLOCK' WHERE pk = 2; +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 +connection node_1b; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +DROP TABLE IF EXISTS c1, c2; +Warnings: +Note 1051 Unknown table 'test.c2' +DROP TABLE p1, p2; +###################################################################### +# Test for DROP TABLE IF EXISTS +###################################################################### +connection node_1; +SET SESSION wsrep_sync_wait=0; +CREATE TABLE p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); +###################################################################### +# +# Scenario #4: DML working on FK parent table tries to replicate, but +# fails in certification for earlier DDL on child table +# and another temporary table. TMP table should be skipped +# but FK child table should be replicated with proper keys +# +###################################################################### +connection node_1; +BEGIN; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +CREATE TEMPORARY TABLE tmp1 (i int); +CREATE TEMPORARY TABLE tmp2 (i int); +DROP TABLE IF EXISTS tmp1, c1, tmp2 ; +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +INSERT INTO p1 VALUES (10, 'TO DEADLOCK'); +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_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +DROP TABLE IF EXISTS c1; +Warnings: +Note 1051 Unknown table 'test.c1' +DROP TABLE p1; +DROP TABLE IF EXISTS tmp1, tmp2; +Warnings: +Note 1051 Unknown table 'test.tmp1,test.tmp2' +###################################################################### +# Test for DROP TABLE IF EXISTS nonexisting, +###################################################################### +###################################################################### +# +# 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 p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); +CREATE TABLE p2 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p2 VALUES (1, 'INITIAL VALUE'); +INSERT INTO p2 VALUES (2, 'INITIAL VALUE'); +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); +CREATE TABLE c2 (pk INTEGER PRIMARY KEY, fk1 INTEGER, fk2 INTEGER, FOREIGN KEY (fk1) REFERENCES p1(pk), FOREIGN KEY (fk2) REFERENCES p2(pk)); +INSERT INTO c2 VALUES (1,1,1), (2,1,2); +connection node_1; +SET AUTOCOMMIT=ON; +START TRANSACTION; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +connection node_2; +SET SESSION wsrep_sync_wait=0; +DROP TABLE IF EXISTS nonexisting, c1 ; +Warnings: +Note 1051 Unknown table 'test.nonexisting' +connection node_1; +COMMIT; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_1; +CREATE TABLE IF NOT EXISTS c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT IGNORE INTO c1 VALUES (1,1); +###################################################################### +# +# 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; +DROP TABLE IF EXISTS nonexisting, c1 ; +Warnings: +Note 1051 Unknown table 'test.nonexisting' +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 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_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_1; +CREATE TABLE IF NOT EXISTS c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT IGNORE INTO c1 VALUES (1,1); +###################################################################### +# +# Scenario #3: 2 DMLs working on two FK parent tables try to replicate, +# but fails in certification for earlier DDL on child table +# which is child to both FK parents +# +###################################################################### +connection node_1; +BEGIN; +connection node_1b; +BEGIN; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +DROP TABLE IF EXISTS nonexisting, c2 ; +Warnings: +Note 1051 Unknown table 'test.nonexisting' +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1b; +UPDATE p2 SET f2 = 'TO DEADLOCK' WHERE pk = 2; +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 +connection node_1b; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +DROP TABLE IF EXISTS c1, c2; +Warnings: +Note 1051 Unknown table 'test.c2' +DROP TABLE p1, p2; +###################################################################### +# Test for DROP TABLE IF EXISTS nonexisting, +###################################################################### +connection node_1; +SET SESSION wsrep_sync_wait=0; +CREATE TABLE p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); +###################################################################### +# +# Scenario #4: DML working on FK parent table tries to replicate, but +# fails in certification for earlier DDL on child table +# and another temporary table. TMP table should be skipped +# but FK child table should be replicated with proper keys +# +###################################################################### +connection node_1; +BEGIN; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +CREATE TEMPORARY TABLE tmp1 (i int); +CREATE TEMPORARY TABLE tmp2 (i int); +DROP TABLE IF EXISTS nonexisting, tmp1, c1, tmp2 ; +Warnings: +Note 1051 Unknown table 'test.nonexisting' +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +INSERT INTO p1 VALUES (10, 'TO DEADLOCK'); +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_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +DROP TABLE IF EXISTS c1; +Warnings: +Note 1051 Unknown table 'test.c1' +DROP TABLE p1; +DROP TABLE IF EXISTS tmp1, tmp2; +Warnings: +Note 1051 Unknown table 'test.tmp1,test.tmp2' diff --git a/mysql-test/suite/galera/r/galera_multi_level_fk_ddl_update.result b/mysql-test/suite/galera/r/galera_multi_level_fk_ddl_update.result new file mode 100644 index 0000000000000..cf693d1fbe279 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_multi_level_fk_ddl_update.result @@ -0,0 +1,288 @@ +connection node_2; +connection node_1; +# +# 1. BF-BF conflict on MDL locks between: DROP TABLE t6 and UPDATE on t1 +# with foreign key references as below: +# - t1<-t2<-t3<-t4 +# - t3<-t5 +# - t2<-t6 +# +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, +t5_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, +KEY key_t5_id(t5_id) +); +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 +); +CREATE TABLE t5 ( +id INT PRIMARY KEY, +t3_id_1 INT NOT NULL, +f2 INTEGER NOT NULL, +KEY key_t3_id_1(t3_id_1), +CONSTRAINT key_t3_id_1 FOREIGN KEY (t3_id_1) REFERENCES t3 (id) ON UPDATE CASCADE ON DELETE CASCADE +); +CREATE TABLE t6 ( +id INT PRIMARY KEY, +t2_id INT NOT NULL, +f2 INTEGER NOT NULL, +KEY key_t2_id_1(t2_id), +CONSTRAINT key_t2_id_1 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,1,1234); +INSERT INTO t2 VALUES (2,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); +INSERT INTO t5 VALUES (1,1,1234); +INSERT INTO t5 VALUES (2,2,1234); +ALTER TABLE t2 ADD CONSTRAINT key_t5_id FOREIGN KEY (t5_id) +REFERENCES t5 (id) ON UPDATE CASCADE ON DELETE CASCADE; +connection node_2; +SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi'; +connection node_1; +DROP TABLE t6; +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; +UPDATE t1 SET f2 = 1 WHERE id=2; +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: 4 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] +include/assert_grep.inc [Foreign key referenced table found: test.t5] +connection node_2; +select * from t1; +id f2 +1 0 +2 1 +select * from t2; +id t1_id t5_id f2 +1 1 1 1234 +2 2 2 1234 +select * from t3; +id t2_id f2 +1 1 1234 +2 2 1234 +select * from t4; +id t3_id f2 +1 1 1234 +2 2 1234 +select * from t5; +id t3_id_1 f2 +1 1 1234 +2 2 1234 +select * from t6; +ERROR 42S02: Table 'test.t6' doesn't exist +connection node_1; +select * from t1; +id f2 +1 0 +2 1 +select * from t2; +id t1_id t5_id f2 +1 1 1 1234 +2 2 2 1234 +select * from t3; +id t2_id f2 +1 1 1234 +2 2 1234 +select * from t4; +id t3_id f2 +1 1 1234 +2 2 1234 +select * from t5; +id t3_id_1 f2 +1 1 1234 +2 2 1234 +select * from t6; +ERROR 42S02: Table 'test.t6' doesn't exist +ALTER TABLE t2 DROP FOREIGN KEY key_t5_id; +DROP TABLE t5, t4, t3, t2, t1; +# +# 2. 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. +# +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; +SET GLOBAL DEBUG_DBUG = '+d,wsrep_print_foreign_keys_table'; +START TRANSACTION; +UPDATE t1 SET f2 = 1 WHERE id=2; +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 1 +select * from t2; +id t1_id f2 +1 1 1234 +2 2 1234 +select * from t3; +id t2_id f2 +connection node_1; +select * from t1; +id f2 +1 0 +2 1 +select * from t2; +id t1_id f2 +1 1 1234 +2 2 1234 +select * from t3; +id t2_id f2 +DROP TABLE t3, t2, t1; +# +# 3. 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. +# +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; +SET GLOBAL DEBUG_DBUG = '+d,wsrep_print_foreign_keys_table'; +START TRANSACTION; +UPDATE t1 SET f2 = 1 WHERE id=2; +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 1 +select * from t2; +id t1_id f2 +1 1 1234 +2 2 1234 +select * from t3; +id t2_id f2 +connection node_1; +select * from t1; +id f2 +1 0 +2 1 +select * from t2; +id t1_id f2 +1 1 1234 +2 2 1234 +select * from t3; +id t2_id f2 +DROP TABLE t3, t2, t1; diff --git a/mysql-test/suite/galera/t/MDEV-35018.test b/mysql-test/suite/galera/t/MDEV-35018.test new file mode 100644 index 0000000000000..490b542bcc0cf --- /dev/null +++ b/mysql-test/suite/galera/t/MDEV-35018.test @@ -0,0 +1,83 @@ +# +# 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_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 information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_apply_waits'` +--echo $expected_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 information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_apply_waits' +--source include/wait_condition.inc +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; + +SET SESSION wsrep_sync_wait = DEFAULT; +SELECT * FROM t1; + +# +# Cleanup +# +SET DEBUG_SYNC = 'RESET'; +SET GLOBAL DEBUG_DBUG = ''; +SET GLOBAL wsrep_slave_threads = DEFAULT; +DROP TABLE t1; diff --git a/mysql-test/suite/galera/t/galera_ddl_fk_conflict.inc b/mysql-test/suite/galera/t/galera_ddl_fk_conflict.inc index 06b7bbe41c4a1..6964446a36352 100644 --- a/mysql-test/suite/galera/t/galera_ddl_fk_conflict.inc +++ b/mysql-test/suite/galera/t/galera_ddl_fk_conflict.inc @@ -69,7 +69,7 @@ SET SESSION wsrep_sync_wait=0; --source include/wait_condition.inc # replicate the DDL to be tested ---eval $table_admin_command TABLE c1 $table_admin_command_end +--eval $table_admin_command c1 $table_admin_command_end --connection node_1 --error ER_LOCK_DEADLOCK @@ -82,6 +82,12 @@ SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +--connection node_1 +--disable_warnings +CREATE TABLE IF NOT EXISTS c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT IGNORE INTO c1 VALUES (1,1); +--enable_warnings + --echo ###################################################################### --echo # --echo # Scenario #2: DML working on FK parent table tries to replicate, but @@ -97,7 +103,7 @@ BEGIN; --source include/galera_set_sync_point.inc --connection node_2 ---eval $table_admin_command TABLE c1 $table_admin_command_end +--eval $table_admin_command c1 $table_admin_command_end --connection node_1a --source include/galera_wait_sync_point.inc @@ -128,6 +134,11 @@ SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +--connection node_1 +--disable_warnings +CREATE TABLE IF NOT EXISTS c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT IGNORE INTO c1 VALUES (1,1); +--enable_warnings --echo ###################################################################### --echo # @@ -149,7 +160,7 @@ BEGIN; --source include/galera_set_sync_point.inc --connection node_2 ---eval $table_admin_command TABLE c2 $table_admin_command_end +--eval $table_admin_command c2 $table_admin_command_end --connection node_1a --source include/galera_wait_sync_point.inc @@ -188,5 +199,5 @@ SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; -DROP TABLE c1, c2; +DROP TABLE IF EXISTS c1, c2; DROP TABLE p1, p2; diff --git a/mysql-test/suite/galera/t/galera_ddl_fk_conflict.test b/mysql-test/suite/galera/t/galera_ddl_fk_conflict.test index 4ec866a9f74b9..b98c6daa73b11 100644 --- a/mysql-test/suite/galera/t/galera_ddl_fk_conflict.test +++ b/mysql-test/suite/galera/t/galera_ddl_fk_conflict.test @@ -17,13 +17,28 @@ SET SESSION wsrep_sync_wait=0; --connection node_1b SET SESSION wsrep_sync_wait=0; ---let $table_admin_command = ALTER +--let $table_admin_command = ALTER TABLE --let $table_admin_command_end = ENGINE=INNODB --source galera_ddl_fk_conflict.inc ---let $table_admin_command = TRUNCATE ---let $table_admin_command_end = +--let $table_admin_command = TRUNCATE TABLE +--let $table_admin_command_end = --source galera_ddl_fk_conflict.inc +--let $table_admin_command = DROP TABLE +--let $table_admin_command_end = +--source galera_ddl_fk_conflict.inc +--source galera_ddl_fk_conflict_with_tmp.inc + +--let $table_admin_command = DROP TABLE IF EXISTS +--let $table_admin_command_end = +--source galera_ddl_fk_conflict.inc +--source galera_ddl_fk_conflict_with_tmp.inc + +--let $table_admin_command = DROP TABLE IF EXISTS nonexisting, +--let $table_admin_command_end = +--source galera_ddl_fk_conflict.inc +--source galera_ddl_fk_conflict_with_tmp.inc + # CHECK and ANALYZE are not affected diff --git a/mysql-test/suite/galera/t/galera_ddl_fk_conflict_with_tmp.inc b/mysql-test/suite/galera/t/galera_ddl_fk_conflict_with_tmp.inc index acf3c54180be7..cc6542b96b031 100644 --- a/mysql-test/suite/galera/t/galera_ddl_fk_conflict_with_tmp.inc +++ b/mysql-test/suite/galera/t/galera_ddl_fk_conflict_with_tmp.inc @@ -35,9 +35,9 @@ BEGIN; --source include/wait_condition.inc --let $wait_condition = SELECT COUNT(*) = 1 FROM c1 --source include/wait_condition.inc -CREATE TEMPORARY TABLE tmp (i int); ---eval $table_admin_command TABLE c1, tmp $table_admin_command_end -DROP TABLE tmp; +CREATE TEMPORARY TABLE tmp1 (i int); +CREATE TEMPORARY TABLE tmp2 (i int); +--eval $table_admin_command tmp1, c1, tmp2 $table_admin_command_end --connection node_1a --source include/galera_wait_sync_point.inc @@ -45,7 +45,7 @@ DROP TABLE tmp; --let $expected_cert_failures = `SELECT VARIABLE_VALUE+1 FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_cert_failures'` --connection node_1 -UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +INSERT INTO p1 VALUES (10, 'TO DEADLOCK'); --send COMMIT --connection node_1a @@ -65,5 +65,6 @@ SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; --connection node_2 SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; -DROP TABLE c1; +DROP TABLE IF EXISTS c1; DROP TABLE p1; +DROP TABLE IF EXISTS tmp1, tmp2; diff --git a/mysql-test/suite/galera/t/galera_multi_level_fk_ddl_update.test b/mysql-test/suite/galera/t/galera_multi_level_fk_ddl_update.test new file mode 100644 index 0000000000000..d3d5586994239 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_multi_level_fk_ddl_update.test @@ -0,0 +1,358 @@ +# +# BF-BF conflict on MDL locks between DDL and update 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 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. + +--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 t6 and UPDATE on t1 +--echo # with foreign key references as below: +--echo # - t1<-t2<-t3<-t4 +--echo # - t3<-t5 +--echo # - t2<-t6 +--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, + t5_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, + KEY key_t5_id(t5_id) +); + +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 +); + +CREATE TABLE t5 ( + id INT PRIMARY KEY, + t3_id_1 INT NOT NULL, + f2 INTEGER NOT NULL, + KEY key_t3_id_1(t3_id_1), + CONSTRAINT key_t3_id_1 FOREIGN KEY (t3_id_1) REFERENCES t3 (id) ON UPDATE CASCADE ON DELETE CASCADE +); + +CREATE TABLE t6 ( + id INT PRIMARY KEY, + t2_id INT NOT NULL, + f2 INTEGER NOT NULL, + KEY key_t2_id_1(t2_id), + CONSTRAINT key_t2_id_1 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,1,1234); +INSERT INTO t2 VALUES (2,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); + +INSERT INTO t5 VALUES (1,1,1234); +INSERT INTO t5 VALUES (2,2,1234); + +ALTER TABLE t2 ADD CONSTRAINT key_t5_id FOREIGN KEY (t5_id) +REFERENCES t5 (id) ON UPDATE CASCADE ON DELETE CASCADE; + + +--let $fk_parent_query = DROP TABLE t6 +--let $fk_child_query = UPDATE t1 SET f2 = 1 WHERE id=2 +--let $fk_mdl_lock_num = 5 +--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: 4 tables +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 4 +--let assert_select= Foreign key referenced table found: +--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 +--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 +--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 +--source include/assert_grep.inc + +--let assert_text= Foreign key referenced table found: test.t5 +--let $assert_file= $MYSQLTEST_VARDIR/log/mysqld.1.err +--let assert_count= 1 +--let assert_select= Foreign key referenced table found: test.t5 +--source include/assert_grep.inc + + +# +# Verify update and drop table has succeded. +# +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 1 FROM test.t1 where id=2 and f2=1; +--source include/wait_condition.inc + +select * from t1; +select * from t2; +select * from t3; +select * from t4; +select * from t5; +--error 1146 +select * from t6; + +--connection node_1 +select * from t1; +select * from t2; +select * from t3; +select * from t4; +select * from t5; +--error 1146 +select * from t6; + + +# +# Cleanup +# +ALTER TABLE t2 DROP FOREIGN KEY key_t5_id; +DROP TABLE t5, t4, t3, t2, t1; + + +--echo # +--echo # 2. BF-BF conflict on MDL locks between: +--echo # ALTER TABLE t3 (whose parent table are t3 -> t2 -> t1), and +--echo # UPDATE on t1 with t2 referencing t1, and t3 referencing t2. +--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) +); + +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 +# +--let $fk_parent_query = ALTER TABLE t3 ADD CONSTRAINT key_t2_id FOREIGN KEY (t2_id) REFERENCES t2 (id) ON UPDATE CASCADE ON DELETE CASCADE +# +# 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). +# +--let $fk_child_query = UPDATE t1 SET f2 = 1 WHERE id=2 +--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= 6 +--let assert_select= Foreign key referenced table found: +--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 +--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 +--source include/assert_grep.inc + + +# +# Verify update and drop table has succeded. +# +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 1 FROM test.t1 where id=2 and f2=1; +--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 t3, t2, t1; + + +--echo # +--echo # 3. BF-BF conflict on MDL locks between: +--echo # CREATE TABLE t3 (whose parent table are t3 -> t2 -> t1), and +--echo # UPDATE on t1 with t2 referencing t1, and t3 referencing t2. +--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 +); + + +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); + + +--let $fk_parent_query = 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) +--let $fk_child_query = UPDATE t1 SET f2 = 1 WHERE id=2 +--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= 8 +--let assert_select= Foreign key referenced table found: +--let $assert_only_after = CURRENT_TEST: +--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 +--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 +--source include/assert_grep.inc + + +# +# Verify update and drop table has succeded. +# +--connection node_2 +--let $wait_condition = SELECT COUNT(*) = 1 FROM test.t1 where id=2 and f2=1; +--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 t3, t2, t1; + + diff --git a/mysql-test/suite/galera/t/galera_multi_level_foreign_key.inc b/mysql-test/suite/galera/t/galera_multi_level_foreign_key.inc new file mode 100644 index 0000000000000..2eb31f7e57783 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_multi_level_foreign_key.inc @@ -0,0 +1,61 @@ +# +# Execute parent query on node_1 and wait for it to reach node_2 +# +--connection node_2 +SET GLOBAL DEBUG_DBUG = '+d,sync.wsrep_apply_toi'; + +--connection node_1 +--eval $fk_parent_query + +--connection node_2 +SET DEBUG_SYNC = "now WAIT_FOR sync.wsrep_apply_toi_reached"; + +SET SESSION wsrep_sync_wait = 0; +--let $expected_apply_waits = query_get_value("SHOW STATUS LIKE 'wsrep_apply_waits'", Value, 1) +--let $expected_apply_waits = `select $expected_apply_waits + 1` + + +# +# Execute child query on node_1. +# If bug is present, expect the wait condition +# to timeout and when the child query applies, it +# will be granted a MDL lock on parent table. +# When resumed, the parent query will +# also try to acquire MDL lock on parent table, +# causing a BF-BF conflict on that MDL lock. +# +--connection node_1 +SET GLOBAL DEBUG_DBUG = '+d,wsrep_print_foreign_keys_table'; +START TRANSACTION; +--eval $fk_child_query +--let $wait_condition = SELECT COUNT(*) = $fk_mdl_lock_num FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA='test' AND LOCK_STATUS="GRANTED" +--source include/wait_condition.inc +COMMIT; + + +# +# Expect the child query to depend on the parent query, +# therefore it should wait for the parent query to +# finish before it can be applied. +# +--connection node_2 +--let $status_var = wsrep_apply_waits +--let $status_var_value = $expected_apply_waits +--source include/wait_for_status_var.inc + +SET GLOBAL DEBUG_DBUG = '-d,sync.wsrep_apply_toi'; +SET DEBUG_SYNC = "now SIGNAL signal.wsrep_apply_toi"; + + +# +# Cleanup +# +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; + diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index dc98f9c8d934e..182a1c4841076 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -536,7 +536,7 @@ bool Sql_cmd_alter_table::execute(THD *thd) DBUG_RETURN(TRUE); /* purecov: inspected */ #ifdef WITH_WSREP - if (WSREP(thd) && + if (WSREP(thd) && wsrep_thd_is_local(thd) && (!thd->is_current_stmt_binlog_format_row() || !thd->find_temporary_table(first_table))) { diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 29daca40acb71..4e44497266f8c 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -3782,6 +3782,31 @@ open_and_process_table(THD *thd, TABLE_LIST *tables, uint *counter, uint flags, thd->pop_internal_handler(); safe_to_ignore_table= no_such_table_handler.safely_trapped_errors(); + +#ifdef WITH_WSREP + if (WSREP(thd) && !thd->wsrep_applier && !error) + { + /* + Append a table level shared key for the referenced/foreign table. + This to avoid potential MDL conflicts with concurrent DDLs. + */ + const int rcode = wsrep_thd_append_table_key(thd, tables->db.str, + tables->table_name.str, WSREP_SERVICE_KEY_SHARED); + if (rcode) + { + WSREP_ERROR("Appending table key failed: %s, %d", + wsrep_thd_query(thd), rcode); + error = true; + } + + DBUG_EXECUTE_IF( + "wsrep_print_foreign_keys_table", + sql_print_information("Foreign key referenced table found: %s.%s", + tables->db.str, + tables->table_name.str); + ); + } +#endif } else if (tables->parent_l && (thd->open_options & HA_OPEN_FOR_REPAIR)) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 377d305179413..0c2d3be5a98d9 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5067,17 +5067,18 @@ mysql_execute_command(THD *thd) lex->create_info.set(DDL_options_st::OPT_IF_EXISTS); #ifdef WITH_WSREP - if (WSREP(thd)) + if (WSREP(thd) && !lex->tmp_table() && wsrep_thd_is_local(thd) && + (!thd->is_current_stmt_binlog_format_row() || + wsrep_table_list_has_non_temp_tables(thd, all_tables))) { - for (TABLE_LIST *table= all_tables; table; table= table->next_global) + wsrep::key_array keys; + if (wsrep_append_fk_parent_table(thd, all_tables, &keys)) { - if (!lex->tmp_table() && - (!thd->is_current_stmt_binlog_format_row() || - !thd->find_temporary_table(table))) - { - WSREP_TO_ISOLATION_BEGIN(NULL, NULL, all_tables); - break; - } + goto wsrep_error_label; + } + if (wsrep_to_isolation_begin(thd, NULL, NULL, all_tables, NULL, &keys)) + { + goto wsrep_error_label; } } #endif /* WITH_WSREP */ diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index bc6c185c8d0eb..ccc24be95d8f2 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1378,84 +1378,114 @@ static void wsrep_keys_free(wsrep_key_arr_t* key_arr) * @return 0 if parent table append was successful, non-zero otherwise. */ -bool -wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* tables, wsrep::key_array* keys) +bool wsrep_append_fk_parent_table(THD *thd, TABLE_LIST *tables, + wsrep::key_array *keys) { - bool fail= false; - TABLE_LIST *table; + assert(wsrep_thd_is_local(thd)); - for (table= tables; table; table= table->next_local) + bool fail= false; + Open_table_context ot_ctx(thd, MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL); + + for (TABLE_LIST *table= tables; table; table= table->next_local) + { + if (!table->table) { - if (is_temporary_table(table)) + TABLE_LIST *save_next_global= table->next_global; + TABLE_LIST::enum_open_strategy save_open_strategy= table->open_strategy; + table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + + Diagnostics_area *da= thd->get_stmt_da(); + Warning_info tmp_wi(thd->query_id, false, true); + da->push_warning_info(&tmp_wi); + + if (open_table(thd, table, &ot_ctx)) { - WSREP_DEBUG("Temporary table %s.%s already opened query=%s", table->db.str, - table->table_name.str, wsrep_thd_query(thd)); - return false; + if (da->is_error() && da->sql_errno() == ER_UNKNOWN_STORAGE_ENGINE) + { + thd->get_stmt_da()->reset_diagnostics_area(); + } + else + { + fail= true; + } } - } - thd->release_transactional_locks(); - uint counter; - MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + da->pop_warning_info(); + table->next_global= save_next_global; + table->open_strategy= save_open_strategy; - 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; + if (fail) + { + WSREP_DEBUG("Unable to open table for FK checks for %s", + wsrep_thd_query(thd)); + goto exit; + } } - for (table= tables; table; table= table->next_local) + if (table->table && !is_temporary_table(table)) { - if (!is_temporary_table(table) && table->table) - { - FOREIGN_KEY_INFO *f_key_info; - List f_key_list; + 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)); - } + 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)); } } + } exit: - DEBUG_SYNC(thd, "wsrep_append_fk_toi_keys_before_close_tables"); + DEBUG_SYNC(thd, "wsrep_append_fk_toi_keys_before_close_tables"); - /* 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->next_global= NULL; - table->mdl_request.ticket= NULL; - } + /* close the table and release MDL locks */ + close_thread_tables(thd); + thd->mdl_context.rollback_to_savepoint(ot_ctx.start_of_statement_svp()); + for (TABLE_LIST *table= tables; table; table= table->next_global) + { + table->table= NULL; + table->mdl_request.ticket= NULL; + } - /* - MDEV-32938: Check if DDL operation has been killed before. + /* + Reopen temporary tables if necessary. + DROP TABLE pre-opens temporary tables, but the corresponding + command does not have the CF_PREOPEN_TMP_TABLES flag set. + */ + const bool preopen_tmp_tables= + thd->lex->sql_command == SQLCOM_DROP_TABLE || + (sql_command_flags[thd->lex->sql_command] & CF_PREOPEN_TMP_TABLES); - It may be that during collecting foreign keys this operation gets BF-aborted - by another already-running TOI operation because it got MDL locks on the same - table for checking foreign keys. - After `close_thread_tables()` has been called it's safe to assume that no-one - can BF-abort this operation as it's not holding any MDL locks any more. - */ - if (!fail) + if (preopen_tmp_tables && thd->open_temporary_tables(tables)) + { + WSREP_INFO("Unable to reopen temporary tables after FK checks"); + fail= true; + } + + /* + MDEV-32938: Check if DDL operation has been killed before. + + It may be that during collecting foreign keys this operation gets + BF-aborted by another already-running TOI operation because it got MDL + locks on the same table for checking foreign keys. After + `close_thread_tables()` has been called it's safe to assume that no-one can + BF-abort this operation as it's not holding any MDL locks any more. + */ + if (!fail) + { + mysql_mutex_lock(&thd->LOCK_thd_kill); + if (thd->killed) { - mysql_mutex_lock(&thd->LOCK_thd_kill); - if (thd->killed) - { - fail= true; - } - mysql_mutex_unlock(&thd->LOCK_thd_kill); + fail= true; } - return fail; + mysql_mutex_unlock(&thd->LOCK_thd_kill); + } + + return fail; } bool wsrep_reload_ssl() @@ -1629,6 +1659,19 @@ static bool wsrep_prepare_keys_for_isolation(THD* thd, return true; } +extern "C" int wsrep_thd_append_table_key(MYSQL_THD thd, + const char* db, + const char* table, + enum Wsrep_service_key_type key_type) +{ + wsrep_key_arr_t key_arr = {0, 0}; + int ret = wsrep_prepare_keys_for_isolation(thd, db, table, NULL, NULL, &key_arr); + ret = ret || wsrep_thd_append_key(thd, key_arr.keys, + (int)key_arr.keys_len, key_type); + wsrep_keys_free(&key_arr); + return ret; +} + /* * Prepare key list from db/table and table_list and append it to Wsrep * with the given key type. @@ -3550,3 +3593,15 @@ void wsrep_commit_empty(THD* thd, bool all) } DBUG_VOID_RETURN; } + +bool wsrep_table_list_has_non_temp_tables(THD *thd, TABLE_LIST *tables) +{ + for (TABLE_LIST *table= tables; table; table= table->next_global) + { + if (!thd->find_temporary_table(table)) + { + return true; + } + } + return false; +} diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index d67f1fdf4795e..02d1d7fd2482c 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -576,6 +576,13 @@ wsrep::key wsrep_prepare_key_for_toi(const char* db, const char* table, void wsrep_wait_ready(THD *thd); void wsrep_ready_set(bool ready_value); + +/** + * Returns true if the given list of tables contains at least one + * non-temporary table. + */ +bool wsrep_table_list_has_non_temp_tables(THD *thd, TABLE_LIST *tables); + #else /* !WITH_WSREP */ /* These macros are needed to compile MariaDB without WSREP support