| Bug #44207 | LEFT JOIN Multi-table DELETE fails to delete records when using foreign keys | ||
|---|---|---|---|
| Submitted: | 10 Apr 2009 15:08 | Modified: | 10 Apr 2009 16:36 |
| Reporter: | Sergey Petrunya | Email Updates: | |
| Status: | Verified | Impact on me: | |
| Category: | MySQL Server | Severity: | S3 (Non-critical) |
| Version: | 5.1/5.0/6.0 | OS: | Any |
| Assigned to: | Assigned Account | CPU Architecture: | Any |
[10 Apr 2009 15:38]
Sergey Petrunya
It is trying to delete records from the 1st table while running the join: (gdb) wher #0 multi_delete::send_data (this=0x8f3cd60, values=@0x8ee68c0) at sql_delete.cc:712 #1 0x0832e486 in end_send (join=0x8f44758, join_tab=0x8f46d60, end_of_records=false) at sql_select.cc:11912 #2 0x08334e8f in evaluate_join_record (join=0x8f44758, join_tab=0x8f46bd8, error=0) at sql_select.cc:11172 #3 0x08335084 in sub_select (join=0x8f44758, join_tab=0x8f46bd8, end_of_records=false) at sql_select.cc:11057 #4 0x08334e8f in evaluate_join_record (join=0x8f44758, join_tab=0x8f46a50, error=0) at sql_select.cc:11172 #5 0x08335084 in sub_select (join=0x8f44758, join_tab=0x8f46a50, end_of_records=false) at sql_select.cc:11057 #6 0x08334e8f in evaluate_join_record (join=0x8f44758, join_tab=0x8f468c8, error=0) at sql_select.cc:11172 #7 0x08335084 in sub_select (join=0x8f44758, join_tab=0x8f468c8, end_of_records=false) at sql_select.cc:11057 #8 0x08334e8f in evaluate_join_record (join=0x8f44758, join_tab=0x8f46740, error=0) at sql_select.cc:11172 #9 0x08335084 in sub_select (join=0x8f44758, join_tab=0x8f46740, end_of_records=false) at sql_select.cc:11057 #10 0x0833d4c8 in do_select (join=0x8f44758, fields=0x8ee68c0, table=0x0, procedure=0x0) at sql_select.cc:10813 #11 0x08356b45 in JOIN::exec (this=0x8f44758) at sql_select.cc:2182 #12 0x0835199a in mysql_select (thd=0x8ee5490, rref_pointer_array=0x8ee6924, tables=0x8f3b538, wild_num=0, fields=@0x8ee68c0, conds=0x8f3cc08, og_num=0, order=0x0, group=0x0, having=0x0, proc_param=0x0, select_options=3489942144, result=0x8f3cd60, unit=0x8ee65b4, select_lex=0x8ee682c) at sql_select.cc:2361 #13 0x082cf146 in mysql_execute_command (thd=0x8ee5490) at sql_parse.cc:3242 ... (gdb) print delete_while_scanning $9 = true (gdb) print secure_counter $10 = -1 Deletion in one table causes InnoDB to immediately delete records to satisfy PK-FK relationships, which then causes the join to miss records and not delete them.
[10 Apr 2009 15:39]
Sergey Petrunya
If I use debugger to set delete_while_scanning=0 in multi_delete::initialize_tables(), then the DELETE statement works correctly.
[10 Apr 2009 15:46]
Sergey Petrunya
The exact criteria of when it's possible to have delete_while_scanning=TRUE are quite complicated as they depend on the join order and whether the PK<->FK relationship(s) are one-to-one or one-to-many. First attempt: "If a multi-table DELETE deletes from a table which is a (direct or indirect) PK/FK ancestor of some other table which is also used in the DELETE statement, set delete_while_scanning=FALSE".
[10 Apr 2009 16:36]
MySQL Verification Team
Thank you for the bug report, verified as described. 5.0.51b and 5.1.26 older released versions present same behavior as well.

