diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result
index d442e4d97ced3b7636ef5cf76a697f2731785acb..e2a5494eaf76e475c78a8df8da9ef2df6029d54d 100644
--- a/mysql-test/r/subselect.result
+++ b/mysql-test/r/subselect.result
@@ -81,21 +81,18 @@ select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1);
 a	b
 1	7
 2	7
-select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1) 
-union (select * from t4 order by a limit 2) limit 3;
+select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1) union (select * from t4 order by a limit 2) limit 3;
 a	b
 1	7
 2	7
 3	8
-select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1)
-union (select * from t4 where t4.b=(select max(t2.a)*4 from t2) order by a);
+select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1) union (select * from t4 where t4.b=(select max(t2.a)*4 from t2) order by a);
 a	b
 1	7
 2	7
 3	8
 4	8
-explain select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1) 
-union (select * from t4 where t4.b=(select max(t2.a)*4 from t2) order by a);
+explain select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1) union (select * from t4 where t4.b=(select max(t2.a)*4 from t2) order by a);
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	t2	ALL	NULL	NULL	NULL	NULL	2	Using where
 2	SUBSELECT	t3	ALL	NULL	NULL	NULL	NULL	3	Using filesort
@@ -330,19 +327,15 @@ Unknown column 'a' in 'having clause'
 SELECT * from t2 where topic IN (SELECT topic FROM t2 GROUP BY date);
 mot	topic	date	pseudo
 joce	40143	2002-10-22	joce
-joce	43506	2002-10-22	joce
 SELECT * from t2 where topic IN (SELECT topic FROM t2 GROUP BY date HAVING topic < 4100);
 mot	topic	date	pseudo
-joce	43506	2002-10-22	joce
 SELECT * from t2 where topic IN (SELECT SUM(topic) FROM t1);
 mot	topic	date	pseudo
 SELECT * from t2 where topic = any (SELECT topic FROM t2 GROUP BY date);
 mot	topic	date	pseudo
 joce	40143	2002-10-22	joce
-joce	43506	2002-10-22	joce
 SELECT * from t2 where topic = any (SELECT topic FROM t2 GROUP BY date HAVING topic < 4100);
 mot	topic	date	pseudo
-joce	43506	2002-10-22	joce
 SELECT * from t2 where topic = any (SELECT SUM(topic) FROM t1);
 mot	topic	date	pseudo
 SELECT * from t2 where topic = all (SELECT topic FROM t2 GROUP BY date);
@@ -351,6 +344,7 @@ joce	40143	2002-10-22	joce
 SELECT * from t2 where topic = all (SELECT topic FROM t2 GROUP BY date HAVING topic < 4100);
 mot	topic	date	pseudo
 joce	40143	2002-10-22	joce
+joce	43506	2002-10-22	joce
 SELECT * from t2 where topic = all (SELECT SUM(topic) FROM t2);
 mot	topic	date	pseudo
 SELECT * from t2 where topic <> any (SELECT SUM(topic) FROM t2);
diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test
index 8b174882bc694bd9e13f1be13205aa2579e6f3dc..c22827dff18172da053ee12cc5f16a58b058d9c7 100644
--- a/mysql-test/t/subselect.test
+++ b/mysql-test/t/subselect.test
@@ -42,12 +42,9 @@ select (select a from t3), a from t2;
 select * from t2 where t2.a=(select a from t1);
 insert into t3 values (6),(7),(3);
 select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1);
-select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1) 
-union (select * from t4 order by a limit 2) limit 3;
-select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1)
-union (select * from t4 where t4.b=(select max(t2.a)*4 from t2) order by a);
-explain select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1) 
-union (select * from t4 where t4.b=(select max(t2.a)*4 from t2) order by a);
+select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1) union (select * from t4 order by a limit 2) limit 3;
+select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1) union (select * from t4 where t4.b=(select max(t2.a)*4 from t2) order by a);
+explain select * from t2 where t2.b=(select a from t3 order by 1 desc limit 1) union (select * from t4 where t4.b=(select max(t2.a)*4 from t2) order by a);
 select (select a from t3 where a<t2.a*4 order by 1 desc limit 1), a from t2;
 select (select t3.a from t3 where a<8 order by 1 desc limit 1), a from 
 (select * from t2 where a>1) as tt;
@@ -190,7 +187,10 @@ SELECT * from t2 where topic = any (SELECT topic FROM t2 GROUP BY date);
 SELECT * from t2 where topic = any (SELECT topic FROM t2 GROUP BY date HAVING topic < 4100);
 SELECT * from t2 where topic = any (SELECT SUM(topic) FROM t1);
 SELECT * from t2 where topic = all (SELECT topic FROM t2 GROUP BY date);
+SELECT topic FROM t2 GROUP BY date;
+SELECT topic FROM t2 GROUP BY date HAVING topic < 4100;
 SELECT * from t2 where topic = all (SELECT topic FROM t2 GROUP BY date HAVING topic < 4100);
+SELECT *, date as fff from t2 where not (SELECT date FROM t2 GROUP BY date HAVING topic < 4100 and fff!=date);
 SELECT * from t2 where topic = all (SELECT SUM(topic) FROM t2);
 SELECT * from t2 where topic <> any (SELECT SUM(topic) FROM t2);
 drop table t1,t2;
diff --git a/sql/item.cc b/sql/item.cc
index 5e74820a3f85e0eff8966ba9a650d8d5d71d920d..81f923faabd1ba2faa13c4bcc46e9fdc1434444d 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -46,6 +46,31 @@ Item::Item():
   loop_id= 0;
 }
 
+Item::Item(Item &item):
+  loop_id(0),
+  str_value(item.str_value),
+  name(item.name),
+  max_length(item.max_length),
+  marker(item.marker),
+  decimals(item.decimals),
+  maybe_null(item.maybe_null),
+  null_value(item.null_value),
+  unsigned_flag(item.unsigned_flag),
+  with_sum_func(item.with_sum_func),
+  fixed(item.fixed)
+{
+  next=current_thd->free_list;			// Put in free list
+  current_thd->free_list= this;
+}
+
+Item_ident::Item_ident(Item_ident &item):
+  Item(item),
+  db_name(item.db_name),
+  table_name(item.table_name),
+  field_name(item.field_name),
+  depended_from(item.depended_from)
+{}
+
 bool Item::check_loop(uint id)
 {
   DBUG_ENTER("Item::check_loop");
@@ -153,6 +178,11 @@ Item_field::Item_field(Field *f) :Item_ident(NullS,f->table_name,f->field_name)
   fixed= 1; // This item is not needed in fix_fields
 }
 
+Item_field::Item_field(Item_field &item):
+  Item_ident(item),
+  field(item.field),
+  result_field(item.result_field)
+{}
 
 void Item_field::set_field(Field *field_par)
 {
@@ -262,6 +292,13 @@ table_map Item_field::used_tables() const
   return field->table->map;
 }
 
+Item * Item_field::get_tmp_table_item()
+{
+  Item_field *new_item= new Item_field(*this);
+  if (new_item)
+    new_item->field= new_item->result_field;
+  return new_item;
+}
 
 String *Item_int::val_str(String *str)
 {
@@ -535,6 +572,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
       SELECT_LEX *last= 0;
       
       Item **refer= (Item **)not_found_item;
+      uint counter= 0;
       // Prevent using outer fields in subselects, that is not supported now
       SELECT_LEX *cursel=(SELECT_LEX *) thd->lex.current_select;
       if (cursel->linkage != DERIVED_TABLE_TYPE)
@@ -546,7 +584,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
 					 (last= sl)->get_table_list(),
 					 0)) != not_found_field)
 	    break;
