Commit 016a09cb authored by Igor Babaev's avatar Igor Babaev

Fixed LP bug #777745.

Fields belonging to views in general cannot be substituted for 
equal items, in particular for constants, because all references
to a view field refer to the same Item_field object while they 
could be used in different OR parts of the where condition and
belong to different equivalence classes (to different Item_equals).
That's why substitution for equal items in any context is allowed
only in place of Item_direct_view_ref objects, but not in place of
Item_fields these objects refer to.
Due to some erroneous code in the patch for bug 717577 substitution
for view fields were allowed in some context.This could lead
to wrong results returned by queries using views.

The fix prohibits substitution of view fields for equal items 
in any context.

The patch also changes slightly the compile method for the Item_func
class. Now if the analyze method returns NULL in his parameter the
compile method is not called for the arguments of the function
at all. A similar change was made for the Item_ref class.     
parent 20c9084d
......@@ -4129,3 +4129,33 @@ Warning 1292 Truncated incorrect INTEGER value: 'VV'
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` > 'JJ')
DROP VIEW v1;
DROP TABLE t1;
#
# Bug#777745: crash with equality propagation
# over view fields
#
CREATE TABLE t1 (a int NOT NULL ) ;
INSERT INTO t1 VALUES (2), (1);
CREATE TABLE t2 (a int NOT NULL , b int NOT NULL) ;
INSERT INTO t2 VALUES (2,20),(2,30);
CREATE VIEW v2 AS SELECT * FROM t2;
EXPLAIN
SELECT * FROM t1,v2
WHERE v2.a = t1.a AND v2.a = 2 AND v2.a IS NULL AND t1.a != 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,v2
WHERE v2.a = t1.a AND v2.a = 2 AND v2.a IS NULL AND t1.a != 0;
a a b
EXPLAIN
SELECT * FROM t1,v2
WHERE v2.a = t1.a AND v2.a = 2 AND v2.a+1 > 2 AND t1.a != 0;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where
1 SIMPLE t2 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join)
SELECT * FROM t1,v2
WHERE v2.a = t1.a AND v2.a = 2 AND v2.a+1 > 2 AND t1.a != 0;
a a b
2 2 20
2 2 30
DROP VIEW v2;
DROP TABLE t1,t2;
......@@ -4073,3 +4073,31 @@ SELECT * FROM v1 WHERE a > 'JJ' OR a AND a = 'VV';
DROP VIEW v1;
DROP TABLE t1;
--echo #
--echo # Bug#777745: crash with equality propagation
--echo # over view fields
--echo #
CREATE TABLE t1 (a int NOT NULL ) ;
INSERT INTO t1 VALUES (2), (1);
CREATE TABLE t2 (a int NOT NULL , b int NOT NULL) ;
INSERT INTO t2 VALUES (2,20),(2,30);
CREATE VIEW v2 AS SELECT * FROM t2;
EXPLAIN
SELECT * FROM t1,v2
WHERE v2.a = t1.a AND v2.a = 2 AND v2.a IS NULL AND t1.a != 0;
SELECT * FROM t1,v2
WHERE v2.a = t1.a AND v2.a = 2 AND v2.a IS NULL AND t1.a != 0;
EXPLAIN
SELECT * FROM t1,v2
WHERE v2.a = t1.a AND v2.a = 2 AND v2.a+1 > 2 AND t1.a != 0;
SELECT * FROM t1,v2
WHERE v2.a = t1.a AND v2.a = 2 AND v2.a+1 > 2 AND t1.a != 0;
DROP VIEW v2;
DROP TABLE t1,t2;
......@@ -4675,7 +4675,7 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal)
The function checks whether a substitution of a field item for
an equal item is valid.
@param arg *arg != NULL && **arg <-> the field is in the context
@param arg *arg != NULL <-> the field is in the context
where substitution for an equal item is valid
@note
......@@ -4701,8 +4701,10 @@ Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal)
bool Item_field::subst_argument_checker(uchar **arg)
{
return (!(*arg) && (result_type() != STRING_RESULT)) ||
((*arg) && (**arg));
return *arg &&
(*arg == (uchar *) Item::ANY_SUBST ||
result_type() != STRING_RESULT ||
(field->flags & BINARY_FLAG));
}
......@@ -6376,9 +6378,11 @@ Item* Item_ref::transform(Item_transformer transformer, uchar *arg)
First the function applies the analyzer to the Item_ref object. Then
if the analizer succeeeds we first applies the compile method to the
object the Item_ref object is referencing. If this returns a new
object the Item_ref object is referencing. If this returns a new
item the old item is substituted for a new one. After this the
transformer is applied to the Item_ref object itself.
The compile function is not called if the analyzer returns NULL
in the parameter arg_p.
@param analyzer the analyzer callback function to be applied to the
nodes of the tree of the object
......@@ -6399,10 +6403,13 @@ Item* Item_ref::compile(Item_analyzer analyzer, uchar **arg_p,
/* Compile the Item we are referencing. */
DBUG_ASSERT((*ref) != NULL);
uchar *arg_v= *arg_p;
Item *new_item= (*ref)->compile(analyzer, &arg_v, transformer, arg_t);
if (new_item && *ref != new_item)
current_thd->change_item_tree(ref, new_item);
if (*arg_p)
{
uchar *arg_v= *arg_p;
Item *new_item= (*ref)->compile(analyzer, &arg_v, transformer, arg_t);
if (new_item && *ref != new_item)
current_thd->change_item_tree(ref, new_item);
}
/* Transform this Item object. */
return (this->*transformer)(arg_t);
......@@ -7270,7 +7277,7 @@ Item_equal *Item_direct_view_ref::find_item_equal(COND_EQUAL *cond_equal)
The function checks whether a substitution of a reference to field item for
an equal item is valid.
@param arg *arg != NULL && **arg <-> the reference is in the context
@param arg *arg != NULL <-> the reference is in the context
where substitution for an equal item is valid
@note
......@@ -7283,11 +7290,19 @@ Item_equal *Item_direct_view_ref::find_item_equal(COND_EQUAL *cond_equal)
*/
bool Item_direct_view_ref::subst_argument_checker(uchar **arg)
{
bool res= (!(*arg) && (result_type() != STRING_RESULT)) ||
((*arg) && (**arg));
bool res= FALSE;
if (*arg)
{
Item *item= real_item();
if (item->type() == FIELD_ITEM &&
(*arg == (uchar *) Item::ANY_SUBST ||
result_type() != STRING_RESULT ||
(((Item_field *) item)->field->flags & BINARY_FLAG)))
res= TRUE;
}
/* Block any substitution into the wrapped object */
if (*arg)
**arg= (uchar) 0;
*arg= NULL;
return res;
}
......
......@@ -1057,12 +1057,23 @@ public:
return FALSE;
}
/*
The enumeration Subst_constraint is currently used only in implementations
of the virtual function subst_argument_checker.
*/
enum Subst_constraint
{
NO_SUBST= 0, /* No substitution for a field is allowed */
ANY_SUBST, /* Any substitution for a field is allowed */
IDENTITY_SUBST /* Substitution for a field is allowed if any two
different values of the field type are not equal */
};
virtual bool subst_argument_checker(uchar **arg)
{
if (*arg)
*arg= NULL;
return TRUE;
{
return (*arg != NULL);
}
/*
@brief
Processor used to check acceptability of an item in the defining
......
......@@ -392,7 +392,10 @@ public:
}
Item *neg_transformer(THD *thd);
virtual Item *negated_item();
bool subst_argument_checker(uchar **arg) { return TRUE; }
bool subst_argument_checker(uchar **arg)
{
return (*arg != NULL);
}
};
class Item_func_not :public Item_bool_func
......
......@@ -352,6 +352,8 @@ Item *Item_func::transform(Item_transformer transformer, uchar *argument)
the old item is substituted for a new one.
After this the transformer is applied to the root node
of the Item_func object.
The compile function is not called if the analyzer returns NULL
in the parameter arg_p.
@param analyzer the analyzer callback function to be applied to the
nodes of the tree of the object
......@@ -369,7 +371,7 @@ Item *Item_func::compile(Item_analyzer analyzer, uchar **arg_p,
{
if (!(this->*analyzer)(arg_p))
return 0;
if (arg_count)
if (*arg_p && arg_count)
{
Item **arg,**arg_end;
for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++)
......@@ -377,7 +379,7 @@ Item *Item_func::compile(Item_analyzer analyzer, uchar **arg_p,
/*
The same parameter value of arg_p must be passed
to analyze any argument of the condition formula.
*/
*/
uchar *arg_v= *arg_p;
Item *new_item= (*arg)->compile(analyzer, &arg_v, transformer, arg_t);
if (new_item && *arg != new_item)
......
......@@ -250,6 +250,21 @@ public:
return FALSE;
}
/*
By default only substitution for a field whose two different values
are never equal is allowed in the arguments of a function.
This is overruled for the direct arguments of comparison functions.
*/
bool subst_argument_checker(uchar **arg)
{
if (*arg)
{
*arg= (uchar *) Item::IDENTITY_SUBST;
return TRUE;
}
return FALSE;
}
/*
We assume the result of any function that has a TIMESTAMP argument to be
timezone-dependent, since a TIMESTAMP value in both numeric and string
......
......@@ -9788,10 +9788,9 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
as soon the field is not of a string type or the field reference is
an argument of a comparison predicate.
*/
uchar is_subst_valid= (uchar) 1;
uchar *is_subst_valid_ptr= &is_subst_valid;
uchar* is_subst_valid= (uchar *) Item::ANY_SUBST;
cond= cond->compile(&Item::subst_argument_checker,
&is_subst_valid_ptr,
&is_subst_valid,
&Item::equal_fields_propagator,
(uchar *) inherited);
cond->update_used_tables();
......
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