From: Ole John Aske Date: February 1 2011 12:20pm Subject: bzr commit into mysql-5.1 branch (ole.john.aske:3571) Bug#57030 List-Archive: http://lists.mysql.com/commits/130119 X-Bug: 57030 Message-Id: <20110201122022.B3712223@fimafeng09.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============1891953139200736833==" --===============1891953139200736833== 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/ based on revid:alfranio.correia@stripped 3571 Ole John Aske 2011-02-01 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. modified: mysql-test/r/range.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-02-01 12:20:16 +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/t/range.test' --- a/mysql-test/t/range.test 2010-08-24 15:51:32 +0000 +++ b/mysql-test/t/range.test 2011-02-01 12:20:16 +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-12 08:55:31 +0000 +++ b/sql/sql_select.cc 2011-02-01 12:20:16 +0000 @@ -3349,26 +3349,7 @@ add_key_field(KEY_FIELD **key_fields,uin eq_func is NEVER true when num_values > 1 */ if (!eq_func) - { - /* - Additional optimization: if we're processing - "t.key BETWEEN c1 AND c1" then proceed as if we were processing - "t.key = c1". - TODO: This is a very limited fix. A more generic fix is possible. - There are 2 options: - A) Make equality propagation code be able to handle BETWEEN - (including cases like t1.key BETWEEN t2.key AND t3.key) - B) Make range optimizer to infer additional "t.key = c" equalities - and use them in equality propagation process (see details in - OptimizerKBAndTodo) - */ - if ((cond->functype() != Item_func::BETWEEN) || - ((Item_func_between*) cond)->negated || - !value[0]->eq(value[1], field->binary())) - return; - eq_func= TRUE; - } - + return; if (field->result_type() == STRING_RESULT) { if ((*value)->result_type() != STRING_RESULT) @@ -3564,9 +3545,65 @@ add_key_fields(JOIN *join, KEY_FIELD **k case Item_func::OPTIMIZE_KEY: { Item **values; - // BETWEEN, IN, NE - if (is_local_field (cond_func->key_item()) && - !(cond_func->used_tables() & OUTER_REF_TABLE_BIT)) + /* + 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)) { values= cond_func->arguments()+1; if (cond_func->functype() == Item_func::NE_FUNC && @@ -3580,21 +3617,6 @@ add_key_fields(JOIN *join, KEY_FIELD **k cond_func->argument_count()-1, usable_tables, sargables); } - if (cond_func->functype() == Item_func::BETWEEN) - { - values= cond_func->arguments(); - for (uint i= 1 ; i < cond_func->argument_count() ; i++) - { - Item_field *field_item; - if (is_local_field (cond_func->arguments()[i])) - { - field_item= (Item_field *) (cond_func->arguments()[i]->real_item()); - add_key_equal_fields(key_fields, *and_level, cond_func, - field_item, 0, values, 1, usable_tables, - sargables); - } - } - } break; } case Item_func::OPTIMIZE_OP: --===============1891953139200736833== 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\ # 8ngficfn0a1358hw # target_branch: file:///net/fimafeng09/export/home/tmp/oleja/mysql\ # /mysql-5.1/ # testament_sha1: 11bff48eb6c0c0610b97c731c9677b1d38e6ab1e # timestamp: 2011-02-01 13:20:22 +0100 # base_revision_id: alfranio.correia@stripped\ # tbrk92p7b691sy7v # # Begin bundle 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 --===============1891953139200736833==--