-	  if ((refer= find_item_in_list(this, sl->item_list,
+	  if ((refer= find_item_in_list(this, sl->item_list, &counter,
 				       REPORT_EXCEPT_NOT_FOUND)) !=
 	     (Item **)not_found_item)
 	    break;
@@ -565,8 +603,16 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
       }
       else if (refer != (Item **)not_found_item)
       {
+	if (!(*refer)->fixed)
+	{
+	  my_error(ER_ILLEGAL_REFERENCE, MYF(0), name,
+		   "forward reference in item list");
+	  return -1;
+	}
+
 	Item_ref *r;
-	*ref= r= new Item_ref(refer, (char *)table_name,
+	*ref= r= new Item_ref(last->ref_pointer_array + counter-1
+			      , (char *)table_name,
 			   (char *)field_name);
 	if (!r)
 	  return 1;
@@ -930,6 +976,7 @@ bool Item_null::send(THD *thd, String *packet)
 
 bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
 {
+  uint counter= 0;
   if (!ref)
   {
     SELECT_LEX *sl= thd->lex.current_select->outer_select();
@@ -940,6 +987,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
     */
     if ((ref= find_item_in_list(this, 
 				*(thd->lex.current_select->get_item_list()),
+				&counter,
 				((sl && 
 				  thd->lex.current_select->linkage !=
 				  DERIVED_TABLE_TYPE) ? 
@@ -961,7 +1009,8 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
       for ( ; sl ; sl= sl->outer_select())
       {
 	if ((ref= find_item_in_list(this, (last= sl)->item_list,
-				   REPORT_EXCEPT_NOT_FOUND)) !=
+				    &counter,
+				    REPORT_EXCEPT_NOT_FOUND)) !=
 	   (Item **)not_found_item)
 	  break;
 	if ((tmp= find_field_in_tables(thd, this,
@@ -980,6 +1029,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
 	// Call to report error
 	find_item_in_list(this,
 			  *(thd->lex.current_select->get_item_list()),
+			  &counter,
 			  REPORT_ALL_ERRORS);
         ref= 0;
 	return 1;
@@ -996,17 +1046,35 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
       }
       else
       {
-	depended_from= last;
+	if (!(*ref)->fixed)
+	{
+	  my_error(ER_ILLEGAL_REFERENCE, MYF(0), name,
+		   "forward reference in item list");
+	  return -1;
+	}
+	ref= (depended_from= last)->ref_pointer_array + counter-1;
 	thd->lex.current_select->mark_as_dependent(last);
 	thd->add_possible_loop(this);
       }
     }
     else if (!ref)
       return 1;
+    else
+    {
+      if (!(*ref)->fixed)
+      {
+	my_error(ER_ILLEGAL_REFERENCE, MYF(0), name,
+		 "forward reference in item list");
+	return -1;
+      }
+      ref= thd->lex.current_select->ref_pointer_array + counter-1;
+    }
+    
     max_length= (*ref)->max_length;
     maybe_null= (*ref)->maybe_null;
     decimals=	(*ref)->decimals;
   }
+
   if (((*ref)->with_sum_func && 
        (depended_from || 
 	!(thd->lex.current_select->linkage != GLOBAL_OPTIONS_TYPE &&
diff --git a/sql/item.h b/sql/item.h
index 4dff0591c09c34a1d05cd1ce4a92d5c14386c773..900315a3bf670146d5956d628e1cbbd31c03eedb 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -50,6 +50,8 @@ class Item {
 
   // alloc & destruct is done as start of select using sql_alloc
   Item();
+  // copy constructor used by Item_field, Item_ref & agregate (sum) functions
+  Item(Item &item);
   virtual ~Item() { name=0; }		/*lint -e1509 */
   void set_name(const char *str,uint length=0);
   void init_make_field(Send_field *tmp_field,enum enum_field_types type);
@@ -67,7 +69,8 @@ class Item {
   virtual longlong val_int()=0;
   virtual String *val_str(String*)=0;
   virtual void make_field(Send_field *field)=0;
-  virtual Field *tmp_table_field(TABLE *t_arg=(TABLE *)0) { return 0; }
+  virtual Field *tmp_table_field() { return 0; }
+  virtual Field *tmp_table_field(TABLE *t_arg) { return 0; }
   virtual const char *full_name() const { return name ? name : "???"; }
   virtual double  val_result() { return val(); }
   virtual longlong val_int_result() { return val_int(); }
@@ -82,12 +85,14 @@ class Item {
   virtual bool const_item() const { return used_tables() == 0; }
   virtual void print(String *str_arg) { str_arg->append(full_name()); }
   virtual void update_used_tables() {}
-  virtual void split_sum_func(List<Item> &fields) {}
+  virtual void split_sum_func(Item **ref_pointer_array, List<Item> &fields) {}
   virtual bool get_date(TIME *ltime,bool fuzzydate);
   virtual bool get_time(TIME *ltime);
   virtual bool is_null() { return 0; };
   virtual bool check_loop(uint id);
   virtual void top_level_item() {}
+  virtual Item * get_same() { return this; }
+  virtual Item * get_tmp_table_item() { return get_same(); }
 
   virtual bool binary() const
   { return str_value.charset()->state & MY_CS_BINSORT ? 1 : 0 ; }
@@ -159,11 +164,14 @@ class Item_ident :public Item
   const char *table_name;
   const char *field_name;
   st_select_lex *depended_from;
+
   Item_ident(const char *db_name_par,const char *table_name_par,
 	     const char *field_name_par)
     :db_name(db_name_par),table_name(table_name_par),
     field_name(field_name_par), depended_from(0)
     { name = (char*) field_name_par; }
+  // copy constructor used by Item_field & Item_ref
+  Item_ident(Item_ident &item);
   const char *full_name() const;
 };
 
@@ -179,6 +187,8 @@ class Item_field :public Item_ident
 	     const char *field_name_par)
     :Item_ident(db_par,table_name_par,field_name_par),field(0),result_field(0)
   {}
+  // copy constructor need to process subselect with temporary tables
+  Item_field(Item_field &item);
   Item_field(Field *field);
   enum Type type() const { return FIELD_ITEM; }
   bool eq(const Item *item, bool binary_cmp) const;
@@ -202,10 +212,12 @@ class Item_field :public Item_ident
   {
     return field->result_type();
   }
-  Field *tmp_table_field(TABLE *t_arg=(TABLE *)0) { return result_field; }
+  Field *tmp_table_field() { return result_field; }
+  Field *tmp_table_field(TABLE *t_arg) { return result_field; }
   bool get_date(TIME *ltime,bool fuzzydate);  
   bool get_time(TIME *ltime);  
   bool is_null() { return field->is_null(); }
+  Item * get_tmp_table_item();
 };
 
 
@@ -448,8 +460,13 @@ class Item_result_field :public Item	/* Item with result field */
 public:
   Field *result_field;				/* Save result here */
   Item_result_field() :result_field(0) {}
+  Item_result_field(Item_result_field &item): Item(item)
+  {
+    result_field= item.result_field;
+  }
   ~Item_result_field() {}			/* Required with gcc 2.95 */
-  Field *tmp_table_field(TABLE *t_arg=(TABLE *)0) { return result_field; }
+  Field *tmp_table_field() { return result_field; }
+  Field *tmp_table_field(TABLE *t_arg) { return result_field; }
   table_map used_tables() const { return 1; }
   virtual void fix_length_and_dec()=0;
 };
@@ -463,6 +480,8 @@ class Item_ref :public Item_ident
     :Item_ident(db_par,table_name_par,field_name_par),ref(0) {}
   Item_ref(Item **item, char *table_name_par,char *field_name_par)
     :Item_ident(NullS,table_name_par,field_name_par),ref(item) {}
+  // copy constructor need to process subselect with temporary tables
+  Item_ref(Item_ref &item): Item_ident(item), ref(item.ref) {}
   enum Type type() const		{ return REF_ITEM; }
   bool eq(const Item *item, bool binary_cmp) const
   { return ref && (*ref)->eq(item, binary_cmp); }
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index dd8d1aeff0293f71bfdf4db3cdd15e57f229878f..f1859e482065b2a4e7f5e5dbe682fd314eb9aba4 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1175,16 +1175,20 @@ void Item_func_in::update_used_tables()
   const_item_cache&=item->const_item();
 }
 
-void Item_func_in::split_sum_func(List<Item> &fields)
+void Item_func_in::split_sum_func(Item **ref_pointer_array, List<Item> &fields)
 {
   if (item->with_sum_func && item->type() != SUM_FUNC_ITEM)
-    item->split_sum_func(fields);
+    item->split_sum_func(ref_pointer_array, fields);
   else if (item->used_tables() || item->type() == SUM_FUNC_ITEM)
   {
+    uint el= fields.elements;
     fields.push_front(item);
-    item=new Item_ref((Item**) fields.head_ref(),0,item->name);
+    ref_pointer_array[el]= item;
+    item=new Item_ref(ref_pointer_array + el,
+		      0, item->name);
+    
   }  
-  Item_func::split_sum_func(fields);
+  Item_func::split_sum_func(ref_pointer_array, fields);
 }
 
 
@@ -1281,7 +1285,7 @@ bool Item_cond::check_loop(uint id)
   DBUG_RETURN(0);
 }
 
-void Item_cond::split_sum_func(List<Item> &fields)
+void Item_cond::split_sum_func(Item **ref_pointer_array, List<Item> &fields)
 {
   List_iterator<Item> li(list);
   Item *item;
@@ -1290,11 +1294,13 @@ void Item_cond::split_sum_func(List<Item> &fields)
   while ((item=li++))
   {
     if (item->with_sum_func && item->type() != SUM_FUNC_ITEM)
-      item->split_sum_func(fields);
+      item->split_sum_func(ref_pointer_array, fields);
     else if (item->used_tables() || item->type() == SUM_FUNC_ITEM)
     {
+      uint el= fields.elements;
       fields.push_front(item);
-      li.replace(new Item_ref((Item**) fields.head_ref(),0,item->name));
+      ref_pointer_array[el]= item;
+      li.replace(new Item_ref(ref_pointer_array + el, 0, item->name));
     }
     item->update_used_tables();
     used_tables_cache|=item->used_tables();
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index b4f4872bd9576a91af0e674a2b093290d196d41f..adab7a042c5c4cccac8b29fb89a3f1130bed6fe9 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -509,7 +509,7 @@ class Item_func_in :public Item_int_func
   enum Functype functype() const { return IN_FUNC; }
   const char *func_name() const { return " IN "; }
   void update_used_tables();
-  void split_sum_func(List<Item> &fields);
+  void split_sum_func(Item **ref_pointer_array, List<Item> &fields);
   bool check_loop(uint id)
   {
     DBUG_ENTER("Item_func_in::check_loop");
@@ -656,7 +656,7 @@ class Item_cond :public Item_bool_func
   table_map used_tables() const;
   void update_used_tables();
   void print(String *str);
-  void split_sum_func(List<Item> &fields);
+  void split_sum_func(Item **ref_pointer_array, List<Item> &fields);
   friend int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds);
   bool check_loop(uint id);
   void top_level_item() { abort_on_null=1; }
diff --git a/sql/item_func.cc b/sql/item_func.cc
index c84b554b522be8e1c7ed17c5f9d45a88ee1b88d4..1d4d26d799ee6c1690bd7aaa6611ea43e3fa2969 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -146,17 +146,19 @@ bool Item_func::check_loop(uint id)
   DBUG_RETURN(0);
 }
 
-void Item_func::split_sum_func(List<Item> &fields)
+void Item_func::split_sum_func(Item **ref_pointer_array, List<Item> &fields)
 {
   Item **arg,**arg_end;
   for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
   {
     if ((*arg)->with_sum_func && (*arg)->type() != SUM_FUNC_ITEM)
-      (*arg)->split_sum_func(fields);
+      (*arg)->split_sum_func(ref_pointer_array, fields);
     else if ((*arg)->used_tables() || (*arg)->type() == SUM_FUNC_ITEM)
     {
+      uint el= fields.elements;
       fields.push_front(*arg);
-      *arg=new Item_ref((Item**) fields.head_ref(),0,(*arg)->name);
+      ref_pointer_array[el]= *arg;
+      *arg=new Item_ref(ref_pointer_array + el, 0, (*arg)->name);
     }
   }
 }
@@ -225,14 +227,11 @@ bool Item_func::eq(const Item *item, bool binary_cmp) const
   return 1;
 }
 
-
 Field *Item_func::tmp_table_field(TABLE *t_arg)
 {
   Field *res;
   LINT_INIT(res);
 
-  if (!t_arg)
-    return result_field;
   switch (result_type()) {
   case INT_RESULT:
     if (max_length > 11)
@@ -303,6 +302,16 @@ void Item_func::fix_num_length_and_dec()
   max_length=float_length(decimals);
 }
 
+Item * Item_func::get_tmp_table_item()
+{
+  if (!with_sum_func && !const_item())
+  {
+    return new Item_field(result_field);
+  }
+  else
+    return get_same();
+}
+
 String *Item_int_func::val_str(String *str)
 {
   longlong nr=val_int();
diff --git a/sql/item_func.h b/sql/item_func.h
index 98e56af368c2bf2ef9de6a80c671e1ea6dd73cf8..dab42f1b3367f16b6e5a73a3dd223ba5a81084c4 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -118,7 +118,7 @@ class Item_func :public Item_result_field
   inline Item **arguments() const { return args; }
   inline uint argument_count() const { return arg_count; }
   inline void remove_arguments() { arg_count=0; }
-  virtual void split_sum_func(List<Item> &fields);
+  virtual void split_sum_func(Item **ref_pointer_array, List<Item> &fields);
   void print(String *str);
   void print_op(String *str);
   void fix_num_length_and_dec();
@@ -132,8 +132,10 @@ class Item_func :public Item_result_field
   }
   bool is_null() { (void) val_int(); return null_value; }
   friend class udf_handler;
+  Field *tmp_table_field() { return result_field; }
   Field *tmp_table_field(TABLE *t_arg);
   bool check_loop(uint id);
+  Item * get_tmp_table_item();
 };
 
 
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index e087664e060b7302b17ca41ecf596528e526f66f..f397379b5be2fb50ac6311279a96b5ce8b29ffcf 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -288,6 +288,8 @@ void Item_in_subselect::single_value_transformer(st_select_lex *select_lex,
 						 compare_func_creator func)
 {
   DBUG_ENTER("Item_in_subselect::single_value_transformer");
+  THD *thd= current_thd;
+
   for (SELECT_LEX * sl= select_lex; sl; sl= sl->next_select())
   {
     Item *item;
@@ -301,12 +303,16 @@ void Item_in_subselect::single_value_transformer(st_select_lex *select_lex,
 
     Item *expr= new Item_outer_select_context_saver(left_expr);
 
-    if (sl->having || sl->with_sum_func || sl->group_list.first ||
-	sl->order_list.first)
+    if (sl->having || sl->with_sum_func || sl->group_list.elements ||
+	sl->order_list.elements)
     {
       sl->item_list.push_back(item);
-      item= (*func)(expr, new Item_ref(sl->item_list.head_ref(),
-					    0, (char*)"<result>"));
+      setup_ref_array(thd, &sl->ref_pointer_array,
+		      1+ select_lex->with_sum_func +
+		      select_lex->order_list.elements +
+		      select_lex->group_list.elements);
+      item= (*func)(expr, new Item_ref(sl->ref_pointer_array,
+				       0, (char*)"<result>"));
       if (sl->having || sl->with_sum_func || sl->group_list.first)
 	if (sl->having)
 	  sl->having= new Item_cond_and(sl->having, item);
@@ -348,7 +354,6 @@ void Item_in_subselect::single_value_transformer(st_select_lex *select_lex,
 	  // it is single select without tables => possible optimization
 	  item= (*func)(left_expr, item);
 	  substitution= item;
-	  THD *thd= current_thd;
 	  if (thd->lex.describe)
 	  {
 	    char warn_buff[MYSQL_ERRMSG_SIZE];
@@ -424,8 +429,12 @@ int subselect_single_select_engine::prepare()
   prepared= 1;
   SELECT_LEX_NODE *save_select= thd->lex.current_select;
   thd->lex.current_select= select_lex;
-  if(join->prepare((TABLE_LIST*) select_lex->table_list.first,
+  if(join->prepare(&select_lex->ref_pointer_array,
+		   (TABLE_LIST*) select_lex->table_list.first,
+		   select_lex->with_wild,
 		   select_lex->where,
+		   select_lex->order_list.elements +
+		   select_lex->group_list.elements,
 		   (ORDER*) select_lex->order_list.first,
 		   (ORDER*) select_lex->group_list.first,
 		   select_lex->having,
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index a1f772f4d467db1a49876eed1adfb65cdec8a4ec..7e40dc9a94c0cc044d7578aecd7b96ba2073bcc8 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -41,9 +41,22 @@ Item_sum::Item_sum(List<Item> &list)
   list.empty();					// Fields are used
 }
 
+Item_sum::Item_sum(Item_sum &item):
+  Item_result_field(item), quick_group(item.quick_group)
+{
+  arg_count= item.arg_count;
+  if (arg_count <= 2)
+    args=tmp_args;
+  else
+    if (!(args=(Item**) sql_alloc(sizeof(Item*)*arg_count)))
+      return;
+  for(uint i= 0; i < arg_count; i++)
+    args[i]= item.args[i];
+}
+
 void Item_sum::mark_as_sum_func()
 {
-  current_thd->lex.current_select->with_sum_func=1;
+  current_thd->lex.current_select->with_sum_func++;
   with_sum_func= 1;
 }
 
@@ -90,6 +103,26 @@ void Item_sum::fix_num_length_and_dec()
   max_length=float_length(decimals);
 }
 
+Item * Item_sum::get_tmp_table_item()
+{
+  Item_sum* sum_item= (Item_sum *) get_same();
+  if (sum_item && sum_item->result_field)	   // If not a const sum func
+  {
+    Field *result_field= sum_item->result_field;
+    for (uint i=0 ; i < sum_item->arg_count ; i++)
+    {
+      Item *arg= sum_item->args[i];
+      if (!arg->const_item())
+      {
+	if (arg->type() == Item::FIELD_ITEM)
+	  ((Item_field*) arg)->field= result_field++;
+	else
+	  sum_item->args[i]= new Item_field(result_field++);
+      }
+    }
+  }
+  return sum_item;
+}
 
 String *
 Item_sum_num::val_str(String *str)
@@ -933,7 +966,7 @@ int dump_leaf(byte* key, uint32 count __attribute__((unused)),
     The first item->rec_offset bytes are taken care of with
     restore_record(table,2) in setup()
   */
-  memcpy(buf + item->rec_offset, key, item->tree.size_of_element);
+  memcpy(buf + item->rec_offset, key, item->tree->size_of_element);
   if ((error = item->table->file->write_row(buf)))
   {
     if (error != HA_ERR_FOUND_DUPP_KEY &&
@@ -946,11 +979,14 @@ int dump_leaf(byte* key, uint32 count __attribute__((unused)),
 
 Item_sum_count_distinct::~Item_sum_count_distinct()
 {
-  if (table)
-    free_tmp_table(current_thd, table);
-  delete tmp_table_param;
-  if (use_tree)
-    delete_tree(&tree);
+  if (!original)
+  {
+    if (table)
+      free_tmp_table(current_thd, table);
+    delete tmp_table_param;
+    if (use_tree)
+      delete_tree(tree);
+  }
 }
 
 
@@ -1072,8 +1108,8 @@ bool Item_sum_count_distinct::setup(THD *thd)
       }
     }
 
-    init_tree(&tree, min(thd->variables.max_heap_table_size,
-			 thd->variables.sortbuff_size/16), 0,
+    init_tree(tree, min(thd->variables.max_heap_table_size,
+			thd->variables.sortbuff_size/16), 0,
 	      key_length, compare_key, 0, NULL, cmp_arg);
     use_tree = 1;
 
@@ -1085,6 +1121,12 @@ bool Item_sum_count_distinct::setup(THD *thd)
     */
     max_elements_in_tree = ((key_length) ? 
 			    thd->variables.max_heap_table_size/key_length : 1);
+
+  }
+  if (original)
+  {
+    original->table= table;
+    original->use_tree= use_tree;
   }
   return 0;
 }
@@ -1094,10 +1136,10 @@ int Item_sum_count_distinct::tree_to_myisam()
 {
   if (create_myisam_from_heap(current_thd, table, tmp_table_param,
 			      HA_ERR_RECORD_FILE_FULL, 1) ||
-      tree_walk(&tree, (tree_walk_action)&dump_leaf, (void*)this,
+      tree_walk(tree, (tree_walk_action)&dump_leaf, (void*)this,
 		left_root_right))
     return 1;
-  delete_tree(&tree);
+  delete_tree(tree);
   use_tree = 0;
   return 0;
 }
@@ -1105,7 +1147,7 @@ int Item_sum_count_distinct::tree_to_myisam()
 void Item_sum_count_distinct::reset()
 {
   if (use_tree)
-    reset_tree(&tree);
+    reset_tree(tree);
   else if (table)
   {
     table->file->extra(HA_EXTRA_NO_CACHE);
@@ -1133,13 +1175,13 @@ bool Item_sum_count_distinct::add()
       If the tree got too big, convert to MyISAM, otherwise insert into the
       tree.
     */
-    if (tree.elements_in_tree > max_elements_in_tree)
+    if (tree->elements_in_tree > max_elements_in_tree)
     {
       if (tree_to_myisam())
 	return 1;
     }
-    else if (!tree_insert(&tree, table->record[0] + rec_offset, 0,
-			  tree.custom_arg))
+    else if (!tree_insert(tree, table->record[0] + rec_offset, 0,
+			  tree->custom_arg))
       return 1;
   }
   else if ((error=table->file->write_row(table->record[0])))
@@ -1161,7 +1203,7 @@ longlong Item_sum_count_distinct::val_int()
   if (!table)					// Empty query
     return LL(0);
   if (use_tree)
-    return tree.elements_in_tree;
+    return tree->elements_in_tree;
   table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
   return table->file->records;
 }
diff --git a/sql/item_sum.h b/sql/item_sum.h
index 23b8482d41a644925ae103067fee374eacce49b7..5e561a05e644aaa243eb27f2e18644ec4090c706 100644
--- a/sql/item_sum.h
+++ b/sql/item_sum.h
@@ -54,6 +54,8 @@ class Item_sum :public Item_result_field
     mark_as_sum_func();
   }
   Item_sum(List<Item> &list);
+  //Copy constructor, need to perform subselects with temporary tables
+  Item_sum(Item_sum &item);
   ~Item_sum() { result_field=0; }
 
   enum Type type() const { return SUM_FUNC_ITEM; }
@@ -75,6 +77,7 @@ class Item_sum :public Item_result_field
   void print(String *str);
   void fix_num_length_and_dec();
   virtual bool setup(THD *thd) {return 0;}
+  Item * get_tmp_table_item();
 };
 
 
@@ -85,6 +88,7 @@ class Item_sum_num :public Item_sum
   Item_sum_num(Item *item_par) :Item_sum(item_par) {}
   Item_sum_num(Item *a, Item* b) :Item_sum(a,b) {}
   Item_sum_num(List<Item> &list) :Item_sum(list) {}
+  Item_sum_num(Item_sum_num &item) :Item_sum(item) {}
   bool fix_fields(THD *, TABLE_LIST *, Item **);
   longlong val_int() { return (longlong) val(); } /* Real as default */
   String *val_str(String*str);
@@ -100,6 +104,7 @@ class Item_sum_int :public Item_sum_num
 public:
   Item_sum_int(Item *item_par) :Item_sum_num(item_par) {}
   Item_sum_int(List<Item> &list) :Item_sum_num(list) {}
+  Item_sum_int(Item_sum_int &item) :Item_sum_num(item) {}
   double val() { return (double) val_int(); }
   String *val_str(String*str);
   enum Item_result result_type () const { return INT_RESULT; }
@@ -113,6 +118,7 @@ class Item_sum_sum :public Item_sum_num
 
   public:
   Item_sum_sum(Item *item_par) :Item_sum_num(item_par),sum(0.0) {}
+  Item_sum_sum(Item_sum_sum &item) :Item_sum_num(item), sum(item.sum) {}
   enum Sumfunctype sum_func () const {return SUM_FUNC;}
   void reset();
   bool add();
@@ -120,6 +126,7 @@ class Item_sum_sum :public Item_sum_num
   void reset_field();
   void update_field(int offset);
   const char *func_name() const { return "sum"; }
+  Item * get_same() { return new Item_sum_sum(*this); }
 };
 
 
@@ -132,6 +139,10 @@ class Item_sum_count :public Item_sum_int
   Item_sum_count(Item *item_par)
     :Item_sum_int(item_par),count(0),used_table_cache(~(table_map) 0)
   {}
+  Item_sum_count(Item_sum_count &item): Item_sum_int(item),
+					count(item.count),
+					used_table_cache(item.used_table_cache)
+  {}
   table_map used_tables() const { return used_table_cache; }
   bool const_item() const { return !used_table_cache; }
   enum Sumfunctype sum_func () const { return COUNT_FUNC; }
@@ -142,6 +153,7 @@ class Item_sum_count :public Item_sum_int
   void reset_field();
   void update_field(int offset);
   const char *func_name() const { return "count"; }
+  Item * get_same() { return new Item_sum_count(*this); }
 };
 
 
@@ -154,7 +166,14 @@ class Item_sum_count_distinct :public Item_sum_int
   bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref);
   uint32 *field_lengths;
   TMP_TABLE_PARAM *tmp_table_param;
-  TREE tree;
+  TREE tree_base;
+  TREE *tree;
+  /*
+    Following is 0 normal object and pointer to original one for copy 
+    (to correctly free resources)
+  */
+  Item_sum_count_distinct *original;
+
   uint key_length;
 
   // calculated based on max_heap_table_size. If reached,
@@ -180,9 +199,19 @@ class Item_sum_count_distinct :public Item_sum_int
 
   public:
   Item_sum_count_distinct(List<Item> &list)
-    :Item_sum_int(list),table(0),used_table_cache(~(table_map) 0),
-     tmp_table_param(0),use_tree(0),always_null(0)
-  { quick_group=0; }
+    :Item_sum_int(list), table(0), used_table_cache(~(table_map) 0),
+     tmp_table_param(0), tree(&tree_base), original(0), use_tree(0),
+     always_null(0)
+  { quick_group= 0; }
+  Item_sum_count_distinct(Item_sum_count_distinct &item):
+    Item_sum_int(item), table(item.table),
+    used_table_cache(item.used_table_cache),
+    field_lengths(item.field_lengths), tmp_table_param(item.tmp_table_param),
+    tree(item.tree), original(&item), key_length(item.key_length),
+    max_elements_in_tree(item.max_elements_in_tree),
+    rec_offset(item.rec_offset), use_tree(item.use_tree),
+    always_null(item.always_null)
+    {}
   ~Item_sum_count_distinct();
   table_map used_tables() const { return used_table_cache; }
   enum Sumfunctype sum_func () const { return COUNT_DISTINCT_FUNC; }
@@ -193,6 +222,7 @@ class Item_sum_count_distinct :public Item_sum_int
   void update_field(int offset) { return ; }	// Never called
   const char *func_name() const { return "count_distinct"; }
   bool setup(THD *thd);
+  Item * get_same() { return new Item_sum_count_distinct(*this); }
 };
 
 
@@ -224,6 +254,8 @@ class Item_sum_avg :public Item_sum_num
 
   public:
   Item_sum_avg(Item *item_par) :Item_sum_num(item_par),count(0) {}
+  Item_sum_avg(Item_sum_avg &item)
+    :Item_sum_num(item), sum(item.sum), count(item.count) {}
   enum Sumfunctype sum_func () const {return AVG_FUNC;}
   void reset();
   bool add();
@@ -233,6 +265,7 @@ class Item_sum_avg :public Item_sum_num
   Item *result_item(Field *field)
   { return new Item_avg_field(this); }
   const char *func_name() const { return "avg"; }
+  Item * get_same() { return new Item_sum_avg(*this); }
 };
 
 class Item_sum_std;
@@ -260,6 +293,9 @@ class Item_sum_std :public Item_sum_num
 
   public:
   Item_sum_std(Item *item_par) :Item_sum_num(item_par),count(0) {}
+  Item_sum_std(Item_sum_std &item):
+    Item_sum_num(item), sum(item.sum), sum_sqr(item.sum_sqr),
+    count(item.count) {}
   enum Sumfunctype sum_func () const { return STD_FUNC; }
   void reset();
   bool add();
@@ -269,6 +305,7 @@ class Item_sum_std :public Item_sum_num
   Item *result_item(Field *field)
   { return new Item_std_field(this); }
   const char *func_name() const { return "std"; }
+  Item * get_same() { return new Item_sum_std(*this); }
 };
 
 
@@ -288,6 +325,10 @@ class Item_sum_hybrid :public Item_sum
   Item_sum_hybrid(Item *item_par,int sign) :Item_sum(item_par),cmp_sign(sign),
     used_table_cache(~(table_map) 0)
   {}
+  Item_sum_hybrid(Item_sum_hybrid &item):
+    Item_sum(item), value(item.value), tmp_value(item.tmp_value),
+    sum(item.sum), sum_int(item.sum_int), hybrid_type(item.hybrid_type),
+    cmp_sign(item.cmp_sign), used_table_cache(used_table_cache) {}
   bool fix_fields(THD *, TABLE_LIST *, Item **);
   table_map used_tables() const { return used_table_cache; }
   bool const_item() const { return !used_table_cache; }
@@ -318,10 +359,12 @@ class Item_sum_min :public Item_sum_hybrid
 {
 public:
   Item_sum_min(Item *item_par) :Item_sum_hybrid(item_par,1) {}
+  Item_sum_min(Item_sum_min &item) :Item_sum_hybrid(item) {}
   enum Sumfunctype sum_func () const {return MIN_FUNC;}
 
   bool add();
   const char *func_name() const { return "min"; }
+  Item * get_same() { return new Item_sum_min(*this); }
 };
 
 
@@ -329,10 +372,12 @@ class Item_sum_max :public Item_sum_hybrid
 {
 public:
   Item_sum_max(Item *item_par) :Item_sum_hybrid(item_par,-1) {}
+  Item_sum_max(Item_sum_max &item) :Item_sum_hybrid(item) {}
   enum Sumfunctype sum_func () const {return MAX_FUNC;}
 
   bool add();
   const char *func_name() const { return "max"; }
+  Item * get_same() { return new Item_sum_max(*this); }
 };
 
 
@@ -344,6 +389,8 @@ class Item_sum_bit :public Item_sum_int
   public:
   Item_sum_bit(Item *item_par,ulonglong reset_arg)
     :Item_sum_int(item_par),reset_bits(reset_arg),bits(reset_arg) {}
+  Item_sum_bit(Item_sum_bit &item):
+    Item_sum_int(item), reset_bits(item.reset_bits), bits(item.bits) {}
   enum Sumfunctype sum_func () const {return SUM_BIT_FUNC;}
   void reset();
   longlong val_int();
@@ -355,9 +402,11 @@ class Item_sum_or :public Item_sum_bit
 {
   public:
   Item_sum_or(Item *item_par) :Item_sum_bit(item_par,LL(0)) {}
+  Item_sum_or(Item_sum_or &item) :Item_sum_bit(item) {}
   bool add();
   void update_field(int offset);
   const char *func_name() const { return "bit_or"; }
+  Item * get_same() { return new Item_sum_or(*this); }
 };
 
 
@@ -365,9 +414,11 @@ class Item_sum_and :public Item_sum_bit
 {
   public:
   Item_sum_and(Item *item_par) :Item_sum_bit(item_par, ~(ulonglong) LL(0)) {}
+  Item_sum_and(Item_sum_and &item) :Item_sum_bit(item) {}
   bool add();
   void update_field(int offset);
   const char *func_name() const { return "bit_and"; }
+  Item * get_same() { return new Item_sum_and(*this); }
 };
 
 /*
@@ -385,6 +436,7 @@ class Item_udf_sum : public Item_sum
   Item_udf_sum( udf_func *udf_arg, List<Item> &list )
     :Item_sum( list ), udf(udf_arg)
   { quick_group=0;}
+  Item_udf_sum(Item_udf_sum &item) :Item_sum(item), udf(item.udf) {}
   ~Item_udf_sum() {}
   const char *func_name() const { return udf.name(); }
   bool fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
@@ -408,11 +460,13 @@ class Item_sum_udf_float :public Item_udf_sum
   Item_sum_udf_float(udf_func *udf_arg) :Item_udf_sum(udf_arg) {}
   Item_sum_udf_float(udf_func *udf_arg, List<Item> &list)
     :Item_udf_sum(udf_arg,list) {}
+  Item_sum_udf_float(Item_sum_udf_float &item): Item_udf_sum(item) {}
   ~Item_sum_udf_float() {}
   longlong val_int() { return (longlong) Item_sum_udf_float::val(); }
   double val();
   String *val_str(String*str);
   void fix_length_and_dec() { fix_num_length_and_dec(); }
+  Item * get_same() { return new Item_sum_udf_float(*this); }
 };
 
 
@@ -422,12 +476,14 @@ class Item_sum_udf_int :public Item_udf_sum
   Item_sum_udf_int(udf_func *udf_arg) :Item_udf_sum(udf_arg) {}
   Item_sum_udf_int(udf_func *udf_arg, List<Item> &list)
     :Item_udf_sum(udf_arg,list) {}
+  Item_sum_udf_int(Item_sum_udf_int &item): Item_udf_sum(item) {}
   ~Item_sum_udf_int() {}
   longlong val_int();
   double val() { return (double) Item_sum_udf_int::val_int(); }
   String *val_str(String*str);
   enum Item_result result_type () const { return INT_RESULT; }
   void fix_length_and_dec() { decimals=0; max_length=21; }
+  Item * get_same() { return new Item_sum_udf_int(*this); }
 };
 
 
@@ -437,6 +493,7 @@ class Item_sum_udf_str :public Item_udf_sum
   Item_sum_udf_str(udf_func *udf_arg) :Item_udf_sum(udf_arg) {}
   Item_sum_udf_str(udf_func *udf_arg, List<Item> &list)
     :Item_udf_sum(udf_arg,list) {}
+  Item_sum_udf_str(Item_sum_udf_str &item): Item_udf_sum(item) {}
   ~Item_sum_udf_str() {}
   String *val_str(String *);
   double val()
@@ -451,6 +508,7 @@ class Item_sum_udf_str :public Item_udf_sum
   }
   enum Item_result result_type () const { return STRING_RESULT; }
   void fix_length_and_dec();
+  Item * get_same() { return new Item_sum_udf_str(*this); }
 };
 
 #else /* Dummy functions to get sql_yacc.cc compiled */
@@ -460,12 +518,14 @@ class Item_sum_udf_float :public Item_sum_num
  public:
   Item_sum_udf_float(udf_func *udf_arg) :Item_sum_num() {}
   Item_sum_udf_float(udf_func *udf_arg, List<Item> &list) :Item_sum_num() {}
+  Item_sum_udf_float(Item_sum_udf_float &item): Item_sum_num(item) {}
   ~Item_sum_udf_float() {}
   enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; }
   double val() { return 0.0; }
   void reset() {}
   bool add() { return 0; }  
   void update_field(int offset) {}
+  Item * get_same() { return new Item_sum_udf_float(*this); }
 };
 
 
@@ -474,6 +534,7 @@ class Item_sum_udf_int :public Item_sum_num
 public:
   Item_sum_udf_int(udf_func *udf_arg) :Item_sum_num() {}
   Item_sum_udf_int(udf_func *udf_arg, List<Item> &list) :Item_sum_num() {}
+  Item_sum_udf_int(Item_sum_udf_int &item): Item_sum_num(item) {}
   ~Item_sum_udf_int() {}
   enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; }
   longlong val_int() { return 0; }
@@ -481,6 +542,7 @@ class Item_sum_udf_int :public Item_sum_num
   void reset() {}
   bool add() { return 0; }  
   void update_field(int offset) {}
+  Item * get_same() { return new Item_sum_udf_int(*this); }
 };
 
 
@@ -489,6 +551,7 @@ class Item_sum_udf_str :public Item_sum_num
 public:
   Item_sum_udf_str(udf_func *udf_arg) :Item_sum_num() {}
   Item_sum_udf_str(udf_func *udf_arg, List<Item> &list)  :Item_sum_num() {}
+  Item_sum_udf_str(Item_sum_udf_str &item): Item_sum_num(item) {}
   ~Item_sum_udf_str() {}
   String *val_str(String *) { null_value=1; return 0; }
   double val() { null_value=1; return 0.0; }
@@ -499,6 +562,7 @@ class Item_sum_udf_str :public Item_sum_num
   void reset() {}
   bool add() { return 0; }  
   void update_field(int offset) {}
+  Item * get_same() { return new Item_sum_udf_str(*this); }
 };
 
 #endif /* HAVE_DLOPEN */
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 40397351c18e05ed229fa4affd61aa47d687689b..5b968ed80ebdba4ec6d545398a1524f6c886dd46 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -330,9 +330,10 @@ class Item_date :public Item_func
   {
     init_make_field(tmp_field,FIELD_TYPE_DATE);
   }
+  Field *tmp_table_field() { return result_field; }
   Field *tmp_table_field(TABLE *t_arg)
   {
-    return (!t_arg) ? result_field : new Field_date(maybe_null, name, t_arg, thd_charset());
+    return (new Field_date(maybe_null, name, t_arg, thd_charset()));
   }  
 };
 
@@ -347,10 +348,10 @@ class Item_date_func :public Item_str_func
   {
     init_make_field(tmp_field,FIELD_TYPE_DATETIME);
   }
+  Field *tmp_table_field() { return result_field; }
   Field *tmp_table_field(TABLE *t_arg)
   {
-    return  (!t_arg) ? result_field : new Field_datetime(maybe_null, name,
-							 t_arg, thd_charset());
+    return  (new Field_datetime(maybe_null, name, t_arg, thd_charset()));
   }
 };
 
@@ -373,10 +374,10 @@ class Item_func_curtime :public Item_func
   {
     init_make_field(tmp_field,FIELD_TYPE_TIME);
   }
+  Field *tmp_table_field() { return result_field; }
   Field *tmp_table_field(TABLE *t_arg)
   {
-    return (!t_arg) ? result_field : 
-    		      new Field_time(maybe_null, name, t_arg, thd_charset());
+    return (new Field_time(maybe_null, name, t_arg, thd_charset()));
   }  
 };
 
@@ -475,10 +476,10 @@ class Item_func_sec_to_time :public Item_str_func
   {
     init_make_field(tmp_field,FIELD_TYPE_TIME);
   }
+  Field *tmp_table_field() { return result_field; }
   Field *tmp_table_field(TABLE *t_arg)
   {
-    return (!t_arg) ? result_field : 
-    		      new Field_time(maybe_null, name, t_arg, thd_charset());
+    return (new Field_time(maybe_null, name, t_arg, thd_charset()));
   }
 };
 
@@ -570,10 +571,10 @@ class Item_date_typecast :public Item_typecast
   {
     init_make_field(tmp_field,FIELD_TYPE_DATE);
   }
+  Field *tmp_table_field() { return result_field; }
   Field *tmp_table_field(TABLE *t_arg)
   {
-    return (!t_arg) ? result_field : 
-    		      new Field_date(maybe_null, name, t_arg, thd_charset());
+    return (new Field_date(maybe_null, name, t_arg, thd_charset()));
   }  
 };
 
@@ -587,10 +588,10 @@ class Item_time_typecast :public Item_typecast
   {
     init_make_field(tmp_field,FIELD_TYPE_TIME);
   }
+  Field *tmp_table_field() { return result_field; }
   Field *tmp_table_field(TABLE *t_arg)
   {
-    return (!t_arg) ? result_field : 
-    		      new Field_time(maybe_null, name, t_arg, thd_charset());
+    return (new Field_time(maybe_null, name, t_arg, thd_charset()));
   }
 };
 
@@ -604,9 +605,9 @@ class Item_datetime_typecast :public Item_typecast
   {
     init_make_field(tmp_field,FIELD_TYPE_DATETIME);
   }
+  Field *tmp_table_field() { return result_field; }
   Field *tmp_table_field(TABLE *t_arg)
   {
-    return (!t_arg) ? result_field : new Field_datetime(maybe_null, name,
-							t_arg, thd_charset());
+    return (new Field_datetime(maybe_null, name, t_arg, thd_charset()));
   }
 };