Description: (Problem reported by Sergey Sytnik at Sun Tech Days) LEFT JOIN multi-table DELETE fails to delete records when run over tables with certain PK/FK relationship. How to repeat: CREATE TABLE a ( id BIGINT NOT NULL AUTO_INCREMENT, PRIMARY KEY (id), uid BIGINT ) ENGINE=InnoDB; CREATE TABLE b ( id BIGINT NOT NULL AUTO_INCREMENT, PRIMARY KEY (id), a_id BIGINT NOT NULL, CONSTRAINT fk_a__id FOREIGN KEY fk_idx_a__id (a_id) REFERENCES a (id) ON DELETE CASCADE ) ENGINE=InnoDB; CREATE TABLE c ( id BIGINT NOT NULL AUTO_INCREMENT, PRIMARY KEY (id) ) ENGINE=InnoDB; CREATE TABLE d ( id BIGINT NOT NULL AUTO_INCREMENT, PRIMARY KEY (id), b_id BIGINT NOT NULL, CONSTRAINT fk_b__id FOREIGN KEY fk_idx_b__id (b_id) REFERENCES b (id) ON DELETE CASCADE, c_id BIGINT NULL, CONSTRAINT fk_c__id FOREIGN KEY fk_idx_c__id (c_id) REFERENCES c (id) ON DELETE CASCADE ) ENGINE=InnoDB; INSERT INTO a VALUES (1, 123), (2, 456), (3, 123); INSERT INTO b VALUES (1, 1), (2, 2), (3, 3), (4, 3); INSERT INTO c VALUES (1), (2), (3), (4), (5), (6); INSERT INTO d VALUES (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 4, 5); mysql> SELECT '=== BEFORE ===' AS 'dump'; SELECT *, 'a' AS name FROM a; SELECT *, 'b' AS name FROM b; SE LECT *, 'c' AS name FROM c; SELECT *, 'd' AS name FROM d; +----------------+ | dump | +----------------+ | === BEFORE === | +----------------+ 1 row in set (0.00 sec) +----+------+------+ | id | uid | name | +----+------+------+ | 1 | 123 | a | | 2 | 456 | a | | 3 | 123 | a | +----+------+------+ 3 rows in set (0.00 sec) +----+------+------+ | id | a_id | name | +----+------+------+ | 1 | 1 | b | | 2 | 2 | b | | 3 | 3 | b | | 4 | 3 | b | +----+------+------+ 4 rows in set (0.00 sec) +----+------+ | id | name | +----+------+ | 1 | c | | 2 | c | | 3 | c | | 4 | c | | 5 | c | | 6 | c | +----+------+ 6 rows in set (0.00 sec) +----+------+------+------+ | id | b_id | c_id | name | +----+------+------+------+ | 1 | 1 | 1 | d | | 2 | 2 | 2 | d | | 3 | 3 | 3 | d | | 4 | 4 | 4 | d | | 5 | 4 | 5 | d | +----+------+------+------+ 5 rows in set (0.00 sec) mysql> -- Run SELECT mysql> mysql> SELECT a.*, c.*, '=== ? ===' AS 'test_select' -> FROM a -> LEFT JOIN b ON b.a_id = a.id -> LEFT JOIN d ON d.b_id = b.id -> LEFT JOIN c ON c.id = d.c_id -> WHERE a.uid = 123; +----+------+------+-------------+ | id | uid | id | test_select | +----+------+------+-------------+ | 1 | 123 | 1 | === ? === | | 3 | 123 | 3 | === ? === | | 3 | 123 | 4 | === ? === | | 3 | 123 | 5 | === ? === | +----+------+------+-------------+ 4 rows in set (0.35 sec) mysql> -- Run DELETE mysql> mysql> DELETE a, c -> FROM a -> LEFT JOIN b ON b.a_id = a.id -> LEFT JOIN d ON d.b_id = b.id -> LEFT JOIN c ON c.id = d.c_id -> WHERE a.uid = 123; Query OK, 4 rows affected (1.13 sec) mysql> mysql> -- Show contents (after) mysql> mysql> SELECT '=== AFTER ===' AS 'dump'; SELECT *, 'a' AS name FROM a; SELECT *, 'b' AS name FROM b; SELECT *, 'c' AS name FROM c; SELECT *, 'd' AS name FROM d; +---------------+ | dump | +---------------+ | === AFTER === | +---------------+ 1 row in set (0.00 sec) +----+------+------+ | id | uid | name | +----+------+------+ | 2 | 456 | a | (OK) +----+------+------+ 1 row in set (0.00 sec) +----+------+------+ | id | a_id | name | +----+------+------+ | 2 | 2 | b | (OK) +----+------+------+ 1 row in set (0.00 sec) +----+------+ | id | name | +----+------+ | 2 | c | | 4 | c | <-- this should have been deleted. | 5 | c | <-- this should have been deleted. | 6 | c | +----+------+ 4 rows in set (0.00 sec) +----+------+------+------+ | id | b_id | c_id | name | +----+------+------+------+ | 2 | 2 | 2 | d | (OK) +----+------+------+------+ 1 row in set (0.00 sec)