From 615d756721f26414f3411ebde2335dbca878333e Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 4 Nov 2010 21:00:33 -0700 Subject: [PATCH] Fixed LP bug #669382. When probing into the hash table of a hashed join cache is performed the key value should not constructed in the buffer used to build keys in the hash tables. The constant parts of these keys copied only once, so they should not be ever overwritten. Otherwise wrong results can be produced by queries that employ hashed join buffers. --- mysql-test/r/join_cache.result | 26 ++++++++++++++++++++++++++ mysql-test/t/join_cache.test | 29 +++++++++++++++++++++++++++++ sql/sql_join_cache.cc | 7 +++++-- sql/sql_select.h | 13 ++++++++----- 4 files changed, 68 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/join_cache.result b/mysql-test/r/join_cache.result index 3c352386b8a..d04a1aaf6a2 100644 --- a/mysql-test/r/join_cache.result +++ b/mysql-test/r/join_cache.result @@ -5589,4 +5589,30 @@ i 6 SET SESSION join_cache_level = DEFAULT; DROP TABLE t1,t2; +# +# Bug #669382: hash join using a ref with constant key parts +# +CREATE TABLE t1 (a int); +INSERT INTO t1 VALUES +(9), (11), (7), (8), (4), (1), (12), (3), (5); +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; +CREATE TABLE t2 (a int, b int, c int, INDEX idx (a,b)); +INSERT INTO t2 VALUES +(8, 80, 800), (1, 10, 100), (1, 11, 101), (3, 30, 300), +(1, 12, 102), (8, 81, 801), (7, 70, 700), (12, 120, 1200), +(8, 82, 802), (1, 13, 103), (1, 14, 104), (3, 31, 301), +(1, 15, 105), (8, 83, 803), (7, 71, 701); +SET SESSION join_cache_level = 4; +SET SESSION join_buffer_size = 192; +EXPLAIN +SELECT t1.a, t2.c FROM t1,t2 WHERE t1.a=t2.a AND t2.b=99; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 36 Using where +1 SIMPLE t2 ref idx idx 10 test.t1.a,const 2 Using join buffer (flat, BNLH join) +SELECT t1.a, t2.c FROM t1,t2 WHERE t1.a=t2.a AND t2.b=99; +a c +SET SESSION join_cache_level = DEFAULT; +SET SESSION join_buffer_size = 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 48a2cd1d4a8..16bd2d515ad 100644 --- a/mysql-test/t/join_cache.test +++ b/mysql-test/t/join_cache.test @@ -2286,5 +2286,34 @@ SET SESSION join_cache_level = DEFAULT; DROP TABLE t1,t2; +--echo # +--echo # Bug #669382: hash join using a ref with constant key parts +--echo # + +CREATE TABLE t1 (a int); +INSERT INTO t1 VALUES + (9), (11), (7), (8), (4), (1), (12), (3), (5); +INSERT INTO t1 SELECT * FROM t1; +INSERT INTO t1 SELECT * FROM t1; + +CREATE TABLE t2 (a int, b int, c int, INDEX idx (a,b)); +INSERT INTO t2 VALUES + (8, 80, 800), (1, 10, 100), (1, 11, 101), (3, 30, 300), + (1, 12, 102), (8, 81, 801), (7, 70, 700), (12, 120, 1200), + (8, 82, 802), (1, 13, 103), (1, 14, 104), (3, 31, 301), + (1, 15, 105), (8, 83, 803), (7, 71, 701); + +SET SESSION join_cache_level = 4; +SET SESSION join_buffer_size = 192; + +EXPLAIN +SELECT t1.a, t2.c FROM t1,t2 WHERE t1.a=t2.a AND t2.b=99; +SELECT t1.a, t2.c FROM t1,t2 WHERE t1.a=t2.a AND t2.b=99; + +SET SESSION join_cache_level = DEFAULT; +SET SESSION join_buffer_size = DEFAULT; + +DROP TABLE t1,t2; + # this must be the last command in the file set @@optimizer_switch=@save_optimizer_switch; diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index 3586426419d..6dce5884c9a 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -2500,6 +2500,9 @@ int JOIN_CACHE_HASHED::init() if ((rc= JOIN_CACHE::init())) DBUG_RETURN (rc); + if (!(key_buff= (uchar*) sql_alloc(key_length))) + DBUG_RETURN(1); + /* Take into account a reference to the next record in the key chain */ pack_length+= get_size_of_rec_offset(); pack_length_with_blob_ptrs+= get_size_of_rec_offset(); @@ -3300,9 +3303,9 @@ 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(ref->key_buff, table->record[0], keyinfo, ref->key_length); + key_copy(key_buff, table->record[0], keyinfo, key_length); /* Look for this key in the join buffer */ - if (!key_search(ref->key_buff, ref->key_length, &key_ref_ptr)) + if (!key_search(key_buff, key_length, &key_ref_ptr)) return 0; return key_ref_ptr+get_size_of_key_offset(); } diff --git a/sql/sql_select.h b/sql/sql_select.h index 74b93c1d37c..10122b02e04 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1133,11 +1133,6 @@ private: /* Size of the offset of a key entry in the hash table */ uint size_of_key_ofs; - /* - Length of a key value. - It is assumed that all key values have the same length. - */ - uint key_length; /* Length of the key entry in the hash table. A key entry either contains the key value, or it contains a reference @@ -1164,6 +1159,14 @@ private: protected: + /* + Length of a key value. + It is assumed that all key values have the same length. + */ + uint key_length; + /* Buffer to store key values for probing */ + uchar *key_buff; + /* Number of key entries in the hash table (number of distinct keys) */ uint key_entries;