MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:konstantin Date:June 14 2006 11:08pm
Subject:bk commit into 5.0 tree (konstantin:1.2168) BUG#18444
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of kostja. When kostja 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
  1.2168 06/06/15 03:08:25 konstantin@stripped +25 -0
   A fix and a test case for
   Bug#19022 "Memory bug when switching db during trigger execution"
   Bug#17199 "Problem when view calls function from another database."
   Bug#18444 "Fully qualified stored function names don't work correctly in
              SELECT statements"
  
   Documentation note: this patch introduces a change in behaviour of prepared
   statements.
  
   This patch adds a few new invariants with regard to how THD::db should
   be used. These invariants should be preserved in future:
  
    - one should never refer to THD::db by pointer and always make a deep copy
      (strmake, strdup)
    - one should never compare two databases by pointer, but use strncmp or
      my_strncasecmp
    - TABLE_LIST object table->db should be always initialized in the parser or
      by creator of the object.
  
      For prepared statements it means that if the current database is changed
      after a statement is prepared, the database that was current at prepare
      remains active. This also means that you can not prepare a statement that
      implicitly refers to the current database if the latter is not set.
      This is not documented, and therefore needs documentation. This is NOT a
      change in behavior for almost all SQL statements except:
       - ALTER TABLE t1 RENAME t2 
       - OPTIMIZE TABLE t1
       - ANALYZE TABLE t1
       - TRUNCATE TABLE t1 --
       until this patch t1 or t2 could be evaluated at the first execution of
       prepared statement. 
  
       CURRENT_DATABASE() still works OK and is evaluated at every execution
       of prepared statement.
  
       Note, that in stored routines this is not an issue as the default
       database is the database of the stored procedure and "use" statement
       is prohibited in stored routines.
  
    This patch makes obsolete the use of check_db_used (it was never used in the
    old code too) and all other places that check for table->db and assign it
    from THD::db if it's NULL, except the parser.
  
   How this patch was created: THD::{db,db_length} were replaced with a
   LEX_STRING, THD::db. All the places that refer to THD::{db,db_length} were
   manually checked and:
    - if the place uses thd->db by pointer, it was fixed to make a deep copy
    - if a place compared two db pointers, it was fixed to compare them by value
      (via strcmp/my_strcasecmp, whatever was approproate)
   Then this intermediate patch was used to write a smaller patch that does the
   same thing but without a rename.
  
   TODO in 5.1:
     - remove check_db_used
     - deploy THD::set_db in mysql_change_db
  
   See also comments to individual files.

  sql/tztime.cc
    1.32 06/06/15 03:08:21 konstantin@stripped +3 -3
    - be nice, never copy thd->db by pointer.

  sql/sql_yacc.yy
    1.470 06/06/15 03:08:21 konstantin@stripped +37 -5
    - make sure that we always copy table->db or name->db or ident->db or
      select_lex->db from thd->db if the former is not set. If thd->db
      is not set but is accessed, return an error.

  sql/sql_view.cc
    1.89 06/06/15 03:08:21 konstantin@stripped +4 -5
    - replace a check with an assert (view->db)

  sql/sql_udf.cc
    1.57 06/06/15 03:08:21 konstantin@stripped +3 -3
    - use thd->set_db instead of direct modification of to thd->db

  sql/sql_trigger.cc
    1.50 06/06/15 03:08:21 konstantin@stripped +3 -6
    While we are at it, replace direct access to thd->db with a method.
    Should simplify future conversion of THD::db to LEX_STRING.

  sql/sql_table.cc
    1.313 06/06/15 03:08:21 konstantin@stripped +2 -1
    - replace a check with an assert (table_ident->db)

  sql/sql_parse.cc
    1.548 06/06/15 03:08:20 konstantin@stripped +69 -109
    - implement the invariant described in the changeset comment.
    - remove juggling with lex->sphead in SQLCOM_CREATE_PROCEDURE:
      now db_load_routine uses its own LEX object and doesn't damage the main
      LEX.
    - add DBUG_ASSERT(0) to unused "check_db_used"

  sql/sql_lex.h
    1.218 06/06/15 03:08:20 konstantin@stripped +5 -0
    - add a comment

  sql/sql_insert.cc
    1.191 06/06/15 03:08:20 konstantin@stripped +7 -8
    - replace checks with asserts: table_list->db must be always set in the parser.

  sql/sql_db.cc
    1.128 06/06/15 03:08:20 konstantin@stripped +3 -8
    While we are at it, replace direct access to thd->db with a method.
    Should simplify future conversion of THD::db to LEX_STRING.

  sql/sql_class.h
    1.289 06/06/15 03:08:20 konstantin@stripped +38 -1
    - introduce 3 THD  methods to work with THD::db:
      .set_db to assign the current database
      .reset_db to reset the current database (temporarily) or set it to NULL
      .opt_copy_db_to - to deep-copy thd->db to a pointer if it's not NULL

  sql/sp_head.h
    1.85 06/06/15 03:08:20 konstantin@stripped +0 -10
    - remove unneded methods ane members

  sql/sp_head.cc
    1.215 06/06/15 03:08:20 konstantin@stripped +18 -48
    - drop sp_name_current_db_new - a creator of sp_name class that was used
    when sp_name was created for an identifier without an explicitly initialized
    database. Now we pass thd->db to constructor of sp_name right in the 
    parser.
    - rewrite sp_head::init_strings: name->m_db is always set now
    - use the new variant of sp_use_new_db
    - we don't need to update thd->db with SP MEM_ROOT pointer anymore when
    parsing a stored procedure, as noone will refer to it (yes!)

  sql/sp.h
    1.34 06/06/15 03:08:20 konstantin@stripped +7 -7
    Add a new declaration for sp_use_new_db (uses LEX_STRINGs) and a comment.

  sql/sp.cc
    1.113 06/06/15 03:08:20 konstantin@stripped +67 -42
    Rewrite sp_use_new_db: this is a cleanup that I needed in order to understand
    this function and ensure that it has no bugs.

  sql/slave.h
    1.93 06/06/15 03:08:20 konstantin@stripped +0 -4
    Remove a declaration for a method that is used only in one module.

  sql/slave.cc
    1.270 06/06/15 03:08:20 konstantin@stripped +9 -7
    While we are at it, replace direct access to thd->db with a method.
    Should simplify future conversion of THD::db to LEX_STRING.

  sql/log_event.cc
    1.205 06/06/15 03:08:20 konstantin@stripped +11 -9
    While we are at it, replace direct access to thd->db with a method.
    Should simplify future conversion of THD::db to LEX_STRING.

  sql/item_strfunc.cc
    1.267 06/06/15 03:08:20 konstantin@stripped +2 -2
    Touch the code that reads thd->db (cleanup).

  mysql-test/t/sp.test
    1.189 06/06/15 03:08:20 konstantin@stripped +46 -0
    Add a test case for Bug#17199 "Problem when view calls function from another
    database." and Bug#18444 "Fully qualified stored function names don't work
    correctly in SELECT statements". Test a complementary problem.

  mysql-test/t/ps.test
    1.65 06/06/15 03:08:20 konstantin@stripped +118 -0
    Add test coverage for prepared statements and current database. In scope of
    work on Bug#17199 "Problem when view calls function from another database."

  mysql-test/t/create.test
    1.78 06/06/15 03:08:20 konstantin@stripped +1 -1
    Update the id of the returned error.

  mysql-test/r/sp.result
    1.201 06/06/15 03:08:20 konstantin@stripped +46 -0
    Update test results (Bug#17199 et al)

  mysql-test/r/ps.result
    1.67 06/06/15 03:08:20 konstantin@stripped +105 -0
    Update test results (Bug#17199 et al)

  mysql-test/r/create.result
    1.116 06/06/15 03:08:20 konstantin@stripped +1 -1
    Modify the result file: a database can never be NULL.

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	konstantin
# Host:	bodhi.netgear
# Root:	/opt/local/work/mysql-5.0-17199

--- 1.266/sql/item_strfunc.cc	2006-05-12 11:15:11 +04:00
+++ 1.267/sql/item_strfunc.cc	2006-06-15 03:08:20 +04:00
@@ -1640,13 +1640,13 @@
 {
   DBUG_ASSERT(fixed == 1);
   THD *thd= current_thd;
-  if (!thd->db)
+  if (thd->db == NULL)
   {
     null_value= 1;
     return 0;
   }
   else
-    str->copy((const char*) thd->db,(uint) strlen(thd->db),system_charset_info);
+    str->copy(thd->db, thd->db_length, system_charset_info);
   return str;
 }
 

--- 1.204/sql/log_event.cc	2006-03-21 16:35:44 +03:00
+++ 1.205/sql/log_event.cc	2006-06-15 03:08:20 +04:00
@@ -1856,9 +1856,10 @@
     don't suffer from these assignments to 0 as DROP TEMPORARY
     TABLE uses the db.table syntax.
   */
-  thd->db= thd->catalog= 0;	        // prevent db from being freed
+  thd->catalog= 0;
+  thd->reset_db(NULL, 0);               // prevent db from being freed
   thd->query= 0;			// just to be sure
-  thd->query_length= thd->db_length =0;
+  thd->query_length= 0;
   VOID(pthread_mutex_unlock(&LOCK_thread_count));
   close_thread_tables(thd);      
   free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
@@ -2845,7 +2846,7 @@
 
     TABLE_LIST tables;
     bzero((char*) &tables,sizeof(tables));
-    tables.db = thd->db;
+    tables.db= thd->strmake(thd->db, thd->db_length);
     tables.alias = tables.table_name = (char*) table_name;
     tables.lock_type = TL_WRITE;
     tables.updating= 1;
@@ -2940,7 +2941,7 @@
       ex.skip_lines = skip_lines;
       List<Item> field_list;
       thd->main_lex.select_lex.context.resolve_in_table_list_only(&tables);
-      set_fields(thd->db, field_list, &thd->main_lex.select_lex.context);
+      set_fields(tables.db, field_list, &thd->main_lex.select_lex.context);
       thd->variables.pseudo_thread_id= thread_id;
       List<Item> set_fields;
       if (net)
@@ -2987,11 +2988,12 @@
 
 error:
   thd->net.vio = 0; 
-  char *save_db= thd->db;
+  const char *remember_db= thd->db;
   VOID(pthread_mutex_lock(&LOCK_thread_count));
-  thd->db= thd->catalog= 0;
+  thd->catalog= 0;
+  thd->reset_db(NULL, 0);
   thd->query= 0;
-  thd->query_length= thd->db_length= 0;
+  thd->query_length= 0;
   VOID(pthread_mutex_unlock(&LOCK_thread_count));
   close_thread_tables(thd);
   if (thd->query_error)
@@ -3008,7 +3010,7 @@
     }
     slave_print_error(rli,sql_errno,"\
 Error '%s' running LOAD DATA INFILE on table '%s'. Default database: '%s'",
-		      err, (char*)table_name, print_slave_db_safe(save_db));
+		      err, (char*)table_name, print_slave_db_safe(remember_db));
     free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
     return 1;
   }
