From da566c16c0c03f56caa8bd6bfd9452b73ad1a456 Mon Sep 17 00:00:00 2001
From: unknown <igor@rurik.mysql.com>
Date: Wed, 26 Nov 2003 17:23:52 -0800
Subject: [PATCH] This ChangeSet Introdices Item_equal. The objects of this
 class represent multiple conjunctive equalities in where conditions:
 =(f1,f2,...fn) <=> f1=f2 and f2= ... and =fn. The objects are used to
 generate new possibale paths to access the tables when executing a query.
 They are also used to optimize the execution plan chosen by the optimizer for
 the query.

sql/item.cc:
  Introduced Item_equal
sql/item.h:
  Introduced Item_equal.
  Added traverse method.
sql/item_cmpfunc.cc:
  Introduced Item_equal.
  Added traverse mehtod.
sql/item_cmpfunc.h:
  Itroduced Item_equal and Item_equal_iterator.
sql/item_func.cc:
  Added traverse method.
  Introduced Item_equal.
sql/item_func.h:
  Introduced Item_equal.
  Added traverse method.
sql/item_row.cc:
  Added traverse method.
sql/item_row.h:
  Added traverse method.
sql/item_strfunc.h:
  Added traverse method.
sql/opt_range.cc:
  Used Item_equal in range analysis.
sql/opt_sum.cc:
  Introduced Item_equal.
sql/sql_list.h:
  Added concat and disjoin methods to lists.
  Fixed remove method for lists.
sql/sql_select.cc:
  Introdiced Item_equal:
    created Item_equal;
    used Item_equal objects to generate new paths to access tables.
    used Item_equal objects to optimize the execution plan chosen by optimizer.
sql/sql_select.h:
  Introduced Item_equal.
---
 sql/item.cc         |  47 ++-
 sql/item.h          |  26 +-
 sql/item_cmpfunc.cc | 233 ++++++++++++-
 sql/item_cmpfunc.h  |  63 ++++
 sql/item_func.cc    |  34 ++
 sql/item_func.h     |   8 +-
 sql/item_row.cc     |  12 +
 sql/item_row.h      |   1 +
 sql/item_strfunc.h  |   8 +
 sql/opt_range.cc    | 222 +++++++-----
 sql/opt_sum.cc      |  15 +
 sql/sql_list.h      |  34 +-
 sql/sql_select.cc   | 801 ++++++++++++++++++++++++++++++++++++++++++--
 sql/sql_select.h    |   2 +
 14 files changed, 1397 insertions(+), 109 deletions(-)

diff --git a/sql/item.cc b/sql/item.cc
index 0c5b6450b4a..5091ea87bee 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -299,7 +299,8 @@ bool DTCollation::aggregate(DTCollation &dt)
   return 0;
 }
 
-Item_field::Item_field(Field *f) :Item_ident(NullS,f->table_name,f->field_name)
+Item_field::Item_field(Field *f)
+  :Item_ident(NullS,f->table_name,f->field_name), item_equal(0)
 {
   set_field(f);
   collation.set(DERIVATION_IMPLICIT);
@@ -313,6 +314,7 @@ Item_field::Item_field(THD *thd, Item_field &item)
    result_field(item.result_field)
 {
   collation.set(DERIVATION_IMPLICIT);
+  item_equal= item.item_equal;
 }
 
 void Item_field::set_field(Field *field_par)
@@ -969,6 +971,49 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
   return 0;
 }
 
+Item_equal *Item_field::find_item_equal(COND_EQUAL *cond_equal)
+{
+  Item_equal *item= 0;
+  while (cond_equal)
+  {
+    List_iterator_fast<Item_equal> li(cond_equal->current_level);
+    while ((item= li++))
+    {
+      if (item->contains(field))
+        return item;
+    }
+    cond_equal= cond_equal->parent_level;
+  }
+  return item;
+}
+
+Item *Item_field::equal_fields_propagator(byte *arg)
+{
+  COND_EQUAL *cond_equal= (COND_EQUAL *) arg;
+  item_equal= find_item_equal(cond_equal);
+  Item *item= 0;
+  if (item_equal)
+    item= item_equal->get_const();
+  if (item)
+    item->fixed= 0;
+  else
+    item= this;
+  return item;
+}
+
+bool Item_field::replace_equal_field_processor(byte *arg)
+{
+  if (item_equal)
+  {
+    Item_field *subst= item_equal->get_first();
+    if (subst && !field->eq(subst->field))
+    {
+      field= subst->field;
+      return 0;
+    }
+  }
+  return 0;
+}
 
 void Item::init_make_field(Send_field *tmp_field,
 			   enum enum_field_types field_type)
diff --git a/sql/item.h b/sql/item.h
index 0a0db619e1a..c10f7f7f9f2 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -83,6 +83,7 @@ class DTCollation {
 };
 
 typedef bool (Item::*Item_processor)(byte *arg);
+typedef Item* (Item::*Item_calculator) (byte *arg);
 
 class Item {
   Item(const Item &);			/* Prevent use of these */
@@ -201,8 +202,15 @@ class Item {
     return (this->*processor)(arg);
   }
 
+   virtual Item* traverse(Item_calculator calculator, byte *arg)
+  {
+    return (this->*calculator)(arg);
+  }
+ 
   virtual bool remove_dependence_processor(byte * arg) { return 0; }
   virtual bool remove_fixed(byte * arg) { fixed= 0; return 0; }
+  virtual Item *equal_fields_propagator(byte * arg) { return this; }
+  virtual bool replace_equal_field_processor(byte * arg) { return 0; }
   
   virtual Item *this_item() { return this; } /* For SPs mostly. */
   virtual Item *this_const_item() const { return const_cast<Item*>(this); } /* For SPs mostly. */
@@ -311,17 +319,21 @@ class Item_ident :public Item
   bool remove_dependence_processor(byte * arg);
 };
 
+class Item_equal;
+class COND_EQUAL;
 
 class Item_field :public Item_ident
 {
   void set_field(Field *field);
 public:
   Field *field,*result_field;
+  Item_equal *item_equal;
   // Item_field() {}
 
   Item_field(const char *db_par,const char *table_name_par,
 	     const char *field_name_par)
-    :Item_ident(db_par,table_name_par,field_name_par),field(0),result_field(0)
+    :Item_ident(db_par,table_name_par,field_name_par),field(0),result_field(0),
+     item_equal(0)
   { collation.set(DERIVATION_IMPLICIT); }
   // Constructor need to process subselect with temporary tables (see Item)
   Item_field(THD *thd, Item_field &item);
@@ -355,6 +367,9 @@ class Item_field :public Item_ident
   bool get_time(TIME *ltime);
   bool is_null() { return field->is_null(); }
   Item *get_tmp_table_item(THD *thd);
+  Item_equal *find_item_equal(COND_EQUAL *cond_equal);
+  Item *equal_fields_propagator(byte *arg);
+  bool replace_equal_field_processor(byte *arg);
   friend class Item_default_value;
   friend class Item_insert_value;
 };
@@ -897,6 +912,15 @@ class Item_default_value : public Item_field
     return arg->walk(processor, args) ||
       (this->*processor)(args);
   }
+
+  Item *traverse(Item_calculator calculator, byte *args)
+  {
+    Item *new_item= arg->traverse(calculator, args);
+    if (!new_item)
+      return 0;
+    arg= new_item;
+    return (this->*calculator)(args);
+  }
 };
 
 class Item_insert_value : public Item_field
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 1629ed6aa80..76cec7b6615 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -225,7 +225,7 @@ void Item_bool_func2::fix_length_and_dec()
   }
   
   // Make a special case of compare with fields to get nicer DATE comparisons
-  if (args[0]->type() == FIELD_ITEM)
+  if (args[0]->type() == FIELD_ITEM && !args[0]->const_item())
   {
     Field *field=((Item_field*) args[0])->field;
     if (field->store_for_compare())
@@ -238,7 +238,7 @@ void Item_bool_func2::fix_length_and_dec()
       }
     }
   }
