Commit 43ea968d authored by Vasil Dimov's avatar Vasil Dimov

Fix Bug#13510739 63775: SERVER CRASH ON HANDLER READ NEXT AFTER DELETE RECORD.

CREATE TABLE bug13510739 (c INTEGER NOT NULL, PRIMARY KEY (c)) ENGINE=INNODB;
INSERT INTO bug13510739 VALUES (1), (2), (3), (4);
DELETE FROM bug13510739 WHERE c=2;
HANDLER bug13510739 OPEN;
HANDLER bug13510739 READ `primary` = (2);
HANDLER bug13510739 READ `primary` NEXT;  <-- crash

The bug is that in the particular testcase row_search_for_mysql() picked up
a delete-marked record and quit, leaving the cursor non-positioned state and
on the subsequent 'get next' call the code crashed because of the
non-positioned cursor.

In row0sel.cc (line numbers from mysql-trunk):

4653         if (rec_get_deleted_flag(rec, comp)) {
...
4679                 if (index == clust_index && unique_search) {
4680 
4681                         err = DB_RECORD_NOT_FOUND;
4682                         
4683                         goto normal_return;
4684                 }       

it quit from here, not storing the cursor position.

In contrast, if the record=2 is not found at all (e.g. sleep(1) after DELETE
to let the purge wipe it away completely) then 'get = 2' does find record=3
and quits from here:

4366                 if (0 != cmp_dtuple_rec(search_tuple, rec, offsets)) {
...
4394                         btr_pcur_store_position(pcur, &mtr);
4395 
4396                         err = DB_RECORD_NOT_FOUND;
4397 #if 0
4398                         ut_print_name(stderr, trx, FALSE, index->name);
4399                         fputs(" record not found 3\n", stderr);
4400 #endif
4401 
4402                         goto normal_return;

Another fix could be to extend the condition on line 4366 to hold only if
seach_tuple matches rec AND if rec is not delete marked.

Notice that in the above test case if we wait about 1 second somewhere after
DELETE and before 'get = 2', then the testcase does not crash and returns 4
instead. Not sure if this is the correct behavior, but this bugfix removes
the crash and makes the code return what it also returns in the non-crashing
case (if rec=2 is not found during 'get = 2', e.g. we have sleep(1) there).

Approved by:	Marko (http://bur03.no.oracle.com/rb/r/863/)
parent b3c3b9f0
CREATE TABLE bug13510739 (c INTEGER NOT NULL, PRIMARY KEY (c)) ENGINE=INNODB;
INSERT INTO bug13510739 VALUES (1), (2), (3), (4);
DELETE FROM bug13510739 WHERE c=2;
HANDLER bug13510739 OPEN;
HANDLER bug13510739 READ `primary` = (2);
c
HANDLER bug13510739 READ `primary` NEXT;
c
4
DROP TABLE bug13510739;
#
# Bug#13510739 63775: SERVER CRASH ON HANDLER READ NEXT AFTER DELETE RECORD.
#
-- source include/have_innodb.inc
CREATE TABLE bug13510739 (c INTEGER NOT NULL, PRIMARY KEY (c)) ENGINE=INNODB;
INSERT INTO bug13510739 VALUES (1), (2), (3), (4);
DELETE FROM bug13510739 WHERE c=2;
HANDLER bug13510739 OPEN;
HANDLER bug13510739 READ `primary` = (2);
# this one crashes the server if the bug is present
HANDLER bug13510739 READ `primary` NEXT;
DROP TABLE bug13510739;
CREATE TABLE bug13510739 (c INTEGER NOT NULL, PRIMARY KEY (c)) ENGINE=INNODB;
INSERT INTO bug13510739 VALUES (1), (2), (3), (4);
DELETE FROM bug13510739 WHERE c=2;
HANDLER bug13510739 OPEN;
HANDLER bug13510739 READ `primary` = (2);
c
HANDLER bug13510739 READ `primary` NEXT;
c
4
DROP TABLE bug13510739;
#
# Bug#13510739 63775: SERVER CRASH ON HANDLER READ NEXT AFTER DELETE RECORD.
#
-- source include/have_innodb_plugin.inc
CREATE TABLE bug13510739 (c INTEGER NOT NULL, PRIMARY KEY (c)) ENGINE=INNODB;
INSERT INTO bug13510739 VALUES (1), (2), (3), (4);
DELETE FROM bug13510739 WHERE c=2;
HANDLER bug13510739 OPEN;
HANDLER bug13510739 READ `primary` = (2);
# this one crashes the server if the bug is present
HANDLER bug13510739 READ `primary` NEXT;
DROP TABLE bug13510739;
...@@ -4208,7 +4208,9 @@ no_gap_lock: ...@@ -4208,7 +4208,9 @@ no_gap_lock:
applicable to unique secondary indexes. Current behaviour is applicable to unique secondary indexes. Current behaviour is
to widen the scope of a lock on an already delete marked record to widen the scope of a lock on an already delete marked record
if the same record is deleted twice by the same transaction */ if the same record is deleted twice by the same transaction */
if (index == clust_index && unique_search) { if (index == clust_index && unique_search
&& !prebuilt->used_in_HANDLER) {
err = DB_RECORD_NOT_FOUND; err = DB_RECORD_NOT_FOUND;
goto normal_return; goto normal_return;
......
2011-12-22 The InnoDB Team
* row/row0sel.c:
Fix Bug#63775 Server crash on handler read next after delete record.
2011-12-13 The InnoDB Team 2011-12-13 The InnoDB Team
* handler/ha_innodb.cc, innodb.test, innodb.result: * handler/ha_innodb.cc, innodb.test, innodb.result:
......
...@@ -4362,7 +4362,9 @@ no_gap_lock: ...@@ -4362,7 +4362,9 @@ no_gap_lock:
applicable to unique secondary indexes. Current behaviour is applicable to unique secondary indexes. Current behaviour is
to widen the scope of a lock on an already delete marked record to widen the scope of a lock on an already delete marked record
if the same record is deleted twice by the same transaction */ if the same record is deleted twice by the same transaction */
if (index == clust_index && unique_search) { if (index == clust_index && unique_search
&& !prebuilt->used_in_HANDLER) {
err = DB_RECORD_NOT_FOUND; err = DB_RECORD_NOT_FOUND;
goto normal_return; goto normal_return;
......
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