From 18dc64eca2bbe29697043dc0ae51ae21d19af365 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sun, 26 Dec 2010 16:31:03 -0800 Subject: [PATCH] Fixed LP bug #694443. One of the hash functions employed by the BNLH join algorithm calculates the the value of hash index for key value utilizing every byte of the key buffer. To make this calculation valid one has to ensure that for any key value unused bytes of the buffer are filled with with a certain filler. We choose 0 as a filler for these bytes. Added an optional boolean parameter with_zerofill to the function key_copy. If the value of the parameter is TRUE all unused bytes of the key buffer is filled with 0. --- mysql-test/r/join_cache.result | 46 ++++++++++++++++++++++++++++++++++ mysql-test/t/join_cache.test | 31 +++++++++++++++++++++++ sql/key.cc | 9 +++++-- sql/mysql_priv.h | 3 ++- sql/sql_join_cache.cc | 2 +- 5 files changed, 87 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/join_cache.result b/mysql-test/r/join_cache.result index bbe326d7b19..9f054984416 100644 --- a/mysql-test/r/join_cache.result +++ b/mysql-test/r/join_cache.result @@ -6122,4 +6122,50 @@ f1 f3 f3 f2 f4 SET SESSION join_cache_level = DEFAULT; SET SESSION optimizer_switch = DEFAULT; DROP TABLE t1,t2; +# +# Bug #694443: hash join using IS NULL the an equi-join condition +# +CREATE TABLE t1 (a int PRIMARY KEY); +INSERT INTO t1 VALUES +(7), (4), (9), (1), (3), (8), (2); +CREATE TABLE t2 (a int, b int, INDEX idx (a)); +INSERT INTO t2 VALUES +(NULL,10), (4,80), (7,70), (6,11), (7,90), (NULL,40), +(4,77), (4,50), (NULL,41), (7,99), (7,88), (8,12), +(1,21), (4,90), (7,91), (8,22), (6,92), (NULL,42), +(2,78), (2,51), (1,43), (5,97), (5,89); +SET SESSION join_cache_level = 1; +EXPLAIN +SELECT * FROM t1,t2 WHERE t1.a < 3 and t2.a IS NULL; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 3 Using where; Using index +1 SIMPLE t2 ref idx idx 5 const 4 Using index condition +SELECT * FROM t1,t2 WHERE t1.a < 3 and t2.a IS NULL; +a a b +1 NULL 10 +1 NULL 40 +1 NULL 41 +1 NULL 42 +2 NULL 10 +2 NULL 40 +2 NULL 41 +2 NULL 42 +SET SESSION join_cache_level = 4; +EXPLAIN +SELECT * FROM t1,t2 WHERE t1.a < 3 and t2.a IS NULL; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 3 Using where; Using index +1 SIMPLE t2 ref idx idx 5 const 4 Using where; Using join buffer (flat, BNLH join) +SELECT * FROM t1,t2 WHERE t1.a < 3 and t2.a IS NULL; +a a b +1 NULL 10 +2 NULL 10 +1 NULL 40 +2 NULL 40 +1 NULL 41 +2 NULL 41 +1 NULL 42 +2 NULL 42 +SET SESSION join_cache_level = DEFAULT; +DROP TABLE t1,t2; set @@optimizer_switch=@save_optimizer_switch; diff --git a/mysql-test/t/join_cache.test b/mysql-test/t/join_cache.test index e538422475e..b2b3413da9b 100644 --- a/mysql-test/t/join_cache.test +++ b/mysql-test/t/join_cache.test @@ -2759,5 +2759,36 @@ SET SESSION optimizer_switch = DEFAULT; DROP TABLE t1,t2; +--echo # +--echo # Bug #694443: hash join using IS NULL the an equi-join condition +--echo # + +CREATE TABLE t1 (a int PRIMARY KEY); +INSERT INTO t1 VALUES + (7), (4), (9), (1), (3), (8), (2); + +CREATE TABLE t2 (a int, b int, INDEX idx (a)); +INSERT INTO t2 VALUES + (NULL,10), (4,80), (7,70), (6,11), (7,90), (NULL,40), + (4,77), (4,50), (NULL,41), (7,99), (7,88), (8,12), + (1,21), (4,90), (7,91), (8,22), (6,92), (NULL,42), + (2,78), (2,51), (1,43), (5,97), (5,89); + +SET SESSION join_cache_level = 1; +EXPLAIN +SELECT * FROM t1,t2 WHERE t1.a < 3 and t2.a IS NULL; +SELECT * FROM t1,t2 WHERE t1.a < 3 and t2.a IS NULL; + +SET SESSION join_cache_level = 4; +EXPLAIN +SELECT * FROM t1,t2 WHERE t1.a < 3 and t2.a IS NULL; +SELECT * FROM t1,t2 WHERE t1.a < 3 and t2.a IS NULL; + + +SET SESSION join_cache_level = DEFAULT; + +DROP TABLE t1,t2; + + # this must be the last command in the file set @@optimizer_switch=@save_optimizer_switch; diff --git a/sql/key.cc b/sql/key.cc index 0de1d1fd642..780cc6733c1 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -103,10 +103,11 @@ int find_ref_key(KEY *key, uint key_count, uchar *record, Field *field, @param from_record full record to be copied from @param key_info descriptor of the index @param key_length specifies length of all keyparts that will be copied + @param with_zerofill skipped bytes in the key buffer to be filled with 0 */ void key_copy(uchar *to_key, uchar *from_record, KEY *key_info, - uint key_length) + uint key_length, bool with_zerofill) { uint length; KEY_PART_INFO *key_part; @@ -129,6 +130,8 @@ void key_copy(uchar *to_key, uchar *from_record, KEY *key_info, The -1 below is to subtract the null byte which is already handled */ length= min(key_length, (uint) key_part->store_length-1); + if (with_zerofill) + bzero((char*) to_key, length); continue; } } @@ -137,7 +140,9 @@ void key_copy(uchar *to_key, uchar *from_record, KEY *key_info, { key_length-= HA_KEY_BLOB_LENGTH; length= min(key_length, key_part->length); - key_part->field->get_key_image(to_key, length, Field::itRAW); + uint bytes= key_part->field->get_key_image(to_key, length, Field::itRAW); + if (with_zerofill && bytes < length) + bzero((char*) to_key + bytes, length - bytes); to_key+= HA_KEY_BLOB_LENGTH; } else diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index ca5ccc699cd..0c929c535c5 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1876,7 +1876,8 @@ void mysql_print_status(); /* key.cc */ int find_ref_key(KEY *key, uint key_count, uchar *record, Field *field, uint *key_length, uint *keypart); -void key_copy(uchar *to_key, uchar *from_record, KEY *key_info, uint key_length); +void key_copy(uchar *to_key, uchar *from_record, KEY *key_info, uint key_length, + bool with_zerofill= FALSE); void key_restore(uchar *to_record, uchar *from_key, KEY *key_info, uint key_length); bool key_cmp_if_same(TABLE *form,const uchar *key,uint index,uint key_length); diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index 01635c56585..6c94ccede47 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -3429,7 +3429,7 @@ uchar *JOIN_CACHE_BNLH::get_matching_chain_by_join_key() TABLE_REF *ref= &join_tab->ref; KEY *keyinfo= table->key_info+ref->key; /* Build the join key value out of the record in the record buffer */ - key_copy(key_buff, table->record[0], keyinfo, key_length); + key_copy(key_buff, table->record[0], keyinfo, key_length, TRUE); /* Look for this key in the join buffer */ if (!key_search(key_buff, key_length, &key_ref_ptr)) return 0;