List:Commits« Previous MessageNext Message »
From:igor Date:September 1 2006 11:23am
Subject:bk commit into 5.0 tree (igor:1.2272) BUG#16081
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of igor. When igor does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2006-09-01 04:23:04-07:00, igor@stripped +5 -0
  Fixed bug #16081: row equalities were not taken into
  account by the optimizer.
  Now all row equalities are converted into conjunctions of
  equalities between row elements. They are taken into account
  by the optimizer together with the original regular equality
  predicates.

  mysql-test/r/join_outer.result@stripped, 2006-09-01 04:22:57-07:00, igor@stripped +1 -1
    Adjusted results after fix for bug #16081.

  mysql-test/r/row.result@stripped, 2006-09-01 04:22:57-07:00, igor@stripped +125 -0
    Added a test cases for bug #16081.

  mysql-test/t/row.test@stripped, 2006-09-01 04:22:57-07:00, igor@stripped +47 -0
    Added a test cases for bug #16081.

  sql/sql_list.h@stripped, 2006-09-01 04:22:57-07:00, igor@stripped +3 -3
    Corrected the copy constructor for the class base_list.
    The previous implementation resulted in creation of an
    inconsistent base_list if the source list was empty.

  sql/sql_select.cc@stripped, 2006-09-01 04:22:57-07:00, igor@stripped +304 -152
    Fixed bug #16081: row equalities were not taken into
    account by the optimizer.
    Now all row equalities are converted into conjunctions of
    equalities between row elements. They are taken into account
    by the optimizer together with the original regular equality
    predicates.

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	igor
# Host:	rurik.mysql.com
# Root:	/home/igor/dev-opt/mysql-5.0-opt-bug16081

--- 1.39/sql/sql_list.h	2006-09-01 04:23:14 -07:00
+++ 1.40/sql/sql_list.h	2006-09-01 04:23:14 -07:00
@@ -94,9 +94,9 @@
   inline base_list() { empty(); }
   inline base_list(const base_list &tmp) :Sql_alloc()
   {
-    elements=tmp.elements;
-    first=tmp.first;
-    last=tmp.last;
+    elements= tmp.elements;
+    first= tmp.first;
+    last= elements ? tmp.last : &first;
   }
   inline base_list(bool error) { }
   inline bool push_back(void *info)

--- 1.447/sql/sql_select.cc	2006-09-01 04:23:14 -07:00
+++ 1.448/sql/sql_select.cc	2006-09-01 04:23:14 -07:00
@@ -6351,29 +6351,30 @@
 
   
 /* 
-  Check whether an item is a simple equality predicate and if so
-  create/find a multiple equality for this predicate
+  Check whether an equality can be used to build multiple equalities
 
   SYNOPSIS
-    check_equality()
-    item       item to check
-    cond_equal multiple equalities that must hold together with the predicate
+    check_simple_equality()
+      left_item   left term of the quality to be checked
+      right_item  right term of the equality to be checked
+      item        equality item if the equality originates from a condition
+                  predicate, 0 if the equality is the result of row elimination
+      cond_equal  multiple equalities that must hold together with the equality
 
   DESCRIPTION
-    This function first checks whether an item is a simple equality i.e.
-    the one that equates a field with another field or a constant
-    (item=constant_item or item=field_item).
-    If this is the case the function looks a for a multiple equality
+    This function first checks whether the equality (left_item=right_item)
+    is a simple equality i.e. the one that equates a field with another field
+    or a constant (field=field_item or field=const_item).
+    If this is the case the function looks for a multiple equality
     in the lists referenced directly or indirectly by cond_equal inferring
     the given simple equality. If it doesn't find any, it builds a multiple
     equality that covers the predicate, i.e. the predicate can be inferred
-    from it.
+    from this multiple equality.
     The built multiple equality could be obtained in such a way:
     create a binary  multiple equality equivalent to the predicate, then
     merge it, if possible, with one of old multiple equalities.
     This guarantees that the set of multiple equalities covering equality
-    predicates will
-    be minimal.
+    predicates will be minimal.
 
   EXAMPLE
     For the where condition
@@ -6391,7 +6392,7 @@
     and will transform *cond_equal into (ptr(CE),[Item_equal(f,e)]).
 
   NOTES
-    Now only fields that have the same type defintions (verified by
+    Now only fields that have the same type definitions (verified by
     the Field::eq_def method) are placed to the same multiple equalities.
     Because of this some equality predicates are not eliminated and
     can be used in the constant propagation procedure.
@@ -6424,170 +6425,282 @@
     copying would be much more complicated.
 
   RETURN
-    TRUE  - if the predicate is a simple equality predicate
-    FALSE - otherwise
+    TRUE    if the predicate is a simple equality predicate to be used
+            for building multiple equalities
+    FALSE   otherwise
 */
 
