List:Commits« Previous MessageNext Message »
From:Dmitry Shulga Date:May 31 2011 1:43pm
Subject:bzr commit into mysql-5.1 branch (Dmitry.Shulga:3634) Bug#47870 Bug#11756013
View as plain text  
#At file:///Users/shulga/projects/mysql/mysql-5.1-bug11756013/ based on revid:davi.arnaut@stripped

 3634 Dmitry Shulga	2011-05-31
      Fixed Bug#11756013 (formerly known as bug#47870): BOGUS "THE TABLE MYSQL.PROC IS MISSING,..."
      
      There is a race condition during concurrent processing of CALL statement to some procedure
      specified by full qualified name SCHEMA_NAME.PROC_NAME and processing of DROP SCHEMA_NAME.
      
      The problem was that there is a window for race condition when one server thread try to load
      a stored procedure/function being executed and other thread try to drop schema that is part of 
      fully qualified name of procedure/function being executed.
      
      This condition race window exists in implementation of function mysql_change_db() called
      by db_load_routine() during loading of stored procedure/function to cache.
      Function mysql_change_db() calls to check_db_dir_existence() that might failed
      because specified database was dropped during concurrent execution of DROP SCHEMA statememt.
      db_load_routine() calls mysql_change_db() with flag 'force_switch' set to true value so when 
      referenced db is not found then my_error() is not called and function mysql_change_db() returns ok.
      This shadows information about schema opening error in db_load_routine(). Then db_load_routine()
      makes attempt to parse stored procedure/function that is failed. This makes to return error to
      sp_cache_routines_and_add_tables_aux() but since during error generation a call to my_error wasn't
      made and hence THD::main_da wasn't set we set the generic "mysql.proc table corrupt" error when running
      sp_cache_routines_and_add_tables_aux().
      
      The solution is to install error hadler inside db_load_routine() before call to mysql_opt_change_db()
      and deinstall after this functuon executed. If error handler called as a result of db check failure
      it remembered this event and later inside db_load_routine() we can check value of this event.
      If this event is signaled about droped schema then we call to my_error to set error in diagnostic area to
      error ER_BAD_DB_ERROR.
     @ sql/sql_db.cc
        Added synchronization point "before_db_dir_check" to emulate a race condition during
        processing of CALL/DROP SCHEMA.

    modified:
      mysql-test/t/sp_sync.test
      sql/sp.cc
      sql/sql_db.cc
=== modified file 'mysql-test/t/sp_sync.test'
--- a/mysql-test/t/sp_sync.test	2010-01-15 08:51:39 +0000
+++ b/mysql-test/t/sp_sync.test	2011-05-31 13:43:02 +0000
@@ -54,5 +54,25 @@ connection default;
 DROP TABLE t1, t2;
 DROP PROCEDURE p1;
 
+--echo #
+--echo # test for bug#
+--echo #
+
+CREATE SCHEMA s1; 
+CREATE PROCEDURE s1.p1() BEGIN END; 
+
+connect (con2, localhost, root); 
+SET DEBUG_SYNC='before_db_dir_check SIGNAL check_db WAIT_FOR dropped_schema'; 
+--send CALL s1.p1 
+
+connection default; 
+SET DEBUG_SYNC='now WAIT_FOR check_db'; 
+DROP SCHEMA s1; 
+SET DEBUG_SYNC='now SIGNAL dropped_schema'; 
+
+connection con2; 
+--error ER_BAD_DB_ERROR 
+--reap
+
 SET DEBUG_SYNC = 'RESET';
 

=== modified file 'sql/sp.cc'
--- a/sql/sp.cc	2010-06-11 12:52:06 +0000
+++ b/sql/sp.cc	2011-05-31 13:43:02 +0000
@@ -707,6 +707,35 @@ Silence_deprecated_warning::handle_error
   return FALSE;
 }
 
+struct Catch_db_not_exists_error : public Internal_error_handler
+{
+public:
+  Catch_db_not_exists_error(bool* db_not_exist)
+  : schema_not_exist(db_not_exist)
+  {}
+  virtual bool handle_error(uint sql_errno, const char *message,
+                            MYSQL_ERROR::enum_warning_level level,
+                            THD *thd);
+private:
+  bool* schema_not_exist;
+};
+
+bool
+Catch_db_not_exists_error::handle_error(uint sql_errno, const char *message,
+                                        MYSQL_ERROR::enum_warning_level level,
+                                        THD *thd)
+{
+  *schema_not_exist= false;
+
+  if (sql_errno == ER_BAD_DB_ERROR &&
+      level == MYSQL_ERROR::WARN_LEVEL_NOTE)
+  {
+    *schema_not_exist= true;
+    return true;
+  }
+  return false;
+}
+
 
 static int
 db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
@@ -720,12 +749,12 @@ db_load_routine(THD *thd, int type, sp_n
   char saved_cur_db_name_buf[NAME_LEN+1];
   LEX_STRING saved_cur_db_name=
     { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
-  bool cur_db_changed;
+  bool cur_db_changed, db_not_exists;
   ulong old_sql_mode= thd->variables.sql_mode;
   ha_rows old_select_limit= thd->variables.select_limit;
   sp_rcontext *old_spcont= thd->spcont;
   Silence_deprecated_warning warning_handler;
-
+  Catch_db_not_exists_error db_not_exists_handler(&db_not_exists);
   char definer_user_name_holder[USERNAME_LENGTH + 1];
   LEX_STRING definer_user_name= { definer_user_name_holder,
                                   USERNAME_LENGTH };
@@ -766,6 +795,7 @@ db_load_routine(THD *thd, int type, sp_n
     goto end;
   }
 
+  thd->push_internal_handler(&db_not_exists_handler);
   /*
     Change the current database (if needed).
 
@@ -776,9 +806,17 @@ db_load_routine(THD *thd, int type, sp_n
                           &cur_db_changed))
   {
     ret= SP_INTERNAL_ERROR;
+    thd->pop_internal_handler();
     goto end;
   }
+  thd->pop_internal_handler();
+  if (db_not_exists)
+  {
+    ret= SP_INTERNAL_ERROR;
+    my_error(ER_BAD_DB_ERROR, MYF(0), name->m_db.str);
 
+    goto end;
+  }
   thd->spcont= NULL;
 
   {

=== modified file 'sql/sql_db.cc'
--- a/sql/sql_db.cc	2010-12-10 07:48:50 +0000
+++ b/sql/sql_db.cc	2011-05-31 13:43:02 +0000
@@ -26,6 +26,7 @@
 #ifdef __WIN__
 #include <direct.h>
 #endif
+#include "debug_sync.h"
 
 #define MAX_DROP_TABLE_Q_LEN      1024
 
@@ -1702,6 +1703,8 @@ bool mysql_change_db(THD *thd, const LEX
   }
 #endif
 
+  DEBUG_SYNC(thd, "before_db_dir_check");
+
   if (check_db_dir_existence(new_db_file_name.str))
   {
     if (force_switch)


Attachment: [text/bzr-bundle] bzr/dmitry.shulga@oracle.com-20110531134302-ct94ykjvm02d6m4p.bundle
Thread
bzr commit into mysql-5.1 branch (Dmitry.Shulga:3634) Bug#47870 Bug#11756013Dmitry Shulga31 May