List:Commits« Previous MessageNext Message »
From:Alexander Nozdrin Date:August 31 2007 4:42pm
Subject:bk commit into 5.1 tree (anozdrin:1.2588) BUG#25843
View as plain text  
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-31 20:42:14+04:00, anozdrin@stripped +13 -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/include/query_cache_sql_prepare.inc@stripped, 2007-08-31 20:42:11+04:00, anozdrin@stripped +218 -0
    The first part of the test case for BUG#25843.

  mysql-test/r/query_cache_ps_no_prot.result@stripped, 2007-08-31 20:42:12+04:00, anozdrin@stripped +158 -0
    Update result file.

  mysql-test/r/query_cache_ps_ps_prot.result@stripped, 2007-08-31 20:42:12+04:00, anozdrin@stripped +158 -0
    Update result file.

  mysql-test/suite/rpl/r/rpl_ps.result@stripped, 2007-08-31 20:42:12+04:00, anozdrin@stripped +39 -0
    Update result file.

  mysql-test/suite/rpl/t/rpl_ps.test@stripped, 2007-08-31 20:42:12+04:00, anozdrin@stripped +68 -0
    The second part of the test case for BUG#25843.

  sql/mysql_priv.h@stripped, 2007-08-31 20:42:12+04:00, anozdrin@stripped +7 -0
    Added mysql_opt_change_db() prototype.

  sql/sp.cc@stripped, 2007-08-31 20:42:12+04:00, anozdrin@stripped +42 -106
    1. Polishing;
    2. sp_use_new_db() has been removed;
    3. Use mysql_opt_change_db() instead of sp_use_new_db().

  sql/sp.h@stripped, 2007-08-31 20:42:12+04:00, anozdrin@stripped +0 -11
    sp_use_new_db() has been removed.
    This function has nothing to do with a) sp and b) *new* database.
    It was merely "switch the current database if needed".

  sql/sp_head.cc@stripped, 2007-08-31 20:42:12+04:00, anozdrin@stripped +13 -9
    1. Polishing.
    2. Use mysql_opt_change_db() instead of sp_use_new_db().

  sql/sql_class.cc@stripped, 2007-08-31 20:42:12+04:00, anozdrin@stripped +4 -3
    Move THD::{db, db_length} into Statement.

  sql/sql_class.h@stripped, 2007-08-31 20:42:12+04:00, anozdrin@stripped +38 -22
    Move THD::{db, db_length} into Statement.

  sql/sql_db.cc@stripped, 2007-08-31 20:42:12+04:00, anozdrin@stripped +97 -1
    Introduce mysql_opt_change_db() as a replacement (and inspired by)
    sp_use_new_db().

  sql/sql_prepare.cc@stripped, 2007-08-31 20:42:12+04:00, anozdrin@stripped +48 -0
    1. Remember the current database in Prepared_statement::prepare().
    2. Switch/restore the current database in Prepared_statement::execute().

diff -Nrup a/mysql-test/include/query_cache_sql_prepare.inc b/mysql-test/include/query_cache_sql_prepare.inc
--- a/mysql-test/include/query_cache_sql_prepare.inc	2007-06-23 21:16:48 +04:00
+++ b/mysql-test/include/query_cache_sql_prepare.inc	2007-08-31 20:42:11 +04:00
@@ -275,5 +275,223 @@ drop table t1;
 --echo ---- disconnect connection con1 ----
 disconnect con1;
 
+#
+# 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;
+
+# -- Create prepared statement, which has no current database.
+
+--echo
+PREPARE stmt_c_3 FROM 'SELECT DATABASE(), @@collation_database';
+
+# -- Here we have: current db: NULL; stmt db: NULL;
+--echo
+EXECUTE stmt_c_3;
+
+--echo
+use mysqltest1;
+
+# -- Here we have: current db: mysqltest1; stmt db: mysqltest2 (non-existent);
+--echo
+EXECUTE stmt_c_2;
+
+--echo
+SELECT DATABASE(), @@collation_database;
+
+# -- Here we have: current db: mysqltest1; stmt db: NULL;
+--echo
+EXECUTE stmt_c_3;
+
+--echo
+SELECT DATABASE(), @@collation_database;
+
+--echo
+DROP DATABASE mysqltest1;
+
+--echo
+use test;
+
+--echo
+--echo ########################################################################
+
+###############################################################################
+
 set @@global.query_cache_size=@initial_query_cache_size;
 flush status; # reset Qcache status variables for next tests
