From: Date: August 22 2007 12:33pm Subject: bk commit into 5.1 tree (anozdrin:1.2569) BUG#25843 List-Archive: http://lists.mysql.com/commits/32868 X-Bug: 25843 Message-Id: <20070822103330.AE3F5C1234@ibm.opbmk> Below is the list of changes that have just been committed into a local 5.1 repository of alik. When alik 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-08-22 14:33:23+04:00, anozdrin@stripped +7 -0 Fix for BUG#25843: changing default database between PREPARE and EXECUTE of statement breaks binlog. There were two problems discovered by this bug: 1. Default (current) database is not fixed at the creation time. That leads to wrong output of DATABASE() function. 2. Database attributes (@@collation_database) are not fixed at the creation time. That leads to wrong resultset. Binlog breakage and Query Cache wrong output happened because of the first problem. The fix is to remember the current database at the PREPARE-time and set it each time at EXECUTE. mysql-test/r/ps-qc.result@stripped, 2007-08-22 14:33:13+04:00, anozdrin@stripped +174 -0 Result file. mysql-test/r/ps-qc.result@stripped, 2007-08-22 14:33:13+04:00, anozdrin@stripped +0 -0 mysql-test/t/ps-qc.opt@stripped, 2007-08-22 14:33:13+04:00, anozdrin@stripped +1 -0 ps-qc.test requires that the Query Cache is operational. mysql-test/t/ps-qc.opt@stripped, 2007-08-22 14:33:13+04:00, anozdrin@stripped +0 -0 mysql-test/t/ps-qc.test@stripped, 2007-08-22 14:33:13+04:00, anozdrin@stripped +242 -0 Test case for BUG#25843. It has to go into a new file, because the Query Cache is required. mysql-test/t/ps-qc.test@stripped, 2007-08-22 14:33:13+04:00, anozdrin@stripped +0 -0 sql/sp.cc@stripped, 2007-08-22 14:33:12+04:00, anozdrin@stripped +32 -25 - Polishing sp_use_new_db(): - renamed no_access_check to force_switch to be more adequate; - fixed comment; sql/sql_class.h@stripped, 2007-08-22 14:33:12+04:00, anozdrin@stripped +34 -5 Polishing: fixed comment. sql/sql_db.cc@stripped, 2007-08-22 14:33:12+04:00, anozdrin@stripped +65 -22 1. Use mysql_change_db_impl() to reset current database instead of THD::set_db() in mysql_rm_db(). THD::set_db() does not take care of THD::db_access and database attributes (@@collation_database). 2. Polishing: add, fix comments. sql/sql_prepare.cc@stripped, 2007-08-22 14:33:12+04:00, anozdrin@stripped +47 -1 1. Add db_name attribute to Prepared_statement class. 2. Remember the current database in Prepared_statement::prepare(). 3. Switch/restore the current database in Prepared_statement::execute(). diff -Nrup a/mysql-test/r/ps-qc.result b/mysql-test/r/ps-qc.result --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/mysql-test/r/ps-qc.result 2007-08-22 14:33:13 +04:00 @@ -0,0 +1,174 @@ +######################################################################## +# +# BUG#25843: Changing default database between PREPARE and EXECUTE of +# statement breaks binlog. +# +######################################################################## + +# +# Check that default database and its attributes are fixed at the +# creation time. +# + +DROP DATABASE IF EXISTS mysqltest1; +DROP DATABASE IF EXISTS mysqltest2; + +CREATE DATABASE mysqltest1 COLLATE utf8_unicode_ci; +CREATE DATABASE mysqltest2 COLLATE utf8_general_ci; + +CREATE TABLE mysqltest1.t1(msg VARCHAR(255)); +CREATE TABLE mysqltest2.t1(msg VARCHAR(255)); + +use mysqltest1; +PREPARE stmt_a_1 FROM 'INSERT INTO t1 VALUES(DATABASE())'; +PREPARE stmt_a_2 FROM 'INSERT INTO t1 VALUES(@@collation_database)'; + +EXECUTE stmt_a_1; +EXECUTE stmt_a_2; + +use mysqltest2; +EXECUTE stmt_a_1; +EXECUTE stmt_a_2; + +SELECT * FROM mysqltest1.t1; +msg +mysqltest1 +utf8_unicode_ci +mysqltest1 +utf8_unicode_ci + +SELECT * FROM mysqltest2.t1; +msg + +DROP PREPARE stmt_a_1; +DROP PREPARE stmt_a_2; + +# +# The Query Cache test case. +# + +DELETE FROM mysqltest1.t1; +DELETE FROM mysqltest2.t1; + +INSERT INTO mysqltest1.t1 VALUES('mysqltest1.t1'); +INSERT INTO mysqltest2.t1 VALUES('mysqltest2.t1'); + +use mysqltest1; +PREPARE stmt_b_1 FROM 'SELECT * FROM t1'; + +use mysqltest2; +PREPARE stmt_b_2 FROM 'SELECT * FROM t1'; + +EXECUTE stmt_b_1; +msg +mysqltest1.t1 + +EXECUTE stmt_b_2; +msg +mysqltest2.t1 + +use mysqltest1; + +EXECUTE stmt_b_1; +msg +mysqltest1.t1 + +EXECUTE stmt_b_2; +msg +mysqltest2.t1 + +DROP PREPARE stmt_b_1; +DROP PREPARE stmt_b_2; + +use test; + +DROP DATABASE mysqltest1; +DROP DATABASE mysqltest2; + +# +# Check that prepared statements work properly when there is no current +# database. +# + +CREATE DATABASE mysqltest1 COLLATE utf8_unicode_ci; +CREATE DATABASE mysqltest2 COLLATE utf8_general_ci; + +use mysqltest1; + +PREPARE stmt_c_1 FROM 'SELECT DATABASE(), @@collation_database'; + +use mysqltest2; + +PREPARE stmt_c_2 FROM 'SELECT DATABASE(), @@collation_database'; + +DROP DATABASE mysqltest2; + +SELECT DATABASE(), @@collation_database; +DATABASE() @@collation_database +NULL latin1_swedish_ci + +EXECUTE stmt_c_1; +DATABASE() @@collation_database +mysqltest1 utf8_unicode_ci + +SELECT DATABASE(), @@collation_database; +DATABASE() @@collation_database +NULL latin1_swedish_ci + +EXECUTE stmt_c_2; +DATABASE() @@collation_database +NULL latin1_swedish_ci +Warnings: +Note 1049 Unknown database 'mysqltest2' + +SELECT DATABASE(), @@collation_database; +DATABASE() @@collation_database +NULL latin1_swedish_ci + +use mysqltest1; + +EXECUTE stmt_c_2; +DATABASE() @@collation_database +NULL latin1_swedish_ci +Warnings: +Note 1049 Unknown database 'mysqltest2' + +SELECT DATABASE(), @@collation_database; +DATABASE() @@collation_database +mysqltest1 utf8_unicode_ci + +DROP DATABASE mysqltest1; + +use test; + +# +# Check that binlog is filled properly. +# + +CREATE DATABASE mysqltest1; +CREATE TABLE t1(c INT); + +RESET MASTER; +RESET SLAVE; + +PREPARE stmt_d_1 FROM 'INSERT INTO t1 VALUES(1)'; + +EXECUTE stmt_d_1; + +use mysqltest1; + +EXECUTE stmt_d_1; + +FLUSH LOGS; + +SHOW BINLOG EVENTS FROM 106; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 106 Query 1 193 use `test`; INSERT INTO t1 VALUES(1) +master-bin.000001 193 Query 1 280 use `test`; INSERT INTO t1 VALUES(1) +master-bin.000001 280 Rotate 1 324 master-bin.000002;pos=4 + +DROP DATABASE mysqltest1; + +use test; + +######################################################################## diff -Nrup a/mysql-test/t/ps-qc.opt b/mysql-test/t/ps-qc.opt --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/mysql-test/t/ps-qc.opt 2007-08-22 14:33:13 +04:00 @@ -0,0 +1 @@ +--query-cache-size=1000000 diff -Nrup a/mysql-test/t/ps-qc.test b/mysql-test/t/ps-qc.test --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/mysql-test/t/ps-qc.test 2007-08-22 14:33:13 +04:00 @@ -0,0 +1,242 @@ +-- source include/not_embedded.inc +-- source include/have_log_bin.inc +-- source include/have_query_cache.inc + +# +# Bug #25843 Changing default database between PREPARE and EXECUTE of statement +# breaks binlog. +# +# There were actually two problems discovered by this bug: +# +# 1. Default (current) database is not fixed at the creation time. +# That leads to wrong output of DATABASE() function. +# +# 2. Database attributes (@@collation_database) are not fixed at the creation +# time. That leads to wrong resultset. +# +# Binlog breakage and Query Cache wrong output happened because of the first +# problem. +# + +--echo ######################################################################## +--echo # +--echo # BUG#25843: Changing default database between PREPARE and EXECUTE of +--echo # statement breaks binlog. +--echo # +--echo ######################################################################## + +############################################################################### + +--echo +--echo # +--echo # Check that default database and its attributes are fixed at the +--echo # creation time. +--echo # + +# Prepare data structures. + +--echo +--disable_warnings +DROP DATABASE IF EXISTS mysqltest1; +DROP DATABASE IF EXISTS mysqltest2; +--enable_warnings + +--echo +CREATE DATABASE mysqltest1 COLLATE utf8_unicode_ci; +CREATE DATABASE mysqltest2 COLLATE utf8_general_ci; + +--echo +CREATE TABLE mysqltest1.t1(msg VARCHAR(255)); +CREATE TABLE mysqltest2.t1(msg VARCHAR(255)); + +# - Create a prepared statement with mysqltest1 as default database; + +--echo + +use mysqltest1; + +PREPARE stmt_a_1 FROM 'INSERT INTO t1 VALUES(DATABASE())'; +PREPARE stmt_a_2 FROM 'INSERT INTO t1 VALUES(@@collation_database)'; + +# - Execute on mysqltest1. + +--echo + +EXECUTE stmt_a_1; +EXECUTE stmt_a_2; + +# - Execute on mysqltest2. + +--echo + +use mysqltest2; + +EXECUTE stmt_a_1; +EXECUTE stmt_a_2; + +# - Check the results; + +--echo +SELECT * FROM mysqltest1.t1; + +--echo +SELECT * FROM mysqltest2.t1; + +# - Drop prepared statements. + +--echo +DROP PREPARE stmt_a_1; +DROP PREPARE stmt_a_2; + +############################################################################### + +--echo +--echo # +--echo # The Query Cache test case. +--echo # + +--echo +DELETE FROM mysqltest1.t1; +DELETE FROM mysqltest2.t1; + +--echo +INSERT INTO mysqltest1.t1 VALUES('mysqltest1.t1'); +INSERT INTO mysqltest2.t1 VALUES('mysqltest2.t1'); + +--echo +use mysqltest1; +PREPARE stmt_b_1 FROM 'SELECT * FROM t1'; + +--echo +use mysqltest2; +PREPARE stmt_b_2 FROM 'SELECT * FROM t1'; + +--echo +EXECUTE stmt_b_1; + +--echo +EXECUTE stmt_b_2; + +--echo +use mysqltest1; + +--echo +EXECUTE stmt_b_1; + +--echo +EXECUTE stmt_b_2; + +--echo +DROP PREPARE stmt_b_1; +DROP PREPARE stmt_b_2; + +# Cleanup. + +--echo +use test; + +--echo +DROP DATABASE mysqltest1; +DROP DATABASE mysqltest2; + +############################################################################### + +--echo +--echo # +--echo # Check that prepared statements work properly when there is no current +--echo # database. +--echo # + +--echo +CREATE DATABASE mysqltest1 COLLATE utf8_unicode_ci; +CREATE DATABASE mysqltest2 COLLATE utf8_general_ci; + +--echo +use mysqltest1; + +--echo +PREPARE stmt_c_1 FROM 'SELECT DATABASE(), @@collation_database'; + +--echo +use mysqltest2; + +--echo +PREPARE stmt_c_2 FROM 'SELECT DATABASE(), @@collation_database'; + +--echo +DROP DATABASE mysqltest2; + +--echo +SELECT DATABASE(), @@collation_database; + +# -- Here we have: current db: NULL; stmt db: mysqltest1; +--echo +EXECUTE stmt_c_1; + +--echo +SELECT DATABASE(), @@collation_database; + +# -- Here we have: current db: NULL; stmt db: mysqltest2 (non-existent); +--echo +EXECUTE stmt_c_2; + +--echo +SELECT DATABASE(), @@collation_database; + +--echo +use mysqltest1; + +# -- Here we have: current db: mysqltest1; stmt db: mysqltest2 (non-existent); +--echo +EXECUTE stmt_c_2; + +--echo +SELECT DATABASE(), @@collation_database; + +--echo +DROP DATABASE mysqltest1; + +--echo +use test; + +############################################################################### + +--echo +--echo # +--echo # Check that binlog is filled properly. +--echo # + +--echo +CREATE DATABASE mysqltest1; +CREATE TABLE t1(c INT); + +--echo +RESET MASTER; +RESET SLAVE; + +--echo +PREPARE stmt_d_1 FROM 'INSERT INTO t1 VALUES(1)'; + +--echo +EXECUTE stmt_d_1; + +--echo +use mysqltest1; + +--echo +EXECUTE stmt_d_1; + +--echo +FLUSH LOGS; + +--echo +SHOW BINLOG EVENTS FROM 106; + +--echo +DROP DATABASE mysqltest1; + +--echo +use test; + +--echo +--echo ######################################################################## diff -Nrup a/sql/sp.cc b/sql/sp.cc --- a/sql/sp.cc 2007-08-15 17:42:56 +04:00 +++ b/sql/sp.cc 2007-08-22 14:33:12 +04:00 @@ -574,7 +574,7 @@ db_load_routine(THD *thd, int type, sp_n 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; thd->spcont= NULL; @@ -2027,34 +2027,41 @@ create_string(THD *thd, String *buf, -/* +/** Change the current database if needed. - SYNOPSIS - sp_use_new_db() - thd thread handle - new_db new database name (a string and its length) - old_db [IN] str points to a buffer where to store the old - database, length contains the size of the buffer - [OUT] if old db was not NULL, its name is copied - to the buffer pointed at by str and length is updated - accordingly. Otherwise str[0] is set to '\0' and length - is set to 0. The out parameter should be used only if - the database name has been changed (see dbchangedp). - dbchangedp [OUT] is set to TRUE if the current database is changed, - FALSE otherwise. A database is not changed if the old - name is the same as the new one, both names are empty, - or an error has occurred. - - RETURN VALUE - 0 success - 1 access denied or out of memory (the error message is - set in THD) + @param[in] thd thread handle + @param[in] new_db new database name + @param[in, out] old_db IN: str points to a buffer where to store + the old database, length contains the + size of the buffer + OUT: if old db was not NULL, its name is + copied to the buffer pointed at by str + and length is updated accordingly. + Otherwise str[0] is set to '\0' and + length is set to 0. The out parameter + should be used only if the database name + has been changed (see dbchangedp). + @param[in] force_switch Flag to mysql_change_db(). For more information, + see mysql_change_db() comment. + @param[out] dbchangedp is set to TRUE if the current database is + changed, FALSE otherwise. The current + database is not changed if the old name + is equal to the new one, both names are + empty, or an error has occurred. + + @return Operation status. + @retval 0 on success + @retval 1 access denied or out of memory + (the error message is set in THD) */ int -sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db, - bool no_access_check, bool *dbchangedp) +sp_use_new_db(THD *thd, + LEX_STRING new_db, + LEX_STRING *old_db, + bool force_switch, + bool *dbchangedp) { int ret; DBUG_ENTER("sp_use_new_db"); @@ -2085,7 +2092,7 @@ sp_use_new_db(THD *thd, LEX_STRING new_d 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; DBUG_RETURN(ret); diff -Nrup a/sql/sql_class.h b/sql/sql_class.h --- a/sql/sql_class.h 2007-08-17 12:18:54 +04:00 +++ b/sql/sql_class.h 2007-08-22 14:33:12 +04:00 @@ -1802,11 +1802,27 @@ public: } } - /* - 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). + /** + Set the current database; use deep copy of C-string. + + @param new_db a pointer to the new database name. + @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) { @@ -1821,6 +1837,19 @@ public: db_length= db ? new_db_len : 0; 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) { db= new_db; diff -Nrup a/sql/sql_db.cc b/sql/sql_db.cc --- a/sql/sql_db.cc 2007-08-15 17:42:58 +04:00 +++ b/sql/sql_db.cc 2007-08-22 14:33:12 +04:00 @@ -39,6 +39,10 @@ static long mysql_rm_known_files(THD *th 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 void mysql_change_db_impl(THD *thd, + LEX_STRING *new_db_name, + ulong new_db_access, + CHARSET_INFO *new_db_charset); /* Database lock hash */ @@ -988,7 +992,7 @@ exit: it to 0. */ 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)); start_waiting_global_read_lock(thd); exit2: @@ -1347,21 +1351,48 @@ static void mysql_change_db_impl(THD *th /** - @brief Change the current database. + @brief Change the current database and its attributes. @param thd thread handle @param new_db_name database name - @param force_switch if this flag is set (TRUE), mysql_change_db() will - switch to NULL db if the specified database is not - available anymore. Corresponding warning will be - thrown in this case. This flag is used to change - database in stored-routine-execution code. - - @details Check that the database name corresponds to a valid and existent - database, check access rights (unless called with no_access_check), and - set the current database. This function is called to change the current - database upon user request (COM_CHANGE_DB command) or temporarily, to - execute a stored routine. + @param force_switch if force_switch is FALSE, then the operation will fail if + + - new_db_name is NULL or empty; + + - OR new database name is invalid + (check_db_name() failed); + + - OR user has no privilege on the new database; + + - OR new database does not exist; + + 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 currently employed. When the replication slave thread switches the @@ -1398,8 +1429,13 @@ bool mysql_change_db(THD *thd, const LEX if (force_switch) { /* - This can only happen when we restore the old db in THD after - execution of a routine is complete. Change db to NULL. + This can happen only if we're switching the current database back + 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); @@ -1417,7 +1453,7 @@ bool mysql_change_db(THD *thd, const LEX if (my_strcasecmp(system_charset_info, new_db_name->str, 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, system_charset_info); @@ -1444,8 +1480,8 @@ bool mysql_change_db(THD *thd, const LEX even if we are called from sp_head::execute(). 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 - to be sure. + from sp_head::execute(). But let's switch the current database to NULL + in this case to be sure. */ if (check_db_name(&new_db_file_name)) @@ -1454,10 +1490,8 @@ bool mysql_change_db(THD *thd, const LEX my_free(new_db_file_name.str, MYF(0)); if (force_switch) - { - /* Change db to NULL. */ mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); - } + DBUG_RETURN(TRUE); } @@ -1492,6 +1526,8 @@ bool mysql_change_db(THD *thd, const LEX { if (force_switch) { + /* Throw a warning and free new_db_file_name. */ + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR), new_db_file_name.str); @@ -1502,12 +1538,19 @@ bool mysql_change_db(THD *thd, const LEX mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); + /* The operation succeed. */ + DBUG_RETURN(FALSE); } else { + /* Report an error an free new_db_file_name. */ + my_error(ER_BAD_DB_ERROR, MYF(0), new_db_file_name.str); my_free(new_db_file_name.str, MYF(0)); + + /* The operation failed. */ + DBUG_RETURN(TRUE); } } @@ -1820,7 +1863,7 @@ bool mysql_rename_db(THD *thd, LEX_STRIN /* Step9: Let's do "use newdb" if we renamed the current database */ if (change_to_newdb) - error|= mysql_change_db(thd, new_db, 0); + error|= mysql_change_db(thd, new_db, FALSE); exit: pthread_mutex_lock(&LOCK_lock_db); diff -Nrup a/sql/sql_prepare.cc b/sql/sql_prepare.cc --- a/sql/sql_prepare.cc 2007-08-15 17:42:59 +04:00 +++ b/sql/sql_prepare.cc 2007-08-22 14:33:12 +04:00 @@ -163,6 +163,12 @@ private: SELECT_LEX and other classes). */ MEM_ROOT main_mem_root; + + /** + Name of the database that was current at the preparation of the + statement. + */ + LEX_STRING db_name; }; @@ -2868,6 +2874,19 @@ bool Prepared_statement::prepare(const c init_param_array(this); lex->set_trg_event_type_for_tables(); + /* Remember the current database. */ + + if (thd->db && thd->db_length) + { + db_name.str= thd->strmake(thd->db, thd->db_length); + db_name.length= thd->db_length; + } + else + { + db_name.str= NULL; + db_name.length= 0; + } + /* While doing context analysis of the query (in check_prepared_statement) we allocate a lot of additional memory: for open tables, JOINs, derived @@ -2892,7 +2911,6 @@ bool Prepared_statement::prepare(const c if (error == 0) error= check_prepared_statement(this, name.str != 0); - /* Currently CREATE PROCEDURE/TRIGGER/EVENT are prohibited in prepared statements: ensure we have no memory leak here if by someone tries @@ -2974,6 +2992,9 @@ bool Prepared_statement::execute(String Query_arena *old_stmt_arena; bool error= TRUE; + LEX_STRING saved_db_name; + bool cur_db_changed= FALSE; + status_var_increment(thd->status_var.com_stmt_execute); /* Check if we got an error when sending long data */ @@ -3022,6 +3043,24 @@ bool Prepared_statement::execute(String */ thd->set_n_backup_statement(this, &stmt_backup); + + /* Switch the current database (if needed). */ + + if (thd->db && !db_name.str || + !thd->db && db_name.str || + strcmp(thd->db, db_name.str) != 0) + { + saved_db_name.str= thd->strmake(thd->db, thd->db_length); + saved_db_name.length= thd->db_length; + + cur_db_changed= TRUE; + + if (mysql_change_db(thd, &db_name, TRUE)) + goto error; + } + + /* Allocate query. */ + if (expanded_query->length() && alloc_query(thd, (char*) expanded_query->ptr(), expanded_query->length()+1)) @@ -3050,6 +3089,8 @@ bool Prepared_statement::execute(String thd->protocol= protocol; /* activate stmt protocol */ + /* Go! */ + if (open_cursor) error= mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR, &result, &cursor); @@ -3067,6 +3108,11 @@ bool Prepared_statement::execute(String query_cache_end_of_result(thd); } } + + /* Restore the current database (if changed). */ + + if (cur_db_changed) + mysql_change_db(thd, &saved_db_name, TRUE); thd->protocol= &thd->protocol_text; /* use normal protocol */