diff --git a/mysql-test/r/reopen_temp_table.result b/mysql-test/r/reopen_temp_table.result index f98ab96747f..6d85703a999 100644 --- a/mysql-test/r/reopen_temp_table.result +++ b/mysql-test/r/reopen_temp_table.result @@ -19,6 +19,28 @@ DROP TABLE t1; # # CREATE & Stored routines # +CREATE FUNCTION f1() RETURNS INT +BEGIN +DROP TEMPORARY TABLE t1; +RETURN 1; +END| +CREATE TEMPORARY TABLE t1 AS SELECT f1(); +ERROR 42S02: Unknown table 'temp_db.t1' +DROP FUNCTION f1; +CREATE FUNCTION f2() RETURNS INT +BEGIN +CREATE TEMPORARY TABLE t2(i INT); +INSERT INTO t2 VALUES(1), (2); +RETURN 1; +END| +CREATE TEMPORARY TABLE t2 AS SELECT f2(); +ERROR 42S01: Table 't2' already exists +SELECT * FROM t2; +i +1 +2 +DROP TABLE t2; +DROP FUNCTION f2; CREATE TEMPORARY TABLE t3 AS SELECT 1 AS a; CREATE PROCEDURE p1() BEGIN diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 2a719eafc76..18484c98162 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -1524,14 +1524,14 @@ drop temporary table t1; return 1; end| create temporary table t1 as select f1(); -ERROR HY000: Can't reopen table: 't1' +ERROR 42S02: Unknown table 'test.t1' create function f2() returns int begin create temporary table t2 as select f1(); return 1; end| create temporary table t1 as select f2(); -ERROR HY000: Can't reopen table: 't1' +ERROR 42S02: Unknown table 'test.t1' drop function f1; drop function f2; create function f1() returns int @@ -1545,7 +1545,7 @@ create temporary table t2 as select f1(); return 1; end| create temporary table t1 as select f2(); -ERROR HY000: Can't reopen table: 't2' +ERROR 42S02: Unknown table 'test.t2,test.t1' drop function f1; drop function f2; create temporary table t2(a int); diff --git a/mysql-test/t/reopen_temp_table.test b/mysql-test/t/reopen_temp_table.test index 0cf3f774985..1daca0322f7 100644 --- a/mysql-test/t/reopen_temp_table.test +++ b/mysql-test/t/reopen_temp_table.test @@ -24,6 +24,31 @@ DROP TABLE t1; --echo # CREATE & Stored routines --echo # +DELIMITER |; +CREATE FUNCTION f1() RETURNS INT +BEGIN + DROP TEMPORARY TABLE t1; + RETURN 1; +END| +DELIMITER ;| +--error ER_BAD_TABLE_ERROR +CREATE TEMPORARY TABLE t1 AS SELECT f1(); +DROP FUNCTION f1; + +DELIMITER |; +CREATE FUNCTION f2() RETURNS INT +BEGIN + CREATE TEMPORARY TABLE t2(i INT); + INSERT INTO t2 VALUES(1), (2); + RETURN 1; +END| +DELIMITER ;| +--error ER_TABLE_EXISTS_ERROR +CREATE TEMPORARY TABLE t2 AS SELECT f2(); +SELECT * FROM t2; +DROP TABLE t2; +DROP FUNCTION f2; + CREATE TEMPORARY TABLE t3 AS SELECT 1 AS a; DELIMITER |; CREATE PROCEDURE p1() diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index 15cd0ec4e81..e30fc6e30d4 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -2231,7 +2231,7 @@ begin return 1; end| delimiter ;| ---error ER_CANT_REOPEN_TABLE +--error ER_BAD_TABLE_ERROR create temporary table t1 as select f1(); delimiter |; @@ -2241,7 +2241,7 @@ begin return 1; end| delimiter ;| ---error ER_CANT_REOPEN_TABLE +--error ER_BAD_TABLE_ERROR create temporary table t1 as select f2(); drop function f1; @@ -2259,7 +2259,7 @@ begin return 1; end| delimiter ;| ---error ER_CANT_REOPEN_TABLE +--error ER_BAD_TABLE_ERROR create temporary table t1 as select f2(); drop function f1; diff --git a/sql/sql_class.h b/sql/sql_class.h index 783f8b9df11..58d720f211e 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -4066,6 +4066,9 @@ public: void mark_tmp_tables_as_free_for_reuse(); void mark_tmp_table_as_free_for_reuse(TABLE *table); + TMP_TABLE_SHARE* save_tmp_table_share(TABLE *table); + void restore_tmp_table_share(TMP_TABLE_SHARE *share); + private: /* Whether a lock has been acquired? */ bool m_tmp_tables_locked; @@ -4617,6 +4620,7 @@ class select_create: public select_insert { /* m_lock or thd->extra_lock */ MYSQL_LOCK **m_plock; bool exit_done; + TMP_TABLE_SHARE *saved_tmp_table_share; public: select_create(THD *thd_arg, TABLE_LIST *table_arg, @@ -4629,7 +4633,8 @@ public: create_info(create_info_par), select_tables(select_tables_arg), alter_info(alter_info_arg), - m_plock(NULL), exit_done(0) + m_plock(NULL), exit_done(0), + saved_tmp_table_share(0) {} int prepare(List &list, SELECT_LEX_UNIT *u); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 13ff5a74b0b..e3f26ccf377 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -4226,6 +4226,18 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) /* abort() deletes table */ DBUG_RETURN(-1); + if (create_info->tmp_table()) + { + /* + When the temporary table was created & opened in create_table_impl(), + the table's TABLE_SHARE (and thus TABLE) object was also linked to THD + temporary tables lists. So, we must temporarily remove it from the + list to keep them inaccessible from inner statements. + e.g. CREATE TEMPORARY TABLE `t1` AS SELECT * FROM `t1`; + */ + saved_tmp_table_share= thd->save_tmp_table_share(create_table->table); + } + if (extra_lock) { DBUG_ASSERT(m_plock == NULL); @@ -4341,6 +4353,27 @@ bool select_create::send_eof() DBUG_RETURN(true); } + if (table->s->tmp_table) + { + /* + Now is good time to add the new table to THD temporary tables list. + But, before that we need to check if same table got created by the sub- + statement. + */ + if (thd->find_tmp_table_share(table->s->table_cache_key.str, + table->s->table_cache_key.length)) + { + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table->alias.c_ptr()); + abort_result_set(); + DBUG_RETURN(true); + } + else + { + DBUG_ASSERT(saved_tmp_table_share); + thd->restore_tmp_table_share(saved_tmp_table_share); + } + } + /* Do an implicit commit at end of statement for non-temporary tables. This can fail, but we should unlock the table @@ -4466,6 +4499,12 @@ void select_create::abort_result_set() { bool tmp_table= table->s->tmp_table; + if (tmp_table) + { + DBUG_ASSERT(saved_tmp_table_share); + thd->restore_tmp_table_share(saved_tmp_table_share); + } + table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE); table->auto_increment_field_not_null= FALSE; diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index c23bbd4ff19..c40ea511aac 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -801,6 +801,50 @@ void THD::mark_tmp_table_as_free_for_reuse(TABLE *table) } +/** + Remove and return the specified table's TABLE_SHARE from the temporary + tables list. + + @param table [IN] Table + + @return TMP_TABLE_SHARE of the specified table. +*/ +TMP_TABLE_SHARE *THD::save_tmp_table_share(TABLE *table) +{ + DBUG_ENTER("THD::save_tmp_table_share"); + + TMP_TABLE_SHARE *share; + + lock_temporary_tables(); + DBUG_ASSERT(temporary_tables); + share= tmp_table_share(table); + temporary_tables->remove(share); + unlock_temporary_tables(); + + DBUG_RETURN(share); +} + + +/** + Add the specified TMP_TABLE_SHARE to the temporary tables list. + + @param share [IN] Table share + + @return void +*/ +void THD::restore_tmp_table_share(TMP_TABLE_SHARE *share) +{ + DBUG_ENTER("THD::restore_tmp_table_share"); + + lock_temporary_tables(); + DBUG_ASSERT(temporary_tables); + temporary_tables->push_front(share); + unlock_temporary_tables(); + + DBUG_VOID_RETURN; +} + + /** If its a replication slave, report whether slave temporary tables exist (Relay_log_info::save_temporary_tables) or report about THD