@@ -3018,7 +3020,7 @@
   {
     slave_print_error(rli,ER_UNKNOWN_ERROR, "\
 Fatal error running LOAD DATA INFILE on table '%s'. Default database: '%s'",
-		      (char*)table_name, print_slave_db_safe(save_db));
+		      (char*)table_name, print_slave_db_safe(remember_db));
     return 1;
   }
 

--- 1.269/sql/slave.cc	2006-05-23 22:16:52 +04:00
+++ 1.270/sql/slave.cc	2006-06-15 03:08:20 +04:00
@@ -1581,9 +1581,8 @@
   // save old db in case we are creating in a different database
   save_db = thd->db;
   save_db_length= thd->db_length;
-  thd->db = (char*)db;
-  DBUG_ASSERT(thd->db != 0);
-  thd->db_length= strlen(thd->db);
+  DBUG_ASSERT(db != 0);
+  thd->reset_db((char*)db, strlen(db));
   mysql_parse(thd, thd->query, packet_len); // run create table
   thd->db = save_db;		// leave things the way the were before
   thd->db_length= save_db_length;
@@ -3704,8 +3703,9 @@
   sql_print_information("Slave I/O thread exiting, read up to log '%s', position %s",
 		  IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff));
   VOID(pthread_mutex_lock(&LOCK_thread_count));
-  thd->query = thd->db = 0; // extra safety
-  thd->query_length= thd->db_length= 0;
+  thd->query= 0; // extra safety
+  thd->query_length= 0;
+  thd->reset_db(NULL, 0);
   VOID(pthread_mutex_unlock(&LOCK_thread_count));
   if (mysql)
   {
@@ -3912,8 +3912,10 @@
     should already have done these assignments (each event which sets these
     variables is supposed to set them to 0 before terminating)).
   */
-  thd->query= thd->db= thd->catalog= 0; 
-  thd->query_length= thd->db_length= 0;
+  thd->catalog= 0; 
+  thd->reset_db(NULL, 0);
+  thd->query= 0; 
+  thd->query_length= 0;
   VOID(pthread_mutex_unlock(&LOCK_thread_count));
   thd->proc_info = "Waiting for slave mutex on exit";
   pthread_mutex_lock(&rli->run_lock);

--- 1.288/sql/sql_class.h	2006-04-13 11:25:52 +04:00
+++ 1.289/sql/sql_class.h	2006-06-15 03:08:20 +04:00
@@ -1570,6 +1570,43 @@
   void restore_sub_statement_state(Sub_statement_state *backup);
   void set_n_backup_active_arena(Query_arena *set, Query_arena *backup);
   void restore_active_arena(Query_arena *set, Query_arena *backup);
+
+  /* Set current database from a NULL-terminated string with length */
+  void set_db(const char *new_db, uint new_db_len)
+  {
+    if (new_db)
+    {
+      /* a shortcut for the case when the name is the same */
+      if (db_length >= new_db_len)
+        memcpy(db, new_db, new_db_len+1);
+      else
+      {
+        safeFree(db);
+        db= my_strdup_with_length(new_db, new_db_len, MYF(MY_WME));
+      }
+      db_length= db ? new_db_len: 0;
+    }
+  }
+  void reset_db(char *new_db, uint new_db_len)
+  {
+    db= new_db;
+    db_length= new_db_len;
+  }
+  bool opt_copy_db_to(char **p_db, uint *p_db_length)
+  {
+    if (*p_db == NULL)
+    {
+      if (db == NULL)
+      {
+        my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
+        return TRUE;
+      }
+      *p_db= stmt_arena->strmake(db, db_length);
+      if (p_db_length)
+        *p_db_length= db_length;
+    }
+    return FALSE;
+  }
 };
 
 
