← Back to team overview

maria-developers team mailing list archive

MDEV-8024 Remove excessive update_used_tables() calls

 

Hi Sergei,

Please review a patch for MDEV-8024.

It's needed to as a pre-requisite for
"MDEV-7950 Item_func::type() takes 0.26% in OLTP RO"


Thanks.
diff --git a/sql/item.cc b/sql/item.cc
index e9bbdc7..502b8ae 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -59,6 +59,21 @@ bool cmp_items(Item *a, Item *b)
 }
 
 
+void
+Used_tables_and_const_cache::
+  used_tables_and_const_cache_update_and_join(List<Item> &list)
+{
+  List_iterator_fast<Item> li(list);
+  Item *item;
+  while ((item=li++))
+  {
+    item->update_used_tables();
+    used_tables_and_const_cache_join(item);
+  }
+}
+
+
+
 /*****************************************************************************
 ** Item functions
 *****************************************************************************/
@@ -4730,10 +4745,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
         else
         {
           Item::Type ref_type= (*reference)->type();
-          prev_subselect_item->used_tables_cache|=
-            (*reference)->used_tables();
-          prev_subselect_item->const_item_cache&=
-            (*reference)->const_item();
+          prev_subselect_item->used_tables_and_const_cache_join(*reference);
           mark_as_dependent(thd, last_checked_context->select_lex,
                             context->select_lex, this,
                             ((ref_type == REF_ITEM || ref_type == FIELD_ITEM) ?
@@ -4759,8 +4771,7 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference)
       if (ref != not_found_item)
       {
         DBUG_ASSERT(*ref && (*ref)->fixed);
-        prev_subselect_item->used_tables_cache|= (*ref)->used_tables();
-        prev_subselect_item->const_item_cache&= (*ref)->const_item();
+        prev_subselect_item->used_tables_and_const_cache_join(*ref);
         break;
       }
     }
@@ -6783,8 +6794,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
           if (ref != not_found_item)
           {
             DBUG_ASSERT(*ref && (*ref)->fixed);
-            prev_subselect_item->used_tables_cache|= (*ref)->used_tables();
-            prev_subselect_item->const_item_cache&= (*ref)->const_item();
+            prev_subselect_item->used_tables_and_const_cache_join(*ref);
             break;
           }
           /*
@@ -6828,10 +6838,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
           if (from_field == view_ref_found)
           {
             Item::Type refer_type= (*reference)->type();
-            prev_subselect_item->used_tables_cache|=
-              (*reference)->used_tables();
-            prev_subselect_item->const_item_cache&=
-              (*reference)->const_item();
+            prev_subselect_item->used_tables_and_const_cache_join(*reference);
             DBUG_ASSERT((*reference)->type() == REF_ITEM);
             mark_as_dependent(thd, last_checked_context->select_lex,
                               context->select_lex, this,
diff --git a/sql/item.h b/sql/item.h
index 5820f11..7e7c573b0 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -3328,6 +3328,63 @@ class Item_args
 };
 
 
+class Used_tables_and_const_cache
+{
+public:
+  /*
+    In some cases used_tables_cache is not what used_tables() return
+    so the method should be used where one need used tables bit map
+    (even internally in Item_func_* code).
+  */
+  table_map used_tables_cache;
+  bool const_item_cache;
+
+  Used_tables_and_const_cache()
+   :used_tables_cache(0),
+    const_item_cache(true)
+  { }
+  Used_tables_and_const_cache(const Used_tables_and_const_cache *other)
+   :used_tables_cache(other->used_tables_cache),
+    const_item_cache(other->const_item_cache)
+  { }
+  void used_tables_and_const_cache_init()
+  {
+    used_tables_cache= 0;
+    const_item_cache= true;
+  }
+  void used_tables_and_const_cache_copy(const Used_tables_and_const_cache *c)
+  {
+    *this= *c;
+  }
+  void used_tables_and_const_cache_join(const Item *item)
+  {
+    used_tables_cache|= item->used_tables();
+    const_item_cache&= item->const_item();
+  }
+  /*
+    Call update_used_tables() for all items in the array "args"
+    and join with the current cache.
+    "this" must be initialized with a constructor or
+    re-initialized with used_tables_and_const_cache_init().
+  */
+  void used_tables_and_const_cache_update_and_join(uint argc, Item **argv)
+  {
+    for (uint i=0 ; i < argc ; i++)
+    {
+      argv[i]->update_used_tables();
+      used_tables_and_const_cache_join(argv[i]);
+    }
+  }
+  /*
+    Call update_used_tables() for all items in the list
+    and join with the current cache.
+    "this" must be initialized with a constructor or
+    re-initialized with used_tables_and_const_cache_init().
+  */
+  void used_tables_and_const_cache_update_and_join(List<Item> &list);
+};
+
+
 /**
   An abstract class representing common features of
   regular functions and aggregate functions.
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 23676d7..eec99d9 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1520,9 +1520,8 @@ bool Item_in_optimizer::fix_left(THD *thd)
   if (args[1]->fixed)
   {
     /* to avoid overriding is called to update left expression */
