From 7042dd38c6bed80bbb7e8ac2c75e5fd689f19836 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Fri, 15 Feb 2013 20:25:27 +0200 Subject: [PATCH] Fixed BUG#51763 Can't delete rows from MEMORY table with HASH key mysql-test/suite/heap/heap.result: Added test case mysql-test/suite/heap/heap.test: Added test case storage/heap/hp_delete.c: Fixed that we don't change order of keys for the current key when we delete them from the hash table. Fixed that 'current_hash_ptr' is correct after heap_delete_key(). Don't "reset current_hash_ptr" on delete; This will improve time a lot for delete of rows when not all rows matches the search criteria. --- mysql-test/suite/heap/heap.result | 19 +++++++++++++ mysql-test/suite/heap/heap.test | 24 ++++++++++++++++ storage/heap/hp_delete.c | 46 +++++++++++++++++++++++++------ 3 files changed, 80 insertions(+), 9 deletions(-) diff --git a/mysql-test/suite/heap/heap.result b/mysql-test/suite/heap/heap.result index ae562a0dda1..ff6b217bcce 100644 --- a/mysql-test/suite/heap/heap.result +++ b/mysql-test/suite/heap/heap.result @@ -790,3 +790,22 @@ select data_length,index_length from information_schema.tables where table_schem data_length index_length 81024 121024 drop table t1; +CREATE TABLE t1 ( +id int(11) NOT NULL AUTO_INCREMENT, +color enum('GREEN', 'WHITE') DEFAULT NULL, +ts int, +PRIMARY KEY (id), +KEY color (color) USING HASH +) ENGINE=MEMORY DEFAULT CHARSET=utf8; +INSERT INTO t1 VALUES("1","GREEN",1); +INSERT INTO t1 VALUES("2","GREEN",1); +INSERT INTO t1 VALUES("3","GREEN",1); +INSERT INTO t1 VALUES("4","GREEN",1); +INSERT INTO t1 VALUES("5","GREEN",1); +INSERT INTO t1 VALUES("6","GREEN",1); +DELETE FROM t1 WHERE id = 1; +INSERT INTO t1 VALUES("7","GREEN", 2); +DELETE FROM t1 WHERE ts = 1 AND color = 'GREEN'; +SELECT * from t1 WHERE ts = 1 AND color = 'GREEN'; +id color ts +DROP TABLE t1; diff --git a/mysql-test/suite/heap/heap.test b/mysql-test/suite/heap/heap.test index 6f5046af139..a421304d6d8 100644 --- a/mysql-test/suite/heap/heap.test +++ b/mysql-test/suite/heap/heap.test @@ -537,3 +537,27 @@ insert into t1 select rand(100000000) from t1; --replace_result 40512 81024 60512 121024 select data_length,index_length from information_schema.tables where table_schema="test" and table_name="t1"; drop table t1; + +# +# BUG#51763 Can't delete rows from MEMORY table with HASH key +# + +CREATE TABLE t1 ( + id int(11) NOT NULL AUTO_INCREMENT, + color enum('GREEN', 'WHITE') DEFAULT NULL, + ts int, + PRIMARY KEY (id), + KEY color (color) USING HASH +) ENGINE=MEMORY DEFAULT CHARSET=utf8; + +INSERT INTO t1 VALUES("1","GREEN",1); +INSERT INTO t1 VALUES("2","GREEN",1); +INSERT INTO t1 VALUES("3","GREEN",1); +INSERT INTO t1 VALUES("4","GREEN",1); +INSERT INTO t1 VALUES("5","GREEN",1); +INSERT INTO t1 VALUES("6","GREEN",1); +DELETE FROM t1 WHERE id = 1; +INSERT INTO t1 VALUES("7","GREEN", 2); +DELETE FROM t1 WHERE ts = 1 AND color = 'GREEN'; +SELECT * from t1 WHERE ts = 1 AND color = 'GREEN'; +DROP TABLE t1; diff --git a/storage/heap/hp_delete.c b/storage/heap/hp_delete.c index d00ac94a918..2bc7d00bfca 100644 --- a/storage/heap/hp_delete.c +++ b/storage/heap/hp_delete.c @@ -48,7 +48,6 @@ int heap_delete(HP_INFO *info, const uchar *record) pos[share->reclength]=0; /* Record deleted */ share->deleted++; share->key_version++; - info->current_hash_ptr=0; #if !defined(DBUG_OFF) && defined(EXTRA_HEAP_DEBUG) DBUG_EXECUTE("check_heap",heap_check_heap(info, 0);); #endif @@ -182,21 +181,50 @@ int hp_delete_key(HP_INFO *info, register HP_KEYDEF *keyinfo, } pos2= hp_mask(lastpos_hashnr, blength, share->records + 1); if (pos2 == hp_mask(pos_hashnr, blength, share->records + 1)) - { /* Identical key-positions */ + { + /* lastpos and the row in the main bucket entry (pos) has the same hash */ if (pos2 != share->records) { - empty[0]=lastpos[0]; + /* + The bucket entry was not deleted. Copy lastpos over the + deleted entry and update previous link to point to it. + */ + empty[0]= lastpos[0]; hp_movelink(lastpos, pos, empty); + if (last_ptr == lastpos) + { + /* + We moved the row that info->current_hash_ptr points to. + Update info->current_hash_ptr to point to the new position. + */ + info->current_hash_ptr= empty; + } DBUG_RETURN(0); } - pos3= pos; /* Link pos->next after lastpos */ - } - else - { - pos3= 0; /* Different positions merge */ - keyinfo->hash_buckets--; + /* + Shrinking the hash table deleted the main bucket entry for this hash. + In this case the last entry was the first key in the key chain. + We move things around so that we keep the original key order to ensure + that heap_rnext() works. + + - Move the row at the main bucket entry to the empty spot. + - Move the last entry first in the new chain. + - Link in the first element of the hash. + */ + empty[0]= pos[0]; + pos[0]= lastpos[0]; + hp_movelink(pos, pos, empty); + + /* Update current_hash_ptr if the entry moved */ + if (last_ptr == lastpos) + info->current_hash_ptr= pos; + else if (last_ptr == pos) + info->current_hash_ptr= empty; + DBUG_RETURN(0); } + pos3= 0; /* Different positions merge */ + keyinfo->hash_buckets--; empty[0]=lastpos[0]; hp_movelink(pos3, empty, pos->next_key); pos->next_key=empty;