diff --git a/sql/item_uniq.h b/sql/item_uniq.h
index 2004be63de2b9fc89d2ddd7470205e206e18382b..9098dfe7d41a0b01d9f8ef5fe365c605293d29bc 100644
--- a/sql/item_uniq.h
+++ b/sql/item_uniq.h
@@ -37,6 +37,7 @@ class Item_sum_unique_users :public Item_sum_num
 public:
   Item_sum_unique_users(Item *name_arg,int start,int end,Item *item_arg)
     :Item_sum_num(item_arg) {}
+  Item_sum_unique_users(Item_sum_unique_users &item): Item_sum_num(item) {}
   double val() { return 0.0; }  
   enum Sumfunctype sum_func () const {return UNIQUE_USERS_FUNC;}
   void reset() {}
@@ -48,4 +49,5 @@ class Item_sum_unique_users :public Item_sum_num
     fixed= 1;
     return 0;
   }
+  Item_sum * get_same() { return new Item_sum_unique_users(*this); }
 };
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 771d105a1c7f7df7d3f61818673909910713959c..b9cd99624ea4abf3b7f7a0343e472744bde67cc4 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -402,18 +402,21 @@ bool net_store_data(String *packet,CONVERT *convert, const char *from,
 bool net_store_data(String *packet, CONVERT *convert, const char *from);
 
 SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length);
-int setup_order(THD *thd,TABLE_LIST *tables, List<Item> &fields,
-                List <Item> &all_fields, ORDER *order);
-int setup_group(THD *thd,TABLE_LIST *tables,List<Item> &fields,
-		List<Item> &all_fields, ORDER *order,
+int setup_ref_array(THD *thd, Item ***rref_pointer_array, uint elements);
+int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
+		List<Item> &fields, List <Item> &all_fields, ORDER *order);
+int setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
+		List<Item> &fields, List<Item> &all_fields, ORDER *order,
 		bool *hidden_group_fields);
 
 int handle_select(THD *thd, LEX *lex, select_result *result);
-int mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &list,COND *conds,
-		 ORDER *order, ORDER *group,Item *having,ORDER *proc_param,
-		 ulong select_type,select_result *result,
-		 SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex,
-		 bool fake_select_lex);
+int mysql_select(THD *thd, Item ***rref_pointer_array,
+		 TABLE_LIST *tables, uint wild_num,  List<Item> &list,
+		 COND *conds, uint og_num, ORDER *order, ORDER *group,
+		 Item *having, ORDER *proc_param, ulong select_type, 
+		 select_result *result, SELECT_LEX_UNIT *unit, 
+		 SELECT_LEX *select_lex, bool fake_select_lex);
+void free_ulderlayed_joins(THD *thd, SELECT_LEX *select);
 void fix_tables_pointers(SELECT_LEX *select_lex);
 int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit,
 			select_result *result);
@@ -440,7 +443,7 @@ int mysql_alter_table(THD *thd, char *new_db, char *new_name,
 		      List<create_field> &fields,
 		      List<Key> &keys,List<Alter_drop> &drop_list,
 		      List<Alter_column> &alter_list,
-                      ORDER *order,
+                      uint order_num, ORDER *order,
 		      bool drop_primary,
 		      enum enum_duplicates handle_duplicates,
 		      enum enum_enable_or_disable keys_onoff=LEAVE_AS_IS,
@@ -456,7 +459,7 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list,
 		     List<Alter_drop> &drop_list);
 int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields,
 		 List<Item> &values,COND *conds,
-                 ORDER *order, ha_rows limit,
+                 uint order_num, ORDER *order, ha_rows limit,
 		 enum enum_duplicates handle_duplicates);
 int mysql_multi_update(THD *thd, TABLE_LIST *table_list,
 		       List<Item> *fields, List<Item> *values,
@@ -575,15 +578,17 @@ SQL_SELECT *make_select(TABLE *head, table_map const_tables,
 enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
 				  IGNORE_ERRORS};
 extern const Item **not_found_item;
-Item ** find_item_in_list(Item *item, List<Item> &items,
+Item ** find_item_in_list(Item *item, List<Item> &items, uint *counter,
 			  find_item_error_report_type report_error);
 bool insert_fields(THD *thd,TABLE_LIST *tables,
 		   const char *db_name, const char *table_name,
 		   List_iterator<Item> *it);
 bool setup_tables(TABLE_LIST *tables);
-int setup_fields(THD *thd,TABLE_LIST *tables,List<Item> &item,
-		 bool set_query_id,List<Item> *sum_func_list,
-		 bool allow_sum_func);
+int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
+	       List<Item> *sum_func_list, uint wild_num);
+int setup_fields(THD *thd, Item** ref_pointer_array, TABLE_LIST *tables,
+		 List<Item> &item, bool set_query_id, 
+		 List<Item> *sum_func_list, bool allow_sum_func);
 int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds);
 int setup_ftfuncs(SELECT_LEX* select);
 int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index e2b36106fb0d661047862ade3fbccff99b56bffd..870c341c148d2f4eede8b98ca14e5033ca8482ec 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -2012,11 +2012,15 @@ const Item **not_found_item= (const Item**) 0x1;
 
 /*
   Find Item in list of items (find_field_in_tables analog)
-  
+
+  TODO
+    is it better return only counter?
+
   SYNOPSIS
     find_item_in_list()
     find - item to find
     items - list of items
+    counter - to return number of found item
     report_error
       REPORT_ALL_ERRORS - report errors, return 0 if error
       REPORT_EXCEPT_NOT_FOUND - do not report 'not found' error and return not_        found_item, report other errors, return 0
@@ -2033,7 +2037,7 @@ const Item **not_found_item= (const Item**) 0x1;
 */
 
 Item **
-find_item_in_list(Item *find, List<Item> &items,
+find_item_in_list(Item *find, List<Item> &items, uint *counter,
 		  find_item_error_report_type report_error)
 {
   List_iterator<Item> li(items);
@@ -2046,8 +2050,10 @@ find_item_in_list(Item *find, List<Item> &items,
     table_name= ((Item_ident*) find)->table_name;
   }
 
+  uint i= 0;
   while ((item=li++))
   {
+    i++;
     if (field_name && item->type() == Item::FIELD_ITEM)
     {
       if (!my_strcasecmp(system_charset_info,
@@ -2064,11 +2070,13 @@ find_item_in_list(Item *find, List<Item> &items,
 			      find->full_name(), current_thd->where);
 	    return (Item**) 0;
 	  }
-	  found=li.ref();
+	  found= li.ref();
+	  *counter= i;
 	}
 	else if (!strcmp(((Item_field*) item)->table_name,table_name))
 	{
-	  found=li.ref();
+	  found= li.ref();
+	  *counter= i;
 	  break;
 	}
       }
@@ -2078,7 +2086,8 @@ find_item_in_list(Item *find, List<Item> &items,
 		     !my_strcasecmp(system_charset_info, 
                                     item->name,find->name)))
     {
-      found=li.ref();
+      found= li.ref();
+      *counter= i;
       break;
     }
   }
@@ -2096,30 +2105,26 @@ find_item_in_list(Item *find, List<Item> &items,
 }
 
 /****************************************************************************
-** Check that all given fields exists and fill struct with current data
+** Expand all '*' in given fields
 ****************************************************************************/
 
-int setup_fields(THD *thd, TABLE_LIST *tables, List<Item> &fields,
-		 bool set_query_id, List<Item> *sum_func_list,
-		 bool allow_sum_func)
+int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
+	       List<Item> *sum_func_list,
+	       uint wild_num)
 {
+  if (!wild_num)
+    return 0;
   reg2 Item *item;
   List_iterator<Item> it(fields);
-  DBUG_ENTER("setup_fields");
-
-  thd->set_query_id=set_query_id;
-  thd->allow_sum_func= allow_sum_func;
-  thd->where="field list";
-
-  while ((item=it++))
-  {
+  while ( wild_num && (item= it++))
+  {    
     if (item->type() == Item::FIELD_ITEM && ((Item_field*) item)->field_name &&
 	((Item_field*) item)->field_name[0] == '*')
     {
       uint elem= fields.elements;
       if (insert_fields(thd,tables,((Item_field*) item)->db_name,
 			((Item_field*) item)->table_name, &it))
-	DBUG_RETURN(-1); /* purecov: inspected */
+	return (-1);
       if (sum_func_list)
       {
 	/*
@@ -2129,22 +2134,43 @@ int setup_fields(THD *thd, TABLE_LIST *tables, List<Item> &fields,
 	*/
 	sum_func_list->elements+= fields.elements - elem;
       }
-    }
-    else
-    {
-      if (item->check_cols(1) ||
-	  item->fix_fields(thd, tables, it.ref()))
-	DBUG_RETURN(-1); /* purecov: inspected */
-      item= *(it.ref()); //Item can be chenged in fix fields
-      if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM &&
-	  sum_func_list)
-	item->split_sum_func(*sum_func_list);
-      thd->used_tables|=item->used_tables();
+      wild_num--;
     }
   }
-  DBUG_RETURN(test(thd->fatal_error || thd->net.report_error));
+  return 0;
 }
 
