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:
   {