@@ -1915,7 +1952,7 @@
 
 class Table_ident :public Sql_alloc
 {
- public:
+public:
   LEX_STRING db;
   LEX_STRING table;
   SELECT_LEX_UNIT *sel;

--- 1.127/sql/sql_db.cc	2006-03-01 17:43:51 +03:00
+++ 1.128/sql/sql_db.cc	2006-06-15 03:08:20 +04:00
@@ -773,8 +773,7 @@
   {
     if (!(thd->slave_thread)) /* a slave thread will free it itself */
       x_free(thd->db);
-    thd->db= 0;
-    thd->db_length= 0;
+    thd->reset_db(NULL, 0);
   }
 exit2:
   VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
@@ -1186,14 +1185,10 @@
   {
     if (!(thd->slave_thread))
       my_free(dbname, MYF(0));
-    thd->db= NULL;
-    thd->db_length= 0;
+    thd->reset_db(NULL, 0);
   }
   else
-  {
-    thd->db= dbname;				// THD::~THD will free this
-    thd->db_length= db_length;
-  }
+    thd->reset_db(dbname, db_length);          // THD::~THD will free this
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
   if (!no_access_check)
     sctx->db_access= db_access;

--- 1.190/sql/sql_insert.cc	2006-05-26 12:51:16 +04:00
+++ 1.191/sql/sql_insert.cc	2006-06-15 03:08:20 +04:00
@@ -298,9 +298,8 @@
   {
     if (thd->locked_tables)
     {
-      if (find_locked_table(thd,
-			    table_list->db ? table_list->db : thd->db,
-			    table_list->table_name))
+      DBUG_ASSERT(table_list->db); /* Must be set in the parser */
+      if (find_locked_table(thd, table_list->db, table_list->table_name))
       {
 	my_error(ER_DELAYED_INSERT_TABLE_LOCKED, MYF(0),
                  table_list->table_name);
@@ -1332,8 +1331,8 @@
   TABLE *table;
   DBUG_ENTER("delayed_get_table");
 
-  if (!table_list->db)
-    table_list->db=thd->db;
+  /* Must be set in the parser */
+  DBUG_ASSERT(table_list->db);
 
   /* Find the thread which handles this table. */
   if (!(tmp=find_handler(thd,table_list)))
@@ -1372,15 +1371,15 @@
       pthread_mutex_lock(&LOCK_thread_count);
       thread_count++;
       pthread_mutex_unlock(&LOCK_thread_count);
-      if (!(tmp->thd.db=my_strdup(table_list->db,MYF(MY_WME))) ||
-	  !(tmp->thd.query=my_strdup(table_list->table_name,MYF(MY_WME))))
+      tmp->thd.set_db(table_list->db, strlen(table_list->db));
+      tmp->thd.query= my_strdup(table_list->table_name,MYF(MY_WME));
+      if (tmp->thd.db == NULL || tmp->thd.query == NULL)
       {
 	delete tmp;
 	my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
 	goto err1;
       }
       tmp->table_list= *table_list;			// Needed to open table
-      tmp->table_list.db= tmp->thd.db;
       tmp->table_list.alias= tmp->table_list.table_name= tmp->thd.query;
       tmp->lock();
       pthread_mutex_lock(&tmp->mutex);

--- 1.217/sql/sql_lex.h	2006-05-30 09:45:16 +04:00
+++ 1.218/sql/sql_lex.h	2006-06-15 03:08:20 +04:00
@@ -758,6 +758,11 @@
     *this= *state;
   }
 
+  /*
+    Direct addition to the list of query tables.
+    If you are using this function you must ensure that the table
+    object, in particular table->db member, is initialized.
+  */
   void add_to_query_tables(TABLE_LIST *table)
   {
     *(table->prev_global= query_tables_last)= table;

--- 1.547/sql/sql_parse.cc	2006-05-30 16:22:06 +04:00
+++ 1.548/sql/sql_parse.cc	2006-06-15 03:08:20 +04:00
@@ -93,8 +93,6 @@
   "NON-EXISTING", "ACTIVE", "IDLE", "PREPARED"
 };
 
-static char empty_c_string[1]= {0};		// Used for not defined 'db'
-
 #ifdef __WIN__
 static void  test_signal(int sig_ptr)
 {
@@ -300,8 +298,7 @@
       thd->db is saved in caller and needs to be freed by caller if this
       function returns 0
     */
-    thd->db= 0;
-    thd->db_length= 0;
+    thd->reset_db(NULL, 0);
     if (mysql_change_db(thd, db, FALSE))
     {
       /* Send the error to the client */
@@ -341,9 +338,8 @@
     if connect failed. Also in case of 'CHANGE USER' failure, current
     database will be switched to 'no database selected'.
   */
-  thd->db= 0;
-  thd->db_length= 0;
-  
+  thd->reset_db(NULL, 0);
+
   USER_RESOURCES ur;
   int res= acl_getroot(thd, &ur, passwd, passwd_len);
 #ifndef EMBEDDED_LIBRARY
@@ -1316,19 +1312,6 @@
   DBUG_RETURN(0);
 }
 
-    /* This works because items are allocated with sql_alloc() */
-
-void free_items(Item *item)
-{
-  Item *next;
-  DBUG_ENTER("free_items");
-  for (; item ; item=next)
-  {
-    next=item->next;
-    item->delete_self();
-  }
-  DBUG_VOID_RETURN;
-}
 
     /* This works because items are allocated with sql_alloc() */
 
@@ -1340,6 +1323,7 @@
   DBUG_VOID_RETURN;
 }
 
+static
 int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd)
 {
   TABLE* table;
@@ -1801,11 +1785,12 @@
     statistic_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS],
 			&LOCK_status);
     bzero((char*) &table_list,sizeof(table_list));
-    if (!(table_list.db=thd->db))
+    if (thd->db == NULL)
     {
       my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
       break;
     }
+    table_list.db= thd->strmake(thd->db, thd->db_length);
     pend= strend(packet);
     thd->convert_string(&conv_name, system_charset_info,
 			packet, (uint) (pend-packet), thd->charset());
@@ -2152,6 +2137,33 @@
 }
 
 
+/*
+  Create a TABLE_LIST object for an INFORMATION_SCHEMA table.
+
+  SYNOPSIS
+    prepare_schema_table()
+      thd              thread handle
+      lex              current lex
+      table_ident      table alias if it's used
+      schema_table_idx the id of the a
+
+  DESCRIPTION
+    This function is used in the parser to convert a SHOW or DESCRIBE
+    table_name command to a SELECT from INFORMATION_SCHEMA.
+    It prepares a SELECT_LEX and a TABLE_LIST object to represent the
+    given command as a SELECT parse tree.
+
+  NOTES
+    Due to the way this function works with memory and LEX it cannot
+    be used outside the parser (parse tree transformations outside
+    the parser break PS and SP).
+
+  RETURN VALUE
+    0                 success
+    1                 out of memory or SHOW commands are not allowed
+                      in this version of the server.
+*/
+
 int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
                          enum enum_schema_tables schema_table_idx)
 {
@@ -2179,13 +2191,10 @@
     DBUG_RETURN(1);
 #else
     {
-      char *db= lex->select_lex.db ? lex->select_lex.db : thd->db;
-      if (!db)
-      {
-	my_message(ER_NO_DB_ERROR,
-                   ER(ER_NO_DB_ERROR), MYF(0)); /* purecov: inspected */
-        DBUG_RETURN(1);				/* purecov: inspected */
-      }
+      char *db;
+      if (thd->opt_copy_db_to(&lex->select_lex.db, 0))
+        DBUG_RETURN(1);
+      db= lex->select_lex.db;
       remove_escape(db);				// Fix escaped '_'
       if (check_db_name(db))
       {
@@ -2202,11 +2211,6 @@
                  db);
 	DBUG_RETURN(1);
       }
-      /*
-        We need to do a copy to make this prepared statement safe if this
-        was thd->db
-      */
-      lex->select_lex.db= thd->strdup(db);
       break;
     }
 #endif
@@ -2739,8 +2743,8 @@
   case SQLCOM_LOAD_MASTER_TABLE:
   {
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (!first_table->db)
-      first_table->db= thd->db;
+    DBUG_ASSERT(first_table->db); /* Must be set in the parser */
+
     if (check_access(thd, CREATE_ACL, first_table->db,
 		     &first_table->grant.privilege, 0, 0,
                      test(first_table->schema_table)))
@@ -2988,25 +2992,8 @@
 	my_error(ER_WRONG_TABLE_NAME, MYF(0), lex->name);
         goto error;
       }
-      if (!select_lex->db)
-      {
-        /*
-          In the case of ALTER TABLE ... RENAME we should supply the
-          default database if the new name is not explicitly qualified
-          by a database. (Bug #11493)
-        */
-        if (lex->alter_info.flags & ALTER_RENAME)
-        {
-          if (! thd->db)
-          {
-            my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
-            goto error;
-          }
-          select_lex->db= thd->db;
-        }
-        else
-          select_lex->db= first_table->db;
-      }
+      /* Must be set in the parser */
+      DBUG_ASSERT(select_lex->db);
       if (check_access(thd, ALTER_ACL, first_table->db,
 		       &first_table->grant.privilege, 0, 0,
                        test(first_table->schema_table)) ||
@@ -3685,12 +3672,8 @@
   }
   case SQLCOM_ALTER_DB:
   {
-    char *db= lex->name ? lex->name : thd->db;
-    if (!db)
-    {
-      my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
-      break;
-    }
+    char *db= lex->name;
+    DBUG_ASSERT(db); /* Must be set in the parser */
     if (!strip_sp(db) || check_db_name(db))
     {
       my_error(ER_WRONG_DB_NAME, MYF(0), lex->name);
@@ -4139,23 +4122,11 @@
   case SQLCOM_CREATE_SPFUNCTION:
   {
     uint namelen;
-    char *name, *db;
+    char *name;
     int result;
 
     DBUG_ASSERT(lex->sphead != 0);
-
-    if (!lex->sphead->m_db.str || !lex->sphead->m_db.str[0])
-    {
-      if (!thd->db)
-      {
-        my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
-        delete lex->sphead;
-        lex->sphead= 0;
-        goto error;
-      }
-      lex->sphead->m_db.length= strlen(thd->db);
-      lex->sphead->m_db.str= thd->db;
-    }
+    DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */
 
     if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0,
                      is_schema_db(lex->sphead->m_db.str)))
@@ -4272,41 +4243,27 @@
     }
 #endif /* NO_EMBEDDED_ACCESS_CHECKS */
 