+/****************************************************************************
+** Check that all given fields exists and fill struct with current data
+****************************************************************************/
+
+int setup_fields(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables, 
+		 List<Item> &fields, bool set_query_id,
+		 List<Item> *sum_func_list, bool allow_sum_func)
+{
+  reg2 Item *item;
+  List_iterator<Item> it(fields);
+  DBUG_ENTER("setup_fields");
+
+  thd->set_query_id=set_query_id;
+  thd->allow_sum_func= allow_sum_func;
+  thd->where="field list";
+
+  for (uint i= 0; (item= it++); i++)
+  {
+    if (item->check_cols(1) ||
+	item->fix_fields(thd, tables, it.ref()))
+      DBUG_RETURN(-1); /* purecov: inspected */
+    item= *(it.ref()); //Item can be chenged in fix fields
+    if (ref_pointer_array)
+      ref_pointer_array[i]= item;
+    if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM &&
+	sum_func_list)
+      item->split_sum_func(ref_pointer_array, *sum_func_list);
+    thd->used_tables|=item->used_tables();
+  }
+  DBUG_RETURN(test(thd->fatal_error || thd->net.report_error));
+}
 
 /*
   Remap table numbers if INSERT ... SELECT
@@ -2459,7 +2485,7 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
   create_info.table_charset=default_charset_info;
   DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
 				&create_info, table_list,
-				fields, keys, drop, alter, (ORDER*)0, FALSE,
+				fields, keys, drop, alter, 0, (ORDER*)0, FALSE,
 				DUP_ERROR));
 }
 
@@ -2476,7 +2502,7 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop)
   create_info.table_charset=default_charset_info;
   DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
 				&create_info, table_list,
-				fields, keys, drop, alter, (ORDER*)0, FALSE,
+				fields, keys, drop, alter, 0, (ORDER*)0, FALSE,
 				DUP_ERROR));
 }
 
diff --git a/sql/sql_class.h b/sql/sql_class.h
index ca56d2dcdf5941c28398536ed7df2e7767939059..38a96d1af660a38dc77f959b07813082c0bde4de 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -376,7 +376,7 @@ struct system_variables
   CONVERT *convert_set;
 };
 
-
+void free_tmp_table(THD *thd, TABLE *entry);
 /*
   For each client connection we create a separate thread with THD serving as
   a thread/connection descriptor
@@ -486,6 +486,7 @@ class THD :public ilink {
   CHARSET_INFO *db_charset;   
   CHARSET_INFO *thd_charset;
   List<Item> *possible_loops; // Items that may cause loops in subselects
+  List<TABLE> temporary_tables_should_be_free; // list of temporary tables
   List	     <MYSQL_ERROR> warn_list;  
   uint	     warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
   uint	     total_warn_count, old_total_warn_count;
@@ -640,6 +641,17 @@ class THD :public ilink {
     net.report_error= 0;
   }
   void add_possible_loop(Item *);
+  void free_tmp_tables()
+  {
+    if (temporary_tables_should_be_free.elements)
+    {
+      List_iterator_fast<TABLE> lt(temporary_tables_should_be_free);
+      TABLE *table;
+      while ((table= lt++))
+	free_tmp_table(this,table);
+      temporary_tables_should_be_free.empty();
+    }
+  }
 };
 
 /*
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 88da3e2505ce6907e4572d87bee329121d8d7229..bb1eb1e1dd2e22d15490bd11060641aa2ae3a252 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -92,6 +92,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
   if ((select && select->check_quick(safe_update, limit)) || !limit)
   {
     delete select;
+    free_ulderlayed_joins(thd, &thd->lex.select_lex);
     send_ok(thd,0L);
     DBUG_RETURN(0);				// Nothing to delete
   }
@@ -103,6 +104,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
     if (safe_update && !using_limit)
     {
       delete select;
+      free_ulderlayed_joins(thd, &thd->lex.select_lex);
       send_error(thd,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
       DBUG_RETURN(1);
     }
@@ -124,7 +126,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
 
     table->io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
                                              MYF(MY_FAE | MY_ZEROFILL));
-    if (setup_order(thd, &tables, fields, all_fields, order) ||
+    if (setup_order(thd, 0, &tables, fields, all_fields, order) ||
         !(sortorder=make_unireg_sortorder(order, &length)) ||
         (table->found_records = filesort(thd, table, sortorder, length,
                                         (SQL_SELECT *) 0, HA_POS_ERROR,
@@ -132,6 +134,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
         == HA_POS_ERROR)
     {
       delete select;
+      free_ulderlayed_joins(thd, &thd->lex.select_lex);
       DBUG_RETURN(-1);		// This will force out message
     }
   }
@@ -207,6 +210,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
     thd->lock=0;
   }
   delete select;
+  free_ulderlayed_joins(thd, &thd->lex.select_lex);
   if (error >= 0 || thd->net.report_error)
     send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN: 0);
   else
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index f0df681113395886f58e36a0648211506fa2e738..2949dae3985bbceb5596a7d7097efc407a14930b 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -58,7 +58,8 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, TABLE_LIST *t)
     
   if (!(res=open_and_lock_tables(thd,tables)))
   {
-    if (setup_fields(thd,tables,item_list,0,0,1))
+    if (setup_wild(thd, tables, item_list, 0, sl->with_wild) ||
+	setup_fields(thd, 0, tables, item_list, 0, 0, 1))
     {
       res=-1;
       goto exit;
@@ -87,9 +88,11 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, TABLE_LIST *t)
 
       SELECT_LEX_NODE *save_current_select= lex->current_select;
       lex->current_select= sl;
-      res= mysql_select(thd, tables,  sl->item_list,
-			sl->where, (ORDER *) sl->order_list.first,
-			(ORDER*) sl->group_list.first,
+      res= mysql_select(thd, &sl->ref_pointer_array, tables, sl->with_wild,
+			sl->item_list, sl->where,
+			sl->order_list.elements+sl->group_list.elements,
+			(ORDER *) sl->order_list.first,
+			(ORDER *) sl->group_list.first,
 			sl->having, (ORDER*) NULL,
 			sl->options | thd->options | SELECT_NO_UNLOCK,
 			derived_result, unit, sl, 0);
@@ -122,6 +125,8 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, TABLE_LIST *t)
     }
     if (res)
       free_tmp_table(thd,table);
+    else
+      thd->temporary_tables_should_be_free.push_front(table);
 exit:
     close_thread_tables(thd);
   }
diff --git a/sql/sql_do.cc b/sql/sql_do.cc
index 2eef088da5b0894ff8ef4f4c0b34a86498ed5e4a..f25c4632e1e26c882fc716ff4e12a6b5d2bab5f0 100644
--- a/sql/sql_do.cc
+++ b/sql/sql_do.cc
@@ -25,7 +25,7 @@ int mysql_do(THD *thd, List<Item> &values)
   List_iterator<Item> li(values);
   Item *value;
   DBUG_ENTER("mysql_do");
-  if (setup_fields(thd,0, values, 0, 0, 0))
+  if (setup_fields(thd, 0, 0, values, 0, 0, 0))
     DBUG_RETURN(-1);
   while ((value = li++))
     value->val_int();
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index f45e09cf0bf464aad6f1f9b839ba290cf44844c0..8d5727d9eb1a8dce62318d4818e7640f74110a0c 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -81,7 +81,7 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields,
 
     thd->dupp_field=0;
     if (setup_tables(&table_list) ||
-	setup_fields(thd,&table_list,fields,1,0,0))
+	setup_fields(thd, 0, &table_list,fields,1,0,0))
       return -1;
     if (thd->dupp_field)
     {
@@ -171,10 +171,10 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
   values= its++;
   if (check_insert_fields(thd,table,fields,*values,1) ||
       setup_tables(insert_table_list) ||
-      setup_fields(thd, insert_table_list, *values, 0, 0, 0) ||
+      setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0) ||
       (duplic == DUP_UPDATE &&
-       (setup_fields(thd, insert_table_list, update_fields, 0, 0, 0) ||
-        setup_fields(thd, insert_table_list, update_values, 0, 0, 0))))
+       (setup_fields(thd, 0, insert_table_list, update_fields, 0, 0, 0) ||
+        setup_fields(thd, 0, insert_table_list, update_values, 0, 0, 0))))
     goto abort;
   if (find_real_table_in_list(table_list->next, 
 			      table_list->db, table_list->real_name))
@@ -194,7 +194,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
 		      MYF(0),counter);
       goto abort;
     }
-    if (setup_fields(thd,insert_table_list,*values,0,0,0))
+    if (setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0))
       goto abort;
   }
   its.rewind ();
@@ -381,11 +381,13 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
 	      thd->cuted_fields);
     ::send_ok(thd,info.copied+info.deleted,(ulonglong)id,buff);
   }
+  free_ulderlayed_joins(thd, &thd->lex.select_lex);
   DBUG_RETURN(0);
 
 abort:
   if (lock_type == TL_WRITE_DELAYED)
     end_delayed_insert(thd);
+  free_ulderlayed_joins(thd, &thd->lex.select_lex);
   DBUG_RETURN(-1);
 }
 
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 833f36dbe9f9bba150a4dd9a781964143bd45ae4..0da47b1264975d0616d5ae6c257b0aee2b0897eb 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -944,6 +944,7 @@ void st_select_lex_node::init_query()
   next= master= slave= link_next= 0;
   prev= link_prev= 0;
   dependent= 0;
+  ref_pointer_array= 0;
 }
 
 void st_select_lex_node::init_select()
@@ -967,6 +968,8 @@ void st_select_lex_unit::init_query()
   union_option= 0;
   prepared= optimized= executed= 0;
   item= 0;
+  union_result= 0;
+  table= 0;
 }
 
 void st_select_lex::init_query()
@@ -979,6 +982,7 @@ void st_select_lex::init_query()
   join= 0;
   olap= UNSPECIFIED_OLAP_TYPE;
   having_fix_field= 0;
+  with_wild= 0;
 }
 
 void st_select_lex::init_select()
@@ -1105,12 +1109,12 @@ bool st_select_lex_node::add_item_to_list(THD *thd, Item *item)
 
 bool st_select_lex_node::add_group_to_list(THD *thd, Item *item, bool asc)
 {
-  return 1; 
+  return 1;
 }
 
 bool st_select_lex_node::add_order_to_list(THD *thd, Item *item, bool asc)
-{ 
-  return add_to_list(thd, order_list,item,asc);
+{
+  return add_to_list(thd, order_list, item, asc);
 }
 
 bool st_select_lex_node::add_ftfunc_to_list(Item_func_match *func)
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 0c761baffa3cf71255352e8f095962aec657e386..a8d55ec91d5e198ceb0339bbbdcef705b520f206 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -208,8 +208,11 @@ class st_select_lex_node {
   List<List_item>     expr_list;
   List<List_item>     when_list;      /* WHEN clause (expression) */
   ha_rows select_limit, offset_limit; /* LIMIT clause parameters */
-  bool with_sum_func;
-  bool	create_refs;
+  // Arrays of pointers to top elements of all_fields list
+  Item **ref_pointer_array;
+
+  uint with_sum_func;   /* sum function indicator and number of it */
+  bool create_refs;
   bool dependent;	/* dependent from outer select subselect */
 
   static void *operator new(size_t size)
