List:Commits« Previous MessageNext Message »
From:konstantin Date:December 8 2006 12:20am
Subject:bk commit into 4.1 tree (kostja:1.2561) BUG#4968
View as plain text  
Below is the list of changes that have just been committed into a local
4.1 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@stripped, 2006-12-08 02:20:09+03:00, kostja@stripped +12 -0
  A fix and test cases for
  Bug#4968 "Stored procedure crash if cursor opened on altered table"
  Bug#19733 "Repeated alter, or repeated create/drop, fails"
  Bug#19182 "CREATE TABLE bar (m INT) SELECT n FROM foo; doesn't work from 
  stored procedure."
  Bug#6895 "Prepared Statements: ALTER TABLE DROP COLUMN does nothing"
  Bug#22060 "ALTER TABLE x AUTO_INCREMENT=y in SP crashes server"
  
  Test cases for bugs 4968, 19733, 6895 will be added in 5.0.
  
  Re-execution of CREATE DATABASE, CREATE TABLE and ALTER TABLE 
  statements in stored routines or as prepared statements caused
  incorrect results (and crashes in versions prior to 5.0.25).
  In 5.1 the problem occured only for CREATE DATABASE, CREATE TABLE
  SELECT and CREATE TABLE with INDEX/DATA DIRECTOY options).
  
  The problem of bugs 4968, 19733, 19282 and 6895 was that functions
  mysql_prepare_table, mysql_create_table and mysql_alter_table were not
  re-execution friendly: during their operation they used to modify contents
  of LEX (members create_info, alter_info, key_list, create_list),
  thus making the LEX unusable for the next execution.
  In particular, these functions removed processed columns and keys from
  create_list, key_list and drop_list. Search the code in sql_table.cc 
  for drop_it.remove() and similar patterns to find evidence.
  
  The fix is to supply to these functions a usable copy of each of the
  above structures at every re-execution of an SQL statement. 
  
  To simplify memory management, LEX::key_list and LEX::create_list
  were added to LEX::alter_info, a fresh copy of which is created for
  every execution.
  
  The problem of crashing bug 22060 stemmed from the fact that the above 
  metnioned functions were not only modifying HA_CREATE_INFO structure in 
  LEX, but also were changing it to point to areas in volatile memory of 
  the execution memory root.
   
  The patch solves this problem by creating and using an on-stack
  copy of HA_CREATE_INFO (note that code in 5.1 already creates and
  uses a copy of this structure in mysql_create_table()/alter_table(),
  but this approach didn't work well for CREATE TABLE SELECT statement).

  mysql-test/r/ps.result@stripped, 2006-12-08 02:20:05+03:00, kostja@stripped +67 -0
    Update test results (Bug#19182, Bug#22060)

  mysql-test/t/ps.test@stripped, 2006-12-08 02:20:05+03:00, kostja@stripped +72 -0
    Add a test case for Bug#19182, Bug#22060 (4.1-only parts)

  sql/mysql_priv.h@stripped, 2006-12-08 02:20:05+03:00, kostja@stripped +7 -12
    LEX::key_list and LEX::create_list were moved to LEX::alter_info.
    Update declarations to use LEX::alter_info instead of these two
    members.

  sql/sql_class.h@stripped, 2006-12-08 02:20:05+03:00, kostja@stripped +10 -9
    Replace pair<columns, keys> with an instance of Alter_info in
    select_create constructor. We create a new copy of Alter_info
    each time we re-execute SELECT .. CREATE prepared statement.

  sql/sql_insert.cc@stripped, 2006-12-08 02:20:05+03:00, kostja@stripped +1 -1
    Adjust to a new signature of create_table_from_items.

  sql/sql_lex.cc@stripped, 2006-12-08 02:20:05+03:00, kostja@stripped +12 -0
    Implement Alter_info::Alter_info that would make a "deep" copy
    of all definition lists (keys, columns).

  sql/sql_lex.h@stripped, 2006-12-08 02:20:05+03:00, kostja@stripped +48 -8
    Move key_list and create_list to class Alter_info. Implement
    Alter_info::Alter_info that can be used with PS and SP.

  sql/sql_list.h@stripped, 2006-12-08 02:20:05+03:00, kostja@stripped +62 -12
    Implement a copy constructor of class List that makes a deep copy
    of all list nodes.

  sql/sql_parse.cc@stripped, 2006-12-08 02:20:05+03:00, kostja@stripped +108 -98
    Adjust to new signatures of mysql_create_table, mysql_alter_table,
    select_create. Functions mysql_create_index and mysql_drop_index has
    become identical after initialization of alter_info was moved to the 
    parser, and were merged. Flag enable_slow_log was not updated for 
    SQLCOM_DROP_INDEX, which is a bug. Just like CREATE INDEX, DROP INDEX
    is currently done via complete table rebuild and is rightfully a slow
    administrative statement.

  sql/sql_show.cc@stripped, 2006-12-08 02:20:06+03:00, kostja@stripped +1 -1
    Adjust mysqld_show_create_db to a new signature.

  sql/sql_table.cc@stripped, 2006-12-08 02:20:06+03:00, kostja@stripped +96 -277
    Adjust mysql_alter_table, mysql_recreate_table, mysql_create_table,
    mysql_prepare_table to new signatures.

  sql/sql_yacc.yy@stripped, 2006-12-08 02:20:06+03:00, kostja@stripped +27 -23
    LEX::key_list and LEX::create_list moved to class Alter_info

# 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:	kostja
# Host:	bodhi.local
# Root:	/opt/local/work/mysql-4.1-4968

--- 1.384/sql/mysql_priv.h	2006-12-08 02:20:16 +03:00
+++ 1.385/sql/mysql_priv.h	2006-12-08 02:20:16 +03:00
@@ -563,25 +563,22 @@ int mysql_prepare_table(THD *thd, HA_CRE
 		       uint &key_count, int select_field_count);
 int mysql_create_table(THD *thd,const char *db, const char *table_name,
 		       HA_CREATE_INFO *create_info,
-		       List<create_field> &fields, List<Key> &keys,
+                       Alter_info *alter_info,
 		       bool tmp_table, uint select_field_count);
 
 TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
 			       const char *db, const char *name,
-			       List<create_field> *extra_fields,
-			       List<Key> *keys,
+                               Alter_info *alter_info,
 			       List<Item> *items,
 			       MYSQL_LOCK **lock);
 int mysql_alter_table(THD *thd, char *new_db, char *new_name,
 		      HA_CREATE_INFO *create_info,
 		      TABLE_LIST *table_list,
-		      List<create_field> &fields,
-		      List<Key> &keys,
+                      Alter_info *alter_info,
 		      uint order_num, ORDER *order,
 		      enum enum_duplicates handle_duplicates,
-                      bool ignore,
-		      ALTER_INFO *alter_info, bool do_send_ok=1);
-int mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool do_send_ok);
+                      bool ignore);
+int mysql_recreate_table(THD *thd, TABLE_LIST *table_list);
 int mysql_create_like_table(THD *thd, TABLE_LIST *table,
                             HA_CREATE_INFO *create_info,
                             Table_ident *src_table);
@@ -590,9 +587,6 @@ bool mysql_rename_table(enum db_type bas
 			const char * old_name,
 			const char *new_db,
 			const char * new_name);
-int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys);
-int mysql_drop_index(THD *thd, TABLE_LIST *table_list,
-		     ALTER_INFO *alter_info);
 int mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
 			 TABLE_LIST *update_table_list,
 			 Item **conds, uint order_num, ORDER *order);
@@ -679,7 +673,8 @@ int get_quote_char_for_identifier(THD *t
 void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild);
 int mysqld_dump_create_info(THD *thd, TABLE *table, int fd = -1);
 int mysqld_show_create(THD *thd, TABLE_LIST *table_list);
-int mysqld_show_create_db(THD *thd, char *dbname, HA_CREATE_INFO *create);
+int mysqld_show_create_db(THD *thd, char *dbname,
+                          const HA_CREATE_INFO *create);
 
 void mysqld_list_processes(THD *thd,const char *user,bool verbose);
 int mysqld_show_status(THD *thd);

--- 1.291/sql/sql_class.h	2006-12-08 02:20:16 +03:00
+++ 1.292/sql/sql_class.h	2006-12-08 02:20:16 +03:00
@@ -1293,20 +1293,21 @@ class select_create: public select_inser
   ORDER *group;
   const char *db;
   const char *name;
-  List<create_field> *extra_fields;
-  List<Key> *keys;
   HA_CREATE_INFO *create_info;
+  Alter_info *alter_info;
   MYSQL_LOCK *lock;
   Field **field;
 public:
   select_create(const char *db_name, const char *table_name,
-		HA_CREATE_INFO *create_info_par,
-		List<create_field> &fields_par,
-		List<Key> &keys_par,
-		List<Item> &select_fields,enum_duplicates duplic, bool ignore)
-    :select_insert (NULL, &select_fields, duplic, ignore), db(db_name),
-    name(table_name), extra_fields(&fields_par),keys(&keys_par),
-    create_info(create_info_par), lock(0)
+                HA_CREATE_INFO *create_info_arg,
+                Alter_info *alter_info_arg,
+                List<Item> &select_fields,
+                enum_duplicates duplic, bool ignore)
+    :select_insert(NULL, &select_fields, duplic, ignore),
+    db(db_name), name(table_name),
+    create_info(create_info_arg),
+    alter_info(alter_info_arg),
+    lock(0)
     {}
   int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
   void store_values(List<Item> &values);

--- 1.173/sql/sql_insert.cc	2006-12-08 02:20:16 +03:00
+++ 1.174/sql/sql_insert.cc	2006-12-08 02:20:16 +03:00
@@ -1808,7 +1808,7 @@ select_create::prepare(List<Item> &value
 
   unit= u;
   table= create_table_from_items(thd, create_info, db, name,
-				 extra_fields, keys, &values, &lock);
+                                 alter_info, &values, &lock);
   if (!table)
     DBUG_RETURN(-1);				// abort() deletes table
 

--- 1.155/sql/sql_lex.cc	2006-12-08 02:20:16 +03:00
+++ 1.156/sql/sql_lex.cc	2006-12-08 02:20:16 +03:00
@@ -1015,6 +1015,18 @@ int yylex(void *arg, void *yythd)
   }
 }
 
+
+Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root)
+  :drop_list(rhs.drop_list, mem_root),
+  alter_list(rhs.alter_list, mem_root),
+  key_list(rhs.key_list, mem_root),
+  create_list(rhs.create_list, mem_root),
+  flags(rhs.flags),
+  keys_onoff(rhs.keys_onoff),
+  tablespace_op(rhs.tablespace_op),
+  is_simple(rhs.is_simple)
+{}
+
 /*
   st_select_lex structures initialisations
 */

--- 1.190/sql/sql_lex.h	2006-12-08 02:20:16 +03:00
+++ 1.191/sql/sql_lex.h	2006-12-08 02:20:16 +03:00
@@ -571,19 +571,61 @@ typedef class st_select_lex SELECT_LEX;
 #define ALTER_ORDER		64
 #define ALTER_OPTIONS		128
 
-typedef struct st_alter_info
+/**
+  @brief Parsing data for CREATE or ALTER TABLE.
+
+  This structure contains a list of columns or indexes to be created,
+  altered or dropped.
+*/
+
+class Alter_info
 {
+public:
   List<Alter_drop>            drop_list;
   List<Alter_column>          alter_list;
+  List<Key>	              key_list;
+  List<create_field>          create_list;
   uint                        flags;
   enum enum_enable_or_disable keys_onoff;
   enum tablespace_op_type     tablespace_op;
   bool                        is_simple;
 
-  st_alter_info(){clear();}
-  void clear(){keys_onoff= LEAVE_AS_IS;tablespace_op= NO_TABLESPACE_OP;}
-  void reset(){drop_list.empty();alter_list.empty();clear();}
-} ALTER_INFO;
+  Alter_info() :
+    flags(0),
+    keys_onoff(LEAVE_AS_IS),
+    tablespace_op(NO_TABLESPACE_OP),
+    is_simple(1)
+  {}
+  void reset()
+  {
+    drop_list.empty();
+    alter_list.empty();
+    key_list.empty();
+    create_list.empty();
+    flags= 0;
+    keys_onoff= LEAVE_AS_IS;
+    tablespace_op= NO_TABLESPACE_OP;
+    is_simple= 1;
+  }
+  /**
+    Construct a copy of this object to be used for mysql_alter_table
+    and mysql_create_table. Historically, these two functions modify
+    their Alter_info arguments. This behaviour breaks re-execution of
+    prepared statements and stored procedures and is compensated by
+    always supplying a copy of Alter_info to these functions.
+    The constructed copy still shares key Key, Alter_drop, create_field
+    and Alter_column elements of the lists - these structures are not
+    modified and thus are not copied.
+
+    @note You need to use check thd->is_fatal_error for out
+    of memory condition after calling this function.
+  */
+  Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root);
+private:
+  Alter_info &operator=(const Alter_info &rhs); // not implemented
+  Alter_info(const Alter_info &rhs);            // not implemented
+};
+
 
 /* The state of the lex parsing. This is saved in the THD struct */
 
@@ -620,8 +662,6 @@ typedef struct st_lex
   List<String>	      interval_list;
   List<LEX_USER>      users_list;
   List<LEX_COLUMN>    columns;
-  List<Key>	      key_list;
-  List<create_field>  create_list;
   List<Item>	      *insert_list,field_list,value_list,update_list;
   List<List_item>     many_values;
   List<set_var_base>  var_list;
@@ -654,7 +694,7 @@ typedef struct st_lex
   bool derived_tables;
   bool safe_to_cache_query;
   bool subqueries, ignore;
-  ALTER_INFO alter_info;
+  Alter_info alter_info;
   /* Prepared statements SQL syntax:*/
   LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
   /* 

--- 1.31/sql/sql_list.h	2006-12-08 02:20:16 +03:00
+++ 1.32/sql/sql_list.h	2006-12-08 02:20:16 +03:00
@@ -66,21 +66,24 @@ public:
   pointer.
 */
 
-class list_node :public Sql_alloc
+
+/**
+  list_node - a node of a single-linked list.
+  @note We never call a destructor for instances of this class.
+*/
+
+struct list_node :public Sql_alloc
 {
-public:
   list_node *next;
   void *info;
   list_node(void *info_par,list_node *next_par)
     :next(next_par),info(info_par)
-    {}
+  {}
   list_node()					/* For end_of_list */
-    {
-      info=0;
-      next= this;
-    }
-  friend class base_list;
-  friend class base_list_iterator;
+  {
+    info= 0;
+    next= this;
+  }
 };
 
 
@@ -96,11 +99,56 @@ public:
 
   inline void empty() { elements=0; first= &end_of_list; last=&first;}
   inline base_list() { empty(); }
+  /**
+    This is a shallow copy constructor that implicitly passes the ownership
+    from the source list to the new instance. The old instance is not
+    updated, so both objects end up sharing the same nodes. If one of
+    the instances then adds or removes a node, the other becomes out of
+    sync ('last' pointer), while still operational. Some old code uses and
+    relies on this behaviour. This logic is quite tricky: please do not use
+    it in any new code.
+  */
   inline base_list(const base_list &tmp) :Sql_alloc()
   {
-    elements=tmp.elements;
-    first=tmp.first;
-    last=tmp.last;
+    elements= tmp.elements;
+    first= tmp.first;
+    last= elements ? tmp.last : &first;
+  }
+  /**
+    Construct a deep copy of the argument in memory root mem_root.
+    The elements themselves are copied by pointer.
+  */
+  inline base_list(const base_list &rhs, MEM_ROOT *mem_root)
+  {
+    if (rhs.elements)
+    {
+      /*
+        It's okay to allocate an array of nodes at once: we never
+        call a destructor for list_node objects anyway.
+      */
+      first= (list_node*) alloc_root(mem_root,
+                                     sizeof(list_node) * rhs.elements);
+      if (first)
+      {
+        elements= rhs.elements;
+        list_node *dst= first;
+        list_node *src= rhs.first;
+        for (; dst < first + elements - 1; dst++, src= src->next)
+        {
+          dst->info= src->info;
+          dst->next= dst + 1;
+        }
+        /* Copy the last node */
+        dst->info= src->info;
+        dst->next= &end_of_list;
+        /* Setup 'last' member */
+        last= &dst->next;
+        return;
+      }
+    }
+    elements= 0;
+    first= &end_of_list;
+    last= &first;
   }
   inline base_list(bool error) { }
   inline bool push_back(void *info)
@@ -327,6 +375,8 @@ template <class T> class List :public ba
 public:
   inline List() :base_list() {}
   inline List(const List<T> &tmp) :base_list(tmp) {}
+  inline List(const List<T> &tmp, MEM_ROOT *mem_root) :
+    base_list(tmp, mem_root) {}
   inline bool push_back(T *a) { return base_list::push_back(a); }
   inline bool push_front(T *a) { return base_list::push_front(a); }
   inline T* head() {return (T*) base_list::head(); }

--- 1.489/sql/sql_parse.cc	2006-12-08 02:20:16 +03:00
+++ 1.490/sql/sql_parse.cc	2006-12-08 02:20:16 +03:00
@@ -2478,6 +2478,22 @@ mysql_execute_command(THD *thd)
     }
     /* Skip first table, which is the table we are creating */
     TABLE_LIST *create_table, *create_table_local;
+    /*
+      Code below (especially in mysql_create_table() and select_create
+      methods) may modify HA_CREATE_INFO structure in LEX, so we have to
+      use a copy of this structure to make execution prepared statement-
+      safe. A shallow copy is enough as this code won't modify any memory
+      referenced from this structure.
+    */
+    HA_CREATE_INFO create_info(lex->create_info);
+    Alter_info alter_info(lex->alter_info, thd->mem_root);
+
+    if (thd->is_fatal_error)
+    {
+      /* out of memory when creating a copy of alter_info */
+      res= 1;
+      goto unsent_create_error;
+    }
     tables= lex->unlink_first_table(tables, &create_table,
 				    &create_table_local);
 
@@ -2485,12 +2501,12 @@ mysql_execute_command(THD *thd)
       goto unsent_create_error;
 
 #ifndef HAVE_READLINK
-    lex->create_info.data_file_name=lex->create_info.index_file_name=0;
+    create_info.data_file_name= create_info.index_file_name= NULL;
 #else
     /* Fix names if symlinked tables */
-    if (append_file_to_dir(thd, &lex->create_info.data_file_name,
+    if (append_file_to_dir(thd, &create_info.data_file_name,
 			   create_table->real_name) ||
-	append_file_to_dir(thd,&lex->create_info.index_file_name,
+	append_file_to_dir(thd, &create_info.index_file_name,
 			   create_table->real_name))
     {
       res=-1;
@@ -2501,14 +2517,14 @@ mysql_execute_command(THD *thd)
       If we are using SET CHARSET without DEFAULT, add an implicite
       DEFAULT to not confuse old users. (This may change).
     */
-    if ((lex->create_info.used_fields & 
+    if ((create_info.used_fields &
 	 (HA_CREATE_USED_DEFAULT_CHARSET | HA_CREATE_USED_CHARSET)) ==
 	HA_CREATE_USED_CHARSET)
     {
-      lex->create_info.used_fields&= ~HA_CREATE_USED_CHARSET;
-      lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
-      lex->create_info.default_table_charset= lex->create_info.table_charset;
-      lex->create_info.table_charset= 0;
+      create_info.used_fields&= ~HA_CREATE_USED_CHARSET;
+      create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET;
+      create_info.default_table_charset= create_info.table_charset;
+      create_info.table_charset= 0;
     }
     /*
       The create-select command will open and read-lock the select table
@@ -2542,11 +2558,14 @@ mysql_execute_command(THD *thd)
       if (!(res=open_and_lock_tables(thd,tables)))
       {
 	res= -1;				// If error
+        /*
+          select_create is currently not re-execution friendly and
+          needs to be created for every execution of a PS/SP.
+        */
         if ((result=new select_create(create_table->db,
                                       create_table->real_name,
-				      &lex->create_info,
-                                      lex->create_list,
-                                      lex->key_list,
+                                      &create_info,
+                                      &alter_info,
                                       select_lex->item_list, lex->duplicates,
                                       lex->ignore)))
         {
@@ -2558,22 +2577,18 @@ mysql_execute_command(THD *thd)
           res=handle_select(thd, lex, result);
           select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE;
         }
-	//reset for PS
-	lex->create_list.empty();
-	lex->key_list.empty();
       }
     }
     else // regular create
     {
       if (lex->name)
-        res= mysql_create_like_table(thd, create_table, &lex->create_info, 
+        res= mysql_create_like_table(thd, create_table, &create_info,
                                      (Table_ident *)lex->name); 
       else
       {
-        res= mysql_create_table(thd,create_table->db,
-			         create_table->real_name, &lex->create_info,
-			         lex->create_list,
-			         lex->key_list,0,0);
+        res= mysql_create_table(thd, create_table->db,
+                                create_table->real_name, &create_info,
+                                &alter_info, 0, 0);
       }
       if (!res)
 	send_ok(thd);
@@ -2591,15 +2606,48 @@ unsent_create_error:
     break;
   }
   case SQLCOM_CREATE_INDEX:
+    /* Fall through */
+  case SQLCOM_DROP_INDEX:
+  /*
+    CREATE INDEX and DROP INDEX are implemented by calling ALTER
+    TABLE with proper arguments. This isn't very fast but it
+    should work for most cases.
+
+    In the future ALTER TABLE will notice that only added
+    indexes and create these one by one for the existing table
+    without having to do a full rebuild.
+
+    One should normally create all indexes with CREATE TABLE or
+    ALTER TABLE.
+  */
+  {
+    Alter_info alter_info(lex->alter_info, thd->mem_root);
+    HA_CREATE_INFO create_info;
+
+    if (thd->is_fatal_error) /* out of memory creating a copy of alter_info*/
+      goto error;
+
     if (check_one_table_access(thd, INDEX_ACL, tables))
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     if (end_active_trans(thd))
-      res= -1;
-    else
-      res = mysql_create_index(thd, tables, lex->key_list);
-    break;
+      goto error;
+    /*
+      Currently CREATE INDEX or DROP INDEX cause a full table rebuild
+      and thus classify as slow administrative statements just like
+      ALTER TABLE.
+    */
+    thd->enable_slow_log= opt_log_slow_admin_statements;
 
+    bzero((char*) &create_info, sizeof(create_info));
+    create_info.db_type= DB_TYPE_DEFAULT;
+    create_info.default_table_charset= thd->variables.collation_database;
+
+    res= mysql_alter_table(thd, tables->db, tables->real_name,
+                           &create_info, tables, &alter_info,
+                           0, (ORDER*)0, DUP_ERROR, 0);
+    break;
+  }
 #ifdef HAVE_REPLICATION
   case SQLCOM_SLAVE_START:
   {
@@ -2642,6 +2690,17 @@ unsent_create_error:
 #else
     {
       ulong priv=0;
+      /*
+        Code in mysql_alter_table() may modify its HA_CREATE_INFO argument,
+        so we have to use a copy of this structure to make execution
+        prepared statement- safe. A shallow copy is enough as no memory
+        referenced from this structure will be modified.
+      */
+      HA_CREATE_INFO create_info(lex->create_info);
+      Alter_info alter_info(lex->alter_info, thd->mem_root);
+
+      if (thd->is_fatal_error) /* out of memory creating a copy of alter_info */
+        goto error;
       if (lex->name && (!lex->name[0] || strlen(lex->name) >
NAME_LEN))
       {
 	net_printf(thd, ER_WRONG_TABLE_NAME, lex->name);
@@ -2655,7 +2714,7 @@ unsent_create_error:
           default database if the new name is not explicitly qualified
           by a database. (Bug #11493)
         */
-        if (lex->alter_info.flags & ALTER_RENAME)
+        if (alter_info.flags & ALTER_RENAME)
         {
           if (! thd->db)
           {
@@ -2671,7 +2730,7 @@ unsent_create_error:
 	  check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv,0,0)||
 	  check_merge_table_access(thd, tables->db,
 				   (TABLE_LIST *)
-				   lex->create_info.merge_list.first))
+				   create_info.merge_list.first))
 	goto error;				/* purecov: inspected */
       if (grant_option)
       {
@@ -2690,26 +2749,26 @@ unsent_create_error:
 	}
       }
       /* Don't yet allow changing of symlinks with ALTER TABLE */
-      if (lex->create_info.data_file_name)
+      if (create_info.data_file_name)
         push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
                      "DATA DIRECTORY option ignored");
-      if (lex->create_info.index_file_name)
+      if (create_info.index_file_name)
         push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, 0,
                      "INDEX DIRECTORY option ignored");
-      lex->create_info.data_file_name=lex->create_info.index_file_name=0;
+      create_info.data_file_name= create_info.index_file_name= NULL;
       /* ALTER TABLE ends previous transaction */
       if (end_active_trans(thd))
 	res= -1;
       else
       {
         thd->enable_slow_log= opt_log_slow_admin_statements;
-	res= mysql_alter_table(thd, select_lex->db, lex->name,
-			       &lex->create_info,
-			       tables, lex->create_list,
-			       lex->key_list,
-			       select_lex->order_list.elements,
+        res= mysql_alter_table(thd, select_lex->db, lex->name,
+                               &create_info,
+                               tables,
+                               &alter_info,
+                               select_lex->order_list.elements,
                                (ORDER *) select_lex->order_list.first,
-			       lex->duplicates, lex->ignore, &lex->alter_info);
+                               lex->duplicates, lex->ignore);
       }
       break;
     }
@@ -2846,7 +2905,7 @@ unsent_create_error:
       goto error; /* purecov: inspected */
     thd->enable_slow_log= opt_log_slow_admin_statements;
     res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
-      mysql_recreate_table(thd, tables, 1) :
+      mysql_recreate_table(thd, tables) :
       mysql_optimize_table(thd, tables, &lex->check_opt);
     /* ! we write after unlocking the table */
     if (!res && !lex->no_write_to_binlog)
@@ -3123,14 +3182,6 @@ unsent_create_error:
     res= mysql_rm_table(thd,tables,lex->drop_if_exists, lex->drop_temporary);
   }
   break;
-  case SQLCOM_DROP_INDEX:
-    if (check_one_table_access(thd, INDEX_ACL, tables))
-      goto error;				/* purecov: inspected */
-    if (end_active_trans(thd))
-      res= -1;
-    else
-      res = mysql_drop_index(thd, tables, &lex->alter_info);
-    break;
   case SQLCOM_SHOW_DATABASES:
 #if defined(DONT_ALLOW_SHOW_COMMANDS)
     send_error(thd,ER_NOT_ALLOWED_COMMAND);   /* purecov: inspected */
@@ -3363,6 +3414,12 @@ purposes internal to the MySQL server", 
     break;
   case SQLCOM_CREATE_DB:
   {
+    /*
+      As mysql_create_db() may modify HA_CREATE_INFO structure passed to
+      it, we need to use a copy of LEX::create_info to make execution
+      prepared statement- safe.
+    */
+    HA_CREATE_INFO create_info(lex->create_info);
     if (end_active_trans(thd))
     {
       res= -1;
@@ -3393,7 +3450,7 @@ purposes internal to the MySQL server", 
     if (check_access(thd,CREATE_ACL,lex->name,0,1,0))
       break;
     res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias : lex->name),
-			 &lex->create_info, 0);
+			 &create_info, 0);
     break;
   }
   case SQLCOM_DROP_DB:
