Search
j0ke.net Open Build Service
>
Projects
>
internetx
:
mysql5
>
mysql-5.0.26
> mysql-5.0.26-mybug25082.patch
Sign Up
|
Log In
Username
Password
Cancel
Overview
Repositories
Revisions
Requests
Users
Advanced
Attributes
Meta
File mysql-5.0.26-mybug25082.patch of Package mysql-5.0.26
from http://mysql.bkbits.net:8080/mysql-5.0-community/?PAGE=gnupatch&REV=1.2410.5.7 --- mysql-test/r/sp.result | 15 ++ mysql-test/t/sp.test | 40 ++++++ sql/mysql_priv.h | 3 sql/sp.cc | 8 - sql/sp_head.cc | 2 sql/sql_db.cc | 326 ++++++++++++++++++++++++++++++++----------------- sql/sql_parse.cc | 16 +- 7 files changed, 287 insertions(+), 123 deletions(-) --- mysql-test/r/sp.result.orig +++ mysql-test/r/sp.result @@ -5457,4 +5457,19 @@ CAD CHF DROP FUNCTION bug21493| DROP TABLE t3,t4| +DROP DATABASE IF EXISTS mysqltest1| +DROP DATABASE IF EXISTS mysqltest2| +CREATE DATABASE mysqltest1| +CREATE DATABASE mysqltest2| +CREATE PROCEDURE mysqltest1.p1() +DROP DATABASE mysqltest2| +use mysqltest2| +CALL mysqltest1.p1()| +Warnings: +Note 1049 Unknown database 'mysqltest2' +SELECT DATABASE()| +DATABASE() +NULL +DROP DATABASE mysqltest1| +use test| drop table t1,t2; --- mysql-test/t/sp.test.orig +++ mysql-test/t/sp.test @@ -6389,6 +6389,46 @@ SELECT bug21493(Member_ID) FROM t3| DROP FUNCTION bug21493| DROP TABLE t3,t4| + +# +# BUG#25082: Default database change on trigger execution breaks replication. +# +# As it turned out, this bug has actually two bugs. So, here we have two test +# cases -- one in sp.test, the other in sp-security.test. +# + +# +# Test case 1: error on dropping the current database. +# + +# Prepare. + +--disable_warnings +DROP DATABASE IF EXISTS mysqltest1| +DROP DATABASE IF EXISTS mysqltest2| +--enable_warnings + +CREATE DATABASE mysqltest1| +CREATE DATABASE mysqltest2| + +# Test. + +CREATE PROCEDURE mysqltest1.p1() + DROP DATABASE mysqltest2| + +use mysqltest2| + +CALL mysqltest1.p1()| + +SELECT DATABASE()| + +# Cleanup. + +DROP DATABASE mysqltest1| + +use test| + + # # BUG#NNNN: New bug synopsis # --- sql/mysql_priv.h.orig +++ sql/mysql_priv.h @@ -648,7 +648,8 @@ int quick_rm_table(enum db_type base,con const char *table_name); void close_cached_table(THD *thd, TABLE *table); bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list); -bool mysql_change_db(THD *thd,const char *name,bool no_access_check); +bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, + bool force_switch); void mysql_parse(THD *thd,char *inBuf,uint length); bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length); bool is_update_query(enum enum_sql_command command); --- sql/sp.cc.orig +++ sql/sp.cc @@ -461,14 +461,14 @@ db_load_routine(THD *thd, int type, sp_n { sp_head *sp= newlex.sphead; - if (dbchanged && (ret= mysql_change_db(thd, old_db.str, 1))) + if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE))) goto end; delete sp; ret= SP_PARSE_ERROR; } else { - if (dbchanged && (ret= mysql_change_db(thd, old_db.str, 1))) + if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE))) goto end; *sphp= newlex.sphead; (*sphp)->set_definer(&definer_user_name, &definer_host_name); @@ -649,7 +649,7 @@ db_create_routine(THD *thd, int type, sp done: close_thread_tables(thd); if (dbchanged) - (void) mysql_change_db(thd, old_db.str, 1); + (void) mysql_change_db(thd, &old_db, 1); DBUG_RETURN(ret); } @@ -1902,7 +1902,7 @@ sp_use_new_db(THD *thd, LEX_STRING new_d DBUG_RETURN(0); } - ret= mysql_change_db(thd, new_db.str, no_access_check); + ret= mysql_change_db(thd, &new_db, no_access_check); *dbchangedp= ret == 0; DBUG_RETURN(ret); --- sql/sp_head.cc.orig +++ sql/sp_head.cc @@ -1148,7 +1148,7 @@ sp_head::execute(THD *thd) (It would generate an error from mysql_change_db() when old_db=="") */ if (! thd->killed) - err_status|= mysql_change_db(thd, old_db.str, 1); + err_status|= mysql_change_db(thd, &old_db, TRUE); } m_flags&= ~IS_INVOKED; DBUG_PRINT("info", --- sql/sql_db.cc.orig +++ sql/sql_db.cc @@ -1122,154 +1122,256 @@ err: } -/* - Change the current database. - - SYNOPSIS - mysql_change_db() - thd thread handle - name database name - no_access_check if TRUE, don't do access check. In this - case name may be "" - - DESCRIPTION - 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. - - NOTES - This function is not the only way to switch the database that - is currently employed. When the replication slave thread - switches the database before executing a query, it calls - thd->set_db directly. However, if the query, in turn, uses - a stored routine, the stored routine will use this function, - even if it's run on the slave. - - This function allocates the name of the database on the system - heap: this is necessary to be able to uniformly change the - database from any module of the server. Up to 5.0 different - modules were using different memory to store the name of the - database, and this led to memory corruption: a stack pointer - set by Stored Procedures was used by replication after the - stack address was long gone. - - This function does not send anything, including error - messages, to the client. If that should be sent to the client, - call net_send_error after this function. +/** + @brief Internal implementation: switch current database to a valid one. - RETURN VALUES - 0 OK - 1 error + @param thd Thread context. + @param new_db_name Name of the database to switch to. The function will + take ownership of the name (the caller must not free + the allocated memory). If the name is NULL, we're + going to switch to NULL db. + @param new_db_access Privileges of the new database. + @param new_db_charset Character set of the new database. */ -bool mysql_change_db(THD *thd, const char *name, bool no_access_check) +static void mysql_change_db_impl(THD *thd, + LEX_STRING *new_db_name, + ulong new_db_access, + CHARSET_INFO *new_db_charset) { - int path_length, db_length; - char *db_name; - bool system_db= 0; + /* 1. Change current database in THD. */ + + if (new_db_name == NULL) + { + /* + THD::set_db() does all the job -- it frees previous database name and + sets the new one. + */ + + thd->set_db(NULL, 0); + } + else if (new_db_name == &information_schema_name) + { + /* + Here we must use THD::set_db(), because we want to copy + information_schema_name constant. + */ + + thd->set_db(information_schema_name.str, information_schema_name.length); + } + else + { + /* + Here we already have a copy of database name to be used in THD. So, + we just call THD::reset_db(). Since THD::reset_db() does not releases + the previous database name, we should do it explicitly. + */ + + x_free(thd->db); + + thd->reset_db(new_db_name->str, new_db_name->length); + } + + /* 2. Update security context. */ + #ifndef NO_EMBEDDED_ACCESS_CHECKS - ulong db_access; - Security_context *sctx= thd->security_ctx; - LINT_INIT(db_access); + thd->security_ctx->db_access= new_db_access; #endif + + /* 3. Update db-charset environment variables. */ + + thd->db_charset= new_db_charset; + thd->variables.collation_database= new_db_charset; +} + + +/** + @brief Change the current database. + + @param thd thread handle + @param 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. + + This function is not the only way to switch the database that is + currently employed. When the replication slave thread switches the + database before executing a query, it calls thd->set_db directly. + However, if the query, in turn, uses a stored routine, the stored routine + will use this function, even if it's run on the slave. + + This function allocates the name of the database on the system heap: this + is necessary to be able to uniformly change the database from any module + of the server. Up to 5.0 different modules were using different memory to + store the name of the database, and this led to memory corruption: + a stack pointer set by Stored Procedures was used by replication after + the stack address was long gone. + + @return Operation status + @retval FALSE Success + @retval TRUE Error +*/ + +bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) +{ + LEX_STRING new_db_file_name; + + Security_context *sctx= thd->security_ctx; + ulong db_access= sctx->db_access; + DBUG_ENTER("mysql_change_db"); - DBUG_PRINT("enter",("name: '%s'",name)); + DBUG_PRINT("enter",("name: '%s'", new_db_name->str)); - if (name == NULL || name[0] == '\0' && no_access_check == FALSE) + if (new_db_name == NULL || + new_db_name->length == 0 || + new_db_name->str == NULL) { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); - DBUG_RETURN(1); /* purecov: inspected */ + if (force_switch) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR)); + + /* Change db to NULL. */ + + mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); + + DBUG_RETURN(FALSE); + } + else + { + my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); + + DBUG_RETURN(TRUE); + } } - else if (name[0] == '\0') + + if (my_strcasecmp(system_charset_info, new_db_name->str, + information_schema_name.str) == 0) { - /* Called from SP to restore the original database, which was NULL */ - DBUG_ASSERT(no_access_check); - system_db= 1; - db_name= NULL; - db_length= 0; - goto end; + /* Switch database to INFORMATION_SCHEMA. */ + + mysql_change_db_impl(thd, &information_schema_name, SELECT_ACL, + system_charset_info); + + DBUG_RETURN(FALSE); } + /* Now we need to make a copy because check_db_name requires a - non-constant argument. TODO: fix check_db_name. + non-constant argument. Actually, it takes database file name. + + TODO: fix check_db_name(). */ - if ((db_name= my_strdup(name, MYF(MY_WME))) == NULL) - DBUG_RETURN(1); /* the error is set */ - db_length= strlen(db_name); - if (check_db_name(db_name)) - { - my_error(ER_WRONG_DB_NAME, MYF(0), db_name); - my_free(db_name, MYF(0)); - DBUG_RETURN(1); - } - DBUG_PRINT("info",("Use database: %s", db_name)); - if (!my_strcasecmp(system_charset_info, db_name, information_schema_name.str)) + + new_db_file_name.str= my_strdup(new_db_name->str, MYF(MY_WME)); + new_db_file_name.length= new_db_name->length; + + if (new_db_file_name.str == NULL) + DBUG_RETURN(TRUE); /* the error is set */ + + /* + NOTE: if check_db_name() fails, we should throw an error in any case, + 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. + */ + + if (check_db_name(new_db_file_name.str)) { - system_db= 1; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - db_access= SELECT_ACL; -#endif - goto end; + my_error(ER_WRONG_DB_NAME, MYF(0), new_db_file_name.str); + 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); } + DBUG_PRINT("info",("Use database: %s", new_db_file_name.str)); + #ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!no_access_check) + if (!force_switch) /* FIXME: this is BUG#27337. */ { - if (test_all_bits(sctx->master_access, DB_ACLS)) - db_access=DB_ACLS; - else - db_access= (acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name, 0) | - sctx->master_access); - if (!(db_access & DB_ACLS) && (!grant_option || - check_grant_db(thd,db_name))) + db_access= + test_all_bits(sctx->master_access, DB_ACLS) ? + DB_ACLS : + acl_get(sctx->host, + sctx->ip, + sctx->priv_user, + new_db_file_name.str, + FALSE) | sctx->master_access; + + if (!force_switch && + !(db_access & DB_ACLS) && + (!grant_option || check_grant_db(thd, new_db_file_name.str))) { my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, - db_name); + new_db_file_name.str); mysql_log.write(thd, COM_INIT_DB, ER(ER_DBACCESS_DENIED_ERROR), - sctx->priv_user, sctx->priv_host, db_name); - my_free(db_name, MYF(0)); - DBUG_RETURN(1); + sctx->priv_user, sctx->priv_host, new_db_file_name.str); + my_free(new_db_file_name.str, MYF(0)); + DBUG_RETURN(TRUE); } } #endif - if (check_db_dir_existence(db_name)) + if (check_db_dir_existence(new_db_file_name.str)) { - my_error(ER_BAD_DB_ERROR, MYF(0), db_name); - my_free(db_name, MYF(0)); - DBUG_RETURN(1); - } + if (force_switch) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_BAD_DB_ERROR, ER(ER_BAD_DB_ERROR), + new_db_file_name.str); -end: - x_free(thd->db); - DBUG_ASSERT(db_name == NULL || db_name[0] != '\0'); - thd->reset_db(db_name, db_length); // THD::~THD will free this -#ifndef NO_EMBEDDED_ACCESS_CHECKS - if (!no_access_check) - sctx->db_access= db_access; -#endif - if (system_db) - { - thd->db_charset= system_charset_info; - thd->variables.collation_database= system_charset_info; + my_free(new_db_file_name.str, MYF(0)); + + /* Change db to NULL. */ + + mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); + + DBUG_RETURN(FALSE); + } + else + { + my_error(ER_BAD_DB_ERROR, MYF(0), new_db_file_name.str); + my_free(new_db_file_name.str, MYF(0)); + DBUG_RETURN(TRUE); + } } - else + + /* + NOTE: in mysql_change_db_impl() new_db_file_name is assigned to THD + attributes and will be freed in THD::~THD(). + */ + { - HA_CREATE_INFO create; + HA_CREATE_INFO db_options; - load_db_opt_by_name(thd, db_name, &create); + load_db_opt_by_name(thd, new_db_name->str, &db_options); - thd->db_charset= create.default_table_charset ? - create.default_table_charset : - thd->variables.collation_server; - thd->variables.collation_database= thd->db_charset; + mysql_change_db_impl(thd, &new_db_file_name, db_access, + db_options.default_table_charset ? + db_options.default_table_charset : + thd->variables.collation_server); } - DBUG_RETURN(0); + + DBUG_RETURN(FALSE); } --- sql/sql_parse.cc.orig +++ sql/sql_parse.cc @@ -291,7 +291,8 @@ int check_user(THD *thd, enum enum_serve bool check_count) { DBUG_ENTER("check_user"); - + LEX_STRING db_str= { (char *) db, db ? strlen(db) : 0 }; + #ifdef NO_EMBEDDED_ACCESS_CHECKS thd->main_security_ctx.master_access= GLOBAL_ACLS; // Full rights /* Change database if necessary */ @@ -302,7 +303,7 @@ int check_user(THD *thd, enum enum_serve function returns 0 */ thd->reset_db(NULL, 0); - if (mysql_change_db(thd, db, FALSE)) + if (mysql_change_db(thd, &db_str, FALSE)) { /* Send the error to the client */ net_send_error(thd); @@ -444,7 +445,7 @@ int check_user(THD *thd, enum enum_serve /* Change database if necessary */ if (db && db[0]) { - if (mysql_change_db(thd, db, FALSE)) + if (mysql_change_db(thd, &db_str, FALSE)) { /* Send error to the client */ net_send_error(thd); @@ -1609,7 +1610,7 @@ bool dispatch_command(enum enum_server_c &LOCK_status); thd->convert_string(&tmp, system_charset_info, packet, strlen(packet), thd->charset()); - if (!mysql_change_db(thd, tmp.str, FALSE)) + if (!mysql_change_db(thd, &tmp, FALSE)) { mysql_log.write(thd,command,"%s",thd->db); send_ok(thd); @@ -3603,9 +3604,14 @@ end_with_restore_list: } #endif case SQLCOM_CHANGE_DB: - if (!mysql_change_db(thd,select_lex->db,FALSE)) + { + LEX_STRING db_str= { (char *) select_lex->db, strlen(select_lex->db) }; + + if (!mysql_change_db(thd, &db_str, FALSE)) send_ok(thd); + break; + } case SQLCOM_LOAD: {