-    used_tables_cache|= args[1]->used_tables();
+    used_tables_and_const_cache_join(args[1]);
     with_sum_func= with_sum_func || args[1]->with_sum_func;
-    const_item_cache= const_item_cache && args[1]->const_item();
   }
   DBUG_RETURN(0);
 }
@@ -1551,8 +1550,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref)
   with_subselect= 1;
   with_sum_func= with_sum_func || args[1]->with_sum_func;
   with_field= with_field || args[1]->with_field;
-  used_tables_cache|= args[1]->used_tables();
-  const_item_cache&= args[1]->const_item();
+  used_tables_and_const_cache_join(args[1]);
   fixed= 1;
   return FALSE;
 }
@@ -2081,11 +2079,10 @@ void Item_func_interval::fix_length_and_dec()
   }
   maybe_null= 0;
   max_length= 2;
-  used_tables_cache|= row->used_tables();
+  used_tables_and_const_cache_join(row);
   not_null_tables_cache= row->not_null_tables();
   with_sum_func= with_sum_func || row->with_sum_func;
   with_field= with_field || row->with_field;
-  const_item_cache&= row->const_item();
 }
 
 
@@ -4302,8 +4299,8 @@ Item_cond::fix_fields(THD *thd, Item **ref)
   List_iterator<Item> li(list);
   Item *item;
   uchar buff[sizeof(char*)];			// Max local vars in function