@@ -4443,15 +4500,17 @@ bool add_field_to_list(THD *thd, char *f
   if (type_modifier & PRI_KEY_FLAG)
   {
     lex->col_list.push_back(new key_part_spec(field_name,0));
-    lex->key_list.push_back(new Key(Key::PRIMARY, NullS, HA_KEY_ALG_UNDEF,
-				    0, lex->col_list));
+    lex->alter_info.key_list.push_back(new Key(Key::PRIMARY, NullS,
+                                               HA_KEY_ALG_UNDEF, 0,
+                                               lex->col_list));
     lex->col_list.empty();
   }
   if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
   {
     lex->col_list.push_back(new key_part_spec(field_name,0));
-    lex->key_list.push_back(new Key(Key::UNIQUE, NullS, HA_KEY_ALG_UNDEF, 0,
-				    lex->col_list));
+    lex->alter_info.key_list.push_back(new Key(Key::UNIQUE, NullS,
+                                               HA_KEY_ALG_UNDEF, 0,
+                                               lex->col_list));
     lex->col_list.empty();
   }
 
@@ -4778,7 +4837,7 @@ bool add_field_to_list(THD *thd, char *f
 					    new_field->sql_type,
 					    new_field->length);
   new_field->char_length= new_field->length;
-  lex->create_list.push_back(new_field);
+  lex->alter_info.create_list.push_back(new_field);
   lex->last_field=new_field;
   DBUG_RETURN(0);
 }
@@ -5455,55 +5514,6 @@ Item * all_any_subquery_creator(Item *le
     return it->upper_item= new Item_func_not_all(it);	/* ALL */
 
   return it->upper_item= new Item_func_nop_all(it);      /* ANY/SOME */
-}
-
-
-/*
-  CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with
-  the proper arguments.  This isn't very fast but it should work for most
-  cases.
-
-  In the future ALTER TABLE will notice that only added indexes
-  and create these one by one for the existing table without having to do
-  a full rebuild.
-
-  One should normally create all indexes with CREATE TABLE or ALTER TABLE.
-*/
-
-int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
-{
-  List<create_field> fields;
-  ALTER_INFO alter_info;
-  alter_info.flags= ALTER_ADD_INDEX;
-  alter_info.is_simple= 0;
-  HA_CREATE_INFO create_info;
-  DBUG_ENTER("mysql_create_index");
-  bzero((char*) &create_info,sizeof(create_info));
-  create_info.db_type=DB_TYPE_DEFAULT;
-  create_info.default_table_charset= thd->variables.collation_database;
-  DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
-				&create_info, table_list,
-				fields, keys, 0, (ORDER*)0,
-				DUP_ERROR, 0, &alter_info));
-}
-
-
-int mysql_drop_index(THD *thd, TABLE_LIST *table_list, ALTER_INFO *alter_info)
-{
-  List<create_field> fields;
-  List<Key> keys;
-  HA_CREATE_INFO create_info;
-  DBUG_ENTER("mysql_drop_index");
-  bzero((char*) &create_info,sizeof(create_info));
-  create_info.db_type=DB_TYPE_DEFAULT;
-  create_info.default_table_charset= thd->variables.collation_database;
-  alter_info->clear();
-  alter_info->flags= ALTER_DROP_INDEX;
-  alter_info->is_simple= 0;
-  DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name,
-				&create_info, table_list,
-				fields, keys, 0, (ORDER*)0,
-				DUP_ERROR, 0, alter_info));
 }
 
 

