MDEV-35018 MDL BF-BF conflict on DROP TABLE

DROP TABLE on child and UPDATE of parent table can cause an MDL BF-BF
conflict when applied concurrently.
DROP TABLE takes MDL locks on both child and its parent table, however
it only it did not add certification keys for the parent table.
This patch adds the following:
 * Append certification keys corresponding to all parent tables
   before DROP TABLE replication.
 * Fix wsrep_append_fk_parent_table() so that it works when it is
   given a table list containing temporary tables.
 * Make sure function wsrep_append_fk_parent_table() is only called
   for local transaction. That was not the case for ALTER TABLE.
 * Add a test case that verifies that UPDATE parent depends on
   preceeding DROP TABLE child.
 * Adapt galera_ddl_fk_conflict test to work with DROP TABLE as well.

Signed-off-by: Julius Goryavsky <julius.goryavsky@mariadb.com>
This commit is contained in:
Daniele Sciascia 2024-09-26 16:07:13 +02:00 committed by Julius Goryavsky
parent a226f12675
commit 841763ecc0
10 changed files with 881 additions and 96 deletions

View file

@ -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;

View file

@ -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'

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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)))
{

View file

@ -5080,17 +5080,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 */

View file

@ -1349,84 +1349,102 @@ 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))
{
WSREP_DEBUG("Temporary table %s.%s already opened query=%s", table->db.str,
table->table_name.str, wsrep_thd_query(thd));
return false;
}
}
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;
thd->release_transactional_locks();
uint counter;
MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
if (open_tables(thd, &tables, &counter, MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL))
{
WSREP_DEBUG("Unable to open table for FK checks for %s", wsrep_thd_query(thd));
fail= true;
goto exit;
}
for (table= tables; table; table= table->next_local)
{
if (!is_temporary_table(table) && table->table)
{
FOREIGN_KEY_INFO *f_key_info;
List<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));
}
}
}
exit:
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;
}
/*
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)
if (open_table(thd, table, &ot_ctx))
{
fail= true;
}
mysql_mutex_unlock(&thd->LOCK_thd_kill);
table->next_global= save_next_global;
table->open_strategy= save_open_strategy;
if (fail)
{
WSREP_DEBUG("Unable to open table for FK checks for %s",
wsrep_thd_query(thd));
goto exit;
}
}
return fail;
if (table->table && !is_temporary_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));
}
}
}
exit:
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(ot_ctx.start_of_statement_svp());
for (TABLE_LIST *table= tables; table; table= table->next_global)
{
table->table= NULL;
table->mdl_request.ticket= NULL;
}
/*
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);
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)
{
fail= true;
}
mysql_mutex_unlock(&thd->LOCK_thd_kill);
}
return fail;
}
bool wsrep_reload_ssl()
@ -3526,3 +3544,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;
}

View file

@ -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