-static bool check_equality(Item *item, COND_EQUAL *cond_equal)
+static bool check_simple_equality(Item *left_item, Item *right_item,
+                                  Item *item, COND_EQUAL *cond_equal)
 {
-  if (item->type() == Item::FUNC_ITEM &&
-         ((Item_func*) item)->functype() == Item_func::EQ_FUNC)
+  if (left_item->type() == Item::REF_ITEM &&
+      ((Item_ref*)left_item)->ref_type() == Item_ref::VIEW_REF)
   {
-    Item *left_item= ((Item_func*) item)->arguments()[0];
-    Item *right_item= ((Item_func*) item)->arguments()[1];
+    if (((Item_ref*)left_item)->depended_from)
+      return FALSE;
+    left_item= left_item->real_item();
+  }
+  if (right_item->type() == Item::REF_ITEM &&
+      ((Item_ref*)right_item)->ref_type() == Item_ref::VIEW_REF)
+  {
+    if (((Item_ref*)right_item)->depended_from)
+      return FALSE;
+    right_item= right_item->real_item();
+  }
+  if (left_item->type() == Item::FIELD_ITEM &&
+      right_item->type() == Item::FIELD_ITEM &&
+      !((Item_field*)left_item)->depended_from &&
+      !((Item_field*)right_item)->depended_from)
+  {
+    /* The predicate the form field1=field2 is processed */
+
+    Field *left_field= ((Item_field*) left_item)->field;
+    Field *right_field= ((Item_field*) right_item)->field;
+
+    if (!left_field->eq_def(right_field))
+      return FALSE;
+
+    /* Search for multiple equalities containing field1 and/or field2 */
+    bool left_copyfl, right_copyfl;
+    Item_equal *left_item_equal=
+               find_item_equal(cond_equal, left_field, &left_copyfl);
+    Item_equal *right_item_equal= 
+               find_item_equal(cond_equal, right_field, &right_copyfl);
 
-    if (left_item->type() == Item::REF_ITEM &&
-        ((Item_ref*)left_item)->ref_type() == Item_ref::VIEW_REF)
+    /* As (NULL=NULL) != TRUE we can't just remove the predicate f=f */
+    if (left_field->eq(right_field)) /* f = f */
+      return (!(left_field->maybe_null() && !left_item_equal)); 
+
+    if (left_item_equal && left_item_equal == right_item_equal)
     {
-      if (((Item_ref*)left_item)->depended_from)
-        return FALSE;
-      left_item= left_item->real_item();
+      /* 
+        The equality predicate is inference of one of the existing
+        multiple equalities, i.e the condition is already covered
+        by upper level equalities
+      */
+       return TRUE;
     }
-    if (right_item->type() == Item::REF_ITEM &&
-        ((Item_ref*)right_item)->ref_type() == Item_ref::VIEW_REF)
+      
+    /* Copy the found multiple equalities at the current level if needed */
+    if (left_copyfl)
     {
-      if (((Item_ref*)right_item)->depended_from)
-        return FALSE;
-      right_item= right_item->real_item();
+      /* left_item_equal of an upper level contains left_item */
+      left_item_equal= new Item_equal(left_item_equal);
+      cond_equal->current_level.push_back(left_item_equal);
     }
-    if (left_item->type() == Item::FIELD_ITEM &&
-        right_item->type() == Item::FIELD_ITEM &&
-        !((Item_field*)left_item)->depended_from &&
-        !((Item_field*)right_item)->depended_from)
+    if (right_copyfl)
     {
-      /* The predicate the form field1=field2 is processed */
+      /* right_item_equal of an upper level contains right_item */
+      right_item_equal= new Item_equal(right_item_equal);
+      cond_equal->current_level.push_back(right_item_equal);
+    }
 
-      Field *left_field= ((Item_field*) left_item)->field;
-      Field *right_field= ((Item_field*) right_item)->field;
+    if (left_item_equal)
+    { 
+      /* left item was found in the current or one of the upper levels */
+      if (! right_item_equal)
+        left_item_equal->add((Item_field *) right_item);
+      else
+      {
+        /* Merge two multiple equalities forming a new one */
+        left_item_equal->merge(right_item_equal);
+        /* Remove the merged multiple equality from the list */
+        List_iterator<Item_equal> li(cond_equal->current_level);
+        while ((li++) != right_item_equal);
+        li.remove();
+      }
+    }
+    else
+    { 
+      /* left item was not found neither the current nor in upper levels  */
+      if (right_item_equal)
+        right_item_equal->add((Item_field *) left_item);
+      else 
+      {
+        /* None of the fields was found in multiple equalities */
+        Item_equal *item= new Item_equal((Item_field *) left_item,
+                                         (Item_field *) right_item);
+        cond_equal->current_level.push_back(item);
+      }
+    }
+    return TRUE;
+  }
 
-      if (!left_field->eq_def(right_field))
-        return FALSE;
+  {
+    /* The predicate of the form field=const/const=field is processed */
+    Item *const_item= 0;
+    Item_field *field_item= 0;
+    if (left_item->type() == Item::FIELD_ITEM &&
+        !((Item_field*)left_item)->depended_from &&
+        right_item->const_item())
+    {
+      field_item= (Item_field*) left_item;
+      const_item= right_item;
+    }
+    else if (right_item->type() == Item::FIELD_ITEM &&
+             !((Item_field*)right_item)->depended_from &&
+             left_item->const_item())
+    {
+      field_item= (Item_field*) right_item;
+      const_item= left_item;
+    }
 
-      if (left_field->eq(right_field))  /* f = f */
-        return TRUE;
-      
-      /* Search for multiple equalities containing field1 and/or field2 */
-      bool left_copyfl, right_copyfl;
-      Item_equal *left_item_equal=
-                 find_item_equal(cond_equal, left_field, &left_copyfl);
-      Item_equal *right_item_equal= 
-                 find_item_equal(cond_equal, right_field, &right_copyfl);
+    if (const_item &&
+        field_item->result_type() == const_item->result_type())
+    {
+      bool copyfl;
 
-      if (left_item_equal && left_item_equal == right_item_equal)
+      if (field_item->result_type() == STRING_RESULT)
       {
-        /* 
-           The equality predicate is inference of one of the existing
-           multiple equalities, i.e the condition is already covered
-           by upper level equalities
-        */
-          return TRUE;
-      }
-      
-      /* Copy the found multiple equalities at the current level if needed */
-      if (left_copyfl)
+        CHARSET_INFO *cs= ((Field_str*) field_item->field)->charset();
+        if (!item)
+        {
+          Item_func_eq *eq_item;
+          if ((eq_item= new Item_func_eq(left_item, right_item)))
+            return FALSE;
+          eq_item->set_cmp_func();
+          eq_item->quick_fix_field();
+          item= eq_item;
+        }  
+        if ((cs != ((Item_func *) item)->compare_collation()) ||
+            !cs->coll->propagate(cs, 0, 0))
+          return FALSE;
+      }
+
+      Item_equal *item_equal = find_item_equal(cond_equal,
+                                               field_item->field, &copyfl);
+      if (copyfl)
       {
-        /* left_item_equal of an upper level contains left_item */
-        left_item_equal= new Item_equal(left_item_equal);
-        cond_equal->current_level.push_back(left_item_equal);
+        item_equal= new Item_equal(item_equal);
+        cond_equal->current_level.push_back(item_equal);
       }
-      if (right_copyfl)
+      if (item_equal)
       {
-        /* right_item_equal of an upper level contains right_item */
-        right_item_equal= new Item_equal(right_item_equal);
-        cond_equal->current_level.push_back(right_item_equal);
-      }
-
-      if (left_item_equal)
-      { 
-        /* left item was found in the current or one of the upper levels */
-        if (! right_item_equal)
-          left_item_equal->add((Item_field *) right_item);
-        else
-        {
-          /* Merge two multiple equalities forming a new one */
-          left_item_equal->merge(right_item_equal);
-          /* Remove the merged multiple equality from the list */
-          List_iterator<Item_equal> li(cond_equal->current_level);
-          while ((li++) != right_item_equal);
-          li.remove();
-        }
+        /* 
+          The flag cond_false will be set to 1 after this, if item_equal
+          already contains a constant and its value is  not equal to
+          the value of const_item.
+        */
+        item_equal->add(const_item);
       }
       else
-      { 
-        /* left item was not found neither the current nor in upper levels  */
-         if (right_item_equal)
-           right_item_equal->add((Item_field *) left_item);
-         else 
-         {
-           /* None of the fields was found in multiple equalities */
-           Item_equal *item= new Item_equal((Item_field *) left_item,
-                                            (Item_field *) right_item);
-           cond_equal->current_level.push_back(item);
-         }
+      {
+        item_equal= new Item_equal(const_item, field_item);
+        cond_equal->current_level.push_back(item_equal);
       }
       return TRUE;
     }
+  }
+  return FALSE;
+}
 