-    /*
-      We need to copy name and db in order to use them for
-      check_routine_access which is called after lex->sphead has
-      been deleted.
-    */
-    name= thd->strdup(name); 
-    lex->sphead->m_db.str= db= thd->strmake(lex->sphead->m_db.str,
-                                            lex->sphead->m_db.length);
     res= (result= lex->sphead->create(thd));
     if (result == SP_OK)
     {
-      /*
-        We must cleanup the unit and the lex here because
-        sp_grant_privileges calls (indirectly) db_find_routine,
-        which in turn may call MYSQLparse with THD::lex.
-        TODO: fix db_find_routine to use a temporary lex.
-      */
-      lex->unit.cleanup();
-      delete lex->sphead;
-      lex->sphead= 0;
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
       /* only add privileges if really neccessary */
       if (sp_automatic_privileges && !opt_noacl &&
           check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS,
-      			       db, name,
+      			       lex->sphead->m_db.str, name,
                                lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1))
       {
         close_thread_tables(thd);
-        if (sp_grant_privileges(thd, db, name, 
+        if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
                                 lex->sql_command == SQLCOM_CREATE_PROCEDURE))
           push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 
 	  	       ER_PROC_AUTO_GRANT_FAIL,
 		       ER(ER_PROC_AUTO_GRANT_FAIL));
       }
 #endif
+      lex->unit.cleanup();
+      delete lex->sphead;
+      lex->sphead= 0;
       send_ok(thd);
     }
     else
@@ -4721,7 +4678,8 @@
         view_store_options(thd, first_table, &buff);
         buff.append(STRING_WITH_LEN("VIEW "));
         /* Test if user supplied a db (ie: we did not use thd->db) */
-        if (first_table->db != thd->db && first_table->db[0])
+        if (first_table->db && first_table->db[0] &&
+            (thd->db == NULL || strcmp(first_table->db, thd->db)))
         {
           append_identifier(thd, &buff, first_table->db,
                             first_table->db_length);
@@ -5244,7 +5202,7 @@
         (want_access & ~EXTRA_ACL) &&
 	thd->db)
       tables->grant.privilege= want_access;
-    else if (tables->db && tables->db == thd->db)
+    else if (tables->db && thd->db && strcmp(tables->db, thd->db) == 0)
     {
       if (found && !grant_option)		// db already checked
 	tables->grant.privilege=found_access;
@@ -5392,22 +5350,25 @@
 
 static bool check_db_used(THD *thd,TABLE_LIST *tables)
 {
+  char *current_db= NULL;
   for (; tables; tables= tables->next_global)
   {
-    if (!tables->db)
+    if (tables->db == NULL)
     {
-      if (!(tables->db=thd->db))
-      {
-	my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR),
-                   MYF(0));                     /* purecov: tested */
-	return TRUE;				/* purecov: tested */
-      }
+      /*
+        This code never works and should be removed in 5.1.  All tables
+        that are added to the list of tables should already have its
+        database field initialized properly (see st_lex::add_table_to_list).
+      */
+      DBUG_ASSERT(0);
+      if (thd->opt_copy_db_to(&current_db, 0))
+        return TRUE;
+      tables->db= current_db;
     }
   }
   return FALSE;
 }
 
-
 /****************************************************************************
 	Check stack size; Send error if there isn't enough stack to continue
 ****************************************************************************/
@@ -6029,17 +5990,14 @@
   }
   else if (thd->db)
   {
-    ptr->db= thd->db;
+    ptr->db= thd->strmake(thd->db, thd->db_length);
     ptr->db_length= thd->db_length;
   }
   else
   {
-    /* The following can't be "" as we may do 'casedn_str()' on it */
-    ptr->db= empty_c_string;
-    ptr->db_length= 0;
+    my_error(ER_NO_DB_ERROR, MYF(0));
+    DBUG_RETURN(0);
   }
-  if (thd->stmt_arena->is_stmt_prepare_or_first_sp_execute())
-    ptr->db= thd->strdup(ptr->db);
 
   ptr->alias= alias_str;
   if (lower_case_table_names && table->table.length)
@@ -7216,6 +7174,8 @@
     my_message(ER_WRONG_VALUE_COUNT, ER(ER_WRONG_VALUE_COUNT), MYF(0));
     DBUG_RETURN(TRUE);
   }
+  if (check_db_used(thd, tables))
+    DBUG_RETURN(TRUE);
   DBUG_RETURN(FALSE);
 }
 

--- 1.312/sql/sql_table.cc	2006-05-30 09:45:17 +04:00
+++ 1.313/sql/sql_table.cc	2006-06-15 03:08:21 +04:00
@@ -2672,7 +2672,8 @@
 
   TABLE_LIST src_tables_list;
   DBUG_ENTER("mysql_create_like_table");
-  src_db= table_ident->db.str ? table_ident->db.str : thd->db;
+  DBUG_ASSERT(table_ident->db.str); /* Must be set in the parser */
+  src_db= table_ident->db.str;
 
   /*
     Validate the source table

--- 1.56/sql/sql_udf.cc	2006-03-20 23:34:55 +03:00
+++ 1.57/sql/sql_udf.cc	2006-06-15 03:08:21 +04:00
@@ -140,6 +140,7 @@
   READ_RECORD read_record_info;
   TABLE *table;
   int error;
+  char *const db= "mysql"; /* A subject to casednstr, can't be constant */
   DBUG_ENTER("ufd_init");
 
   if (initialized)
@@ -161,13 +162,12 @@
   initialized = 1;
   new_thd->thread_stack= (char*) &new_thd;
   new_thd->store_globals();
-  new_thd->db= my_strdup("mysql", MYF(0));
-  new_thd->db_length=5;
+  new_thd->set_db(db, strlen(db));
 
   bzero((gptr) &tables,sizeof(tables));
   tables.alias= tables.table_name= (char*) "func";
   tables.lock_type = TL_READ;
-  tables.db=new_thd->db;
+  tables.db= db;
 
   if (simple_open_n_lock_tables(new_thd, &tables))
   {

--- 1.469/sql/sql_yacc.yy	2006-05-15 00:51:02 +04:00
+++ 1.470/sql/sql_yacc.yy	2006-06-15 03:08:21 +04:00
@@ -1237,12 +1237,18 @@
 	  }
 	| ident
 	  {
+            THD *thd= YYTHD;
+            LEX_STRING db= { 0, 0 };
 	    if (check_routine_name($1))
             {
 	      my_error(ER_SP_WRONG_NAME, MYF(0), $1.str);
 	      YYABORT;
 	    }
-	    $$= sp_name_current_db_new(YYTHD, $1);
+            if (thd->opt_copy_db_to(&db.str, &db.length))
+              YYABORT;
+	    $$= new sp_name(db, $1);
+            if ($$)
+	      $$->init_qname(YYTHD);
 	  }
 	;
 
@@ -2405,14 +2411,20 @@
         | LIKE table_ident
           {
             LEX *lex=Lex;
+            THD *thd= lex->thd;
             if (!(lex->name= (char *)$2))
               YYABORT;
+            if (thd->opt_copy_db_to(&($2->db.str), &($2->db.length)))
+              YYABORT;
           }
         | '(' LIKE table_ident ')'
           {
             LEX *lex=Lex;
+            THD *thd= lex->thd;
             if (!(lex->name= (char *)$3))
               YYABORT;
+            if (thd->opt_copy_db_to(&($3->db.str), &($3->db.length)))
+              YYABORT;
           }
         ;
 
@@ -3240,7 +3252,9 @@
 	  lex->key_list.empty();
 	  lex->col_list.empty();
           lex->select_lex.init_order();