-  if (args[1]->type() == FIELD_ITEM)
+  if (args[1]->type() == FIELD_ITEM && !args[1]->const_item())
   {
     Field *field=((Item_field*) args[1])->field;
     if (field->store_for_compare())
@@ -1712,6 +1712,21 @@ bool Item_cond::walk(Item_processor processor, byte *arg)
   return Item_func::walk(processor, arg);
 }
 
+Item *Item_cond::traverse(Item_calculator calculator, byte *arg)
+{
+  List_iterator<Item> li(list);
+  Item *item;
+  while ((item= li++))
+  {
+    Item *new_item= item->traverse(calculator, arg);
+    if (!new_item)
+      return 0;
+    if (new_item != item)
+      li.replace(new_item);
+  }
+  return Item_func::traverse(calculator, arg);
+}
+
 void Item_cond::split_sum_func(Item **ref_pointer_array, List<Item> &fields)
 {
   List_iterator<Item> li(list);
@@ -2499,3 +2514,217 @@ Item *Item_cond_or::neg_transformer()		/* NOT(a OR b OR ...)  -> */
   neg_arguments();
   return new Item_cond_and(list);
 }
+
+Item_equal::Item_equal(Item_field *f1, Item_field *f2)
+  : Item_bool_func(), const_item(0), eval_item(0), cond_false(0)
+{
+  const_item_cache= 0;
+  fields.push_back(f1);
+  fields.push_back(f2);
+}
+
+Item_equal::Item_equal(Item *c, Item_field *f)
+  : Item_bool_func(), eval_item(0), cond_false(0)
+{
+  const_item_cache= 0;
+  fields.push_back(f);
+  const_item= c;
+}
+
+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;
+  while ((item= li++))
+  {
+    fields.push_back(item);
+  }
+  const_item= item_equal->const_item;
+  cond_false= item_equal->cond_false;
+}
+
+void Item_equal::add(Item *c)
+{
+  if (cond_false)
+    return;
+  if (!const_item)
+  {
+    const_item= c;
+    return;
+  }
+  Item_func_eq *func= new Item_func_eq(c, const_item);
+  func->set_cmp_func();
+  cond_false =  !(func->val_int());
+}
+
+void Item_equal::add(Item_field *f)
+{
+  fields.push_back(f);
+}
+
+bool Item_equal::contains(Field *field)
+{
+  List_iterator_fast<Item_field> it(fields);
+  Item_field *item;
+  while ((item= it++))
+  {
+    if (field->eq(item->field))
+        return 1;
+  }
+  return 0;
+}
+
+void Item_equal::merge(Item_equal *item)
+{
+  fields.concat(&item->fields);
+  Item *c= item->const_item;
+  if (c)
+  {
+    /* 
+      The flag cond_false will be set to 1 after this, if 
+      the multiple equality already contains a constant and its 
+      value is  not equal to the value of c.
+    */
+    add(const_item);
+  }
+  cond_false|= item->cond_false;
+} 
+
+void Item_equal::sort(void *table_join_idx)
+{
+  bool swap;
+  void **idx= (void **) table_join_idx;
+  List_iterator<Item_field> it(fields);
+  do
+  {
+    Item_field *item1= it++;
+    Item_field **ref1= it.ref();
+    Item_field *item2;
+    Item_field **ref2;
+
+    if (!item1)
+      break; 
+    swap= FALSE;
+    while ((item2= it++))
+    {
+      ref2= it.ref();
+      if (idx[item1->field->table->tablenr] >
+          idx[item2->field->table->tablenr])
+      {
+        Item_field *item= *ref1;
+        *ref1= *ref2;
+        *ref2= item;
+        swap= TRUE;
+      }
+      else
+      {
+        item1= item2;
+        ref1= ref2;
+      }
+    }
+    it.rewind();
+  } while (swap);
+}
+
+bool Item_equal::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
+{
+  List_iterator_fast<Item_field> li(fields);
+  Item *item;
+  not_null_tables_cache= used_tables_cache= 0;
+  const_item_cache= 0;
+  while ((item=li++))
+  {
+    table_map tmp_table_map;
+    used_tables_cache|= item->used_tables();
+    tmp_table_map= item->not_null_tables();
+    not_null_tables_cache|= tmp_table_map;
+    if (item->maybe_null)
+      maybe_null=1;
+  }
+  fix_length_and_dec();
+  fixed= 1;
+  return 0;
+}
+
+void Item_equal::update_used_tables()
+{
+  List_iterator_fast<Item_field> li(fields);
+  Item *item;
+  not_null_tables_cache= used_tables_cache= 0;
+  while ((item=li++))
+  {
+    item->update_used_tables();
+    used_tables_cache|= item->used_tables();
+    const_item_cache&= item->const_item();
+  }
+}
+
+longlong Item_equal::val_int()
+{
+  if (cond_false)
+    return 0;
+  List_iterator_fast<Item_field> it(fields);
+  Item *item= const_item ? const_item : it++;
+  if ((null_value= item->null_value))
+    return 0;
+  eval_item->store_value(item);
+  while((item= it++))
+  {
+    if ((null_value= item->null_value) || eval_item->cmp(item))
+      return 0;
+  }
+  return 1;
+}
+
+void Item_equal::fix_length_and_dec()
+{
+  Item *item= const_item ? const_item : get_first();
+  eval_item= cmp_item::get_comparator(item);
+  if (item->result_type() == STRING_RESULT)
+    eval_item->cmp_charset= cmp_collation.collation;
+}
+
+bool Item_equal::walk(Item_processor processor, byte *arg)
+{
+  List_iterator_fast<Item_field> it(fields);
+  Item *item;
+  while ((item= it++))
+    if (item->walk(processor, arg))
+      return 1;
+  return Item_func::walk(processor, arg);
+}
+
+Item *Item_equal::traverse(Item_calculator calculator, byte *arg)
+{
+  List_iterator<Item_field> it(fields);
+  Item *item;
+  while ((item= it++))
+  {
+    Item *new_item= item->traverse(calculator, arg);
+    if (!new_item)
+      return 0;
+    if (new_item != item)
+      it.replace((Item_field *) new_item);
+  }
+  return Item_func::traverse(calculator, arg);
+}
+
+void Item_equal::print(String *str)
+{
+  str->append(func_name());
+  str->append('(');
+  List_iterator_fast<Item_field> it(fields);
+  Item *item;
+  if ((item= it++))
+    item->print(str);
+  while ((item= it++))
+  {
+    str->append(',');
+    str->append(' ');
+    item->print(str);
+  }
+  str->append(')');
+}
+
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index dac7a2d43eb..cf14fbc17ea 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -908,13 +908,76 @@ class Item_cond :public Item_bool_func
   void top_level_item() { abort_on_null=1; }
   void copy_andor_arguments(THD *thd, Item_cond *item);
   bool walk(Item_processor processor, byte *arg);
+  Item *traverse(Item_calculator calculator, byte *arg);
   void neg_arguments();
 };
 
 
+class Item_equal: public Item_bool_func
+{
+  List<Item_field> fields; /* list of equal field items                    */
+  Item *const_item;        /* optional constant item equal to fields items */
+  cmp_item *eval_item;
+  bool cond_false;
+  DTCollation cmp_collation;
+public:
+  inline Item_equal()
+    : Item_bool_func(), const_item(0), eval_item(0), cond_false(0)
+  { const_item_cache=0 ;}
+  Item_equal(Item_field *f1, Item_field *f2);
+  Item_equal(Item *c, Item_field *f);
+  Item_equal(Item_equal *item_equal);
+  inline Item* get_const() { return const_item; }
+  void add(Item *c);
+  void add(Item_field *f);
+  bool is_false() { return cond_false; }
+  bool contains(Field *field);
+  Item_field* get_first() { return fields.head(); }
+  void merge(Item_equal *item);
+  enum Functype functype() const { return MULT_EQUAL_FUNC; }
+  longlong val_int(); 
+  const char *func_name() const { return "multiple equal"; }
+  optimize_type select_optimize() const { return OPTIMIZE_EQUAL; }
+  void sort(void *table_join_idx);
+  friend class Item_equal_iterator;
+  void fix_length_and_dec();
+  bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref);
+  void update_used_tables();
+  bool walk(Item_processor processor, byte *arg);
+  Item *traverse(Item_calculator calculator, byte *arg);
+  void print(String *str);
+}; 
+
+class COND_EQUAL
+{
+public:
+  COND_EQUAL *parent_level;
+  List<Item_equal> current_level;
+  COND_EQUAL() { parent_level= 0; }
+};
+
+
+class Item_equal_iterator :List_iterator_fast<Item_field>
+{
+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();
+  }
+};
+
 class Item_cond_and :public Item_cond
 {
 public:
+  COND_EQUAL cond_equal;  
   Item_cond_and() :Item_cond() {}
   Item_cond_and(Item *i1,Item *i2) :Item_cond(i1,i2) {}
   Item_cond_and(THD *thd, Item_cond_and &item) :Item_cond(thd, item) {}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 7e03f8c9c45..4337ee631f4 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -242,6 +242,40 @@ bool Item_func::walk (Item_processor processor, byte *argument)
   return (this->*processor)(argument);
 }
 