-  not_null_tables_cache= used_tables_cache= 0;
-  const_item_cache= 1;
+  not_null_tables_cache= 0;
+  used_tables_and_const_cache_init();
 
   /*
     and_table_cache is the value that Item_cond_or() returns for
@@ -4453,8 +4450,7 @@ void Item_cond::fix_after_pullout(st_select_lex *new_parent, Item **ref)
   List_iterator<Item> li(list);
   Item *item;
 
-  used_tables_cache=0;
-  const_item_cache=1;
+  used_tables_and_const_cache_init();
 
   and_tables_cache= ~(table_map) 0; // Here and below we do as fix_fields does
   not_null_tables_cache= 0;
@@ -4464,8 +4460,7 @@ void Item_cond::fix_after_pullout(st_select_lex *new_parent, Item **ref)
     table_map tmp_table_map;
     item->fix_after_pullout(new_parent, li.ref());
     item= *li.ref();
-    used_tables_cache|= item->used_tables();
-    const_item_cache&= item->const_item();
+    used_tables_and_const_cache_join(item);
 
     if (item->const_item())
       and_tables_cache= (table_map) 0;
@@ -4648,22 +4643,6 @@ Item_cond::used_tables() const
 }
 
 
-void Item_cond::update_used_tables()
-{
-  List_iterator_fast<Item> li(list);
-  Item *item;
-
-  used_tables_cache=0;
-  const_item_cache=1;
-  while ((item=li++))
-  {
-    item->update_used_tables();
-    used_tables_cache|= item->used_tables();
-    const_item_cache&= item->const_item();
-  }
-}
-
-
 void Item_cond::print(String *str, enum_query_type query_type)
 {
   str->append('(');
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 2d039d5..bf388ef 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -1763,7 +1763,11 @@ class Item_cond :public Item_bool_func
   enum Type type() const { return COND_ITEM; }
   List<Item>* argument_list() { return &list; }
   table_map used_tables() const;
-  void update_used_tables();
+  void update_used_tables()
+  {
+    used_tables_and_const_cache_init();
+    used_tables_and_const_cache_update_and_join(list);
+  }
   virtual void print(String *str, enum_query_type query_type);
   void split_sum_func(THD *thd, Item **ref_pointer_array, List<Item> &fields);
   friend int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 50bc85f..4007d6b 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -178,8 +178,44 @@ Item_func::fix_fields(THD *thd, Item **ref)
   Item **arg,**arg_end;
   uchar buff[STACK_BUFF_ALLOC];			// Max argument in function
 
-  used_tables_cache= not_null_tables_cache= 0;
-  const_item_cache=1;
+  // used_tables_and_const_cache_init(); // QQ: is it needed, for safety?
+  /*
+    Comments about the commented used_tables_and_const_cache_init() above:
+    
+    The "Used_tables_and_const_cache" part of Item_func is initialized in
+    Item_func::Item_func(). So normally, the above initialization is not needed.
+    And it does pass all tests.
+    
+    However, in rare cases "fixed" can be set back to "false" after
+    fix_fields() was called for the very first time.
+    
+    These functions reset "fixed" to "false":
+    -  unfix_fields()
+    -  st_select_lex_unit::reinit_exec_mechanism()
+    -  make_in_exists_conversion()
+    -  convert_join_subqueries_to_semijoins()
+    
+    Possible solutions:
+    - Keep the above initialization commented (or just remove it) and hope
+      that everything will work fine :)
+      Adding some DBUG_ASSERTs at least would nice.
+      
+    - Uncomment initialization used_tables_and_const_cache_init()
+      (this will give better reliability with slighly worse performance)
+      This is what was before this patch.
+      
+    - Make Item::fixed private (or at least protected), add a new method:
+      
+      void Item::unfix()
+      {
+        fixed= 0;
+        used_tables_and_const_cache_init();
+      }
+      and use it instead of direct assignment "item->fixed= false;".
+    
+  */
+  
+  not_null_tables_cache= 0;
 
   /*
     Use stack limit of STACK_MIN_SIZE * 2 since
@@ -221,8 +257,7 @@ Item_func::fix_fields(THD *thd, Item **ref)
 
       with_sum_func= with_sum_func || item->with_sum_func;
       with_field= with_field || item->with_field;
-      used_tables_cache|=     item->used_tables();
-      const_item_cache&=      item->const_item();
+      used_tables_and_const_cache_join(item);
       with_subselect|=        item->has_subquery();
     }
   }
@@ -269,8 +304,8 @@ void Item_func::fix_after_pullout(st_select_lex *new_parent, Item **ref)
 {
   Item **arg,**arg_end;
 
-  used_tables_cache= not_null_tables_cache= 0;
-  const_item_cache=1;
+  used_tables_and_const_cache_init();
+  not_null_tables_cache= 0;
 
   if (arg_count)
   {
@@ -279,9 +314,8 @@ void Item_func::fix_after_pullout(st_select_lex *new_parent, Item **ref)
       (*arg)->fix_after_pullout(new_parent, arg);
       Item *item= *arg;
 
-      used_tables_cache|=     item->used_tables();
+      used_tables_and_const_cache_join(item);
       not_null_tables_cache|= item->not_null_tables();
-      const_item_cache&=      item->const_item();
     }
   }
 }
@@ -436,19 +470,6 @@ void Item_func::split_sum_func(THD *thd, Item **ref_pointer_array,
 }
 
 
-void Item_func::update_used_tables()
-{
-  used_tables_cache=0;
-  const_item_cache=1;
-  for (uint i=0 ; i < arg_count ; i++)
-  {
-    args[i]->update_used_tables();
-    used_tables_cache|=args[i]->used_tables();
-    const_item_cache&=args[i]->const_item();
-  }
-}
-
-
 table_map Item_func::used_tables() const
 {
   return used_tables_cache;
@@ -3503,8 +3524,7 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func,
 
   /* Fix all arguments */
   func->maybe_null=0;
-  used_tables_cache=0;
-  const_item_cache=1;
+  used_tables_and_const_cache_init();
 
   if ((f_args.arg_count=arg_count))
   {
@@ -3546,8 +3566,7 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func,
       func->with_sum_func= func->with_sum_func || item->with_sum_func;
       func->with_field= func->with_field || item->with_field;
       func->with_subselect|= item->with_subselect;
-      used_tables_cache|=item->used_tables();
-      const_item_cache&=item->const_item();
+      used_tables_and_const_cache_join(item);
       f_args.arg_type[i]=item->result_type();
     }
     //TODO: why all following memory is not allocated with 1 call of sql_alloc?
