Commit 679088a2 authored by unknown's avatar unknown
Browse files

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
Loading
Loading
Loading
Loading
+122 −131
Original line number Diff line number Diff line
@@ -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. */
        /* 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;