+Item *Item_func::traverse(Item_calculator calculator, byte *argument)
+{
+  if (arg_count)
+  {
+    Item **arg,**arg_end;
+    for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++)
+    {
+      Item *new_item= (*arg)->traverse(calculator, argument);
+      if (!new_item)
+	return 0;
+      *arg= new_item;
+    }
+  }
+  return (this->*calculator)(argument);
+}
+
+Item *Item_func::equal_fields_propagator(byte *argument)
+{
+  if (arg_count)
+  {
+    Item **arg,**arg_end;
+    for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++)
+    {
+      if (!(*arg)->fixed)
+      {
+        fix_fields(current_thd, 0, 0);
+        break;
+      }
+    }
+  }
+  return this;
+}
+
+
 void Item_func::split_sum_func(Item **ref_pointer_array, List<Item> &fields)
 {
   Item **arg, **arg_end;
diff --git a/sql/item_func.h b/sql/item_func.h
index d3cfaf63b5a..f92eea1bc94 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -40,7 +40,8 @@ class Item_func :public Item_result_field
   enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC,
 		  GE_FUNC,GT_FUNC,FT_FUNC,
 		  LIKE_FUNC,NOTLIKE_FUNC,ISNULL_FUNC,ISNOTNULL_FUNC,
-		  COND_AND_FUNC, COND_OR_FUNC, COND_XOR_FUNC, BETWEEN, IN_FUNC,
+		  COND_AND_FUNC, COND_OR_FUNC, COND_XOR_FUNC,
+                  BETWEEN, IN_FUNC, MULT_EQUAL_FUNC,
 		  INTERVAL_FUNC, ISNOTNULLTEST_FUNC,
 		  SP_EQUALS_FUNC, SP_DISJOINT_FUNC,SP_INTERSECTS_FUNC,
 		  SP_TOUCHES_FUNC,SP_CROSSES_FUNC,SP_WITHIN_FUNC,
@@ -49,7 +50,8 @@ class Item_func :public Item_result_field
 		  SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN,
 		  NOT_FUNC, NOT_ALL_FUNC,
                   GUSERVAR_FUNC};
-  enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL };
+  enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL,
+                       OPTIMIZE_EQUAL };
   enum Type type() const { return FUNC_ITEM; }
   virtual enum Functype functype() const   { return UNKNOWN_FUNC; }
   Item_func(void):
@@ -146,6 +148,8 @@ class Item_func :public Item_result_field
   bool agg_arg_collations_for_comparison(DTCollation &c, Item **items, uint nitems);
 
   bool walk(Item_processor processor, byte *arg);
+  Item *traverse(Item_calculator calculator, byte *arg);
+  Item *equal_fields_propagator(byte *arg);
 };
 
 
diff --git a/sql/item_row.cc b/sql/item_row.cc
index 89b38c8a753..d7afe9ad5f0 100644
--- a/sql/item_row.cc
+++ b/sql/item_row.cc
@@ -140,6 +140,18 @@ bool Item_row::walk(Item_processor processor, byte *arg)
   return (this->*processor)(arg);
 }
 
+Item *Item_row::traverse(Item_calculator calculator, byte *arg)
+{
+  for (uint i= 0; i < arg_count; i++)
+  {
+    Item *new_item= items[i]->traverse(calculator, arg);
+    if (!new_item)
+      return 0;
+    items[i]= new_item;
+  }
+  return (this->*calculator)(arg);
+}
+
 void Item_row::bring_value()
 {
   for (uint i= 0; i < arg_count; i++)
diff --git a/sql/item_row.h b/sql/item_row.h
index a09bd1a2c31..de6c18bf0d9 100644
--- a/sql/item_row.h
+++ b/sql/item_row.h
@@ -71,6 +71,7 @@ class Item_row: public Item
   void print(String *str);
 
   bool walk(Item_processor processor, byte *arg);
+  Item *traverse(Item_calculator calculator, byte *arg);
 
   uint cols() { return arg_count; }
   Item* el(uint i) { return items[i]; }
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index a7949511f02..f628160d868 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -427,6 +427,14 @@ class Item_func_make_set :public Item_str_func
     return item->walk(processor, arg) ||
       Item_str_func::walk(processor, arg);
   }
+  Item *traverse(Item_calculator calculator, byte *arg)
+  {
+    Item *new_item= item->traverse(calculator, arg);
+    if (!new_item)
+      return 0;
+    item= new_item;
+    return Item_str_func::traverse(calculator, arg);
+  }
   void print(String *str);
 };
 
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index e2761832e65..4626bffc3e2 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -762,11 +762,72 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
   DBUG_RETURN(records ? test(quick) : -1);
 }
 
+static SEL_TREE *get_func_mm_tree(PARAM *param, Item_func *cond_func, 
+                                  Field *field, Item *value,
+                                  Item_result cmp_type)
+{
+  SEL_TREE *tree= 0;
+  DBUG_ENTER("get_func_mm_tree");
+
+  if (cond_func->functype() == Item_func::NE_FUNC)
+  {
+  
+    tree= get_mm_parts(param, field, Item_func::LT_FUNC,
+		       value, cmp_type);
+    if (tree)
+    {
+      tree= tree_or(param, tree, get_mm_parts(param, field,
+					       Item_func::GT_FUNC,
+					       value, cmp_type));
+    }
+  }
+  else if (cond_func->functype() == Item_func::BETWEEN)
+  {
+  
+    tree= get_mm_parts(param, field, Item_func::GE_FUNC,
+		       cond_func->arguments()[1],cmp_type);
+    if (tree)
+    {
+      tree= tree_and(param, tree, get_mm_parts(param, field,
+					       Item_func::LE_FUNC,
+					       cond_func->arguments()[2],
+                                               cmp_type));
+    }
+  }
+  else if (cond_func->functype() == Item_func::IN_FUNC)
+  {
+    Item_func_in *func=(Item_func_in*) cond_func;
+    tree= get_mm_parts(param, field, Item_func::EQ_FUNC,
+                       func->arguments()[1], cmp_type);
+    if (tree)
+    {
+      for (uint i =2 ; i < func->argument_count() ; i++)
+      {
+        tree=  tree_or(param, tree, get_mm_parts(param, field, 
+                                                 Item_func::EQ_FUNC,
+                                                 func->arguments()[i],
+                                                 cmp_type));
+      }
+    }
+  }
+  else 
+  {
+    Item_func::Functype func_type=
+      (value != cond_func->arguments()[0]) ? cond_func->functype() :
+        ((Item_bool_func2*) cond_func)->rev_functype();
+    tree= get_mm_parts(param, field, func_type, value, cmp_type);
+  }
+  DBUG_RETURN(tree);
+}
+
 	/* make a select tree of all keys in condition */
 
 static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
 {
   SEL_TREE *tree=0;
+  SEL_TREE *ftree= 0;
+  Item_field *field_item= 0;
+  Item *value;
   DBUG_ENTER("get_mm_tree");
 
   if (cond->type() == Item::COND_ITEM)
@@ -814,9 +875,12 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
     DBUG_RETURN(new SEL_TREE(SEL_TREE::IMPOSSIBLE));
   }
 