diff --git a/sql/item_func.h b/sql/item_func.h
index 653c08e..c78c585 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -31,7 +31,7 @@ extern "C"				/* Bug in BSDI include file */
 #endif
 
 
-class Item_func :public Item_func_or_sum
+class Item_func :public Item_func_or_sum, public Used_tables_and_const_cache
 {
   void sync_with_sum_func_and_with_field(List<Item> &list);
 protected:
@@ -42,15 +42,8 @@ class Item_func :public Item_func_or_sum
   uint allowed_arg_cols;
   String *val_str_from_val_str_ascii(String *str, String *str2);
 public:
-  /*
-    In some cases used_tables_cache is not what used_tables() return
-    so the method should be used where one need used tables bit map 
-    (even internally in Item_func_* code).
-  */
-  table_map used_tables_cache;
   table_map not_null_tables_cache;
 
-  bool const_item_cache;
   enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC,
 		  GE_FUNC,GT_FUNC,FT_FUNC,
 		  LIKE_FUNC,ISNULL_FUNC,ISNOTNULL_FUNC,
@@ -118,11 +111,9 @@ class Item_func :public Item_func_or_sum
   }
   // Constructor used for Item_cond_and/or (see Item comment)
   Item_func(THD *thd, Item_func *item)
-   :Item_func_or_sum(thd, item),
+   :Item_func_or_sum(thd, item), Used_tables_and_const_cache(item),
     allowed_arg_cols(item->allowed_arg_cols),
-    used_tables_cache(item->used_tables_cache),
-    not_null_tables_cache(item->not_null_tables_cache),
-    const_item_cache(item->const_item_cache)
+    not_null_tables_cache(item->not_null_tables_cache)
   {
   }
   bool fix_fields(THD *, Item **ref);
@@ -130,7 +121,11 @@ class Item_func :public Item_func_or_sum
   void quick_fix_field();
   table_map used_tables() const;
   table_map not_null_tables() const;
-  void update_used_tables();
+  void update_used_tables()
+  {
+    used_tables_and_const_cache_init();
+    used_tables_and_const_cache_update_and_join(arg_count, args);
+  }
   bool eq(const Item *item, bool binary_cmp) const;
   virtual optimize_type select_optimize() const { return OPTIMIZE_NONE; }
   virtual bool have_rev_func() const { return 0; }
@@ -1367,8 +1362,7 @@ class Item_udf_func :public Item_func
   {
     DBUG_ASSERT(fixed == 0);
     bool res= udf.fix_fields(thd, this, arg_count, args);
-    used_tables_cache= udf.used_tables_cache;
-    const_item_cache= udf.const_item_cache;
+    used_tables_and_const_cache_copy(udf.used_tables_and_const_cache());
     fixed= 1;
     return res;
   }
diff --git a/sql/item_row.cc b/sql/item_row.cc
index 3548a6b..1fc2000 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -39,8 +39,7 @@
 */
 
 Item_row::Item_row(List<Item> &arg):
