Both these two bugs happened due to the following problem.
When a view column is referenced in the query an Item_direct_view_ref
object is created that is refers to the Item_field for the column.
All references to the same view column refer to the same Item_field.
Different references can belong to different AND/OR levels and,
as a result, can be included in different Item_equal object.
These Item_equal objects may include different constant objects.
If these constant objects are substituted for the Item_field created
for a view column we have a conflict situation when the second
substitution annuls the first substitution. This leads to
wrong result sets returned by the query. Bug #724942 demonstrates
such an erroneous behaviour.
Test case of the bug #717577 produces wrong result sets because best
equal fields of the multiple equalities built for different OR levels
of the WHERE condition differs. The subsitution for the best equal field
in the second OR branch overwrites the the substitution made for the
first branch.

To avoid such conflicts we have to substitute for the references
to the view columns rather than for the underlying field items.
To make such substitutions possible we have to include into
multiple equalities references to view columns rather than 
field items created for such columns.

This patch modifies the Item_equal class to include references
to view columns into multiple equality objects. It also performs
a clean up of the class methods and adds more comments. The methods
of the Item_direct_view_ref class that assist substitutions for
references to view columns has been also added by this patch.
This commit is contained in:
Igor Babaev 2011-04-26 19:58:41 -07:00
commit 8d9dd21d85
11 changed files with 817 additions and 256 deletions

View file

