Commit b4c2f3f8 authored by evgen@moonbone.local's avatar evgen@moonbone.local

Fixed bug#21475: Wrongly applied constant propagation leads to a false comparison.

A date can be represented as an int (like 20060101) and as a string (like
"2006.01.01"). When a DATE/TIME field is compared in one SELECT against both
representations the constant propagation mechanism leads to comparison
of DATE as a string and DATE as an int. In this example it compares 2006 and
20060101 integers. Obviously it fails comparison although they represents the
same date.


Now the Item_bool_func2::fix_length_and_dec() function sets the comparison
context for items being compared. I.e. if items compared as strings the
comparison context is STRING.
The constant propagation mechanism now doesn't mix items used in different
comparison contexts. The context check is done in the
Item_field::equal_fields_propagator() and in the change_cond_ref_to_const() 
functions.

Also the better fix for bug 21159 is introduced.
parent 270629a1
......@@ -168,3 +168,14 @@ dt
0000-00-00 00:00:00
0000-00-00 00:00:00
drop table t1;
CREATE TABLE t1(a DATETIME NOT NULL);
INSERT INTO t1 VALUES ('20060606155555');
SELECT a FROM t1 WHERE a=(SELECT MAX(a) FROM t1) AND (a="20060606155555");
a
2006-06-06 15:55:55
PREPARE s FROM 'SELECT a FROM t1 WHERE a=(SELECT MAX(a) FROM t1) AND (a="20060606155555")';
EXECUTE s;
a
2006-06-06 15:55:55
DROP PREPARE s;
DROP TABLE t1;
......@@ -114,3 +114,14 @@ select * from t1;
drop table t1;
# End of 4.1 tests
#
# Bug#21475: Wrongly applied constant propagation leads to a false comparison.
#
CREATE TABLE t1(a DATETIME NOT NULL);
INSERT INTO t1 VALUES ('20060606155555');
SELECT a FROM t1 WHERE a=(SELECT MAX(a) FROM t1) AND (a="20060606155555");
PREPARE s FROM 'SELECT a FROM t1 WHERE a=(SELECT MAX(a) FROM t1) AND (a="20060606155555")';
EXECUTE s;
DROP PREPARE s;
DROP TABLE t1;
......@@ -3777,7 +3777,18 @@ Item *Item_field::equal_fields_propagator(byte *arg)
Item *item= 0;
if (item_equal)
item= item_equal->get_const();
if (!item)
/*
Disable const propagation for items used in different comparison contexts.
This must be done because, for example, Item_hex_string->val_int() is not
the same as (Item_hex_string->val_str() in BINARY column)->val_int().
We cannot simply disable the replacement in a particular context (
e.g. <bin_col> = <int_col> AND <bin_col> = <hex_string>) since
Items don't know the context they are in and there are functions like
IF (<hex_string>, 'yes', 'no').
The same problem occurs when comparing a DATE/TIME field with a
DATE/TIME represented as an int and as a string.
*/
if (!item || item->cmp_context != cmp_context)
item= this;
return item;
}
......
......@@ -465,7 +465,7 @@ class Item {
my_bool with_subselect; /* If this item is a subselect or some
of its arguments is or contains a
subselect */
Item_result cmp_context; /* Comparison context */
// alloc & destruct is done as start of select using sql_alloc
Item();
/*
......
......@@ -405,7 +405,8 @@ void Item_bool_func2::fix_length_and_dec()
agg_arg_charsets(coll, args, 2, MY_COLL_CMP_CONV, 1))
return;
args[0]->cmp_context= args[1]->cmp_context=
item_cmp_type(args[0]->result_type(), args[1]->result_type());
// Make a special case of compare with fields to get nicer DATE comparisons
if (functype() == LIKE_FUNC) // Disable conversion in case of LIKE function.
......@@ -426,6 +427,7 @@ void Item_bool_func2::fix_length_and_dec()
{
cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,
INT_RESULT); // Works for all types.
args[0]->cmp_context= args[1]->cmp_context= INT_RESULT;
return;
}
}
......@@ -440,6 +442,7 @@ void Item_bool_func2::fix_length_and_dec()
{
cmp.set_cmp_func(this, tmp_arg, tmp_arg+1,
INT_RESULT); // Works for all types.
args[0]->cmp_context= args[1]->cmp_context= INT_RESULT;
return;
}
}
......
......@@ -6516,23 +6516,9 @@ static bool check_equality(Item *item, COND_EQUAL *cond_equal)
field_item= (Item_field*) right_item;
const_item= left_item;
}
/*
Disable const propagation for Item_hex_string.
This must be done because Item_hex_string->val_int() is not
the same as (Item_hex_string->val_str() in BINARY column)->val_int().
We cannot simply disable the replacement in a particular context (
e.g. <bin_col> = <int_col> AND <bin_col> = <hex_string>) since
Items don't know the context they are in and there are functions like
IF (<hex_string>, 'yes', 'no').
Note that this will disable some valid cases as well
(e.g. : <bin_col> = <hex_string> AND <bin_col2> = <bin_col>) but
there's no way to distinguish the valid cases without having the
Item's parent say something like : Item->set_context(Item::STRING_RESULT)
and have all the Items that contain other Items do that consistently.
*/
if (const_item &&
field_item->result_type() == const_item->result_type() &&
const_item->type() != Item::VARBIN_ITEM)
field_item->result_type() == const_item->result_type())
{
bool copyfl;
......@@ -7188,6 +7174,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
Item_func::Functype functype= func->functype();
if (right_item->eq(field,0) && left_item != value &&
right_item->cmp_context == field->cmp_context &&
(left_item->result_type() != STRING_RESULT ||
value->result_type() != STRING_RESULT ||
left_item->collation.collation == value->collation.collation))
......@@ -7209,6 +7196,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
}
}
else if (left_item->eq(field,0) && right_item != value &&
left_item->cmp_context == field->cmp_context &&
(right_item->result_type() != STRING_RESULT ||
value->result_type() != STRING_RESULT ||
right_item->collation.collation == value->collation.collation))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment