mirror of
https://github.com/MariaDB/server.git
synced 2026-01-06 05:22:24 +03:00
Fix for bug#57030: ('BETWEEN' evaluation is incorrect')
Root cause for this bug is that the optimizer try to detect&
optimize the special case:
'<field> BETWEEN c1 AND c1' and handle this as the condition '<field> = 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 '<field> BETWEEN val1 and val2' the
BETWEEN operation is represented with an argementlist containing the
values [<field>, val1, val2] - add_key_field() is then called with
parameters field=<field>, *value=val1.
However, if the BETWEEN predicate specified:
1) '<const1> BETWEEN<const2> AND<field>
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) '<const1> GE<const2> AND<const1> LE<field>'
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<const1> and<const2> for equality. If they was equal, the
condition 1) was incorrectly 'optimized' to:
3) '<field> EQ <const1>'
This fix moves this optimization of '<field> 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.
This commit is contained in:
@@ -3349,26 +3349,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, Item_func *cond,
|
||||
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 **key_fields, uint *and_level,
|
||||
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 <cmp> value[]' if the
|
||||
condition is of the form::
|
||||
'<field> 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] <cmp> 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 **key_fields, uint *and_level,
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user