-  table_map ref_tables=cond->used_tables();
+  table_map ref_tables= 0;
+  table_map param_comp= ~(param->prev_tables | param->read_tables |
+		          param->current_table);
   if (cond->type() != Item::FUNC_ITEM)
   {						// Should be a field
+    ref_tables= cond->used_tables();
     if ((ref_tables & param->current_table) ||
 	(ref_tables & ~(param->prev_tables | param->read_tables)))
       DBUG_RETURN(0);
@@ -828,76 +892,98 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
     DBUG_RETURN(0);				// Can't be calculated
 
   if (cond_func->functype() == Item_func::BETWEEN)
-  {
+  { 
     if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM)
     {
-      Field *field=((Item_field*) (cond_func->arguments()[0]))->field;
-      Item_result cmp_type=field->cmp_type();
-      DBUG_RETURN(tree_and(param,
-			   get_mm_parts(param, field,
-					Item_func::GE_FUNC,
-					cond_func->arguments()[1], cmp_type),
-			   get_mm_parts(param, field,
-					Item_func::LE_FUNC,
-					cond_func->arguments()[2], cmp_type)));
+      field_item= (Item_field*) (cond_func->arguments()[0]);
+      value= NULL;
     }
-    DBUG_RETURN(0);
+    else
+      DBUG_RETURN(0);
   }
-  if (cond_func->functype() == Item_func::IN_FUNC)
-  {						// COND OR
+  else if (cond_func->functype() == Item_func::IN_FUNC)
+  {
     Item_func_in *func=(Item_func_in*) cond_func;
     if (func->key_item()->type() == Item::FIELD_ITEM)
     {
-      Field *field=((Item_field*) (func->key_item()))->field;
-      Item_result cmp_type=field->cmp_type();
-      tree= get_mm_parts(param,field,Item_func::EQ_FUNC,
-			 func->arguments()[1],cmp_type);
-      if (!tree)
-	DBUG_RETURN(tree);			// Not key field
-      for (uint i=2 ; i < func->argument_count(); i++)
+      field_item= (Item_field*) (func->key_item());
+      value= NULL;
+    }
+    else
+      DBUG_RETURN(0);
+  }
+  else if (cond_func->functype() == Item_func::MULT_EQUAL_FUNC)
+  {
+    Item_equal *item_equal= (Item_equal *) cond;    
+    Item_equal_iterator it(*item_equal);
+    if (!(value= item_equal->get_const()))
+      value= it++;
+    while (value)
+    {
+      ref_tables= value->used_tables();
+      Item_equal_iterator li(*item_equal);
+      while ((field_item= li++))
       {
-	SEL_TREE *new_tree=get_mm_parts(param,field,Item_func::EQ_FUNC,
-					func->arguments()[i],cmp_type);
-	tree=tree_or(param,tree,new_tree);
+        if (field_item != value)
+        {
+          Field *field= field_item->field;
+          Item_result cmp_type= field->cmp_type();
+          if (!((ref_tables | field->table->map) & param_comp))
+          {
+            tree= get_mm_parts(param, field, Item_func::EQ_FUNC,
+		               value,cmp_type);
+            ftree= !ftree ? tree : tree_and(param, ftree, tree);
+          }
+        }
       }
-      DBUG_RETURN(tree);
-    }
-    DBUG_RETURN(0);				// Can't optimize this IN
-  }
-
-  if (ref_tables & ~(param->prev_tables | param->read_tables |
-		     param->current_table))
-    DBUG_RETURN(0);				// Can't be calculated yet
-  if (!(ref_tables & param->current_table))
-    DBUG_RETURN(new SEL_TREE(SEL_TREE::MAYBE)); // This may be false or true
-
-  /* check field op const */
-  /* btw, ft_func's arguments()[0] isn't FIELD_ITEM.  SerG*/
-  if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM)
-  {
-    tree= get_mm_parts(param,
-		       ((Item_field*) (cond_func->arguments()[0]))->field,
-		       cond_func->functype(),
-		       cond_func->arg_count > 1 ? cond_func->arguments()[1] :
-		       0,
-		       ((Item_field*) (cond_func->arguments()[0]))->field->
-		       cmp_type());
-  }
-  /* check const op field */
-  if (!tree &&
-      cond_func->have_rev_func() &&
-      cond_func->arguments()[1]->type() == Item::FIELD_ITEM)
-  {
-    DBUG_RETURN(get_mm_parts(param,
-			     ((Item_field*)
-			      (cond_func->arguments()[1]))->field,
-			     ((Item_bool_func2*) cond_func)->rev_functype(),
-			     cond_func->arguments()[0],
-			     ((Item_field*)
-			      (cond_func->arguments()[1]))->field->cmp_type()
-			     ));
+      if (item_equal->get_const())
+        break;
+      value= it++;
+    }
+    DBUG_RETURN(ftree);
   }
-  DBUG_RETURN(tree);
+  else if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM)
+  {
+    field_item= (Item_field*) (cond_func->arguments()[0]);
+    value= cond_func->arg_count > 1 ? cond_func->arguments()[1] : 0;
+  }
+  else if (cond_func->have_rev_func() &&
+             cond_func->arguments()[1]->type() == Item::FIELD_ITEM)
+  {
+    field_item= (Item_field*) (cond_func->arguments()[1]);
+    value= cond_func->arguments()[0];
+  }
+  else
+    DBUG_RETURN(0);
+
+  for (uint i= 0; i < cond_func->arg_count; i++)
+  {
+    Item *arg= cond_func->arguments()[i];
+    if (arg != field_item)
+      ref_tables|= arg->used_tables();
+  }
+  Field *field= field_item->field;
+  Item_result cmp_type= field->cmp_type();
+  if (!((ref_tables | field->table->map) & param_comp))
+    ftree= get_func_mm_tree(param, cond_func, field, value, cmp_type);
+  Item_equal *item_equal= field_item->item_equal;
+  if (item_equal)
+  {
+    Item_equal_iterator it(*item_equal);
+    Item_field *item;
+    while ((item= it++))
+    {
+      Field *f= item->field;
+      if (field->eq(f))
+        continue;
+      if (!((ref_tables | f->table->map) & param_comp))
+      {
+        tree= get_func_mm_tree(param, cond_func, f, value, cmp_type);
+        ftree= !ftree ? tree : tree_and(param, ftree, tree);
+      }
+    }
+  }
+  DBUG_RETURN(ftree);
 }
 
 
@@ -905,17 +991,10 @@ static SEL_TREE *
 get_mm_parts(PARAM *param, Field *field, Item_func::Functype type, 
 	     Item *value, Item_result cmp_type)
 {
-  bool ne_func= FALSE;
   DBUG_ENTER("get_mm_parts");
   if (field->table != param->table)
     DBUG_RETURN(0);
 
-  if (type == Item_func::NE_FUNC)
-  {
-    ne_func= TRUE;
-    type= Item_func::LT_FUNC;
-  }
-
   KEY_PART *key_part = param->key_parts;
   KEY_PART *end = param->key_parts_end;
   SEL_TREE *tree=0;
@@ -951,13 +1030,6 @@ get_mm_parts(PARAM *param, Field *field, Item_func::Functype type,
     }
   }
 
-  if (ne_func)
-  {
-    SEL_TREE *tree2= get_mm_parts(param, field, Item_func::GT_FUNC,
-                                  value, cmp_type);
-    if (tree2)
-      tree= tree_or(param,tree,tree2);
-  }
   DBUG_RETURN(tree);
 }
 
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
index 932aceebdbb..ce650701347 100644
--- a/sql/opt_sum.cc
+++ b/sql/opt_sum.cc
@@ -352,6 +352,18 @@ static bool simple_pred(Item_func *func_item, Item **args, bool *inv_order)
   Item *item;
   *inv_order= 0;
   switch (func_item->argument_count()) {
+  case 0:
+    /* MULT_EQUAL_FUNC */
+    {
+      Item_equal *item_equal= (Item_equal *) func_item;
+      Item_equal_iterator it(*item_equal);
+      args[0]= it++;
+      if (it++)
+        return 0;
+      if (!(args[1]= item_equal->get_const()))
+        return 0;
+    }
+    break;
   case 1:
     /* field IS NULL */
     item= func_item->arguments()[0];
@@ -492,6 +504,9 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
   case Item_func::BETWEEN:
     between= 1;
     break;
+  case Item_func::MULT_EQUAL_FUNC:
+    eq_type= 1;
+    break;
   default:
     return 0;                                        // Can't optimize function
   }