--- 1.208/sql/sql_show.cc	2006-12-08 02:20:16 +03:00
+++ 1.209/sql/sql_show.cc	2006-12-08 02:20:16 +03:00
@@ -845,7 +845,7 @@ mysqld_show_create(THD *thd, TABLE_LIST 
 }
 
 int mysqld_show_create_db(THD *thd, char *dbname,
-			  HA_CREATE_INFO *create_info)
+			  const HA_CREATE_INFO *create_info)
 {
   int length;
   char	path[FN_REFLEN];

--- 1.309/sql/sql_table.cc	2006-12-08 02:20:16 +03:00
+++ 1.310/sql/sql_table.cc	2006-12-08 02:20:16 +03:00
@@ -455,8 +455,7 @@ void calculate_interval_lengths(CHARSET_
     mysql_prepare_table()
     thd			Thread object
     create_info		Create information (like MAX_ROWS)
-    fields		List of fields to create
-    keys		List of keys to create
+    alter_info          List of columns and indexes to create
 
   DESCRIPTION
     Prepares the table and key structures for table creation.
@@ -467,8 +466,8 @@ void calculate_interval_lengths(CHARSET_
 */
 
 int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
-			List<create_field> &fields,
-			List<Key> &keys, bool tmp_table, uint &db_options,
+                        Alter_info *alter_info,
+			bool tmp_table, uint &db_options,
 			handler *file, KEY *&key_info_buffer,
 			uint *key_count, int select_field_count)
 {
@@ -481,10 +480,11 @@ int mysql_prepare_table(THD *thd, HA_CRE
   int		timestamps= 0, timestamps_with_niladic= 0;
   int		field_no,dup_no;
   int		select_field_pos,auto_increment=0;
+  List_iterator<create_field> it(alter_info->create_list);
+  List_iterator<create_field> it2(alter_info->create_list);
   DBUG_ENTER("mysql_prepare_table");
 
-  List_iterator<create_field> it(fields),it2(fields);
-  select_field_pos=fields.elements - select_field_count;
+  select_field_pos= alter_info->create_list.elements - select_field_count;
   null_fields=blob_columns=0;
 
   for (field_no=0; (sql_field=it++) ; field_no++)
@@ -882,7 +882,8 @@ int mysql_prepare_table(THD *thd, HA_CRE
 
   /* Create keys */
 
-  List_iterator<Key> key_iterator(keys), key_iterator2(keys);
+  List_iterator<Key> key_iterator(alter_info->key_list);
+  List_iterator<Key> key_iterator2(alter_info->key_list);
   uint key_parts=0, fk_key_count=0;
   bool primary_key=0,unique_key=0;
   Key *key, *key2;
@@ -1332,23 +1333,27 @@ int mysql_prepare_table(THD *thd, HA_CRE
 
   SYNOPSIS
     mysql_create_table()
-    thd			Thread object
-    db			Database
-    table_name		Table name
-    create_info		Create information (like MAX_ROWS)
-    fields		List of fields to create
-    keys		List of keys to create
-    tmp_table		Set to 1 if this is an internal temporary table
-			(From ALTER TABLE)
+    thd                  Thread object
+    db                   Database
+    table_name           Table name
+    create_info [in/out] Create information (like MAX_ROWS)
+    alter_info  [in/out] List of columns and indexes to create
+    tmp_table            Set to 1 if this is an internal temporary table
+                         (From ALTER TABLE)
 
   DESCRIPTION
-    If one creates a temporary table, this is automaticly opened
+    If one creates a temporary table, this is automatically opened
 
     no_log is needed for the case of CREATE ... SELECT,
     as the logging will be done later in sql_insert.cc
     select_field_count is also used for CREATE ... SELECT,
     and must be zero for standard create of table.
 
+    Note that structures passed as 'create_info' and 'alter_info' parameters
+    may be modified by this function. It is responsibility of the caller to
+    make a copy of create_info in order to provide correct execution in
+    prepared statements/stored routines.
+
   RETURN VALUES
     0	ok
     -1	error
@@ -1356,8 +1361,8 @@ int mysql_prepare_table(THD *thd, HA_CRE
 
 int mysql_create_table(THD *thd,const char *db, const char *table_name,
 		       HA_CREATE_INFO *create_info,
-		       List<create_field> &fields,
-		       List<Key> &keys,bool tmp_table,
+                       Alter_info *alter_info,
+                       bool tmp_table,
 		       uint select_field_count)
 {
   char		path[FN_REFLEN];
@@ -1370,7 +1375,7 @@ int mysql_create_table(THD *thd,const ch
   DBUG_ENTER("mysql_create_table");
 
   /* Check for duplicate fields and check type of table to create */
-  if (!fields.elements)
+  if (!alter_info->create_list.elements)
   {
     my_error(ER_TABLE_MUST_HAVE_COLUMNS,MYF(0));
     DBUG_RETURN(-1);
@@ -1422,10 +1427,10 @@ int mysql_create_table(THD *thd,const ch
     create_info->default_table_charset= db_info.default_table_charset;
   }
 
-  if (mysql_prepare_table(thd, create_info, fields,
-			  keys, tmp_table, db_options, file,
-			  key_info_buffer, &key_count,
-			  select_field_count))
+  if (mysql_prepare_table(thd, create_info, alter_info, tmp_table,
+                          db_options, file,
+                          key_info_buffer, &key_count,
+                          select_field_count))
     DBUG_RETURN(-1);
 
       /* Check if table exists */
@@ -1502,8 +1507,8 @@ int mysql_create_table(THD *thd,const ch
   create_info->table_options=db_options;
 
   if (rea_create_table(thd, path, db, table_name,
-                       create_info, fields, key_count,
-		       key_info_buffer))
+                       create_info, alter_info->create_list,
+                       key_count, key_info_buffer))
     goto end;
   if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
   {
@@ -1591,8 +1596,7 @@ make_unique_key_name(const char *field_n
 
 TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
 			       const char *db, const char *name,
-			       List<create_field> *extra_fields,
-			       List<Key> *keys,
+                               Alter_info *alter_info,
 			       List<Item> *items,
 			       MYSQL_LOCK **lock)
 {
@@ -1626,7 +1630,7 @@ TABLE *create_table_from_items(THD *thd,
 					   ((Item_field *)item)->field :
 					   (Field*) 0))))
       DBUG_RETURN(0);
-    extra_fields->push_back(cr_field);
+    alter_info->create_list.push_back(cr_field);
   }
   /* create and lock table */
   /* QQ: create and open should be done atomic ! */
@@ -1640,8 +1644,8 @@ TABLE *create_table_from_items(THD *thd,
     open_table().
   */
   tmp_disable_binlog(thd);
-  if (!mysql_create_table(thd,db,name,create_info,*extra_fields,
-			 *keys,0,select_field_count))
+  if (!mysql_create_table(thd, db, name, create_info, alter_info,
+                          0, select_field_count))
   {
     if (!(table=open_table(thd,db,name,name,(bool*) 0)))
       quick_rm_table(create_info->db_type,db,table_case_name(create_info,name));
@@ -2145,6 +2149,7 @@ send_result_message:
 
     case HA_ADMIN_TRY_ALTER:
     {
+      my_bool save_no_send_ok= thd->net.no_send_ok;
       /*
         This is currently used only by InnoDB. ha_innobase::optimize() answers
         "try with alter", so here we close the table, do an ALTER TABLE,
@@ -2154,7 +2159,9 @@ send_result_message:
       TABLE_LIST *save_next= table->next;
       table->next= 0;
       tmp_disable_binlog(thd); // binlogging is done by caller if wanted
-      result_code= mysql_recreate_table(thd, table, 0);
+      thd->net.no_send_ok= TRUE;
+      result_code= mysql_recreate_table(thd, table);
+      thd->net.no_send_ok= save_no_send_ok;
       reenable_binlog(thd);
       close_thread_tables(thd);
       if (!result_code) // recreation went ok
@@ -2634,219 +2641,25 @@ err:
 }
 
 
-#ifdef NOT_USED
-/*
-  CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with
-  the proper arguments.  This isn't very fast but it should work for most
-  cases.
-  One should normally create all indexes with CREATE TABLE or ALTER TABLE.
-*/
-
-int mysql_create_indexes(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
-{
-  List<create_field> fields;
-  List<Alter_drop>   drop;
-  List<Alter_column> alter;
-  HA_CREATE_INFO     create_info;
-  int		     rc;
-  uint		     idx;
-  uint		     db_options;
-  uint		     key_count;
-  TABLE		     *table;
-  Field		     **f_ptr;
-  KEY		     *key_info_buffer;
-  char		     path[FN_REFLEN+1];
-  DBUG_ENTER("mysql_create_index");
-
-  /*
-    Try to use online generation of index.
-    This requires that all indexes can be created online.
-    Otherwise, the old alter table procedure is executed.
-
-    Open the table to have access to the correct table handler.
-  */
-  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
-    DBUG_RETURN(-1);
-
-  /*
-    The add_index method takes an array of KEY structs for the new indexes.
-    Preparing a new table structure generates this array.
-    It needs a list with all fields of the table, which does not need to
-    be correct in every respect. The field names are important.
-  */
-  for (f_ptr= table->field; *f_ptr; f_ptr++)
-  {
-    create_field *c_fld= new create_field(*f_ptr, *f_ptr);
-    c_fld->unireg_check= Field::NONE; /*avoid multiple auto_increments*/
-    fields.push_back(c_fld);
-  }
-  bzero((char*) &create_info,sizeof(create_info));
-  create_info.db_type=DB_TYPE_DEFAULT;
-  create_info.default_table_charset= thd->variables.collation_database;
-  db_options= 0;
-  if (mysql_prepare_table(thd, &create_info, fields,
-			  keys, /*tmp_table*/ 0, db_options, table->file,
-			  key_info_buffer, key_count,
-			  /*select_field_count*/ 0))
-    DBUG_RETURN(-1);
-
-  /*
-    Check if all keys can be generated with the add_index method.
-    If anyone cannot, then take the old way.
-  */
-  for (idx=0; idx< key_count; idx++)
-  {
-    DBUG_PRINT("info", ("creating index %s", key_info_buffer[idx].name));
-    if (!(table->file->index_ddl_flags(key_info_buffer+idx)&
-	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
-      break ;
-  }
-  if ((idx < key_count)|| !key_count)
-  {
-    /* Re-initialize the create_info, which was changed by prepare table. */
-    bzero((char*) &create_info,sizeof(create_info));
-    create_info.db_type=DB_TYPE_DEFAULT;
-    create_info.default_table_charset= thd->variables.collation_database;
-    /* Cleanup the fields list. We do not want to create existing fields. */
-    fields.delete_elements();
-    if (real_alter_table(thd, table_list->db, table_list->real_name,
-			 &create_info, table_list, table,
-			 fields, keys, drop, alter, 0, (ORDER*)0,
-			 ALTER_ADD_INDEX, DUP_ERROR))
-      /* Don't need to free((gptr) key_info_buffer);*/
-      DBUG_RETURN(-1);
-  }
-  else
-  {
-    if (table->file->add_index(table, key_info_buffer, key_count)||
-        build_table_path(path, sizeof(path), table_list->db,
-                         (lower_case_table_names == 2) ?
-                         table_list->alias : table_list->real_name,
-                         reg_ext) == 0 ||
-	mysql_create_frm(thd, path, &create_info,
-			 fields, key_count, key_info_buffer, table->file))
-      /* don't need to free((gptr) key_info_buffer);*/
-      DBUG_RETURN(-1);
-  }
-  /* don't need to free((gptr) key_info_buffer);*/
-  DBUG_RETURN(0);
-}
-
-
-int mysql_drop_indexes(THD *thd, TABLE_LIST *table_list,
-		       List<Alter_drop> &drop)
-{
-  List<create_field> fields;
-  List<Key>	     keys;
-  List<Alter_column> alter;
-  HA_CREATE_INFO     create_info;
-  uint		     idx;
-  uint		     db_options;
-  uint		     key_count;
-  uint		     *key_numbers;
-  TABLE		     *table;
-  Field		     **f_ptr;
-  KEY		     *key_info;
-  KEY		     *key_info_buffer;
-  char		     path[FN_REFLEN];
-  DBUG_ENTER("mysql_drop_index");
-
-  /*
-    Try to use online generation of index.
-    This requires that all indexes can be created online.
-    Otherwise, the old alter table procedure is executed.
-
-    Open the table to have access to the correct table handler.
-  */
-  if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
-    DBUG_RETURN(-1);
-
-  /*
-    The drop_index method takes an array of key numbers.
-    It cannot get more entries than keys in the table.
-  */
-  key_numbers= (uint*) thd->alloc(sizeof(uint*)*table->keys);
-  key_count= 0;
-
-  /*
-    Get the number of each key and check if it can be created online.
-  */
-  List_iterator<Alter_drop> drop_it(drop);
-  Alter_drop *drop_key;
-  while ((drop_key= drop_it++))
-  {
-    /* Find the key in the table. */
-    key_info=table->key_info;
-    for (idx=0; idx< table->keys; idx++, key_info++)
-    {
-      if (!my_strcasecmp(system_charset_info, key_info->name, drop_key->name))
-	break;
-    }
-    if (idx>= table->keys)
-    {
-      my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0), drop_key->name);
-      /*don't need to free((gptr) key_numbers);*/
-      DBUG_RETURN(-1);
-    }
-    /*
-      Check if the key can be generated with the add_index method.
-      If anyone cannot, then take the old way.
-    */
-    DBUG_PRINT("info", ("dropping index %s", table->key_info[idx].name));
-    if (!(table->file->index_ddl_flags(table->key_info+idx)&
-	  (HA_DDL_ONLINE| HA_DDL_WITH_LOCK)))
-      break ;
-    key_numbers[key_count++]= idx;
-  }
-
-  bzero((char*) &create_info,sizeof(create_info));
-  create_info.db_type=DB_TYPE_DEFAULT;
-  create_info.default_table_charset= thd->variables.collation_database;
-
-  if ((drop_key)|| (drop.elements<= 0))
-  {
-    if (real_alter_table(thd, table_list->db, table_list->real_name,
-			 &create_info, table_list, table,
-			 fields, keys, drop, alter, 0, (ORDER*)0,
-			 ALTER_DROP_INDEX, DUP_ERROR))
-      /*don't need to free((gptr) key_numbers);*/
-      DBUG_RETURN(-1);
-  }
-  else
-  {
-    db_options= 0;
-    if (table->file->drop_index(table, key_numbers, key_count)||
-	mysql_prepare_table(thd, &create_info, fields,
-			    keys, /*tmp_table*/ 0, db_options, table->file,
-			    key_info_buffer, key_count,
-			    /*select_field_count*/ 0)||
-        build_table_path(path, sizeof(path), table_list->db,
-                         (lower_case_table_names == 2) ?
-                         table_list->alias : table_list->real_name,
-                         reg_ext) == 0 ||
-	mysql_create_frm(thd, path, &create_info,
-			 fields, key_count, key_info_buffer, table->file))
-      /*don't need to free((gptr) key_numbers);*/
-      DBUG_RETURN(-1);
-  }
-
-  /*don't need to free((gptr) key_numbers);*/
-  DBUG_RETURN(0);
-}
-#endif /* NOT_USED */
 
 
 /*
   Alter table
+
+
+  NOTE
+    The structures passed as 'create_info' and 'alter_info' parameters may
+    be modified by this function. It is responsibility of the caller to make
+    a copy of create_info in order to provide correct execution in prepared
+    statements/stored routines.
 */
 
 int mysql_alter_table(THD *thd,char *new_db, char *new_name,
 		      HA_CREATE_INFO *create_info,
 		      TABLE_LIST *table_list,
-		      List<create_field> &fields, List<Key> &keys,
+                      Alter_info *alter_info,
 		      uint order_num, ORDER *order,
-		      enum enum_duplicates handle_duplicates, bool ignore,
-		      ALTER_INFO *alter_info, bool do_send_ok)
+		      enum enum_duplicates handle_duplicates, bool ignore)
 {
   TABLE *table,*new_table;
   int error;
@@ -3008,8 +2821,7 @@ int mysql_alter_table(THD *thd,char *new
 	Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
 	mysql_bin_log.write(&qinfo);
       }
-      if (do_send_ok)
-        send_ok(thd);
+      send_ok(thd);
     }
     else if (error > 0)
     {
@@ -3035,10 +2847,9 @@ int mysql_alter_table(THD *thd,char *new
 
   restore_record(table,default_values);		// Empty record for DEFAULT
   List_iterator<Alter_drop> drop_it(alter_info->drop_list);
-  List_iterator<create_field> def_it(fields);
+  List_iterator<create_field> def_it(alter_info->create_list);
   List_iterator<Alter_column> alter_it(alter_info->alter_list);
-  List<create_field> create_list;		// Add new fields here
-  List<Key> key_list;				// Add new keys here
+  Alter_info new_info;                   // Add new columns and indexes here
   create_field *def;
 
   /*
@@ -3084,13 +2895,13 @@ int mysql_alter_table(THD *thd,char *new
       def->field=field;
       if (!def->after)
       {
-	create_list.push_back(def);
+	new_info.create_list.push_back(def);
 	def_it.remove();
       }
     }
     else
     {						// Use old field value
-      create_list.push_back(def=new create_field(field,field));
+      new_info.create_list.push_back(def= new create_field(field, field));
       alter_it.rewind();			// Change default if ALTER
       Alter_column *alter;
       while ((alter=alter_it++))
@@ -3111,7 +2922,7 @@ int mysql_alter_table(THD *thd,char *new
     }
   }
   def_it.rewind();
-  List_iterator<create_field> find_it(create_list);
+  List_iterator<create_field> find_it(new_info.create_list);
   while ((def=def_it++))			// Add new columns
   {
     if (def->change && ! def->field)
@@ -3120,9 +2931,9 @@ int mysql_alter_table(THD *thd,char *new
       DBUG_RETURN(-1);
     }
     if (!def->after)
-      create_list.push_back(def);
+      new_info.create_list.push_back(def);
     else if (def->after == first_keyword)
-      create_list.push_front(def);
+      new_info.create_list.push_front(def);
     else
     {
       create_field *find;
@@ -3146,7 +2957,7 @@ int mysql_alter_table(THD *thd,char *new
 	     table_name);
     DBUG_RETURN(-1);
   }
-  if (!create_list.elements)
+  if (!new_info.create_list.elements)
   {
     my_error(ER_CANT_REMOVE_ALL_FIELDS,MYF(0));
     DBUG_RETURN(-1);
@@ -3157,8 +2968,8 @@ int mysql_alter_table(THD *thd,char *new
     for which some fields exists.
   */
 
-  List_iterator<Key> key_it(keys);
-  List_iterator<create_field> field_it(create_list);
+  List_iterator<Key> key_it(alter_info->key_list);
+  List_iterator<create_field> field_it(new_info.create_list);
   List<key_part_spec> key_parts;
 
   KEY *key_info=table->key_info;
@@ -3216,24 +3027,37 @@ int mysql_alter_table(THD *thd,char *new
 					    key_part_length));
     }
     if (key_parts.elements)
-      key_list.push_back(new Key(key_info->flags & HA_SPATIAL ? Key::SPATIAL :
-				 (key_info->flags & HA_NOSAME ?
-				 (!my_strcasecmp(system_charset_info,
-						 key_name, primary_key_name) ?
-				  Key::PRIMARY	: Key::UNIQUE) :
-				  (key_info->flags & HA_FULLTEXT ?
-				   Key::FULLTEXT : Key::MULTIPLE)),
-				 key_name,
-				 key_info->algorithm,
-                                 test(key_info->flags & HA_GENERATED_KEY),
-				 key_parts));
+    {
+      Key *key;
+      enum Key::Keytype key_type;
+
+      if (key_info->flags & HA_SPATIAL)
+        key_type= Key::SPATIAL;
+      else if (key_info->flags & HA_NOSAME)
+      {
+        if (! my_strcasecmp(system_charset_info, key_name, primary_key_name))
+          key_type= Key::PRIMARY;
+        else
+          key_type= Key::UNIQUE;
+      }
+      else if (key_info->flags & HA_FULLTEXT)
+        key_type= Key::FULLTEXT;
+      else
+        key_type= Key::MULTIPLE;
+
+      key= new Key(key_type, key_name,
+                   key_info->algorithm,
+                   test(key_info->flags & HA_GENERATED_KEY),
+                   key_parts);
+      new_info.key_list.push_back(key);
+    }
   }
   {
     Key *key;
     while ((key=key_it++))			// Add new keys
     {
       if (key->type != Key::FOREIGN_KEY)
-	key_list.push_back(key);
+        new_info.key_list.push_back(key);
       if (key->name &&
 	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
       {
@@ -3337,7 +3161,7 @@ int mysql_alter_table(THD *thd,char *new
     /* We don't log the statement, it will be logged later. */
     tmp_disable_binlog(thd);
     error= mysql_create_table(thd, new_db, tmp_name,
-                              create_info,create_list,key_list,1,0);
+                              create_info, &new_info, 1, 0);
     reenable_binlog(thd);
     if (error)
       DBUG_RETURN(error);
@@ -3366,9 +3190,9 @@ int mysql_alter_table(THD *thd,char *new
   next_insert_id=thd->next_insert_id;		// Remember for loggin
   copied=deleted=0;
   if (!new_table->is_view)
-    error=copy_data_between_tables(table,new_table,create_list,
-				   handle_duplicates, ignore,
-				   order_num, order, &copied, &deleted);
+    error= copy_data_between_tables(table, new_table, new_info.create_list,
+                                    handle_duplicates, ignore,
+                                    order_num, order, &copied, &deleted);
   thd->last_insert_id=next_insert_id;		// Needed for correct log
   thd->count_cuted_fields= CHECK_FIELD_IGNORE;
 
@@ -3574,8 +3398,7 @@ end_temporary:
   my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
 	      (ulong) (copied + deleted), (ulong) deleted,
 	      (ulong) thd->cuted_fields);
-  if (do_send_ok)
-    send_ok(thd,copied+deleted,0L,tmp_name);
+  send_ok(thd, copied + deleted, 0L, tmp_name);
   thd->some_tables_deleted=0;
   DBUG_RETURN(0);
 
@@ -3760,30 +3583,26 @@ copy_data_between_tables(TABLE *from,TAB
     mysql_recreate_table()
     thd			Thread handler
     tables		Tables to recreate
-    do_send_ok          If we should send_ok() or leave it to caller
 
  RETURN
     Like mysql_alter_table().
 */
-int mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
-                         bool do_send_ok)
+int mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
 {
-  DBUG_ENTER("mysql_recreate_table");
   LEX *lex= thd->lex;
   HA_CREATE_INFO create_info;
-  lex->create_list.empty();
-  lex->key_list.empty();
-  lex->col_list.empty();
-  lex->alter_info.reset();
-  lex->alter_info.is_simple= 0;                 // Force full recreate
+  Alter_info alter_info;
+
+  DBUG_ENTER("mysql_recreate_table");
+
   bzero((char*) &create_info,sizeof(create_info));
   create_info.db_type=DB_TYPE_DEFAULT;
   create_info.row_type=ROW_TYPE_DEFAULT;
   create_info.default_table_charset=default_charset_info;
+  alter_info.is_simple= 0;                 // Force full recreate
   DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
-                                table_list, lex->create_list,
-                                lex->key_list, 0, (ORDER *) 0,
-                                DUP_ERROR, 0, &lex->alter_info, do_send_ok));
+                                table_list, &alter_info,
+                                0, (ORDER *) 0, DUP_ERROR, 0));
 }
 
 

--- 1.401/sql/sql_yacc.yy	2006-12-08 02:20:16 +03:00
+++ 1.402/sql/sql_yacc.yy	2006-12-08 02:20:16 +03:00
@@ -1021,8 +1021,7 @@ create:
 						  TL_READ_NO_INSERT:
 						  TL_READ)))
 	    YYABORT;
-	  lex->create_list.empty();
-	  lex->key_list.empty();
+          lex->alter_info.reset();
 	  lex->col_list.empty();
 	  lex->change=NullS;
 	  bzero((char*) &lex->create_info,sizeof(lex->create_info));
@@ -1040,16 +1039,18 @@ create:
 	    if (!lex->current_select->add_table_to_list(lex->thd, $7, NULL,
 							TL_OPTION_UPDATING))
 	      YYABORT;
-	    lex->create_list.empty();
-	    lex->key_list.empty();
+            lex->alter_info.reset();
+            lex->alter_info.is_simple= 0;
+            lex->alter_info.flags= ALTER_ADD_INDEX;
 	    lex->col_list.empty();
 	    lex->change=NullS;
 	  }
 	   '(' key_list ')'
 	  {
 	    LEX *lex=Lex;
+            Key *key= new Key($2, $4.str, $5, 0, lex->col_list);
 
-	    lex->key_list.push_back(new Key($2,$4.str, $5, 0, lex->col_list));
+            lex->alter_info.key_list.push_back(key);
 	    lex->col_list.empty();
 	  }
 	| CREATE DATABASE opt_if_not_exists ident
@@ -1305,29 +1306,34 @@ key_def:
 	key_type opt_ident key_alg '(' key_list ')'
 	  {
 	    LEX *lex=Lex;
-	    lex->key_list.push_back(new Key($1,$2, $3, 0, lex->col_list));
+            Key *key= new Key($1, $2, $3, 0, lex->col_list);
+            lex->alter_info.key_list.push_back(key);
+
 	    lex->col_list.empty();		/* Alloced by sql_alloc */
 	  }
 	| opt_constraint constraint_key_type opt_ident key_alg '(' key_list ')'
 	  {
 	    LEX *lex=Lex;
 	    const char *key_name= $3 ? $3:$1;
-	    lex->key_list.push_back(new Key($2, key_name, $4, 0,
-				    lex->col_list));
+            Key *key= new Key($2, key_name, $4, 0, lex->col_list);
+            lex->alter_info.key_list.push_back(key);
 	    lex->col_list.empty();		/* Alloced by sql_alloc */
 	  }
 	| opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references
 	  {
 	    LEX *lex=Lex;
-	    lex->key_list.push_back(new foreign_key($4 ? $4:$1, lex->col_list,
-				    $8,
-				    lex->ref_list,
-				    lex->fk_delete_opt,
-				    lex->fk_update_opt,
-				    lex->fk_match_option));
-	    lex->key_list.push_back(new Key(Key::MULTIPLE, $4 ? $4 : $1,
-					    HA_KEY_ALG_UNDEF, 1,
-					    lex->col_list));
+            const char *key_name= $4 ? $4 : $1;
+            Key *key= new foreign_key(key_name, lex->col_list,
+                                      $8,
+                                      lex->ref_list,
+                                      lex->fk_delete_opt,
+                                      lex->fk_update_opt,
+                                      lex->fk_match_option);
+            lex->alter_info.key_list.push_back(key);
+            key= new Key(Key::MULTIPLE, key_name,
+                         HA_KEY_ALG_UNDEF, 1,
+                         lex->col_list);
+            lex->alter_info.key_list.push_back(key);
 	    lex->col_list.empty();		/* Alloced by sql_alloc */
 	  }
 	| constraint opt_check_constraint
@@ -1850,8 +1856,6 @@ alter:
 	  if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
 						 TL_OPTION_UPDATING))
 	    YYABORT;
-	  lex->create_list.empty();
-	  lex->key_list.empty();
 	  lex->col_list.empty();
           lex->select_lex.init_order();
 	  lex->select_lex.db=lex->name=0;
@@ -1859,9 +1863,7 @@ alter:
 	  lex->create_info.db_type= DB_TYPE_DEFAULT;
 	  lex->create_info.default_table_charset= NULL;
 	  lex->create_info.row_type= ROW_TYPE_NOT_USED;
-	  lex->alter_info.reset();          
-	  lex->alter_info.is_simple= 1;
-	  lex->alter_info.flags= 0;
+          lex->alter_info.reset();
 	}
 	alter_list
 	{}
@@ -4030,7 +4032,9 @@ drop:
 	  {
 	     LEX *lex=Lex;
 	     lex->sql_command= SQLCOM_DROP_INDEX;
-	     lex->alter_info.drop_list.empty();
+             lex->alter_info.reset();
+             lex->alter_info.is_simple= 0;
+             lex->alter_info.flags= ALTER_DROP_INDEX;
 	     lex->alter_info.drop_list.push_back(new Alter_drop(Alter_drop::KEY,
                                                                 $3.str));
 	     if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,

--- 1.53/mysql-test/r/ps.result	2006-12-08 02:20:16 +03:00
+++ 1.54/mysql-test/r/ps.result	2006-12-08 02:20:16 +03:00
@@ -1035,4 +1035,71 @@ EXECUTE stmt USING @a;
 0	0
 DEALLOCATE PREPARE stmt;
 DROP TABLE t1;
+DROP TABLE IF EXISTS t1, t2;
+CREATE TABLE t1 (i INT);
+PREPARE st_19182
+FROM "CREATE TABLE t2 (i INT, j INT, KEY (i), KEY(j)) SELECT i FROM t1";
+EXECUTE st_19182;
+DESC t2;
+Field	Type	Null	Key	Default	Extra
+j	int(11)	YES	MUL	NULL	
+i	int(11)	YES	MUL	NULL	
+DROP TABLE t2;
+EXECUTE st_19182;
+DESC t2;
+Field	Type	Null	Key	Default	Extra
+j	int(11)	YES	MUL	NULL	
+i	int(11)	YES	MUL	NULL	
+DEALLOCATE PREPARE st_19182;
+DROP TABLE t2, t1;
+drop database if exists mysqltest;
+drop table if exists t1, t2;
+create database mysqltest character set utf8;
+prepare stmt1 from "create table mysqltest.t1 (c char(10))";
+prepare stmt2 from "create table mysqltest.t2 select 'test'";
+execute stmt1;
+execute stmt2;
+show create table mysqltest.t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `c` char(10) default NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8
+show create table mysqltest.t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `test` char(4) character set latin1 NOT NULL default ''
+) ENGINE=MyISAM DEFAULT CHARSET=utf8
+drop table mysqltest.t1;
+drop table mysqltest.t2;
+alter database mysqltest character set latin1;
+execute stmt1;
+execute stmt2;
+show create table mysqltest.t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `c` char(10) character set utf8 default NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+show create table mysqltest.t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `test` char(4) NOT NULL default ''
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+drop database mysqltest;
+deallocate prepare stmt1;
+deallocate prepare stmt2;
+execute stmt;
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `c` char(10) default NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQLTEST_VARDIR/tmp/'
+drop table t1;
+execute stmt;
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `c` char(10) default NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1 DATA DIRECTORY='MYSQLTEST_VARDIR/tmp/'
+drop table t1;
+deallocate prepare stmt;
 End of 4.1 tests.

--- 1.55/mysql-test/t/ps.test	2006-12-08 02:20:16 +03:00
+++ 1.56/mysql-test/t/ps.test	2006-12-08 02:20:16 +03:00
@@ -1068,5 +1068,77 @@ EXECUTE stmt USING @a;
 DEALLOCATE PREPARE stmt;
 DROP TABLE t1;
 
+#
+# Bug#19182: CREATE TABLE bar (m INT) SELECT n FROM foo; doesn't work
+# from stored procedure.
+#
+# The cause of a bug was that cached LEX::create_list was modified,
+# and then together with LEX::key_list was reset.
+#
+--disable_warnings
+DROP TABLE IF EXISTS t1, t2;
+--enable_warnings
 
+CREATE TABLE t1 (i INT);
+
+PREPARE st_19182
+FROM "CREATE TABLE t2 (i INT, j INT, KEY (i), KEY(j)) SELECT i FROM t1";
+
+EXECUTE st_19182;
+DESC t2;
+
+DROP TABLE t2;
+
+# Check that on second execution we don't loose 'j' column and the keys
+# on 'i' and 'j' columns.
+EXECUTE st_19182;
+DESC t2;
+
+DEALLOCATE PREPARE st_19182;
+DROP TABLE t2, t1;
+
+#
+# Bug #22060 "ALTER TABLE x AUTO_INCREMENT=y in SP crashes server"
+#
+# Code which implemented CREATE/ALTER TABLE and CREATE DATABASE
+# statement modified HA_CREATE_INFO structure in LEX, making these
+# statements PS/SP-unsafe (their re-execution might have resulted
+# in incorrect results).
+#
+--disable_warnings
+drop database if exists mysqltest;
+drop table if exists t1, t2;
+--enable_warnings
+# CREATE TABLE and CREATE TABLE ... SELECT
+create database mysqltest character set utf8;
+prepare stmt1 from "create table mysqltest.t1 (c char(10))";
+prepare stmt2 from "create table mysqltest.t2 select 'test'";
+execute stmt1;
+execute stmt2;
+show create table mysqltest.t1;
+show create table mysqltest.t2;
+drop table mysqltest.t1;
+drop table mysqltest.t2;
+alter database mysqltest character set latin1;
+execute stmt1;
+execute stmt2;
+show create table mysqltest.t1;
+show create table mysqltest.t2;
+drop database mysqltest;
+deallocate prepare stmt1;
+deallocate prepare stmt2;
+# CREATE TABLE with DATA DIRECTORY option
+--disable_query_log
+eval prepare stmt from "create table t1 (c char(10)) data
directory='$MYSQLTEST_VARDIR/tmp'";
+--enable_query_log
+execute stmt;
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+show create table t1;
+drop table t1;
+execute stmt;
+--replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+show create table t1;
+drop table t1;
+deallocate prepare stmt;
+#
 --echo End of 4.1 tests.
Thread
bk commit into 4.1 tree (kostja:1.2561) BUG#4968konstantin8 Dec