From: Ole John Aske Date: December 21 2010 12:49pm Subject: bzr commit into mysql-5.1 branch (ole.john.aske:3529) Bug#57030 List-Archive: http://lists.mysql.com/commits/127421 X-Bug: 57030 Message-Id: <20101221124908.C98C6223@fimafeng09.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============8030301460347974684==" --===============8030301460347974684== 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:georgi.kodinov@stripped 3529 Ole John Aske 2010-12-21 Another updated fix for bug#57030 after review by Evgeny Potemkin ('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. Compared to first commit, a few new testcases has also been added as suggested by Evgeny Potemkin. 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 2010-12-21 12:49:04 +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 2010-12-21 12:49:04 +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 2010-12-16 14:40:52 +0000 +++ b/sql/sql_select.cc 2010-12-21 12:49:04 +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: --===============8030301460347974684== 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\ # 0auo9iqinp63m2wj # target_branch: file:///net/fimafeng09/export/home/tmp/oleja/mysql\ # /mysql-5.1/ # testament_sha1: 14e0ef162ffb54ee74afc45b5c69b6a001397dce # timestamp: 2010-12-21 13:49:08 +0100 # base_revision_id: georgi.kodinov@stripped\ # 9imm43geck5u55qw # # Begin bundle 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 --===============8030301460347974684==--