-	  lex->select_lex.db=lex->name=0;
+	  lex->select_lex.db=
+            ((TABLE_LIST*) lex->select_lex.table_list.first)->db;
+          lex->name=0;
 	  bzero((char*) &lex->create_info,sizeof(lex->create_info));
 	  lex->create_info.db_type= DB_TYPE_DEFAULT;
 	  lex->create_info.default_table_charset= NULL;
@@ -3258,8 +3272,11 @@
           opt_create_database_options
 	  {
 	    LEX *lex=Lex;
+            THD *thd= Lex->thd;
 	    lex->sql_command=SQLCOM_ALTER_DB;
 	    lex->name= $3;
+            if (thd->opt_copy_db_to(&lex->name, 0))
+              YYABORT;
 	  }
 	| ALTER PROCEDURE sp_name
 	  {
@@ -3421,14 +3438,17 @@
 	| RENAME opt_to table_ident
 	  {
 	    LEX *lex=Lex;
+            THD *thd= lex->thd;
 	    lex->select_lex.db=$3->db.str;
-	    lex->name= $3->table.str;
+            if (thd->opt_copy_db_to(&lex->select_lex.db, 0))
+              YYABORT;
             if (check_table_name($3->table.str,$3->table.length) ||
                 $3->db.str && check_db_name($3->db.str))
             {
               my_error(ER_WRONG_TABLE_NAME, MYF(0), $3->table.str);
               YYABORT;
             }
+	    lex->name= $3->table.str;
 	    lex->alter_info.flags|= ALTER_RENAME;
 	  }
 	| CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate
@@ -4742,7 +4762,13 @@
 #endif /* HAVE_DLOPEN */
             {
 	      LEX *lex= Lex;
-              sp_name *name= sp_name_current_db_new(YYTHD, $1);
+              THD *thd= lex->thd;
+              LEX_STRING db= { 0, 0 };
+              if (thd->opt_copy_db_to(&db.str, &db.length))
+                YYABORT;
+              sp_name *name= new sp_name(db, $1);
+              if (name)
+                name->init_qname(thd);
 
               sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION);
               if ($4)
@@ -8460,7 +8486,13 @@
 	'*'
 	  {
 	    LEX *lex= Lex;
-	    lex->current_select->db= lex->thd->db;
+            THD *thd= lex->thd;
+            if (thd->db == NULL)
+            {
+              my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
+              break;
+            }
+            lex->current_select->db= thd->strmake(thd->db, thd->db_length);
 	    if (lex->grant == GLOBAL_ACLS)
 	      lex->grant = DB_ACLS & ~GRANT_ACL;
 	    else if (lex->columns.elements)

--- 1.88/sql/sql_view.cc	2006-05-04 23:19:28 +04:00
+++ 1.89/sql/sql_view.cc	2006-06-15 03:08:21 +04:00
@@ -452,15 +452,15 @@
   */
   for (sl= select_lex; sl; sl= sl->next_select())
   {
-    char *db= view->db ? view->db : thd->db;
+    DBUG_ASSERT(view->db);                     /* Must be set in the parser */
     List_iterator_fast<Item> it(sl->item_list);
     Item *item;
-    fill_effective_table_privileges(thd, &view->grant, db,
+    fill_effective_table_privileges(thd, &view->grant, view->db,
                                     view->table_name);
     while ((item= it++))
     {
       Item_field *fld;
-      uint priv= (get_column_grant(thd, &view->grant, db,
+      uint priv= (get_column_grant(thd, &view->grant, view->db,
                                     view->table_name, item->name) &
                   VIEW_ANY_ACL);
       if ((fld= item->filed_for_view_update()))
@@ -641,8 +641,7 @@
 
       if (!parser->ok() || !is_equal(&view_type, parser->type()))
       {
-        my_error(ER_WRONG_OBJECT, MYF(0),
-                 (view->db ? view->db : thd->db), view->table_name, "VIEW");
+        my_error(ER_WRONG_OBJECT, MYF(0), view->db, view->table_name, "VIEW");
         DBUG_RETURN(-1);
       }
 

--- 1.31/sql/tztime.cc	2006-04-24 18:56:56 +04:00
+++ 1.32/sql/tztime.cc	2006-06-15 03:08:21 +04:00
@@ -1548,6 +1548,7 @@
   TABLE *table;
   Tz_names_entry *tmp_tzname;
   my_bool return_val= 1;
+  char *const db= "mysql";
   int res;
   DBUG_ENTER("my_tz_init");
 
@@ -1604,13 +1605,12 @@
     leap seconds shared by all time zones.
   */
 
-  thd->db= my_strdup("mysql",MYF(0));
-  thd->db_length= 5;				// Safety
+  thd->set_db(db, strlen(db));
   bzero((char*) &tables_buff, sizeof(TABLE_LIST));
   tables_buff[0].alias= tables_buff[0].table_name=
     (char*)"time_zone_leap_second";
   tables_buff[0].lock_type= TL_READ;
-  tables_buff[0].db= thd->db;
+  tables_buff[0].db= db;
   /*
     Fill TABLE_LIST for the rest of the time zone describing tables
     and link it to first one.

--- 1.49/sql/sql_trigger.cc	2006-03-28 01:01:48 +04:00
+++ 1.50/sql/sql_trigger.cc	2006-06-15 03:08:21 +04:00
@@ -932,8 +932,7 @@
 
       save_db.str= thd->db;
       save_db.length= thd->db_length;
-      thd->db_length= strlen(db);
-      thd->db= (char *) db;
+      thd->reset_db((char*) db, strlen(db));
       while ((trg_create_str= it++))
       {
         trg_sql_mode= itm++;
@@ -1035,8 +1034,7 @@
 
         lex_end(&lex);
       }
-      thd->db= save_db.str;
-      thd->db_length= save_db.length;
+      thd->reset_db(save_db.str, save_db.length);
       thd->lex= old_lex;
       thd->spcont= save_spcont;
       thd->variables.sql_mode= save_sql_mode;
@@ -1049,8 +1047,7 @@
       thd->lex= old_lex;
       thd->spcont= save_spcont;
       thd->variables.sql_mode= save_sql_mode;
-      thd->db= save_db.str;
-      thd->db_length= save_db.length;
+      thd->reset_db(save_db.str, save_db.length);
       DBUG_RETURN(1);
     }
 

--- 1.115/mysql-test/r/create.result	2006-03-30 08:13:19 +04:00
+++ 1.116/mysql-test/r/create.result	2006-06-15 03:08:20 +04:00
@@ -607,7 +607,7 @@
 use mysqltest;
 drop database mysqltest;
 create table test.t1 like x;
-ERROR 42000: Incorrect database name 'NULL'
+ERROR 3D000: No database selected
 drop table if exists test.t1;
 create database mysqltest;
 use mysqltest;

--- 1.77/mysql-test/t/create.test	2006-03-30 08:13:19 +04:00
+++ 1.78/mysql-test/t/create.test	2006-06-15 03:08:20 +04:00
@@ -517,7 +517,7 @@
 create database mysqltest;
 use mysqltest;
 drop database mysqltest;
---error 1102
+--error ER_NO_DB_ERROR 
 create table test.t1 like x;
 --disable_warnings
 drop table if exists test.t1;

--- 1.200/mysql-test/r/sp.result	2006-05-18 14:44:12 +04:00
+++ 1.201/mysql-test/r/sp.result	2006-06-15 03:08:20 +04:00
@@ -4990,4 +4990,50 @@
 DROP FUNCTION bug18037_f1|
 DROP PROCEDURE bug18037_p1|
 DROP PROCEDURE bug18037_p2|
+use test|
+create table t3 (i int)|
+insert into t3 values (1), (2)|
+create database mysqltest1|
+use mysqltest1|
+create function bug17199() returns varchar(2) deterministic return 'ok'|
+use test|
+select *, mysqltest1.bug17199() from t3|
+i	mysqltest1.bug17199()
+1	ok
+2	ok
+use mysqltest1|
+create function bug18444(i int) returns int no sql deterministic return i + 1|
+use test|
+select mysqltest1.bug18444(i) from t3|
+mysqltest1.bug18444(i)
+2
+3
+drop database mysqltest1|
+create database mysqltest1 charset=utf8|
+create database mysqltest2 charset=utf8|
+create procedure mysqltest1.p1()
+begin
+-- alters the default collation of database test 
+alter database character set koi8r;
+end|
+use mysqltest1|
+call p1()|
+show create database mysqltest1|
+Database	Create Database
+mysqltest1	CREATE DATABASE `mysqltest1` /*!40100 DEFAULT CHARACTER SET koi8r */
+show create database mysqltest2|
+Database	Create Database
+mysqltest2	CREATE DATABASE `mysqltest2` /*!40100 DEFAULT CHARACTER SET utf8 */
+alter database test character set utf8; 
+use mysqltest2|
+call mysqltest1.p1()|
+show create database mysqltest1|
+Database	Create Database
+mysqltest1	CREATE DATABASE `mysqltest1` /*!40100 DEFAULT CHARACTER SET koi8r */
+show create database mysqltest2|
+Database	Create Database
+mysqltest2	CREATE DATABASE `mysqltest2` /*!40100 DEFAULT CHARACTER SET utf8 */
+drop database mysqltest1|
+drop database mysqltest2|
+use test|
 drop table t1,t2;

--- 1.188/mysql-test/t/sp.test	2006-05-18 14:44:12 +04:00
+++ 1.189/mysql-test/t/sp.test	2006-06-15 03:08:20 +04:00
@@ -5888,6 +5888,52 @@
 DROP PROCEDURE bug18037_p1|
 DROP PROCEDURE bug18037_p2|
 
+#
+# Bug#17199: "Table not found" error occurs if the query contains a call
+#            to a function from another database.
+#            See also ps.test for an additional test case for this bug.
+#
+use test|
+create table t3 (i int)|
+insert into t3 values (1), (2)|
+create database mysqltest1|
+use mysqltest1|
+create function bug17199() returns varchar(2) deterministic return 'ok'|
+use test|
+select *, mysqltest1.bug17199() from t3|
+#
+# Bug#18444: Fully qualified stored function names don't work correctly
+#            in select statements
+#
+use mysqltest1|
+create function bug18444(i int) returns int no sql deterministic return i + 1|
+use test|
+select mysqltest1.bug18444(i) from t3|
+drop database mysqltest1|
+#
+# Check that current database has no influence to a stored procedure
+#
+create database mysqltest1 charset=utf8|
+create database mysqltest2 charset=utf8|
+create procedure mysqltest1.p1()
+begin
+-- alters the default collation of database test 
+  alter database character set koi8r;
+end|
+use mysqltest1|
+call p1()|
+show create database mysqltest1|
+show create database mysqltest2|
+alter database test character set utf8; 
+use mysqltest2|
+call mysqltest1.p1()|
+show create database mysqltest1|
+show create database mysqltest2|
+drop database mysqltest1|
+drop database mysqltest2|
+#
+# Restore the old environemnt
+use test|
 
 #
 # BUG#NNNN: New bug synopsis

--- 1.112/sql/sp.cc	2006-05-04 16:30:35 +04:00
+++ 1.113/sql/sp.cc	2006-06-15 03:08:20 +04:00
@@ -404,7 +404,8 @@
 {
   LEX *old_lex= thd->lex, newlex;
   String defstr;
-  char olddb[128];
+  char old_db_buf[NAME_LEN+1];
+  LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
   bool dbchanged;
   ulong old_sql_mode= thd->variables.sql_mode;
   ha_rows old_select_limit= thd->variables.select_limit;
@@ -450,9 +451,7 @@
     goto end;
   }
 
-  dbchanged= FALSE;
-  if ((ret= sp_use_new_db(thd, name->m_db.str, olddb, sizeof(olddb),
-			  1, &dbchanged)))
+  if ((ret= sp_use_new_db(thd, name->m_db, &old_db, 1, &dbchanged)))
     goto end;
 
   lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length());
@@ -462,14 +461,14 @@
   {
     sp_head *sp= newlex.sphead;
 
-    if (dbchanged && (ret= mysql_change_db(thd, olddb, 1)))
+    if (dbchanged && (ret= mysql_change_db(thd, old_db.str, 1)))
       goto end;
     delete sp;
     ret= SP_PARSE_ERROR;
   }
   else
   {
-    if (dbchanged && (ret= mysql_change_db(thd, olddb, 1)))
+    if (dbchanged && (ret= mysql_change_db(thd, old_db.str, 1)))
       goto end;
     *sphp= newlex.sphead;
     (*sphp)->set_definer(&definer_user_name, &definer_host_name);
@@ -505,15 +504,14 @@
   int ret;
   TABLE *table;
   char definer[USER_HOST_BUFF_SIZE];
-  char olddb[128];
+  char old_db_buf[NAME_LEN+1];
+  LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
   bool dbchanged;
   DBUG_ENTER("db_create_routine");
   DBUG_PRINT("enter", ("type: %d name: %.*s",type,sp->m_name.length,
                        sp->m_name.str));
 
-  dbchanged= FALSE;
-  if ((ret= sp_use_new_db(thd, sp->m_db.str, olddb, sizeof(olddb),
-			  0, &dbchanged)))
+  if ((ret= sp_use_new_db(thd, sp->m_db, &old_db, 0, &dbchanged)))
   {
     ret= SP_NO_DB_ERROR;
     goto done;
@@ -641,7 +639,7 @@
 done:
   close_thread_tables(thd);
   if (dbchanged)
-    (void)mysql_change_db(thd, olddb, 1);
+    (void) mysql_change_db(thd, old_db.str, 1);
   DBUG_RETURN(ret);
 }
 
@@ -1814,49 +1812,76 @@
 }
 
 
-//
-// Utilities...
-//
+
+/*
+  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 NULL 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)
+*/
 
 int
-sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddblen,
+sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db,
 	      bool no_access_check, bool *dbchangedp)
 {
-  bool changeit;
+  int ret;
+  static char empty_c_string[1]= {0};          /* used for not defined db */
   DBUG_ENTER("sp_use_new_db");
-  DBUG_PRINT("enter", ("newdb: %s", newdb));
+  DBUG_PRINT("enter", ("newdb: %s", new_db.str));
 
-  if (! newdb)
-    newdb= (char *)"";
-  if (thd->db && thd->db[0])
-  {
-    if (my_strcasecmp(system_charset_info, thd->db, newdb) == 0)
-      changeit= 0;
-    else
-    {
-      changeit= 1;
-      strnmov(olddb, thd->db, olddblen);
-    }
+  /*
+    Set new_db to an empty string if it's NULL because mysql_change_db
+    requires a non-NULL argument.
+    new_db.str can be NULL only if we're restoring the old database after
+    execution of a stored procedure, and there were no current database
+    selected. The stored procedure itself must always have its database
+    initialized.
+  */
+  if (new_db.str == NULL)
+    new_db.str= empty_c_string;
+
+  if (thd->db)
+  {
+    old_db->length= (strmake(old_db->str, thd->db, old_db->length) -
+                     old_db->str);
   }
   else
-  {				// thd->db empty
-    if (newdb[0])
-      changeit= 1;
-    else
-      changeit= 0;
-    olddb[0] = '\0';
+  {
+    old_db->str[0]= '\0';
+    old_db->length= 0;
   }
-  if (!changeit)
+
+  /* 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);
   }
-  else
-  {
-    int ret= mysql_change_db(thd, newdb, no_access_check);
 
-    if (! ret)
-      *dbchangedp= TRUE;
-    DBUG_RETURN(ret);
-  }
+  ret= mysql_change_db(thd, new_db.str, no_access_check);
+
+  *dbchangedp= ret == 0;
+  DBUG_RETURN(ret);
 }
+

--- 1.33/sql/sp.h	2006-01-26 15:29:42 +03:00
+++ 1.34/sql/sp.h	2006-06-15 03:08:20 +04:00
@@ -104,15 +104,15 @@
 TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup);
 void close_proc_table(THD *thd, Open_tables_state *backup);
 
-//
-// Utilities...
-//
 
-// Do a "use newdb". The current db is stored at olddb.
-// If newdb is the same as the current one, nothing is changed.
-// dbchangedp is set to true if the db was actually changed.
+/*
+  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, char *newdb, char *olddb, uint olddbmax,
+sp_use_new_db(THD *thd, LEX_STRING new_db, LEX_STRING *old_db,
 	      bool no_access_check, bool *dbchangedp);
 
 #endif /* _SP_H_ */

--- 1.214/sql/sp_head.cc	2006-05-23 23:00:59 +04:00
+++ 1.215/sql/sp_head.cc	2006-06-15 03:08:20 +04:00
@@ -376,24 +376,6 @@
 	  m_name.length, m_name.str);
 }
 
-sp_name *
-sp_name_current_db_new(THD *thd, LEX_STRING name)
-{
-  sp_name *qname;
-
-  if (! thd->db)
-    qname= new sp_name(name);
-  else
-  {
-    LEX_STRING db;
-
-    db.length= strlen(thd->db);
-    db.str= thd->strmake(thd->db, db.length);
-    qname= new sp_name(db, name);
-  }
-  qname->init_qname(thd);
-  return qname;
-}
 
 /*
   Check that the name 'ident' is ok. It's assumed to be an 'ident'
@@ -504,27 +486,20 @@
   /* During parsing, we must use thd->mem_root */
   MEM_ROOT *root= thd->mem_root;
 
+  DBUG_ASSERT(name);
+  /* Must be initialized in the parser */
+  DBUG_ASSERT(name->m_db.str && name->m_db.length);
+
   /* We have to copy strings to get them into the right memroot */
-  if (name)
-  {
-    m_db.length= name->m_db.length;
-    if (name->m_db.length == 0)
-      m_db.str= NULL;
-    else
-      m_db.str= strmake_root(root, name->m_db.str, name->m_db.length);
-    m_name.length= name->m_name.length;
-    m_name.str= strmake_root(root, name->m_name.str, name->m_name.length);
-
-    if (name->m_qname.length == 0)
-      name->init_qname(thd);
-    m_qname.length= name->m_qname.length;
-    m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length);
-  }
-  else if (thd->db)
-  {
-    m_db.length= thd->db_length;
-    m_db.str= strmake_root(root, thd->db, m_db.length);
-  }
+  m_db.length= name->m_db.length;
+  m_db.str= strmake_root(root, name->m_db.str, name->m_db.length);
+  m_name.length= name->m_name.length;
+  m_name.str= strmake_root(root, name->m_name.str, name->m_name.length);
+
+  if (name->m_qname.length == 0)
+    name->init_qname(thd);
+  m_qname.length= name->m_qname.length;
+  m_qname.str= strmake_root(root, name->m_qname.str, m_qname.length);
 
   if (m_param_begin && m_param_end)
   {
@@ -933,7 +908,8 @@
 sp_head::execute(THD *thd)
 {
   DBUG_ENTER("sp_head::execute");
-  char olddb[128];
+  char old_db_buf[NAME_LEN+1];
+  LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) };
   bool dbchanged;
   sp_rcontext *ctx;
   bool err_status= FALSE;
@@ -980,10 +956,8 @@
                m_first_instance->m_last_cached_sp == this) ||
               (m_recursion_level + 1 == m_next_cached_sp->m_recursion_level));
 
-  dbchanged= FALSE;
   if (m_db.length &&
-      (err_status= sp_use_new_db(thd, m_db.str, olddb, sizeof(olddb), 0,
-                                 &dbchanged)))
+      (err_status= sp_use_new_db(thd, m_db, &old_db, 0, &dbchanged)))
     goto done;
 
   if ((ctx= thd->spcont))
@@ -1155,10 +1129,10 @@
   {
     /*
       No access check when changing back to where we came from.
-      (It would generate an error from mysql_change_db() when olddb=="")
+      (It would generate an error from mysql_change_db() when old_db=="")
     */
     if (! thd->killed)
-      err_status|= mysql_change_db(thd, olddb, 1);
+      err_status|= mysql_change_db(thd, old_db.str, 1);
   }
   m_flags&= ~IS_INVOKED;
   DBUG_PRINT("info",
@@ -1816,9 +1790,6 @@
                       (ulong) &mem_root, (ulong) &thd->mem_root));
   free_list= thd->free_list; // Keep the old list
   thd->free_list= NULL;	// Start a new one
-  /* Copy the db, since substatements will point to it */
-  m_thd_db= thd->db;
-  thd->db= thd->strmake(thd->db, thd->db_length);
   m_thd= thd;
   DBUG_VOID_RETURN;
 }
