Commit 679088a2 authored by unknown's avatar unknown

Fix for Bug#8801: the bug was in co-operation of Item_ref

with view-merge algorithm and prepared statements: in case when some
Item_ref pointing to a view column was substituted with a reference 
pointing to the view expression for that column
Item_ref::ref member of the original Item_ref was left pointing to 
not_found_item (0x1).
As we currently perform expression substition part of the view-merge 
algorithm per each execution of a prepared statement or stored procedure, 
we need to preserve original Item_ref objects usable.


sql/item.cc:
  Set member Item_ref::ref to null whenever the item itself is substituted 
  with another item.
  This is necessary if we want to re-execute a prepared statement next time.
  
  Additionally Item_ref::fix_fields() implementation was cleaned up
  (by Monty and myself) to reduce the number of if branches. This
  doesn't change the logic of this function.
parent ad1c1e07
......@@ -3513,6 +3513,9 @@ Item_ref::Item_ref(Item **item, const char *table_name_par,
Item_field::fix_fields, here we first search the SELECT and GROUP BY
clauses, and then we search the FROM clause.
POSTCONDITION
Item_ref::ref is 0 or points to a valid item
RETURN
TRUE if error
FALSE on success
......@@ -3534,6 +3537,19 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
if (ref == not_found_item) /* This reference was not resolved. */
{
TABLE_LIST *table_list;
Field *from_field;
SELECT_LEX *last;
ref= 0;
if (!outer_sel || (current_sel->master_unit()->first_select()->linkage ==
DERIVED_TABLE_TYPE))
{
/* The current reference cannot be resolved in this query. */
my_error(ER_BAD_FIELD_ERROR,MYF(0),
this->full_name(), current_thd->where);
return TRUE;
}
/*
If there is an outer select, and it is not a derived table (which do
not support the use of outer fields for now), try to resolve this
......@@ -3543,13 +3559,10 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
subselects may contain columns with the same names. The subselects are
searched starting from the innermost.
*/
if (outer_sel && (current_sel->master_unit()->first_select()->linkage !=
DERIVED_TABLE_TYPE))
{
TABLE_LIST *table_list;
Field *from_field= (Field*) not_found_field;
SELECT_LEX *last= 0;
from_field= (Field*) not_found_field;
last= 0;
/* The following loop will always be excuted at least once */
for ( ; outer_sel ;
outer_sel= (prev_unit= outer_sel->master_unit())->outer_select())
{
......@@ -3568,16 +3581,24 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
prev_subselect_item->const_item_cache&= (*ref)->const_item();
break;
}
/*
Set ref to 0 to ensure that we get an error in case we replaced
this item with another item and still use this item in some
other place of the parse tree.
*/
ref= 0;
}
/* Search in the tables of the FROM clause of the outer select. */
table_list= outer_sel->get_table_list();
if (outer_sel->resolve_mode == SELECT_LEX::INSERT_MODE && table_list)
{
/*
It is a primary INSERT st_select_lex => do not resolve against the
first table.
It is a primary INSERT st_select_lex => do not resolve against
the first table.
*/
table_list= table_list->next_local;
}
place= prev_subselect_item->parsing_place;
/*
......@@ -3599,18 +3620,13 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
field expression to 'reference', i.e. it substitute that
expression instead of this Item_ref
*/
if ((from_field= find_field_in_tables(thd, this, table_list,
from_field= find_field_in_tables(thd, this, table_list,
reference,
IGNORE_EXCEPT_NON_UNIQUE,
TRUE)) !=
not_found_field)
{
if (from_field != view_ref_found)
{
prev_subselect_item->used_tables_cache|= from_field->table->map;
prev_subselect_item->const_item_cache= 0;
}
else
TRUE);
if (! from_field)
return TRUE;
if (from_field == view_ref_found)
{
Item::Type type= (*reference)->type();
prev_subselect_item->used_tables_cache|=
......@@ -3628,9 +3644,14 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
*/
return FALSE;
}
if (from_field != not_found_field)
{
prev_subselect_item->used_tables_cache|= from_field->table->map;
prev_subselect_item->const_item_cache= 0;
break;
}
}
DBUG_ASSERT(from_field == not_found_field);
/* Reference is not found => depend on outer (or just error). */
prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT;
......@@ -3641,25 +3662,8 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
break; /* Do not consider derived tables. */
}
DBUG_ASSERT(ref != 0);
if (!from_field)
return TRUE;
if (ref == not_found_item && from_field == not_found_field)
{
my_error(ER_BAD_FIELD_ERROR, MYF(0),
this->full_name(), current_thd->where);
ref= 0; // Safety
return TRUE;
}
DBUG_ASSERT(from_field != 0 && from_field != view_ref_found);
if (from_field != not_found_field)
{
/*
Set ref to 0 as we are replacing this item with the found item and
this will ensure we get an error if this item would be used
elsewhere
*/
ref= 0; // Safety
if (from_field != view_ref_found)
{
Item_field* fld;
if (!(fld= new Item_field(from_field)))
......@@ -3668,34 +3672,20 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
mark_as_dependent(thd, last, thd->lex->current_select, this, fld);
return FALSE;
}
/*
We can leave expression substituted from view for next PS/SP
re-execution (i.e. do not register this substitution for reverting
on cleanup() (register_item_tree_changing())), because this subtree
will be fix_field'ed during setup_tables()->setup_ancestor()
(i.e. before all other expressions of query, and references on
tables which do not present in query will not make problems.
Also we suppose that view can't be changed during PS/SP life.
*/
}
else
{
/* Should be checked in resolve_ref_in_select_and_group(). */
DBUG_ASSERT(*ref && (*ref)->fixed);
mark_as_dependent(thd, last, current_sel, this, this);
}
}
else
if (ref == 0)
{
/* The current reference cannot be resolved in this query. */
my_error(ER_BAD_FIELD_ERROR,MYF(0),
/* The item was not a table field and not a reference */
my_error(ER_BAD_FIELD_ERROR, MYF(0),
this->full_name(), current_thd->where);
return TRUE;
}
/* Should be checked in resolve_ref_in_select_and_group(). */
DBUG_ASSERT(*ref && (*ref)->fixed);
mark_as_dependent(thd, last, current_sel, this, this);
}
}
DBUG_ASSERT(*ref);
/*
Check if this is an incorrect reference in a group function or forward
reference. Do not issue an error if this is an unnamed reference inside an
......@@ -3716,11 +3706,12 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference)
set_properties();
if (ref && (*ref)->check_cols(1))
return 1;
return 0;
if ((*ref)->check_cols(1))
return TRUE;
return FALSE;
}
void Item_ref::set_properties()
{
max_length= (*ref)->max_length;
......
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