From: Ole John Aske Date: January 14 2011 3:08pm Subject: bzr commit into mysql-5.1-telco-7.0 branch (ole.john.aske:4115) List-Archive: http://lists.mysql.com/commits/128804 Message-Id: <20110114150815.15A5F223@fimafeng09.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============2963815364553706475==" --===============2963815364553706475== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///net/fimafeng09/export/home/tmp/oleja/mysql/mysql-5.1-telco-7.0/ based on revid:ole.john.aske@stripped 4115 Ole John Aske 2011-01-14 Backport to mysql-5.1-telco-7.0: fix for bug#57030: ('BETWEEN' evaluation is incorrect') Root cause for this bug is that the optimizer try to detect& optimize the special case: ' BETWEEN c1 AND c1' and handle this as the condition ' = c1' This was implemented inside add_key_field(.. *field, *value[]...) which assumed field to refer key Field, and value[] to refer a [low...high] constant pair. value[0] and value[1] was then compared for equality. In a 'normal' BETWEEN condition of the form ' BETWEEN val1 and val2' the BETWEEN operation is represented with an argementlist containing the values [, val1, val2] - add_key_field() is then called with parameters field=, *value=val1. However, if the BETWEEN predicate specified: 1) ' BETWEEN AND the 'field' and 'value' arguments to add_key_field() had to be swapped. This was implemented by trying to cheat add_key_field() to handle it like: 2) ' GE AND LE' As we didn't really replace the BETWEEN operation with 'ge' and 'le', add_key_field() still handled it as a 'BETWEEN' and compared the (swapped) arguments and for equality. If they was equal, the condition 1) was incorrectly 'optimized' to: 3) ' EQ' This fix moves this optimization of ' BETWEEN c1 AND c1' into add_key_fields() which then calls add_key_equal_fields() to collect key equality / comparison for the key fields in the BETWEEN condition. @ mysql-test/suite/ndb/r/ndb_condition_pushdown.result Updated result file as BETWEEN optimization now correctly recognice 1) ('1901-01-01 01:01:01' between date_time and date_time) as 2) ('1901-01-01 01:01:01' = date_time) And thus is able to create a REF operation on the table instead of a 'range' modified: mysql-test/r/range.result mysql-test/suite/ndb/r/ndb_condition_pushdown.result mysql-test/t/range.test sql/sql_select.cc === modified file 'mysql-test/r/range.result' --- a/mysql-test/r/range.result 2010-08-24 15:51:32 +0000 +++ b/mysql-test/r/range.result 2011-01-14 15:08:09 +0000 @@ -1666,4 +1666,105 @@ c_key c_notkey 1 1 3 3 DROP TABLE t1; +# +# Bug #57030: 'BETWEEN' evaluation is incorrect +# +CREATE TABLE t1(pk INT PRIMARY KEY, i4 INT); +CREATE UNIQUE INDEX i4_uq ON t1(i4); +INSERT INTO t1 VALUES (1,10), (2,20), (3,30); +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const i4_uq i4_uq 5 const 1 +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 10; +pk i4 +1 10 +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND i4; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 const i4_uq i4_uq 5 const 1 +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND i4; +pk i4 +1 10 +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND i4; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range i4_uq i4_uq 5 NULL 3 Using where +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND i4; +pk i4 +1 10 +2 20 +3 30 +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range i4_uq i4_uq 5 NULL 1 Using where +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND 10; +pk i4 +1 10 +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND 10; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 3 +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND 10; +pk i4 +1 10 +2 20 +3 30 +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 11 AND 11; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +SELECT * FROM t1 WHERE 10 BETWEEN 11 AND 11; +pk i4 +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 100 AND 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE +SELECT * FROM t1 WHERE 10 BETWEEN 100 AND 0; +pk i4 +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 100 AND 0; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +SELECT * FROM t1 WHERE i4 BETWEEN 100 AND 0; +pk i4 +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 99999999999999999; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range i4_uq i4_uq 5 NULL 2 Using where +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 99999999999999999; +pk i4 +1 10 +2 20 +3 30 +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 999999999999999 AND 30; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +SELECT * FROM t1 WHERE i4 BETWEEN 999999999999999 AND 30; +pk i4 +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND '20'; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 range i4_uq i4_uq 5 NULL 1 Using where +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND '20'; +pk i4 +1 10 +2 20 +EXPLAIN +SELECT * FROM t1, t1 as t2 WHERE t2.pk BETWEEN t1.i4 AND t1.i4; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL i4_uq NULL NULL NULL 3 +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.i4 1 Using where +SELECT * FROM t1, t1 as t2 WHERE t2.pk BETWEEN t1.i4 AND t1.i4; +pk i4 pk i4 +EXPLAIN +SELECT * FROM t1, t1 as t2 WHERE t1.i4 BETWEEN t2.pk AND t2.pk; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL i4_uq NULL NULL NULL 3 +1 SIMPLE t2 eq_ref PRIMARY PRIMARY 4 test.t1.i4 1 Using where +SELECT * FROM t1, t1 as t2 WHERE t1.i4 BETWEEN t2.pk AND t2.pk; +pk i4 pk i4 +DROP TABLE t1; End of 5.1 tests === modified file 'mysql-test/suite/ndb/r/ndb_condition_pushdown.result' --- a/mysql-test/suite/ndb/r/ndb_condition_pushdown.result 2011-01-14 10:42:53 +0000 +++ b/mysql-test/suite/ndb/r/ndb_condition_pushdown.result 2011-01-14 15:08:09 +0000 @@ -1307,7 +1307,7 @@ select auto from t1 where ('1901-01-01 01:01:01' between date_time and date_time) order by auto; id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 range medium_index medium_index 3 NULL # Using where with pushed condition; Using filesort +1 SIMPLE t1 ref medium_index medium_index 3 const # Using where with pushed condition; Using filesort select auto from t1 where ("aaaa" between string and string) and ("aaaa" between vstring and vstring) and === modified file 'mysql-test/t/range.test' --- a/mysql-test/t/range.test 2010-08-24 15:51:32 +0000 +++ b/mysql-test/t/range.test 2011-01-14 15:08:09 +0000 @@ -1325,4 +1325,71 @@ SELECT * FROM t1 WHERE 2 NOT BETWEEN c_n DROP TABLE t1; +--echo # +--echo # Bug #57030: 'BETWEEN' evaluation is incorrect +--echo # + +# Test some BETWEEN predicates which does *not* follow the +# 'normal' pattern of BETWEEN AND + +CREATE TABLE t1(pk INT PRIMARY KEY, i4 INT); +CREATE UNIQUE INDEX i4_uq ON t1(i4); + +INSERT INTO t1 VALUES (1,10), (2,20), (3,30); + +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 10; +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 10; + +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND i4; +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND i4; + +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND i4; +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND i4; + +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND 10; +SELECT * FROM t1 WHERE 10 BETWEEN i4 AND 10; + +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND 10; +SELECT * FROM t1 WHERE 10 BETWEEN 10 AND 10; + +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 11 AND 11; +SELECT * FROM t1 WHERE 10 BETWEEN 11 AND 11; + +EXPLAIN +SELECT * FROM t1 WHERE 10 BETWEEN 100 AND 0; +SELECT * FROM t1 WHERE 10 BETWEEN 100 AND 0; + +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 100 AND 0; +SELECT * FROM t1 WHERE i4 BETWEEN 100 AND 0; + +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 99999999999999999; +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND 99999999999999999; + +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 999999999999999 AND 30; +SELECT * FROM t1 WHERE i4 BETWEEN 999999999999999 AND 30; + +EXPLAIN +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND '20'; +SELECT * FROM t1 WHERE i4 BETWEEN 10 AND '20'; + +#Should detect the EQ_REF 't2.pk=t1.i4' +EXPLAIN +SELECT * FROM t1, t1 as t2 WHERE t2.pk BETWEEN t1.i4 AND t1.i4; +SELECT * FROM t1, t1 as t2 WHERE t2.pk BETWEEN t1.i4 AND t1.i4; + +EXPLAIN +SELECT * FROM t1, t1 as t2 WHERE t1.i4 BETWEEN t2.pk AND t2.pk; +SELECT * FROM t1, t1 as t2 WHERE t1.i4 BETWEEN t2.pk AND t2.pk; + +DROP TABLE t1; + --echo End of 5.1 tests === modified file 'sql/sql_select.cc' --- a/sql/sql_select.cc 2011-01-14 14:26:14 +0000 +++ b/sql/sql_select.cc 2011-01-14 15:08:09 +0000 @@ -3330,6 +3330,9 @@ add_key_field(KEY_FIELD **key_fields,uin eq_func is NEVER true when num_values > 1 */ if (!eq_func) +#ifndef MCP_BUG57030 + return; +#else { /* Additional optimization: if we're processing @@ -3350,6 +3353,7 @@ add_key_field(KEY_FIELD **key_fields,uin eq_func= TRUE; } +#endif if (field->result_type() == STRING_RESULT) { if ((*value)->result_type() != STRING_RESULT) @@ -3545,9 +3549,71 @@ add_key_fields(JOIN *join, KEY_FIELD **k case Item_func::OPTIMIZE_KEY: { Item **values; +#ifndef MCP_BUG57030 + /* + Build list of possible keys for 'a BETWEEN low AND high'. + It is handled similar to the equivalent condition + 'a >= low AND a <= high': + */ + if (cond_func->functype() == Item_func::BETWEEN) + { + Item_field *field_item; + bool equal_func= FALSE; + uint num_values= 2; + values= cond_func->arguments(); + + bool binary_cmp= (values[0]->real_item()->type() == Item::FIELD_ITEM) + ? ((Item_field*)values[0]->real_item())->field->binary() + : TRUE; + + /* + Additional optimization: If 'low = high': + Handle as if the condition was "t.key = low". + */ + if (!((Item_func_between*)cond_func)->negated && + values[1]->eq(values[2], binary_cmp)) + { + equal_func= TRUE; + num_values= 1; + } + + /* + Append keys for 'field value[]' if the + condition is of the form:: + ' BETWEEN value[1] AND value[2]' + */ + if (is_local_field (values[0])) + { + field_item= (Item_field *) (values[0]->real_item()); + add_key_equal_fields(key_fields, *and_level, cond_func, + field_item, equal_func, &values[1], + num_values, usable_tables, sargables); + } + /* + Append keys for 'value[0] field' if the + condition is of the form: + 'value[0] BETWEEN field1 AND field2' + */ + for (uint i= 1; i <= num_values; i++) + { + if (is_local_field (values[i])) + { + field_item= (Item_field *) (values[i]->real_item()); + add_key_equal_fields(key_fields, *and_level, cond_func, + field_item, equal_func, values, + 1, usable_tables, sargables); + } + } + } // if ( ... Item_func::BETWEEN) + + // IN, NE + else if (is_local_field (cond_func->key_item()) && + !(cond_func->used_tables() & OUTER_REF_TABLE_BIT)) +#else // BETWEEN, IN, NE if (is_local_field (cond_func->key_item()) && !(cond_func->used_tables() & OUTER_REF_TABLE_BIT)) +#endif /* MCP_BUG57030 */ { values= cond_func->arguments()+1; if (cond_func->functype() == Item_func::NE_FUNC && @@ -3561,6 +3627,7 @@ add_key_fields(JOIN *join, KEY_FIELD **k cond_func->argument_count()-1, usable_tables, sargables); } +#ifdef MCP_BUG57030 if (cond_func->functype() == Item_func::BETWEEN) { values= cond_func->arguments(); @@ -3576,6 +3643,7 @@ add_key_fields(JOIN *join, KEY_FIELD **k } } } +#endif break; } case Item_func::OPTIMIZE_OP: --===============2963815364553706475== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/ole.john.aske@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: ole.john.aske@stripped\ # tm8ue7igyvq6dnyj # target_branch: file:///net/fimafeng09/export/home/tmp/oleja/mysql\ # /mysql-5.1-telco-7.0/ # testament_sha1: d7aebee5909e895112c627bf7a8e46eba6c7ca5a # timestamp: 2011-01-14 16:08:14 +0100 # base_revision_id: ole.john.aske@stripped\ # cybdrhkoo5ckvxfi # # Begin bundle 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 nTGK8ZjIIoLYnGyY8KFHv+LuSKcKEg4RQRvA --===============2963815364553706475==--