@@ -1834,7 +1805,6 @@
   DBUG_PRINT("info", ("mem_root 0x%lx returned from thd mem root 0x%lx",
                       (ulong) &mem_root, (ulong) &thd->mem_root));
   thd->free_list= flist;	// Restore the old one
-  thd->db= m_thd_db;		// Restore the original db pointer
   thd->mem_root= m_thd_root;
   m_thd= NULL;
   DBUG_VOID_RETURN;

--- 1.84/sql/sp_head.h	2006-05-15 14:01:52 +04:00
+++ 1.85/sql/sp_head.h	2006-06-15 03:08:20 +04:00
@@ -61,13 +61,6 @@
   */
   LEX_STRING m_sroutines_key;
 
-  sp_name(LEX_STRING name)
-    : m_name(name)
-  {
-    m_db.str= m_qname.str= m_sroutines_key.str= 0;
-    m_db.length= m_qname.length= m_sroutines_key.length= 0;
-  }
-
   sp_name(LEX_STRING db, LEX_STRING name)
     : m_db(db), m_name(name)
   {
@@ -101,8 +94,6 @@
   {}
 };
 
-sp_name *
-sp_name_current_db_new(THD *thd, LEX_STRING name);
 
 bool
 check_routine_name(LEX_STRING name);
@@ -355,7 +346,6 @@
 
   MEM_ROOT *m_thd_root;		// Temp. store for thd's mem_root
   THD *m_thd;			// Set if we have reset mem_root
