diff --git a/mysql-test/r/derived.result b/mysql-test/r/derived.result
index 170c393524b226110705ee295c777d75016ac7a4..a89494645fab6719c4edfb3e11288865dce87553 100644
--- a/mysql-test/r/derived.result
+++ b/mysql-test/r/derived.result
@@ -59,7 +59,7 @@ explain select * from t1 as x1, (select * from t1) as x2;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	x1	ALL	NULL	NULL	NULL	NULL	4	
 1	PRIMARY	<derived2>	ALL	NULL	NULL	NULL	NULL	4	
-2	DERIVED	x1	ALL	NULL	NULL	NULL	NULL	4	
+2	DERIVED	t1	ALL	NULL	NULL	NULL	NULL	4	
 drop table if exists  t2,t3;
 select * from (select 1) as a;
 1
@@ -141,7 +141,7 @@ a	t
 explain select count(*) from t1 as tt1, (select * from t1) as tt2;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Select tables optimized away
-2	DERIVED	tt1	ALL	NULL	NULL	NULL	NULL	10000	
+2	DERIVED	t1	index	NULL	a	4	NULL	10000	Using index
 drop table t1;
 SELECT * FROM (SELECT (SELECT * FROM (SELECT 1 as a) as a )) as b;
 (SELECT * FROM (SELECT 1 as a) as a )
@@ -189,13 +189,13 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	m2	ALL	NULL	NULL	NULL	NULL	9	
 1	PRIMARY	<derived2>	ALL	NULL	NULL	NULL	NULL	6	Using where
 2	DERIVED	mp	ALL	NULL	NULL	NULL	NULL	9	Using temporary; Using filesort
-2	DERIVED	m2	index	NULL	PRIMARY	3	NULL	9	Using index
+2	DERIVED	m1	index	NULL	PRIMARY	3	NULL	9	Using index
 explain SELECT STRAIGHT_JOIN d.pla_id, m2.test FROM t1 m2  INNER JOIN (SELECT mp.pla_id, MIN(m1.matintnum) AS matintnum FROM t2 mp INNER JOIN t1 m1 ON mp.mat_id=m1.mat_id GROUP BY mp.pla_id) d ON d.matintnum=m2.matintnum;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	m2	ALL	NULL	NULL	NULL	NULL	9	
 1	PRIMARY	<derived2>	ALL	NULL	NULL	NULL	NULL	6	Using where
 2	DERIVED	mp	ALL	NULL	NULL	NULL	NULL	9	Using temporary; Using filesort
-2	DERIVED	m2	index	NULL	PRIMARY	3	NULL	9	Using index
+2	DERIVED	m1	index	NULL	PRIMARY	3	NULL	9	Using index
 drop table t1,t2;
 SELECT a.x FROM (SELECT 1 AS x) AS a HAVING a.x = 1;
 x
