1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-27 13:04:36 +03:00
Files
mariadb/mysql-test/suite/innodb/r/truncate_foreign.result
Marko Mäkelä c3c53926c4 MDEV-26554: Races between INSERT on child and DDL on parent table
The SQL layer never acquires metadata locks (MDL) on the tables
that the tables that DML statement accesses is modifying.

However, the storage engine must access the parent table in order to
ensure that the child table will not refer to a non-existing record
in the parent table.

During certain DDL operations, the InnoDB table metadata (dict_table_t)
may be be freed and rebuilt. This would cause a race condition with
a concurrent INSERT that is attempting to report a FOREIGN KEY violation.

We work around the insufficient MDL during DML by acquiring exclusive
InnoDB table locks on all child tables during DDL. To avoid deadlocks,
we will follow the following order of acquisition:

1. tables whose REFERENCES clauses point to the current table
2. the current table that is being subjected to DDL
3. mysql.innodb_table_stats
4. mysql.innodb_index_stats
5. the InnoDB dictionary tables (SYS_TABLES and so on)
6. exclusive dict_sys.latch
2021-10-18 18:03:12 +03:00

92 lines
2.7 KiB
Plaintext

CREATE TABLE parent (a INT PRIMARY KEY) ENGINE=InnoDB;
INSERT INTO parent SET a=1;
CREATE TABLE child (a INT PRIMARY KEY, FOREIGN KEY (a) REFERENCES parent(a)
ON UPDATE CASCADE)
ENGINE=InnoDB;
INSERT INTO child SET a=1;
TRUNCATE TABLE parent;
ERROR 42000: Cannot truncate a table referenced in a foreign key constraint (`test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`a`) REFERENCES `test`.`parent` (`a`))
TRUNCATE TABLE child;
INSERT INTO child SET a=1;
UPDATE parent SET a=2;
SELECT * FROM child;
a
2
connect dml,localhost,root;
SET DEBUG_SYNC='foreign_constraint_update_cascade SIGNAL fk WAIT_FOR go';
UPDATE parent SET a=3;
connection default;
SET DEBUG_SYNC='now WAIT_FOR fk';
SET lock_wait_timeout=1;
TRUNCATE TABLE child;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SET DEBUG_SYNC='now SIGNAL go';
connection dml;
SELECT * FROM child;
a
3
SET DEBUG_SYNC='foreign_constraint_check_for_update SIGNAL fk WAIT_FOR go';
DELETE FROM parent;
connection default;
SET DEBUG_SYNC='now WAIT_FOR fk';
SET lock_wait_timeout=1;
TRUNCATE TABLE child;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SET DEBUG_SYNC='now SIGNAL go';
connection dml;
ERROR 23000: Cannot delete or update a parent row: a foreign key constraint fails (`test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`a`) REFERENCES `parent` (`a`) ON UPDATE CASCADE)
SELECT * FROM child;
a
3
INSERT INTO parent SET a=5;
SET DEBUG_SYNC='foreign_constraint_check_for_ins SIGNAL fk WAIT_FOR go';
INSERT INTO child SET a=5;
connection default;
SET DEBUG_SYNC='now WAIT_FOR fk';
SET foreign_key_checks=0, innodb_lock_wait_timeout=0;
TRUNCATE TABLE parent;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SET DEBUG_SYNC='now SIGNAL go';
connection dml;
SELECT * FROM parent;
a
3
5
SELECT * FROM child;
a
3
5
disconnect dml;
connection default;
SET DEBUG_SYNC = RESET;
DROP TABLE child, parent;
#
# MDEV-24532 Table corruption ER_NO_SUCH_TABLE_IN_ENGINE or
# ER_CRASHED_ON_USAGE after ALTER on table with foreign key
#
CREATE TABLE t1 (a INT, b INT, PRIMARY KEY (a)) ENGINE=InnoDB;
ALTER TABLE t1 ADD FOREIGN KEY (b) REFERENCES t1 (a) ON UPDATE CASCADE;
LOCK TABLE t1 WRITE;
TRUNCATE TABLE t1;
ALTER TABLE t1 ADD c INT;
UNLOCK TABLES;
DROP TABLE t1;
#
# MDEV-26052 Assertion prebuilt->trx_id < table->def_trx_id failed
#
call mtr.add_suppression("InnoDB: In ALTER TABLE `test`\\.`t1` has or is");
CREATE TABLE t1 (pk INT, a INT, PRIMARY KEY (pk), KEY (a)) ENGINE=InnoDB;
SET FOREIGN_KEY_CHECKS=0;
ALTER TABLE t1 ADD FOREIGN KEY (a) REFERENCES t1 (a), ALGORITHM=COPY;
INSERT INTO t1 VALUES (1,1);
LOCK TABLES t1 WRITE;
TRUNCATE t1;
ERROR HY000: Cannot add foreign key constraint for `t1`
INSERT INTO t1 VALUES (2,2);
SELECT * FROM t1;
pk a
1 1
2 2
DROP TABLE t1;
# End of 10.6 tests