-  char *m_thd_db;		// Original thd->db pointer
 
   sp_pcontext *m_pcont;		// Parse context
   List<LEX> m_lex;		// Temp. store for the other lex

--- 1.92/sql/slave.h	2006-01-03 19:54:36 +03:00
+++ 1.93/sql/slave.h	2006-06-15 03:08:20 +04:00
@@ -526,10 +526,6 @@
 		       MASTER_INFO* mi,
                        bool high_priority);
 
-/* If fd is -1, dump to NET */
-int mysql_table_dump(THD* thd, const char* db,
-		     const char* tbl_name, int fd = -1);
-
 /* retrieve table from master and copy to slave*/
 int fetch_master_table(THD* thd, const char* db_name, const char* table_name,
 		       MASTER_INFO* mi, MYSQL* mysql, bool overwrite);

--- 1.66/mysql-test/r/ps.result	2006-05-29 18:27:39 +04:00
+++ 1.67/mysql-test/r/ps.result	2006-06-15 03:08:20 +04:00
@@ -1158,3 +1158,108 @@
 Error	1146	Table 'test.t4' doesn't exist
 deallocate prepare stmt;
 drop table t1, t2, t3;
+create database mysqltest_long_database_name_to_thrash_heap;
+use test;
+create table t1 (i int);
+prepare stmt from "alter table test.t1 rename t1";
+use mysqltest_long_database_name_to_thrash_heap;
+execute stmt;
+show tables like 't1';
+Tables_in_mysqltest_long_database_name_to_thrash_heap (t1)
+prepare stmt from "alter table test.t1 rename t1";
+use test;
+execute stmt;
+show tables like 't1';
+Tables_in_test (t1)
+use mysqltest_long_database_name_to_thrash_heap;
+show tables like 't1';
+Tables_in_mysqltest_long_database_name_to_thrash_heap (t1)
+t1
+deallocate prepare stmt;
+use mysqltest_long_database_name_to_thrash_heap;
+prepare stmt_create from "create table t1 (i int)";
+prepare stmt_insert from "insert into t1 (i) values (1)";
+prepare stmt_update from "update t1 set i=2";
+prepare stmt_delete from "delete from t1 where i=2";
+prepare stmt_select from "select * from t1";
+prepare stmt_alter from "alter table t1 add column (b int)";
+prepare stmt_alter1 from "alter table t1 drop column b";
+prepare stmt_analyze from "analyze table t1";
+prepare stmt_optimize from "optimize table t1";
+prepare stmt_show from "show tables like 't1'";
+prepare stmt_truncate from "truncate table t1";
+prepare stmt_drop from "drop table t1";
+drop table t1;
+use test;
+execute stmt_create;
+show tables like 't1';
+Tables_in_test (t1)
+use mysqltest_long_database_name_to_thrash_heap;
+show tables like 't1';
+Tables_in_mysqltest_long_database_name_to_thrash_heap (t1)
+t1
+use test;
+execute stmt_insert;
+select * from mysqltest_long_database_name_to_thrash_heap.t1;
+i
+1
+execute stmt_update;
+select * from mysqltest_long_database_name_to_thrash_heap.t1;
+i
+2
+execute stmt_delete;
+execute stmt_select;
+i
+execute stmt_alter;
+show columns from mysqltest_long_database_name_to_thrash_heap.t1;
+Field	Type	Null	Key	Default	Extra
+i	int(11)	YES		NULL	
+b	int(11)	YES		NULL	
+execute stmt_alter1;
+show columns from mysqltest_long_database_name_to_thrash_heap.t1;
+Field	Type	Null	Key	Default	Extra
+i	int(11)	YES		NULL	
+execute stmt_analyze;
+Table	Op	Msg_type	Msg_text
+mysqltest_long_database_name_to_thrash_heap.t1	analyze	status	Table is already up to date
+execute stmt_optimize;
+Table	Op	Msg_type	Msg_text
+mysqltest_long_database_name_to_thrash_heap.t1	optimize	status	Table is already up to date
+execute stmt_show;
+Tables_in_mysqltest_long_database_name_to_thrash_heap (t1)
+t1
+execute stmt_truncate;
+execute stmt_drop;
+show tables like 't1';
+Tables_in_test (t1)
+use mysqltest_long_database_name_to_thrash_heap;
+show tables like 't1';
+Tables_in_mysqltest_long_database_name_to_thrash_heap (t1)
+drop database mysqltest_long_database_name_to_thrash_heap;
+prepare stmt_create from "create table t1 (i int)";
+ERROR 3D000: No database selected
+prepare stmt_insert from "insert into t1 (i) values (1)";
+ERROR 3D000: No database selected
+prepare stmt_update from "update t1 set i=2";
+ERROR 3D000: No database selected
+prepare stmt_delete from "delete from t1 where i=2";
+ERROR 3D000: No database selected
+prepare stmt_select from "select * from t1";
+ERROR 3D000: No database selected
+prepare stmt_alter from "alter table t1 add column (b int)";
+ERROR 3D000: No database selected
+prepare stmt_alter1 from "alter table t1 drop column b";
+ERROR 3D000: No database selected
+prepare stmt_analyze from "analyze table t1";
+ERROR 3D000: No database selected
+prepare stmt_optimize from "optimize table t1";
+ERROR 3D000: No database selected
+prepare stmt_show from "show tables like 't1'";
+ERROR 3D000: No database selected
+prepare stmt_truncate from "truncate table t1";
+ERROR 3D000: No database selected
+prepare stmt_drop from "drop table t1";
+ERROR 3D000: No database selected
+create temporary table t1 (i int);
+ERROR 3D000: No database selected
+use test;

--- 1.64/mysql-test/t/ps.test	2006-05-29 18:27:39 +04:00
+++ 1.65/mysql-test/t/ps.test	2006-06-15 03:08:20 +04:00
@@ -1146,4 +1146,122 @@
 execute stmt;
 deallocate prepare stmt;
 drop table t1, t2, t3;
+
+#
+# Bug#17199 "Table not found" error occurs if the query contains a call
+#            to a function from another database.
+#            Test prepared statements- related behaviour.
+#
+#
+# ALTER TABLE RENAME and Prepared Statements: wrong DB name buffer was used
+# in ALTER ... RENAME which caused memory corruption in prepared statements.
+# No need to fix this problem in 4.1 as ALTER TABLE is not allowed in
+# Prepared Statements in 4.1.
+#
+create database mysqltest_long_database_name_to_thrash_heap;
+use test;
+create table t1 (i int);
+prepare stmt from "alter table test.t1 rename t1";
+use mysqltest_long_database_name_to_thrash_heap;
+execute stmt;
+show tables like 't1';
+prepare stmt from "alter table test.t1 rename t1";
+use test;
+execute stmt;
+show tables like 't1';
+use mysqltest_long_database_name_to_thrash_heap;
+show tables like 't1';
+deallocate prepare stmt;
+#
+# Check that a prepared statement initializes its current database at
+# PREPARE, and then works correctly even if the current database has been
+# changed.
+# 
+use mysqltest_long_database_name_to_thrash_heap; 
+# Necessary for preparation of INSERT/UPDATE/DELETE to succeed
+prepare stmt_create from "create table t1 (i int)";
+prepare stmt_insert from "insert into t1 (i) values (1)";
+prepare stmt_update from "update t1 set i=2";
+prepare stmt_delete from "delete from t1 where i=2";
+prepare stmt_select from "select * from t1";
+prepare stmt_alter from "alter table t1 add column (b int)";
+prepare stmt_alter1 from "alter table t1 drop column b";
+prepare stmt_analyze from "analyze table t1";
+prepare stmt_optimize from "optimize table t1";
+prepare stmt_show from "show tables like 't1'";
+prepare stmt_truncate from "truncate table t1";
+prepare stmt_drop from "drop table t1";
+# Drop the table that was used to prepare INSERT/UPDATE/DELETE: we will
+# create a new one by executing stmt_create
+drop table t1;
+# Switch the current database
+use test;
+# Check that all prepared statements operate on the database that was
+# active at PREPARE
+execute stmt_create;
+# should return empty set
+show tables like 't1';
+use mysqltest_long_database_name_to_thrash_heap;
+show tables like 't1';
+use test;
+execute stmt_insert;
+select * from mysqltest_long_database_name_to_thrash_heap.t1;
+execute stmt_update;
+select * from mysqltest_long_database_name_to_thrash_heap.t1;
+execute stmt_delete;
+execute stmt_select;
+execute stmt_alter;
+show columns from mysqltest_long_database_name_to_thrash_heap.t1;
+execute stmt_alter1;
+show columns from mysqltest_long_database_name_to_thrash_heap.t1;
+execute stmt_analyze;
+execute stmt_optimize;
+execute stmt_show;
+execute stmt_truncate;
+execute stmt_drop;
+show tables like 't1';
+use mysqltest_long_database_name_to_thrash_heap;
+show tables like 't1';
+#
+# Attempt a statement PREPARE when there is no current database:
+# is expected to return an error.
+#
+drop database mysqltest_long_database_name_to_thrash_heap;
+--error ER_NO_DB_ERROR
+prepare stmt_create from "create table t1 (i int)";
+--error ER_NO_DB_ERROR
+prepare stmt_insert from "insert into t1 (i) values (1)";
+--error ER_NO_DB_ERROR
+prepare stmt_update from "update t1 set i=2";
+--error ER_NO_DB_ERROR
+prepare stmt_delete from "delete from t1 where i=2";
+--error ER_NO_DB_ERROR
+prepare stmt_select from "select * from t1";
+--error ER_NO_DB_ERROR
+prepare stmt_alter from "alter table t1 add column (b int)";
+--error ER_NO_DB_ERROR
+prepare stmt_alter1 from "alter table t1 drop column b";
+--error ER_NO_DB_ERROR
+prepare stmt_analyze from "analyze table t1";
+--error ER_NO_DB_ERROR
+prepare stmt_optimize from "optimize table t1";
+--error ER_NO_DB_ERROR
+prepare stmt_show from "show tables like 't1'";
+--error ER_NO_DB_ERROR
+prepare stmt_truncate from "truncate table t1";
+--error ER_NO_DB_ERROR
+prepare stmt_drop from "drop table t1";
+#
+# The above has automatically deallocated all our statements.
+#
+# Attempt to CREATE a temporary table when no DB used: it should fail
+# This proves that no table can be used without explicit specification of
+# its database if there is no current database. 
+#
+--error ER_NO_DB_ERROR
+create temporary table t1 (i int);
+#
+# Restore the old environemnt
+#
+use test;
 # End of 5.0 tests
Thread
bk commit into 5.0 tree (konstantin:1.2168) BUG#18444konstantin15 Jun