← Back to team overview

maria-developers team mailing list archive

Step#5: MDEV-7950, , Item_func::type() takes 0.26% in OLTP RO

 

Hi Sergey,

please review the next step for MDEV-7950.

This step changes the function remove_eq_conds() into a method in Item.

It removes 6 virtual calls for Item_func::type(), and adds only 2
virtual calls for Item***::remove_eq_conds().

Thanks.
diff --git a/sql/item.h b/sql/item.h
index 017d348..a02a4e2 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1135,6 +1135,8 @@ class Item: public Type_std_attributes
     DBUG_ASSERT(!cond_equal_ref || !cond_equal_ref[0]);
     return this;
   }
+  virtual COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
+                                bool top_level);
   /*
     Checks whether the item is:
     - a simple equality (field=field_item or field=constant_item), or
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 5064521..11358ca 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -409,6 +409,8 @@ class Item_bool_func2 :public Item_bool_func
     Item_bool_func::cleanup();
     cmp.cleanup();
   }
+  COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
+                        bool top_level);
 
   friend class  Arg_comparator;
 };
@@ -1467,6 +1469,8 @@ class Item_func_isnull :public Item_func_null_predicate
       const_item_cache= args[0]->const_item();
     }
   }
+  COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
+                        bool top_level);
   table_map not_null_tables() const { return 0; }
   Item *neg_transformer(THD *thd);
 };
@@ -1775,6 +1779,8 @@ class Item_cond :public Item_bool_func
   COND *build_equal_items(THD *thd, COND_EQUAL *inherited,
                           bool link_item_fields,
                           COND_EQUAL **cond_equal_ref);
+  COND *remove_eq_conds(THD *thd, Item::cond_result *cond_value,
+                        bool top_level);
   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/sql_delete.cc b/sql/sql_delete.cc
index a9470dd..213013b 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -372,7 +372,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
   if (conds)
   {
     Item::cond_result result;
-    conds= remove_eq_conds(thd, conds, &result);
+    conds= conds->remove_eq_conds(thd, &result, true);
     if (result == Item::COND_FALSE)             // Impossible where
     {
       limit= 0;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index d26e502..2d8cd91 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -3862,7 +3862,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
     COND_EQUAL *orig_cond_equal = join->cond_equal;
 
     conds->update_used_tables();
-    conds= remove_eq_conds(join->thd, conds, &join->cond_value);
+    conds= conds->remove_eq_conds(join->thd, &join->cond_value, true);
     if (conds && conds->type() == Item::COND_ITEM &&
         ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC)
       join->cond_equal= &((Item_cond_and*) conds)->m_cond_equal;
@@ -14708,7 +14708,7 @@ optimize_cond(JOIN *join, COND *conds,
       Remove all and-levels where CONST item != CONST item
     */
     DBUG_EXECUTE("where",print_where(conds,"after const change", QT_ORDINARY););
-    conds= remove_eq_conds(thd, conds, cond_value);
+    conds= conds->remove_eq_conds(thd, cond_value, true);
     if (conds && conds->type() == Item::COND_ITEM &&
         ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC)
       *cond_equal= &((Item_cond_and*) conds)->m_cond_equal;
@@ -14945,295 +14945,267 @@ bool cond_is_datetime_is_null(Item *cond)
  =>  SELECT * FROM t1 WHERE (b = 5) AND (a = 5)                            
 */
 