@ -5516,43 +5516,92 @@ Item *Item_bool_rowready_func2::negated_item()
return 0;
}
Item_equal::Item_equal(Item_field *f1, Item_field *f2)
: Item_bool_func(), const_item(0), eval_item(0), cond_false(0),
compare_as_dates(FALSE)
{
const_item_cache= 0;
fields.push_back(f1);
fields.push_back(f2);
}
Item_equal::Item_equal(Item *c, Item_field *f)
/**
Construct a minimal multiple equality item
@param f1 the first equal item
@param f2 the second equal item
@param with_const_item TRUE if the first item is constant
@details
The constructor builds a new item equal object for the equality f1=f2.
One of the equal items can be constant. If this is the case it is passed
always as the first parameter and the parameter with_const_item serves
as an indicator of this case.
Currently any non-constant parameter items must point to an item of the
of the type Item_field or Item_direct_view_ref(Item_field).
*/
Item_equal::Item_equal(Item *f1, Item *f2, bool with_const_item)
: Item_bool_func(), eval_item(0), cond_false(0)
{
const_item_cache= 0;
fields.push_back(f);
const_item= c;
compare_as_dates= f->is_datetime();
with_const= with_const_item;
compare_as_dates= with_const_item && f2->is_datetime();
equal_items.push_back(f1);
equal_items.push_back(f2);
}
/**
Copy constructor for a multiple equality
@param item_equal source item for the constructor
@details
The function creates a copy of an Item_equal object.
This constructor is used when an item belongs to a multiple equality
of an upper level (an upper AND/OR level or an upper level of a nested
outer join).
*/
Item_equal::Item_equal(Item_equal *item_equal)
: Item_bool_func(), eval_item(0), cond_false(0)
{
const_item_cache= 0;
List_iterator_fast<Item_field> li(item_equal->fields);
Item_field *item;
List_iterator_fast<Item> li(item_equal->equal_items);
Item *item;
while ((item= li++))
{
fields.push_back(item);
equal_items.push_back(item);
}
const_item= item_equal->const_item;
with_const= item_equal->with_const;
compare_as_dates= item_equal->compare_as_dates;
cond_false= item_equal->cond_false;
}
void Item_equal::compare_const(Item *c)
/*
@brief
Add a constant item to the Item_equal object
@param[in] c the constant to add
@param[in] f item from the list equal_items the item c is equal to
(this parameter is optional)
@details
The method adds the constant item c to the equal_items list. If the list
doesn't have any constant item yet the item c is just put in the front
the list. Otherwise the value of c is compared with the value of the
constant item from equal_items. If they are not equal cond_false is set
to TRUE. This serves as an indicator that this Item_equal is always FALSE.
The optional parameter f is used to adjust the flag compare_as_dates.
*/
void Item_equal::add_const(Item *c, Item *f)
{
if (cond_false)
return;
if (!with_const)
{
with_const= TRUE;
if (f)
compare_as_dates= f->is_datetime();
equal_items.push_front(c);
return;
}
Item *const_item= get_const();
if (compare_as_dates)
{
cmp.set_datetime_cmp_func(this, &c, &const_item);
@ -5570,64 +5619,28 @@ void Item_equal::compare_const(Item *c)
}
void Item_equal::add(Item *c, Item_field *f)
{
if (cond_false)
return;
if (!const_item)
{
DBUG_ASSERT(f);
const_item= c;
compare_as_dates= f->is_datetime();
return;
}
compare_const(c);
}
void Item_equal::add(Item *c)
{
if (cond_false)
return;
if (!const_item)
{
const_item= c;
return;
}
compare_const(c);
}
void Item_equal::add(Item_field *f)
{
fields.push_back(f);
}
uint Item_equal::members()
{
return fields.elements;
}
/**
Check whether a field is referred in the multiple equality.
The function checks whether field is occurred in the Item_equal object .
@brief
Check whether a field is referred to in the multiple equality
@param field field whose occurrence is to be checked
@details
The function checks whether field is referred to by one of the
items from the equal_items list.
@retval
1 if nultiple equality contains a reference to field
1 if multiple equality contains a reference to field
@retval
0 otherwise
*/
bool Item_equal::contains(Field *field)
{
List_iterator_fast<Item_field> it(fields);
Item_field *item;
while ((item= it++))
Item_equal_fields_iterator it(*this);
while (it++)
{
if (field->eq(item->field))
if (field->eq(it.get_curr_field()))
return 1;
}
return 0;
@ -5635,110 +5648,168 @@ bool Item_equal::contains(Field *field)
/**
Join members of another Item_equal object.
@brief
Join members of another Item_equal object
The function actually merges two multiple equalities.
After this operation the Item_equal object additionally contains
the field items of another item of the type Item_equal.
If the optional constant items are not equal the cond_false flag is
set to 1.
@param item multiple equality whose members are to be joined
@details
The function actually merges two multiple equalities. After this operation
the Item_equal object additionally contains the field items of another item of
the type Item_equal.
If the optional constant items are not equal the cond_false flag is set to TRUE.
@notes
The function is called for any equality f1=f2 such that f1 and f2 are items
of the type Item_field or Item_direct_view_ref(Item_field), and, f1->field is
referred to in the list this->equal_items, while the list item->equal_items
contains a reference to f2->field.
*/
void Item_equal::merge(Item_equal *item)
{
fields.concat(&item->fields);
Item *c= item->const_item;
Item *c= item->get_const();
if (c)
item->equal_items.pop();
equal_items.concat(&item->equal_items);
if (c)
{
/*
The flag cond_false will be set to 1 after this, if
The flag cond_false will be set to TRUE after this if
the multiple equality already contains a constant and its
value is not equal to the value of c.
value is not equal to the value of c.
*/
add(c);
add_const(c);
}
cond_false|= item->cond_false;
}
/**
Order field items in multiple equality according to a sorting criteria.
@brief
Order equal items of the multiple equality according to a sorting criteria
The function perform ordering of the field items in the Item_equal
object according to the criteria determined by the cmp callback parameter.
If cmp(item_field1,item_field2,arg)<0 than item_field1 must be
placed after item_fiel2.
@param compare function to compare items from the equal_items list
@param arg context extra parameter for the cmp function
The function sorts field items by the bubble sort algorithm.
@details
The function performs ordering of the items from the equal_items list
according to the criteria determined by the cmp callback parameter.
If cmp(item1,item2,arg)<0 than item1 must be placed after item2.
@notes
The function sorts equal items by the bubble sort algorithm.
The list of field items is looked through and whenever two neighboring
members follow in a wrong order they are swapped. This is performed
again and again until we get all members in a right order.
@param compare function to compare field item
@param arg context extra parameter for the cmp function
*/
void Item_equal::sort(Item_field_cmpfunc compare, void *arg)
{
bubble_sort<Item_field>(&fields, compare, arg);
bubble_sort<Item>(&equal_items, compare, arg);
}
/**
Check appearance of new constant items in the multiple equality object.
@brief
Check appearance of new constant items in the multiple equality object
The function checks appearance of new constant items among
the members of multiple equalities. Each new constant item is
compared with the designated constant item if there is any in the
multiple equality. If there is none the first new constant item
becomes designated.
@details
The function checks appearance of new constant items among the members
of the equal_items list. Each new constant item is compared with
the constant item from the list if there is any. If there is none the first
new constant item is placed at the very beginning of the list and
with_const is set to TRUE. If it happens that the compared constant items
are unequal then the flag cond_false is set to TRUE.
@notes
Currently this function is called only after substitution of constant tables.
*/
void Item_equal::update_const()
{
List_iterator<Item_field> it(fields);
Item *item;
while ((item= it++))
List_iterator<Item> it(equal_items);
if (with_const)
it++;
Item *item= it++;
while (item)
{
if (item->const_item())
{
it.remove();
add(item);
Item *next_item= it++;
add_const(item);
item= next_item;
}
else
item= it++;
}
}
/**
@brief
Fix fields in a completely built multiple equality
@param thd currently not used thread handle
@param ref not used
@details
This function is called once the multiple equality has been built out of
the WHERE/ON condition and no new members are expected to be added to the
equal_items list anymore.
As any implementation of the virtual fix_fields method the function
calculates the cached values of not_null_tables_cache, used_tables_cache,
const_item_cache and calls fix_length_and_dec().
Additionally the function sets a reference to the Item_equal object in
the non-constant items of the equal_items list unless such a reference has
been already set.
@notes
Currently this function is called only in the function
build_equal_items_for_cond.
@retval
FALSE always
*/
bool Item_equal::fix_fields(THD *thd, Item **ref)
{
List_iterator_fast<Item_field> li(fields);
Item_field *item;
{
DBUG_ASSERT(fixed == 0);
Item_equal_fields_iterator it(*this);
Item *item;
not_null_tables_cache= used_tables_cache= 0;
const_item_cache= 0;
while ((item= li++))
while ((item= it++))
{
table_map tmp_table_map;
used_tables_cache|= item->used_tables();
tmp_table_map= item->not_null_tables();
not_null_tables_cache|= tmp_table_map;
if (item->maybe_null)
maybe_null=1;
item->item_equal= this;
maybe_null= 1;
if (!item->get_item_equal())
item->set_item_equal(this);
}
fix_length_and_dec();
fixed= 1;
return 0;
return FALSE;
}
/**
Update the value of the used table attribute and other attributes
*/
void Item_equal::update_used_tables()
{
List_iterator_fast<Item_field> li(fields);
Item *item;
not_null_tables_cache= used_tables_cache= 0;
if ((const_item_cache= cond_false))
return;
Item_equal_fields_iterator it(*this);
Item *item;
const_item_cache= 1;
while ((item=li++))
while ((item= it++))
{
item->update_used_tables();
used_tables_cache|= item->used_tables();
@ -5746,28 +5817,54 @@ void Item_equal::update_used_tables()
}
}
/**
@brief
Evaluate multiple equality
@details
The function evaluate multiple equality to a boolean value.
The function ignores non-constant items from the equal_items list.
The function returns 1 if all constant items from the list are equal.
It returns 0 if there are unequal constant items in the list or
one of the constant items is evaluated to NULL.
@notes
Currently this function can be called only at the optimization
stage after the constant table substitution, since all Item_equals
are eliminated before the execution stage.
@retval
0 multiple equality is always FALSE or NULL
1 otherwise
*/
longlong Item_equal::val_int()
{
Item_field *item_field;
if (cond_false)
return 0;
List_iterator_fast<Item_field> it(fields);
Item *item= const_item ? const_item : it++;
if ((null_value= item->is_null()))
return 0;
Item *item= get_const();
Item_equal_fields_iterator it(*this);
if (!item)
item= it++;
eval_item->store_value(item);
while ((item_field= it++))
if ((null_value= item->null_value))
return 0;
while ((item= it++))
{
Field *field= it.get_curr_field();
/* Skip fields of non-const tables. They haven't been read yet */
if (item_field->field->table->const_table)
if (field->table->const_table)
{
if ((null_value= item_field->is_null()) || eval_item->cmp(item_field))
if (eval_item->cmp(item) || (null_value= item->null_value))
return 0;
}
}
return 1;
}
void Item_equal::fix_length_and_dec()
{
Item *item= get_first(NULL);
@ -5775,10 +5872,11 @@ void Item_equal::fix_length_and_dec()
item->collation.collation);
}
bool Item_equal::walk(Item_processor processor, bool walk_subquery, uchar *arg)
{
List_iterator_fast<Item_field> it(fields);
Item *item;
Item_equal_fields_iterator it(*this);
while ((item= it++))
{
if (item->walk(processor, walk_subquery, arg))
@ -5787,12 +5885,13 @@ bool Item_equal::walk(Item_processor processor, bool walk_subquery, uchar *arg)
return Item_func::walk(processor, walk_subquery, arg);
}
Item *Item_equal::transform(Item_transformer transformer, uchar *arg)
{
DBUG_ASSERT(!current_thd->is_stmt_prepare());
List_iterator<Item_field> it(fields);
Item *item;
Item_equal_fields_iterator it(*this);
while ((item= it++))
{
Item *new_item= item->transform(transformer, arg);
@ -5811,19 +5910,15 @@ Item *Item_equal::transform(Item_transformer transformer, uchar *arg)
return Item_func::transform(transformer, arg);
}
void Item_equal::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
List_iterator_fast<Item_field> it(fields);
List_iterator_fast<Item> it(equal_items);
Item *item;
if (const_item)
const_item->print(str, query_type);
else
{
item= it++;
item->print(str, query_type);
}
item= it++;
item->print(str, query_type);
while ((item= it++))
{
str->append(',');
@ -5834,6 +5929,14 @@ void Item_equal::print(String *str, enum_query_type query_type)
}
CHARSET_INFO *Item_equal::compare_collation()
{
Item_equal_fields_iterator it(*this);
Item *item= it++;
return item->collation.collation;
}
/*
@brief Get the first equal field of multiple equality.
@param[in] field the field to get equal field to
@ -5859,13 +5962,14 @@ void Item_equal::print(String *str, enum_query_type query_type)
@retval 0 if no field found.
*/
Item_field* Item_equal::get_first(Item_field *field)
Item* Item_equal::get_first(Item *field_item)
{
List_iterator<Item_field> it(fields);
Item_field *item;
Item_equal_fields_iterator it(*this);
Item *item;
JOIN_TAB *field_tab;
if (!field)
return fields.head();
if (!field_item)
return (it++);
Field *field= ((Item_field *) (field_item->real_item()))->field;
/*
Of all equal fields, return the first one we can use. Normally, this is the
@ -5887,9 +5991,9 @@ Item_field* Item_equal::get_first(Item_field *field)
in presense of SJM nests.
*/
field_tab= field->field->table->reginfo.join_tab;
field_tab= field->table->reginfo.join_tab;
TABLE_LIST *emb_nest= field->field->table->pos_in_table_list->embedding;
TABLE_LIST *emb_nest= field->table->pos_in_table_list->embedding;
if (emb_nest && emb_nest->sj_mat_info && emb_nest->sj_mat_info->is_used)
{
@ -5916,13 +6020,13 @@ Item_field* Item_equal::get_first(Item_field *field)
/* Find an item to substitute for. */
while ((item= it++))
{
if (item->field->table->reginfo.join_tab >= first)
if (it.get_curr_field()->table->reginfo.join_tab >= first)
{
/*
If we found given field then return NULL to avoid unnecessary
substitution.
*/
return (item != field) ? item : NULL;
return (item != field_item) ? item : NULL;
}
}
}
@ -5946,7 +6050,8 @@ Item_field* Item_equal::get_first(Item_field *field)
*/
while ((item= it++))
{
TABLE_LIST *emb_nest= item->field->table->pos_in_table_list->embedding;
Item_field *fld_item= (Item_field *) (item->real_item());
TABLE_LIST *emb_nest= fld_item->field->table->pos_in_table_list->embedding;
if (!emb_nest || !emb_nest->sj_mat_info ||
!emb_nest->sj_mat_info->is_used)
{
@ -5954,7 +6059,7 @@ Item_field* Item_equal::get_first(Item_field *field)
}
}
#endif
return fields.head();
return equal_items.head();
}
// Shouldn't get here.
DBUG_ASSERT(0);