@@ -269,11 +272,10 @@ class select_union;
 class st_select_lex_unit: public st_select_lex_node {
 protected:
   List<Item> item_list; 
-  List<JOIN*> joins; /* list of *JOINs, to delete it in cleanup() */
   TABLE_LIST result_table_list;
   select_union *union_result;
   TABLE *table; /* temporary table using for appending UNION results */
-  THD *thd;
+
   select_result *result;
   int res;
   bool describe, found_rows_for_union,
@@ -290,6 +292,8 @@ class st_select_lex_unit: public st_select_lex_node {
   ha_rows select_limit_cnt, offset_limit_cnt;
   /* not NULL if union used in subselect, point to subselect item */
   Item_subselect *item;
+  THD *thd;
+
   uint union_option;
 
   void init_query();
@@ -336,6 +340,7 @@ class st_select_lex: public st_select_lex_node
   const char *type; /* type of select for EXPLAIN */
   uint in_sum_expr;
   uint select_number; /* number of select (used for EXPLAIN) */
+  uint with_wild; /* item list contain '*' */
   bool  braces;   	/* SELECT ... UNION (SELECT ... ) <- this braces */
   /* TRUE when having fix field called in processing of this SELECT */
   bool having_fix_field;
diff --git a/sql/sql_list.h b/sql/sql_list.h
index 56e6528f214d55b4a6c33466b2aaa07dc71f1a60..c9f569accf6b0113155899fd35affacc6b6ca11c 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -147,12 +147,20 @@ class base_list :public Sql_alloc {
 
 class base_list_iterator
 {
+protected:
   base_list *list;
   list_node **el,**prev,*current;
+  void sublist(base_list &ls, uint elm)
+  {
+    ls.first= *el;
+    ls.last= list->last;
+    ls.elements= elm;
+  }
 public:
-  base_list_iterator(base_list &list_par) :list(&list_par),el(&list_par.first),
-    prev(0),current(0)
+  base_list_iterator(base_list &list_par) 
+    :list(&list_par), el(&list_par.first), prev(0), current(0)
   {}
+
   inline void *next(void)
   {
     prev=el;
@@ -212,7 +220,6 @@ class base_list_iterator
   friend class error_list_iterator;
 };
 
-
 template <class T> class List :public base_list
 {
 public:
@@ -260,6 +267,10 @@ template <class T> class List_iterator_fast :public base_list_iterator
   List_iterator_fast(List<T> &a) : base_list_iterator(a) {}
   inline T* operator++(int) { return (T*) base_list_iterator::next_fast(); }
   inline void rewind(void)  { base_list_iterator::rewind(); }
+  void sublist(List<T> &list, uint el)
+  {
+    base_list_iterator::sublist(list, el);
+  }
 };
 
 
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 00450a3b86cc040b3b3d6da600ac045ae1525c6d..2e059612014e784879db771c91f6dc90fde328e6 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -117,7 +117,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
   else
   {						// Part field list
     thd->dupp_field=0;
-    if (setup_tables(table_list) || setup_fields(thd,table_list,fields,1,0,0))
+    if (setup_tables(table_list) ||
+	setup_fields(thd, 0, table_list, fields, 1, 0, 0))
       DBUG_RETURN(-1);
     if (thd->dupp_field)
     {
diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc
index 6eb4fbcaaf674d4fdd3ccbd992f8bfce30dea33a..93dff84bf0b50f7f2702670ab4c6cb85f608cb5d 100644
--- a/sql/sql_olap.cc
+++ b/sql/sql_olap.cc
@@ -164,8 +164,10 @@ int handle_olaps(LEX *lex, SELECT_LEX *select_lex)
 
 
   if (setup_tables((TABLE_LIST *)select_lex->table_list.first) ||
-      setup_fields(lex->thd,(TABLE_LIST *)select_lex->table_list.first,select_lex->item_list,1,&all_fields,1) ||
-      setup_fields(lex->thd,(TABLE_LIST *)select_lex->table_list.first,item_list_copy,1,&all_fields,1))
+      setup_fields(lex->thd, 0, (TABLE_LIST *)select_lex->table_list.first,
+		   select_lex->item_list, 1, &all_fields,1) ||
+      setup_fields(lex->thd, 0, (TABLE_LIST *)select_lex->table_list.first,
+		   item_list_copy, 1, &all_fields, 1))
     return -1;
 
   if (select_lex->olap == CUBE_TYPE)
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 90568bfcc5e2f89c49962d5714456a671c99918e..b608221e7f04c0d08c50900543b0675eac49ddef 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1915,6 +1915,7 @@ mysql_execute_command(THD *thd)
 			       &lex->create_info,
 			       tables, lex->create_list,
 			       lex->key_list, lex->drop_list, lex->alter_list,
+			       select_lex->order_list.elements,
                                (ORDER *) select_lex->order_list.first,
 			       lex->drop_primary, lex->duplicates,
 			       lex->alter_keys_onoff, lex->simple_alter);
@@ -2026,8 +2027,8 @@ mysql_execute_command(THD *thd)
       res= mysql_alter_table(thd, NullS, NullS, &create_info,
 			     tables, lex->create_list,
 			     lex->key_list, lex->drop_list, lex->alter_list,
-                             (ORDER *) 0,
-			     0,DUP_ERROR);
+                             0, (ORDER *) 0,
+			     0, DUP_ERROR);
     }
     else
       res = mysql_optimize_table(thd, tables, &lex->check_opt);
@@ -2047,6 +2048,7 @@ mysql_execute_command(THD *thd)
                       select_lex->item_list,
                       lex->value_list,
                       select_lex->where,
+		      select_lex->order_list.elements,
                       (ORDER *) select_lex->order_list.first,
                       select_lex->select_limit,
                       lex->duplicates);
@@ -2246,10 +2248,12 @@ mysql_execute_command(THD *thd)
     if (!thd->fatal_error && (result= new multi_delete(thd,aux_tables,
 						       table_count)))
     {
-      res= mysql_select(thd,select_lex->get_table_list(),
+      res= mysql_select(thd, &select_lex->ref_pointer_array,
+			select_lex->get_table_list(),
+			select_lex->with_wild,
 			select_lex->item_list,
 			select_lex->where,
-			(ORDER *)NULL,(ORDER *)NULL,(Item *)NULL,
+			0, (ORDER *)NULL, (ORDER *)NULL, (Item *)NULL,
 			(ORDER *)NULL,
 			select_lex->options | thd->options |
 			SELECT_NO_JOIN_CACHE,
@@ -2741,6 +2745,7 @@ mysql_execute_command(THD *thd)
     send_ok(thd);
     break;
   }
+  thd->free_tmp_tables();
   thd->proc_info="query end";			// QQ
   if (res < 0)
     send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
@@ -2988,6 +2993,7 @@ mysql_init_query(THD *thd)
   LEX *lex=&thd->lex;
   lex->unit.init_query();
   lex->unit.init_select();
+  lex->unit.thd= thd;
   lex->select_lex.init_query();
   lex->value_list.empty();
   lex->param_list.empty();
@@ -3044,6 +3050,7 @@ mysql_new_select(LEX *lex, bool move_down)
       return 1;
     unit->init_query();
     unit->init_select();
+    unit->thd= lex->thd;
     unit->include_down(lex->current_select);
     select_lex->include_down(unit);
   }
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 9d6e6d75ade4fef8ced00ce1c9c1c136c105cc69..6af2528074c12f919e56c2fdfbeb64d2a93fb1a7 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -365,7 +365,7 @@ static int check_prepare_fields(THD *thd,TABLE *table, List<Item> &fields,
 
     thd->dupp_field=0;
     if (setup_tables(&table_list) ||
-	setup_fields(thd,&table_list,fields,1,0,0))
+	setup_fields(thd, 0, &table_list, fields, 1, 0, 0))
       return -1;
     if (thd->dupp_field)
     {
@@ -446,8 +446,9 @@ static bool mysql_test_upd_fields(PREP_STMT *stmt, TABLE_LIST *table_list,
   if (!(table = open_ltable(thd,table_list,table_list->lock_type)))
     DBUG_RETURN(1);
 
-  if (setup_tables(table_list) || setup_fields(thd,table_list,fields,1,0,0) || 
-      setup_conds(thd,table_list,&conds))      
+  if (setup_tables(table_list) ||
+      setup_fields(thd, 0, table_list, fields, 1, 0, 0) || 
+      setup_conds(thd, table_list, &conds))      
     DBUG_RETURN(1);
 
   /* 
@@ -488,10 +489,11 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables,
   
   thd->used_tables=0;	// Updated by setup_fields
   if (setup_tables(tables) ||
-      setup_fields(thd,tables,fields,1,&all_fields,1) ||
+      setup_fields(thd, 0, tables,fields,1,&all_fields,1) ||
       setup_conds(thd,tables,&conds) ||
-      setup_order(thd,tables,fields,all_fields,order) ||
-      setup_group(thd,tables,fields,all_fields,group,&hidden_group_fields))
+      setup_order(thd, 0, tables, fields, all_fields, order) ||
+      setup_group(thd, 0, tables, fields, all_fields, group,
+		  &hidden_group_fields))
     DBUG_RETURN(1);
 
   if (having)
@@ -502,7 +504,7 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables,
 	|| thd->fatal_error)
       DBUG_RETURN(1);				
     if (having->with_sum_func)
-      having->split_sum_func(all_fields);
+      having->split_sum_func(0, all_fields);
   }
   if (setup_ftfuncs(&thd->lex.select_lex)) 
     DBUG_RETURN(1);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 5b8e20859825649914141bc1947f383a41b1143d..50bea2376c3e205406ad9039f6e4cc00779fe415 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -58,7 +58,7 @@ static bool make_simple_join(JOIN *join,TABLE *tmp_table);
 static bool make_join_select(JOIN *join,SQL_SELECT *select,COND *item);
 static void make_join_readinfo(JOIN *join,uint options);
 static void join_free(JOIN *join, bool full);
-static bool only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables);
+static bool only_eq_ref_tables(JOIN *join, ORDER *order, table_map tables);
 static void update_depend_map(JOIN *join);
 static void update_depend_map(JOIN *join, ORDER *order);
 static ORDER *remove_const(JOIN *join,ORDER *first_order,COND *cond,
@@ -140,8 +140,16 @@ static TABLE *get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables);
 static void calc_group_buffer(JOIN *join,ORDER *group);
 static bool alloc_group_fields(JOIN *join,ORDER *group);
 static bool make_sum_func_list(JOIN *join,List<Item> &fields);
-static bool change_to_use_tmp_fields(List<Item> &func);
-static bool change_refs_to_tmp_fields(THD *thd, List<Item> &func);
+// Create list for using with tempory table
+static bool change_to_use_tmp_fields(Item **ref_pointer_array,
+				     List<Item> &new_list1,
+				     List<Item> &new_list2,
+				     uint elements, List<Item> &items);
+// Create list for using with tempory table
+static bool change_refs_to_tmp_fields(THD *thd, Item **ref_pointer_array,
+				      List<Item> &new_list1,
+				      List<Item> &new_list2,
+				      uint elements, List<Item> &items);
 static void init_tmptable_sum_functions(Item_sum **func);
 static void update_tmptable_sum_func(Item_sum **func,TABLE *tmp_table);
 static void copy_sum_funcs(Item_sum **func_ptr);
@@ -164,17 +172,21 @@ int handle_select(THD *thd, LEX *lex, select_result *result)
   if (select_lex->next_select())
     res=mysql_union(thd,lex,result);
   else
-    res=mysql_select(thd,(TABLE_LIST*) select_lex->table_list.first,
-		     select_lex->item_list,
-		     select_lex->where,
-		     (ORDER*) select_lex->order_list.first,
-		     (ORDER*) select_lex->group_list.first,
-		     select_lex->having,
-		     (ORDER*) lex->proc_list.first,
-		     select_lex->options | thd->options,
-		     result, &(lex->unit), &(lex->select_lex), 0);
+    res= mysql_select(thd, &select_lex->ref_pointer_array,
+		      (TABLE_LIST*) select_lex->table_list.first,
+		      select_lex->with_wild, select_lex->item_list,
+		      select_lex->where,
+		      select_lex->order_list.elements +
+		      select_lex->group_list.elements,
+		      (ORDER*) select_lex->order_list.first,
+		      (ORDER*) select_lex->group_list.first,
+		      select_lex->having,
+		      (ORDER*) lex->proc_list.first,
+		      select_lex->options | thd->options,
+		      result, &(lex->unit), &(lex->select_lex), 0);
   if (res && result)
     result->abort();
+
   if (res || thd->net.report_error)
   {
     send_error(thd, 0, NullS);
@@ -204,7 +216,8 @@ void fix_tables_pointers(SELECT_LEX *select_lex)
 /*
   Inline function to setup clauses without sum functions
 */
-inline int setup_without_group(THD *thd, TABLE_LIST *tables,
+inline int setup_without_group(THD *thd, Item **ref_pointer_array,
+			       TABLE_LIST *tables,
 			       List<Item> &fields,
 			       List<Item> &all_fields,
 			       COND **conds,
@@ -213,10 +226,11 @@ inline int setup_without_group(THD *thd, TABLE_LIST *tables,
 {
   bool save_allow_sum_func= thd->allow_sum_func;
   thd->allow_sum_func= 0;
-  int res= (setup_conds(thd,tables, conds) ||
-	    setup_order(thd,tables, fields, all_fields, order) ||
-	    setup_group(thd,tables, fields, all_fields, group,
-			hidden_group_fields));
+  int res= (setup_conds(thd, tables, conds) ||
+	    setup_order(thd, ref_pointer_array, tables, fields, all_fields,
+			order) ||
+	    setup_group(thd, ref_pointer_array, tables, fields, all_fields,
+			group, hidden_group_fields));
   thd->allow_sum_func= save_allow_sum_func;
   return res;
 }
@@ -232,8 +246,10 @@ inline int setup_without_group(THD *thd, TABLE_LIST *tables,
           0 on success
 */
 int
-JOIN::prepare(TABLE_LIST *tables_init,
-	      COND *conds_init, ORDER *order_init, ORDER *group_init,
+JOIN::prepare(Item ***rref_pointer_array,
+	      TABLE_LIST *tables_init,
+	      uint wild_num, COND *conds_init, uint og_num,
+	      ORDER *order_init, ORDER *group_init,
 	      Item *having_init,
 	      ORDER *proc_param_init, SELECT_LEX *select,
 	      SELECT_LEX_UNIT *unit, bool fake_select_lex)
@@ -254,11 +270,19 @@ JOIN::prepare(TABLE_LIST *tables_init,
   /* Check that all tables, fields, conds and order are ok */
 
   if (setup_tables(tables_list) ||
-      setup_fields(thd,tables_list,fields_list,1,&all_fields,1) ||
-      setup_without_group(thd, tables_list, fields_list, all_fields,
-			  &conds, order, group_list, &hidden_group_fields))
+      setup_wild(thd, tables_list, fields_list, &all_fields, wild_num) ||
+      setup_ref_array(thd, rref_pointer_array, (fields_list.elements +
+						select_lex->with_sum_func +
+						og_num)) ||
+      setup_fields(thd, (*rref_pointer_array), tables_list, fields_list, 1,
+		   &all_fields, 1) ||
+      setup_without_group(thd, (*rref_pointer_array), tables_list, fields_list,
+			  all_fields, &conds, order, group_list, 
+			  &hidden_group_fields))
     DBUG_RETURN(-1);				/* purecov: inspected */
 
+  ref_pointer_array= *rref_pointer_array;
+
   if (having)
   {
     thd->where="having clause";
@@ -270,7 +294,7 @@ JOIN::prepare(TABLE_LIST *tables_init,
     if (having_fix_rc || thd->net.report_error)
       DBUG_RETURN(-1);				/* purecov: inspected */
     if (having->with_sum_func)
-      having->split_sum_func(all_fields);
+      having->split_sum_func(ref_pointer_array, all_fields);
   }
   if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */
     DBUG_RETURN(-1);
@@ -303,7 +327,7 @@ JOIN::prepare(TABLE_LIST *tables_init,
     for (table=tables_list ; table ; table=table->next)
       tables++;
   }
-  procedure=setup_procedure(thd,proc_param,result,fields_list,&error);
+  procedure= setup_procedure(thd, proc_param, result, fields_list, &error);
   if (error)
     DBUG_RETURN(-1);				/* purecov: inspected */
   if (procedure)
@@ -378,6 +402,9 @@ int
 JOIN::optimize()
 {
   DBUG_ENTER("JOIN::optimize");
+  if (optimized)
+    DBUG_RETURN(0);
+  optimized= 1;
 
 #ifdef HAVE_REF_TO_FIELDS			// Not done yet
   /* Add HAVING to WHERE if possible */
@@ -638,7 +665,7 @@ JOIN::optimize()
     we must add the removed reference to the select for the table.
     We only need to do this when we have a simple_order or simple_group
     as in other cases the join is done before the sort.
-    */
+  */
   if ((order || group_list) && join_tab[const_tables].type != JT_ALL &&
       join_tab[const_tables].type != JT_FT &&
       (order && simple_order || group_list && simple_group))
@@ -658,19 +685,117 @@ JOIN::optimize()
   {
     need_tmp=1; simple_order=simple_group=0;	// Force tmp table without sort
   }
+
+  if (select_options & SELECT_DESCRIBE)
+    DBUG_RETURN(0);
+
+  tmp_having= having;
+  having= 0;
+
+  /* Perform FULLTEXT search before all regular searches */
+  init_ftfuncs(thd, select_lex, test(order));
+  /* Create a tmp table if distinct or if the sort is too complicated */
+  if (need_tmp)
+  {
+    DBUG_PRINT("info",("Creating tmp table"));
+    thd->proc_info="Creating tmp table";
+
+    init_items_ref_array();
+    
+    tmp_table_param.hidden_field_count= (all_fields.elements -
+					 fields_list.elements);
+    if (!(exec_tmp_table1 =
+	  create_tmp_table(thd, &tmp_table_param, all_fields,
+			   ((!simple_group && !procedure &&
+			     !(test_flags & TEST_NO_KEY_GROUP)) ?
+			    group_list : (ORDER*) 0),
+			   group_list ? 0 : select_distinct,
+			   group_list && simple_group,
+			   select_options,
+			   (order == 0 || skip_sort_order) ? select_limit :
+			   HA_POS_ERROR)))
+      DBUG_RETURN(1);
+    
+    //thd->temporary_tables_should_be_free.push_front(exec_tmp_table1);
+    if (having && 
+	(sort_and_group || (exec_tmp_table1->distinct && !group_list)))
+      having= tmp_having;
+
+    /* if group or order on first table, sort first */
+    if (group_list && simple_group)
+    {
+      DBUG_PRINT("info",("Sorting for group"));
+      thd->proc_info="Sorting for group";
+      if (create_sort_index(thd, &join_tab[const_tables], group_list,
+			    HA_POS_ERROR, HA_POS_ERROR) ||
+	  make_sum_func_list(this, all_fields) ||
+	  alloc_group_fields(this, group_list))
+	DBUG_RETURN(1);
+      group_list=0;
+    }
+    else
+    {
+      if (make_sum_func_list(this, all_fields))
+	DBUG_RETURN(1);
+      if (!group_list && ! exec_tmp_table1->distinct && order && simple_order)
+      {
+	DBUG_PRINT("info",("Sorting for order"));
+	thd->proc_info="Sorting for order";
+	if (create_sort_index(thd, &join_tab[const_tables], order,
+                              HA_POS_ERROR, HA_POS_ERROR))
+	  DBUG_RETURN(1);
+	order=0;
+      }
+    }
+    
+    /*
+      Optimize distinct when used on some of the tables
+      SELECT DISTINCT t1.a FROM t1,t2 WHERE t1.b=t2.b
+      In this case we can stop scanning t2 when we have found one t1.a
+    */
+
+    if (exec_tmp_table1->distinct)
+    {
+      table_map used_tables= thd->used_tables;
+      JOIN_TAB *join_tab= this->join_tab+tables-1;
+      do
+      {
+	if (used_tables & join_tab->table->map)
+	  break;
+	join_tab->not_used_in_distinct=1;
+      } while (join_tab-- != this->join_tab);
+      /* Optimize "select distinct b from t1 order by key_part_1 limit #" */
+      if (order && skip_sort_order)
+      {
+ 	/* Should always succeed */
+	if (test_if_skip_sort_order(&this->join_tab[const_tables],
+				    order, unit->select_limit_cnt, 0))
+	  order=0;
+      }
+    }
+    
+    if (select_lex != &thd->lex.select_lex &&
+	select_lex->linkage != DERIVED_TABLE_TYPE)
+    {
+      if (!(tmp_join= (JOIN*)thd->alloc(sizeof(JOIN))))
+	DBUG_RETURN(-1);
+      restore_tmp();
+    }
+  }
+
   DBUG_RETURN(0);
 }
 
 /*
-  Global optimization (with subselect) must be here (TODO)
+  Restore values in temporary join
 */
 
-int
-JOIN::global_optimize()
+void JOIN::restore_tmp()
 {
-  return 0;
+  memcpy(tmp_join, this, (size_t) sizeof(JOIN));
 }
 
+
 int
 JOIN::reinit()
 {
@@ -695,6 +820,28 @@ JOIN::reinit()
       func->null_value= 1;
   }
 
+  if (exec_tmp_table1)
+  {
+    exec_tmp_table1->file->extra(HA_EXTRA_RESET_STATE);
+    exec_tmp_table1->file->delete_all_rows();
+    free_io_cache(exec_tmp_table1);
+    memcpy(ref_pointer_array, items0, ref_pointer_array_size);
+  }
+  if (exec_tmp_table2)
+  {
+    exec_tmp_table2->file->extra(HA_EXTRA_RESET_STATE);
+    exec_tmp_table2->file->delete_all_rows();
+    free_io_cache(exec_tmp_table2);
+  }
+  if (items0)
+    memcpy(ref_pointer_array, items0, ref_pointer_array_size);
+
+  tmp_table_param.copy_funcs.empty();
+  tmp_table_param.copy_field= tmp_table_param.copy_field_end= 0;
+
+  if (tmp_join)
+    restore_tmp();
+
   DBUG_RETURN(0);
 }
 
@@ -743,14 +890,11 @@ JOIN::exec()
 			    !group_list,
 			    select_options,
 			    zero_result_cause,
-			    having,procedure,
+			    having, procedure,
 			    unit);
     DBUG_VOID_RETURN;
   }
 
-  Item *having_list = having;
-  having = 0;
-
   if (select_options & SELECT_DESCRIBE)
   {
     if (!order && !no_order)
@@ -761,6 +905,7 @@ JOIN::exec()
 	  test_if_skip_sort_order(&join_tab[const_tables], order,
 				  select_limit, 0))))
       order=0;
+    having= tmp_having;
     select_describe(this, need_tmp,
 		    order != 0 && !skip_sort_order,
 		    select_distinct);
@@ -769,120 +914,85 @@ JOIN::exec()
   }
 
   /* Perform FULLTEXT search before all regular searches */
-  init_ftfuncs(thd, select_lex, test(order));
+  //init_ftfuncs(thd, select_lex, test(order));
+
+  JOIN *curr_join= this;
+  List<Item> *curr_all_fields= &all_fields;
+  List<Item> *curr_fields_list= &fields_list;
+  TABLE *curr_tmp_table= 0;
 
   /* Create a tmp table if distinct or if the sort is too complicated */
   if (need_tmp)
   {
-    DBUG_PRINT("info",("Creating tmp table"));
-    thd->proc_info="Creating tmp table";
-
-    tmp_table_param.hidden_field_count= (all_fields.elements -
-					 fields_list.elements);
-    if (!(exec_tmp_table =
-	  create_tmp_table(thd, &tmp_table_param, all_fields,
-			   ((!simple_group && !procedure &&
-			     !(test_flags & TEST_NO_KEY_GROUP)) ?
-			    group_list : (ORDER*) 0),
-			   group_list ? 0 : select_distinct,
-			   group_list && simple_group,
-			   select_options,
-			   (order == 0 || skip_sort_order) ? select_limit :
-			   HA_POS_ERROR)))
-      DBUG_VOID_RETURN;
-
-    if (having_list && 
-	(sort_and_group || (exec_tmp_table->distinct && !group_list)))
-      having=having_list;
-
-    /* if group or order on first table, sort first */
-    if (group_list && simple_group)
-    {
-      DBUG_PRINT("info",("Sorting for group"));
-      thd->proc_info="Sorting for group";
-      if (create_sort_index(thd, &join_tab[const_tables], group_list,
-			    HA_POS_ERROR, HA_POS_ERROR) ||
-	  make_sum_func_list(this, all_fields) ||
-	  alloc_group_fields(this, group_list))
-	DBUG_VOID_RETURN;
-      group_list=0;
-    }
-    else
-    {
-      if (make_sum_func_list(this, all_fields))
-	DBUG_VOID_RETURN;
-      if (!group_list && ! exec_tmp_table->distinct && order && simple_order)
-      {
-	DBUG_PRINT("info",("Sorting for order"));
-	thd->proc_info="Sorting for order";
-	if (create_sort_index(thd, &join_tab[const_tables], order,
-                              HA_POS_ERROR, HA_POS_ERROR))
-	  DBUG_VOID_RETURN;
-	order=0;
-      }
-    }
-
-    /*
-      Optimize distinct when used on some of the tables
-      SELECT DISTINCT t1.a FROM t1,t2 WHERE t1.b=t2.b
-      In this case we can stop scanning t2 when we have found one t1.a
-    */
-
-    if (exec_tmp_table->distinct)
-    {
-      table_map used_tables= thd->used_tables;
-      JOIN_TAB *join_tab= this->join_tab+tables-1;
-      do
-      {
-	if (used_tables & join_tab->table->map)
-	  break;
-	join_tab->not_used_in_distinct=1;
-      } while (join_tab-- != this->join_tab);
-      /* Optimize "select distinct b from t1 order by key_part_1 limit #" */
-      if (order && skip_sort_order)
-      {
- 	/* Should always succeed */
-	if (test_if_skip_sort_order(&this->join_tab[const_tables],
-				    order, unit->select_limit_cnt, 0))
-	  order=0;
-      }
-    }
-
+    if (tmp_join)
+      curr_join= tmp_join;
+    curr_tmp_table= exec_tmp_table1;
     /* Copy data to the temporary table */
     thd->proc_info= "Copying to tmp table";
-    if ((tmp_error= do_select(this, (List<Item> *) 0, exec_tmp_table, 0)))
+    
+    if ((tmp_error= do_select(curr_join, (List<Item> *) 0, curr_tmp_table, 0)))
     {
       error= tmp_error;
       DBUG_VOID_RETURN;
     }
-    if (having)
-      having= having_list= 0;			// Allready done
-
+    curr_tmp_table->file->info(HA_STATUS_VARIABLE);
+    
+    if (curr_join->having)
+      curr_join->having= curr_join->tmp_having= 0; // Allready done
+    
     /* Change sum_fields reference to calculated fields in tmp_table */
-    if (sort_and_group || exec_tmp_table->group)
+    curr_join->all_fields= *curr_all_fields;
+    if (!items1)
     {
-      if (change_to_use_tmp_fields(all_fields))
-	DBUG_VOID_RETURN;
-      tmp_table_param.field_count+= tmp_table_param.sum_func_count+
-	tmp_table_param.func_count;
-      tmp_table_param.sum_func_count= tmp_table_param.func_count= 0;
+      items1= items0 + all_fields.elements;
+      if (sort_and_group || curr_tmp_table->group)
+      {
+	if (change_to_use_tmp_fields(items1,
+				     tmp_fields_list1, tmp_all_fields1,
+				     fields_list.elements, all_fields))
+	  DBUG_VOID_RETURN;
+      }
+      else
+      {
+	if (change_refs_to_tmp_fields(thd, items1,
+				      tmp_fields_list1, tmp_all_fields1,
+				      fields_list.elements, all_fields))
+	  DBUG_VOID_RETURN;
+      }
+      curr_join->tmp_all_fields1= tmp_all_fields1;
+      curr_join->tmp_fields_list1= tmp_fields_list1;
+      curr_join->items1= items1;
+    }
+    curr_all_fields= &tmp_all_fields1;
+    curr_fields_list= &tmp_fields_list1;
+    memcpy(ref_pointer_array, items1, ref_pointer_array_size);
+    
+    if (sort_and_group || curr_tmp_table->group)
+    {
+      curr_join->tmp_table_param.field_count+= 
+	curr_join->tmp_table_param.sum_func_count+
+	curr_join->tmp_table_param.func_count;
+      curr_join->tmp_table_param.sum_func_count= 
+	curr_join->tmp_table_param.func_count= 0;
     }
     else
     {
-      if (change_refs_to_tmp_fields(thd,all_fields))
-	DBUG_VOID_RETURN;
-      tmp_table_param.field_count+= tmp_table_param.func_count;
-      tmp_table_param.func_count= 0;
+      curr_join->tmp_table_param.field_count+= 
+	curr_join->tmp_table_param.func_count;
+      curr_join->tmp_table_param.func_count= 0;
     }
+    
+    // procedure can't be used inside subselect => we do nothing special for it
     if (procedure)
       procedure->update_refs();
-    if (exec_tmp_table->group)
+    
+    if (curr_tmp_table->group)
     {						// Already grouped
-      if (!order && !no_order)
-	order= group_list;			/* order by group */
-      group_list= 0;
+      if (!curr_join->order && !curr_join->no_order)
+	curr_join->order= curr_join->group_list;  /* order by group */
+      curr_join->group_list= 0;
     }
-
+    
     /*
       If we have different sort & group then we must sort the data by group
       and copy it to another tmp table
@@ -891,142 +1001,199 @@ JOIN::exec()
       like SEC_TO_TIME(SUM(...)).
     */
 
-    if (group_list && (!test_if_subpart(group_list,order) || 
-		       select_distinct) ||
-	(select_distinct &&
-	 tmp_table_param.using_indirect_summary_function))
+    if (curr_join->group_list && (!test_if_subpart(curr_join->group_list,
+						   curr_join->order) || 
+				  curr_join->select_distinct) ||
+	(curr_join->select_distinct &&
+	 curr_join->tmp_table_param.using_indirect_summary_function))
     {					/* Must copy to another table */
-      TABLE *tmp_table2;
       DBUG_PRINT("info",("Creating group table"));
-
+      
       /* Free first data from old join */
-      join_free(this, 0);
-      if (make_simple_join(this, exec_tmp_table))
-	DBUG_VOID_RETURN;
-      calc_group_buffer(this, group_list);
-      count_field_types(&tmp_table_param, all_fields,
-			select_distinct && !group_list);
-      tmp_table_param.hidden_field_count= (all_fields.elements-
-					   fields_list.elements);
-
-      /* group data to new table */
-      if (!(tmp_table2 = create_tmp_table(thd, &tmp_table_param, all_fields,
-					  (ORDER*) 0,
-					  select_distinct && !group_list,
-					  1, select_options, HA_POS_ERROR)))
+      join_free(curr_join, 0);
+      if (make_simple_join(curr_join, curr_tmp_table))
 	DBUG_VOID_RETURN;
+      calc_group_buffer(curr_join, group_list);
+      count_field_types(&curr_join->tmp_table_param, curr_join->tmp_all_fields1,
+			curr_join->select_distinct && !curr_join->group_list);
+      curr_join->tmp_table_param.hidden_field_count= 
+	(curr_join->tmp_all_fields1.elements-
+	 curr_join->tmp_fields_list1.elements);
+      
+      
+      if (exec_tmp_table2)
+	curr_tmp_table= exec_tmp_table2;
+      else
+      {
+	/* group data to new table */
+	if (!(curr_tmp_table=
+	      exec_tmp_table2= create_tmp_table(thd,
+						&curr_join->tmp_table_param,
+						*curr_all_fields,
+						(ORDER*) 0,
+						curr_join->select_distinct && 
+						!curr_join->group_list,
+						1, curr_join->select_options,
+						HA_POS_ERROR)))
+	  DBUG_VOID_RETURN;
+	//thd->temporary_tables_should_be_free.push_front(exec_tmp_table2);
+	curr_join->exec_tmp_table2= exec_tmp_table2;
+      }
       if (group_list)
       {
-	thd->proc_info="Creating sort index";
-	if (create_sort_index(thd, join_tab, group_list, HA_POS_ERROR,
-			      HA_POS_ERROR) ||
-	    alloc_group_fields(this, group_list))
+	thd->proc_info= "Creating sort index";
+	if (create_sort_index(thd, curr_join->join_tab, curr_join->group_list,
+			      HA_POS_ERROR, HA_POS_ERROR) ||
+	    alloc_group_fields(curr_join, curr_join->group_list))
 	{
-	  free_tmp_table(thd,tmp_table2);	/* purecov: inspected */
 	  DBUG_VOID_RETURN;
 	}
-	group_list= 0;
+	curr_join->group_list= 0;
       }
+      
       thd->proc_info="Copying to group table";
       tmp_error= -1;
-      if (make_sum_func_list(this, all_fields) ||
-	  (tmp_error=do_select(this, (List<Item> *) 0,tmp_table2,0)))
+      if (make_sum_func_list(curr_join, *curr_all_fields) ||
+	  (tmp_error= do_select(curr_join, (List<Item> *) 0, curr_tmp_table,
+				0)))
       {
-	error=tmp_error;
-	free_tmp_table(thd,tmp_table2);
+	error= tmp_error;
 	DBUG_VOID_RETURN;
       }
-      end_read_record(&join_tab->read_record);
-      free_tmp_table(thd,exec_tmp_table);
-      const_tables= tables;                   // Mark free for join_free()
-      exec_tmp_table= tmp_table2;
-      join_tab[0].table= 0;                   // Table is freed
-
-      if (change_to_use_tmp_fields(all_fields)) // No sum funcs anymore
-	DBUG_VOID_RETURN;
-      tmp_table_param.field_count+= tmp_table_param.sum_func_count;
-      tmp_table_param.sum_func_count= 0;
-    }
-
-    if (exec_tmp_table->distinct)
-      select_distinct=0;			/* Each row is unique */
-
-    join_free(this, 0);				/* Free quick selects */
+      end_read_record(&curr_join->join_tab->read_record);
+      curr_join->const_tables= curr_join->tables; // Mark free for join_free()
+      curr_join->join_tab[0].table= 0;           // Table is freed
+      
+      // No sum funcs anymore
+      if (!items2)
+      {
+	items2= items1 + all_fields.elements;
+	if (change_to_use_tmp_fields(items2,
+				     tmp_fields_list2, tmp_all_fields2, 
+				     fields_list.elements, tmp_all_fields1))
+	  DBUG_VOID_RETURN;
+	curr_join->tmp_fields_list2= tmp_fields_list2;
+	curr_join->tmp_all_fields2= tmp_all_fields2;
+      }
+      curr_fields_list= &curr_join->tmp_fields_list2;
+      curr_all_fields= &curr_join->tmp_all_fields2;
+      memcpy(ref_pointer_array, items2, ref_pointer_array_size);
+      curr_join->tmp_table_param.field_count+= 
+	curr_join->tmp_table_param.sum_func_count;
+      curr_join->tmp_table_param.sum_func_count= 0;
+    }
+    if (curr_tmp_table->distinct)
+      curr_join->select_distinct=0;		/* Each row is unique */
+    
+    join_free(curr_join, 0);			/* Free quick selects */
     if (select_distinct && ! group_list)
     {
       thd->proc_info="Removing duplicates";
-      if (having_list)
-	having_list->update_used_tables();
-      if (remove_duplicates(this, exec_tmp_table, fields_list, having_list))
+      if (curr_join->tmp_having)
+	curr_join->tmp_having->update_used_tables();
+      if (remove_duplicates(curr_join, curr_tmp_table,
+			    curr_join->fields_list, curr_join->tmp_having))
 	DBUG_VOID_RETURN;
-      having_list=0;
-      select_distinct=0;
+      curr_join->tmp_having=0;
+      curr_join->select_distinct=0;
     }
-    exec_tmp_table->reginfo.lock_type=TL_UNLOCK;
-    if (make_simple_join(this, exec_tmp_table))
+    curr_tmp_table->reginfo.lock_type= TL_UNLOCK;
+    if (make_simple_join(curr_join, curr_tmp_table))
       DBUG_VOID_RETURN;
-    calc_group_buffer(this, group_list);
-    count_field_types(&tmp_table_param, all_fields, 0);
+    calc_group_buffer(curr_join, curr_join->group_list);
+    count_field_types(&curr_join->tmp_table_param, *curr_all_fields, 0);
+    
   }
   if (procedure)
   {
-    if (procedure->change_columns(fields_list) ||
-	result->prepare(fields_list, unit))
+    if (procedure->change_columns(*curr_fields_list) ||
+	result->prepare(*curr_fields_list, unit))
       DBUG_VOID_RETURN;
-    count_field_types(&tmp_table_param, all_fields, 0);
+    count_field_types(&curr_join->tmp_table_param, *curr_all_fields, 0);
   }
-  if (group || tmp_table_param.sum_func_count ||
+  
+  if (curr_join->group || curr_join->tmp_table_param.sum_func_count ||
       (procedure && (procedure->flags & PROC_GROUP)))
   {
-    alloc_group_fields(this, group_list);
-    setup_copy_fields(thd, &tmp_table_param,all_fields);
-    if (make_sum_func_list(this, all_fields) || thd->fatal_error)
+    alloc_group_fields(curr_join, curr_join->group_list);
+    if (!items3)
+    {
+      if (!items0)
+	init_items_ref_array();
+      items3= ref_pointer_array + (all_fields.elements*4);
+      setup_copy_fields(thd, &curr_join->tmp_table_param,
+			items3, tmp_fields_list3, tmp_all_fields3,
+			curr_fields_list->elements, *curr_all_fields);
+      tmp_table_param.save_copy_funcs= curr_join->tmp_table_param.copy_funcs;
+      tmp_table_param.save_copy_field= curr_join->tmp_table_param.copy_field;
+      tmp_table_param.save_copy_field_end=
+	curr_join->tmp_table_param.copy_field_end;
+      curr_join->tmp_all_fields3= tmp_all_fields3;
+      curr_join->tmp_fields_list3= tmp_fields_list3;
+    }
+    else
+    {
+      curr_join->tmp_table_param.copy_funcs= tmp_table_param.save_copy_funcs;
+      curr_join->tmp_table_param.copy_field= tmp_table_param.save_copy_field;
+      curr_join->tmp_table_param.copy_field_end=
+	tmp_table_param.save_copy_field_end;
+    }
+    curr_fields_list= &tmp_fields_list3;
+    curr_all_fields= &tmp_all_fields3;
+    memcpy(ref_pointer_array, items3, ref_pointer_array_size);
+
+    if (make_sum_func_list(curr_join, *curr_all_fields) ||
+	thd->fatal_error)
       DBUG_VOID_RETURN;
   }
-  if (group_list || order)
+  if (curr_join->group_list || curr_join->order)
   {
     DBUG_PRINT("info",("Sorting for send_fields"));
     thd->proc_info="Sorting result";
     /* If we have already done the group, add HAVING to sorted table */
-    if (having_list && ! group_list && ! sort_and_group)
+    if (curr_join->tmp_having && ! curr_join->group_list && 
+	! curr_join->sort_and_group)
     {
-      having_list->update_used_tables();  // Some tables may have been const
-      JOIN_TAB *table= &join_tab[const_tables];
-      table_map used_tables= const_table_map | table->table->map;
+      // Some tables may have been const
+      curr_join->tmp_having->update_used_tables();
+      JOIN_TAB *table= &curr_join->join_tab[const_tables];
+      table_map used_tables= curr_join->const_table_map | table->table->map;
 
-      Item* sort_table_cond= make_cond_for_table(having_list, used_tables,
+      Item* sort_table_cond= make_cond_for_table(curr_join->tmp_having,
+						 used_tables,
 						 used_tables);
       if (sort_table_cond)
       {
 	if (!table->select)
-	  if (!(table->select=new SQL_SELECT))
+	  if (!(table->select= new SQL_SELECT))
 	    DBUG_VOID_RETURN;
 	if (!table->select->cond)
-	  table->select->cond=sort_table_cond;
+	  table->select->cond= sort_table_cond;
 	else					// This should never happen
-	  if (!(table->select->cond=new Item_cond_and(table->select->cond,
-						      sort_table_cond)))
+	  if (!(table->select->cond= new Item_cond_and(table->select->cond,
+						       sort_table_cond)))
 	    DBUG_VOID_RETURN;
 	table->select_cond=table->select->cond;
 	table->select_cond->top_level_item();
 	DBUG_EXECUTE("where",print_where(table->select->cond,
 					 "select and having"););
-	having_list= make_cond_for_table(having_list, ~ (table_map) 0,
-					 ~used_tables);
+	curr_join->tmp_having= make_cond_for_table(curr_join->tmp_having,
+						   ~ (table_map) 0,
+						   ~used_tables);
 	DBUG_EXECUTE("where",print_where(conds,"having after sort"););
       }
     }
     {
       if (group)
-	select_limit= HA_POS_ERROR;
+	curr_join->select_limit= HA_POS_ERROR;
       else
       {
 	/*
 	  We can abort sorting after thd->select_limit rows if we there is no
 	  WHERE clause for any tables after the sorted one.
 	*/
-	JOIN_TAB *table= &join_tab[const_tables+1];
-	JOIN_TAB *end_table= &join_tab[tables];
+	JOIN_TAB *table= &curr_join->join_tab[const_tables+1];
+	JOIN_TAB *end_table= &curr_join->join_tab[tables];
 	for (; table < end_table ; table++)
 	{
 	  /*
@@ -1038,21 +1205,22 @@ JOIN::exec()
 	  if (table->select_cond || (table->keyuse && !table->on_expr))
 	  {
 	    /* We have to sort all rows */
-	    select_limit= HA_POS_ERROR;
+	    curr_join->select_limit= HA_POS_ERROR;
 	    break;
 	  }
 	}
       }
-      if (create_sort_index(thd, &join_tab[const_tables],
-			    group_list ? group_list : order,
-			    select_limit, unit->select_limit_cnt))
-	DBUG_VOID_RETURN;   
+      if (create_sort_index(thd, &curr_join->join_tab[curr_join->const_tables],
+			    curr_join->group_list ? 
+			    curr_join->group_list : curr_join->order,
+			    curr_join->select_limit, unit->select_limit_cnt))
+	DBUG_VOID_RETURN;
     }
   }
-  having=having_list;				// Actually a parameter
+  curr_join->having= curr_join->tmp_having;
   thd->proc_info="Sending data";
   error= thd->net.report_error ||
-    do_select(this, &fields_list, NULL, procedure);
+    do_select(curr_join, curr_fields_list, NULL, procedure);
   DBUG_VOID_RETURN;
 }
 
@@ -1065,10 +1233,18 @@ JOIN::cleanup(THD *thd)
 {
   DBUG_ENTER("JOIN::cleanup");
 
+  select_lex->join= 0;
+
+  if (tmp_join)
+    memcpy(this, tmp_join, sizeof(tmp_join));
+
+
   lock=0;                                     // It's faster to unlock later
   join_free(this, 1);
-  if (exec_tmp_table)
-    free_tmp_table(thd, exec_tmp_table);
+   if (exec_tmp_table1)
+     free_tmp_table(thd, exec_tmp_table1);
+   if (exec_tmp_table2)
+     free_tmp_table(thd, exec_tmp_table2);
   delete select;
   delete_dynamic(&keyuse);
   delete procedure;
@@ -1076,18 +1252,7 @@ JOIN::cleanup(THD *thd)
        unit != 0;
        unit= unit->next_unit())
   {
-    for (SELECT_LEX *sl= unit->first_select();
-	 sl != 0;
-	 sl= sl->next_select())
-    {
-      if (sl->join)
-      {
-	int err= sl->join->cleanup(thd);
-	if (err)
-	  error= err;
-	sl->join= 0;
-      }
-    }
+    error|= unit->cleanup();
   }
   DBUG_RETURN(error);
 }
@@ -1111,11 +1276,12 @@ bool JOIN::check_loop(uint id)
 }
 
 int
-mysql_select(THD *thd, TABLE_LIST *tables, List<Item> &fields, COND *conds,
-	     ORDER *order, ORDER *group,Item *having, ORDER *proc_param,
-	     ulong select_options, select_result *result, 
-	     SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex,
-	     bool fake_select_lex)
+mysql_select(THD *thd, Item ***rref_pointer_array,
+	     TABLE_LIST *tables, uint wild_num, List<Item> &fields, 
+	     COND *conds, uint og_num,  ORDER *order, ORDER *group,
+	     Item *having, ORDER *proc_param, ulong select_options,
+	     select_result *result, SELECT_LEX_UNIT *unit,
+	     SELECT_LEX *select_lex, bool fake_select_lex)
 {
   int err;
   bool free_join= 1;
@@ -1140,7 +1306,8 @@ mysql_select(THD *thd, TABLE_LIST *tables, List<Item> &fields, COND *conds,
     thd->proc_info="init";
     thd->used_tables=0;                         // Updated by setup_fields
 
-    if (join->prepare(tables, conds, order, group, having, proc_param,
+    if (join->prepare(rref_pointer_array, tables, wild_num,
+		      conds, og_num, order, group, having, proc_param,
 		      select_lex, unit, fake_select_lex))
     {
       DBUG_RETURN(-1);
@@ -1172,7 +1339,7 @@ mysql_select(THD *thd, TABLE_LIST *tables, List<Item> &fields, COND *conds,
     goto err;					// 1
   }
 
-  if (thd->net.report_error || (free_join && join->global_optimize()))
+  if (thd->net.report_error)
     goto err;
 
   join->exec();
@@ -1180,10 +1347,14 @@ mysql_select(THD *thd, TABLE_LIST *tables, List<Item> &fields, COND *conds,
 err:
   if (free_join)
   {
-    thd->limit_found_rows = join->send_records;
-    thd->examined_row_count = join->examined_rows;
+    JOIN *curr_join= (join->need_tmp&&join->tmp_join?
+		      (join->tmp_join->error=join->error,join->tmp_join):
+		      join);
+    
+    thd->limit_found_rows= curr_join->send_records;
+    thd->examined_row_count= curr_join->examined_rows;
     thd->proc_info="end";
-    err= (fake_select_lex ? join->error : join->cleanup(thd));
+    err= (fake_select_lex ? curr_join->error : join->cleanup(thd));
     if (thd->net.report_error)
       err= -1;
     delete join;
@@ -3068,6 +3239,7 @@ join_free(JOIN *join, bool full)
 	delete tab->select;
 	delete tab->quick;
 	x_free(tab->cache.buff);
+	tab->cache.buff= 0;
 	if (tab->table)
 	{
 	  if (tab->table->key_read)
@@ -3094,9 +3266,12 @@ join_free(JOIN *join, bool full)
     mysql_unlock_read_tables(join->thd, join->lock);// Don't free join->lock
     join->lock=0;
   }
-  join->group_fields.delete_elements();
-  join->tmp_table_param.copy_funcs.delete_elements();
-  join->tmp_table_param.cleanup();
+  if (full)
+  {
+    join->group_fields.delete_elements();
+    join->tmp_table_param.copy_funcs.delete_elements();
+    join->tmp_table_param.cleanup();
+  }
   DBUG_VOID_RETURN;
 }
 
@@ -4124,9 +4299,11 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
   {
     uint alloc_length=ALIGN_SIZE(reclength+MI_UNIQUE_HASH_LENGTH+1);
     table->rec_buff_length=alloc_length;
-    if (!(table->record[0]= (byte *) my_malloc(alloc_length*3, MYF(MY_WME))))
+    byte * t;
+    if (!(t= table->record[0]= (byte *) my_malloc(alloc_length*3, MYF(MY_WME))))
       goto err;
-    table->record[1]= table->record[0]+alloc_length;
+    table->record[1]= t+alloc_length;
+    //table->record[1]= table->record[0]+alloc_length;
     table->record[2]= table->record[1]+alloc_length;
   }
   copy_func[0]=0;				// End marker
@@ -6752,31 +6929,32 @@ cp_buffer_from_ref(TABLE_REF *ref)
 */
 
 static int
-find_order_in_list(THD *thd,TABLE_LIST *tables,ORDER *order,List<Item> &fields,
+find_order_in_list(THD *thd, Item **ref_pointer_array,
+		   TABLE_LIST *tables,ORDER *order, List<Item> &fields,
 		   List<Item> &all_fields)
 {
   if ((*order->item)->type() == Item::INT_ITEM)
   {						/* Order by position */
     Item *item=0;
-    List_iterator<Item> li(fields);
 
-    for (uint count= (uint) ((Item_int*) (*order->item))->value ;
-	 count-- && (item=li++) ;) ;
-    if (!item)
+    uint count= (uint) ((Item_int*) (*order->item))->value;
+    if (count > fields.elements)
     {
       my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),
 		      MYF(0),(*order->item)->full_name(),
 	       thd->where);
       return 1;
     }
-    order->item=li.ref();
-    order->in_field_list=1;
+    order->item= ref_pointer_array + count-1;
+    order->in_field_list= 1;
     return 0;
   }
-  Item **item= find_item_in_list(*order->item, fields, IGNORE_ERRORS);
+  uint counter= 0;
+  Item **item= find_item_in_list(*order->item, fields, &counter,
+				 IGNORE_ERRORS);
   if (item)
   {
-    order->item=item;				// use it
+    order->item= ref_pointer_array + counter-1;
     order->in_field_list=1;
     return 0;
   }
@@ -6785,24 +6963,43 @@ find_order_in_list(THD *thd,TABLE_LIST *tables,ORDER *order,List<Item> &fields,
   if (it->check_cols(1) || it->fix_fields(thd, tables, order->item) ||
       thd->fatal_error)
     return 1;					// Wrong field
-  all_fields.push_front(*order->item);		// Add new field to field list
-  order->item=(Item**) all_fields.head_ref();
+  uint el= all_fields.elements;
+  all_fields.push_front(it);		        // Add new field to field list
+  ref_pointer_array[el]= it;
+  order->item= ref_pointer_array + el;
   return 0;
 }
 
+/*
+  Allocate array of references to address all_fileds list elements
+*/
+
+int setup_ref_array(THD* thd, Item ***rref_pointer_array, uint elements)
+{
+  if (*rref_pointer_array)
+    return 0;
+
+  /* TODO: may be better allocate only one and all other on demand? */
+  if (!(*rref_pointer_array= 
+	(Item **)thd->alloc(sizeof(Item*) * elements * 5)))
+    return -1;
+  else 
+    return 0;
+}
 
 /*
   Change order to point at item in select list. If item isn't a number
   and doesn't exits in the select list, add it the the field list.
 */
 
-int setup_order(THD *thd,TABLE_LIST *tables,List<Item> &fields,
-	     List<Item> &all_fields, ORDER *order)
+int setup_order(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
+		List<Item> &fields, List<Item> &all_fields, ORDER *order)
 {
   thd->where="order clause";
   for (; order; order=order->next)
   {
-    if (find_order_in_list(thd,tables,order,fields,all_fields))
+    if (find_order_in_list(thd, ref_pointer_array, tables, order, fields,
+			   all_fields))
       return 1;
   }
   return 0;
@@ -6810,8 +7007,9 @@ int setup_order(THD *thd,TABLE_LIST *tables,List<Item> &fields,
 
 
 int
-setup_group(THD *thd,TABLE_LIST *tables,List<Item> &fields,
-	    List<Item> &all_fields, ORDER *order, bool *hidden_group_fields)
+setup_group(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
+	    List<Item> &fields, List<Item> &all_fields, ORDER *order,
+	    bool *hidden_group_fields)
 {
   *hidden_group_fields=0;
   if (!order)
@@ -6829,7 +7027,8 @@ setup_group(THD *thd,TABLE_LIST *tables,List<Item> &fields,
   thd->where="group statement";
   for (; order; order=order->next)
   {
-    if (find_order_in_list(thd,tables,order,fields,all_fields))
+    if (find_order_in_list(thd, ref_pointer_array, tables, order, fields,
+			   all_fields))
       return 1;
     (*order->item)->marker=1;		/* Mark found */
     if ((*order->item)->with_sum_func)
@@ -6873,9 +7072,11 @@ setup_new_fields(THD *thd,TABLE_LIST *tables,List<Item> &fields,
   DBUG_ENTER("setup_new_fields");
 
   thd->set_query_id=1;				// Not really needed, but...
+  uint counter= 0;
   for (; new_field ; new_field= new_field->next)
   {
-    if ((item= find_item_in_list(*new_field->item, fields, IGNORE_ERRORS)))
+    if ((item= find_item_in_list(*new_field->item, fields, &counter,
+				 IGNORE_ERRORS)))
       new_field->item=item;			/* Change to shared Item */
     else
     {
@@ -7126,41 +7327,55 @@ test_if_group_changed(List<Item_buff> &list)
 */
 
 bool
-setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields)
+setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
+		  Item **ref_pointer_array,
+		  List<Item> &new_list1, List<Item> &new_list2,
+		  uint elements, List<Item> &fields)
 {
   Item *pos;
-  List_iterator<Item> li(fields);
+  List_iterator_fast<Item> li(fields);
   Copy_field *copy;
   DBUG_ENTER("setup_copy_fields");
+  new_list1.empty();
+  new_list2.empty();
+  List_iterator_fast<Item> itr(new_list2);
+
+  uint i, border= fields.elements - elements;
 
   if (!(copy=param->copy_field= new Copy_field[param->field_count]))
     goto err2;
 
   param->copy_funcs.empty();
-  while ((pos=li++))
+  for (i= 0; (pos= li++); i++)
   {
     if (pos->type() == Item::FIELD_ITEM)
     {
-      Item_field *item=(Item_field*) pos;
+      Item_field *item;
+      if (!(item= new Item_field(*((Item_field*) pos))))
+	goto err;
+      pos= item;
       if (item->field->flags & BLOB_FLAG)
       {
-	if (!(pos=new Item_copy_string(pos)))
+	if (!(pos= new Item_copy_string(pos)))
 	  goto err;
-	VOID(li.replace(pos));
 	if (param->copy_funcs.push_back(pos))
 	  goto err;
-	continue;
       }
-
-      /* set up save buffer and change result_field to point at saved value */
-      Field *field= item->field;
-      item->result_field=field->new_field(&thd->mem_root,field->table);
-      char *tmp=(char*) sql_alloc(field->pack_length()+1);
-      if (!tmp)
-	goto err;
-      copy->set(tmp, item->result_field);
-      item->result_field->move_field(copy->to_ptr,copy->to_null_ptr,1);
-      copy++;
+      else
+      {
+	/* 
+	   set up save buffer and change result_field to point at 
+	   saved value
+	*/
+	Field *field= item->field;
+	item->result_field=field->new_field(&thd->mem_root,field->table);
+	char *tmp=(char*) sql_alloc(field->pack_length()+1);
+	if (!tmp)
+	  goto err;
+	copy->set(tmp, item->result_field);
+	item->result_field->move_field(copy->to_ptr,copy->to_null_ptr,1);
+	copy++;
+      }
     }
     else if ((pos->type() == Item::FUNC_ITEM ||
 	      pos->type() == Item::COND_ITEM) &&
@@ -7174,12 +7389,18 @@ setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields)
       */
       if (!(pos=new Item_copy_string(pos)))
 	goto err;
-      VOID(li.replace(pos));
       if (param->copy_funcs.push_back(pos))
 	goto err;
     }
+    new_list2.push_back(pos);
+    ref_pointer_array[((i < border)? fields.elements-i-1 : i-border)]=
+      pos;
   }
   param->copy_field_end= copy;
+
+  for (i= 0; i < border; i++)
+    itr++;
+  itr.sublist(new_list1, elements);
   DBUG_RETURN(0);
 
  err:
@@ -7244,51 +7465,63 @@ make_sum_func_list(JOIN *join,List<Item> &fields)
 
 
 /*
-  Change all funcs and sum_funcs to fields in tmp table
+  Change all funcs and sum_funcs to fields in tmp table,  and create
+  new list of all items
 */
 
 static bool
-change_to_use_tmp_fields(List<Item> &items)
+change_to_use_tmp_fields(Item **ref_pointer_array,
+			 List<Item> &new_list1, List<Item> &new_list2,
+			 uint elements, List<Item> &items)
 {
-  List_iterator<Item> it(items);
+  List_iterator_fast<Item> it(items);
   Item *item_field,*item;
+  new_list1.empty();
+  new_list2.empty();
 
-  while ((item=it++))
+  uint i, border= items.elements - elements;
+  for (i= 0; (item= it++); i++)
   {
     Field *field;
+    
     if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM)
-      continue;
-    if (item->type() == Item::FIELD_ITEM)
-    {
-      ((Item_field*) item)->field=
-	((Item_field*) item)->result_field;
-    }
-    else if ((field=item->tmp_table_field()))
-    {
-      if (item->type() == Item::SUM_FUNC_ITEM && field->table->group)
-	item_field=((Item_sum*) item)->result_item(field);
-      else
-	item_field=(Item*) new Item_field(field);
-      if (!item_field)
-	return TRUE;				// Fatal error
-      item_field->name=item->name;		/*lint -e613 */
-#ifndef DBUG_OFF
-      if (_db_on_ && !item_field->name)
+      item_field= item;
+    else
+      if (item->type() == Item::FIELD_ITEM)
       {
-	char buff[256];
-	String str(buff,sizeof(buff),default_charset_info);
-	str.length(0);
-	item->print(&str);
-	item_field->name=sql_strmake(str.ptr(),str.length());
+	item_field= item->get_tmp_table_item();
       }
+      else if ((field= item->tmp_table_field()))
+      {
+	if (item->type() == Item::SUM_FUNC_ITEM && field->table->group)
+	  item_field= ((Item_sum*) item)->result_item(field);
+	else
+	  item_field= (Item*) new Item_field(field);
+	if (!item_field)
+	  return TRUE;				// Fatal error
+	item_field->name= item->name;		/*lint -e613 */
+#ifndef DBUG_OFF
+	if (_db_on_ && !item_field->name)
+	{
+	  char buff[256];
+	  String str(buff,sizeof(buff),default_charset_info);
+	  str.length(0);
+	  item->print(&str);
+	  item_field->name= sql_strmake(str.ptr(),str.length());
+	}
 #endif
-#ifdef DELETE_ITEMS
-      delete it.replace(item_field);		/*lint -e613 */
-#else
-      (void) it.replace(item_field);		/*lint -e613 */
-#endif
-    }
+      }
+      else
+	item_field= item;
+    new_list2.push_back(item_field);
+    ref_pointer_array[((i < border)? items.elements-i-1 : i-border)]=
+      item_field;
   }
+
+  List_iterator_fast<Item> itr(new_list2);
+  for (i= 0; i < border; i++)
+    itr++;
+  itr.sublist(new_list1, elements);
   return FALSE;
 }
 
@@ -7299,52 +7532,29 @@ change_to_use_tmp_fields(List<Item> &items)
 */
 
 static bool
-change_refs_to_tmp_fields(THD *thd,List<Item> &items)
+change_refs_to_tmp_fields(THD *thd, Item **ref_pointer_array,
+			  List<Item> &new_list1,
+			  List<Item> &new_list2, uint elements,
+			  List<Item> &items)
 {
-  List_iterator<Item> it(items);
-  Item *item;
+  List_iterator_fast<Item> it(items);
+  Item *item, *new_item;
+  new_list1.empty();
+  new_list2.empty();
 
-  while ((item= it++))
+  uint i, border= items.elements - elements;
+  for (i= 0; (item= it++); i++)
   {
-    if (item->type() == Item::SUM_FUNC_ITEM)
-    {
-      if (!item->const_item())
-      {
-	Item_sum *sum_item= (Item_sum*) item;
-	if (sum_item->result_field)		// If not a const sum func
-	{
-	  Field *result_field=sum_item->result_field;
-	  for (uint i=0 ; i < sum_item->arg_count ; i++)
-	  {
-	    Item *arg= sum_item->args[i];
-	    if (!arg->const_item())
-	    {
-	      if (arg->type() == Item::FIELD_ITEM)
-		((Item_field*) arg)->field= result_field++;
-	      else
-		sum_item->args[i]= new Item_field(result_field++);
-	    }
-	  }
-	}
-      }
-    }
-    else if (item->with_sum_func)
-      continue;
-    else if ((item->type() == Item::FUNC_ITEM ||
-	      item->type() == Item::COND_ITEM) &&
-	     !item->const_item())
-    {						/* All funcs are stored */
-#ifdef DELETE_ITEMS
-      delete it.replace(new Item_field(((Item_func*) item)->result_field));
-#else
-      (void) it.replace(new Item_field(((Item_func*) item)->result_field));
-#endif
-    }
-    else if (item->type() == Item::FIELD_ITEM)	/* Change refs */
-    {
-      ((Item_field*)item)->field=((Item_field*) item)->result_field;
-    }
+    new_list2.push_back(new_item= item->get_tmp_table_item());
+    ref_pointer_array[((i < border)? items.elements-i-1 : i-border)]=
+      new_item;
   }
+
+  List_iterator_fast<Item> itr(new_list2);
+  for (i= 0; i < border; i++)
+    itr++;
+  itr.sublist(new_list1, elements);
+
   return thd->fatal_error;
 }
 
@@ -7683,9 +7893,12 @@ int mysql_explain_select(THD *thd, SELECT_LEX *select_lex, char const *type,
   select_lex->type= type;
   thd->lex.current_select= select_lex;
   SELECT_LEX_UNIT *unit=  select_lex->master_unit();
-  int res= mysql_select(thd,(TABLE_LIST*) select_lex->table_list.first,
-			select_lex->item_list,
+  int res= mysql_select(thd, &select_lex->ref_pointer_array,
+			(TABLE_LIST*) select_lex->table_list.first,
+			select_lex->with_wild, select_lex->item_list,
 			select_lex->where,
+			select_lex->order_list.elements +
+			select_lex->group_list.elements,
 			(ORDER*) select_lex->order_list.first,
 			(ORDER*) select_lex->group_list.first,
 			select_lex->having,
@@ -7694,4 +7907,17 @@ int mysql_explain_select(THD *thd, SELECT_LEX *select_lex, char const *type,
 			result, unit, select_lex, 0);
   DBUG_RETURN(res);
 }
+
+/*
   
+*/
+
+void free_ulderlayed_joins(THD *thd, SELECT_LEX *select)
+{
+  for (SELECT_LEX_UNIT *unit= select->first_inner_unit();
+       unit;
+       unit= unit->next_unit())
+    unit->cleanup();
+}
+
+
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 1fbe20528310bf21c54a5a0beb53a8888988eff4..20972635d33b46b525f0a061810a2810d0014642 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -119,8 +119,10 @@ class TMP_TABLE_PARAM :public Sql_alloc
 {
  public:
   List<Item> copy_funcs;
+  List<Item> save_copy_funcs;
   List_iterator_fast<Item> copy_funcs_it;
   Copy_field *copy_field, *copy_field_end;
+  Copy_field *save_copy_field, *save_copy_field_end;
   byte	    *group_buff;
   Item_result_field **funcs;
   MI_COLUMNDEF *recinfo,*start_recinfo;
@@ -166,10 +168,13 @@ class JOIN :public Sql_alloc
   List<Item> *fields;
   List<Item_buff> group_fields;
   TABLE    *tmp_table;
+  // used to store 2 possible tmp table of SELECT
+  TABLE    *exec_tmp_table1, *exec_tmp_table2;
   THD	   *thd;
   Item_sum  **sum_funcs;
   Procedure *procedure;
   Item	    *having;
+  Item      *tmp_having; // To store Having when processed tenporary table
   uint	    select_options;
   select_result *result;
   TMP_TABLE_PARAM tmp_table_param;
@@ -178,6 +183,8 @@ class JOIN :public Sql_alloc
   SELECT_LEX_UNIT *unit;
   // select that processed
   SELECT_LEX *select_lex;
+  
+  JOIN *tmp_join; // copy of this JOIN to be used with temporary tables
 
   bool select_distinct, //Is select distinct?
     no_order, simple_order, simple_group,
@@ -186,7 +193,11 @@ class JOIN :public Sql_alloc
     buffer_result;
   DYNAMIC_ARRAY keyuse;
   Item::cond_result cond_value;
-  List<Item> all_fields;
+  List<Item> all_fields; // to store all fields that used in query
+  //Above list changed to use temporary table
+  List<Item> tmp_all_fields1, tmp_all_fields2, tmp_all_fields3;
+  //Part, shared with list above, emulate following list
+  List<Item> tmp_fields_list1, tmp_fields_list2, tmp_fields_list3;
   List<Item> & fields_list; // hold field list passed to mysql_select
   int error;
 
@@ -194,11 +205,14 @@ class JOIN :public Sql_alloc
   COND *conds;                            // ---"---
   TABLE_LIST *tables_list;           //hold 'tables' parameter of mysql_selec
   SQL_SELECT *select;                //created in optimisation phase
-  TABLE      *exec_tmp_table;        //used in 'exec' to hold temporary
-
+  Item **ref_pointer_array; //used pointer reference for this select
+  // Copy of above to be used with different lists
+  Item **items0, **items1, **items2, **items3;
+  uint ref_pointer_array_size; // size of above in bytes
   const char *zero_result_cause; // not 0 if exec must return zero result
   
-  my_bool union_part; // this subselect is part of union 
+  bool union_part; // this subselect is part of union 
+  bool optimized; // flag to avoid double optimization in EXPLAIN
 
   JOIN(THD *thd, List<Item> &fields,
        ulong select_options, select_result *result):
@@ -208,14 +222,16 @@ class JOIN :public Sql_alloc
     sort_and_group(0), first_record(0),
     do_send_rows(1),
     send_records(0), found_records(0), examined_rows(0),
+    exec_tmp_table1(0), exec_tmp_table2(0),
     thd(thd),
     sum_funcs(0),
     procedure(0),
-    having(0),
+    having(0), tmp_having(0),
     select_options(select_options),
     result(result),
     lock(thd->lock),
     select_lex(0), //for safety
+    tmp_join(0),
     select_distinct(test(select_options & SELECT_DISTINCT)),
     no_order(0), simple_order(0), simple_group(0), skip_sort_order(0),
     need_tmp(0),
@@ -226,8 +242,10 @@ class JOIN :public Sql_alloc
     fields_list(fields),
     error(0),
     select(0),
-    exec_tmp_table(0),
-    zero_result_cause(0)
+    ref_pointer_array(0), items0(0), items1(0), items2(0), items3(0),
+    ref_pointer_array_size(0),
+    zero_result_cause(0),
+    optimized(0)
   {
     fields_list = fields;
     bzero((char*) &keyuse,sizeof(keyuse));
@@ -235,16 +253,23 @@ class JOIN :public Sql_alloc
     tmp_table_param.end_write_records= HA_POS_ERROR;
   }
   
-  int prepare(TABLE_LIST *tables,
-	      COND *conds, ORDER *order, ORDER *group, Item *having,
-	      ORDER *proc_param, SELECT_LEX *select, SELECT_LEX_UNIT *unit,
-	      bool fake_select_lex);
+  int prepare(Item ***rref_pointer_array, TABLE_LIST *tables, uint wind_num,
+	      COND *conds, uint og_num, ORDER *order, ORDER *group,
+	      Item *having, ORDER *proc_param, SELECT_LEX *select,
+	      SELECT_LEX_UNIT *unit, bool fake_select_lex);
   int optimize();
-  int global_optimize();
   int reinit();
   void exec();
   int cleanup(THD *thd);
   bool check_loop(uint id);
+  void restore_tmp();
+
+  inline void init_items_ref_array()
+  {
+    items0= ref_pointer_array + all_fields.elements;
+    ref_pointer_array_size= all_fields.elements*sizeof(Item*);
+    memcpy(items0, ref_pointer_array, ref_pointer_array_size);
+  }
 };
 
 
@@ -263,7 +288,10 @@ TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
 void free_tmp_table(THD *thd, TABLE *entry);
 void count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields,
 		       bool reset_with_sum_func);
-bool setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,List<Item> &fields);
+bool setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,
+		       Item **ref_pointer_array,
+		       List<Item> &new_list1, List<Item> &new_list2,
+		       uint elements, List<Item> &fields);
 void copy_fields(TMP_TABLE_PARAM *param);
 void copy_funcs(Item_result_field **func_ptr);
 bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index d343ccd39f57097419c41f6e16d43ab8b11080ef..072e0de04327c6e87b2480fbd32eeb66cfba9d5f 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -37,7 +37,7 @@ static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end);
 static int copy_data_between_tables(TABLE *from,TABLE *to,
 				    List<create_field> &create,
 				    enum enum_duplicates handle_duplicates,
-                                    ORDER *order,
+                                    uint order_num, ORDER *order,
 				    ha_rows *copied,ha_rows *deleted);
 
 /*****************************************************************************
@@ -1415,7 +1415,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
 		      List<create_field> &fields,
 		      List<Key> &keys,List<Alter_drop> &drop_list,
 		      List<Alter_column> &alter_list,
-                      ORDER *order,
+                      uint order_num, ORDER *order,
 		      bool drop_primary,
 		      enum enum_duplicates handle_duplicates,
 	              enum enum_enable_or_disable keys_onoff,
@@ -1877,7 +1877,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
   if (!new_table->is_view)
     error=copy_data_between_tables(table,new_table,create_list,
 				   handle_duplicates,
-				   order, &copied, &deleted);
+				   order_num, order, &copied, &deleted);
   thd->last_insert_id=next_insert_id;		// Needed for correct log
   thd->count_cuted_fields=0;			// Don`t calc cuted fields
   new_table->time_stamp=save_time_stamp;
@@ -2090,7 +2090,7 @@ static int
 copy_data_between_tables(TABLE *from,TABLE *to,
                          List<create_field> &create,
 			 enum enum_duplicates handle_duplicates,
-                         ORDER *order,
+                         uint order_num, ORDER *order,
 			 ha_rows *copied,
                          ha_rows *deleted)
 {
@@ -2138,7 +2138,10 @@ copy_data_between_tables(TABLE *from,TABLE *to,
     tables.db	 = from->table_cache_key;
     error=1;
 
-    if (setup_order(thd, &tables, fields, all_fields, order) ||
+    if (setup_ref_array(thd, &thd->lex.select_lex.ref_pointer_array,
+			order_num)||
+	setup_order(thd, thd->lex.select_lex.ref_pointer_array,
+		    &tables, fields, all_fields, order) ||
         !(sortorder=make_unireg_sortorder(order, &length)) ||
         (from->found_records = filesort(thd, from, sortorder, length, 
 					(SQL_SELECT *) 0, HA_POS_ERROR,
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 6e8c2ebdb5c6a383290c6ba8d7efa84b7a204f7c..70eb39007d2d7bd06901a2c620f93fc6b7f450bb 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -108,7 +108,6 @@ bool select_union::flush()
   return 0;
 }
 
-typedef JOIN * JOIN_P;
 int st_select_lex_unit::prepare(THD *thd, select_result *result)
 {
   DBUG_ENTER("st_select_lex_unit::prepare");
@@ -116,11 +115,9 @@ int st_select_lex_unit::prepare(THD *thd, select_result *result)
   if (prepared)
     DBUG_RETURN(0);
   prepared= 1;
-  union_result=0;
   res= 0;
   found_rows_for_union= 0;
   TMP_TABLE_PARAM tmp_table_param;
-  this->thd= thd;
   this->result= result;
   SELECT_LEX_NODE *lex_select_save= thd->lex.current_select;
   SELECT_LEX *sl;
@@ -143,7 +140,9 @@ int st_select_lex_unit::prepare(THD *thd, select_result *result)
     while ((item= it++))
       if (item_list.push_back(item))
 	goto err;
-    if (setup_fields(thd,first_table,item_list,0,0,1))
+    if (setup_wild(thd, first_table, item_list, 0,
+		   first_select()->with_wild) ||
+	setup_fields(thd, 0, first_table, item_list, 0, 0, 1))
       goto err;
   }
 
@@ -169,13 +168,11 @@ int st_select_lex_unit::prepare(THD *thd, select_result *result)
   union_result->tmp_table_param=&tmp_table_param;
 
   // prepare selects
-  joins.empty();
   for (sl= first_select(); sl; sl= sl->next_select())
   {
     JOIN *join= new JOIN(thd, sl->item_list, 
 			 sl->options | thd->options | SELECT_NO_UNLOCK,
 			 union_result);
-    joins.push_back(new JOIN_P(join));
     thd->lex.current_select= sl;
     offset_limit_cnt= sl->offset_limit;
     select_limit_cnt= sl->select_limit+sl->offset_limit;
@@ -184,8 +181,11 @@ int st_select_lex_unit::prepare(THD *thd, select_result *result)
     if (select_limit_cnt == HA_POS_ERROR)
       sl->options&= ~OPTION_FOUND_ROWS;
 
-    res= join->prepare((TABLE_LIST*) sl->table_list.first,
+    res= join->prepare(&sl->ref_pointer_array,
+		       (TABLE_LIST*) sl->table_list.first, sl->with_wild,
 		       sl->where,
+		       ((sl->braces) ? sl->order_list.elements : 0) +
+		       sl->group_list.elements,
 		       (sl->braces) ? 
 		       (ORDER *)sl->order_list.first : (ORDER *) 0,
 		       (ORDER*) sl->group_list.first,
@@ -286,8 +286,9 @@ int st_select_lex_unit::exec()
 	select_limit_cnt= HA_POS_ERROR;		// no limit
       if (select_limit_cnt == HA_POS_ERROR)
 	thd->options&= ~OPTION_FOUND_ROWS;
-      res= mysql_select(thd,&result_table_list,
-			item_list, NULL,
+      res= mysql_select(thd, &ref_pointer_array, &result_table_list,
+			0, item_list, NULL,
+			global_parameters->order_list.elements,
 			(ORDER*)global_parameters->order_list.first,
 			(ORDER*) NULL, NULL, (ORDER*) NULL,
 			thd->options, result, this, first_select(), 1);
@@ -303,20 +304,24 @@ int st_select_lex_unit::exec()
 int st_select_lex_unit::cleanup()
 {
   DBUG_ENTER("st_select_lex_unit::cleanup");
+
+  int error= 0;
+
   if (union_result)
   {
     delete union_result;
-    free_tmp_table(thd,table);
+    if (table)
+      free_tmp_table(thd, table);
     table= 0; // Safety
   }
-  List_iterator<JOIN*> j(joins);
-  JOIN** join;
-  while ((join= j++))
+  for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
   {
-    (*join)->cleanup(thd);
-    delete *join;
-    delete join;
+    JOIN *join;
+    if ((join= sl->join))
+    {
+      error|= sl->join->cleanup(thd);
+      delete join;
+    }
   }
-  joins.empty();
-  DBUG_RETURN(0);
+  DBUG_RETURN(error);
 }
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index de5cb9ef45be052935c77d455ccd4af4ded2a598..f32260f97f6d336c03f59b54ce42581a38381a67 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -50,7 +50,7 @@ int mysql_update(THD *thd,
                  List<Item> &fields,
 		 List<Item> &values,
                  COND *conds,
-                 ORDER *order,
+                 uint order_num, ORDER *order,
 		 ha_rows limit,
 		 enum enum_duplicates handle_duplicates)
 {
@@ -109,7 +109,7 @@ int mysql_update(THD *thd,
 
   /* Check the fields we are going to modify */
   table->grant.want_privilege=want_privilege;
-  if (setup_fields(thd,update_table_list,fields,1,0,0))
+  if (setup_fields(thd, 0, update_table_list, fields, 1, 0, 0))
     DBUG_RETURN(-1);				/* purecov: inspected */
   if (table->timestamp_field)
   {
@@ -122,8 +122,9 @@ int mysql_update(THD *thd,
 
   /* Check values */
   table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege);
-  if (setup_fields(thd,update_table_list,values,0,0,0))
+  if (setup_fields(thd, 0, update_table_list, values, 0, 0, 0))
   {
+    free_ulderlayed_joins(thd, &thd->lex.select_lex);
     DBUG_RETURN(-1);				/* purecov: inspected */
   }
 
@@ -134,6 +135,7 @@ int mysql_update(THD *thd,
       (select && select->check_quick(safe_update, limit)) || !limit)
   {
     delete select;
+    free_ulderlayed_joins(thd, &thd->lex.select_lex);
     if (error)
     {
       DBUG_RETURN(-1);				// Error in where
@@ -148,6 +150,7 @@ int mysql_update(THD *thd,
     if (safe_update && !using_limit)
     {
       delete select;
+      free_ulderlayed_joins(thd, &thd->lex.select_lex);
       send_error(thd,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
       DBUG_RETURN(1);
     }
@@ -175,6 +178,7 @@ int mysql_update(THD *thd,
 			  DISK_BUFFER_SIZE, MYF(MY_WME)))
     {
       delete select; /* purecov: inspected */
+      free_ulderlayed_joins(thd, &thd->lex.select_lex);
       DBUG_RETURN(-1);
     }
     if (old_used_keys & ((key_map) 1 << used_index))
@@ -197,7 +201,10 @@ int mysql_update(THD *thd,
 
       table->io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
                                                MYF(MY_FAE | MY_ZEROFILL));
-      if (setup_order(thd, &tables, fields, all_fields, order) ||
+      if (setup_ref_array(thd, &thd->lex.select_lex.ref_pointer_array,
+			order_num)||
+	  setup_order(thd, thd->lex.select_lex.ref_pointer_array,
+		      &tables, fields, all_fields, order) ||
           !(sortorder=make_unireg_sortorder(order, &length)) ||
           (table->found_records = filesort(thd, table, sortorder, length,
                                            (SQL_SELECT *) 0,
@@ -205,6 +212,7 @@ int mysql_update(THD *thd,
           == HA_POS_ERROR)
       {
 	delete select;
+	free_ulderlayed_joins(thd, &thd->lex.select_lex);
 	DBUG_RETURN(-1);
       }
     }
@@ -258,6 +266,7 @@ int mysql_update(THD *thd,
     if (error >= 0)
     {
       delete select;
+      free_ulderlayed_joins(thd, &thd->lex.select_lex);
       DBUG_RETURN(-1);
     }
   }
@@ -343,6 +352,7 @@ int mysql_update(THD *thd,
   }
 
   delete select;
+  free_ulderlayed_joins(thd, &thd->lex.select_lex);
   if (error >= 0)
     send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0); /* purecov: inspected */
   else
@@ -357,6 +367,7 @@ int mysql_update(THD *thd,
   }
   thd->count_cuted_fields=0;			/* calc cuted fields */
   free_io_cache(table);
+
   DBUG_RETURN(0);
 }
 
@@ -388,7 +399,7 @@ int mysql_multi_update(THD *thd,
     DBUG_RETURN(res);
 
   thd->select_limit=HA_POS_ERROR;
-  if (setup_fields(thd, table_list, *fields, 1, 0, 0))
+  if (setup_fields(thd, 0, table_list, *fields, 1, 0, 0))
     DBUG_RETURN(-1);
 
   /*
@@ -411,8 +422,9 @@ int mysql_multi_update(THD *thd,
     DBUG_RETURN(-1);
 
   List<Item> total_list;
-  res= mysql_select(thd,table_list,total_list,
-		    conds, (ORDER *) NULL, (ORDER *)NULL, (Item *) NULL,
+  res= mysql_select(thd, &select_lex->ref_pointer_array,
+		    table_list, select_lex->with_wild, total_list,
+		    conds, 0, (ORDER *) NULL, (ORDER *)NULL, (Item *) NULL,
 		    (ORDER *)NULL,
 		    options | SELECT_NO_JOIN_CACHE,
 		    result, unit, select_lex, 0);
@@ -467,7 +479,7 @@ int multi_update::prepare(List<Item> &not_used_values, SELECT_LEX_UNIT *unit)
     reference tables
   */
 
-  if (setup_fields(thd, all_tables, *values, 1,0,0))
+  if (setup_fields(thd, 0, all_tables, *values, 1, 0, 0))
     DBUG_RETURN(1);
 
   /*
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index ed8e8f0fb51102264ec5b0b0ef6dd7a4ed9f0354..de20d8c2b6efd2bbf00197e31058fd76eeb446f5 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1760,8 +1760,10 @@ select_item_list:
 	| select_item
 	| '*'
 	  {
-	    if (add_item_to_list(YYTHD, new Item_field(NULL,NULL,"*")))
+	    THD *thd= YYTHD;
+	    if (add_item_to_list(thd, new Item_field(NULL, NULL, "*")))
 	      YYABORT;
+	    (thd->lex.current_select->select_lex()->with_wild)++;
 	  };
 
 
@@ -3631,10 +3633,19 @@ insert_ident:
 	| table_wild	 { $$=$1; };
 
 table_wild:
-	ident '.' '*' { $$ = new Item_field(NullS,$1.str,"*"); }
+	ident '.' '*' 
+	{
+	  $$ = new Item_field(NullS,$1.str,"*");
+	  Lex->current_select->select_lex()->with_wild++;
+	}
 	| ident '.' ident '.' '*'
-	{ $$ = new Item_field((YYTHD->client_capabilities &
-   CLIENT_NO_SCHEMA ? NullS : $1.str),$3.str,"*"); };
+	{
+	  $$ = new Item_field((YYTHD->client_capabilities &
+   			     CLIENT_NO_SCHEMA ? NullS : $1.str),
+			     $3.str,"*");
+	  Lex->current_select->select_lex()->with_wild++;
+	}
+	;
 
 order_ident:
 	expr { $$=$1; };