← Back to team overview

maria-developers team mailing list archive

Re: [Commits] Rev 2908: Fixed LP bugs #717577, #724942. in file:///home/igor/maria/maria-5.3-bug717577/

 

Hello Igor,

First, an overall comment: there are lots of typos/coding style violations in
the patch. To reduce amount of effort spent on such things, I was just fixing 
them as I saw them and I'm attaching the patch with all the fixes (i.e. this
patch should be applied on top of the patch that I was reviewing).

More important comments are bellow, marked with 'psergey:'
On Fri, Mar 25, 2011 at 10:31:19PM -0700, Igor Babaev wrote:
> At file:///home/igor/maria/maria-5.3-bug717577/
> 
> ------------------------------------------------------------
> revno: 2908
> revision-id: igor@xxxxxxxxxxxx-20110326053117-x50cah9krd4f1345
> parent: wlad@xxxxxxxxxxxxxxxx-20110212174322-f7ptnc0u32vasdwy
> committer: Igor Babaev <igor@xxxxxxxxxxxx>
> branch nick: maria-5.3-bug717577
> timestamp: Fri 2011-03-25 22:31:17 -0700
> message:
>   Fixed LP bugs #717577, #724942.
>   
>   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.

> === modified file 'sql/item.cc'
> --- a/sql/item.cc	2011-02-01 03:33:32 +0000
> +++ b/sql/item.cc	2011-03-26 05:31:17 +0000
> @@ -7226,6 +7233,129 @@
>    return FALSE;
>  }
>  
> +
> +Item_equal *Item_direct_view_ref::find_item_equal(COND_EQUAL *cond_equal)
> +{
> +  Item* field_item= real_item();
> +  if (field_item->type() != FIELD_ITEM)
> +    return NULL;
> +  return ((Item_field *) field_item)->find_item_equal(cond_equal);  
> +}
> +
> +
> +/**
> +  Check whether a reference to field item can be substituted b an equal item
> +
> +  @details
> +  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
> +               where substitution for an equal item is valid
> +
> +  @note
> +    See also the note for Item_field::subst_argument_checker
> +
> +  @retval
> +    TRUE   substitution is valid
> +  @retval
> +    FALSE  otherwise
> +*/
> +bool Item_direct_view_ref::subst_argument_checker(uchar **arg)
> +{
> +  bool res=  (!(*arg) && (result_type() != STRING_RESULT)) ||
> +          ((*arg) && (**arg));
> +  if (*arg)
> +    **arg= (uchar) 0;
psergey: What is the above for? Do I understand it correctly that this is needed so 
that Item_field that this item is wrapping is not substituted?
Please add a comment.
> +  return res; 
> +}
> +
> +
> +/**
> +  Set a pointer to the multiple equality the view field reference belongs to
> +  (if any).
> +
> +  @details
> +  The function looks for a multiple equality containing this item of the type
> +  Item_direct_view_ref among those referenced by arg.
> +  In the case such equality exists the function does the following.
> +  If the found multiple equality contains a constant, then the item
> +  is substituted for this constant, otherwise the function sets a pointer
> +  to the multiple equality in the item.
> +
> +  @param arg    reference to list of multiple equalities where
> +                the item (this object) is to be looked for
> +
> +  @note
> +    This function is supposed to be called as a callback parameter in calls
> +    of the compile method.
> +
> +  @note 
> +    The function calls Item_field::equal_fields_propagator for the field item
> +    this->real_item() to do the job. Then it takes the pointer to equal_item
> +    from this field item and assigns it to this->item_equal.
> +
> +  @return
> +    - pointer to the replacing constant item, if the field item was substituted
> +    - pointer to the field item, otherwise.
> +*/
> +
> +Item *Item_direct_view_ref::equal_fields_propagator(uchar *arg)
> +{
> +  Item *field_item= real_item();
> +  if (field_item->type() != FIELD_ITEM)
> +    return this;
> +  Item *item= field_item->equal_fields_propagator(arg);
> +  set_item_equal(field_item->get_item_equal());
> +  field_item->set_item_equal(0);
psergey: Please use NULL here, we use that for pointers.
> +  if (item != field_item)
> +    return item;
> +  return this;
> +}
> +
...
> === modified file 'sql/item_cmpfunc.cc'
> --- a/sql/item_cmpfunc.cc	2011-02-06 04:57:03 +0000
> +++ b/sql/item_cmpfunc.cc	2011-03-26 05:31:17 +0000
> @@ -5516,43 +5516,93 @@
>    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 if 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 refer to an item of the
> +  of the type FIELD_ITEM.
psergey: 
The above is still true for the case where passed item is an
Item_direct_view_ref which refers to an Item_field. The wording may be
confusing for the new readers, though. Please change to explicitly indicate 
that f1 and f2 may point to 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 list equal_items. If the list
> +  hasn't not contained any constant item yet the item c is just added
> +  to the very beginning of 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.
psergey:
Does function comment really need to duplicate the function body? In my
opinion, the above should be split into multiple comments inside the function
body.
> +  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);
...
> @@ -5811,19 +5911,15 @@
>    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 +5930,14 @@
>  }
>  
>  
> +CHARSET_INFO *Item_equal::compare_collation()
psergey: One expects compare_xxx() functions to compare something, 
in particular this function looks like it's going to compare some collation.
Could you please rename it to e.g. comparison_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
...
> === modified file 'sql/item_cmpfunc.h'
> --- a/sql/item_cmpfunc.h	2010-10-10 14:18:11 +0000
> +++ b/sql/item_cmpfunc.h	2011-03-26 05:31:17 +0000
> @@ -1672,23 +1707,47 @@
>  };
>  
>  
psergey: Please add a one-line comment describing what this class does.
> -class Item_equal_iterator : public List_iterator_fast<Item_field>
> +class Item_equal_fields_iterator : public List_iterator_fast<Item>
>  {
> +  Item_equal *item_equal;
> +  Item *curr_item;
>  public:
> -  inline Item_equal_iterator(Item_equal &item_equal) 
> -    :List_iterator_fast<Item_field> (item_equal.fields)
> -  {}
> -  inline Item_field* operator++(int)
> -  { 
> -    Item_field *item= (*(List_iterator_fast<Item_field> *) this)++;
> -    return  item;
> -  }
> -  inline void rewind(void) 
> -  { 
> -    List_iterator_fast<Item_field>::rewind();
> -  }
> +  Item_equal_fields_iterator(Item_equal &item_eq) 
> +    :List_iterator_fast<Item> (item_eq.equal_items)
> +  {
> +    curr_item= NULL;
> +    item_equal= &item_eq;
> +    if (item_eq.with_const)
> +    {
> +      List_iterator_fast<Item> *list_it= (List_iterator_fast<Item> *) this;
psergey: Why do redundant explicit typecasting?
> +      curr_item=  (*list_it)++;
> +    }
> +  }
> +  Item* operator++(int)
> +  { 
> +    List_iterator_fast<Item> *list_it= (List_iterator_fast<Item> *) this;
psergey: Why do redundant explicit typecasting?
> +    curr_item= (*list_it)++;
> +    return curr_item;
> +  }
> +  Item ** ref()
> +  {
> +    return List_iterator_fast<Item>::ref();
> +  }
> +  void rewind(void) 
> +  { 
> +    List_iterator_fast<Item> *list_it= (List_iterator_fast<Item> *) this;
> +    list_it->rewind();
> +    if (item_equal->with_const)
> +      curr_item= (*list_it)++;
> +  }  
> +  Field *get_curr_field()
> +  {
> +    Item_field *item= (Item_field *) (curr_item->real_item());
> +     return item->field;
> +  }  
>  };
>  
> +
>  class Item_cond_and :public Item_cond
>  {
>  public:
> 
> === modified file 'sql/sql_select.cc'
> --- a/sql/sql_select.cc	2011-02-11 10:27:35 +0000
> +++ b/sql/sql_select.cc	2011-03-26 05:31:17 +0000
> @@ -9623,7 +9630,8 @@
>        args->concat((List<Item> *)&cond_equal.current_level);
>      }
>    }
> -  else if (cond->type() == Item::FUNC_ITEM)
> +  else if (cond->type() == Item::FUNC_ITEM ||
> +           cond->real_item()->type() == Item::FIELD_ITEM)
psergey:
As far as I understand the last part of the condition is there so 
that direct references like 

  ... AND  view.column AND ...

are processed with subst_argument_checker/equal_fields_propagator. 
Is it intentional that direct references like 


  ... AND base_table.column AND ... 

are now processed with subst_argument_checker/equal_fields_propagator as well?

>    {
>      List<Item> eq_list;
>      /*

-- 
BR
 Sergey
-- 
Sergey Petrunia, Software Developer
Monty Program AB, http://askmonty.org
Blog: http://s.petrunia.net/blog


Follow ups