-    {
-      /* The predicate of the form field=const/const=field is processed */
-      Item *const_item= 0;
-      Item_field *field_item= 0;
-      if (left_item->type() == Item::FIELD_ITEM &&
-          !((Item_field*)left_item)->depended_from &&
-          right_item->const_item())
-      {
-        field_item= (Item_field*) left_item;
-        const_item= right_item;
-      }
-      else if (right_item->type() == Item::FIELD_ITEM &&
-               !((Item_field*)right_item)->depended_from &&
-               left_item->const_item())
-      {
-        field_item= (Item_field*) right_item;
-        const_item= left_item;
-      }
 
-      if (const_item &&
-          field_item->result_type() == const_item->result_type())
-      {
-        bool copyfl;
+/* 
+  Convert row equalities into a conjunction of regular equalities
 
-        if (field_item->result_type() == STRING_RESULT)
-        {
-          CHARSET_INFO *cs= ((Field_str*) field_item->field)->charset();
-          if ((cs != ((Item_cond *) item)->compare_collation()) ||
-              !cs->coll->propagate(cs, 0, 0))
-            return FALSE;
-        }
+  SYNOPSIS
+    check_row_equality()
+      left_row   left term of the row equality to be processed     
+      right_row  right term of the row equality to be processed
+      cond_equal multiple equalities that must hold together with the predicate
+      eq_list    results of conversions of row equalities that are not simple
+                 enough to form multiple equalities
 
-        Item_equal *item_equal = find_item_equal(cond_equal,
-                                                 field_item->field, &copyfl);
-        if (copyfl)
-        {
-          item_equal= new Item_equal(item_equal);
-          cond_equal->current_level.push_back(item_equal);
-        }
-        if (item_equal)
-        {
-          /* 
-            The flag cond_false will be set to 1 after this, if item_equal
-            already contains a constant and its value is  not equal to
-            the value of const_item.
-          */
-          item_equal->add(const_item);
-        }
-        else
-        {
-          item_equal= new Item_equal(const_item, field_item);
-          cond_equal->current_level.push_back(item_equal);
-        }
-        return TRUE;
-      }
+  DESCRIPTION
+    The function converts a row equality of the form (E1,...,En)=(E'1,...,E'n)
+    into a list of equalities E1=E'1,...,En=E'n. For each of these equalities
+    Ei=E'i the function checks whether it is a simple equality or a row equality.
+    If it is a simple equality it is used to expand multiple equalities of
+    cond_equal. If it is a row equality it converted to a sequence of equalities
+    between row elements. If Ei=E'i is neither a simple equality nor a row
+    equality the item for this predicate is added to eq_list.
+
+  RETURN
+    TRUE    if conversion has succeeded (no fatal error) 
+    FALSE   otherwise  
+*/
+ 
+static bool check_row_equality(Item *left_row, Item_row *right_row,
+                               COND_EQUAL *cond_equal, List<Item>* eq_list)
+{ 
+  uint n= left_row->cols();
+  for (uint i= 0 ; i < n; i++)
+  {
+    bool is_converted;
+    Item *left_item= left_row->el(i);
+    Item *right_item= right_row->el(i);
+    if (left_item->type() == Item::ROW_ITEM &&
+        right_item->type() == Item::ROW_ITEM)
+      is_converted= check_row_equality((Item_row *) left_item,
+	                               (Item_row *) right_item,
+				       cond_equal, eq_list);
+    else 
+      is_converted= check_simple_equality(left_item, right_item, 0, cond_equal);
+
+    if (!is_converted)  
+    {
+      Item_func_eq *eq_item;
+      if (!(eq_item= new Item_func_eq(left_item, right_item)))
+        return FALSE;
+      eq_item->set_cmp_func();
+      eq_item->quick_fix_field();
+      eq_list->push_back(eq_item);
     }
   }
+  return TRUE;
+}
+
+
+/* 
+  Eliminate row equalities and form multiple equalities predicates 
+
+  SYNOPSIS
+    check_equality()
+      item       predicate to process     
+      cond_equal multiple equalities that must hold together with the predicate
+      eq_list    results of conversions of row equalities that are not simple
+                 enough to form multiple equalities
+
+  DESCRIPTION
+    This function checks whether the item is a simple equality
+    i.e. the one that equates a field with another field or a constant
+    (field=field_item or field=constant_item), or, a row equality.
+    For a simple equality the function looks for a multiple equality
+    in the lists referenced directly or indirectly by cond_equal inferring
+    the given simple equality. If it doesn't find any, it builds/expands
+    multiple equality that covers the predicate.
+    Row equalities are eliminated substituted for conjunctive regular equalities
+    which are treated in the same way as original equality predicates.
+
+  RETURN
+    TRUE   if re-writing rules have been applied
+    FALSE  otherwise, i.e.
+           if the predicate is not an equality,
+           or, if the equality is neither a simple one nor a row equality,
+           or, if the procedure fails by a fatal error.
+*/
+
+static bool check_equality(Item *item, COND_EQUAL *cond_equal,
+                           List<Item> *eq_list)
+{
+  if (item->type() == Item::FUNC_ITEM &&
+         ((Item_func*) item)->functype() == Item_func::EQ_FUNC)
+  {
+    Item *left_item= ((Item_func*) item)->arguments()[0];
+    Item *right_item= ((Item_func*) item)->arguments()[1];
+
+    if (left_item->type() == Item::ROW_ITEM &&
+        right_item->type() == Item::ROW_ITEM)
+      return check_row_equality((Item_row *) left_item,
+                                (Item_row *) right_item,
+                                cond_equal, eq_list);
+    else 
+      return check_simple_equality(left_item, right_item, item, cond_equal);
+  } 
   return FALSE;
 }
 