-  Item(), used_tables_cache(0), not_null_tables_cache(0),
-  const_item_cache(1), with_null(0)
+  Item(), Used_tables_and_const_cache(), not_null_tables_cache(0), with_null(0)
 {
 
   //TODO: think placing 2-3 component items in item (as it done for function)
@@ -126,8 +125,7 @@ void Item_row::cleanup()
 
   Item::cleanup();
   /* Reset to the original values */
-  used_tables_cache= 0;
-  const_item_cache= 1;
+  used_tables_and_const_cache_init();
   with_null= 0;
 
   DBUG_VOID_RETURN;
@@ -143,29 +141,14 @@ void Item_row::split_sum_func(THD *thd, Item **ref_pointer_array,
 }
 
 
-void Item_row::update_used_tables()
-{
-  used_tables_cache= 0;
-  const_item_cache= 1;
-  for (uint i= 0; i < arg_count; i++)
-  {
-    items[i]->update_used_tables();
-    used_tables_cache|= items[i]->used_tables();
-    const_item_cache&= items[i]->const_item();
-  }
-}
-
-
 void Item_row::fix_after_pullout(st_select_lex *new_parent, Item **ref)
 {
-  used_tables_cache= 0;
-  const_item_cache= 1;
+  used_tables_and_const_cache_init();
   not_null_tables_cache= 0;
   for (uint i= 0; i < arg_count; i++)
   {
     items[i]->fix_after_pullout(new_parent, &items[i]);
-    used_tables_cache|= items[i]->used_tables();
-    const_item_cache&= items[i]->const_item();
+    used_tables_and_const_cache_join(items[i]);
     not_null_tables_cache|= items[i]->not_null_tables();
   }
 }
diff --git a/sql/item_row.h b/sql/item_row.h
index aa56068..31efc01 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -17,22 +17,20 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
 
-class Item_row: public Item
+class Item_row: public Item, private Used_tables_and_const_cache
 {
   Item **items;
-  table_map used_tables_cache, not_null_tables_cache;
+  table_map not_null_tables_cache;
   uint arg_count;
-  bool const_item_cache;
   bool with_null;
 public:
   Item_row(List<Item> &);
   Item_row(Item_row *item):
     Item(),
+    Used_tables_and_const_cache(item),
     items(item->items),
-    used_tables_cache(item->used_tables_cache),
     not_null_tables_cache(0),
     arg_count(item->arg_count),
-    const_item_cache(item->const_item_cache),
     with_null(0)
   {}
 
@@ -71,7 +69,11 @@ class Item_row: public Item
   bool const_item() const { return const_item_cache; };
   enum Item_result result_type() const { return ROW_RESULT; }
   Item_result cmp_type() const { return ROW_RESULT; }
-  void update_used_tables();
+  void update_used_tables()
+  {
+    used_tables_and_const_cache_init();
+    used_tables_and_const_cache_update_and_join(arg_count, items);
+  }
   table_map not_null_tables() const { return not_null_tables_cache; }
   virtual void print(String *str, enum_query_type query_type);
 
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 32d6362..496cb11 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -49,8 +49,9 @@ const char *exists_outer_expr_name= "<exists outer expr>";
 int check_and_do_in_subquery_rewrites(JOIN *join);
 
 Item_subselect::Item_subselect():
-  Item_result_field(), value_assigned(0), own_engine(0), thd(0), old_engine(0), 
-  used_tables_cache(0), have_to_be_excluded(0), const_item_cache(1),
+  Item_result_field(), Used_tables_and_const_cache(),
+  value_assigned(0), own_engine(0), thd(0), old_engine(0), 
+  have_to_be_excluded(0),
   inside_first_fix_fields(0), done_first_fix_fields(FALSE), 
   expr_cache(0), forced_const(FALSE), substitution(0), engine(0), eliminated(FALSE),
   changed(0), is_correlated(FALSE)
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index add2e0b..47a143e 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -44,7 +44,8 @@ class Cached_item;
 
 /* base class for subselects */
 
-class Item_subselect :public Item_result_field
+class Item_subselect :public Item_result_field,
+                      protected Used_tables_and_const_cache
 {
   bool value_assigned;   /* value already assigned to subselect */
   bool own_engine;  /* the engine was not taken from other Item_subselect */
@@ -53,16 +54,12 @@ class Item_subselect :public Item_result_field
   THD *thd;
   /* old engine if engine was changed */
   subselect_engine *old_engine;
-  /* cache of used external tables */
-  table_map used_tables_cache;
   /* allowed number of columns (1 for single value subqueries) */
   uint max_columns;
   /* where subquery is placed */
   enum_parsing_place parsing_place;
   /* work with 'substitution' */
   bool have_to_be_excluded;
-  /* cache of constant state */
-  bool const_item_cache;
   
   bool inside_first_fix_fields;
   bool done_first_fix_fields;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 20a829e..e8cce56 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -12770,7 +12770,9 @@ static bool check_equality(THD *thd, Item *item, COND_EQUAL *cond_equal,
   @param inherited  path to all inherited multiple equality items
 
   @return
-    pointer to the transformed condition
+    pointer to the transformed condition,
+    whose Used_tables_and_const_cache is up to date,
+    so no additional update_used_tables() is needed on the result.
 */
 
 static COND *build_equal_items_for_cond(THD *thd, COND *cond,
@@ -12784,9 +12786,9 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
   if (cond->type() == Item::COND_ITEM)
   {
     List<Item> eq_list;
-    bool and_level= ((Item_cond*) cond)->functype() ==
-      Item_func::COND_AND_FUNC;
-    List<Item> *args= ((Item_cond*) cond)->argument_list();
+    Item_cond *cond_item= (Item_cond*) cond;
+    bool and_level= cond_item->functype() == Item_func::COND_AND_FUNC;
+    List<Item> *args= cond_item->argument_list();
     
     List_iterator<Item> li(*args);
     Item *item;
@@ -12837,8 +12839,10 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
     /*
        Make replacement of equality predicates for lower levels
        of the condition expression.
+       Update used_tables_cache and const_item_cache on the way.
     */
     li.rewind();
+    cond_item->used_tables_and_const_cache_init();
     while ((item= li++))
     { 
       Item *new_item;
@@ -12853,12 +12857,27 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
         */
         li.replace(new_item);
       }
+      cond_item->used_tables_and_const_cache_join(new_item);
     }
     if (and_level)
     {
       args->append(&eq_list);
       args->append((List<Item> *)&cond_equal.current_level);
+      /*
+        QQ: perhaps we could do this:
+        
+        cond_item->used_tables_and_const_cache_update_and_join(eq_list);
+        cond_item->used_tables_and_const_cache_update_and_join(
+                                    *(List<Item> *) &cond_equal.current_level);
+
+        instead of the cond_item->update_used_tables() call below.
+        But initializing 2 iterators will probably be even slower than
+        redundant iterations over the topmost elements in "args",
+        which were already processed in the "while" loop above.
+      */
+      cond_item->update_used_tables();
     }
+    return cond_item;
   }
   else if (cond->type() == Item::FUNC_ITEM ||
            cond->real_item()->type() == Item::FIELD_ITEM)
@@ -12890,8 +12909,9 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
           item_equal->upper_levels= inherited;
           return item_equal;
 	}
-
-        return eq_list.pop();
+        Item *res= eq_list.pop();
+        res->update_used_tables();
+        return res;
       }
       else
       {
@@ -12913,7 +12933,7 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
         and_cond->cond_equal.copy(cond_equal);
         cond_equal.current_level= and_cond->cond_equal.current_level;
         args->append((List<Item> *)&cond_equal.current_level);
-        
+        and_cond->update_used_tables();
         return and_cond;
       }
     }
@@ -12928,8 +12948,8 @@ static COND *build_equal_items_for_cond(THD *thd, COND *cond,
                         &is_subst_valid, 
                         &Item::equal_fields_propagator,
                         (uchar *) inherited);
-    cond->update_used_tables();
   }
