diff options
-rw-r--r-- | mysql-test/r/bug39022.result | 32 | ||||
-rw-r--r-- | mysql-test/t/bug39022.test | 58 | ||||
-rw-r--r-- | sql/sql_select.cc | 46 |
3 files changed, 125 insertions, 11 deletions
diff --git a/mysql-test/r/bug39022.result b/mysql-test/r/bug39022.result new file mode 100644 index 00000000000..1c02d7873e4 --- /dev/null +++ b/mysql-test/r/bug39022.result @@ -0,0 +1,32 @@ +# +# Bug #39022: Mysql randomly crashing in lock_sec_rec_cons_read_sees +# +CREATE TABLE t1(a TINYINT NOT NULL,b TINYINT,PRIMARY KEY(b)) ENGINE=innodb; +CREATE TABLE t2(d TINYINT NOT NULL,UNIQUE KEY(d)) ENGINE=innodb; +INSERT INTO t1 VALUES (13,0),(8,1),(9,2),(6,3), +(11,5),(11,6),(7,7),(7,8),(4,9),(6,10),(3,11),(11,12), +(12,13),(7,14); +INSERT INTO t2 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10), +(11),(12),(13),(14); +# in thread1 +START TRANSACTION; +# in thread2 +REPLACE INTO t2 VALUES (-17); +SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); +d +# in thread1 +REPLACE INTO t1(a,b) VALUES (67,20); +# in thread2 +COMMIT; +START TRANSACTION; +REPLACE INTO t1(a,b) VALUES (65,-50); +REPLACE INTO t2 VALUES (-91); +SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); +# in thread1 +# should not crash +SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +# in thread2 +d +# in default +DROP TABLE t1,t2; diff --git a/mysql-test/t/bug39022.test b/mysql-test/t/bug39022.test new file mode 100644 index 00000000000..1a1d10f5592 --- /dev/null +++ b/mysql-test/t/bug39022.test @@ -0,0 +1,58 @@ +-- source include/have_log_bin.inc +-- source include/have_innodb.inc + +--echo # +--echo # Bug #39022: Mysql randomly crashing in lock_sec_rec_cons_read_sees +--echo # + +CREATE TABLE t1(a TINYINT NOT NULL,b TINYINT,PRIMARY KEY(b)) ENGINE=innodb; +CREATE TABLE t2(d TINYINT NOT NULL,UNIQUE KEY(d)) ENGINE=innodb; +INSERT INTO t1 VALUES (13,0),(8,1),(9,2),(6,3), +(11,5),(11,6),(7,7),(7,8),(4,9),(6,10),(3,11),(11,12), +(12,13),(7,14); +INSERT INTO t2 VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10), +(11),(12),(13),(14); + +connect (thread1, localhost, root,,); +connect (thread2, localhost, root,,); + +connection thread1; +--echo # in thread1 +START TRANSACTION; + +connection thread2; +--echo # in thread2 +REPLACE INTO t2 VALUES (-17); +SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); + +connection thread1; +--echo # in thread1 +REPLACE INTO t1(a,b) VALUES (67,20); + +connection thread2; +--echo # in thread2 +COMMIT; +START TRANSACTION; +REPLACE INTO t1(a,b) VALUES (65,-50); +REPLACE INTO t2 VALUES (-91); +send; +SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); #waits + +connection thread1; +--echo # in thread1 + +--echo # should not crash +--error ER_LOCK_DEADLOCK +SELECT d FROM t2,t1 WHERE d=(SELECT MAX(a) FROM t1 WHERE t1.a > t2.d); #crashes + +connection thread2; +--echo # in thread2 +REAP; + +connection default; +--echo # in default + +disconnect thread1; +disconnect thread2; + +DROP TABLE t1,t2; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 9136b2e57e3..bdd139edafc 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -11521,21 +11521,45 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last) return NESTED_LOOP_KILLED; // Aborted by user /* purecov: inspected */ } SQL_SELECT *select=join_tab->select; - if (rc == NESTED_LOOP_OK && - (!join_tab->cache.select || !join_tab->cache.select->skip_record())) + if (rc == NESTED_LOOP_OK) { - uint i; - reset_cache_read(&join_tab->cache); - for (i=(join_tab->cache.records- (skip_last ? 1 : 0)) ; i-- > 0 ;) + bool consider_record= !join_tab->cache.select || + !join_tab->cache.select->skip_record(); + + /* + Check for error: skip_record() can execute code by calling + Item_subselect::val_*. We need to check for errors (if any) + after such call. + */ + if (join->thd->is_error()) + { + reset_cache_write(&join_tab->cache); + return NESTED_LOOP_ERROR; + } + + if (consider_record) { - read_cached_record(join_tab); - if (!select || !select->skip_record()) + uint i; + reset_cache_read(&join_tab->cache); + for (i=(join_tab->cache.records- (skip_last ? 1 : 0)) ; i-- > 0 ;) { - rc= (join_tab->next_select)(join,join_tab+1,0); - if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS) + read_cached_record(join_tab); + if (!select || !select->skip_record()) { - reset_cache_write(&join_tab->cache); - return rc; + /* + Check for error: skip_record() can execute code by calling + Item_subselect::val_*. We need to check for errors (if any) + after such call. + */ + if (join->thd->is_error()) + rc= NESTED_LOOP_ERROR; + else + rc= (join_tab->next_select)(join,join_tab+1,0); + if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS) + { + reset_cache_write(&join_tab->cache); + return rc; + } } } } |