+                          
 /* 
   Replace all equality predicates in a condition by multiple equality items
 
@@ -6659,10 +6772,12 @@
   Item_equal *item_equal;
   uint members;
   COND_EQUAL cond_equal;
+  COND *new_cond;
   cond_equal.upper_levels= inherited;
 
   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();
@@ -6685,7 +6800,7 @@
           structure here because it's restored before each
           re-execution of any prepared statement/stored procedure.
         */
-        if (check_equality(item, &cond_equal))
+        if (check_equality(item, &cond_equal, &eq_list))
           li.remove();
       }
 
@@ -6732,10 +6847,14 @@
       }
     }
     if (and_level)
+    {
+      args->concat(&eq_list);
       args->concat((List<Item> *)&cond_equal.current_level);
+    }
   }
   else if (cond->type() == Item::FUNC_ITEM)
   {
+    List<Item> eq_list;
     /*
       If an equality predicate forms the whole and level,
       we call it standalone equality and it's processed here.
@@ -6746,12 +6865,45 @@
       for WHERE a=b AND c=d AND (b=c OR d=5)
       b=c is replaced by =(a,b,c,d).  
      */
-    if (check_equality(cond, &cond_equal) &&
-        (item_equal= cond_equal.current_level.pop()))
+    if (check_equality(cond, &cond_equal, &eq_list))
     {
-      item_equal->fix_length_and_dec();
-      item_equal->update_used_tables();
-      return item_equal;
+      int n= cond_equal.current_level.elements + eq_list.elements;
+      if (n == 0)
+        return new Item_int((longlong) 1,1);
+      else if (n == 1)
+      {
+        if ((item_equal= cond_equal.current_level.pop()))
+        {
+          item_equal->fix_length_and_dec();
+          item_equal->update_used_tables();
+          return item_equal;
+	}
+        else
+          return eq_list.pop();
+      }
+      else
+      {
+        /* 
+          Here a new AND level must be created. It can happen only
+          when a row equality is processed as a standalone predicate.
+	*/
+        Item_cond_and *and_cond= new Item_cond_and(eq_list);
+        and_cond->quick_fix_field();
+        List<Item> *args= and_cond->argument_list();
+        List_iterator_fast<Item_equal> it(cond_equal.current_level);
+        while ((item_equal= it++))
+        {
+          item_equal->fix_length_and_dec();
+          item_equal->update_used_tables();
+          members= item_equal->members();
+          if (cond_equal.max_members < members)
+            cond_equal.max_members= members; 
+        }
+        and_cond->cond_equal= cond_equal;
+        args->concat((List<Item> *)&cond_equal.current_level);
+        
+        return and_cond;
+      }
     }
     /* 
       For each field reference in cond, not from equal item predicates,
@@ -7038,7 +7190,7 @@
 
 /* 
   Substitute every field reference in a condition by the best equal field 
-  and eliminate all multiplle equality predicates
+  and eliminate all multiple equality predicates
  
   SYNOPSIS
     substitute_for_best_equal_field()

--- 1.23/mysql-test/r/row.result	2006-09-01 04:23:14 -07:00
+++ 1.24/mysql-test/r/row.result	2006-09-01 04:23:14 -07:00
@@ -181,3 +181,128 @@
 select row(NULL,1)=(2,0);
 row(NULL,1)=(2,0)
 0
+CREATE TABLE t1 (a int, b int, PRIMARY KEY (a,b));
+INSERT INTO t1 VALUES (1,1), (2,1), (3,1), (1,2), (3,2), (3,3);
+EXPLAIN SELECT * FROM t1 WHERE a=3 AND b=2;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	const	PRIMARY	PRIMARY	8	const,const	1	Using index
+EXPLAIN SELECT * FROM t1 WHERE (a,b)=(3,2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	const	PRIMARY	PRIMARY	8	const,const	1	Using index
+SELECT * FROM t1 WHERE a=3 and b=2;
+a	b
+3	2
+SELECT * FROM t1 WHERE (a,b)=(3,2);
+a	b
+3	2
+CREATE TABLE t2 (a int, b int, c int, PRIMARY KEY (a,b,c));
+INSERT INTO t2 VALUES
+(1,1,2), (3,1,3), (1,2,2), (4,4,2),
+(1,1,1), (3,1,1), (1,2,1);
+EXPLAIN SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t1.b=t2.b;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	PRIMARY	PRIMARY	8	NULL	6	Using index
+1	SIMPLE	t2	ref	PRIMARY	PRIMARY	8	test.t1.a,test.t1.b	1	Using index
+EXPLAIN SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	PRIMARY	PRIMARY	8	NULL	6	Using index
+1	SIMPLE	t2	ref	PRIMARY	PRIMARY	8	test.t1.a,test.t1.b	1	Using index
+SELECT * FROM t1,t2 WHERE t1.a=t2.a and t1.b=t2.b;
+a	b	a	b	c
+1	1	1	1	1
+1	1	1	1	2
+1	2	1	2	1
+1	2	1	2	2
+3	1	3	1	1
+3	1	3	1	3
+SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b);
+a	b	a	b	c
+1	1	1	1	1
+1	1	1	1	2
+1	2	1	2	1
+1	2	1	2	2
+3	1	3	1	1
+3	1	3	1	3
+EXPLAIN SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t1.b=2;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	PRIMARY	PRIMARY	8	NULL	5	Using where; Using index
+1	SIMPLE	t2	ref	PRIMARY	PRIMARY	4	test.t1.a	1	Using index
+EXPLAIN SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	PRIMARY	PRIMARY	8	NULL	5	Using where; Using index
+1	SIMPLE	t2	ref	PRIMARY	PRIMARY	4	test.t1.a	1	Using index
+SELECT * FROM t1,t2 WHERE t1.a=1 and t1.b=t2.b;
+a	b	a	b	c
+1	1	1	1	2
+1	1	3	1	3
+1	2	1	2	2
+1	1	1	1	1
+1	1	3	1	1
+1	2	1	2	1
+SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,2);
+a	b	a	b	c
+1	2	1	1	1
+1	2	1	1	2
+1	2	1	2	1
+1	2	1	2	2
+3	2	3	1	1
+3	2	3	1	3
+EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b+1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	PRIMARY	PRIMARY	8	NULL	6	Using index
+1	SIMPLE	t2	ref	PRIMARY	PRIMARY	4	test.t1.a	1	Using where; Using index
+Warnings:
+Note	1003	select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`c` AS `c` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`a` = `test`.`t1`.`a`) and (`test`.`t1`.`b` = (`test`.`t2`.`b` + 1)))
+SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b+1);
+a	b	a	b	c
+1	2	1	1	1
+1	2	1	1	2
+3	2	3	1	1
+3	2	3	1	3
+EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE (t1.a-1,t1.b)=(t2.a-1,t2.b+1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	PRIMARY	8	NULL	6	Using index
+1	SIMPLE	t2	index	NULL	PRIMARY	12	NULL	7	Using where; Using index
+Warnings:
+Note	1003	select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`c` AS `c` from `test`.`t1` join `test`.`t2` where (((`test`.`t1`.`a` - 1) = (`test`.`t2`.`a` - 1)) and (`test`.`t1`.`b` = (`test`.`t2`.`b` + 1)))
+SELECT * FROM t1,t2 WHERE (t1.a-1,t1.b)=(t2.a-1,t2.b+1);
+a	b	a	b	c
+1	2	1	1	2
+3	2	3	1	3
+1	2	1	1	1
+3	2	3	1	1
+EXPLAIN SELECT * FROM t2 WHERE a=3 AND b=2;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t2	ref	PRIMARY	PRIMARY	8	const,const	1	Using index
+EXPLAIN SELECT * FROM t2 WHERE (a,b)=(3,2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t2	ref	PRIMARY	PRIMARY	8	const,const	1	Using index
+SELECT * FROM t2 WHERE a=3 and b=2;
+a	b	c
+SELECT * FROM t2 WHERE (a,b)=(3,2);
+a	b	c
+EXPLAIN SELECT * FROM t1,t2 WHERE t2.a=t1.a AND t2.b=2 AND t2.c=1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	PRIMARY	PRIMARY	8	NULL	6	Using index
+1	SIMPLE	t2	eq_ref	PRIMARY	PRIMARY	12	test.t1.a,const,const	1	Using index
+EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE (t2.a,(t2.b,t2.c))=(t1.a,(2,1));
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	PRIMARY	PRIMARY	8	NULL	6	Using index
+1	SIMPLE	t2	eq_ref	PRIMARY	PRIMARY	12	test.t1.a,const,const	1	Using index
+Warnings:
+Note	1003	select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`c` AS `c` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`c` = 1) and (`test`.`t2`.`b` = 2) and (`test`.`t2`.`a` = `test`.`t1`.`a`))
+SELECT * FROM t1,t2 WHERE (t2.a,(t2.b,t2.c))=(t1.a,(2,1));
+a	b	a	b	c
+1	1	1	2	1
+1	2	1	2	1
+EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE t2.a=t1.a AND (t2.b,t2.c)=(2,1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	PRIMARY	PRIMARY	8	NULL	6	Using index
+1	SIMPLE	t2	eq_ref	PRIMARY	PRIMARY	12	test.t1.a,const,const	1	Using index
+Warnings:
+Note	1003	select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b`,`test`.`t2`.`c` AS `c` from `test`.`t1` join `test`.`t2` where ((`test`.`t2`.`c` = 1) and (`test`.`t2`.`b` = 2) and (`test`.`t2`.`a` = `test`.`t1`.`a`))
+SELECT * FROM t1,t2 WHERE t2.a=t1.a AND (t2.b,t2.c)=(2,1);
+a	b	a	b	c
+1	1	1	2	1
+1	2	1	2	1
+DROP TABLE t1,t2;

--- 1.20/mysql-test/t/row.test	2006-09-01 04:23:14 -07:00
+++ 1.21/mysql-test/t/row.test	2006-09-01 04:23:14 -07:00
@@ -92,3 +92,50 @@
 #
 SELECT ROW(1,1,1) = ROW(1,1,1) as `1`, ROW(1,1,1) = ROW(1,2,1) as `0`, ROW(1,NULL,1) = ROW(2,2,1) as `0`, ROW(1,NULL,1) = ROW(1,2,2) as `0`, ROW(1,NULL,1) = ROW(1,2,1) as `null` ;
 select row(NULL,1)=(2,0);
+
+#
+# Bug #16081: row equalities are to be used for query optimizations  
+#
+
+CREATE TABLE t1 (a int, b int, PRIMARY KEY (a,b));
+INSERT INTO t1 VALUES (1,1), (2,1), (3,1), (1,2), (3,2), (3,3);
+
+EXPLAIN SELECT * FROM t1 WHERE a=3 AND b=2;
+EXPLAIN SELECT * FROM t1 WHERE (a,b)=(3,2);
+SELECT * FROM t1 WHERE a=3 and b=2;
+SELECT * FROM t1 WHERE (a,b)=(3,2);
+
+CREATE TABLE t2 (a int, b int, c int, PRIMARY KEY (a,b,c));
+INSERT INTO t2 VALUES
+  (1,1,2), (3,1,3), (1,2,2), (4,4,2),
+  (1,1,1), (3,1,1), (1,2,1);
+
+EXPLAIN SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t1.b=t2.b;
+EXPLAIN SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b);
+SELECT * FROM t1,t2 WHERE t1.a=t2.a and t1.b=t2.b;
+SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b);
+
+EXPLAIN SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t1.b=2;
+EXPLAIN SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,2);
+SELECT * FROM t1,t2 WHERE t1.a=1 and t1.b=t2.b;
+SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,2);
+
+EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b+1);
+SELECT * FROM t1,t2 WHERE (t1.a,t1.b)=(t2.a,t2.b+1);
+
+EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE (t1.a-1,t1.b)=(t2.a-1,t2.b+1);
+SELECT * FROM t1,t2 WHERE (t1.a-1,t1.b)=(t2.a-1,t2.b+1);
+
+EXPLAIN SELECT * FROM t2 WHERE a=3 AND b=2;
+EXPLAIN SELECT * FROM t2 WHERE (a,b)=(3,2);
+SELECT * FROM t2 WHERE a=3 and b=2;
+SELECT * FROM t2 WHERE (a,b)=(3,2);
+
+EXPLAIN SELECT * FROM t1,t2 WHERE t2.a=t1.a AND t2.b=2 AND t2.c=1;
+EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE (t2.a,(t2.b,t2.c))=(t1.a,(2,1));
+SELECT * FROM t1,t2 WHERE (t2.a,(t2.b,t2.c))=(t1.a,(2,1));
+
+EXPLAIN EXTENDED SELECT * FROM t1,t2 WHERE t2.a=t1.a AND (t2.b,t2.c)=(2,1);
+SELECT * FROM t1,t2 WHERE t2.a=t1.a AND (t2.b,t2.c)=(2,1);
+
+DROP TABLE t1,t2;

--- 1.51/mysql-test/r/join_outer.result	2006-09-01 04:23:14 -07:00
+++ 1.52/mysql-test/r/join_outer.result	2006-09-01 04:23:14 -07:00
@@ -1126,7 +1126,7 @@
 7	8	7	5
 EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a = t2.a OR t1.a = t2.b;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t2	ALL	PRIMARY	NULL	NULL	NULL	4	Using where
+1	SIMPLE	t2	ALL	PRIMARY	NULL	NULL	NULL	4	
 1	SIMPLE	t1	eq_ref	PRIMARY	PRIMARY	4	test.t2.a	1	
 EXPLAIN SELECT * FROM t1 LEFT JOIN t2 ON t1.a = t2.a WHERE t1.a IN(t2.a, t2.b);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
Thread
bk commit into 5.0 tree (igor:1.2272) BUG#16081igor1 Sep