+  cond->update_used_tables();
   return cond;
 }
 
@@ -13016,7 +13036,6 @@ static COND *build_equal_items(JOIN *join, COND *cond,
   if (cond) 
   {
     cond= build_equal_items_for_cond(thd, cond, inherited, link_equal_fields);
-    cond->update_used_tables();
     if (cond->type() == Item::COND_ITEM &&
         ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
       cond_equal= &((Item_cond_and*) cond)->cond_equal;
diff --git a/sql/sql_udf.h b/sql/sql_udf.h
index 076c544..161bd7a 100644
--- a/sql/sql_udf.h
+++ b/sql/sql_udf.h
@@ -52,7 +52,7 @@ typedef struct st_udf_func
 
 class Item_result_field;
 
-class udf_handler :public Sql_alloc
+class udf_handler :public Sql_alloc, private Used_tables_and_const_cache
 {
  protected:
   udf_func *u_d;
@@ -65,8 +65,6 @@ class udf_handler :public Sql_alloc
   Item **args;
 
  public:
-  table_map used_tables_cache;
-  bool const_item_cache;
   bool not_original;
   udf_handler(udf_func *udf_arg) :u_d(udf_arg), buffers(0), error(0),
     is_null(0), initialized(0), not_original(0)
@@ -79,6 +77,10 @@ class udf_handler :public Sql_alloc
   bool fix_fields(THD *thd, Item_func_or_sum *item,
 		  uint arg_count, Item **args);
   void cleanup();
+  const Used_tables_and_const_cache *used_tables_and_const_cache() const
+  {
+    return this;
+  }
   double val(my_bool *null_value)
   {
     is_null= 0;

Follow ups