@@ -229,8 +229,8 @@ explain select count(*) from t1 INNER JOIN (SELECT A.E1, A.E2, A.E3 FROM t1 AS A
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	PRIMARY	<derived2>	ALL	NULL	NULL	NULL	NULL	2	
 1	PRIMARY	t1	eq_ref	PRIMARY	PRIMARY	4	THEMAX.E2	1	Using where
-2	DERIVED	t1	ALL	NULL	NULL	NULL	NULL	2	Using where
-3	DEPENDENT SUBQUERY	B	ALL	NULL	NULL	NULL	NULL	2	Using where
+2	DERIVED	A	index	NULL	PRIMARY	4	NULL	2	Using where; Using index
+3	DEPENDENT SUBQUERY	B	index	NULL	PRIMARY	4	NULL	2	Using where; Using index
 drop table t1;
 create table t1 (a int);
 insert into t1 values (1),(2);
@@ -298,3 +298,14 @@ INSERT INTO t3 VALUES (1000,0.00),(1001,0.25),(1002,0.50),(1003,0.75),(1008,1.00
 select 497, TMP.ID, NULL from (select 497 as ID, MAX(t3.DATA) as DATA      from t1 join t2 on (t1.ObjectID = t2.ID) join t3 on (t1.ObjectID = t3.ID) group by t2.ParID order by DATA DESC) as TMP;
 497	ID	NULL
 drop table t1, t2, t3;
+CREATE TABLE t1 (name char(1) default NULL, val int(5) default NULL);
+INSERT INTO t1 VALUES ('a',1),  ('a',2),  ('a',2),  ('a',2),  ('a',3),  ('a',6), ('a',7), ('a',11), ('a',11), ('a',12), ('a',13), ('a',13), ('a',20), ('b',2), ('b',3), ('b',4), ('b',5);
+SELECT s.name, AVG(s.val) AS median FROM (SELECT x.name, x.val FROM t1 x, t1 y WHERE x.name=y.name GROUP BY x.name, x.val HAVING SUM(y.val <= x.val) >= COUNT(*)/2 AND SUM(y.val >= x.val) >= COUNT(*)/2) AS s GROUP BY s.name;
+name	median
+a	7.0000
+b	3.5000
+explain SELECT s.name, AVG(s.val) AS median FROM (SELECT x.name, x.val FROM t1 x, t1 y WHERE x.name=y.name GROUP BY x.name, x.val HAVING SUM(y.val <= x.val) >= COUNT(*)/2 AND SUM(y.val >= x.val) >= COUNT(*)/2) AS s GROUP BY s.name;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	PRIMARY	<derived2>	ALL	NULL	NULL	NULL	NULL	3	Using temporary; Using filesort
+2	DERIVED	x	ALL	NULL	NULL	NULL	NULL	17	Using temporary; Using filesort
+2	DERIVED	y	ALL	NULL	NULL	NULL	NULL	17	Using where
diff --git a/mysql-test/t/derived.test b/mysql-test/t/derived.test
index 154fc4b38342eeed2c64386abf5c3dd45ff36243..b412555f1e80bed4d4c010178c6fcc24e9352384 100644
--- a/mysql-test/t/derived.test
+++ b/mysql-test/t/derived.test
@@ -183,3 +183,12 @@ CREATE TABLE t3 (
 INSERT INTO t3 VALUES (1000,0.00),(1001,0.25),(1002,0.50),(1003,0.75),(1008,1.00),(1009,1.25),(1010,1.50),(1011,1.75);
 select 497, TMP.ID, NULL from (select 497 as ID, MAX(t3.DATA) as DATA      from t1 join t2 on (t1.ObjectID = t2.ID) join t3 on (t1.ObjectID = t3.ID) group by t2.ParID order by DATA DESC) as TMP;
 drop table t1, t2, t3;
+
+
+#
+# explain derived
+#
+CREATE TABLE t1 (name char(1) default NULL, val int(5) default NULL);
+INSERT INTO t1 VALUES ('a',1),  ('a',2),  ('a',2),  ('a',2),  ('a',3),  ('a',6), ('a',7), ('a',11), ('a',11), ('a',12), ('a',13), ('a',13), ('a',20), ('b',2), ('b',3), ('b',4), ('b',5);
+SELECT s.name, AVG(s.val) AS median FROM (SELECT x.name, x.val FROM t1 x, t1 y WHERE x.name=y.name GROUP BY x.name, x.val HAVING SUM(y.val <= x.val) >= COUNT(*)/2 AND SUM(y.val >= x.val) >= COUNT(*)/2) AS s GROUP BY s.name;
+explain SELECT s.name, AVG(s.val) AS median FROM (SELECT x.name, x.val FROM t1 x, t1 y WHERE x.name=y.name GROUP BY x.name, x.val HAVING SUM(y.val <= x.val) >= COUNT(*)/2 AND SUM(y.val >= x.val) >= COUNT(*)/2) AS s GROUP BY s.name;
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index e7faaf1767dabbf90929515b0ebb639811390313..c1365baaf0a189fecaf5483c2e9a7a66cc533b2c 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -481,14 +481,13 @@ int mysql_select(THD *thd, Item ***rref_pointer_array,
 		 SELECT_LEX *select_lex);
 void free_underlaid_joins(THD *thd, SELECT_LEX *select);
 void fix_tables_pointers(SELECT_LEX *select_lex);
-void fix_tables_pointers(SELECT_LEX_UNIT *select_lex);
 int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit,
 			select_result *result);
 int mysql_explain_select(THD *thd, SELECT_LEX *sl, char const *type,
 			 select_result *result);
 int mysql_union(THD *thd, LEX *lex, select_result *result,
 		SELECT_LEX_UNIT *unit);
-int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *s, TABLE_LIST *t);
+int mysql_handle_derived(LEX *lex);
 Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
 			Item ***copy_func, Field **from_field,
 			bool group,bool modify_item);
@@ -676,6 +675,7 @@ int setup_ftfuncs(SELECT_LEX* select);
 int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
 void wait_for_refresh(THD *thd);
 int open_tables(THD *thd,TABLE_LIST *tables);
+int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
 int open_and_lock_tables(THD *thd,TABLE_LIST *tables);
 int lock_tables(THD *thd,TABLE_LIST *tables);
 TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index 81ea9d9e2ac4791e2c438b0b506e94306fd5f4f6..d125c95e839b89267550869df259eee87db4c76e 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -732,7 +732,7 @@ static int fetch_db_tables(THD *thd, MYSQL *mysql, const char *db,
     int error;
     if (table_rules_on)
     {
-      table.next= 0;
+      bzero((char*) &table, sizeof(table)); //just for safe
       table.db= (char*) db;
       table.real_name= (char*) table_name;
       table.updating= 1;
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 77131a378694a6fae3011ff1a1c88ce98ed0b1d6..8ef6a32a4309ec47a8161c71b77ae1a72af5b12b 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -2247,7 +2247,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
   }
 #endif
 
-  if (open_and_lock_tables(thd,tables))
+  if (simple_open_n_lock_tables(thd,tables))
   {						// Should never happen
     close_thread_tables(thd);			/* purecov: deadcode */
     DBUG_RETURN(-1);				/* purecov: deadcode */
@@ -2395,7 +2395,7 @@ int mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
   }
 
   /* open the mysql.user and mysql.db tables */
-
+  bzero((char*) &tables,sizeof(tables));
   tables[0].alias=tables[0].real_name=(char*) "user";
   tables[1].alias=tables[1].real_name=(char*) "db";
   tables[0].next=tables+1;
@@ -2421,7 +2421,7 @@ int mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
   }
 #endif
 
-  if (open_and_lock_tables(thd,tables))
+  if (simple_open_n_lock_tables(thd,tables))
   {						// This should never happen
     close_thread_tables(thd);			/* purecov: deadcode */
     DBUG_RETURN(-1);				/* purecov: deadcode */
@@ -2517,7 +2517,7 @@ my_bool grant_init(THD *org_thd)
   thd->store_globals();
   thd->db= my_strdup("mysql",MYF(0));
   thd->db_length=5;				// Safety
-  bzero((char*) &tables,sizeof(tables));
+  bzero((char*) &tables, sizeof(tables));
   tables[0].alias=tables[0].real_name= (char*) "tables_priv";
   tables[1].alias=tables[1].real_name= (char*) "columns_priv";
   tables[0].next=tables+1;
@@ -3376,7 +3376,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
   }
 #endif
 
