From 86505c3c545d3866a85464b0762b452d85599993 Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Thu, 22 Dec 2011 12:55:44 +0200 Subject: [PATCH] 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/) --- .../suite/innodb/r/innodb_bug13510739.result | 10 ++++++++++ .../suite/innodb/t/innodb_bug13510739.test | 20 +++++++++++++++++++ .../innodb_plugin/r/innodb_bug13510739.result | 10 ++++++++++ .../innodb_plugin/t/innodb_bug13510739.test | 20 +++++++++++++++++++ storage/innobase/row/row0sel.c | 4 +++- storage/innodb_plugin/ChangeLog | 5 +++++ storage/innodb_plugin/row/row0sel.c | 4 +++- 7 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 mysql-test/suite/innodb/r/innodb_bug13510739.result create mode 100644 mysql-test/suite/innodb/t/innodb_bug13510739.test create mode 100644 mysql-test/suite/innodb_plugin/r/innodb_bug13510739.result create mode 100644 mysql-test/suite/innodb_plugin/t/innodb_bug13510739.test diff --git a/mysql-test/suite/innodb/r/innodb_bug13510739.result b/mysql-test/suite/innodb/r/innodb_bug13510739.result new file mode 100644 index 00000000000..8aa4323eeb0 --- /dev/null +++ b/mysql-test/suite/innodb/r/innodb_bug13510739.result @@ -0,0 +1,10 @@ +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; diff --git a/mysql-test/suite/innodb/t/innodb_bug13510739.test b/mysql-test/suite/innodb/t/innodb_bug13510739.test new file mode 100644 index 00000000000..f10bcd8e272 --- /dev/null +++ b/mysql-test/suite/innodb/t/innodb_bug13510739.test @@ -0,0 +1,20 @@ +# +# 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; diff --git a/mysql-test/suite/innodb_plugin/r/innodb_bug13510739.result b/mysql-test/suite/innodb_plugin/r/innodb_bug13510739.result new file mode 100644 index 00000000000..8aa4323eeb0 --- /dev/null +++ b/mysql-test/suite/innodb_plugin/r/innodb_bug13510739.result @@ -0,0 +1,10 @@ +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; diff --git a/mysql-test/suite/innodb_plugin/t/innodb_bug13510739.test b/mysql-test/suite/innodb_plugin/t/innodb_bug13510739.test new file mode 100644 index 00000000000..b8ffcc7b22b --- /dev/null +++ b/mysql-test/suite/innodb_plugin/t/innodb_bug13510739.test @@ -0,0 +1,20 @@ +# +# 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; diff --git a/storage/innobase/row/row0sel.c b/storage/innobase/row/row0sel.c index 9ab6424a012..915cc8339d4 100644 --- a/storage/innobase/row/row0sel.c +++ b/storage/innobase/row/row0sel.c @@ -4208,7 +4208,9 @@ no_gap_lock: applicable to unique secondary indexes. Current behaviour is 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 (index == clust_index && unique_search) { + if (index == clust_index && unique_search + && !prebuilt->used_in_HANDLER) { + err = DB_RECORD_NOT_FOUND; goto normal_return; diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index d836b390c52..ab0c68ee297 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,8 @@ +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 * handler/ha_innodb.cc, innodb.test, innodb.result: diff --git a/storage/innodb_plugin/row/row0sel.c b/storage/innodb_plugin/row/row0sel.c index 32f21dbe198..54172e71a47 100644 --- a/storage/innodb_plugin/row/row0sel.c +++ b/storage/innodb_plugin/row/row0sel.c @@ -4362,7 +4362,9 @@ no_gap_lock: applicable to unique secondary indexes. Current behaviour is 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 (index == clust_index && unique_search) { + if (index == clust_index && unique_search + && !prebuilt->used_in_HANDLER) { + err = DB_RECORD_NOT_FOUND; goto normal_return;