diff --git a/sql/sql_list.h b/sql/sql_list.h
index 7200046e6c5..d7a62b7fd7d 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -121,10 +121,12 @@ class base_list :public Sql_alloc
   void remove(list_node **prev)
   {
     list_node *node=(*prev)->next;
-    delete *prev;
-    *prev=node;
     if (!--elements)
       last= &first;
+    else if (last == &(*prev)->next)
+      last= prev;
+    delete *prev;
+    *prev=node;
   }
   inline void *pop(void)
   {
@@ -135,6 +137,30 @@ class base_list :public Sql_alloc
       last= &first;
     return tmp->info;
   }
+  inline void concat(base_list *list)
+  {
+    if (!list->is_empty())
+    {
+      *last= list->first;
+      last= list->last;
+      elements+= list->elements;
+    }
+  }
+  inline void disjoin(base_list *list)
+  {
+    list_node **prev= &first;
+    list_node *node= first;
+    list_node *list_first= list->first;
+    elements=0;
+    while (node && node != list_first)
+    {
+      prev= &node->next;
+      node= node->next;
+      elements++;
+    }
+    *prev= *last;
+    last= prev;
+  }
   inline list_node* last_node() { return *last; }
   inline list_node* first_node() { return first;}
   inline void *head() { return first->info; }
@@ -245,6 +271,8 @@ template <class T> class List :public base_list
   inline T* head() {return (T*) base_list::head(); }
   inline T** head_ref() {return (T**) base_list::head_ref(); }
   inline T* pop()  {return (T*) base_list::pop(); }
+  inline void concat(List<T> *list) { return base_list::concat(list); }
+  inline void disjoin(List<T> *list) { return base_list::disjoin(list); }
   void delete_elements(void)
   {
     list_node *element,*next;
@@ -265,6 +293,8 @@ template <class T> class List_iterator :public base_list_iterator
   inline T* operator++(int) { return (T*) base_list_iterator::next(); }
   inline T *replace(T *a)   { return (T*) base_list_iterator::replace(a); }
   inline T *replace(List<T> &a) { return (T*) base_list_iterator::replace(a); }
+  inline void rewind(void)  { base_list_iterator::rewind(); }
+  inline void remove()      { base_list_iterator::remove(); }
   inline void after(T *a)   { base_list_iterator::after(a); }
   inline T** ref(void)	    { return (T**) base_list_iterator::ref(); }
 };
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index eea5ed72a3a..7c0464bec05 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -72,6 +72,11 @@ static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables,
 			    uint select_options, const char *info,
 			    Item *having, Procedure *proc,
 			    SELECT_LEX_UNIT *unit);
+static Item *flatten_condition(COND *cond);
+static COND *build_all_equal_items(COND *cond,
+                                        COND_EQUAL *inherited);
+static COND* substitute_for_best_equal_field(COND *cond,
+                                             void *table_join_idx);
 static COND *optimize_cond(COND *conds,Item::cond_result *cond_value);
 static COND *remove_eq_conds(COND *cond,Item::cond_result *cond_value);
 static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item);
@@ -526,6 +531,56 @@ JOIN::optimize()
   }
 #endif
 
+  /* eliminate NOT operators */
+  conds= eliminate_not_funcs(conds);
+  DBUG_EXECUTE("where", print_where(conds, "after negation elimination"););
+
+  /* Eliminate nested AND/OR in conditions */
+  if (conds)
+    conds= flatten_condition(conds);
+  {
+    TABLE_LIST *tables;
+    for (tables= tables_list; tables; tables= tables->next)
+    { 
+      if (tables->on_expr)
+        tables->on_expr= flatten_condition(tables->on_expr);
+    }
+  }
+
+  /* 
+    Build all multiple equality predicates and eliminate equality
+    predicates that can be inferred from these multiple equalities.
+    For each reference of a field included into a multiple equality
+    that occurs in a function set a pointer to the multiple equality
+    predicate. Substitute a constant instead of this field if the
+    multiple equality contains a constant.
+  */
+  if (conds) 
+  {
+    conds= build_all_equal_items(conds, NULL);
+    conds->update_used_tables();
+    if (conds->type() == Item::COND_ITEM &&
+        ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC)
+      cond_equal= &((Item_cond_and*) conds)->cond_equal;
+    else if (conds->type() == Item::FUNC_ITEM &&
+             ((Item_cond*) conds)->functype() == Item_func::MULT_EQUAL_FUNC)
+    {
+      cond_equal= new COND_EQUAL;
+      cond_equal->current_level.push_back((Item_equal *) conds);
+    }
+  }
+  {
+    TABLE_LIST *tables;
+    for (tables= tables_list; tables; tables= tables->next)
+    { 
+      if (tables->on_expr)
+      {
+        tables->on_expr= build_all_equal_items(tables->on_expr, cond_equal);
+        tables->on_expr->update_used_tables();  
+      }
+    }
+  }
+
   conds= optimize_cond(conds,&cond_value);
   if (thd->net.report_error)
   {
@@ -626,6 +681,30 @@ JOIN::optimize()
     }
     mysql_unlock_some_tables(thd, this->table, const_tables);
   }
+  /*
+    Among the equal fields belonging to the same multiple equality
+    choose the one that is to be retrieved first and substitute
+    all references to these in where condition for a reference for
+    the selected field.
+  */
+  if (conds)
+  {
+    conds= substitute_for_best_equal_field(conds, map2table);
+    conds->update_used_tables();
+  }
+  {
+    TABLE_LIST *tables;
+    for (tables= tables_list; tables; tables= tables->next)
+    { 
+      if (tables->on_expr)
+      {
+        tables->on_expr= substitute_for_best_equal_field(tables->on_expr,
+                                                         map2table);
+        tables->on_expr->update_used_tables();
+        map2table[tables->table->tablenr]->on_expr= tables->on_expr;
+      }
+    }
+  }
   if (!conds && outer_join)
   {
     /* Handle the case where we have an OUTER JOIN without a WHERE */
@@ -2202,6 +2281,35 @@ add_key_field(KEY_FIELD **key_fields,uint and_level,
 }
 
 
+static void
+add_key_equal_fields(KEY_FIELD **key_fields, uint and_level,
+                     Item_field *field_item,
+                     bool eq_func, Item **val,
+                     uint num_values, table_map usable_tables)
+{
+  Field *field= field_item->field;
+  add_key_field(key_fields, and_level, field,
+                eq_func, val, num_values, usable_tables);
+  Item_equal *item_equal= field_item->item_equal;
+  if (item_equal)
+  { 
+    /*
+      Add to the set of possible key values every substitution of
+      the field for an equal field included into item_equal
+    */
+    Item_equal_iterator it(*item_equal);
+    Item_field *item;
+    while ((item= it++))
+    {
+      if (!field->eq(item->field))
+      {
+        add_key_field(key_fields, and_level, item->field,
+                      eq_func, val, num_values, usable_tables);
+      }
+    }
+  }
+}
+
 static void
 add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level,
 	       COND *cond, table_map usable_tables)
@@ -2247,11 +2355,11 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level,
     // BETWEEN or IN
     if (cond_func->key_item()->real_item()->type() == Item::FIELD_ITEM &&
 	!(cond_func->used_tables() & OUTER_REF_TABLE_BIT))