-  if (open_and_lock_tables(thd, tables))
+  if (simple_open_n_lock_tables(thd, tables))
   {						// This should never happen
     close_thread_tables(thd);
     DBUG_RETURN(-1);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 3125c3927519e96cbffc3fd85e016723733e772b..5707b58299fe33a874379d5631ad169363865490 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1305,6 +1305,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
       goto err;					// Can't repair the table
 
     TABLE_LIST table_list;
+    bzero((char*) &table_list, sizeof(table_list)); // just for safe
     table_list.db=(char*) db;
     table_list.real_name=(char*) name;
     table_list.next=0;
@@ -1372,11 +1373,13 @@ int open_tables(THD *thd,TABLE_LIST *start)
   thd->proc_info="Opening tables";
   for (tables=start ; tables ; tables=tables->next)
   {
+    if (tables->derived)
+      continue;
     if (!tables->table &&
-	!(tables->table=open_table(thd,
-				   tables->db,
-				   tables->real_name,
-				   tables->alias, &refresh)))
+	!(tables->table= open_table(thd,
+				    tables->db,
+				    tables->real_name,
+				    tables->alias, &refresh)))
     {
       if (refresh)				// Refresh in progress
       {
@@ -1522,15 +1525,47 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type)
 
 
 /*
-  Open all tables in list and locks them for read.
-  The lock will automaticly be freed by close_thread_tables()
+  Open all tables in list and locks them for read without derived
+  tables processing.
+
+  SYNOPSIS
+    simple_open_n_lock_tables()
+    thd		- thread handler
+    tables	- list of tables for open&locking
+
+  NOTE
+    The lock will automaticly be freed by close_thread_tables()
 */
 
-int open_and_lock_tables(THD *thd,TABLE_LIST *tables)
+int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
 {
-  if (open_tables(thd,tables) || lock_tables(thd,tables))
-    return -1;					/* purecov: inspected */
-  return 0;
+  DBUG_ENTER("open_n_lock_tables");
+  if (open_tables(thd, tables) || lock_tables(thd, tables))
+    DBUG_RETURN(-1);				/* purecov: inspected */
+  DBUG_RETURN(0);
+}
+
+
+/*
+  Open all tables in list, locks them and process derived tables
+  tables processing.
+
+  SYNOPSIS
+    simple_open_n_lock_tables()
+    thd		- thread handler
+    tables	- list of tables for open&locking
+
+  NOTE
+    The lock will automaticly be freed by close_thread_tables()
+*/
+
+int open_and_lock_tables(THD *thd, TABLE_LIST *tables)
+{
+  DBUG_ENTER("open_and_lock_tables");
+  if (open_tables(thd, tables) || lock_tables(thd, tables))
+    DBUG_RETURN(-1);				/* purecov: inspected */
+  fix_tables_pointers(thd->lex->all_selects_list);
+  DBUG_RETURN(mysql_handle_derived(thd->lex));
 }
 
 
@@ -1563,12 +1598,18 @@ int lock_tables(THD *thd,TABLE_LIST *tables)
     DBUG_ASSERT(thd->lock == 0);	// You must lock everything at once
     uint count=0;
     for (table = tables ; table ; table=table->next)
-      count++;
+    {
+      if (!table->derived)
+	count++;
+    }
     TABLE **start,**ptr;
     if (!(ptr=start=(TABLE**) sql_alloc(sizeof(TABLE*)*count)))
       return -1;
     for (table = tables ; table ; table=table->next)
-      *(ptr++)= table->table;
+    {
+      if (!table->derived)
+	*(ptr++)= table->table;
+    }
     if (!(thd->lock=mysql_lock_tables(thd,start,count)))
       return -1;				/* purecov: inspected */
   }
@@ -1576,7 +1617,8 @@ int lock_tables(THD *thd,TABLE_LIST *tables)
   {
     for (table = tables ; table ; table=table->next)
     {
-      if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
+      if (!table->derived && 
+	  check_lock_and_start_stmt(thd, table->table, table->lock_type))
       {
 	ha_rollback_stmt(thd);
 	return -1;
@@ -2165,6 +2207,7 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name,
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
       /* Ensure that we have access right to all columns */
       if (!(table->grant.privilege & SELECT_ACL) &&
+	  !tables->derived &&
 	  check_grant_all_columns(thd,SELECT_ACL,table))
 	DBUG_RETURN(-1);
 #endif
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 9ac7b7596fecdb118a5fcda410a5711dc305b3a7..2204466b9d13d4ed3eb34c1e4f450286f1362b7d 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -43,7 +43,6 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order,
 
   if ((open_and_lock_tables(thd, table_list)))
     DBUG_RETURN(-1);
-  fix_tables_pointers(thd->lex->all_selects_list);
   table= table_list->table;
   table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
   thd->proc_info="init";
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 374e56ecdd46b1fedb17778eeff3901859684a73..8fa5694714cc7869b577a9498644719054a8521c 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -25,6 +25,49 @@
 #include "sql_select.h"
 #include "sql_acl.h"
 
+int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *s, TABLE_LIST *t);
+
+/*
+  Resolve derived tables in all queries
+
+  SYNOPSIS
+    mysql_handle_derived()
+    lex                 LEX for this thread
+
+  RETURN
+    0	ok
+    -1	Error
+    1	Error and error message given
+*/
+int
+mysql_handle_derived(LEX *lex)
+{
+  int res= 0;
+  if (lex->derived_tables)
+  {
+    for (SELECT_LEX *sl= lex->all_selects_list;
+	 sl;
+	 sl= sl->next_select_in_list())
+    {
+      for (TABLE_LIST *cursor= sl->get_table_list();
+	   cursor;
+	   cursor= cursor->next)
+      {
+	if (cursor->derived && (res=mysql_derived(lex->thd, lex,
+						  cursor->derived,
+						  cursor)))
+	{
+	  if (res < 0 || lex->thd->net.report_error)
+	    send_error(lex->thd, lex->thd->killed ? ER_SERVER_SHUTDOWN : 0);
+	  return 1;
+	}
+      }
+    }
+  }
+  return 0;
+}
+
+
 /*
   Resolve derived tables in all queries
 
@@ -49,9 +92,6 @@
     Derived tables is stored in thd->derived_tables and freed in
     close_thread_tables()
 
-  TODO
-    Move creation of derived tables in open_and_lock_tables()
-
   RETURN
     0	ok
     1	Error
@@ -72,143 +112,87 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit,
   bool is_subsel= first_select->first_inner_unit() ? 1: 0;
   SELECT_LEX *save_current_select= lex->current_select;
   DBUG_ENTER("mysql_derived");
-  
-  /*
-    In create_total_list, derived tables have to be treated in case of
-    EXPLAIN, This is because unit/node is not deleted in that
-    case. Current code in this function has to be improved to
-    recognize better when this function is called from derived tables
-    and when from other functions.
-  */
-  if ((is_union || is_subsel) && unit->create_total_list(thd, lex, &tables, 1))
-    DBUG_RETURN(-1);
 
+  if (!(derived_result= new select_union(0)))
+    DBUG_RETURN(1); // out of memory
+
+  // st_select_lex_unit::prepare correctly work for single select
+  if ((res= unit->prepare(thd, derived_result, 0)))
+    goto exit;
+
+	
+  derived_result->tmp_table_param.init();
+  derived_result->tmp_table_param.field_count= unit->types.elements;
   /*
-    We have to do access checks here as this code is executed before any
-    sql command is started to execute.
+    Temp table is created so that it hounours if UNION without ALL is to be 
+    processed
   */
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
-  if (tables)
-    res= check_table_access(thd,SELECT_ACL, tables,0);
-  else
-    res= check_access(thd, SELECT_ACL, any_db,0,0,0);
-  if (res)
-    DBUG_RETURN(1);
-#endif
-
-  if (!(res=open_and_lock_tables(thd,tables)))
+  if (!(table= create_tmp_table(thd, &derived_result->tmp_table_param,
+				unit->types, (ORDER*) 0, 
+				is_union && !unit->union_option, 1,
+				(first_select->options | thd->options |
+				 TMP_TABLE_ALL_COLUMNS),
+				HA_POS_ERROR,
+				org_table_list->alias)))
   {
-    if (is_union || is_subsel)
-    {
-      /* 
-	 The following code is a re-do of fix_tables_pointers() found
-	 in sql_select.cc for UNION's within derived tables. The only
-	 difference is in navigation, as in derived tables we care for
-	 this level only.
-
-      */
-      fix_tables_pointers(unit);
-    }
+    res= -1;
+    goto exit;
+  }
+  derived_result->set_table(table);
 
-    if (!(derived_result= new select_union(0)))
-      DBUG_RETURN(1); // out of memory
 
-    // st_select_lex_unit::prepare correctly work for single select
-    if ((res= unit->prepare(thd, derived_result, 0)))
-      goto exit;
+  if (is_union)
+    res= mysql_union(thd, lex, derived_result, unit);
+  else
+  {
+    unit->offset_limit_cnt= first_select->offset_limit;
+    unit->select_limit_cnt= first_select->select_limit+
+      first_select->offset_limit;
+    if (unit->select_limit_cnt < first_select->select_limit)
+      unit->select_limit_cnt= HA_POS_ERROR;
+    if (unit->select_limit_cnt == HA_POS_ERROR)
+      first_select->options&= ~OPTION_FOUND_ROWS;
+
+    lex->current_select= first_select;
+    res= mysql_select(thd, &first_select->ref_pointer_array, 
+		      (TABLE_LIST*) first_select->table_list.first,
+		      first_select->with_wild,
+		      first_select->item_list, first_select->where,
+		      (first_select->order_list.elements+
+		       first_select->group_list.elements),
+		      (ORDER *) first_select->order_list.first,
+		      (ORDER *) first_select->group_list.first,
+		      first_select->having, (ORDER*) NULL,
+		      (first_select->options | thd->options |
+		       SELECT_NO_UNLOCK),
+		      derived_result, unit, first_select);
+  }
 
-    /* 
-       This is done in order to redo all field optimisations when any of the 
-       involved tables is used in the outer query 
-    */
-    if (tables)
-    {
-      for (TABLE_LIST *cursor= tables;  cursor;  cursor= cursor->next)
-	cursor->table->clear_query_id= 1;
-    }
-	
-    derived_result->tmp_table_param.init();
-    derived_result->tmp_table_param.field_count= unit->types.elements;
+  if (!res)
+  {
     /*
-      Temp table is created so that it hounours if UNION without ALL is to be 
-      processed
+      Here we entirely fix both TABLE_LIST and list of SELECT's as
+      there were no derived tables
     */
-    if (!(table= create_tmp_table(thd, &derived_result->tmp_table_param,
-				  unit->types, (ORDER*) 0, 
-				  is_union && !unit->union_option, 1,
-				  (first_select->options | thd->options |
-				   TMP_TABLE_ALL_COLUMNS),
-				  HA_POS_ERROR,
-				  org_table_list->alias)))
-    {
-      res= -1;
-      goto exit;
-    }
-    derived_result->set_table(table);
-
-    if (is_union)
-      res= mysql_union(thd, lex, derived_result, unit);
+    if (derived_result->flush())
+      res= 1;
     else
     {
-      unit->offset_limit_cnt= first_select->offset_limit;
-      unit->select_limit_cnt= first_select->select_limit+
-	first_select->offset_limit;
-      if (unit->select_limit_cnt < first_select->select_limit)
-	unit->select_limit_cnt= HA_POS_ERROR;
-      if (unit->select_limit_cnt == HA_POS_ERROR)
-	first_select->options&= ~OPTION_FOUND_ROWS;
-
-      lex->current_select= first_select;
-      res= mysql_select(thd, &first_select->ref_pointer_array, 
-			(TABLE_LIST*) first_select->table_list.first,
-			first_select->with_wild,
-			first_select->item_list, first_select->where,
-			(first_select->order_list.elements+
-			 first_select->group_list.elements),
-			(ORDER *) first_select->order_list.first,
-			(ORDER *) first_select->group_list.first,
-			first_select->having, (ORDER*) NULL,
-			(first_select->options | thd->options |
-			 SELECT_NO_UNLOCK),
-			derived_result, unit, first_select);
-    }
-
-    if (!res)
-    {
-      /*
-	Here we entirely fix both TABLE_LIST and list of SELECT's as
-	there were no derived tables
-      */
-      if (derived_result->flush())
-	res= 1;
-      else
+      org_table_list->real_name= table->real_name;
+      org_table_list->table= table;
+      if (org_table_list->table_list)
       {
-	org_table_list->real_name=table->real_name;
-	org_table_list->table=table;
-	table->derived_select_number= first_select->select_number;
-	table->tmp_table= TMP_TABLE;
+	org_table_list->table_list->real_name= table->real_name;
+	org_table_list->table_list->table= table;
+      }
+      table->derived_select_number= first_select->select_number;
+      table->tmp_table= TMP_TABLE;
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
-	org_table_list->grant.privilege= SELECT_ACL;
+      org_table_list->grant.privilege= SELECT_ACL;
 #endif
-	if (lex->describe)
-	{
-	  // to fix a problem in EXPLAIN
-	  if (tables)
-	  {
-	    for (TABLE_LIST *cursor= tables;  cursor;  cursor= cursor->next)
-	      if (cursor->table_list)
-		cursor->table_list->table=cursor->table;
-	  }
-	}
-	else
-	{
-	  unit->exclude_tree();
-	  unit->cleanup();
-	}
-	org_table_list->db= (char *)"";
-	  // Force read of table stats in the optimizer
-	table->file->info(HA_STATUS_VARIABLE);
-      }
+      org_table_list->db= (char *)"";
+      // Force read of table stats in the optimizer
+      table->file->info(HA_STATUS_VARIABLE);
     }
 
     if (res)
@@ -223,7 +207,6 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit,
 exit:
     delete derived_result;
     lex->current_select= save_current_select;
-    close_thread_tables(thd, 0, 1);
   }
   DBUG_RETURN(res);
 }
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index c2f3e737dafc0fba1460009d62c3dfa1a44b6ed5..342089987dc5a42fcbc7e0e5f341f5b012c95c0d 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -188,7 +188,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
     res= open_and_lock_tables(thd, table_list);
   if (res)
     DBUG_RETURN(-1);
-  fix_tables_pointers(thd->lex->all_selects_list);
 
   table= table_list->table;
   thd->proc_info="init";
@@ -646,7 +645,8 @@ public:
     thd.command=COM_DELAYED_INSERT;
     thd.lex->current_select= 0; /* for my_message_sql */
 
-    bzero((char*) &thd.net,sizeof(thd.net));	// Safety
+    bzero((char*) &thd.net, sizeof(thd.net));		// Safety
+    bzero((char*) &table_list, sizeof(table_list));	// Safety
     thd.system_thread= SYSTEM_THREAD_DELAYED_INSERT;
     thd.host_or_ip= "";
     bzero((char*) &info,sizeof(info));
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 65c958093bd658c1fec09e78c1bcfe70d30312c9..62f255ea17881f8c6a59334e11fc42eee0d97add 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1286,12 +1286,10 @@ bool st_select_lex::test_limit()
     !0 - error
 */
 bool st_select_lex_unit::create_total_list(THD *thd_arg, st_lex *lex,
-					   TABLE_LIST **result_arg,
-					   bool check_derived)
+					   TABLE_LIST **result_arg)
 {
   *result_arg= 0;
-  res= create_total_list_n_last_return(thd_arg, lex, &result_arg,
-				       check_derived);
+  res= create_total_list_n_last_return(thd_arg, lex, &result_arg);
   return res;
 }
 
@@ -1303,8 +1301,7 @@ bool st_select_lex_unit::create_total_list(THD *thd_arg, st_lex *lex,
     thd            THD pointer
     lex            pointer on LEX stricture
     result         pointer on pointer on result list of tables pointer
-    check_derived  force derived table chacking (used for creating 
-                   table list for derived query)
+
   DESCRIPTION
     This is used for UNION & subselect to create a new table list of all used 
     tables.
@@ -1318,8 +1315,7 @@ bool st_select_lex_unit::create_total_list(THD *thd_arg, st_lex *lex,
 bool st_select_lex_unit::
 create_total_list_n_last_return(THD *thd_arg,
 				st_lex *lex,
-				TABLE_LIST ***result_arg,
-				bool check_derived)
+				TABLE_LIST ***result_arg)
 {
   TABLE_LIST *slave_list_first=0, **slave_list_last= &slave_list_first;
   TABLE_LIST **new_table_list= *result_arg, *aux;
@@ -1345,15 +1341,12 @@ create_total_list_n_last_return(THD *thd_arg,
       return 1;
     }
 
-    if (sl->linkage == DERIVED_TABLE_TYPE && !check_derived)
-      goto end;
-
     for (SELECT_LEX_UNIT *inner=  sl->first_inner_unit();
 	 inner;
 	 inner= inner->next_unit())
     {
       if (inner->create_total_list_n_last_return(thd, lex,
-						 &slave_list_last, 0))
+						 &slave_list_last))
 	return 1;
     }
 
@@ -1400,63 +1393,75 @@ end:
   return 0;
 }
 
+
 st_select_lex_unit* st_select_lex_unit::master_unit()
 {
     return this;
 }
 
+
 st_select_lex* st_select_lex_unit::outer_select()
 {
   return (st_select_lex*) master;
 }
 
+
 bool st_select_lex::add_order_to_list(THD *thd, Item *item, bool asc)
 {
   return add_to_list(thd, order_list, item, asc);
 }
 
+
 bool st_select_lex::add_item_to_list(THD *thd, Item *item)
 {
   return item_list.push_back(item);
 }
 
+
 bool st_select_lex::add_group_to_list(THD *thd, Item *item, bool asc)
 {
   return add_to_list(thd, group_list, item, asc);
 }
 
+
 bool st_select_lex::add_ftfunc_to_list(Item_func_match *func)
 {
   return !func || ftfunc_list->push_back(func); // end of memory?
 }
 
+
 st_select_lex_unit* st_select_lex::master_unit()
 {
   return (st_select_lex_unit*) master;
 }
 
+
 st_select_lex* st_select_lex::outer_select()
 {
   return (st_select_lex*) master->get_master();
 }
 
+
 bool st_select_lex::set_braces(bool value)
 {
   braces= value;
   return 0; 
 }
 
+
 bool st_select_lex::inc_in_sum_expr()
 {
   in_sum_expr++;
   return 0;
 }
 
+
 uint st_select_lex::get_in_sum_expr()
 {
   return in_sum_expr;
 }
 
+
 TABLE_LIST* st_select_lex::get_table_list()
 {
   return (TABLE_LIST*) table_list.first;
@@ -1467,21 +1472,25 @@ List<Item>* st_select_lex::get_item_list()
   return &item_list;
 }
 
+
 List<String>* st_select_lex::get_use_index()
 {
   return use_index_ptr;
 }
 
+
 List<String>* st_select_lex::get_ignore_index()
 {
   return ignore_index_ptr;
 }
 
+
 ulong st_select_lex::get_table_join_options()
 {
   return table_join_options;
 }
 
+
 bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
 {
   if (ref_pointer_array)
@@ -1493,6 +1502,58 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
 			       order_group_num)* 5)) == 0;
 }
 
+
+/*
+  Find db.table which will be updated in this unit
+
+  SYNOPSIS
+    st_select_lex_unit::check_updateable()
+    db		- data base name
+    table	- real table name
+
+  RETURN
+    1 - found
+    0 - OK (table did not found)
+*/
+bool st_select_lex_unit::check_updateable(char *db, char *table)
+{
+  for(SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
+    if (sl->check_updateable(db, table))
+      return 1;
+  return 0;
+}
+
+
+/*
+  Find db.table which will be updated in this select and 
+  underlayed ones (except derived tables)
+
+  SYNOPSIS
+    st_select_lex::check_updateable()
+    db		- data base name
+    table	- real table name
+
+  RETURN
+    1 - found
+    0 - OK (table did not found)
+*/
+bool st_select_lex::check_updateable(char *db, char *table)
+{
+  if (find_real_table_in_list(get_table_list(), db, table))
+    return 1;
+
+  for (SELECT_LEX_UNIT *un= first_inner_unit();
+       un;
+       un= un->next_unit())
+  {
+    if (un->first_select()->linkage != DERIVED_TABLE_TYPE &&
+	un->check_updateable(db, table))
+      return 1;
+  }
+  return 0;
+}
+
+
 void st_select_lex_unit::print(String *str)
 {
   for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
@@ -1535,6 +1596,7 @@ void st_select_lex::print_order(String *str, ORDER *order)
   }
 }
  