diff -Nrup a/mysql-test/r/query_cache_ps_no_prot.result b/mysql-test/r/query_cache_ps_no_prot.result
--- a/mysql-test/r/query_cache_ps_no_prot.result	2007-06-23 21:16:48 +04:00
+++ b/mysql-test/r/query_cache_ps_no_prot.result	2007-08-31 20:42:12 +04:00
@@ -371,5 +371,163 @@ Variable_name	Value
 Qcache_hits	21
 drop table t1;
 ---- disconnect connection con1 ----
+########################################################################
+#
+# 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
+
+PREPARE stmt_c_3 FROM 'SELECT DATABASE(), @@collation_database';
+
+EXECUTE stmt_c_3;
+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
+
+EXECUTE stmt_c_3;
+DATABASE()	@@collation_database
+NULL	latin1_swedish_ci
+
+SELECT DATABASE(), @@collation_database;
+DATABASE()	@@collation_database
+mysqltest1	utf8_unicode_ci
+
+DROP DATABASE mysqltest1;
+
+use test;
+
+########################################################################
 set @@global.query_cache_size=@initial_query_cache_size;
 flush status;
diff -Nrup a/mysql-test/r/query_cache_ps_ps_prot.result b/mysql-test/r/query_cache_ps_ps_prot.result
--- a/mysql-test/r/query_cache_ps_ps_prot.result	2007-06-23 21:16:48 +04:00
+++ b/mysql-test/r/query_cache_ps_ps_prot.result	2007-08-31 20:42:12 +04:00
@@ -371,5 +371,163 @@ Variable_name	Value
 Qcache_hits	19
 drop table t1;
 ---- disconnect connection con1 ----
+########################################################################
+#
+# 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
+
+PREPARE stmt_c_3 FROM 'SELECT DATABASE(), @@collation_database';
+
+EXECUTE stmt_c_3;
+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
+
+EXECUTE stmt_c_3;
+DATABASE()	@@collation_database
+NULL	latin1_swedish_ci
+
+SELECT DATABASE(), @@collation_database;
+DATABASE()	@@collation_database
+mysqltest1	utf8_unicode_ci
+
+DROP DATABASE mysqltest1;
+
+use test;
+
+########################################################################
 set @@global.query_cache_size=@initial_query_cache_size;
 flush status;
diff -Nrup a/mysql-test/suite/rpl/r/rpl_ps.result b/mysql-test/suite/rpl/r/rpl_ps.result
--- a/mysql-test/suite/rpl/r/rpl_ps.result	2007-06-27 16:27:30 +04:00
+++ b/mysql-test/suite/rpl/r/rpl_ps.result	2007-08-31 20:42:12 +04:00
@@ -26,5 +26,44 @@ from-master-2-'',
 from-var-from-master-3
 drop table t1;
 stop slave;
+
+########################################################################
+#
+# BUG#25843: Changing default database between PREPARE and EXECUTE of
+# statement breaks binlog.
+#
+########################################################################
+
+#
+# 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
+slave-bin.000001	106	Query	2	193	use `test`; INSERT INTO t1 VALUES(1)
+slave-bin.000001	193	Query	2	280	use `test`; INSERT INTO t1 VALUES(1)
+slave-bin.000001	280	Rotate	2	323	slave-bin.000002;pos=4
+
+DROP DATABASE mysqltest1;
+
+use test;
+
+########################################################################
 reset master;
 reset slave;
