Cleanup-patch for BUG#25843: changing default database between

PREPARE and EXECUTE of statement breaks binlog.
parent 4c2a63ea
...@@ -574,7 +574,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ...@@ -574,7 +574,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp,
we'll update it later in switch_query_ctx(). we'll update it later in switch_query_ctx().
*/ */
if ((ret= sp_use_new_db(thd, name->m_db, &old_db, 1, &dbchanged))) if ((ret= sp_use_new_db(thd, name->m_db, &old_db, TRUE, &dbchanged)))
goto end; goto end;
thd->spcont= NULL; thd->spcont= NULL;
...@@ -2027,34 +2027,41 @@ create_string(THD *thd, String *buf, ...@@ -2027,34 +2027,41 @@ create_string(THD *thd, String *buf,
/* /**
Change the current database if needed. Change the current database if needed.
SYNOPSIS @param[in] thd thread handle
sp_use_new_db() @param[in] new_db new database name
thd thread handle @param[in, out] old_db IN: str points to a buffer where to store
new_db new database name (a string and its length) the old database, length contains the
old_db [IN] str points to a buffer where to store the old size of the buffer
database, length contains the size of the buffer OUT: if old db was not NULL, its name is
[OUT] if old db was not NULL, its name is copied copied to the buffer pointed at by str
to the buffer pointed at by str and length is updated and length is updated accordingly.
accordingly. Otherwise str[0] is set to '\0' and length Otherwise str[0] is set to '\0' and
is set to 0. The out parameter should be used only if length is set to 0. The out parameter
the database name has been changed (see dbchangedp). should be used only if the database name
dbchangedp [OUT] is set to TRUE if the current database is changed, has been changed (see dbchangedp).
FALSE otherwise. A database is not changed if the old @param[in] force_switch Flag to mysql_change_db(). For more information,
name is the same as the new one, both names are empty, see mysql_change_db() comment.
or an error has occurred. @param[out] dbchangedp is set to TRUE if the current database is
changed, FALSE otherwise. The current
RETURN VALUE database is not changed if the old name
0 success is equal to the new one, both names are
1 access denied or out of memory (the error message is empty, or an error has occurred.
set in THD)
@return Operation status.
@retval 0 on success
@retval 1 access denied or out of memory
(the error message is set in THD)
*/ */
int int
sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db, sp_use_new_db(THD *thd,
bool no_access_check, bool *dbchangedp) LEX_STRING new_db,
LEX_STRING *old_db,
bool force_switch,
bool *dbchangedp)
{ {
int ret; int ret;
DBUG_ENTER("sp_use_new_db"); DBUG_ENTER("sp_use_new_db");
...@@ -2085,7 +2092,7 @@ sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db, ...@@ -2085,7 +2092,7 @@ sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db,
DBUG_RETURN(0); DBUG_RETURN(0);
} }
ret= mysql_change_db(thd, &new_db, no_access_check); ret= mysql_change_db(thd, &new_db, force_switch);
*dbchangedp= ret == 0; *dbchangedp= ret == 0;
DBUG_RETURN(ret); DBUG_RETURN(ret);
......
...@@ -1802,11 +1802,27 @@ public: ...@@ -1802,11 +1802,27 @@ public:
} }
} }
/* /**
Initialize the current database from a NULL-terminated string with length Set the current database; use deep copy of C-string.
If we run out of memory, we free the current database and return TRUE.
This way the user will notice the error as there will be no current @param new_db a pointer to the new database name.
database selected (in addition to the error message set by malloc). @param new_db_len length of the new database name.
Initialize the current database from a NULL-terminated string with
length. If we run out of memory, we free the current database and
return TRUE. This way the user will notice the error as there will be
no current database selected (in addition to the error message set by
malloc).
@note This operation just sets {thd->db, thd->db_length}. Switching the
current database usually involves other actions, like switching other
database attributes including security context. In the future, this
operation will be made private and more convenient interface will be
provided.
@return Operation status
@retval FALSE Success
@retval TRUE Out-of-memory error
*/ */
bool set_db(const char *new_db, size_t new_db_len) bool set_db(const char *new_db, size_t new_db_len)
{ {
...@@ -1821,6 +1837,19 @@ public: ...@@ -1821,6 +1837,19 @@ public:
db_length= db ? new_db_len : 0; db_length= db ? new_db_len : 0;
return new_db && !db; return new_db && !db;
} }
/**
Set the current database; use shallow copy of C-string.
@param new_db a pointer to the new database name.
@param new_db_len length of the new database name.
@note This operation just sets {thd->db, thd->db_length}. Switching the
current database usually involves other actions, like switching other
database attributes including security context. In the future, this
operation will be made private and more convenient interface will be
provided.
*/
void reset_db(char *new_db, size_t new_db_len) void reset_db(char *new_db, size_t new_db_len)
{ {
db= new_db; db= new_db;
......
...@@ -39,6 +39,10 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, ...@@ -39,6 +39,10 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path); static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path);
static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error); static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error);
static void mysql_change_db_impl(THD *thd,
LEX_STRING *new_db_name,
ulong new_db_access,
CHARSET_INFO *new_db_charset);
/* Database lock hash */ /* Database lock hash */
...@@ -988,7 +992,7 @@ exit: ...@@ -988,7 +992,7 @@ exit:
it to 0. it to 0.
*/ */
if (thd->db && !strcmp(thd->db, db)) if (thd->db && !strcmp(thd->db, db))
thd->set_db(NULL, 0); mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
start_waiting_global_read_lock(thd); start_waiting_global_read_lock(thd);
exit2: exit2:
...@@ -1347,21 +1351,48 @@ static void mysql_change_db_impl(THD *thd, ...@@ -1347,21 +1351,48 @@ static void mysql_change_db_impl(THD *thd,
/** /**
@brief Change the current database. @brief Change the current database and its attributes.
@param thd thread handle @param thd thread handle
@param new_db_name database name @param new_db_name database name
@param force_switch if this flag is set (TRUE), mysql_change_db() will @param force_switch if force_switch is FALSE, then the operation will fail if
switch to NULL db if the specified database is not
available anymore. Corresponding warning will be - new_db_name is NULL or empty;
thrown in this case. This flag is used to change
database in stored-routine-execution code. - OR new database name is invalid
(check_db_name() failed);
@details Check that the database name corresponds to a valid and existent
database, check access rights (unless called with no_access_check), and - OR user has no privilege on the new database;
set the current database. This function is called to change the current
database upon user request (COM_CHANGE_DB command) or temporarily, to - OR new database does not exist;
execute a stored routine.
if force_switch is TRUE, then
- if new_db_name is NULL or empty, the current
database will be NULL, @@collation_database will
be set to @@collation_server, the operation will
succeed.
- if new database name is invalid
(check_db_name() failed), the current database
will be NULL, @@collation_database will be set to
@@collation_server, but the operation will fail;
- user privileges will not be checked
(THD::db_access however is updated);
TODO: is this really the intention?
(see sp-security.test).
- if new database does not exist,the current database
will be NULL, @@collation_database will be set to
@@collation_server, a warning will be thrown, the
operation will succeed.
@details The function checks that the database name corresponds to a
valid and existent database, checks access rights and changes the current
database with database attributes (@@collation_database session variable,
THD::db_access).
This function is not the only way to switch the database that is This function is not the only way to switch the database that is
currently employed. When the replication slave thread switches the currently employed. When the replication slave thread switches the
...@@ -1398,8 +1429,13 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) ...@@ -1398,8 +1429,13 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
if (force_switch) if (force_switch)
{ {
/* /*
This can only happen when we restore the old db in THD after This can happen only if we're switching the current database back
execution of a routine is complete. Change db to NULL. after loading stored program. The thing is that loading of stored
program can happen when there is no current database.
TODO: actually, new_db_name and new_db_name->str seem to be always
non-NULL. In case of stored program, new_db_name->str == "" and
new_db_name->length == 0.
*/ */
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
...@@ -1417,7 +1453,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) ...@@ -1417,7 +1453,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
if (my_strcasecmp(system_charset_info, new_db_name->str, if (my_strcasecmp(system_charset_info, new_db_name->str,
INFORMATION_SCHEMA_NAME.str) == 0) INFORMATION_SCHEMA_NAME.str) == 0)
{ {
/* Switch database to INFORMATION_SCHEMA. */ /* Switch the current database to INFORMATION_SCHEMA. */
mysql_change_db_impl(thd, &INFORMATION_SCHEMA_NAME, SELECT_ACL, mysql_change_db_impl(thd, &INFORMATION_SCHEMA_NAME, SELECT_ACL,
system_charset_info); system_charset_info);
...@@ -1444,8 +1480,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) ...@@ -1444,8 +1480,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
even if we are called from sp_head::execute(). even if we are called from sp_head::execute().
It's next to impossible however to get this error when we are called It's next to impossible however to get this error when we are called
from sp_head::execute(). But let's switch database to NULL in this case from sp_head::execute(). But let's switch the current database to NULL
to be sure. in this case to be sure.
*/ */
if (check_db_name(&new_db_file_name)) if (check_db_name(&new_db_file_name))
...@@ -1454,10 +1490,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) ...@@ -1454,10 +1490,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
my_free(new_db_file_name.str, MYF(0)); my_free(new_db_file_name.str, MYF(0));
if (force_switch) if (force_switch)
{
/* Change db to NULL. */
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
}
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
...@@ -1492,6 +1526,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) ...@@ -1492,6 +1526,8 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
{ {
if (force_switch) if (force_switch)
{ {
/* Throw a warning and free new_db_file_name. */
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR), ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR),
new_db_file_name.str); new_db_file_name.str);
...@@ -1502,12 +1538,19 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) ...@@ -1502,12 +1538,19 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server);
/* The operation succeed. */
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }
else else
{ {
/* Report an error and free new_db_file_name. */
my_error(ER_BAD_DB_ERROR, MYF(0), new_db_file_name.str); my_error(ER_BAD_DB_ERROR, MYF(0), new_db_file_name.str);
my_free(new_db_file_name.str, MYF(0)); my_free(new_db_file_name.str, MYF(0));
/* The operation failed. */
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
} }
...@@ -1820,7 +1863,7 @@ bool mysql_rename_db(THD *thd, LEX_STRING *old_db, LEX_STRING *new_db) ...@@ -1820,7 +1863,7 @@ bool mysql_rename_db(THD *thd, LEX_STRING *old_db, LEX_STRING *new_db)
/* Step9: Let's do "use newdb" if we renamed the current database */ /* Step9: Let's do "use newdb" if we renamed the current database */
if (change_to_newdb) if (change_to_newdb)
error|= mysql_change_db(thd, new_db, 0); error|= mysql_change_db(thd, new_db, FALSE);
exit: exit:
pthread_mutex_lock(&LOCK_lock_db); pthread_mutex_lock(&LOCK_lock_db);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment