summaryrefslogtreecommitdiff
path: root/sql/sp.cc
diff options
context:
space:
mode:
authorDmitry Shulga <Dmitry.Shulga@oracle.com>2011-06-23 20:41:04 +0700
committerDmitry Shulga <Dmitry.Shulga@oracle.com>2011-06-23 20:41:04 +0700
commit07056eb984573b483576d45d7b8c259ad66dcd66 (patch)
treeade6ce3b5681ea2fa357915b4210ff12d7af9d9c /sql/sp.cc
parentb3696af2e66fb4d72815d2d7754740c2fd8043fd (diff)
downloadmariadb-git-07056eb984573b483576d45d7b8c259ad66dcd66.tar.gz
Fixed Bug#11756013 (formerly known as bug#47870):
BOGUS "THE TABLE MYSQL.PROC IS MISSING,..." There was a race condition between loading a stored routine (function/procedure/trigger) specified by fully qualified name SCHEMA_NAME.PROC_NAME and dropping the stored routine database. The problem was that there is a window for race condition when one server thread tries to load a stored routine being executed and the other thread tries to drop the stored routine schema. This condition race window exists in implementation of function mysql_change_db() called by db_load_routine() during loading of stored routine to cache. Function mysql_change_db() calls check_db_dir_existence() that might failed because specified database was dropped during concurrent execution of DROP SCHEMA statement. 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 routine 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 fix is to install an error handler inside db_load_routine() for the mysql_op_change_db() call, and check later if the ER_BAD_DB_ERROR was caught.
Diffstat (limited to 'sql/sp.cc')
-rw-r--r--sql/sp.cc42
1 files changed, 41 insertions, 1 deletions
diff --git a/sql/sp.cc b/sql/sp.cc
index ddddaee2e10..78d1dc7e22c 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -708,6 +708,37 @@ Silence_deprecated_warning::handle_error(uint sql_errno, const char *message,
}
+class Bad_db_error_handler : public Internal_error_handler
+{
+public:
+ Bad_db_error_handler()
+ :m_error_caught(false)
+ {}
+
+ virtual bool handle_error(uint sql_errno, const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd);
+
+ bool error_caught() const { return m_error_caught; }
+
+private:
+ bool m_error_caught;
+};
+
+bool
+Bad_db_error_handler::handle_error(uint sql_errno, const char *message,
+ MYSQL_ERROR::enum_warning_level level,
+ THD *thd)
+{
+ if (sql_errno == ER_BAD_DB_ERROR)
+ {
+ m_error_caught= true;
+ return true;
+ }
+ return false;
+}
+
+
static int
db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
ulong sql_mode, const char *params, const char *returns,
@@ -725,7 +756,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
ha_rows old_select_limit= thd->variables.select_limit;
sp_rcontext *old_spcont= thd->spcont;
Silence_deprecated_warning warning_handler;
-
+ Bad_db_error_handler db_not_exists_handler;
char definer_user_name_holder[USERNAME_LENGTH + 1];
LEX_STRING definer_user_name= { definer_user_name_holder,
USERNAME_LENGTH };
@@ -766,6 +797,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
goto end;
}
+ thd->push_internal_handler(&db_not_exists_handler);
/*
Change the current database (if needed).
@@ -776,9 +808,17 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
&cur_db_changed))
{
ret= SP_INTERNAL_ERROR;
+ thd->pop_internal_handler();
goto end;
}
+ thd->pop_internal_handler();
+ if (db_not_exists_handler.error_caught())
+ {
+ ret= SP_INTERNAL_ERROR;
+ my_error(ER_BAD_DB_ERROR, MYF(0), name->m_db.str);
+ goto end;
+ }
thd->spcont= NULL;
{