Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
MDEV-10087 mysqld_update()/mysql_delete() continues execution even af…
…ter subquery with JOIN gets error from storage engine The issue is that record_should_be_deleted() returns true in mysql_delete() even if sub-select with join gets error from storage engine when DELETE FROM ... WHERE ... IN (SELECT ...) statement is executed. The same is true for mysql_update() where select->skip_record() returns true even if sub-select with join gets error from storage engine. In the test case if sub-select is chosen as deadlock victim the whole transaction is rolled back during sub-select execution, but mysql_delete()/mysql_update() continues transaction execution and invokes table->delete_row() as record_should_be_deleted() wrongly returns true in mysql_delete() and table->update_row() as select->skip_record(thd) wrongly returns 1 for mysql_update(). record_should_be_deleted() wrogly returns true because thd->is_error() returns false SQL_SELECT::skip_record() invoked from record_should_be_deleted(). It's supposed that THD error should be set in rr_handle_error() called from rr_sequential() during sub-select JOIN::exec_inner() execution. But rr_handle_error() does not set THD error because READ_RECORD::print_error is not set in JOIN_TAB::read_record. READ_RECORD::print_error should be initialized in init_read_record()/init_read_record_idx(). But make_join_readinfo() does not invoke init_read_record()/init_read_record_idx() for JOIN_TAB::read_record. The fix is to set JOIN_TAB::read_record.print_error in make_join_readinfo(), i.e. in the same place where JOIN_TAB::read_record.table is set. Reviewed by Sergey Petrunya.
- Loading branch information
1 parent
0ffdcf6
commit 0235a52
Showing
3 changed files
with
132 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| CREATE TABLE t1 ( | ||
| pkey int NOT NULL PRIMARY KEY, | ||
| c int | ||
| ) ENGINE=InnoDB; | ||
| INSERT INTO t1 VALUES(1,1); | ||
| CREATE TABLE t2 ( | ||
| pkey int NOT NULL PRIMARY KEY, | ||
| c int | ||
| ) ENGINE=InnoDB DEFAULT CHARSET=latin1; | ||
| INSERT INTO t2 VALUES (2, NULL); | ||
| CREATE TABLE t3 (c int) engine = InnoDB; | ||
| INSERT INTO t3 VALUES (10), (20), (30), (40), (50); | ||
| connect con1, localhost,root,,; | ||
| connection default; | ||
| START TRANSACTION; | ||
| UPDATE t3 SET c=c+1000; | ||
| SELECT * FROM t1 FOR UPDATE; | ||
| pkey c | ||
| 1 1 | ||
| connection con1; | ||
| START TRANSACTION; | ||
| DELETE FROM t2 WHERE c NOT IN (SELECT ref_0.pkey FROM t1 AS ref_0 INNER JOIN t1 AS ref_1 ON ref_0.c = ref_0.pkey); | ||
| connection default; | ||
| SELECT * FROM t2 FOR UPDATE; | ||
| pkey c | ||
| 2 NULL | ||
| COMMIT; | ||
| connection con1; | ||
| ERROR 40001: Deadlock found when trying to get lock; try restarting transaction | ||
| COMMIT; | ||
| connection default; | ||
| START TRANSACTION; | ||
| UPDATE t3 SET c=c+1000; | ||
| SELECT * FROM t1 FOR UPDATE; | ||
| pkey c | ||
| 1 1 | ||
| connection con1; | ||
| START TRANSACTION; | ||
| UPDATE t2 SET pkey=pkey+10 WHERE c NOT IN (SELECT ref_0.pkey FROM t1 AS ref_0 INNER JOIN t1 AS ref_1 ON ref_0.c = ref_0.pkey); | ||
| connection default; | ||
| SELECT * FROM t2 FOR UPDATE; | ||
| pkey c | ||
| 2 NULL | ||
| COMMIT; | ||
| connection con1; | ||
| ERROR 40001: Deadlock found when trying to get lock; try restarting transaction | ||
| COMMIT; | ||
| disconnect con1; | ||
| connection default; | ||
| DROP TABLE t1,t2,t3; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| --source include/have_innodb.inc | ||
| --source include/count_sessions.inc | ||
|
|
||
| CREATE TABLE t1 ( | ||
| pkey int NOT NULL PRIMARY KEY, | ||
| c int | ||
| ) ENGINE=InnoDB; | ||
|
|
||
| INSERT INTO t1 VALUES(1,1); | ||
|
|
||
| CREATE TABLE t2 ( | ||
| pkey int NOT NULL PRIMARY KEY, | ||
| c int | ||
| ) ENGINE=InnoDB DEFAULT CHARSET=latin1; | ||
|
|
||
| INSERT INTO t2 VALUES (2, NULL); | ||
|
|
||
| # The following table is to increase tansaction weight on deadlock resolution | ||
| CREATE TABLE t3 (c int) engine = InnoDB; | ||
| INSERT INTO t3 VALUES (10), (20), (30), (40), (50); | ||
|
|
||
| --let $i= 2 | ||
| --let $delete= 2 | ||
| --let $update= 1 | ||
| --connect(con1, localhost,root,,) | ||
|
|
||
| while($i) { | ||
| --connection default | ||
| START TRANSACTION; # trx 1 | ||
| # The following update is necessary to increase the transaction weight, which is | ||
| # calculated as the number of locks + the number of undo records during deadlock | ||
| # report. Victim's transaction should have minimum weight. We need trx 2 to be | ||
| # choosen as victim, that's why we need to increase the current transaction | ||
| # weight. | ||
| UPDATE t3 SET c=c+1000; | ||
| SELECT * FROM t1 FOR UPDATE; | ||
|
|
||
| --connection con1 | ||
| START TRANSACTION; # trx 2 | ||
| # 1) read record from t2, lock it | ||
| # 2) check if the read record should be deleted, i.e. read record from t1, | ||
| # as the record from t1 is locked by trx 1, the subselect will be suspended. | ||
| # see 'while' loop in mysql_delete() or mysql_update() and | ||
| # select->skip_record(thd) call for details. | ||
| if ($i == $delete) { | ||
| --send DELETE FROM t2 WHERE c NOT IN (SELECT ref_0.pkey FROM t1 AS ref_0 INNER JOIN t1 AS ref_1 ON ref_0.c = ref_0.pkey) | ||
| } | ||
| if ($i == $update) { | ||
| --send UPDATE t2 SET pkey=pkey+10 WHERE c NOT IN (SELECT ref_0.pkey FROM t1 AS ref_0 INNER JOIN t1 AS ref_1 ON ref_0.c = ref_0.pkey) | ||
| } | ||
|
|
||
| --connection default | ||
| let $wait_condition= | ||
| SELECT count(*) = 1 FROM information_schema.processlist | ||
| WHERE (state = 'Sending data' OR state = "Updating") | ||
| AND (info LIKE 'delete from t2 where%' OR | ||
| info LIKE 'UPDATE t2 SET pkey=pkey+10 WHERE%'); | ||
| --source include/wait_condition.inc | ||
|
|
||
| # The record from t2 is locked by the previous delete, so trx 2 is waiting for | ||
| # trx 1, and trx 1 will be blocked by trx 2 with the following SELECT. So we | ||
| # have deadlock here. And trx 2 is chosen as deadlock victim as trx 1 has | ||
| # greater weight. | ||
| SELECT * FROM t2 FOR UPDATE; | ||
| COMMIT; | ||
|
|
||
| --connection con1 | ||
| # If the bug is not fixed, there will be assertion failure as | ||
| # mysql_delete()/mysql_update() will continue execution despite its subselect | ||
| # got deadlock error | ||
| --error ER_LOCK_DEADLOCK | ||
| --reap | ||
| COMMIT; | ||
| --dec $i | ||
| } | ||
|
|
||
| --disconnect con1 | ||
|
|
||
| --connection default | ||
| DROP TABLE t1,t2,t3; | ||
| --source include/wait_until_count_sessions.inc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters