[Commits] f6920c09: MDEV-5535: Cannot reopen temporary table

Nirbhay Choubey nirbhay at mariadb.com
Fri May 27 05:13:28 EEST 2016


revision-id: f6920c0963b6a1a82b4c758e55e4ebf73b38fc23 (mariadb-10.1.8-83-gf6920c09)
parent(s): 56a2835872c4ac7296ec0ae2ff618822855b0fc0
author: Nirbhay Choubey
committer: Nirbhay Choubey
timestamp: 2016-05-26 22:13:24 -0400
message:

MDEV-5535: Cannot reopen temporary table

Temporary table being created by outer statement
should not be visible to inner statement. And if
inner statement creates a table with same name.
The whole statement should fail with
ER_TABLE_EXISTS_ERROR.

Implemented by temporarily de-linking the TABLE_SHARE
being created by outer statement so that it remains
hidden to the inner statement.

---
 mysql-test/r/reopen_temp_table.result | 22 ++++++++++++++++++
 mysql-test/t/reopen_temp_table.test   | 25 +++++++++++++++++++++
 sql/sql_class.h                       |  7 +++++-
 sql/sql_insert.cc                     | 39 ++++++++++++++++++++++++++++++++
 sql/temporary_tables.cc               | 42 +++++++++++++++++++++++++++++++++++
 5 files changed, 134 insertions(+), 1 deletion(-)

diff --git a/mysql-test/r/reopen_temp_table.result b/mysql-test/r/reopen_temp_table.result
index f98ab96..6d85703 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/t/reopen_temp_table.test b/mysql-test/t/reopen_temp_table.test
index 0cf3f77..1daca03 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/sql/sql_class.h b/sql/sql_class.h
index a70c4a9..137f3d5 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -4027,6 +4027,9 @@ class THD :public Statement,
   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;
@@ -4538,6 +4541,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,
@@ -4550,7 +4554,8 @@ class select_create: public select_insert {
     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<Item> &list, SELECT_LEX_UNIT *u);
 
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index de45c10..00830e5 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -4199,6 +4199,18 @@ static TABLE *create_table_from_items(THD *thd,
     /* 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);
@@ -4312,6 +4324,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
@@ -4436,6 +4469,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 1514b72..b3603e8 100644
--- a/sql/temporary_tables.cc
+++ b/sql/temporary_tables.cc
@@ -771,6 +771,48 @@ 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();
+  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();
+  temporary_tables.push_front(share);
+  unlock_temporary_tables();
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
   Check whether slave temporary tables exist. The decision is made based on
   the existence of TMP_TABLE_SHAREs in Relay_log_info::save_temporary_tables
   list. Check THD::rgi_slave before calling this method.


More information about the commits mailing list