+
 void st_select_lex::print_limit(THD *thd, String *str)
 {
   if (!thd)
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index bcbd60e971647789b05ca118e3ecf2c43eb0600d..b1f0b63b5cd6d5d647aed2929ba54087d80fd7dd 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -336,8 +336,7 @@ public:
   uint union_option;
 
   void init_query();
-  bool create_total_list(THD *thd, st_lex *lex, TABLE_LIST **result,
-			 bool check_current_derived);
+  bool create_total_list(THD *thd, st_lex *lex, TABLE_LIST **result);
   st_select_lex_unit* master_unit();
   st_select_lex* outer_select();
   st_select_lex* first_select() { return (st_select_lex*) slave; }
@@ -355,14 +354,15 @@ public:
   int exec();
   int cleanup();
 
+  bool check_updateable(char *db, char *table);
   void print(String *str);
+  
 
   friend void mysql_init_query(THD *thd);
   friend int subselect_union_engine::exec();
 private:
   bool create_total_list_n_last_return(THD *thd, st_lex *lex,
-				       TABLE_LIST ***result,
-				       bool check_current_derived);
+				       TABLE_LIST ***result);
 };
 typedef class st_select_lex_unit SELECT_LEX_UNIT;
 
@@ -497,6 +497,7 @@ public:
     init_select();
   }
   bool setup_ref_array(THD *thd, uint order_group_num);
+  bool check_updateable(char *db, char *table);
   void print(THD *thd, String *str);
   static void print_order(String *str, ORDER *order);
   void print_limit(THD *thd, String *str);
diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc
index 1d16771c1a43f651faf620085460fa0468c309fd..efc4cf0921d603c762adfffb8b15ae235be2fa48 100644
--- a/sql/sql_olap.cc
+++ b/sql/sql_olap.cc
@@ -143,18 +143,6 @@ int handle_olaps(LEX *lex, SELECT_LEX *select_lex)
   int count=select_lex->group_list.elements;
   int sl_return=0;
 
-// a fix for UNION's
-  for (TABLE_LIST *cursor= (TABLE_LIST *)select_lex->table_list.first;
-       cursor;
-       cursor=cursor->next)
-  {
-    if (cursor->do_redirect)
-    {
-      //Sinisa TODO: there are function for this purpose: fix_tables_pointers
-      cursor->table= cursor->table_list->table;
-      cursor->do_redirect= 0;
-    }
-  }
 
   lex->last_selects=select_lex;
 
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index cbf091069d6d4016bac5aa9eec388f266f9773fd..dbce5cf75a420bdb001d5076dd251fd4fd501223 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1789,34 +1789,9 @@ mysql_execute_command(THD *thd)
 #endif
   }
 #endif /* !HAVE_REPLICATION */
-  /*
-    TODO: make derived tables processing 'inside' SELECT processing.
-    TODO: solve problem with depended derived tables in subselects
-  */
-  if (lex->derived_tables)
-  {
-    for (SELECT_LEX *sl= lex->all_selects_list;
-	 sl;
-	 sl= sl->next_select_in_list())
-    {
-      for (TABLE_LIST *cursor= sl->get_table_list();
-	   cursor;
-	   cursor= cursor->next)
-      {
-	if (cursor->derived && (res=mysql_derived(thd, lex,
-						  cursor->derived,
-						  cursor)))
-	{
-	  if (res < 0 || thd->net.report_error)
-	    send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
-	  DBUG_VOID_RETURN;
-	}
-      }
-    }
-  }
   if (&lex->select_lex != lex->all_selects_list &&
       lex->sql_command != SQLCOM_CREATE_TABLE &&
-      lex->unit.create_total_list(thd, lex, &tables, 0))
+      lex->unit.create_total_list(thd, lex, &tables))
     DBUG_VOID_RETURN;
   
   /*
@@ -1875,7 +1850,6 @@ mysql_execute_command(THD *thd)
 	}
 	else
 	  thd->send_explain_fields(result);
-	fix_tables_pointers(lex->all_selects_list);
 	res= mysql_explain_union(thd, &thd->lex->unit, result);
 	MYSQL_LOCK *save_lock= thd->lock;
 	thd->lock= (MYSQL_LOCK *)0;
@@ -1918,7 +1892,6 @@ mysql_execute_command(THD *thd)
 		   (res= open_and_lock_tables(thd,tables))))
 	break;
 
-    fix_tables_pointers(lex->all_selects_list);
     res= mysql_do(thd, *lex->insert_list);
     if (thd->net.report_error)
       res= -1;
@@ -2127,7 +2100,7 @@ mysql_execute_command(THD *thd)
     lex->select_lex.table_list.first= (byte*) (tables);
     create_table->next= 0;
     if (&lex->select_lex != lex->all_selects_list &&
-	lex->unit.create_total_list(thd, lex, &tables, 0))
+	lex->unit.create_total_list(thd, lex, &tables))
       DBUG_VOID_RETURN;
 
     ulong want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
@@ -2342,6 +2315,8 @@ mysql_execute_command(THD *thd)
       if (grant_option)
       {
 	TABLE_LIST old_list,new_list;
+	bzero((char*) &old_list, sizeof(old_list));
+	bzero((char*) &new_list, sizeof(new_list)); // Safety
 	old_list=table[0];
 	new_list=table->next[0];
 	old_list.next=new_list.next=0;
@@ -2673,23 +2648,15 @@ mysql_execute_command(THD *thd)
       }
       if (!walk)
       {
-	if (lex->derived_tables)
-	{
-	  // are we trying to delete derived table?
-	  for (walk= (TABLE_LIST*) tables; walk; walk= walk->next)
-	  {
-	    if (!strcmp(auxi->real_name,walk->alias) &&
-		walk->derived)
-	    {
-	      net_printf(thd, ER_NON_UPDATABLE_TABLE,
-			 auxi->real_name, "DELETE");
-	      goto error;
-	    }
-	  }
-	}
 	net_printf(thd, ER_NONUNIQ_TABLE, auxi->real_name);
 	goto error;
       }
+      if (walk->derived)
+      {
+	net_printf(thd, ER_NON_UPDATABLE_TABLE,
+		   auxi->real_name, "DELETE");
+	goto error;
+      }
       walk->lock_type= auxi->lock_type;
       auxi->table_list=  walk;		// Remember corresponding table
     }
@@ -2703,21 +2670,27 @@ mysql_execute_command(THD *thd)
       break;
     /* Fix tables-to-be-deleted-from list to point at opened tables */
     for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next)
-      auxi->table= auxi->table_list->table;
-    if (&lex->select_lex != lex->all_selects_list)
     {
-      for (TABLE_LIST *t= select_lex->get_table_list();
-	   t; t= t->next)
+      auxi->table= auxi->table_list->table;
+      /* 
+	 Multi-delete can't be constucted over-union => we always have
+	 single SELECT on top and have to check underlayed SELECTs of it
+      */
+      for (SELECT_LEX_UNIT *un= lex->select_lex.first_inner_unit();
+	   un;
+	   un= un->next_unit())
       {
-	if (find_real_table_in_list(t->table_list->next, t->db, t->real_name))
+	if (un->first_select()->linkage != DERIVED_TABLE_TYPE &&
+	    un->check_updateable(auxi->table_list->db,
+				 auxi->table_list->real_name))
 	{
-	  my_error(ER_UPDATE_TABLE_USED, MYF(0), t->real_name);
+	  my_error(ER_UPDATE_TABLE_USED, MYF(0), auxi->table_list->real_name);
 	  res= -1;
 	  break;
 	}
       }
     }
-    fix_tables_pointers(lex->all_selects_list);
+
     if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables,
 							  table_count)))
     {
@@ -2962,7 +2935,6 @@ mysql_execute_command(THD *thd)
     if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
 		   (res= open_and_lock_tables(thd,tables))))
       break;
-    fix_tables_pointers(lex->all_selects_list);
     if (!(res= sql_set_variables(thd, &lex->var_list)))
       send_ok(thd);
     if (thd->net.report_error)
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index de0d0c8aca8dd6f3d744132d17c39c7e1d9e89ac..1d5233d5803297a637b982ba920a584e1bf8357a 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -733,7 +733,7 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
     DBUG_RETURN(1);
 #endif
   if ((&lex->select_lex != lex->all_selects_list &&
-       lex->unit.create_total_list(thd, lex, &tables, 0)))
+       lex->unit.create_total_list(thd, lex, &tables)))
    DBUG_RETURN(1);
     
   if (open_and_lock_tables(thd, tables))
@@ -746,7 +746,6 @@ static bool mysql_test_select_fields(Prepared_statement *stmt,
   }
   else 
   {
-    fix_tables_pointers(lex->all_selects_list);
     if (!result && !(result= new select_send()))
     {
       send_error(thd, ER_OUT_OF_RESOURCES);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index f6e7a474eca578a2e6d0d349d12312fe8bec856f..9ad7ba606be950b2d180df531631505b35e1e987 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -175,7 +175,6 @@ int handle_select(THD *thd, LEX *lex, select_result *result)
   register SELECT_LEX *select_lex = &lex->select_lex;
   DBUG_ENTER("handle_select");
 
-  fix_tables_pointers(lex->all_selects_list);
   if (select_lex->next_select())
     res=mysql_union(thd, lex, result, &lex->unit);
   else
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 7cc71117914fec8ec945ac3e6bf9c0250abad91f..aa1d7673176f29dfa2a615ed1fd9401bb5aab96a 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -427,7 +427,6 @@ public:
 
 bool cp_buffer_from_ref(TABLE_REF *ref);
 bool error_if_full_join(JOIN *join);
-void relink_tables(SELECT_LEX *select_lex);
 int report_error(TABLE *table, int error);
 int safe_index_read(JOIN_TAB *tab);
 COND *eliminate_not_funcs(COND *cond);
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
index 337f2540a398470ac15febb75cb274e9a9f180cc..6e8aae54b239e781d5b8170d6688b3348c8e2e78 100644
--- a/sql/sql_udf.cc
+++ b/sql/sql_udf.cc
@@ -149,7 +149,7 @@ void udf_init()
   tables.lock_type = TL_READ;
   tables.db=new_thd->db;
 
-  if (open_and_lock_tables(new_thd, &tables))
+  if (simple_open_n_lock_tables(new_thd, &tables))
   {
     DBUG_PRINT("error",("Can't open udf table"));
     sql_print_error("Can't open mysql/func table");
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 8ee00f2bca6b0595fec046a8dc406482bc3dd57e..45f7a73f6b5c06cda929844b4330b8d641994683 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -79,7 +79,6 @@ int mysql_update(THD *thd,
   if ((open_and_lock_tables(thd, table_list)))
     DBUG_RETURN(-1);
   thd->proc_info="init";
-  fix_tables_pointers(thd->lex->all_selects_list);
   table= table_list->table;
   table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
 
@@ -431,7 +430,6 @@ int mysql_multi_update(THD *thd,
 #endif
   if ((res=open_and_lock_tables(thd,table_list)))
     DBUG_RETURN(res);
-  fix_tables_pointers(thd->lex->all_selects_list);
 
   select_lex->select_limit= HA_POS_ERROR;