diff -Nrup a/mysql-test/suite/rpl/t/rpl_ps.test b/mysql-test/suite/rpl/t/rpl_ps.test
--- a/mysql-test/suite/rpl/t/rpl_ps.test	2007-06-27 16:27:27 +04:00
+++ b/mysql-test/suite/rpl/t/rpl_ps.test	2007-08-31 20:42:12 +04:00
@@ -46,6 +46,74 @@ stop slave;
 
 # End of 4.1 tests
 
+#
+# 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 #
+--echo # BUG#25843: Changing default database between PREPARE and EXECUTE of
+--echo # statement breaks binlog.
+--echo #
+--echo ########################################################################
+
+###############################################################################
+
+--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 ########################################################################
+
+###############################################################################
+
 reset master;
 reset slave;
 disconnect master;
diff -Nrup a/sql/mysql_priv.h b/sql/mysql_priv.h
--- a/sql/mysql_priv.h	2007-08-16 22:02:22 +04:00
+++ b/sql/mysql_priv.h	2007-08-31 20:42:12 +04:00
@@ -937,8 +937,15 @@ bool mysql_rename_tables(THD *thd, TABLE
 bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db,
                       char *new_table_name, char *new_table_alias,
                       bool skip_error);
+
 bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name,
                      bool force_switch);
+
+bool mysql_opt_change_db(THD *thd,
+                         const LEX_STRING *new_db_name,
+                         LEX_STRING *saved_db_name,
+                         bool force_switch,
+                         bool *cur_db_changed);
 
 void mysql_parse(THD *thd, const char *inBuf, uint length,
                  const char ** semicolon);
