Commit 410e1a72 authored by Georgi Kodinov's avatar Georgi Kodinov

Bug #40113: Embedded SELECT inside UPDATE or DELETE can timeout

without error

When using quick access methods for searching rows in UPDATE or 
DELETE there was no check if a fatal error was not already sent 
to the client while evaluating the quick condition.
As a result a false OK (following the error) was sent to the 
client and the error was thus transformed into a warning.

Fixed by checking for errors sent to the client during 
SQL_SELECT::check_quick() and treating them as real errors.

Fixed a wrong test case in group_min_max.test
Fixed a wrong return code in mysql_update() and mysql_delete()

mysql-test/r/bug40113.result:
  Bug #40013: test case
mysql-test/r/group_min_max.result:
  Bug #40013: fixed a wrong test case
mysql-test/t/bug40113-master.opt:
  Bug #40013: test case
mysql-test/t/bug40113.test:
  Bug #40013: test case
mysql-test/t/group_min_max.test:
  Bug #40013: fixed a wrong test case
sql/sql_delete.cc:
  Bug #40113: check for errors evaluating the quick select
sql/sql_update.cc:
  Bug #40113: check for errors evaluating the quick select
parent 54414741
#
# Bug #40113: Embedded SELECT inside UPDATE or DELETE can timeout
# without error
#
CREATE TABLE t1 (a int, b int, PRIMARY KEY (a,b)) ENGINE=InnoDB;
INSERT INTO t1 (a,b) VALUES (1070109,99);
CREATE TABLE t2 (b int, a int, PRIMARY KEY (b)) ENGINE=InnoDB;
INSERT INTO t2 (b,a) VALUES (7,1070109);
SELECT * FROM t1;
a b
1070109 99
BEGIN;
SELECT b FROM t2 WHERE b=7 FOR UPDATE;
b
7
BEGIN;
SELECT b FROM t2 WHERE b=7 FOR UPDATE;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
INSERT INTO t1 (a) VALUES ((SELECT a FROM t2 WHERE b=7));
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
UPDATE t1 SET a='7000000' WHERE a=(SELECT a FROM t2 WHERE b=7);
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
DELETE FROM t1 WHERE a=(SELECT a FROM t2 WHERE b=7);
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SELECT * FROM t1;
a b
1070109 99
DROP TABLE t2, t1;
End of 5.0 tests
...@@ -2278,12 +2278,10 @@ Handler_read_key 8 ...@@ -2278,12 +2278,10 @@ Handler_read_key 8
Handler_read_next 0 Handler_read_next 0
FLUSH STATUS; FLUSH STATUS;
DELETE FROM t3 WHERE (SELECT (SELECT MAX(b) FROM t1 GROUP BY a HAVING a < 2) x DELETE FROM t3 WHERE (SELECT (SELECT MAX(b) FROM t1 GROUP BY a HAVING a < 2) x
FROM t1) > 10000; FROM t1 WHERE a = 1 AND b = 1) > 10000;
Warnings:
Error 1242 Subquery returns more than 1 row
SHOW STATUS LIKE 'handler_read__e%'; SHOW STATUS LIKE 'handler_read__e%';
Variable_name Value Variable_name Value
Handler_read_key 8 Handler_read_key 9
Handler_read_next 1 Handler_read_next 1
DROP TABLE t1,t2,t3; DROP TABLE t1,t2,t3;
CREATE TABLE t1 (a int, INDEX idx(a)); CREATE TABLE t1 (a int, INDEX idx(a));
......
--innodb_lock_wait_timeout=1
--source include/have_innodb.inc
--echo #
--echo # Bug #40113: Embedded SELECT inside UPDATE or DELETE can timeout
--echo # without error
--echo #
CREATE TABLE t1 (a int, b int, PRIMARY KEY (a,b)) ENGINE=InnoDB;
INSERT INTO t1 (a,b) VALUES (1070109,99);
CREATE TABLE t2 (b int, a int, PRIMARY KEY (b)) ENGINE=InnoDB;
INSERT INTO t2 (b,a) VALUES (7,1070109);
SELECT * FROM t1;
BEGIN;
SELECT b FROM t2 WHERE b=7 FOR UPDATE;
CONNECT (addconroot, localhost, root,,);
CONNECTION addconroot;
BEGIN;
--error ER_LOCK_WAIT_TIMEOUT
SELECT b FROM t2 WHERE b=7 FOR UPDATE;
--error ER_LOCK_WAIT_TIMEOUT
INSERT INTO t1 (a) VALUES ((SELECT a FROM t2 WHERE b=7));
--error ER_LOCK_WAIT_TIMEOUT
UPDATE t1 SET a='7000000' WHERE a=(SELECT a FROM t2 WHERE b=7);
--error ER_LOCK_WAIT_TIMEOUT
DELETE FROM t1 WHERE a=(SELECT a FROM t2 WHERE b=7);
SELECT * FROM t1;
CONNECTION default;
DISCONNECT addconroot;
DROP TABLE t2, t1;
--echo End of 5.0 tests
...@@ -866,7 +866,7 @@ DELETE FROM t3 WHERE (SELECT MAX(b) FROM t1 GROUP BY a HAVING a < 2) > 10000; ...@@ -866,7 +866,7 @@ DELETE FROM t3 WHERE (SELECT MAX(b) FROM t1 GROUP BY a HAVING a < 2) > 10000;
SHOW STATUS LIKE 'handler_read__e%'; SHOW STATUS LIKE 'handler_read__e%';
FLUSH STATUS; FLUSH STATUS;
DELETE FROM t3 WHERE (SELECT (SELECT MAX(b) FROM t1 GROUP BY a HAVING a < 2) x DELETE FROM t3 WHERE (SELECT (SELECT MAX(b) FROM t1 GROUP BY a HAVING a < 2) x
FROM t1) > 10000; FROM t1 WHERE a = 1 AND b = 1) > 10000;
SHOW STATUS LIKE 'handler_read__e%'; SHOW STATUS LIKE 'handler_read__e%';
DROP TABLE t1,t2,t3; DROP TABLE t1,t2,t3;
......
...@@ -144,6 +144,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ...@@ -144,6 +144,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
delete select; delete select;
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
thd->row_count_func= 0; thd->row_count_func= 0;
/*
Error was already created by quick select evaluation (check_quick()).
TODO: Add error code output parameter to Item::val_xxx() methods.
Currently they rely on the user checking DA for
errors when unwinding the stack after calling Item::val_xxx().
*/
if (thd->net.report_error)
DBUG_RETURN(TRUE);
send_ok(thd,0L); send_ok(thd,0L);
/* /*
...@@ -407,7 +415,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds) ...@@ -407,7 +415,7 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
if (select_lex->inner_refs_list.elements && if (select_lex->inner_refs_list.elements &&
fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array))
DBUG_RETURN(-1); DBUG_RETURN(TRUE);
select_lex->fix_prepare_information(thd, conds, &fake_conds); select_lex->fix_prepare_information(thd, conds, &fake_conds);
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
......
...@@ -230,7 +230,7 @@ int mysql_update(THD *thd, ...@@ -230,7 +230,7 @@ int mysql_update(THD *thd,
if (select_lex->inner_refs_list.elements && if (select_lex->inner_refs_list.elements &&
fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array))
DBUG_RETURN(-1); DBUG_RETURN(1);
if (conds) if (conds)
{ {
...@@ -247,7 +247,14 @@ int mysql_update(THD *thd, ...@@ -247,7 +247,14 @@ int mysql_update(THD *thd,
{ {
delete select; delete select;
free_underlaid_joins(thd, select_lex); free_underlaid_joins(thd, select_lex);
if (error) /*
There was an error or the error was already sent by
the quick select evaluation.
TODO: Add error code output parameter to Item::val_xxx() methods.
Currently they rely on the user checking DA for
errors when unwinding the stack after calling Item::val_xxx().
*/
if (error || thd->net.report_error)
{ {
DBUG_RETURN(1); // Error in where DBUG_RETURN(1); // Error in where
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment