List:Commits« Previous MessageNext Message »
From:Alexander Nozdrin Date:November 29 2010 2:13pm
Subject:bzr commit into mysql-trunk-bugfixing branch (alexander.nozdrin:3391)
Bug#27480
View as plain text  
#At file:///home/alik/MySQL/bzr/00/bug27480/mysql-trunk-bugfixing-bug27480/ based on revid:luis.soares@stripped

 3391 Alexander Nozdrin	2010-11-29
      Prerequisite patch for Bug#27480 (Extend CREATE TEMPORARY TABLES privilege
      to allow temp table operations):
      
        - move opening of temporary tables out of open_table();
      
        - make open_table() to work with base tables and views only.
          It will be renamed to open_base_table_or_view()
          in a follow-up patch.
      
        - introduce open_temporary_table() to open temporary tables
          (similar to open_table());
      
        - introduce open_and_process_temporary_table() to fully prepare
          temporary tables for use (similar to open_and_process_table());
      
        - introduce a new "command flag" (CF_PREOPEN_TMP_TABLES) to mark
          statements that work with temporary tables, thus temporary tables
          should be opened for those statements;
      
        - open temporary tables in a unified way in the beginning of
          the statements marked with CF_PREOPEN_TMP_TABLES flag;
      
        - introduce a new "command flag" (CF_HA_CLOSE) to mark statements
          for which open handlers (by HANDLER OPEN) should be closed;
      
        - close open handlers in a unified way in the beginning of
          the statements marked with CF_HA_CLOSE flag.

    modified:
      mysql-test/r/handler_myisam.result
      mysql-test/r/merge.result
      mysql-test/r/temp_table.result
      mysql-test/t/handler_myisam.test
      mysql-test/t/merge.test
      mysql-test/t/temp_table.test
      sql/sp_head.cc
      sql/sql_admin.cc
      sql/sql_base.cc
      sql/sql_base.h
      sql/sql_class.h
      sql/sql_db.cc
      sql/sql_handler.cc
      sql/sql_insert.cc
      sql/sql_parse.cc
      sql/sql_prepare.cc
      sql/sql_rename.cc
      sql/sql_show.cc
      sql/sql_table.cc
      sql/sql_truncate.cc
      sql/sql_view.cc
=== modified file 'mysql-test/r/handler_myisam.result'
--- a/mysql-test/r/handler_myisam.result	2010-11-18 16:34:56 +0000
+++ b/mysql-test/r/handler_myisam.result	2010-11-29 14:13:07 +0000
@@ -1858,4 +1858,70 @@ a	b
 4	40
 HANDLER t1 CLOSE;
 DROP TABLE t1;
+#
+# Bug#27480
+#
+CREATE TEMPORARY TABLE t1 AS SELECT 1 AS f1;
+# -- CREATE TABLE
+HANDLER t1 OPEN;
+CREATE TABLE t2 SELECT * FROM t1;
+HANDLER t1 READ FIRST;
+ERROR 42S02: Unknown table 't1' in HANDLER
+DROP TABLE t2;
+# -- REPAIR TABLE
+HANDLER t1 OPEN;
+REPAIR TABLE t1;
+Table	Op	Msg_type	Msg_text
+test.t1	repair	status	OK
+HANDLER t1 READ FIRST;
+ERROR 42S02: Unknown table 't1' in HANDLER
+# -- ANALYZE TABLE
+HANDLER t1 OPEN;
+ANALYZE TABLE t1;
+Table	Op	Msg_type	Msg_text
+test.t1	analyze	status	OK
+HANDLER t1 READ FIRST;
+ERROR 42S02: Unknown table 't1' in HANDLER
+# -- OPTIMIZE TABLE
+HANDLER t1 OPEN;
+OPTIMIZE TABLE t1;
+Table	Op	Msg_type	Msg_text
+test.t1	optimize	status	OK
+HANDLER t1 READ FIRST;
+ERROR 42S02: Unknown table 't1' in HANDLER
+# -- CHECK TABLE
+HANDLER t1 OPEN;
+CHECK TABLE t1;
+Table	Op	Msg_type	Msg_text
+test.t1	check	status	OK
+HANDLER t1 READ FIRST;
+ERROR 42S02: Unknown table 't1' in HANDLER
+# -- ALTER TABLE
+HANDLER t1 OPEN;
+ALTER TABLE t1 ADD COLUMN b INT;
+HANDLER t1 READ FIRST;
+ERROR 42S02: Unknown table 't1' in HANDLER
+# -- DROP TABLE
+HANDLER t1 OPEN;
+DROP TABLE t1;
+HANDLER t1 READ FIRST;
+ERROR 42S02: Unknown table 't1' in HANDLER
+CREATE TEMPORARY TABLE t1(a INT, b INT, INDEX i(a));
+set global keycache1.key_cache_block_size=2048;
+set global keycache1.key_buffer_size=1*1024*1024;
+set global keycache1.key_buffer_size=1024*1024;
+# -- CACHE INDEX
+HANDLER t1 OPEN;
+CACHE INDEX t1 IN keycache1;
+Table	Op	Msg_type	Msg_text
+test.t1	assign_to_keycache	status	OK
+HANDLER t1 READ FIRST;
+ERROR 42S02: Unknown table 't1' in HANDLER
+# -- LOAD INDEX
+HANDLER t1 OPEN;
+LOAD INDEX INTO CACHE t1;
+Table	Op	Msg_type	Msg_text
+test.t1	preload_keys	status	OK
+HANDLER t1 READ FIRST;
+ERROR 42S02: Unknown table 't1' in HANDLER
 End of 5.1 tests

=== modified file 'mysql-test/r/merge.result'
--- a/mysql-test/r/merge.result	2010-11-17 15:23:17 +0000
+++ b/mysql-test/r/merge.result	2010-11-29 14:13:07 +0000
@@ -2716,10 +2716,7 @@ DROP TABLE tm1, t1, t2, t3, t4, t5;
 CREATE TEMPORARY TABLE t1 (c1 INT);
 ALTER TABLE t1 ENGINE=MERGE UNION(t_not_exists,t1);
 OPTIMIZE TABLE t1;
-Table	Op	Msg_type	Msg_text
-test.t1	optimize	Error	Table 'test.t_not_exists' doesn't exist
-test.t1	optimize	Error	Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
-test.t1	optimize	error	Corrupt
+ERROR HY000: Can't reopen table: 't1'
 DROP TABLE t1;
 #
 # Bug#36171 - CREATE TEMPORARY TABLE and MERGE engine

=== modified file 'mysql-test/r/temp_table.result'
--- a/mysql-test/r/temp_table.result	2010-08-30 06:38:09 +0000
+++ b/mysql-test/r/temp_table.result	2010-11-29 14:13:07 +0000
@@ -223,3 +223,46 @@ CREATE TEMPORARY TABLE bug48067.t1 (c1 i
 DROP DATABASE bug48067;
 DROP TEMPORARY table bug48067.t1;
 End of 5.1 tests
+CREATE TEMPORARY TABLE t1(a INT);
+CREATE TEMPORARY TABLE t2(b INT);
+CREATE TEMPORARY TABLE t3(c INT);
+INSERT INTO t1 VALUES (1), (2), (3);
+INSERT INTO t2 VALUES (11), (12), (13);
+INSERT INTO t3 VALUES (101), (102), (103);
+ANALYZE TABLE t1, t2, t3;
+Table	Op	Msg_type	Msg_text
+test.t1	analyze	status	OK
+test.t2	analyze	status	OK
+test.t3	analyze	status	OK
+INSERT INTO t1 VALUES (1), (2), (3);
+INSERT INTO t2 VALUES (11), (12), (13);
+INSERT INTO t3 VALUES (101), (102), (103);
+CHECK TABLE t1, t2, t3;
+Table	Op	Msg_type	Msg_text
+test.t1	check	status	OK
+test.t2	check	status	OK
+test.t3	check	status	OK
+INSERT INTO t1 VALUES (1), (2), (3);
+INSERT INTO t2 VALUES (11), (12), (13);
+INSERT INTO t3 VALUES (101), (102), (103);
+CHECKSUM TABLE t1, t2, t3;
+Table	Checksum
+test.t1	xxx
+test.t2	xxx
+test.t3	xxx
+INSERT INTO t1 VALUES (1), (2), (3);
+INSERT INTO t2 VALUES (11), (12), (13);
+INSERT INTO t3 VALUES (101), (102), (103);
+OPTIMIZE TABLE t1, t2, t3;
+Table	Op	Msg_type	Msg_text
+test.t1	optimize	status	OK
+test.t2	optimize	status	OK
+test.t3	optimize	status	OK
+INSERT INTO t1 VALUES (1), (2), (3);
+INSERT INTO t2 VALUES (11), (12), (13);
+INSERT INTO t3 VALUES (101), (102), (103);
+REPAIR TABLE t1, t2, t3;
+Table	Op	Msg_type	Msg_text
+test.t1	repair	status	OK
+test.t2	repair	status	OK
+test.t3	repair	status	OK

=== modified file 'mysql-test/t/handler_myisam.test'
--- a/mysql-test/t/handler_myisam.test	2010-06-09 10:45:04 +0000
+++ b/mysql-test/t/handler_myisam.test	2010-11-29 14:13:07 +0000
@@ -97,4 +97,73 @@ HANDLER t1 CLOSE;
 DROP TABLE t1;
 
 
+--echo #
+--echo # Bug#27480
+--echo #
+
+CREATE TEMPORARY TABLE t1 AS SELECT 1 AS f1; 
+
+--echo # -- CREATE TABLE
+HANDLER t1 OPEN;
+CREATE TABLE t2 SELECT * FROM t1;
+--error ER_UNKNOWN_TABLE
+HANDLER t1 READ FIRST;
+
+DROP TABLE t2;
+
+--echo # -- REPAIR TABLE
+HANDLER t1 OPEN;
+REPAIR TABLE t1;
+--error ER_UNKNOWN_TABLE
+HANDLER t1 READ FIRST;
+
+--echo # -- ANALYZE TABLE
+HANDLER t1 OPEN;
+ANALYZE TABLE t1;
+--error ER_UNKNOWN_TABLE
+HANDLER t1 READ FIRST;
+
+--echo # -- OPTIMIZE TABLE
+HANDLER t1 OPEN;
+OPTIMIZE TABLE t1;
+--error ER_UNKNOWN_TABLE
+HANDLER t1 READ FIRST;
+
+--echo # -- CHECK TABLE
+HANDLER t1 OPEN;
+CHECK TABLE t1;
+--error ER_UNKNOWN_TABLE
+HANDLER t1 READ FIRST;
+
+--echo # -- ALTER TABLE
+HANDLER t1 OPEN;
+ALTER TABLE t1 ADD COLUMN b INT;
+--error ER_UNKNOWN_TABLE
+HANDLER t1 READ FIRST;
+
+--echo # -- DROP TABLE
+HANDLER t1 OPEN;
+DROP TABLE t1;
+--error ER_UNKNOWN_TABLE
+HANDLER t1 READ FIRST;
+
+CREATE TEMPORARY TABLE t1(a INT, b INT, INDEX i(a));
+
+set global keycache1.key_cache_block_size=2048;
+set global keycache1.key_buffer_size=1*1024*1024;
+set global keycache1.key_buffer_size=1024*1024;
+
+--echo # -- CACHE INDEX
+HANDLER t1 OPEN;
+CACHE INDEX t1 IN keycache1;
+--error ER_UNKNOWN_TABLE
+HANDLER t1 READ FIRST;
+
+--echo # -- LOAD INDEX
+HANDLER t1 OPEN;
+LOAD INDEX INTO CACHE t1;
+--error ER_UNKNOWN_TABLE
+HANDLER t1 READ FIRST;
+
+
 --echo End of 5.1 tests

=== modified file 'mysql-test/t/merge.test'
--- a/mysql-test/t/merge.test	2010-10-26 09:10:59 +0000
+++ b/mysql-test/t/merge.test	2010-11-29 14:13:07 +0000
@@ -2195,6 +2195,7 @@ DROP TABLE tm1, t1, t2, t3, t4, t5;
 --echo #
 CREATE TEMPORARY TABLE t1 (c1 INT);
 ALTER TABLE t1 ENGINE=MERGE UNION(t_not_exists,t1);
+--error ER_CANT_REOPEN_TABLE
 OPTIMIZE TABLE t1;
 DROP TABLE t1;
 

=== modified file 'mysql-test/t/temp_table.test'
--- a/mysql-test/t/temp_table.test	2010-06-23 11:34:40 +0000
+++ b/mysql-test/t/temp_table.test	2010-11-29 14:13:07 +0000
@@ -251,3 +251,40 @@ DROP DATABASE bug48067;
 DROP TEMPORARY table bug48067.t1;
 
 --echo End of 5.1 tests
+
+# Check admin statements.
+
+CREATE TEMPORARY TABLE t1(a INT);
+CREATE TEMPORARY TABLE t2(b INT);
+CREATE TEMPORARY TABLE t3(c INT);
+
+INSERT INTO t1 VALUES (1), (2), (3);
+INSERT INTO t2 VALUES (11), (12), (13);
+INSERT INTO t3 VALUES (101), (102), (103);
+
+ANALYZE TABLE t1, t2, t3;
+
+INSERT INTO t1 VALUES (1), (2), (3);
+INSERT INTO t2 VALUES (11), (12), (13);
+INSERT INTO t3 VALUES (101), (102), (103);
+
+CHECK TABLE t1, t2, t3;
+
+INSERT INTO t1 VALUES (1), (2), (3);
+INSERT INTO t2 VALUES (11), (12), (13);
+INSERT INTO t3 VALUES (101), (102), (103);
+
+--replace_column 2 xxx
+CHECKSUM TABLE t1, t2, t3;
+
+INSERT INTO t1 VALUES (1), (2), (3);
+INSERT INTO t2 VALUES (11), (12), (13);
+INSERT INTO t3 VALUES (101), (102), (103);
+
+OPTIMIZE TABLE t1, t2, t3;
+
+INSERT INTO t1 VALUES (1), (2), (3);
+INSERT INTO t2 VALUES (11), (12), (13);
+INSERT INTO t3 VALUES (101), (102), (103);
+
+REPAIR TABLE t1, t2, t3;

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2010-11-29 11:28:55 +0000
+++ b/sql/sp_head.cc	2010-11-29 14:13:07 +0000
@@ -2952,6 +2952,9 @@ sp_lex_keeper::reset_lex_and_exec_core(T
   reinit_stmt_before_use(thd, m_lex);
 
   if (open_tables)
+    res= open_and_process_temporary_table_list(thd, m_lex->query_tables);
+
+  if (!res && open_tables)
     res= instr->exec_open_and_lock_tables(thd, m_lex->query_tables);
 
   if (!res)
@@ -4175,6 +4178,11 @@ sp_head::add_used_tables_to_table_list(T
       table->prelocking_placeholder= 1;
       table->belong_to_view= belong_to_view;
       table->trg_event_map= stab->trg_event_map;
+
+      table->open_type=
+        find_temporary_table(thd, table->db, table->table_name) ?
+        OT_TEMPORARY_OR_BASE : OT_BASE_ONLY;
+
       /*
         Since we don't allow DDL on base tables in prelocked mode it
         is safe to infer the type of metadata lock from the type of

=== modified file 'sql/sql_admin.cc'
--- a/sql/sql_admin.cc	2010-11-18 16:34:56 +0000
+++ b/sql/sql_admin.cc	2010-11-29 14:13:07 +0000
@@ -88,8 +88,7 @@ static int prepare_for_repair(THD *thd, 
                                  MDL_EXCLUSIVE, MDL_TRANSACTION);
 
     if (lock_table_names(thd, table_list, table_list->next_global,
-                         thd->variables.lock_wait_timeout,
-                         MYSQL_OPEN_SKIP_TEMPORARY))
+                         thd->variables.lock_wait_timeout, 0))
       DBUG_RETURN(0);
     has_mdl_lock= TRUE;
 
@@ -262,6 +261,8 @@ static bool mysql_admin_table(THD* thd, 
                               HA_CHECK_OPT* check_opt,
                               const char *operator_name,
                               thr_lock_type lock_type,
+                              bool any_priv_will_do,
+                              ulong op_privs,
                               bool open_for_modify,
                               bool no_warnings_for_error,
                               uint extra_open_options,
@@ -292,8 +293,6 @@ static bool mysql_admin_table(THD* thd, 
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 
-  mysql_ha_rm_tables(thd, tables);
-
   for (table= tables; table; table= table->next_local)
   {
     char table_name[NAME_LEN*2+2];
@@ -301,6 +300,29 @@ static bool mysql_admin_table(THD* thd, 
     bool fatal_error=0;
     bool open_error;
 
+    /*
+      Here we need to open list of tables (one table is not enough) because
+      of merge tables: children of merge tables must be opened here.
+    */
+
+    TABLE_LIST *next_global_tl= table->next_global;
+
+    if (open_and_process_temporary_table_list(thd, table))
+      DBUG_RETURN(TRUE);
+
+    TABLE_LIST *last_added_tl;
+
+    for (last_added_tl= table;
+         last_added_tl && last_added_tl->next_global != next_global_tl;
+         last_added_tl= last_added_tl->next_global)
+      ;
+
+    if (op_privs)
+    {
+      if (check_table_access(thd, op_privs, table, any_priv_will_do, 1, FALSE))
+        DBUG_RETURN(TRUE);
+    }
+
     DBUG_PRINT("admin", ("table: '%s'.'%s'", table->db, table->table_name));
     DBUG_PRINT("admin", ("extra_open_options: %u", extra_open_options));
     strxmov(table_name, db, ".", table->table_name, NullS);
@@ -316,11 +338,10 @@ static bool mysql_admin_table(THD* thd, 
                                 MDL_SHARED_NO_READ_WRITE : MDL_SHARED_READ);
     /* open only one table from local list of command */
     {
-      TABLE_LIST *save_next_global, *save_next_local;
-      save_next_global= table->next_global;
-      table->next_global= 0;
-      save_next_local= table->next_local;
-      table->next_local= 0;
+      TABLE_LIST *next_global_saved= last_added_tl->next_global;
+      TABLE_LIST *next_local_saved= last_added_tl->next_local;
+      last_added_tl->next_global= NULL;
+      last_added_tl->next_local= NULL;
       select->table_list.first= table;
       /*
         Time zone tables and SP tables can be add to lex->query_tables list,
@@ -343,71 +364,71 @@ static bool mysql_admin_table(THD* thd, 
 
       open_error= open_and_lock_tables(thd, table, TRUE, 0);
       thd->no_warnings_for_error= 0;
-      table->next_global= save_next_global;
-      table->next_local= save_next_local;
+      last_added_tl->next_global= next_global_saved;
+      last_added_tl->next_local= next_local_saved;
       thd->open_options&= ~extra_open_options;
+    }
 
-      /*
-        If open_and_lock_tables() failed, close_thread_tables() will close
-        the table and table->table can therefore be invalid.
-      */
-      if (open_error)
-        table->table= NULL;
+    /*
+      If open_and_lock_tables() failed, close_thread_tables() will close
+      the table and table->table can therefore be invalid.
+    */
+    if (open_error)
+      table->table= NULL;
 
+    /*
+      Under locked tables, we know that the table can be opened,
+      so any errors opening the table are logical errors.
+      In these cases it does not make sense to try to repair.
+    */
+    if (open_error && thd->locked_tables_mode)
+    {
+      result_code= HA_ADMIN_FAILED;
+      goto send_result;
+    }
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+    if (table->table)
+    {
       /*
-        Under locked tables, we know that the table can be opened,
-        so any errors opening the table are logical errors.
-        In these cases it does not make sense to try to repair.
+        Set up which partitions that should be processed
+        if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
+        CACHE INDEX/LOAD INDEX for specified partitions
       */
-      if (open_error && thd->locked_tables_mode)
-      {
-        result_code= HA_ADMIN_FAILED;
-        goto send_result;
-      }
-#ifdef WITH_PARTITION_STORAGE_ENGINE
-      if (table->table)
-      {
-        /*
-          Set up which partitions that should be processed
-          if ALTER TABLE t ANALYZE/CHECK/OPTIMIZE/REPAIR PARTITION ..
-          CACHE INDEX/LOAD INDEX for specified partitions
-        */
-        Alter_info *alter_info= &lex->alter_info;
+      Alter_info *alter_info= &lex->alter_info;
 
-        if (alter_info->flags & ALTER_ADMIN_PARTITION)
+      if (alter_info->flags & ALTER_ADMIN_PARTITION)
+      {
+        if (!table->table->part_info)
         {
-          if (!table->table->part_info)
-          {
-            my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
-            DBUG_RETURN(TRUE);
-          }
-          uint num_parts_found;
-          uint num_parts_opt= alter_info->partition_names.elements;
-          num_parts_found= set_part_state(alter_info, table->table->part_info,
-                                          PART_ADMIN);
-          if (num_parts_found != num_parts_opt &&
-              (!(alter_info->flags & ALTER_ALL_PARTITION)))
-          {
-            char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
-            size_t length;
-            DBUG_PRINT("admin", ("sending non existent partition error"));
-            protocol->prepare_for_resend();
-            protocol->store(table_name, system_charset_info);
-            protocol->store(operator_name, system_charset_info);
-            protocol->store(STRING_WITH_LEN("error"), system_charset_info);
-            length= my_snprintf(buff, sizeof(buff),
-                                ER(ER_DROP_PARTITION_NON_EXISTENT),
-                                table_name);
-            protocol->store(buff, length, system_charset_info);
-            if(protocol->write())
-              goto err;
-            my_eof(thd);
+          my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
+          DBUG_RETURN(TRUE);
+        }
+        uint num_parts_found;
+        uint num_parts_opt= alter_info->partition_names.elements;
+        num_parts_found= set_part_state(alter_info, table->table->part_info,
+                                        PART_ADMIN);
+        if (num_parts_found != num_parts_opt &&
+            (!(alter_info->flags & ALTER_ALL_PARTITION)))
+        {
+          char buff[FN_REFLEN + MYSQL_ERRMSG_SIZE];
+          size_t length;
+          DBUG_PRINT("admin", ("sending non existent partition error"));
+          protocol->prepare_for_resend();
+          protocol->store(table_name, system_charset_info);
+          protocol->store(operator_name, system_charset_info);
+          protocol->store(STRING_WITH_LEN("error"), system_charset_info);
+          length= my_snprintf(buff, sizeof(buff),
+                              ER(ER_DROP_PARTITION_NON_EXISTENT),
+                              table_name);
+          protocol->store(buff, length, system_charset_info);
+          if(protocol->write())
             goto err;
-          }
+          my_eof(thd);
+          goto err;
         }
       }
-#endif
     }
+#endif
     DBUG_PRINT("admin", ("table: 0x%lx", (long) table->table));
 
     if (prepare_func)
@@ -560,6 +581,15 @@ static bool mysql_admin_table(THD* thd, 
         trans_rollback(thd);
         close_thread_tables(thd);
         thd->mdl_context.release_transactional_locks();
+
+        /*
+          table_list->table has been closed and freed. Do not reference
+          uninitialized data. open_tables() could fail.
+        */
+        table->table= NULL;
+        /* Same applies to MDL ticket. */
+        table->mdl_request.ticket= NULL;
+
         tmp_disable_binlog(thd); // binlogging is done by caller if wanted
         result_code= mysql_recreate_table(thd, table);
         reenable_binlog(thd);
@@ -684,6 +714,15 @@ send_result_message:
       trans_commit(thd);
       close_thread_tables(thd);
       thd->mdl_context.release_transactional_locks();
+
+      /*
+         table_list->table has been closed and freed. Do not reference
+         uninitialized data. open_tables() could fail.
+       */
+      table->table= NULL;
+      /* Same applies to MDL ticket. */
+      table->mdl_request.ticket= NULL;
+
       DEBUG_SYNC(thd, "ha_admin_try_alter");
       protocol->store(STRING_WITH_LEN("note"), system_charset_info);
       protocol->store(STRING_WITH_LEN(
@@ -710,8 +749,8 @@ send_result_message:
       trans_commit(thd);
       close_thread_tables(thd);
       thd->mdl_context.release_transactional_locks();
-      table->table= NULL;
-      if (!result_code) // recreation went ok
+      if (!result_code && // recreation went ok
+          !table->table)  // not a temporary table opened above
       {
         /* Clear the ticket released above. */
         table->mdl_request.ticket= NULL;
@@ -824,7 +863,6 @@ send_result_message:
     trans_commit_implicit(thd);
     close_thread_tables(thd);
     thd->mdl_context.release_transactional_locks();
-
     /*
       If it is CHECK TABLE v1, v2, v3, and v1, v2, v3 are views, we will run
       separate open_tables() for each CHECK TABLE argument.
@@ -853,8 +891,6 @@ err:
   trans_rollback(thd);
   close_thread_tables(thd);			// Shouldn't be needed
   thd->mdl_context.release_transactional_locks();
-  if (table)
-    table->table=0;
   DBUG_RETURN(TRUE);
 }
 
@@ -890,8 +926,10 @@ bool mysql_assign_to_keycache(THD* thd, 
   mysql_mutex_unlock(&LOCK_global_system_variables);
   check_opt.key_cache= key_cache;
   DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
-				"assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
-				0, 0, &handler::assign_to_keycache, 0));
+                                "assign_to_keycache", TL_READ_NO_INSERT,
+                                FALSE, 0,
+                                FALSE, FALSE, 0, NULL,
+                                &handler::assign_to_keycache, NULL));
 }
 
 
@@ -917,8 +955,10 @@ bool mysql_preload_keys(THD* thd, TABLE_
     outdated information if parallel inserts into cache blocks happen.
   */
   DBUG_RETURN(mysql_admin_table(thd, tables, 0,
-				"preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
-				&handler::preload_keys, 0));
+                                "preload_keys", TL_READ_NO_INSERT,
+                                FALSE, 0,
+                                FALSE, FALSE, 0, NULL,
+                                &handler::preload_keys, NULL));
 }
 
 
@@ -929,13 +969,12 @@ bool Sql_cmd_analyze_table::execute(THD 
   thr_lock_type lock_type = TL_READ_NO_INSERT;
   DBUG_ENTER("Sql_cmd_analyze_table::execute");
 
-  if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table,
-                         FALSE, UINT_MAX, FALSE))
-    goto error;
   thd->enable_slow_log= opt_log_slow_admin_statements;
   res= mysql_admin_table(thd, first_table, &thd->lex->check_opt,
-                         "analyze", lock_type, 1, 0, 0, 0,
-                         &handler::ha_analyze, 0);
+                         "analyze", lock_type,
+                         FALSE, SELECT_ACL | INSERT_ACL,
+                         TRUE, FALSE, 0, NULL, &handler::ha_analyze, NULL);
+
   /* ! we write after unlocking the table */
   if (!res && !thd->lex->no_write_to_binlog)
   {
@@ -947,7 +986,6 @@ bool Sql_cmd_analyze_table::execute(THD 
   thd->lex->select_lex.table_list.first= first_table;
   thd->lex->query_tables= first_table;
 
-error:
   DBUG_RETURN(res);
 }
 
@@ -965,7 +1003,9 @@ bool Sql_cmd_check_table::execute(THD *t
   thd->enable_slow_log= opt_log_slow_admin_statements;
 
   res= mysql_admin_table(thd, first_table, &thd->lex->check_opt, "check",
-                         lock_type, 0, 0, HA_OPEN_FOR_REPAIR, 0,
+                         lock_type,
+                         TRUE, SELECT_ACL,
+                         FALSE, FALSE, HA_OPEN_FOR_REPAIR, NULL,
                          &handler::ha_check, &view_checksum);
 
   thd->lex->select_lex.table_list.first= first_table;
@@ -989,8 +1029,9 @@ bool Sql_cmd_optimize_table::execute(THD
   res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
     mysql_recreate_table(thd, first_table) :
     mysql_admin_table(thd, first_table, &thd->lex->check_opt,
-                      "optimize", TL_WRITE, 1, 0, 0, 0,
-                      &handler::ha_optimize, 0);
+                      "optimize", TL_WRITE,
+                      FALSE, SELECT_ACL | INSERT_ACL,
+                      TRUE, FALSE, 0, NULL, &handler::ha_optimize, 0);
   /* ! we write after unlocking the table */
   if (!res && !thd->lex->no_write_to_binlog)
   {
@@ -1018,10 +1059,12 @@ bool Sql_cmd_repair_table::execute(THD *
     goto error; /* purecov: inspected */
   thd->enable_slow_log= opt_log_slow_admin_statements;
   res= mysql_admin_table(thd, first_table, &thd->lex->check_opt, "repair",
-                         TL_WRITE, 1,
+                         TL_WRITE,
+                         FALSE, SELECT_ACL| INSERT_ACL,
+                         TRUE,
                          test(thd->lex->check_opt.sql_flags & TT_USEFRM),
                          HA_OPEN_FOR_REPAIR, &prepare_for_repair,
-                         &handler::ha_repair, 0);
+                         &handler::ha_repair, NULL);
 
   /* ! we write after unlocking the table */
   if (!res && !thd->lex->no_write_to_binlog)

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2010-11-23 22:37:59 +0000
+++ b/sql/sql_base.cc	2010-11-29 14:13:07 +0000
@@ -2557,46 +2557,42 @@ tdc_wait_for_old_version(THD *thd, const
 }
 
 
-/*
-  Open a table.
+/**
+  Open a base table.
 
-  SYNOPSIS
-    open_table()
-    thd                 Thread context.
-    table_list          Open first table in list.
-    action       INOUT  Pointer to variable of enum_open_table_action type
+  @param thd            Thread context.
+  @param table_list     Open first table in list.
+  @param action[in,out] Pointer to variable of enum_open_table_action type
                         which will be set according to action which is
                         required to remedy problem appeared during attempt
                         to open table.
-    flags               Bitmap of flags to modify how open works:
-                          MYSQL_OPEN_IGNORE_FLUSH - Open table even if
-                          someone has done a flush or there is a pending
-                          exclusive metadata lock requests against it
-                          (i.e. request high priority metadata lock).
-                          No version number checking is done.
-                          MYSQL_OPEN_TEMPORARY_ONLY - Open only temporary
-                          table not the base table or view.
-                          MYSQL_OPEN_TAKE_UPGRADABLE_MDL - Obtain upgradable
-                          metadata lock for tables on which we are going to
-                          take some kind of write table-level lock.
-
-  IMPLEMENTATION
-    Uses a cache of open tables to find a table not in use.
-
-    If TABLE_LIST::open_strategy is set to OPEN_IF_EXISTS, the table is opened
-    only if it exists. If the open strategy is OPEN_STUB, the underlying table
-    is never opened. In both cases, metadata locks are always taken according
-    to the lock strategy.
-
-  RETURN
-    TRUE  Open failed. "action" parameter may contain type of action
-          needed to remedy problem before retrying again.
-    FALSE Success. Members of TABLE_LIST structure are filled properly (e.g.
-          TABLE_LIST::table is set for real tables and TABLE_LIST::view is
-          set for views).
+  @param flags          Bitmap of flags to modify how open works:
+                        MYSQL_OPEN_IGNORE_FLUSH - Open table even if
+                        someone has done a flush or there is a pending
+                        exclusive metadata lock requests against it (i.e.
+                        request high priority metadata lock). No version
+                        number checking is done.
+                        MYSQL_OPEN_TAKE_UPGRADABLE_MDL - Obtain upgradable
+                        metadata lock for tables on which we are going to
+                        take some kind of write table-level lock.
+
+  Uses a cache of open tables to find a TABLE instance not in use.
+
+  If TABLE_LIST::open_strategy is set to OPEN_IF_EXISTS, the table is
+  opened only if it exists. If the open strategy is OPEN_STUB, the
+  underlying table is never opened. In both cases, metadata locks are
+  always taken according to the lock strategy.
+
+  The function used to open temporary tables, but now it opens base tables
+  only.
+
+  @retval TRUE  Open failed. "action" parameter may contain type of action
+                needed to remedy problem before retrying again.
+  @retval FALSE Success. Members of TABLE_LIST structure are filled properly
+                (e.g.  TABLE_LIST::table is set for real tables and
+                TABLE_LIST::view is set for views).
 */
 
-
 bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
                 Open_table_context *ot_ctx)
 {
@@ -2611,6 +2607,14 @@ bool open_table(THD *thd, TABLE_LIST *ta
   my_hash_value_type hash_value;
   DBUG_ENTER("open_table");
 
+  /*
+    The table must not be opened already. The table can be pre-opened for
+    some statements if it is a temporary table.
+
+    open_temporary_table() must be used to open temporary tables.
+  */
+  DBUG_ASSERT(!table_list->table);
+
   /* an open table operation needs a lot of the stack space */
   if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias))
     DBUG_RETURN(TRUE);
@@ -2622,62 +2626,10 @@ bool open_table(THD *thd, TABLE_LIST *ta
                TMP_TABLE_KEY_EXTRA);
 
   /*
-    Unless requested otherwise, try to resolve this table in the list
-    of temporary tables of this thread. In MySQL temporary tables
-    are always thread-local and "shadow" possible base tables with the
-    same name. This block implements the behaviour.
-    TODO: move this block into a separate function.
-  */
-  if (table_list->open_type != OT_BASE_ONLY &&
-      ! (flags & MYSQL_OPEN_SKIP_TEMPORARY))
-  {
-    for (table= thd->temporary_tables; table ; table=table->next)
-    {
-      if (table->s->table_cache_key.length == key_length +
-          TMP_TABLE_KEY_EXTRA &&
-	  !memcmp(table->s->table_cache_key.str, key,
-		  key_length + TMP_TABLE_KEY_EXTRA))
-      {
-        /*
-          We're trying to use the same temporary table twice in a query.
-          Right now we don't support this because a temporary table
-          is always represented by only one TABLE object in THD, and
-          it can not be cloned. Emit an error for an unsupported behaviour.
-        */
-	if (table->query_id)
-	{
-          DBUG_PRINT("error",
-                     ("query_id: %lu  server_id: %u  pseudo_thread_id: %lu",
-                      (ulong) table->query_id, (uint) thd->server_id,
-                      (ulong) thd->variables.pseudo_thread_id));
-	  my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias);
-	  DBUG_RETURN(TRUE);
-	}
-	table->query_id= thd->query_id;
-	thd->thread_specific_used= TRUE;
-        DBUG_PRINT("info",("Using temporary table"));
-        goto reset;
-      }
-    }
-  }
-
-  if (table_list->open_type == OT_TEMPORARY_ONLY ||
-      (flags & MYSQL_OPEN_TEMPORARY_ONLY))
-  {
-    if (table_list->open_strategy == TABLE_LIST::OPEN_NORMAL)
-    {
-      my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->table_name);
-      DBUG_RETURN(TRUE);
-    }
-    else
-      DBUG_RETURN(FALSE);
-  }
-
-  /*
-    The table is not temporary - if we're in pre-locked or LOCK TABLES
-    mode, let's try to find the requested table in the list of pre-opened
-    and locked tables. If the table is not there, return an error - we can't
-    open not pre-opened tables in pre-locked/LOCK TABLES mode.
+    If we're in pre-locked or LOCK TABLES mode, let's try to find the
+    requested table in the list of pre-opened and locked tables. If the
+    table is not there, return an error - we can't open not pre-opened
+    tables in pre-locked/LOCK TABLES mode.
     TODO: move this block into a separate function.
   */
   if (thd->locked_tables_mode &&
@@ -2736,6 +2688,7 @@ bool open_table(THD *thd, TABLE_LIST *ta
       DBUG_PRINT("info",("Using locked table"));
       goto reset;
     }
+
     /*
       Is this table a view and not a base table?
       (it is work around to allow to open view with locked tables,
@@ -2773,7 +2726,7 @@ bool open_table(THD *thd, TABLE_LIST *ta
     }
     /*
       No table in the locked tables list. In case of explicit LOCK TABLES
-      this can happen if a user did not include the able into the list.
+      this can happen if a user did not include the table into the list.
       In case of pre-locked mode locked tables list is generated automatically,
       so we may only end up here if the table did not exist when
       locked tables list was created.
@@ -2785,10 +2738,7 @@ bool open_table(THD *thd, TABLE_LIST *ta
     DBUG_RETURN(TRUE);
   }
 
-  /*
-    Non pre-locked/LOCK TABLES mode, and the table is not temporary.
-    This is the normal use case.
-  */
+  /* Non pre-locked/LOCK TABLES mode. This is the normal use case. */
 
   if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
   {
@@ -3997,8 +3947,7 @@ recover_from_failed_open(THD *thd)
     case OT_DISCOVER:
       {
         if ((result= lock_table_names(thd, m_failed_table, NULL,
-                                      get_timeout(),
-                                      MYSQL_OPEN_SKIP_TEMPORARY)))
+                                      get_timeout(), 0)))
           break;
 
         tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
@@ -4014,8 +3963,7 @@ recover_from_failed_open(THD *thd)
     case OT_REPAIR:
       {
         if ((result= lock_table_names(thd, m_failed_table, NULL,
-                                      get_timeout(),
-                                      MYSQL_OPEN_SKIP_TEMPORARY)))
+                                      get_timeout(), 0)))
           break;
 
         tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
@@ -4344,6 +4292,25 @@ open_and_process_table(THD *thd, LEX *le
     error= TRUE;
     goto end;
   }
+
+  /*
+    If this TABLE_LIST object has an associated open TABLE object
+    (TABLE_LIST::table is not NULL), that TABLE object must be a pre-opened
+    temporary table.
+  */
+  if (tables->table)
+  {
+    DBUG_ASSERT(is_temporary_table(tables->table));
+    DBUG_RETURN(FALSE);
+  }
+
+  /*
+    If we're in CREATE TEMPORARY TABLE statement, we should not proceed
+    to the actual opening.
+  */
+  if (tables->open_type == OT_TEMPORARY_ONLY)
+    DBUG_RETURN(FALSE);
+
   DBUG_PRINT("tcache", ("opening table: '%s'.'%s'  item: %p",
                         tables->db, tables->table_name, tables)); //psergey: invalid read of size 1 here
   (*counter)++;
@@ -4353,6 +4320,26 @@ open_and_process_table(THD *thd, LEX *le
 
   if (tables->prelocking_placeholder)
   {
+    if (tables->open_type == OT_TEMPORARY_OR_BASE)
+    {
+      /*
+        We're opening a table from the prelocking list. When adding to this
+        list, TABLE_LIST::open_type is set as follows:
+          - if there is a temporary table with that name,
+            open_type is OT_TEMPORARY_OR_BASE (there might be also a base
+            table, but that's not important because temporary tables take
+            precedence over base ones);
+          - otherwise (no temporary table), open_type is OT_BASE_ONLY.
+
+        If there is a temporary table for that TABLE_LIST instance, there
+        is no reason to pre-open base table. Proper table will be choosen
+        and opened during execution.
+      */
+      DBUG_RETURN(FALSE);
+    }
+
+    DBUG_ASSERT(tables->open_type == OT_BASE_ONLY);
+
     /*
       For the tables added by the pre-locking code, attempt to open
       the table but fail silently if the table does not exist.
@@ -4454,6 +4441,7 @@ open_and_process_table(THD *thd, LEX *le
       goto end;
   }
 
+  /* Set appropriate TABLE::lock_type. */
   if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables_mode)
   {
     if (tables->lock_type == TL_WRITE_DEFAULT)
@@ -4464,6 +4452,8 @@ open_and_process_table(THD *thd, LEX *le
     else
       tables->table->reginfo.lock_type= tables->lock_type;
   }
+
+  /* Copy grant information from TABLE_LIST instance to TABLE one. */
   tables->table->grant= tables->grant;
 
   /* Check and update metadata version of a base table. */
@@ -4551,18 +4541,17 @@ lock_table_names(THD *thd,
   for (table= tables_start; table && table != tables_end;
        table= table->next_global)
   {
-    if (table->mdl_request.type >= MDL_SHARED_NO_WRITE &&
-        !(table->open_type == OT_TEMPORARY_ONLY ||
-          (flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
-          (table->open_type != OT_BASE_ONLY &&
-           ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
-           find_temporary_table(thd, table))))
+    if (table->mdl_request.type < MDL_SHARED_NO_WRITE ||
+        table->open_type == OT_TEMPORARY_ONLY ||
+        (table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table)))
     {
-      if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) &&
-          schema_set.insert(table))
-        return TRUE;
-      mdl_requests.push_front(&table->mdl_request);
+      continue;
     }
+
+    if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) && schema_set.insert(table))
+      return TRUE;
+
+    mdl_requests.push_front(&table->mdl_request);
   }
 
   if (! (flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK) &&
@@ -4630,35 +4619,34 @@ open_tables_check_upgradable_mdl(THD *th
   for (table= tables_start; table && table != tables_end;
        table= table->next_global)
   {
-    if (table->mdl_request.type >= MDL_SHARED_NO_WRITE &&
-        !(table->open_type == OT_TEMPORARY_ONLY ||
-          (flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
-          (table->open_type != OT_BASE_ONLY &&
-           ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
-           find_temporary_table(thd, table))))
-    {
-      /*
-        We don't need to do anything about the found TABLE instance as it
-        will be handled later in open_tables(), we only need to check that
-        an upgradable lock is already acquired. When we enter LOCK TABLES
-        mode, SNRW locks are acquired before all other locks. So if under
-        LOCK TABLES we find that there is TABLE instance with upgradeable
-        lock, all other instances of TABLE for the same table will have the
-        same ticket.
-
-        Note that this works OK even for CREATE TABLE statements which
-        request X type of metadata lock. This is because under LOCK TABLES
-        such statements don't create the table but only check if it exists
-        or, in most complex case, only insert into it.
-        Thus SNRW lock should be enough.
-
-        Note that find_table_for_mdl_upgrade() will report an error if
-        no suitable ticket is found.
-      */
-      if (!find_table_for_mdl_upgrade(thd->open_tables, table->db,
-                                      table->table_name, FALSE))
-        return TRUE;
+    if (table->mdl_request.type < MDL_SHARED_NO_WRITE ||
+        table->open_type == OT_TEMPORARY_ONLY ||
+        (table->open_type == OT_TEMPORARY_OR_BASE && is_temporary_table(table)))
+    {
+      continue;
     }
+
+    /*
+      We don't need to do anything about the found TABLE instance as it
+      will be handled later in open_tables(), we only need to check that
+      an upgradable lock is already acquired. When we enter LOCK TABLES
+      mode, SNRW locks are acquired before all other locks. So if under
+      LOCK TABLES we find that there is TABLE instance with upgradeable
+      lock, all other instances of TABLE for the same table will have the
+      same ticket.
+
+      Note that this works OK even for CREATE TABLE statements which
+      request X type of metadata lock. This is because under LOCK TABLES
+      such statements don't create the table but only check if it exists
+      or, in most complex case, only insert into it.
+      Thus SNRW lock should be enough.
+
+      Note that find_table_for_mdl_upgrade() will report an error if
+      no suitable ticket is found.
+    */
+    if (!find_table_for_mdl_upgrade(thd->open_tables, table->db,
+                                    table->table_name, FALSE))
+      return TRUE;
   }
 
   return FALSE;
@@ -4837,6 +4825,10 @@ restart:
           if (ot_ctx.recover_from_failed_open(thd))
             goto err;
 
+          /* Re-open temporary tables after close_tables_for_reopen(). */
+          if (open_and_process_temporary_table_list(thd, *start))
+            goto err;
+
           error= FALSE;
           goto restart;
         }
@@ -4883,6 +4875,10 @@ restart:
             if (ot_ctx.recover_from_failed_open(thd))
               goto err;
 
+            /* Re-open temporary tables after close_tables_for_reopen(). */
+            if (open_and_process_temporary_table_list(thd, *start))
+              goto err;
+
             error= FALSE;
             goto restart;
           }
@@ -4913,6 +4909,11 @@ restart:
   {
     TABLE *tbl= tables->table;
 
+    /*
+      NOTE: temporary merge tables should be processed here too, because
+      a temporary merge table can be based on non-temporary tables.
+    */
+
     /* Schema tables may not have a TABLE object here. */
     if (tbl && tbl->file->ht->db_type == DB_TYPE_MRG_MYISAM)
     {
@@ -5942,6 +5943,153 @@ static void update_field_dependencies(TH
 }
 
 
+/**
+  Find a temporary table specified by TABLE_LIST instance in the cache and
+  prepare its TABLE instance for use.
+
+  This function tries to resolve this table in the list of temporary tables
+  of this thread. Temporary tables are thread-local and "shadow" base
+  tables with the same name.
+
+  open_temporary_table() should be considered as an internal function.
+  Usually, open_and_process_temporary_table() should be used instead to
+  open temporary table completely. The only case where
+  open_temporary_table() is used directly is implementation of INSERT INTO
+  statement (sql_insert.cc).
+
+  Note: we used to check global_read_lock before opening temporary tables.
+  However, that limitation was artificial and is removed now.
+
+  @return Error status.
+    @retval FALSE On success. If a temporary table exists for the given
+                  key, tl->table is set.
+    @retval TRUE  On error. my_error() has been called.
+*/
+
+bool open_temporary_table(THD *thd, TABLE_LIST *tl)
+{
+  DBUG_ENTER("open_temporary_table");
+  DBUG_PRINT("enter", ("table: '%s'.'%s'", tl->db, tl->table_name));
+
+  if (tl->open_type == OT_BASE_ONLY)
+  {
+    DBUG_PRINT("info", ("skip_temporary is set"));
+
+    tl->table= NULL;
+    DBUG_RETURN(FALSE);
+  }
+
+  char table_key[MAX_DBKEY_LENGTH];
+  uint table_key_length= create_table_def_key(thd, table_key, tl, 1);
+
+  TABLE *table= find_temporary_table(thd, table_key, table_key_length);
+
+  if (!table)
+  {
+    tl->table= NULL;
+    DBUG_RETURN(FALSE);
+  }
+
+  if (table->query_id)
+  {
+    /*
+      We're trying to use the same temporary table twice in a query.
+      Right now we don't support this because a temporary table is always
+      represented by only one TABLE object in THD, and it can not be
+      cloned. Emit an error for an unsupported behaviour.
+    */
+
+    DBUG_PRINT("error",
+               ("query_id: %lu  server_id: %u  pseudo_thread_id: %lu",
+                (ulong) table->query_id, (uint) thd->server_id,
+                (ulong) thd->variables.pseudo_thread_id));
+    my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias);
+    DBUG_RETURN(TRUE);
+  }
+
+  table->query_id= thd->query_id;
+  thd->thread_specific_used= TRUE;
+
+  tl->updatable= 1; // It is not derived table nor non-updatable VIEW.
+  tl->table= table;
+
+  table->init(thd, tl);
+
+  DBUG_PRINT("info", ("Using temporary table"));
+  DBUG_RETURN(FALSE);
+}
+
+
+bool open_and_process_temporary_table_list(THD *thd, TABLE_LIST *tl_list)
+{
+  DBUG_ENTER("open_and_process_temporary_table_list");
+
+  for (TABLE_LIST *tl= tl_list; tl; tl= tl->next_global)
+  {
+    if (open_and_process_temporary_table(thd, tl))
+      DBUG_RETURN(TRUE);
+  }
+
+  DBUG_RETURN(FALSE);
+}
+
+
+/**
+  Open a temporary table specified by TABLE_LIST instance.
+
+  @return Error status.
+    @retval FALSE On success. Opened temporary table is returned in
+                  TABLE_LIST::table member. If TABLE_LIST::table is NULL,
+                  the specified temporary table does not exist.
+    @retval TRUE  On error. my_error() has been called.
+*/
+
+bool open_and_process_temporary_table(THD *thd, TABLE_LIST *tl)
+{
+  DBUG_ENTER("open_and_process_temporary_table");
+
+  if (tl->schema_table || tl->derived)
+    DBUG_RETURN(FALSE);
+
+  if (tl->table)
+    DBUG_RETURN(FALSE); // The table is already open.
+
+  if (open_temporary_table(thd, tl))
+    DBUG_RETURN(TRUE);
+
+  /*
+    If 'tl->table' is NULL, that means there is no temporary table for the
+    table specified by 'tl'. This is not an error.
+  */
+  if (!tl->table)
+    DBUG_RETURN(FALSE);
+
+  /* Set appropriate TABLE::lock_type. */
+  if (tl->lock_type != TL_UNLOCK && !thd->locked_tables_mode)
+  {
+    if (tl->lock_type == TL_WRITE_DEFAULT)
+      tl->table->reginfo.lock_type= thd->update_lock_default;
+    else if (tl->lock_type == TL_READ_DEFAULT)
+      tl->table->reginfo.lock_type= read_lock_type_for_table(thd, thd->lex, tl);
+    else
+      tl->table->reginfo.lock_type= tl->lock_type;
+  }
+
+  /* Check and update metadata version of a base table. */
+  if (check_and_update_table_version(thd, tl, tl->table->s))
+    DBUG_RETURN(TRUE);
+
+  /* MERGE tables need to access parent and child TABLE_LISTs. */
+  DBUG_ASSERT(tl->table->pos_in_table_list == tl);
+
+  /* Non-MERGE tables ignore this call. */
+  if (tl->table->file->extra(HA_EXTRA_ADD_CHILDREN_LIST))
+    DBUG_RETURN(TRUE);
+
+  DBUG_RETURN(FALSE);
+}
+
+
 /*
   Find a field by name in a view that uses merge algorithm.
 

=== modified file 'sql/sql_base.h'
--- a/sql/sql_base.h	2010-11-23 22:37:59 +0000
+++ b/sql/sql_base.h	2010-11-29 14:13:07 +0000
@@ -93,7 +93,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST 
 /* mysql_lock_tables() and open_table() flags bits */
 #define MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK      0x0001
 #define MYSQL_OPEN_IGNORE_FLUSH                 0x0002
-#define MYSQL_OPEN_TEMPORARY_ONLY               0x0004
+/* MYSQL_OPEN_TEMPORARY_ONLY (0x0004) is not used anymore. */
 #define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY      0x0008
 #define MYSQL_LOCK_LOG_TABLE                    0x0010
 /**
@@ -106,8 +106,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST 
   a new instance of the table.
 */
 #define MYSQL_OPEN_GET_NEW_TABLE                0x0040
-/** Don't look up the table in the list of temporary tables. */
-#define MYSQL_OPEN_SKIP_TEMPORARY               0x0080
+/* 0x0080 used to be MYSQL_OPEN_SKIP_TEMPORARY */
 /** Fail instead of waiting when conficting metadata lock is discovered. */
 #define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT         0x0100
 /** Open tables using MDL_SHARED lock instead of one specified in parser. */
@@ -139,7 +138,6 @@ TABLE *open_ltable(THD *thd, TABLE_LIST 
                             MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |\
                             MYSQL_LOCK_IGNORE_TIMEOUT |\
                             MYSQL_OPEN_GET_NEW_TABLE |\
-                            MYSQL_OPEN_SKIP_TEMPORARY |\
                             MYSQL_OPEN_HAS_MDL_LOCK)
 
 bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
@@ -269,6 +267,9 @@ void close_temporary_table(THD *thd, TAB
 void close_temporary(TABLE *table, bool free_share, bool delete_table);
 bool rename_temporary_table(THD* thd, TABLE *table, const char *new_db,
 			    const char *table_name);
+bool open_and_process_temporary_table_list(THD *thd, TABLE_LIST *tl_list);
+bool open_and_process_temporary_table(THD *thd, TABLE_LIST *tl);
+bool open_temporary_table(THD *thd, TABLE_LIST *tl);
 bool is_equal(const LEX_STRING *a, const LEX_STRING *b);
 
 /* Functions to work with system tables. */
@@ -572,6 +573,31 @@ private:
 
 
 /**
+  Indicate if a TABLE instance represents a temporary table.
+*/
+
+inline bool is_temporary_table(TABLE *table)
+{
+  /*
+    NOTE: 'table->s' might be NULL for specially constructed TABLE_LIST
+    instances, like for SHOW TRIGGERS LIKE ...
+  */
+
+  return table->s && table->s->tmp_table != NO_TMP_TABLE;
+}
+
+
+/**
+  Indicate if a TABLE_LIST instance represents a temporary table.
+*/
+
+inline bool is_temporary_table(TABLE_LIST *tl)
+{
+  return tl->table ? is_temporary_table(tl->table) : FALSE;
+}
+
+
+/**
   This internal handler is used to trap ER_NO_SUCH_TABLE.
 */
 

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2010-11-29 11:28:55 +0000
+++ b/sql/sql_class.h	2010-11-29 14:13:07 +0000
@@ -3699,6 +3699,18 @@ public:
 */
 #define CF_WRITE_RPL_INFO_COMMAND (1U << 12)
 
+/**
+  Identifies statements which may deal with temporary tables and for which
+  temporary tables should be pre-opened to simplify privilege checks.
+*/
+#define CF_PREOPEN_TMP_TABLES   (1U << 12)
+
+/**
+  Identfies statements for which open handlers should be closed in the
+  beginning of the statement.
+*/
+#define CF_HA_CLOSE             (1U << 13)
+
 /* Bits in server_command_flags */
 
 /**

=== modified file 'sql/sql_db.cc'
--- a/sql/sql_db.cc	2010-11-18 16:34:56 +0000
+++ b/sql/sql_db.cc	2010-11-29 14:13:07 +0000
@@ -823,8 +823,7 @@ bool mysql_rm_db(THD *thd,char *db,bool 
   }
 
   /* Lock all tables and stored routines about to be dropped. */
-  if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout,
-                       MYSQL_OPEN_SKIP_TEMPORARY) ||
+  if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout, 0) ||
       lock_db_routines(thd, db))
   {
     thd->pop_internal_handler();

=== modified file 'sql/sql_handler.cc'
--- a/sql/sql_handler.cc	2010-11-18 16:34:56 +0000
+++ b/sql/sql_handler.cc	2010-11-29 14:13:07 +0000
@@ -287,12 +287,13 @@ bool mysql_ha_open(THD *thd, TABLE_LIST 
   */
   DBUG_ASSERT(! hash_tables->table);
 
-  /*
-    We use open_tables() here, rather than, say,
-    open_ltable() or open_table() because we would like to be able
-    to open a temporary table.
-  */
-  error= open_tables(thd, &hash_tables, &counter, 0);
+  error= open_and_process_temporary_table(thd, hash_tables);
+
+  if (is_temporary_table(hash_tables))
+    hash_tables->table->grant= hash_tables->grant;
+
+  if (!error && !hash_tables->table)
+    error= open_tables(thd, &hash_tables, &counter, 0);
 
   if (! error &&
       ! (hash_tables->table->file->ha_table_flags() & HA_CAN_SQL_HANDLER))

=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc	2010-11-29 03:17:40 +0000
+++ b/sql/sql_insert.cc	2010-11-29 14:13:07 +0000
@@ -3782,8 +3782,7 @@ static TABLE *create_table_from_items(TH
       }
       else
       {
-        Open_table_context ot_ctx(thd, MYSQL_OPEN_TEMPORARY_ONLY);
-        if (open_table(thd, create_table, thd->mem_root, &ot_ctx))
+        if (open_temporary_table(thd, create_table))
         {
           /*
             This shouldn't happen as creation of temporary table should make
@@ -3793,7 +3792,9 @@ static TABLE *create_table_from_items(TH
           drop_temporary_table(thd, create_table, NULL);
         }
         else
+        {
           table= create_table->table;
+        }
       }
     }
     if (!table)                                   // open failed

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2010-11-29 11:28:55 +0000
+++ b/sql/sql_parse.cc	2010-11-29 14:13:07 +0000
@@ -23,7 +23,7 @@
                               // set_handler_table_locks,
                               // lock_global_read_lock,
                               // make_global_read_lock_block_commit
-#include "sql_base.h"         // find_temporary_tablesx
+#include "sql_base.h"         // find_temporary_table
 #include "sql_cache.h"        // QUERY_CACHE_FLAGS_SIZE, query_cache_*
 #include "sql_show.h"         // mysqld_list_*, mysqld_show_*,
                               // calc_sum_of_all_status
@@ -43,7 +43,6 @@
 #include "sql_table.h"        // mysql_create_like_table,
                               // mysql_create_table,
                               // mysql_alter_table,
-                              // mysql_recreate_table,
                               // mysql_backup_table,
                               // mysql_restore_table
 #include "sql_reload.h"       // reload_acl_and_cache
@@ -370,7 +369,7 @@ void init_update_queries(void)
   sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND;
   sql_command_flags[SQLCOM_SHOW_GRANTS]=      CF_STATUS_COMMAND;
   sql_command_flags[SQLCOM_SHOW_CREATE_DB]=   CF_STATUS_COMMAND;
-  sql_command_flags[SQLCOM_SHOW_CREATE]=  CF_STATUS_COMMAND;
+  sql_command_flags[SQLCOM_SHOW_CREATE]=      CF_STATUS_COMMAND;
   sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND;
   sql_command_flags[SQLCOM_SHOW_SLAVE_STAT]=  CF_STATUS_COMMAND;
   sql_command_flags[SQLCOM_SHOW_CREATE_PROC]= CF_STATUS_COMMAND;
@@ -446,6 +445,53 @@ void init_update_queries(void)
   sql_command_flags[SQLCOM_CREATE_SERVER]=      CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_ALTER_SERVER]=       CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_DROP_SERVER]=        CF_AUTO_COMMIT_TRANS;
+
+  /*
+    The following statements can deal with temporary tables,
+    so temporary tables should be pre-opened for those statements to
+    simplify privilege checking.
+
+    There are other statements that deal with temporary tables and open
+    them, but which are not listed here. The thing is that the order of
+    pre-opening temporary tables for those statements is somewhat custom.
+  */
+
+  sql_command_flags[SQLCOM_CREATE_TABLE]|=    CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_CREATE_INDEX]|=    CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_ALTER_TABLE]|=     CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_TRUNCATE]|=        CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_LOAD]|=            CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_DROP_INDEX]|=      CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_CREATE_VIEW]|=     CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_UPDATE]|=          CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_UPDATE_MULTI]|=    CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_INSERT]|=          CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_INSERT_SELECT]|=   CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_DELETE]|=          CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_DELETE_MULTI]|=    CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_REPLACE]|=         CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_REPLACE_SELECT]|=  CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_SELECT]|=          CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_SET_OPTION]|=      CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_DO]|=              CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_CALL]|=            CF_PREOPEN_TMP_TABLES;
+  sql_command_flags[SQLCOM_CHECKSUM]|=        CF_PREOPEN_TMP_TABLES;
+
+  /*
+    DDL statements should start with closing opened handlers.
+  */
+
+  sql_command_flags[SQLCOM_CREATE_TABLE]|=    CF_HA_CLOSE;
+  sql_command_flags[SQLCOM_ALTER_TABLE]|=     CF_HA_CLOSE;
+  sql_command_flags[SQLCOM_TRUNCATE]|=        CF_HA_CLOSE;
+  sql_command_flags[SQLCOM_REPAIR]|=          CF_HA_CLOSE;
+  sql_command_flags[SQLCOM_OPTIMIZE]|=        CF_HA_CLOSE;
+  sql_command_flags[SQLCOM_ANALYZE]|=         CF_HA_CLOSE;
+  sql_command_flags[SQLCOM_CHECK]|=           CF_HA_CLOSE;
+  sql_command_flags[SQLCOM_CREATE_INDEX]=     CF_HA_CLOSE;
+  sql_command_flags[SQLCOM_DROP_INDEX]=       CF_HA_CLOSE;
+  sql_command_flags[SQLCOM_PRELOAD_KEYS]|=    CF_HA_CLOSE;
+  sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]|=  CF_HA_CLOSE;
 }
 
 bool sqlcom_can_generate_row_events(const THD *thd)
@@ -1200,6 +1246,9 @@ bool dispatch_command(enum enum_server_c
     thd->set_query(fields, query_length);
     general_log_print(thd, command, "%s %s", table_list.table_name, fields);
 
+    if (open_and_process_temporary_table(thd, &table_list))
+      break;
+
     if (check_table_access(thd, SELECT_ACL, &table_list,
                            TRUE, UINT_MAX, FALSE))
       break;
@@ -2017,6 +2066,15 @@ mysql_execute_command(THD *thd)
     DEBUG_SYNC(thd,"before_execute_sql_command");
 #endif
 
+  if (sql_command_flags[lex->sql_command] & CF_HA_CLOSE)
+    mysql_ha_rm_tables(thd, all_tables);
+
+  if (sql_command_flags[lex->sql_command] & CF_PREOPEN_TMP_TABLES)
+  {
+    if (open_and_process_temporary_table_list(thd, all_tables))
+      goto error;
+  }
+
   switch (lex->sql_command) {
 
   case SQLCOM_SHOW_EVENTS:
@@ -2089,7 +2147,7 @@ mysql_execute_command(THD *thd)
     res= execute_sqlcom_select(thd, all_tables);
     break;
   }
-case SQLCOM_PREPARE:
+  case SQLCOM_PREPARE:
   {
     mysql_sql_stmt_prepare(thd);
     break;
@@ -2318,9 +2376,20 @@ case SQLCOM_PREPARE:
       goto end_with_restore_list;
     }
 
+    if (lex->create_info.merge_list.elements)
+    {
+      if (open_and_process_temporary_table_list(
+            thd, lex->create_info.merge_list.first))
+      {
+        res= 1;
+        goto end_with_restore_list;
+      }
+    }
+
     if ((res= create_table_precheck(thd, select_tables, create_table)))
       goto end_with_restore_list;
 
+
     /* Might have been updated in create_table_precheck */
     create_info.alias= create_table->alias;
 
@@ -2358,9 +2427,6 @@ case SQLCOM_PREPARE:
     }
 #endif
 
-    /* Close any open handlers for the table. */
-    mysql_ha_rm_tables(thd, create_table);
-
     if (select_lex->item_list.elements)		// With select
     {
       select_result *result;
@@ -2518,6 +2584,7 @@ end_with_restore_list:
       goto error;
 
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
+
     if (check_one_table_access(thd, INDEX_ACL, all_tables))
       goto error; /* purecov: inspected */
     /*
@@ -2662,6 +2729,13 @@ end_with_restore_list:
       else
       {
         /*
+          Temporary tables should be opened for SHOW CREATE TABLE, but not
+          for SHOW CREATE VIEW.
+        */
+        if (open_and_process_temporary_table_list(thd, all_tables))
+          goto error;
+
+        /*
           The fact that check_some_access() returned FALSE does not mean that
           access is granted. We need to check if first_table->grant.privilege
           contains any table-specific privilege.
@@ -3135,6 +3209,22 @@ end_with_restore_list:
     thd->mdl_context.release_transactional_locks();
     if (res)
       goto error;
+
+    /*
+      Here we have to pre-open temporary tables for LOCK TABLES.
+
+      CF_PREOPEN_TMP_TABLES is not set for this SQL statement simply
+      because LOCK TABLES calls close_thread_tables() firstly (it's called
+      from release_transactional_locks() above).
+
+      close_thread_tables() closes all open tables, so even if
+      CF_PREOPEN_TMP_TABLES was set and the tables would be pre-opened in a
+      usual way, they would be closed by close_thread_tables().
+    */
+
+    if (open_and_process_temporary_table_list(thd, all_tables))
+      goto error;
+
     if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
                            FALSE, UINT_MAX, FALSE))
       goto error;
@@ -3550,6 +3640,7 @@ end_with_restore_list:
     if (check_global_access(thd,RELOAD_ACL))
       goto error;
 
+
     if (first_table && lex->type & REFRESH_READ_LOCK)
     {
       /* Check table-level privileges. */
@@ -4537,6 +4628,9 @@ bool check_single_table_access(THD *thd,
                    0, no_errors))
     goto deny;
 
+  if (is_temporary_table(all_tables))
+    all_tables->table->grant= all_tables->grant;
+
   /* Show only 1 table for check_grant */
   if (!(all_tables->belong_to_view &&
         (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) &&
@@ -4958,10 +5052,10 @@ check_table_access(THD *thd, ulong requi
 
     DBUG_PRINT("info", ("derived: %d  view: %d", tables->derived != 0,
                         tables->view != 0));
-    if (tables->is_anonymous_derived_table() ||
-        (tables->table && tables->table->s &&
-         (int)tables->table->s->tmp_table))
+
+    if (tables->is_anonymous_derived_table())
       continue;
+
     thd->security_ctx= sctx;
 
     if (check_access(thd, want_access, tables->get_db_name(),
@@ -4969,6 +5063,9 @@ check_table_access(THD *thd, ulong requi
                      &tables->grant.m_internal,
                      0, no_errors))
       goto deny;
+
+    if (is_temporary_table(tables))
+      tables->table->grant= tables->grant;
   }
   thd->security_ctx= backup_ctx;
   return check_grant(thd,requirements,org_tables,

=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc	2010-11-26 15:20:05 +0000
+++ b/sql/sql_prepare.cc	2010-11-29 14:13:07 +0000
@@ -105,6 +105,7 @@ When one supplies long data for a placeh
 #include "sp_head.h"
 #include "sp.h"
 #include "sp_cache.h"
+#include "sql_handler.h"  // mysql_ha_rm_tables
 #include "probes_mysql.h"
 #ifdef EMBEDDED_LIBRARY
 /* include MYSQL_BIND headers */
@@ -1715,6 +1716,15 @@ static bool mysql_test_create_table(Prep
   TABLE_LIST *create_table= lex->query_tables;
   TABLE_LIST *tables= lex->create_last_non_select_table->next_global;
 
+  if (lex->create_info.merge_list.elements)
+  {
+    if (open_and_process_temporary_table_list(
+          thd, lex->create_info.merge_list.first))
+    {
+      DBUG_RETURN(TRUE);
+    }
+  }
+
   if (create_table_precheck(thd, tables, create_table))
     DBUG_RETURN(TRUE);
 
@@ -1778,6 +1788,12 @@ static bool mysql_test_create_view(Prepa
   if (create_view_precheck(thd, tables, view, lex->create_view_mode))
     goto err;
 
+  for (TABLE_LIST *tl= tables; tl; tl= tl->next_global)
+  {
+    if (is_temporary_table(tl))
+      tl->table->grant= tl->grant;
+  }
+
   if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL))
     goto err;
 
@@ -1964,6 +1980,20 @@ static bool check_prepared_statement(Pre
   if (tables)
     thd->warning_info->opt_clear_warning_info(thd->query_id);
 
+  if (sql_command_flags[sql_command] & CF_HA_CLOSE)
+    mysql_ha_rm_tables(thd, tables);
+
+  /*
+    Open temporary tables that are known now. Temporary tables added by
+    prelocking will be opened afterwards (after the switch below).
+  */
+
+  if (sql_command_flags[sql_command] & CF_PREOPEN_TMP_TABLES)
+  {
+    if (open_and_process_temporary_table_list(thd, tables))
+      goto error;
+  }
+
   switch (sql_command) {
   case SQLCOM_REPLACE:
   case SQLCOM_INSERT:
@@ -2094,6 +2124,88 @@ static bool check_prepared_statement(Pre
     }
     break;
   }
+
+  /*
+    Open temporary tables added by prelocking.
+
+    Here we need to open temporary tables added by prelocking and ensure
+    that HA_ATTACH_CHILDREN is called for them.
+
+    This is needed to handle properly the case as follows:
+      - create temporary tables 'tmp1' and 'tmp2;
+      - create a merge table 'mrg1' from 'tmp1' and 'tmp2';
+      - create a stored routine (let's say a stored function),
+        which is using 'mrg1';
+      - prepare a statement that deals with the stored routine
+        (e.g. SELECT <stored function>).
+
+    So, the case might be like this:
+      CREATE TEMPORARY TABLE tmp1(a INT);
+      CREATE TEMPORARY TABLE tmp2(a INT);
+      CREATE TABLE mrg1(a INT) ENGINE=MERGE UNION=(tmp1, tmp2);
+      CREATE FUNCTION f1() RETURNS INT RETURN (SELECT COUNT(*) FROM mrg1);
+      PREPARE stmt1 FROM 'SELECT f1()';
+
+    What's going on is this:
+      - 'tables' are empty for the statement being prepared;
+      - thus, before the "big switch" no temporary table is opened;
+      - after 'f1' is opened, 'mrg1' is added to the prelocking list;
+      - after 'mrg1' is opened, 'tmp1' and 'tmp2' are added to 'tables';
+      - so, here we have not-opened temporary tables 'tmp1' and 'tmp2'
+        in the 'tables' list.
+
+    The thing is that HA_ATTACH_CHILDREN must be called for all merge
+    children during the prepare phase. This is critical because merge
+    children remembers "TABLE_SHARE ref type" and "TABLE_SHARE def version"
+    in the HA_ATTACH_CHILDREN operation.
+
+    If HA_ATTACH_CHILDREN is not called, these attributes are not set.
+    Then, during the first EXECUTE, those attributes need to be updated.
+    That would cause statement re-preparing (because changing those
+    attributes during EXECUTE is caught by THD::m_reprepare_observer).
+    The problem is that since those attributes are not set in merge children,
+    another round of PREPARE will not help. The attributes will be
+    remembered, but as soon as close_thread_tables() is called at the end
+    of PREPARE, this information will be lost again.
+  */
+
+  if (sql_command_flags[sql_command] & CF_PREOPEN_TMP_TABLES)
+  {
+    List<TABLE_LIST> new_tl_arr;
+
+    for (TABLE_LIST *tl= lex->query_tables; tl; tl= tl->next_global)
+    {
+      if (tl->table)
+        continue;
+
+      if (open_and_process_temporary_table(thd, tl))
+        goto error;
+
+      new_tl_arr.push_back(tl);
+    }
+
+    /*
+      Ensure that HA_ATTACH_CHILDREN has been called for newly added (by
+      prelocking) merge tables.
+    */
+    {
+      List_iterator_fast<TABLE_LIST> new_tl_arr_it(new_tl_arr);
+      TABLE_LIST *tl;
+      while ((tl= new_tl_arr_it++))
+      {
+        if (!tl->table ||
+            tl->table->file->ht->db_type != DB_TYPE_MRG_MYISAM)
+          continue;
+
+        /* MERGE tables need to access parent and child TABLE_LISTs. */
+        DBUG_ASSERT(tl->table->pos_in_table_list == tl);
+
+        if (tl->table->file->extra(HA_EXTRA_ATTACH_CHILDREN))
+          goto error;
+      }
+    }
+  }
+
   if (res == 0)
     DBUG_RETURN(stmt->is_sql_prepare() ?
                 FALSE : (send_prep_stmt(stmt, 0) || thd->protocol->flush()));

=== modified file 'sql/sql_rename.cc'
--- a/sql/sql_rename.cc	2010-11-18 16:34:56 +0000
+++ b/sql/sql_rename.cc	2010-11-29 14:13:07 +0000
@@ -139,8 +139,7 @@ bool mysql_rename_tables(THD *thd, TABLE
     }
   }
 
-  if (lock_table_names(thd, table_list, 0, thd->variables.lock_wait_timeout,
-                       MYSQL_OPEN_SKIP_TEMPORARY))
+  if (lock_table_names(thd, table_list, 0, thd->variables.lock_wait_timeout, 0))
     goto err;
 
   for (ren_table= table_list; ren_table; ren_table= ren_table->next_local)

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2010-11-29 11:28:55 +0000
+++ b/sql/sql_show.cc	2010-11-29 14:13:07 +0000
@@ -3019,11 +3019,18 @@ fill_schema_show_cols_or_idxs(THD *thd, 
     SQLCOM_SHOW_FIELDS is used because it satisfies 'only_view_structure()' 
   */
   lex->sql_command= SQLCOM_SHOW_FIELDS;
-  res= open_normal_and_derived_tables(thd, show_table_list,
-                                      (MYSQL_OPEN_IGNORE_FLUSH |
-                                       MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
-                                       (can_deadlock ?
-                                        MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)));
+
+  res= open_and_process_temporary_table_list(thd, show_table_list);
+
+  if (!res)
+  {
+    res= open_normal_and_derived_tables(thd, show_table_list,
+                                        (MYSQL_OPEN_IGNORE_FLUSH |
+                                         MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
+                                         (can_deadlock ?
+                                          MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)));
+  }
+
   lex->sql_command= save_sql_command;
 
   DEBUG_SYNC(thd, "after_open_table_ignore_flush");
@@ -3628,6 +3635,8 @@ int get_all_tables(THD *thd, TABLE_LIST 
             show_table_list->i_s_requested_object=
               schema_table->i_s_requested_object;
             DEBUG_SYNC(thd, "before_open_in_get_all_tables");
+            if (open_and_process_temporary_table_list(thd, show_table_list))
+              goto err;
             res= open_normal_and_derived_tables(thd, show_table_list,
                    (MYSQL_OPEN_IGNORE_FLUSH |
                     MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |

=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc	2010-11-29 11:28:55 +0000
+++ b/sql/sql_table.cc	2010-11-29 14:13:07 +0000
@@ -2048,8 +2048,8 @@ bool mysql_rm_table(THD *thd,TABLE_LIST 
   {
     if (!thd->locked_tables_mode)
     {
-      if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout,
-                           MYSQL_OPEN_SKIP_TEMPORARY))
+      if (lock_table_names(thd, tables, NULL,
+                           thd->variables.lock_wait_timeout, 0))
         DBUG_RETURN(true);
       for (table= tables; table; table= table->next_local)
         tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name,
@@ -5935,8 +5935,6 @@ bool mysql_alter_table(THD *thd,char *ne
   build_table_filename(reg_path, sizeof(reg_path) - 1, db, table_name, reg_ext, 0);
   build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0);
 
-  mysql_ha_rm_tables(thd, table_list);
-
   /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */
   if (alter_info->tablespace_op != NO_TABLESPACE_OP)
     /* Conditionally writes to binlog. */
@@ -6553,14 +6551,12 @@ bool mysql_alter_table(THD *thd,char *ne
   {
     if (table->s->tmp_table)
     {
-      Open_table_context ot_ctx(thd, (MYSQL_OPEN_IGNORE_FLUSH |
-                                      MYSQL_LOCK_IGNORE_TIMEOUT));
       TABLE_LIST tbl;
       bzero((void*) &tbl, sizeof(tbl));
       tbl.db= new_db;
       tbl.table_name= tbl.alias= tmp_name;
       /* Table is in thd->temporary_tables */
-      (void) open_table(thd, &tbl, thd->mem_root, &ot_ctx);
+      (void) open_temporary_table(thd, &tbl);
       new_table= tbl.table;
     }
     else
@@ -7274,13 +7270,6 @@ bool mysql_recreate_table(THD *thd, TABL
 
   DBUG_ENTER("mysql_recreate_table");
   DBUG_ASSERT(!table_list->next_global);
-  /*
-    table_list->table has been closed and freed. Do not reference
-    uninitialized data. open_tables() could fail.
-  */
-  table_list->table= NULL;
-  /* Same applies to MDL ticket. */
-  table_list->mdl_request.ticket= NULL;
   /* Set lock type which is appropriate for ALTER TABLE. */
   table_list->lock_type= TL_READ_NO_INSERT;
   /* Same applies to MDL request. */
@@ -7318,13 +7307,16 @@ bool mysql_checksum_table(THD *thd, TABL
   /* Open one table after the other to keep lock time as short as possible. */
   for (table= tables; table; table= table->next_local)
   {
+    TABLE *t= table->table;
     char table_name[NAME_LEN*2+2];
-    TABLE *t;
 
     strxmov(table_name, table->db ,".", table->table_name, NullS);
 
-    t= table->table= open_n_lock_single_table(thd, table, TL_READ, 0);
-    thd->clear_error();			// these errors shouldn't get client
+    if (!table->table)
+    {
+      t= table->table= open_n_lock_single_table(thd, table, TL_READ, 0);
+      thd->clear_error();			// these errors shouldn't get client
+    }
 
     protocol->prepare_for_resend();
     protocol->store(table_name, system_charset_info);
@@ -7422,11 +7414,9 @@ bool mysql_checksum_table(THD *thd, TABL
       if (! thd->in_sub_stmt)
         trans_rollback_stmt(thd);
       close_thread_tables(thd);
-      /*
-        Don't release metadata locks, this will be done at
-        statement end.
-      */
-      table->table=0;				// For query cache
+
+      if (open_and_process_temporary_table_list(thd, tables))
+        goto err;
     }
     if (protocol->write())
       goto err;

=== modified file 'sql/sql_truncate.cc'
--- a/sql/sql_truncate.cc	2010-10-21 11:34:17 +0000
+++ b/sql/sql_truncate.cc	2010-11-29 14:13:07 +0000
@@ -20,7 +20,7 @@
 #include "sql_table.h"   // write_bin_log
 #include "sql_handler.h" // mysql_ha_rm_tables
 #include "datadict.h"    // dd_recreate_table()
-#include "lock.h"        // MYSQL_OPEN_TEMPORARY_ONLY
+#include "lock.h"        // MYSQL_OPEN_* flags
 #include "sql_acl.h"     // DROP_ACL
 #include "sql_parse.h"   // check_one_table_access()
 #include "sql_truncate.h"
@@ -194,9 +194,7 @@ int Sql_cmd_truncate_table::handler_trun
   */
 
   /* If it is a temporary table, no need to take locks. */
-  if (is_tmp_table)
-    flags= MYSQL_OPEN_TEMPORARY_ONLY;
-  else
+  if (!is_tmp_table)
   {
     /* We don't need to load triggers. */
     DBUG_ASSERT(table_ref->trg_event_map == 0);
@@ -211,7 +209,7 @@ int Sql_cmd_truncate_table::handler_trun
       the MDL lock taken above and otherwise there is no way to
       wait for FLUSH TABLES in deadlock-free fashion.
     */
-    flags= MYSQL_OPEN_IGNORE_FLUSH | MYSQL_OPEN_SKIP_TEMPORARY;
+    flags= MYSQL_OPEN_IGNORE_FLUSH;
     /*
       Even though we have an MDL lock on the table here, we don't
       pass MYSQL_OPEN_HAS_MDL_LOCK to open_and_lock_tables
@@ -340,8 +338,7 @@ bool Sql_cmd_truncate_table::lock_table(
     /* Acquire an exclusive lock. */
     DBUG_ASSERT(table_ref->next_global == NULL);
     if (lock_table_names(thd, table_ref, NULL,
-                         thd->variables.lock_wait_timeout,
-                         MYSQL_OPEN_SKIP_TEMPORARY))
+                         thd->variables.lock_wait_timeout, 0))
       DBUG_RETURN(TRUE);
 
     if (dd_check_storage_engine_flag(thd, table_ref->db, table_ref->table_name,
@@ -393,26 +390,27 @@ bool Sql_cmd_truncate_table::lock_table(
 bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
 {
   int error;
-  TABLE *table;
   bool binlog_stmt;
   DBUG_ENTER("Sql_cmd_truncate_table::truncate_table");
 
+  DBUG_ASSERT((!table_ref->table) ||
+              (table_ref->table && table_ref->table->s));
+
   /* Initialize, or reinitialize in case of reexecution (SP). */
   m_ticket_downgrade= NULL;
 
-  /* Remove table from the HANDLER's hash. */
-  mysql_ha_rm_tables(thd, table_ref);
-
   /* If it is a temporary table, no need to take locks. */
-  if ((table= find_temporary_table(thd, table_ref)))
+  if (is_temporary_table(table_ref))
   {
+    TABLE *tmp_table= table_ref->table;
+
     /* In RBR, the statement is not binlogged if the table is temporary. */
     binlog_stmt= !thd->is_current_stmt_binlog_format_row();
 
     /* Note that a temporary table cannot be partitioned. */
-    if (ha_check_storage_engine_flag(table->s->db_type(), HTON_CAN_RECREATE))
+    if (ha_check_storage_engine_flag(tmp_table->s->db_type(), HTON_CAN_RECREATE))
     {
-      if ((error= recreate_temporary_table(thd, table)))
+      if ((error= recreate_temporary_table(thd, tmp_table)))
         binlog_stmt= FALSE; /* No need to binlog failed truncate-by-recreate. */
 
       DBUG_ASSERT(! thd->transaction.stmt.modified_non_trans_table);

=== modified file 'sql/sql_view.cc'
--- a/sql/sql_view.cc	2010-11-18 16:34:56 +0000
+++ b/sql/sql_view.cc	2010-11-29 14:13:07 +0000
@@ -441,6 +441,12 @@ bool mysql_create_view(THD *thd, TABLE_L
     goto err;
   }
 
+  if (open_and_process_temporary_table_list(thd, lex->query_tables))
+  {
+    res= TRUE;
+    goto err;
+  }
+
   view= lex->unlink_first_table(&link_to_local);
 
   if (mode == VIEW_ALTER && fill_defined_view_parts(thd, view))
@@ -1639,8 +1645,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIS
     DBUG_RETURN(TRUE);
   }
 
-  if (lock_table_names(thd, views, 0, thd->variables.lock_wait_timeout,
-                       MYSQL_OPEN_SKIP_TEMPORARY))
+  if (lock_table_names(thd, views, 0, thd->variables.lock_wait_timeout, 0))
     DBUG_RETURN(TRUE);
 
   for (view= views; view; view= view->next_local)


Attachment: [text/bzr-bundle] bzr/alexander.nozdrin@oracle.com-20101129141307-r3tx8bmhtx7pdcn2.bundle
Thread
bzr commit into mysql-trunk-bugfixing branch (alexander.nozdrin:3391)Bug#27480Alexander Nozdrin29 Nov