diff -Nrup a/sql/sp.cc b/sql/sp.cc
--- a/sql/sp.cc	2007-08-30 20:06:17 +04:00
+++ b/sql/sp.cc	2007-08-31 20:42:12 +04:00
@@ -519,9 +519,10 @@ db_load_routine(THD *thd, int type, sp_n
 {
   LEX *old_lex= thd->lex, newlex;
   String defstr;
-  char old_db_buf[NAME_LEN+1];
-  LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
-  bool dbchanged;
+  char saved_cur_db_name_buf[NAME_LEN+1];
+  LEX_STRING saved_cur_db_name=
+    { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
+  bool cur_db_changed;
   ulong old_sql_mode= thd->variables.sql_mode;
   ha_rows old_select_limit= thd->variables.select_limit;
   sp_rcontext *old_spcont= thd->spcont;
@@ -566,16 +567,16 @@ db_load_routine(THD *thd, int type, sp_n
   }
 
   /*
-    Change current database if needed.
+    Change the current database (if needed).
 
-    collation_database will be updated here. However, it can be wrong,
-    because it will contain the current value of the database collation.
-    We need collation_database to be fixed at the creation time -- so
-    we'll update it later in switch_query_ctx().
+    TODO: why do we force switch here?
   */
 
-  if ((ret= sp_use_new_db(thd, name->m_db, &old_db, TRUE, &dbchanged)))
+  if (mysql_opt_change_db(thd, &name->m_db, &saved_cur_db_name, TRUE,
+                          &cur_db_changed))
+  {
     goto end;
+  }
 
   thd->spcont= NULL;
 
@@ -584,34 +585,42 @@ db_load_routine(THD *thd, int type, sp_n
 
     lex_start(thd);
 
-    if (parse_sql(thd, &lip, creation_ctx) || newlex.sphead == NULL)
-    {
-      sp_head *sp= newlex.sphead;
+    ret= parse_sql(thd, &lip, creation_ctx) || newlex.sphead == NULL;
 
-      if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE)))
-        goto end;
-      delete sp;
-      ret= SP_PARSE_ERROR;
+    /*
+      Force switching back to the saved current database (if changed),
+      because it may be NULL. In this case, mysql_change_db() would
+      generate an error.
+    */
+
+    if (cur_db_changed && mysql_change_db(thd, &saved_cur_db_name, TRUE))
+    {
+      delete newlex.sphead;
+      ret= -1;
+      goto end;
     }
-    else
+
+    if (ret)
     {
-      if (dbchanged && (ret= mysql_change_db(thd, &old_db, TRUE)))
-        goto end;
-      *sphp= newlex.sphead;
-      (*sphp)->set_definer(&definer_user_name, &definer_host_name);
-      (*sphp)->set_info(created, modified, &chistics, sql_mode);
-      (*sphp)->set_creation_ctx(creation_ctx);
-      (*sphp)->optimize();
-      /*
-        Not strictly necessary to invoke this method here, since we know
-        that we've parsed CREATE PROCEDURE/FUNCTION and not an
-        UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
-        maintain the invariant that this method is called for each
-        distinct statement, in case its logic is extended with other
-        types of analyses in future.
-      */
-      newlex.set_trg_event_type_for_tables();
+      delete newlex.sphead;
+      ret= SP_PARSE_ERROR;
+      goto end;
     }
+
+    *sphp= newlex.sphead;
+    (*sphp)->set_definer(&definer_user_name, &definer_host_name);
+    (*sphp)->set_info(created, modified, &chistics, sql_mode);
+    (*sphp)->set_creation_ctx(creation_ctx);
+    (*sphp)->optimize();
+    /*
+      Not strictly necessary to invoke this method here, since we know
+      that we've parsed CREATE PROCEDURE/FUNCTION and not an
+      UPDATE/DELETE/INSERT/REPLACE/LOAD/CREATE TABLE, but we try to
+      maintain the invariant that this method is called for each
+      distinct statement, in case its logic is extended with other
+      types of analyses in future.
+    */
+    newlex.set_trg_event_type_for_tables();
   }
 
 end:
@@ -2023,77 +2032,4 @@ create_string(THD *thd, String *buf,
   }
   buf->append(body, bodylen);
   return TRUE;
-}
-
-
-
-/**
-  Change the current database if needed.
-
-  @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 force_switch,
-              bool *dbchangedp)
-{
-  int ret;
-  DBUG_ENTER("sp_use_new_db");
-  DBUG_PRINT("enter", ("newdb: %s", new_db.str));
-
-  /*
-    A stored routine always belongs to some database. The
-    old database (old_db) might be NULL, but to restore the
-    old database we will use mysql_change_db.
-  */
-  DBUG_ASSERT(new_db.str && new_db.length);
-
-  if (thd->db)
-  {
-    old_db->length= (strmake(old_db->str, thd->db, old_db->length) -
-                     old_db->str);
-  }
-  else
-  {
-    old_db->str[0]= '\0';
-    old_db->length= 0;
-  }
-
-  /* Don't change the database if the new name is the same as the old one. */
-  if (my_strcasecmp(system_charset_info, old_db->str, new_db.str) == 0)
-  {
-    *dbchangedp= FALSE;
-    DBUG_RETURN(0);
-  }
-
-  ret= mysql_change_db(thd, &new_db, force_switch);
-
-  *dbchangedp= ret == 0;
-  DBUG_RETURN(ret);
 }
diff -Nrup a/sql/sp.h b/sql/sp.h
--- a/sql/sp.h	2007-06-01 11:43:52 +04:00
+++ b/sql/sp.h	2007-08-31 20:42:12 +04:00
@@ -85,15 +85,4 @@ extern "C" uchar* sp_sroutine_key(const 
 */
 TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup);
 
-
-/*
-  Do a "use new_db". The current db is stored at old_db.  If new_db is the
-  same as the current one, nothing is changed.  dbchangedp is set to true if
-  the db was actually changed.
-*/
-
-int
-sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db,
-	      bool no_access_check, bool *dbchangedp);
-
 #endif /* _SP_H_ */
diff -Nrup a/sql/sp_head.cc b/sql/sp_head.cc
--- a/sql/sp_head.cc	2007-08-29 23:59:33 +04:00
+++ b/sql/sp_head.cc	2007-08-31 20:42:12 +04:00
@@ -1015,9 +1015,10 @@ bool
 sp_head::execute(THD *thd)
 {
   DBUG_ENTER("sp_head::execute");
-  char old_db_buf[NAME_LEN+1];
-  LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
-  bool dbchanged;
+  char saved_cur_db_name_buf[NAME_LEN+1];
+  LEX_STRING saved_cur_db_name=
+    { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
+  bool cur_db_changed= FALSE;
   sp_rcontext *ctx;
   bool err_status= FALSE;
   uint ip= 0;
@@ -1072,8 +1073,11 @@ sp_head::execute(THD *thd)
   */
 
   if (m_db.length &&
-      (err_status= sp_use_new_db(thd, m_db, &old_db, 0, &dbchanged)))
+      (err_status= mysql_opt_change_db(thd, &m_db, &saved_cur_db_name, FALSE,
+                                       &cur_db_changed)))
+  {
     goto done;
+  }
 
   if ((ctx= thd->spcont))
     ctx->clear_handler();
@@ -1252,14 +1256,14 @@ sp_head::execute(THD *thd)
     If the DB has changed, the pointer has changed too, but the
     original thd->db will then have been freed
   */
-  if (dbchanged)
+  if (cur_db_changed && !thd->killed)
   {
     /*
-      No access check when changing back to where we came from.
-      (It would generate an error from mysql_change_db() when old_db=="")
+      Force switching back to the saved current database, because it may be
+      NULL. In this case, mysql_change_db() would generate an error.
     */
-    if (! thd->killed)
-      err_status|= mysql_change_db(thd, &old_db, TRUE);
+
+    err_status|= mysql_change_db(thd, &saved_cur_db_name, TRUE);
   }
   m_flags&= ~IS_INVOKED;
   DBUG_PRINT("info",
diff -Nrup a/sql/sql_class.cc b/sql/sql_class.cc
--- a/sql/sql_class.cc	2007-08-20 21:13:25 +04:00
+++ b/sql/sql_class.cc	2007-08-31 20:42:12 +04:00
@@ -387,7 +387,6 @@ THD::THD()
   init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
   stmt_arena= this;
   thread_stack= 0;
-  db= 0;
   catalog= (char*)"std"; // the only catalog we have for now
   main_security_ctx.init();
   security_ctx= &main_security_ctx;
@@ -395,7 +394,7 @@ THD::THD()
   query_start_used= 0;
   count_cuted_fields= CHECK_FIELD_IGNORE;
   killed= NOT_KILLED;
-  db_length= col_access=0;
+  col_access=0;
   query_error= thread_specific_used= FALSE;
   hash_clear(&handler_tables_hash);
   tmp_table=0;
@@ -2040,7 +2039,9 @@ Statement::Statement(LEX *lex_arg, MEM_R
   lex(lex_arg),
   query(0),
   query_length(0),
-  cursor(0)
+  cursor(0),
+  db(NULL),
+  db_length(0)
 {
   name.str= NULL;
 }
diff -Nrup a/sql/sql_class.h b/sql/sql_class.h
--- a/sql/sql_class.h	2007-08-30 20:06:17 +04:00
+++ b/sql/sql_class.h	2007-08-31 20:42:12 +04:00
@@ -593,6 +593,22 @@ public:
   uint32 query_length;                          // current query length
   Server_side_cursor *cursor;
 
+  /**
+    Name of the current (default) database.
+
+    If there is the current (default) database, "db" contains its name. If
+    there is no current (default) database, "db" is NULL and "db_length" is
+    0. In other words, "db", "db_length" must either be NULL, or contain a
+    valid database name.
+
+    @note this attribute is set and alloced by the slave SQL thread (for
+    the THD of that thread); that thread is (and must remain, for now) the
+    only responsible for freeing this member.
+  */
+
+  char *db;
+  uint db_length;
+
 public:
 
   /* This constructor is called for backup statements */
@@ -1024,18 +1040,21 @@ public:
   */
   char	  *thread_stack;
 
+  /**
+    Currently selected catalog.
+  */
+  char *catalog;
+
   /*
-    db - currently selected database
-    catalog - currently selected catalog
-    WARNING: some members of THD (currently 'db', 'catalog' and 'query')  are
-    set and alloced by the slave SQL thread (for the THD of that thread); that
-    thread is (and must remain, for now) the only responsible for freeing these
-    3 members. If you add members here, and you add code to set them in
-    replication, don't forget to free_them_and_set_them_to_0 in replication
-    properly. For details see the 'err:' label of the handle_slave_sql()
-    in sql/slave.cc.
-   */
-  char   *db, *catalog;
+    WARNING: some members of THD (currently 'Statement::db',
+    'catalog' and 'query')  are set and alloced by the slave SQL thread
+    (for the THD of that thread); that thread is (and must remain, for now)
+    the only responsible for freeing these 3 members. If you add members
+    here, and you add code to set them in replication, don't forget to
+    free_them_and_set_them_to_0 in replication properly. For details see
+    the 'err:' label of the handle_slave_sql() in sql/slave.cc.
+  */
+
   Security_context main_security_ctx;
   Security_context *security_ctx;
 
@@ -1390,7 +1409,6 @@ public:
   uint	     tmp_table, global_read_lock;
   uint	     server_status,open_options;
   enum enum_thread_type system_thread;
-  uint       db_length;
   uint       select_number;             //number of select (used for EXPLAIN)
   /* variables.transaction_isolation is reset to this after each commit */
   enum_tx_isolation session_tx_isolation;
@@ -1814,11 +1832,10 @@ public:
     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.
+    @note This operation just sets {db, 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
@@ -1844,11 +1861,10 @@ public:
     @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.
+    @note This operation just sets {db, 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)
   {
diff -Nrup a/sql/sql_db.cc b/sql/sql_db.cc
--- a/sql/sql_db.cc	2007-08-30 20:06:17 +04:00
+++ b/sql/sql_db.cc	2007-08-31 20:42:12 +04:00
@@ -1350,8 +1350,67 @@ static void mysql_change_db_impl(THD *th
 }
 
 
+
+/**
+  Backup the current database name before switch.
+
+  @param[in]      thd             thread handle
+  @param[in, out] saved_db_name   IN: "str" points to a buffer where to store
+                                  the old database name, "length" contains the
+                                  buffer size
+                                  OUT: if the current (default) database is
+                                  not NULL, its name is copied to the
+                                  buffer pointed at by "str"
+                                  and "length" is updated accordingly.
+                                  Otherwise "str" is set to NULL and
+                                  "length" is set to 0.
+*/
+
+static void backup_current_db_name(THD *thd,
+                                   LEX_STRING *saved_db_name)
+{
+  if (!thd->db)
+  {
+    /* No current (default) database selected. */
+
+    saved_db_name->str= NULL;
+    saved_db_name->length= 0;
+  }
+  else
+  {
+    strmake(saved_db_name->str, thd->db, saved_db_name->length);
+    saved_db_name->length= thd->db_length;
+  }
+}
+
+
+/**
+  Return TRUE if db1_name is equal to db2_name, FALSE otherwise.
+
+  The function allows to compare database names according to the MySQL
+  rules. The database names db1 and db2 are equal if:
+     - db1 is NULL and db2 is NULL;
+     or
+     - db1 is not-NULL, db2 is not-NULL, db1 is equal (ignoring case) to
+       db2 in system character set (UTF8).
+*/
+
+static inline bool
+cmp_db_names(const char *db1_name,
+             const char *db2_name)
+{
+  return
+         /* db1 is NULL and db2 is NULL */
+         !db1_name && !db2_name ||
+
+         /* db1 is not-NULL, db2 is not-NULL, db1 == db2. */
+         db1_name && db2_name &&
+         my_strcasecmp(system_charset_info, db1_name, db2_name) == 0;
+}
+
+
 /**
-  @brief Change the current database and its attributes.
+  @brief Change the current database and its attributes unconditionally.
 
   @param thd          thread handle
   @param new_db_name  database name
@@ -1565,6 +1624,43 @@ bool mysql_change_db(THD *thd, const LEX
   mysql_change_db_impl(thd, &new_db_file_name, db_access, db_default_cl);
 
   DBUG_RETURN(FALSE);
+}
+
+
+/**
+  Change the current database and its attributes if needed.
+
+  @param          thd             thread handle
+  @param          new_db_name     database name
+  @param[in, out] saved_db_name   IN: "str" points to a buffer where to store
+                                  the old database name, "length" contains the
+                                  buffer size
+                                  OUT: if the current (default) database is
+                                  not NULL, its name is copied to the
+                                  buffer pointed at by "str"
+                                  and "length" is updated accordingly.
+                                  Otherwise "str" is set to NULL and
+                                  "length" is set to 0.
+  @param          force_switch    @see mysql_change_db()
+  @param[out]     cur_db_changed  out-flag to indicate whether the current
+                                  database has been changed (valid only if
+                                  the function suceeded)
+*/
+
+bool mysql_opt_change_db(THD *thd,
+                         const LEX_STRING *new_db_name,
+                         LEX_STRING *saved_db_name,
+                         bool force_switch,
+                         bool *cur_db_changed)
+{
+  *cur_db_changed= !cmp_db_names(thd->db, new_db_name->str);
+
+  if (!*cur_db_changed)
+    return FALSE;
+
+  backup_current_db_name(thd, saved_db_name);
+
+  return mysql_change_db(thd, new_db_name, force_switch);
 }
 
 
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-31 20:42:12 +04:00
@@ -2868,6 +2868,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= this->strmake(thd->db, thd->db_length);
+    db_length= thd->db_length;
+  }
+  else
+  {
+    db= NULL;
+    db_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
@@ -2974,6 +2987,13 @@ bool Prepared_statement::execute(String 
   Query_arena *old_stmt_arena;
   bool error= TRUE;
 
+  char saved_cur_db_name_buf[NAME_LEN+1];
+  LEX_STRING saved_cur_db_name=
+    { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
+  bool cur_db_changed;
+
+  LEX_STRING stmt_db_name= { db, db_length };
+
   status_var_increment(thd->status_var.com_stmt_execute);
 
   /* Check if we got an error when sending long data */
@@ -3022,6 +3042,21 @@ bool Prepared_statement::execute(String 
   */
 
   thd->set_n_backup_statement(this, &stmt_backup);
+
+  /*
+    Change the current database (if needed).
+
+    Force switching, because the database of the prepared statement may be
+    NULL (prepared statements can be created while no current database
+    selected).
+  */
+
+  if (mysql_opt_change_db(thd, &stmt_db_name, &saved_cur_db_name, TRUE,
+                          &cur_db_changed))
+    goto error;
+
+  /* Allocate query. */
+
   if (expanded_query->length() &&
       alloc_query(thd, (char*) expanded_query->ptr(),
                   expanded_query->length()+1))
@@ -3050,6 +3085,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 +3104,17 @@ bool Prepared_statement::execute(String 
       query_cache_end_of_result(thd);
     }
   }
+
+  /*
+    Restore the current database (if changed).
+
+    Force switching back to the saved current database (if changed),
+    because it may be NULL. In this case, mysql_change_db() would generate
+    an error.
+  */
+
+  if (cur_db_changed)
+    mysql_change_db(thd, &saved_cur_db_name, TRUE);
 
   thd->protocol= &thd->protocol_text;         /* use normal protocol */
 
Thread
bk commit into 5.1 tree (anozdrin:1.2588) BUG#25843Alexander Nozdrin31 Aug
  • Re: bk commit into 5.1 tree (anozdrin:1.2588) BUG#25843Konstantin Osipov31 Aug