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;