-static COND *
-internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
-{
-  if (cond->type() == Item::COND_ITEM)
-  {
-    bool and_level= ((Item_cond*) cond)->functype()
-      == Item_func::COND_AND_FUNC;
-    List<Item> *cond_arg_list= ((Item_cond*) cond)->argument_list();
-
-    if (and_level)
-    {
-      /* 
-        Remove multiple equalities that became always true (e.g. after
-        constant row substitution).
-        They would be removed later in the function anyway, but the list of
-        them cond_equal.current_level also  must be adjusted correspondingly.
-        So it's easier  to do it at one pass through the list of the equalities.
-      */ 
-       List<Item_equal> *cond_equalities=
-        &((Item_cond_and *) cond)->m_cond_equal.current_level;
-       cond_arg_list->disjoin((List<Item> *) cond_equalities);
-       List_iterator<Item_equal> it(*cond_equalities);
-       Item_equal *eq_item;
-       while ((eq_item= it++))
-       {
-         if (eq_item->const_item() && eq_item->val_int())
-           it.remove();
-       }  
-       cond_arg_list->append((List<Item> *) cond_equalities);
-    }
 
-    List<Item_equal> new_equalities;
-    List_iterator<Item> li(*cond_arg_list);
-    bool should_fix_fields= 0;
-    Item::cond_result tmp_cond_value;
-    Item *item;
+COND *
+Item_cond::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
+                           bool top_level)
+{
+  bool and_level= functype() == Item_func::COND_AND_FUNC;
+  List<Item> *cond_arg_list= argument_list();
 
+  if (and_level)
+  {
     /* 
-      If the list cond_arg_list became empty then it consisted only
-      of always true multiple equalities.
+      Remove multiple equalities that became always true (e.g. after
+      constant row substitution).
+      They would be removed later in the function anyway, but the list of
+      them cond_equal.current_level also  must be adjusted correspondingly.
+      So it's easier  to do it at one pass through the list of the equalities.
     */ 
-    *cond_value= cond_arg_list->elements ? Item::COND_UNDEF : Item::COND_TRUE;
+     List<Item_equal> *cond_equalities=
+      &((Item_cond_and *) this)->m_cond_equal.current_level;
+     cond_arg_list->disjoin((List<Item> *) cond_equalities);
+     List_iterator<Item_equal> it(*cond_equalities);
+     Item_equal *eq_item;
+     while ((eq_item= it++))
+     {
+       if (eq_item->const_item() && eq_item->val_int())
+         it.remove();
+     }  
+     cond_arg_list->append((List<Item> *) cond_equalities);
+  }
+
+  List<Item_equal> new_equalities;
+  List_iterator<Item> li(*cond_arg_list);
+  bool should_fix_fields= 0;
+  Item::cond_result tmp_cond_value;
+  Item *item;
 
-    while ((item=li++))
+  /* 
+    If the list cond_arg_list became empty then it consisted only
+    of always true multiple equalities.
+  */ 
+  *cond_value= cond_arg_list->elements ? Item::COND_UNDEF : Item::COND_TRUE;
+
+  while ((item=li++))
+  {
+    Item *new_item= item->remove_eq_conds(thd, &tmp_cond_value, false);
+    if (!new_item)
     {
-      Item *new_item=internal_remove_eq_conds(thd, item, &tmp_cond_value);
-      if (!new_item)
-      {
-        /* This can happen only when item is converted to TRUE or FALSE */
-	li.remove();
-      }
-      else if (item != new_item)
+      /* This can happen only when item is converted to TRUE or FALSE */
+      li.remove();
+    }
+    else if (item != new_item)
+    {
+      /* 
+        This can happen when:
+        - item was an OR formula converted to one disjunct
+        - item was an AND formula converted to one conjunct
+        In these cases the disjunct/conjunct must be merged into the
+        argument list of cond.
+      */
+      if (new_item->type() == Item::COND_ITEM &&
+          item->type() == Item::COND_ITEM)
       {
-        /* 
-          This can happen when:
-          - item was an OR formula converted to one disjunct
-          - item was an AND formula converted to one conjunct
-          In these cases the disjunct/conjunct must be merged into the
-          argument list of cond.
-	*/
-        if (new_item->type() == Item::COND_ITEM &&
-            item->type() == Item::COND_ITEM)
+        DBUG_ASSERT(functype() == ((Item_cond *) new_item)->functype());          
+        List<Item> *new_item_arg_list=
+          ((Item_cond *) new_item)->argument_list();
+        if (and_level)
         {
-          DBUG_ASSERT(((Item_cond *) cond)->functype() == 
-                      ((Item_cond *) new_item)->functype());          
-	  List<Item> *new_item_arg_list=
-            ((Item_cond *) new_item)->argument_list();
-          if (and_level)
-	  {
+          /*
+            If new_item is an AND formula then multiple equalities
+            of new_item_arg_list must merged into multiple equalities
+            of cond_arg_list. 
+          */
+          List<Item_equal> *new_item_equalities=
+            &((Item_cond_and *) new_item)->m_cond_equal.current_level;
+          if (!new_item_equalities->is_empty())
+          {
             /*
-              If new_item is an AND formula then multiple equalities
-              of new_item_arg_list must merged into multiple equalities
-              of cond_arg_list. 
-	    */
-            List<Item_equal> *new_item_equalities=
-              &((Item_cond_and *) new_item)->m_cond_equal.current_level;
-            if (!new_item_equalities->is_empty())
-	    {
-              /*
-                Cut the multiple equalities from the new_item_arg_list and
-                append them on the list new_equalities. Later the equalities
-                from this list will be merged into the multiple equalities
-                of cond_arg_list all together.
-	      */
-              new_item_arg_list->disjoin((List<Item> *) new_item_equalities);
-              new_equalities.append(new_item_equalities);
-            }
-          }
-          if (new_item_arg_list->is_empty())
-	    li.remove();
-	  else
-	  {
-            uint cnt= new_item_arg_list->elements;
-            li.replace(*new_item_arg_list);
-            /* Make iterator li ignore new items */
-            for (cnt--; cnt; cnt--)
-              li++;
-            should_fix_fields= 1;
+              Cut the multiple equalities from the new_item_arg_list and
+              append them on the list new_equalities. Later the equalities
+              from this list will be merged into the multiple equalities
+              of cond_arg_list all together.
+            */
+            new_item_arg_list->disjoin((List<Item> *) new_item_equalities);
+            new_equalities.append(new_item_equalities);
           }
         }
-        else if (and_level && 
-                 new_item->type() == Item::FUNC_ITEM && 
-                 ((Item_cond*) new_item)->functype() ==
-                  Item_func::MULT_EQUAL_FUNC)
-	{
+        if (new_item_arg_list->is_empty())
           li.remove();
-          new_equalities.push_back((Item_equal *) new_item);
-        }
         else
-	{
-          if (new_item->type() == Item::COND_ITEM &&
-              ((Item_cond*) new_item)->functype() == 
-              ((Item_cond*) cond)->functype())
-	  {
-	    List<Item> *new_item_arg_list=
-              ((Item_cond *) new_item)->argument_list();
-            uint cnt= new_item_arg_list->elements;
-            li.replace(*new_item_arg_list);
-            /* Make iterator li ignore new items */
-            for (cnt--; cnt; cnt--)
-              li++;
-          }
-          else
-            li.replace(new_item);
+        {
+          uint cnt= new_item_arg_list->elements;
+          li.replace(*new_item_arg_list);
+          /* Make iterator li ignore new items */
+          for (cnt--; cnt; cnt--)
+            li++;
           should_fix_fields= 1;
-        } 
-      }   
-      if (*cond_value == Item::COND_UNDEF)
-	*cond_value=tmp_cond_value;
-      switch (tmp_cond_value) {
-      case Item::COND_OK:			// Not TRUE or FALSE
-	if (and_level || *cond_value == Item::COND_FALSE)
-	  *cond_value=tmp_cond_value;
-	break;
-      case Item::COND_FALSE:
-	if (and_level)
-	{
-	  *cond_value=tmp_cond_value;
-	  return (COND*) 0;			// Always false
-	}
-	break;
-      case Item::COND_TRUE:
-	if (!and_level)
-	{
-	  *cond_value= tmp_cond_value;
-	  return (COND*) 0;			// Always true
-	}
-	break;
-      case Item::COND_UNDEF:			// Impossible
-	break; /* purecov: deadcode */
-      }
-    }
-    if (!new_equalities.is_empty())
-    {
-      DBUG_ASSERT(and_level);
-      /* 
-        Merge multiple equalities that were cut from the results of 
-        simplification of OR formulas converted into AND formulas.
-        These multiple equalities are to be merged into the
-        multiple equalities of  cond_arg_list.
-      */
-      COND_EQUAL *cond_equal= &((Item_cond_and *) cond)->m_cond_equal;
-      List<Item_equal> *cond_equalities= &cond_equal->current_level;
-      cond_arg_list->disjoin((List<Item> *) cond_equalities);
-      Item_equal *equality;
-      List_iterator_fast<Item_equal> it(new_equalities);
-      while ((equality= it++))
-      {
-	equality->upper_levels= cond_equal->upper_levels;
-        equality->merge_into_list(thd, cond_equalities, false, false);
-        List_iterator_fast<Item_equal> ei(*cond_equalities);
-        while ((equality= ei++))
-	{
-          if (equality->const_item() && !equality->val_int())
-	  {
-            *cond_value= Item::COND_FALSE;
-            return (COND*) 0;
-          }
         }
       }
-      cond_arg_list->append((List<Item> *) cond_equalities);
-      /* 
-        Propagate the newly formed multiple equalities to
-        the all AND/OR levels of cond 
-      */
-      bool is_simplifiable_cond= false;
-      propagate_new_equalities(thd, cond, cond_equalities,
-                               cond_equal->upper_levels,
-                               &is_simplifiable_cond);
-      /*
-        If the above propagation of multiple equalities brings us
-        to multiple equalities that are always FALSE then try to
-        simplify the condition with remove_eq_cond() again.
-      */ 
-      if (is_simplifiable_cond)
+      else if (and_level && 
+               new_item->type() == Item::FUNC_ITEM && 
+               ((Item_cond*) new_item)->functype() ==
+                Item_func::MULT_EQUAL_FUNC)
+      {
+        li.remove();
+        new_equalities.push_back((Item_equal *) new_item);
+      }
+      else
       {
-        if (!(cond= internal_remove_eq_conds(thd, cond, cond_value)))
-          return cond;
+        if (new_item->type() == Item::COND_ITEM &&
+            ((Item_cond*) new_item)->functype() ==  functype())
+        {
+          List<Item> *new_item_arg_list=
+            ((Item_cond *) new_item)->argument_list();
+          uint cnt= new_item_arg_list->elements;
+          li.replace(*new_item_arg_list);
+          /* Make iterator li ignore new items */
+          for (cnt--; cnt; cnt--)
+            li++;
+        }
+        else
+          li.replace(new_item);
+        should_fix_fields= 1;
       } 
-      should_fix_fields= 1;
-    }
-    if (should_fix_fields)
-      cond->update_used_tables();
-
-    if (!((Item_cond*) cond)->argument_list()->elements ||
-	*cond_value != Item::COND_OK)
-      return (COND*) 0;
-    if (((Item_cond*) cond)->argument_list()->elements == 1)
-    {						// Remove list
-      item= ((Item_cond*) cond)->argument_list()->head();
-      ((Item_cond*) cond)->argument_list()->empty();
-      return item;
+    }   
+    if (*cond_value == Item::COND_UNDEF)
+      *cond_value= tmp_cond_value;
+    switch (tmp_cond_value) {
+    case Item::COND_OK:                        // Not TRUE or FALSE
+      if (and_level || *cond_value == Item::COND_FALSE)
+        *cond_value=tmp_cond_value;
+      break;
+    case Item::COND_FALSE:
+      if (and_level)
+      {
+        *cond_value= tmp_cond_value;
+        return (COND*) 0;                        // Always false
+      }
+      break;
+    case Item::COND_TRUE:
+      if (!and_level)
+      {
+        *cond_value= tmp_cond_value;
+        return (COND*) 0;                        // Always true
+      }
+      break;
+    case Item::COND_UNDEF:                        // Impossible
+      break; /* purecov: deadcode */
     }
   }
-  else if (cond_is_datetime_is_null(cond))
+  COND *cond= this;
+  if (!new_equalities.is_empty())
   {
-    /* fix to replace 'NULL' dates with '0' (shreeve@xxxxxxx) */
-    /*
-      See BUG#12594011
-      Documentation says that
-      SELECT datetime_notnull d FROM t1 WHERE d IS NULL
-      shall return rows where d=='0000-00-00'
-
-      Thus, for DATE and DATETIME columns defined as NOT NULL,
-      "date_notnull IS NULL" has to be modified to
-      "date_notnull IS NULL OR date_notnull == 0" (if outer join)
-      "date_notnull == 0"                         (otherwise)
-
+    DBUG_ASSERT(and_level);
+    /* 
+      Merge multiple equalities that were cut from the results of 
+      simplification of OR formulas converted into AND formulas.
+      These multiple equalities are to be merged into the
+      multiple equalities of  cond_arg_list.
     */
-    Item **args= ((Item_func_isnull*) cond)->arguments();
-    Field *field=((Item_field*) args[0])->field;
-
-    Item *item0= new(thd->mem_root) Item_int((longlong)0, 1);
-    Item *eq_cond= new(thd->mem_root) Item_func_eq(args[0], item0);
-    if (!eq_cond)
-      return cond;
-
-        if (field->table->pos_in_table_list->is_inner_table_of_outer_join())
-    {
-      // outer join: transform "col IS NULL" to "col IS NULL or col=0"
-      Item *or_cond= new(thd->mem_root) Item_cond_or(eq_cond, cond);
-      if (!or_cond)
-        return cond;
-      cond= or_cond;
+    COND_EQUAL *cond_equal= &((Item_cond_and *) this)->m_cond_equal;
+    List<Item_equal> *cond_equalities= &cond_equal->current_level;
+    cond_arg_list->disjoin((List<Item> *) cond_equalities);
+    Item_equal *equality;
+    List_iterator_fast<Item_equal> it(new_equalities);
+    while ((equality= it++))
+    {
+      equality->upper_levels= cond_equal->upper_levels;
+      equality->merge_into_list(thd, cond_equalities, false, false);
+      List_iterator_fast<Item_equal> ei(*cond_equalities);
+      while ((equality= ei++))
+      {
+        if (equality->const_item() && !equality->val_int())
+        {
+          *cond_value= Item::COND_FALSE;
+          return (COND*) 0;
+        }
+      }
     }
-    else
+    cond_arg_list->append((List<Item> *) cond_equalities);
+    /* 
+      Propagate the newly formed multiple equalities to
+      the all AND/OR levels of cond 
+    */
+    bool is_simplifiable_cond= false;
+    propagate_new_equalities(thd, this, cond_equalities,
+                             cond_equal->upper_levels,
+                             &is_simplifiable_cond);
+    /*
+      If the above propagation of multiple equalities brings us
+      to multiple equalities that are always FALSE then try to
+      simplify the condition with remove_eq_cond() again.
+    */ 
+    if (is_simplifiable_cond)
     {
-      // not outer join: transform "col IS NULL" to "col=0"
-      cond= eq_cond;
-    }
+      if (!(cond= cond->remove_eq_conds(thd, cond_value, false)))
+        return cond;
+    } 
+    should_fix_fields= 1;
+  }
+  if (should_fix_fields)
+    cond->update_used_tables();
+
+  if (!((Item_cond*) cond)->argument_list()->elements ||
+      *cond_value != Item::COND_OK)
+    return (COND*) 0;
+  if (((Item_cond*) cond)->argument_list()->elements == 1)
+  {                                                // Remove list
+    item= ((Item_cond*) cond)->argument_list()->head();
+    ((Item_cond*) cond)->argument_list()->empty();
+    return item;
+  }
+  *cond_value= Item::COND_OK;
+  return cond;
+}
 
-    cond->fix_fields(thd, &cond);
 
-    if (cond->const_item() && !cond->is_expensive())
-    {
-      *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
-      return (COND*) 0;
-    }
+COND *
+Item::remove_eq_conds(THD *thd, Item::cond_result *cond_value, bool top_level)
+{
+  if (const_item() && !is_expensive())
+  {
+    *cond_value= eval_const_cond(this) ? Item::COND_TRUE : Item::COND_FALSE;
+    return (COND*) 0;
   }
-  else if (cond->const_item() && !cond->is_expensive())
+  *cond_value= Item::COND_OK;
+  return this;                                        // Point at next and level
+}
+
+
+COND *
+Item_bool_func2::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
+                                 bool top_level)
+{
+  if (const_item() && !is_expensive())
   {
-    *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
+    *cond_value= eval_const_cond(this) ? Item::COND_TRUE : Item::COND_FALSE;
     return (COND*) 0;
   }
-  else if ((*cond_value= cond->eq_cmp_result()) != Item::COND_OK)
-  {						// boolan compare function
-    Item *left_item=	((Item_func*) cond)->arguments()[0];
-    Item *right_item= ((Item_func*) cond)->arguments()[1];
-    if (left_item->eq(right_item,1))
+  if ((*cond_value= eq_cmp_result()) != Item::COND_OK)
+  {
+    if (args[0]->eq(args[1], true))
     {
-      if (!left_item->maybe_null ||
-	  ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)
-	return (COND*) 0;			// Compare of identical items
+      if (!args[0]->maybe_null || functype() == Item_func::EQUAL_FUNC)
+        return (COND*) 0;                       // Compare of identical items
     }
   }
-  *cond_value=Item::COND_OK;
-  return cond;					// Point at next and level
+  *cond_value= Item::COND_OK;
+  return this;                                  // Point at next and level
 }
 
+
 /**
   Remove const and eq items. Return new item, or NULL if no condition
   cond_value is set to according:
   COND_OK    query is possible (field = constant)
-  COND_TRUE  always true	( 1 = 1 )
-  COND_FALSE always false	( 1 = 2 )
+  COND_TRUE  always true       ( 1 = 1 )
+  COND_FALSE always false      ( 1 = 2 )
 
   SYNPOSIS
     remove_eq_conds()
-    thd 			THD environment
+    thd                         THD environment
     cond                        the condition to handle
     cond_value                  the resulting value of the condition
 
@@ -15245,11 +15217,66 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
 */
 
 COND *
-remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
+Item_func_isnull::remove_eq_conds(THD *thd, Item::cond_result *cond_value,
+                                  bool top_level)
 {
-  if (cond->type() == Item::FUNC_ITEM &&
-      ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC)
+  if (args[0]->type() == Item::FIELD_ITEM)
   {
+    Field *field= ((Item_field*) args[0])->field;
+
+    if (((field->type() == MYSQL_TYPE_DATE) ||
+         (field->type() == MYSQL_TYPE_DATETIME)) &&
+         (field->flags & NOT_NULL_FLAG))                           
+    {
+      /* fix to replace 'NULL' dates with '0' (shreeve@xxxxxxx) */
+      /*
+        See BUG#12594011
+        Documentation says that
+        SELECT datetime_notnull d FROM t1 WHERE d IS NULL
+        shall return rows where d=='0000-00-00'
+
+        Thus, for DATE and DATETIME columns defined as NOT NULL,
+        "date_notnull IS NULL" has to be modified to
+        "date_notnull IS NULL OR date_notnull == 0" (if outer join)
+        "date_notnull == 0"                         (otherwise)
+
+      */
+
+      Item *item0= new(thd->mem_root) Item_int((longlong)0, 1);
+      Item *eq_cond= new(thd->mem_root) Item_func_eq(args[0], item0);
+      if (!eq_cond)
+        return this;
+
+      COND *cond= this;
+      if (field->table->pos_in_table_list->is_inner_table_of_outer_join())
+      {
+        // outer join: transform "col IS NULL" to "col IS NULL or col=0"
+        Item *or_cond= new(thd->mem_root) Item_cond_or(eq_cond, this);
+        if (!or_cond)
+          return this;
+        cond= or_cond;
+      }
+      else
+      {
+        // not outer join: transform "col IS NULL" to "col=0"
+        cond= eq_cond;
+      }
+
+      cond->fix_fields(thd, &cond);
+      /*
+        Note: although args[0] is a field, cond can still be a constant
+        (in case field is a part of a dependent subquery).
+
+        Note: we call cond->Item::remove_eq_conds() non-virtually (statically)
+        for performance purpose.
+        A non-qualified call, i.e. just cond->remove_eq_conds(),
+        would call Item_bool_func2::remove_eq_conds() instead, which would
+        try to do some extra job to detect if args[0] and args[1] are
+        equivalent items. We know they are not (we have field=0 here).
+      */
+      return cond->Item::remove_eq_conds(thd, cond_value, false);
+    }
+    
     /*
       Handles this special case for some ODBC applications:
       The are requesting the row that was just updated with a auto_increment
@@ -15258,35 +15285,37 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
       SELECT * from table_name where auto_increment_column IS NULL
       This will be changed to:
       SELECT * from table_name where auto_increment_column = LAST_INSERT_ID
+      
+      Note, this substitution is done if the NULL test is the only condition!
+      If the NULL test is a part of a more complex condition, it is not
+      substituted and is treated normally:
+        WHERE auto_increment IS NULL AND something_else
     */
 
-    Item_func_isnull *func=(Item_func_isnull*) cond;
-    Item **args= func->arguments();
-    if (args[0]->type() == Item::FIELD_ITEM)
+    if (top_level) // "auto_increment_column IS NULL" is the only condition
     {
-      Field *field=((Item_field*) args[0])->field;
       if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null &&
-	  (thd->variables.option_bits & OPTION_AUTO_IS_NULL) &&
-	  (thd->first_successful_insert_id_in_prev_stmt > 0 &&
+          (thd->variables.option_bits & OPTION_AUTO_IS_NULL) &&
+          (thd->first_successful_insert_id_in_prev_stmt > 0 &&
            thd->substitute_null_with_insert_id))
       {
-#ifdef HAVE_QUERY_CACHE
-	query_cache_abort(&thd->query_cache_tls);
-#endif
-	COND *new_cond;
-	if ((new_cond= new Item_func_eq(args[0],
-					new Item_int("last_insert_id()",
+  #ifdef HAVE_QUERY_CACHE
+        query_cache_abort(&thd->query_cache_tls);
+  #endif
+        COND *new_cond, *cond= this;
+        if ((new_cond= new Item_func_eq(args[0],
+                                        new Item_int("last_insert_id()",
                                                      thd->read_first_successful_insert_id_in_prev_stmt(),
                                                      MY_INT64_NUM_DECIMAL_DIGITS))))
-	{
-	  cond=new_cond;
+        {
+          cond= new_cond;
           /*
             Item_func_eq can't be fixed after creation so we do not check
             cond->fixed, also it do not need tables so we use 0 as second
             argument.
           */
-	  cond->fix_fields(thd, &cond);
-	}
+          cond->fix_fields(thd, &cond);
+        }
         /*
           IS NULL should be mapped to LAST_INSERT_ID only for first row, so
           clear for next row
@@ -15298,7 +15327,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
       }
     }
   }
-  return internal_remove_eq_conds(thd, cond, cond_value); // Scan all the condition
+  return Item::remove_eq_conds(thd, cond_value, top_level);
 }
 
 
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 66789ee..2c87233 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -1803,7 +1803,6 @@ bool cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref);
 bool error_if_full_join(JOIN *join);
 int report_error(TABLE *table, int error);
 int safe_index_read(JOIN_TAB *tab);
-COND *remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value);
 int get_quick_record(SQL_SELECT *select);
 SORT_FIELD * make_unireg_sortorder(THD *thd, ORDER *order, uint *length,
                                   SORT_FIELD *sortorder);
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 37ad416..e490edc 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -377,7 +377,7 @@ int mysql_update(THD *thd,
   if (conds)
   {
     Item::cond_result cond_value;
-    conds= remove_eq_conds(thd, conds, &cond_value);
+    conds= conds->remove_eq_conds(thd, &cond_value, true);
     if (cond_value == Item::COND_FALSE)
     {
       limit= 0;                                   // Impossible WHERE