List:Commits« Previous MessageNext Message »
From:Davi Arnaut Date:October 16 2007 11:15pm
Subject:bk commit into 5.0 tree (davi:1.2541) BUG#30882
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of davi. When davi does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-10-16 20:15:27-03:00, davi@stripped +4 -0
  
  Bug#30882 Dropping a temporary table inside a stored function may cause a server crash
  
  If a stored function that contains a drop temporary table statement
  is invoked by a create temporary table of the same name may cause
  a server crash. The problem is that when dropping a table no check
  is done to ensure that table is not being used by some outer query
  (or outer statement), potentially leaving the outer query with a
  reference to a stale (freed) table.
  
  The solution is when dropping a temporary table, always check if
  the table is being used by some outer statement as a temporary
  table can be dropped inside stored procedures.

  mysql-test/r/sp.result@stripped, 2007-10-16 20:15:25-03:00, davi@stripped +8 -0
    Add test case result for Bug#30882

  mysql-test/t/sp.test@stripped, 2007-10-16 20:15:25-03:00, davi@stripped +15 -0
    Add test case for Bug#30882

  sql/sql_base.cc@stripped, 2007-10-16 20:15:25-03:00, davi@stripped +24 -0
    When entering in prelocked mode, mark as free for reuse all temp
    tables which are not in use by the main statement.

  sql/sql_table.cc@stripped, 2007-10-16 20:15:25-03:00, davi@stripped +57 -5
    Introduce the drop_temporary_table function which is almost a
    exact copy of close_temporary_table(), but perform checks to
    verify if the table is not being used.

diff -Nrup a/mysql-test/r/sp.result b/mysql-test/r/sp.result
--- a/mysql-test/r/sp.result	2007-10-07 19:05:51 -03:00
+++ b/mysql-test/r/sp.result	2007-10-16 20:15:25 -03:00
@@ -6565,4 +6565,12 @@ f1()
 DROP TABLE t1;
 DROP FUNCTION f1;
 
+create function f1() returns int
+begin
+drop temporary table t1;
+return 1;
+end|
+create temporary table t1 as select f1();
+ERROR HY000: Can't reopen table: 't1'
+drop function f1;
 End of 5.0 tests
diff -Nrup a/mysql-test/t/sp.test b/mysql-test/t/sp.test
--- a/mysql-test/t/sp.test	2007-10-07 19:05:51 -03:00
+++ b/mysql-test/t/sp.test	2007-10-16 20:15:25 -03:00
@@ -7677,4 +7677,19 @@ DROP FUNCTION f1;
 
 ###########################################################################
 
+#
+# Bug#30882 Dropping a temporary table inside a stored function may cause a server crash
+#
+
+delimiter |;
+create function f1() returns int
+begin
+ drop temporary table t1;
+ return 1;
+end|
+delimiter ;|
+--error ER_CANT_REOPEN_TABLE
+create temporary table t1 as select f1();
+drop function f1;
+
 --echo End of 5.0 tests
diff -Nrup a/sql/sql_base.cc b/sql/sql_base.cc
--- a/sql/sql_base.cc	2007-10-09 12:02:57 -03:00
+++ b/sql/sql_base.cc	2007-10-16 20:15:25 -03:00
@@ -454,6 +454,20 @@ static void mark_used_tables_as_free_for
       table->query_id= 0;
 }
 
+/**
+  Mark all tables in the list which are not used by current statement
+  as free for reuse.
+
+  @param thd thread context
+  @param table head of the list of tables
+*/
+
+static void mark_not_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
+{
+  for (; table ; table= table->next)
+    if (table->query_id != thd->query_id)
+      table->query_id= 0;
+}
 
 /*
   Close all tables used by the current substatement, or all tables
@@ -3247,6 +3261,11 @@ int lock_tables(THD *thd, TABLE_LIST *ta
         and was marked as occupied during open_tables() as free for reuse.
       */
       mark_real_tables_as_free_for_reuse(first_not_own);
+      /*
+        Mark temporary tables which are not used by this statement as free
+        for reuse.
+      */
+      mark_not_used_tables_as_free_for_reuse(thd, thd->temporary_tables);
       DBUG_PRINT("info",("prelocked_mode= PRELOCKED"));
       thd->prelocked_mode= PRELOCKED;
     }
@@ -3271,6 +3290,11 @@ int lock_tables(THD *thd, TABLE_LIST *ta
     if (thd->lex->requires_prelocking())
     {
       mark_real_tables_as_free_for_reuse(first_not_own);
+      /*
+        Mark temporary tables which are not used by this statement as free
+        for reuse.
+      */
+      mark_not_used_tables_as_free_for_reuse(thd, thd->temporary_tables);
       DBUG_PRINT("info", ("thd->prelocked_mode= PRELOCKED_UNDER_LOCK_TABLES"));
       thd->prelocked_mode= PRELOCKED_UNDER_LOCK_TABLES;
     }
diff -Nrup a/sql/sql_table.cc b/sql/sql_table.cc
--- a/sql/sql_table.cc	2007-08-05 00:53:13 -03:00
+++ b/sql/sql_table.cc	2007-10-16 20:15:25 -03:00
@@ -72,6 +72,48 @@ uint build_table_path(char *buff, size_t
 }
 
 
+/**
+  Drop a user temporary table.
+
+  @param thd Thread context
+  @param db Database name
+  @param table_name Table name
+
+  @retval  0  the table was found and dropped successfully.
+  @retval  1  the table was not found in the list of temporary tables
+              of this thread
+  @retval -1  the table is in use by a outer query
+*/
+
+static int drop_temporary_table(THD *thd, const char *db, const char *table_name)
+{
+  TABLE *table,**prev;
+  DBUG_ENTER("drop_temporary_table");
+
+  if (!(prev= find_temporary_table(thd, db, table_name)))
+    DBUG_RETURN(1);
+  table= *prev;
+
+  if (table->query_id == thd->query_id ||
+      table->query_id == thd->warn_id ||
+      thd->prelocked_mode && table->query_id)
+  {
+    my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias);
+    DBUG_RETURN(-1);
+  }
+
+  *prev= table->next;
+  /*
+    If LOCK TABLES list is not empty and contains this table,
+    unlock the table and remove the table from this list.
+  */
+  mysql_lock_remove(thd, thd->locked_tables, table, FALSE);
+  close_temporary(table, 1);
+  if (thd->slave_thread)
+    --slave_open_temp_tables;
+  DBUG_RETURN(0);
+}
+
 
 /*
  delete (drop) tables.
@@ -240,13 +282,23 @@ int mysql_rm_table_part2(THD *thd, TABLE
     db_type table_type= DB_TYPE_UNKNOWN;
 
     mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, TRUE);
-    if (!close_temporary_table(thd, db, table->table_name))
-    {
-      tmp_table_deleted=1;
-      continue;					// removed temporary table
+
+    error= drop_temporary_table(thd, db, table->table_name);
+
+    switch (error) {
+    case  0:
+      // removed temporary table
+      tmp_table_deleted= 1;
+      continue;
+    case -1:
+      // table already in use
+      error= 1;
+      goto err_with_placeholders;
+    default:
+      // temporary table not found
+      error= 0;
     }
 
-    error=0;
     if (!drop_temporary)
     {
       abort_locked_tables(thd, db, table->table_name);
Thread
bk commit into 5.0 tree (davi:1.2541) BUG#30882Davi Arnaut17 Oct