-      add_key_field(key_fields,*and_level,
-		    ((Item_field*) (cond_func->key_item()->real_item()))->
-		    field, 0,
-                    cond_func->arguments()+1, cond_func->argument_count()-1,
-                    usable_tables);
+      add_key_equal_fields(key_fields,*and_level,
+                           (Item_field*) (cond_func->key_item()->real_item()),
+                           0, cond_func->arguments()+1,
+                           cond_func->argument_count()-1,
+                           usable_tables);
     break;
   case Item_func::OPTIMIZE_OP:
   {
@@ -2261,21 +2369,19 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level,
     if (cond_func->arguments()[0]->real_item()->type() == Item::FIELD_ITEM &&
 	!(cond_func->arguments()[0]->used_tables() & OUTER_REF_TABLE_BIT))
     {
-      add_key_field(key_fields,*and_level,
-		    ((Item_field*) (cond_func->arguments()[0])->real_item())
-		    ->field,
-		    equal_func,
-                    cond_func->arguments()+1, 1, usable_tables);
+      add_key_equal_fields(key_fields, *and_level,
+	                (Item_field*) (cond_func->arguments()[0])->real_item(),
+		           equal_func,
+		           cond_func->arguments()+1, 1, usable_tables);
     }
     if (cond_func->arguments()[1]->real_item()->type() == Item::FIELD_ITEM &&
 	cond_func->functype() != Item_func::LIKE_FUNC &&
 	!(cond_func->arguments()[1]->used_tables() & OUTER_REF_TABLE_BIT))
     {
-      add_key_field(key_fields,*and_level,
-		    ((Item_field*) (cond_func->arguments()[1])->real_item())
-		    ->field,
-		    equal_func,
-		    cond_func->arguments(),1,usable_tables);
+      add_key_equal_fields(key_fields,*and_level,
+                       (Item_field*) (cond_func->arguments()[1])->real_item(),
+		           equal_func,
+		           cond_func->arguments(),1,usable_tables);
     }
     break;
   }
@@ -2287,15 +2393,55 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level,
       Item *tmp=new Item_null;
       if (!tmp)					// Should never be true
 	return;
-      add_key_field(key_fields,*and_level,
-		    ((Item_field*) (cond_func->arguments()[0])->real_item())
-		    ->field,
+      add_key_equal_fields(key_fields,*and_level,
+		    (Item_field*) (cond_func->arguments()[0])->real_item(),
 		    cond_func->functype() == Item_func::ISNULL_FUNC,
 		    &tmp, 1, usable_tables);
     }
     break;
+  case Item_func::OPTIMIZE_EQUAL:
+    Item_equal *item_equal= (Item_equal *) cond;
+    Item *const_item= item_equal->get_const();
+    Item_equal_iterator it(*item_equal);
+    Item_field *item;
+    if (const_item)
+    {
+      /*
+        For each field field1 from item_equal consider the equality 
+        field1=const_item as a condition allowing an index access of the table
+        with field1 by the keys value of field1.
+      */   
+      while ((item= it++))
+      {
+        add_key_field(key_fields, *and_level, item->field,
+                      TRUE, &const_item, 1, usable_tables);
+      }
+    }
+    else 
+    {
+      /*
+        Consider all pairs of different fields included into item_equal.
+        For each of them (field1, field1) consider the equality 
+        field1=field2 as a condition allowing an index access of the table
+        with field1 by the keys value of field2.
+      */   
+      Item_equal_iterator fi(*item_equal);
+      while ((item= fi++))
+      {
+        Field *field= item->field;
+        while ((item= it++))
+        {
+          if (!field->eq(item->field))
+          {
+            add_key_field(key_fields, *and_level, field,
+                          TRUE, (Item **) &item, 1, usable_tables);
+          }
+        }
+        it.rewind();
+      }
+    }
+    break;
   }
-  return;
 }
 
 /*
@@ -2669,21 +2815,33 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
 	  {
             uint keypart=keyuse->keypart;
 	    uint found_part_ref_or_null= KEY_OPTIMIZE_REF_OR_NULL;
+            bool usable= 0;
+            table_map best_part_found_ref= 0;
+            double best_prev_record_reads= DBL_MAX;
 	    do
 	    {
 	      if (!(rest_tables & keyuse->used_tables) &&
 		  !(found_ref_or_null & keyuse->optimize))
 	      {
 		found_part|=keyuse->keypart_map;
-		found_ref|= keyuse->used_tables;
+                double tmp= prev_record_reads(join, 
+                              (table_map) (found_ref | keyuse->used_tables));
+                if (tmp < best_prev_record_reads)
+                {
+                  best_part_found_ref= keyuse->used_tables;
+                  best_prev_record_reads= tmp;
+                }
 		if (rec > keyuse->ref_table_rows)
 		  rec= keyuse->ref_table_rows;
 		found_part_ref_or_null&= keyuse->optimize;
+                usable= 1;
               }
 	      keyuse++;
-	      found_ref_or_null|= found_part_ref_or_null;
+              found_ref|= best_part_found_ref;
 	    } while (keyuse->table == table && keyuse->key == key &&
 		     keyuse->keypart == keypart);
+            if (usable)
+              found_ref_or_null|= found_part_ref_or_null;
 	  } while (keyuse->table == table && keyuse->key == key);
 
 	  /*
@@ -3394,7 +3552,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
       }
 
       COND *tmp=make_cond_for_table(cond,used_tables,current_map);
-      if (!tmp && tab->quick)
+      if (!tmp && tab->quick && tab->type == JT_ALL)
       {						// Outer join
 	/*
 	  Hack to handle the case where we only refer to a table
@@ -4089,6 +4247,583 @@ template class List<Item_func_match>;
 template class List_iterator<Item_func_match>;
 #endif
 
+/* 
+  Eliminate nesting in AND/OR subexpressions od a condition
+ 
+  SYNOPSIS
+    flatten_condition()
+    cond          condition where to eliminate nesting
+
+  DESCRIPTION
+    The function traverse the condition and recursively eliminates
+    nesting for AND/OR subexpressions:
+    ... AND (p AND ... r) AND ... => ... AND p AND ... r AND ...
+    ... OR (p OR ... r) OR ... => ... OR p OR ... r OR ...
+ 
+  NOTES
+    Nesting in AND/OR subexpresions inside of NOT/XOR formulas is not
+    eliminated. 
+
+  RETURN
+    The transformed condition
+*/
+static Item *flatten_condition(COND *cond)
+{
+  if (cond->type() == Item::COND_ITEM)
+  {
+    Item_func::Functype functype= ((Item_cond*) cond)->functype();
+    if (functype == Item_func::COND_AND_FUNC ||
+        functype == Item_func::COND_OR_FUNC)
+    {
+
+      List<Item> *args= ((Item_cond*) cond)->argument_list();    
+      List_iterator<Item> li(*args);
+      Item *item;
+      List<Item> list;
+      while ((item= li++))
+      { 
+        item= flatten_condition(item);
+        if (item->type() == Item::COND_ITEM &&
+            ((Item_func*) item)->functype() == functype)
+        { 
+          list.concat(((Item_cond*) item)->argument_list());
+          li.remove();
+        }
+      }
+      args->concat(&list);
+    }
+  }
+  return cond;
+}
+
+
+/* 
+  Find the multiple equality predicate containing a field
+ 
+  SYNOPSIS
+    find_item_equal()
+    cond_equal          multiple equalities to search in
+    field               field to look for
+    inherited_fl  :out  set up to TRUE iff multiple equality is found
+                        on upper levels (not on current level of cond_equal) 
+
+  DESCRIPTION
+    The function retrieves the multiple equalities accessed through
+    the con_equal structure from current level and up looking for
+    an equality containing field. It stops retrieval as soon as the equality
+    is found and set up inherited_fl to TRUE if it's found on upper levels.
+
+  RETURN
+    Item_equal for the found multiple equality predicate if a success;
+    NULL - otherwise.
+*/
+
+Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field,
+                            bool *inherited_fl)
+{
+  Item_equal *item= 0;
+  bool in_upper_level= FALSE;
+  while (cond_equal)
+  {
+    List_iterator_fast<Item_equal> li(cond_equal->current_level);
+    while ((item= li++))
+    {
+      if (item->contains(field))
+        goto finish;
+    }
+    in_upper_level= TRUE;
+    cond_equal= cond_equal->parent_level;
+  }
+  in_upper_level= FALSE;
+finish:
+  if (inherited_fl)
+    *inherited_fl= in_upper_level;
+  return item;
+}
+  
+/* 
+  Check whether an item is a simple equality predicate and if so
+  create/find a multiple equality for this predicate
+
+  SYNOPSIS
+    check_equality()
+    item       item to check
+    cond_equal multiple equalities that must hold together with the predicate
+
+  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.
+    If this is the case the function looks a for a multiple equality
+    in the lists referenced directly or indirectly by cond_equal. If it
+    doesn't find any it builds a multiple equality that covers 
+    the predicate, i.e. the predicate can be inferred from it. The built
+    multiple equality could be obtained in such a way: create a binary
+    multiple equality equivalent to the predicate, than merge it, if
+    possible, with one of old multiple equalities. This guarantees that
+    the set of multiple equalities covering equality predicates will
+    be minimal.
+
+  NOTES
+    Now only fields that have the same type defintions (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 constant propagation procedure.
+    We could weeken the equlity test as soon as at least one of the 
+    equal fields is to be equal to a constant. It would require a 
+    more complicated implementation: we would have to store, in
+    general case, its own constant for each fields from the multiple
+    equality. But at the same time it would allow us to get rid
+    of constant propagation completely: it would be done by the call
+    to build_all_equal_items.
+    
+  IMPLEMENTATION
+    The implementation does not follow exactly the above rules to
+    build a new multiple equality for the equality predicate.
+    If it processes the equality of the form field1=field2, it
+    looks for multiple equalities me1 containig field1 and me2 containing
+    field2. If only one of them is found the fuction expand it by
+    the lacking field. If multiple equalities for both fields are
+    found they are merged. If both searches fail a new multiple equality
+    containing just field1 and field2 is added to the existing
+    multiple equalities.
+    If the function processes the predicate of the form field1=const,
+    it looks for a multiple equality containing field1. If found, the 
+    function checks the constant of the multiple equality. If the value
+    is unknown, it is setup to const. Otherwise the value is compared with
+    const and the evaluation of the equality predicate is performed.
+    When expanding/merging equality predicates from the upper levels
+    the function first copies them for the current level. It looks
+    acceptable, as this happens rarely. The implementation without
+    copying would be much more complicated.
+
+  RETURN
+    TRUE  - if the predicate is a simple equality predicate
+    FALSE - otherwise
+*/
+
+static bool check_equality(Item *item, COND_EQUAL *cond_equal)
+{
+  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::FIELD_ITEM &&
+        right_item->type() == Item::FIELD_ITEM)
+    {
+      /* 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;
+
+      if (left_field->eq(right_field))
+        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 (left_item_equal && left_item_equal == right_item_equal)
+      {
+        /* 
+           The equality predicate is inference of one of the existing
+           multiple equalities
+        */
+          return TRUE;
+      }
+      
+      /* Copy the found multiple equalities at the current level if needed */
+      if (left_copyfl)
+      {
+        left_item_equal= new Item_equal(left_item_equal);
+        cond_equal->current_level.push_back(left_item_equal);
+      }
+      if (right_copyfl)
+      {
+        right_item_equal= new Item_equal(right_item_equal);
+        cond_equal->current_level.push_back(right_item_equal);
+      }
+
+      if (left_item_equal)
+      { 
+        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
+      { 
+         if (right_item_equal)
+           right_item_equal->add((Item_field *) left_item);
+         else 
+         {
+           /* Multiple equalities for neither of the fields has been found */
+           Item_equal *item= new Item_equal((Item_field *) left_item,
+                                            (Item_field *) right_item);
+           cond_equal->current_level.push_back(item);
+         }
+      }
+      return TRUE;
+    }
+    else
+    {
+      /* 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 && 
+          right_item->const_item())
+      {
+        field_item= (Item_field*) left_item;
+        const_item= right_item;
+      }
+      else if (right_item->type() == Item::FIELD_ITEM && 
+               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;
+        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;
+      }
+    }
+  }
+  return FALSE;
+}
+
+/* 
+  Replace all equality predicates in a condition by multiple equality items
+
+  SYNOPSIS
+    build_all_equal_items()
+    cond       condition(expression) where to make replacement
+    inherited  path to all inherited multiple equality items
+
+  DESCRIPTION
+    At each 'and' level the function detects items for equality predicates
+    and replaced them by a set of multiple equality items of class Item_equal,
+    taking into account inherited equalities from upper levels. 
+    If an equality predicate is used not in a conjunction it's just
+    replaced by a multiple equality predicate.
+    The functuion also traverse the cond tree and and for each field reference
+    sets a ponter to the multiple equality item containing the field, if there
+    is any. If this multiple equality equates fields to a constant the
+    function replace the field reference by the constant. 
+
+  NOTES
+    Multiple equality predicate =(f1,..fn) is equivalent to the conjuction of
+    f1=f2, .., fn-1=fn. It substitutes any inference from these
+    equality predicates that is equivalent to the conjunction.
+    Thus, =(a1,a2,a3) can substitute for ((a1=a3) AND (a2=a3) AND (a2=a1)) as
+    it is equivalent to ((a1=a2) AND (a2=a3)).
+    The function always makes a subsitution of all equality predicates occured
+    in a conjuction for a minimal set of multiple equality predicates.
+    This set can be considered as a canonical representation of the
+    sub-conjunction of the equality predicates.
+    E.g. (t1.a=t2.b AND t2.b>5 AND t1.a=t3.c) is replaced by 
+    (=(t1.a,t2.b,t3.c) AND t2.b>5), not by
+    (=(t1.a,t2.b) AND =(t1.a,t3.c) AND t2.b>5);
+    while (t1.a=t2.b AND t2.b>5 AND t3.c,t4.d) is replaced by
+    (=(t1.a,t2.b) AND =(t3.c=t4.d) AND t2.b>5),
+    but if additionally =(t4.d,t2.b) is inherited, it
+    will be replaced by (=(t1.a,t2.b,t3.c,t4.d) AND t2.b>5)
+
+  IMPLEMENTATION
+    The function performs the substitution in a recursive descent by
+    the condtion tree, passing to the next AND level a chain of multiple
+    equality predicates which have been built at the upper levels.
+    The Item_equal items built at the level are attached to other 
+    non-equality conjucts as a sublist. The pointer to the inherited
+    multiple equalities is saved in the and condition object (Item_cond_and).
+    This chain allows us for any field reference occurence easyly to find a 
+    multiple equality that must be held for this occurence.
+
+  RETURN
+    pointer to the transformed condition
+*/
+
+static COND *build_all_equal_items(COND *cond,
+                                        COND_EQUAL *inherited)
+{
+  COND_EQUAL cond_equal;
+  cond_equal.parent_level= inherited;
+
+  if (cond->type() == Item::COND_ITEM)
+  {
+    bool and_level= ((Item_cond*) cond)->functype() ==
+      Item_func::COND_AND_FUNC;
+    List<Item> *args= ((Item_cond*) cond)->argument_list();
+    
+    List_iterator<Item> li(*args);
+    Item *item;
+
+    if (and_level)
+    {
+      /*
+         Retrieve all conjucts of this level detecting the equality
+         that are subject to substitution by multiple equality items and
+         removing each such predicate from the conjunction after after having 
+         found/created a multiple equality whose inference the predicate is.
+     */      
+      while ((item= li++))
+      {
+        if (check_equality(item, &cond_equal))
+          li.remove();
+      }
+
+      List_iterator_fast<Item_equal> it(cond_equal.current_level);
+      while ((item= it++))
+      {
+        item->fix_fields(current_thd, 0, 0);
+      }      
+      ((Item_cond_and*)cond)->cond_equal= cond_equal;
+      inherited= &(((Item_cond_and*)cond)->cond_equal);
+    }
+    /*
+       Make replacement of equality predicates for lower levels
+       of the condition expression.
+    */
+    List_iterator<Item> it(*args);
+    while((item= it++))
+    { 
+      Item *new_item;
+      if ((new_item = build_all_equal_items(item, inherited))!= item)
+      {
+        /* This replacement happens only for standalone equalities */
+        it.replace(new_item);
+      }
+    }
+    if (and_level)
+      args->concat((List<Item> *)&cond_equal.current_level);
+  }
+  else if (cond->type() == Item::FUNC_ITEM)
+  {
+    /* Standalone equalities are handled here */
+    Item_equal *item_equal;
+    if (check_equality(cond, &cond_equal) &&
+        (item_equal= cond_equal.current_level.pop()))
+    {
+      item_equal->fix_fields(current_thd, 0, 0);
+      return item_equal;
+    }
+    else
+    {
+      cond= cond->traverse(&Item::equal_fields_propagator,
+                           (byte *) inherited);
+      cond->update_used_tables();
+    }
+  }
+  return cond;
+}    
+
+
+/* 
+  Generate minimal set of simple equalities equivalent to a multiple equality
+ 
+  SYNOPSIS
+    eliminate_item_equal()
+    cond            condition to add the generated equality to
+    cond_equal      structure to access multiple equality of upper levels
+    item_equal      multiple equality to generate simple equality from     
+
+  DESCRIPTION
+    The function retrieves the fields of the multiple equality item
+    item_equal and  for each field f:
+    - if item_equal contains const it generates the equality f=const_item;
+    - otherwise, if f is not the first field, generates the equality
+      f=item_equal->get_first().
+    All generated equality are added to the cond conjunction.
+
+  NOTES
+    Before generating an equality function checks that it has not
+    been generated for multiple equalies of the upper levels.
+    If cond is equal to 0, then not more then one equality is generated
+    and a pointer to it is returned as the result of the function.
+
+  RETURN
+    The condition with generated simple equalities or
+    a pointer to the simple generated equality.
+*/
+
+static Item *eliminate_item_equal(COND *cond, COND_EQUAL *cond_equal,
+                                  Item_equal *item_equal)
+{
+  Item *item_const= item_equal->get_const();
+  Item_equal_iterator it(*item_equal);
+  Item *head; 
+  if (item_const)
+    head= item_const;
+  else
+  {
+    head= item_equal->get_first();
+    it++;
+  }
+  Item_field *item_field;
+  Item *new_item= 0;
+  while ((item_field= it++))
+  {
+    Item_equal *upper= item_field->find_item_equal(cond_equal);
+    Item_field *item= item_field;
+    if (upper)
+    { 
+      if (item_const)
+      {
+        if (upper->get_const())
+          item= 0;
+      }
+      else
+      {
+        Item_equal_iterator li(*item_equal);
+        while ((item= li++) != item_field)
+        {
+          if (item->find_item_equal(cond_equal) == upper)
+            break;
+        }
+      }
+    }
+    if (item == item_field)
+    {
+      if (!cond && new_item)
+      {
+        cond= new Item_cond_and();
+        ((Item_cond *) cond)->add(new_item);
+      }
+      item_field->item_equal= item_equal;
+      new_item= new Item_func_eq(item_field, head);
+      ((Item_func_eq *) new_item)->fix_length_and_dec();
+      if (cond)
+        ((Item_cond *) cond)->add(new_item);
+    }
+  }
+  if (!cond)
+    cond= (COND *) new_item;
+  return cond;
+}
+
+
+/* 
+  Substitute every field reference in a condition by the best equal field 
+  and eliminate all multiplle equality predicates
+ 
+  SYNOPSIS
+    substitute_for_best_equal_field()
+    cond            condition to process
+    table_join_idx  index to tables determining field preference
+
+  DESCRIPTION
+    The function retrieves the cond condition and for each encountered
+    multiple equality predicate it sorts the field references in it
+    according to the order of tables specified by the table_join_idx
+    parameter. Then it eliminates the multiple equality predicate it
+    replacing it by the conjunction of simple equality predicates 
+    equating every field from the multiple equality to the first
+    field in it, or to the constant, if there is any.
+    After this the function retrieves all other conjuncted
+    predicates substitute every field reference by the field reference
+    to the first equal field or equal constant if there are any.
+ 
+  NOTES
+    At the first glance full sort of fields in multiple equality
+    seems to be an overkill. Yet it's not the case due to possible
+    new fields in multiple equality item of lower levels. We want
+    the order in them to comply with the order of upper levels.
+
+  RETURN
+    The transformed condition
+*/
+
+static COND* substitute_for_best_equal_field(COND *cond,
+                                             void *table_join_idx)
+{
+  Item_equal *item_equal;
+
+  if (cond->type() == Item::COND_ITEM)
+  {
+    List<Item> *cond_list= ((Item_cond*) cond)->argument_list();
+    COND_EQUAL *cond_equal= 0;
+
+    bool and_level= ((Item_cond*) cond)->functype() ==
+                      Item_func::COND_AND_FUNC;
+    if (and_level)
+    {
+      cond_equal= &((Item_cond_and *) cond)->cond_equal;
+      cond_list->disjoin((List<Item> *) &cond_equal->current_level);
+
+      List_iterator_fast<Item_equal> it(cond_equal->current_level);      
+      while((item_equal= it++))
+      {
+        item_equal->sort(table_join_idx);
+      }
+    }
+    
+    List_iterator<Item> li(*cond_list);
+    Item *item;
+    while ((item= li++))
+    {
+      Item *new_item =substitute_for_best_equal_field(item,
+                                                        table_join_idx);
+      if (new_item != item)
+        li.replace(new_item);
+   }
+
+    if (and_level)
+    {
+      List_iterator_fast<Item_equal> it(cond_equal->current_level);
+      while((item_equal= it++))
+      {
+        eliminate_item_equal(cond, cond_equal->parent_level, item_equal);
+      }
+    }
+  }
+  else if (cond->type() == Item::FUNC_ITEM && 
+           ((Item_cond*) cond)->functype() == Item_func::MULT_EQUAL_FUNC)
+  {
+    item_equal= (Item_equal *) cond;
+    item_equal->sort(table_join_idx);
+    return eliminate_item_equal(0, 0, item_equal);
+  }
+  else
+    cond->walk(&Item::replace_equal_field_processor, 0);
+  return cond;
+}
+
+
 /*
   change field = field to field = const for each found field = const in the
   and_level
@@ -4318,9 +5053,6 @@ optimize_cond(COND *conds,Item::cond_result *cond_value)
     return conds;
   }
   DBUG_EXECUTE("where",print_where(conds,"original"););
-  /* eliminate NOT operators */
-  conds= eliminate_not_funcs(conds);
-  DBUG_EXECUTE("where", print_where(conds, "after negation elimination"););
   /* change field = field to field = const for each found field = const */
   propagate_cond_constants((I_List<COND_CMP> *) 0,conds,conds);
   /*
@@ -4412,7 +5144,8 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value)
     }
   }
   else if (cond->type() == Item::FUNC_ITEM &&
-	   ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC)
+	   ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC &&
+           !cond->const_item())
   {
     /*
       Handles this special case for some ODBC applications:
@@ -4463,6 +5196,22 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value)
       }
     }
   }
+  else if (cond->type() == Item::FUNC_ITEM &&
+	   ((Item_func*) cond)->functype() == Item_func::MULT_EQUAL_FUNC)
+  {
+    /*
+      The is_false method for an multiple equality item returns 1
+      when the conjunction with this item originally contained an
+      equality that was inconsistent with the multiple equality
+      predicate or has been inherited from other multiple equality
+      for which is_false returns 1.
+    */
+    if (((Item_equal *) cond)->is_false())
+    {
+      *cond_value= Item::COND_FALSE;
+      return (COND*) 0;
+    }
+  }
   else if (cond->const_item())
   {
     *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 24854713a0e..ad2adc2ee0f 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -184,6 +184,7 @@ class JOIN :public Sql_alloc
   ORDER *order, *group_list, *proc_param; //hold parameters of mysql_select
   COND *conds;                            // ---"---
   Item *conds_history;                    // store WHERE for explain
+  COND_EQUAL *cond_equal;
   TABLE_LIST *tables_list;           //hold 'tables' parameter of mysql_selec
   SQL_SELECT *select;                //created in optimisation phase
   Item **ref_pointer_array; //used pointer reference for this select
@@ -243,6 +244,7 @@ class JOIN :public Sql_alloc
     ref_pointer_array_size= 0;
     zero_result_cause= 0;
     optimized= 0;
+    cond_equal= 0;
 
     fields_list= fields;
     bzero((char*) &keyuse,sizeof(keyuse));
-- 
2.30.9