MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:konstantin Date:May 28 2007 11:30am
Subject:bk commit into 5.1 tree (kostja:1.2515) BUG#4968
View as plain text  
Below is the list of changes that have just been committed into a local
5.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, 2007-05-28 15:30:01+04:00, kostja@vajra.(none) +17 -0
  5.1 version of a fix and test cases for bugs:
  Bug#4968 ""Stored procedure crash if cursor opened on altered table"
  Bug#6895 "Prepared Statements: ALTER TABLE DROP COLUMN does nothing"
  Bug#19182 "CREATE TABLE bar (m INT) SELECT n FROM foo; doesn't work from 
  stored procedure."
  Bug#19733 "Repeated alter, or repeated create/drop, fails"
  Bug#22060 "ALTER TABLE x AUTO_INCREMENT=y in SP crashes server"
  Bug#24879 "Prepared Statements: CREATE TABLE (UTF8 KEY) produces a 
  growing key length" (this bug is not fixed 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 are not
  re-execution friendly: during their operation they 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 in mysql_execute_command.
  
  Additionally, this patch splits the part of mysql_alter_table
  that analizes and rewrites information from the parser into
  a separate function - mysql_prepare_alter_table, in analogy with
  mysql_prepare_table, which is renamed to mysql_prepare_create_table.

  mysql-test/r/ps.result@stripped, 2007-05-28 15:29:55+04:00, kostja@vajra.(none) +168 -0
    Update test results (Bug#19182, Bug#22060, Bug#4968, Bug#6895)

  mysql-test/r/sp.result@stripped, 2007-05-28 15:29:55+04:00, kostja@vajra.(none) +17 -0
    Update results (Bug#19733)

  mysql-test/t/ps.test@stripped, 2007-05-28 15:29:55+04:00, kostja@vajra.(none) +194 -0
    Add test cases for Bug#19182, Bug#22060, Bug#4968, Bug#6895

  mysql-test/t/sp.test@stripped, 2007-05-28 15:29:55+04:00, kostja@vajra.(none) +28 -0
    Add a test case for Bug#19733

  sql/field.h@stripped, 2007-05-28 15:29:55+04:00, kostja@vajra.(none) +3 -0
    Implement a deep copy constructor for create_field

  sql/mysql_priv.h@stripped, 2007-05-28 15:29:55+04:00, kostja@vajra.(none) +13 -24
    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.
    Remove declarations of mysql_add_index, mysql_drop_index.

  sql/sql_class.cc@stripped, 2007-05-28 15:29:56+04:00, kostja@vajra.(none) +34 -0
    Implement deep copy constructors.

  sql/sql_class.h@stripped, 2007-05-28 15:29:56+04:00, kostja@vajra.(none) +49 -12
    Implement (almost) deep copy constructors for key_part_spec, 
    Alter_drop, Alter_column, Key, foreign_key.
    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, 2007-05-28 15:29:56+04:00, kostja@vajra.(none) +8 -9
    Adjust to a new signature of create_table_from_items.

  sql/sql_lex.cc@stripped, 2007-05-28 15:29:56+04:00, kostja@vajra.(none) +47 -0
    Implement Alter_info::Alter_info that would make a "deep" copy
    of all definition lists (keys, columns).
    Move is_partition_management() from sql_partition.cc (feature-based
    file division is evil).

  sql/sql_lex.h@stripped, 2007-05-28 15:29:56+04:00, kostja@vajra.(none) +42 -9
    Move key_list and create_list to class Alter_info. Implement
    Alter_info::Alter_info that can be used with PS and SP.
    Get rid of Alter_info::clear() which was an attempt to save on
    matches and always use Alter_info::reset().
    Implement an auxiliary Alter_info::init_for_create_from_alter()
    which is used in mysql_alter_table.

  sql/sql_list.cc@stripped, 2007-05-28 15:29:56+04:00, kostja@vajra.(none) +34 -0
      Implement a copy constructor of class List that makes a deep copy
      of all list nodes.

  sql/sql_list.h@stripped, 2007-05-28 15:29:56+04:00, kostja@vajra.(none) +70 -9
    Implement a way to make a deep copy of all list nodes.

  sql/sql_parse.cc@stripped, 2007-05-28 15:29:56+04:00, kostja@vajra.(none) +130 -118
    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 was a bug.
    Just like CREATE INDEX, DROP INDEX is currently done via complete 
    table rebuild and is rightfully a slow administrative statement.

  sql/sql_partition.cc@stripped, 2007-05-28 15:29:57+04:00, kostja@vajra.(none) +5 -34
    Move is_partition_management() to sql_lex.cc
    Adjust code to the new Alter_info.

  sql/sql_table.cc@stripped, 2007-05-28 15:29:57+04:00, kostja@vajra.(none) +727 -820
    Adjust mysql_alter_table, mysql_recreate_table, mysql_create_table,
    mysql_prepare_table to new signatures.
    Rename mysql_prepare_table to mysql_prepare_create_table. Make
    sure it follows the convention and returns FALSE for success and
    TRUE for error.
    Move parts of mysql_alter_table to mysql_prepare_alter_table.
    Move the first invokation of mysql_prepare_table from mysql_alter_table
    to compare_tables, as it was needed only for the purpose
    of correct comparison.
    Since now Alter_info itself is created in the runtime mem root,
    adjust mysql_prepare_table to always allocate memory in the
    runtime memory root.
    Remove dead code.

  sql/sql_yacc.yy@stripped, 2007-05-28 15:29:57+04:00, kostja@vajra.(none) +33 -29
    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:	vajra.(none)
# Root:	/opt/local/work/mysql-5.1-4968-clean

--- 1.226/sql/field.h	2007-05-18 00:21:25 +04:00
+++ 1.227/sql/field.h	2007-05-28 15:29:55 +04:00
@@ -1622,6 +1622,9 @@ public:
   uint	offset,pack_flag;
   create_field() :after(0) {}
   create_field(Field *field, Field *orig_field);
+  /* Used to make a clone of this object for ALTER/CREATE TABLE */
+  create_field *clone(MEM_ROOT *mem_root) const
+    { return new (mem_root) create_field(*this); }
   void create_length_to_internal_length(void);
 
   /* Init for a tmp table field. To be extended if need be. */

--- 1.511/sql/mysql_priv.h	2007-05-24 21:11:57 +04:00
+++ 1.512/sql/mysql_priv.h	2007-05-28 15:29:55 +04:00
@@ -960,32 +960,26 @@ int prepare_create_field(create_field *s
 			 longlong table_flags);
 bool 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, uint select_field_count,
-                        bool use_copy_create_info);
+                        Alter_info *alter_info,
+                        bool tmp_table, uint select_field_count);
 bool mysql_create_table_no_lock(THD *thd, const char *db,
                                 const char *table_name,
                                 HA_CREATE_INFO *create_info,
-                                List<create_field> &fields, List<Key> &keys,
-                                bool tmp_table, uint select_field_count,
-                                bool use_copy_create_info);
+                                Alter_info *alter_info,
+                                bool tmp_table, uint select_field_count);
 
 bool 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,
-                       uint order_num, ORDER *order, bool ignore,
-                       ALTER_INFO *alter_info, bool do_send_ok);
-bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool do_send_ok);
-bool mysql_create_like_table(THD *thd, TABLE_LIST *table, TABLE_LIST *src_table,
+                       Alter_info *alter_info,
+                       uint order_num, ORDER *order, bool ignore);
+bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list);
+bool mysql_create_like_table(THD *thd, TABLE_LIST *table,
+                             TABLE_LIST *src_table,
                              HA_CREATE_INFO *create_info);
 bool mysql_rename_table(handlerton *base, const char *old_db,
                         const char * old_name, const char *new_db,
                         const char * new_name, uint flags);
-bool mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys);
-bool mysql_drop_index(THD *thd, TABLE_LIST *table_list,
-                      ALTER_INFO *alter_info);
 bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
                           Item **conds, uint order_num, ORDER *order);
 int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields,
@@ -1309,14 +1303,13 @@ char *make_default_log_name(char *buff,c
 
 #ifdef WITH_PARTITION_STORAGE_ENGINE
 uint fast_alter_partition_table(THD *thd, TABLE *table,
-                                ALTER_INFO *alter_info,
+                                Alter_info *alter_info,
                                 HA_CREATE_INFO *create_info,
                                 TABLE_LIST *table_list,
-                                List<create_field> *create_list,
-                                List<Key> *key_list, char *db,
+                                char *db,
                                 const char *table_name,
                                 uint fast_alter_partition);
-uint prep_alter_part_table(THD *thd, TABLE *table, ALTER_INFO *alter_info,
+uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
                            HA_CREATE_INFO *create_info,
                            handlerton *old_db_type,
                            bool *partition_changed,
@@ -1348,11 +1341,7 @@ typedef struct st_lock_param_type
   ulonglong deleted;
   THD *thd;
   HA_CREATE_INFO *create_info;
-  ALTER_INFO *alter_info;
-  List<create_field> *create_list;
-  List<create_field> new_create_list;
-  List<Key> *key_list;
-  List<Key> new_key_list;
+  Alter_info *alter_info;
   TABLE *table;
   KEY *key_info_buffer;
   const char *db;

--- 1.334/sql/sql_class.cc	2007-05-15 17:44:38 +04:00
+++ 1.335/sql/sql_class.cc	2007-05-28 15:29:56 +04:00
@@ -91,6 +91,40 @@ bool key_part_spec::operator==(const key
   return length == other.length && !strcmp(field_name, other.field_name);
 }
 
+/**
+  Construct an (almost) deep copy of this key. Only those
+  elements that are known to never change are not copied.
+  If out of memory, a partial copy is returned and an error is set
+  in THD.
+*/
+
+Key::Key(const Key &rhs, MEM_ROOT *mem_root)
+  :type(rhs.type),
+  key_create_info(rhs.key_create_info),
+  columns(rhs.columns, mem_root),
+  name(rhs.name),
+  generated(rhs.generated)
+{
+  list_copy_and_replace_each_value(columns, mem_root);
+}
+
+/**
+  Construct an (almost) deep copy of this foreign key. Only those
+  elements that are known to never change are not copied.
+  If out of memory, a partial copy is returned and an error is set
+  in THD.
+*/
+
+foreign_key::foreign_key(const foreign_key &rhs, MEM_ROOT *mem_root)
+  :Key(rhs),
+  ref_table(rhs.ref_table),
+  ref_columns(rhs.ref_columns),
+  delete_opt(rhs.delete_opt),
+  update_opt(rhs.update_opt),
+  match_opt(rhs.match_opt)
+{
+  list_copy_and_replace_each_value(ref_columns, mem_root);
+}
 
 /*
   Test if a foreign key (= generated key) is a prefix of the given key

--- 1.362/sql/sql_class.h	2007-05-18 00:27:08 +04:00
+++ 1.363/sql/sql_class.h	2007-05-28 15:29:56 +04:00
@@ -90,6 +90,17 @@ public:
   uint length;
   key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {}
   bool operator==(const key_part_spec& other) const;
+  /**
+    Construct a copy of this key_part_spec. field_name is copied
+    by-pointer as it is known to never change. At the same time
+    'length' may be reset in mysql_prepare_create_table, and this
+    is why we supply it with a copy.
+
+    @return If out of memory, 0 is returned and an error is set in
+    THD.
+  */
+  key_part_spec *clone(MEM_ROOT *mem_root) const
+  { return new (mem_root) key_part_spec(*this); }
 };
 
 
@@ -100,6 +111,12 @@ public:
   enum drop_type type;
   Alter_drop(enum drop_type par_type,const char *par_name)
     :name(par_name), type(par_type) {}
+  /**
+    Used to make a clone of this object for ALTER/CREATE TABLE
+    @sa comment for key_part_spec::clone
+  */
+  Alter_drop *clone(MEM_ROOT *mem_root) const
+    { return new (mem_root) Alter_drop(*this); }
 };
 
 
@@ -109,6 +126,12 @@ public:
   Item *def;
   Alter_column(const char *par_name,Item *literal)
     :name(par_name), def(literal) {}
+  /**
+    Used to make a clone of this object for ALTER/CREATE TABLE
+    @sa comment for key_part_spec::clone
+  */
+  Alter_column *clone(MEM_ROOT *mem_root) const
+    { return new (mem_root) Alter_column(*this); }
 };
 
 
@@ -127,9 +150,16 @@ public:
     :type(type_par), key_create_info(*key_info_arg), columns(cols),
     name(name_arg), generated(generated_arg)
   {}
-  ~Key() {}
+  Key(const Key &rhs, MEM_ROOT *mem_root);
+  virtual ~Key() {}
   /* Equality comparison of keys (ignoring name) */
   friend bool foreign_key_prefix(Key *a, Key *b);
+  /**
+    Used to make a clone of this object for ALTER/CREATE TABLE
+    @sa comment for key_part_spec::clone
+  */
+  virtual Key *clone(MEM_ROOT *mem_root) const
+    { return new (mem_root) Key(*this, mem_root); }
 };
 
 class Table_ident;
@@ -152,6 +182,13 @@ public:
     delete_opt(delete_opt_arg), update_opt(update_opt_arg),
     match_opt(match_opt_arg)
   {}
+  foreign_key(const foreign_key &rhs, MEM_ROOT *mem_root);
+  /**
+    Used to make a clone of this object for ALTER/CREATE TABLE
+    @sa comment for key_part_spec::clone
+  */
+  virtual Key *clone(MEM_ROOT *mem_root) const
+  { return new (mem_root) foreign_key(*this, mem_root); }
 };
 
 typedef struct st_mysql_lock
@@ -1944,20 +1981,20 @@ class select_insert :public select_resul
 class select_create: public select_insert {
   ORDER *group;
   TABLE_LIST *create_table;
-  List<create_field> *extra_fields;
-  List<Key> *keys;
   HA_CREATE_INFO *create_info;
+  Alter_info *alter_info;
   Field **field;
 public:
-  select_create (TABLE_LIST *table_arg,
-		 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, NULL, &select_fields, 0, 0, duplic, ignore),
-    create_table(table_arg), extra_fields(&fields_par),keys(&keys_par),
-    create_info(create_info_par)
-    {}
+  select_create(TABLE_LIST *table_arg,
+                HA_CREATE_INFO *create_info_arg,
+                Alter_info *alter_info_arg,
+                List<Item> &select_fields,
+                enum_duplicates duplic, bool ignore)
+    :select_insert(NULL, NULL, &select_fields, 0, 0, duplic, ignore),
+    create_table(table_arg),
+    create_info(create_info_arg),
+    alter_info(alter_info_arg)
+  {}
   int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
 
   void binlog_show_create_table(TABLE **tables, uint count);

--- 1.269/sql/sql_insert.cc	2007-05-22 23:41:36 +04:00
+++ 1.270/sql/sql_insert.cc	2007-05-28 15:29:56 +04:00
@@ -3138,11 +3138,11 @@ bool select_insert::send_eof()
                           temporary table flag)
       create_table in     Pointer to TABLE_LIST object providing database
                           and name for table to be created or to be open
-      extra_fields in/out Initial list of fields for table to be created
-      keys         in     List of keys for table to be created
+      alter_info   in/out Initial list of columns and indexes for the table
+                          to be created
       items        in     List of items which should be used to produce rest
                           of fields for the table (corresponding fields will
-                          be added to the end of 'extra_fields' list)
+                          be added to the end of alter_info->create_list)
       lock         out    Pointer to the MYSQL_LOCK object for table created
                           (or open temporary table) will be returned in this
                           parameter. Since this table is not included in
@@ -3171,8 +3171,7 @@ bool select_insert::send_eof()
 
 static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
                                       TABLE_LIST *create_table,
-                                      List<create_field> *extra_fields,
-                                      List<Key> *keys,
+                                      Alter_info *alter_info,
                                       List<Item> *items,
                                       MYSQL_LOCK **lock,
                                       TABLEOP_HOOKS *hooks)
@@ -3236,7 +3235,7 @@ static TABLE *create_table_from_items(TH
       DBUG_RETURN(0);
     if (item->maybe_null)
       cr_field->flags &= ~NOT_NULL_FLAG;
-    extra_fields->push_back(cr_field);
+    alter_info->create_list.push_back(cr_field);
   }
 
   DBUG_EXECUTE_IF("sleep_create_select_before_create", my_sleep(6000000););
@@ -3261,8 +3260,8 @@ static TABLE *create_table_from_items(TH
     tmp_disable_binlog(thd);
     if (!mysql_create_table_no_lock(thd, create_table->db,
                                     create_table->table_name,
-                                    create_info, *extra_fields, *keys, 0,
-                                    select_field_count, 0))
+                                    create_info, alter_info, 0,
+                                    select_field_count))
     {
 
       if (create_info->table_existed &&
@@ -3388,7 +3387,7 @@ select_create::prepare(List<Item> &value
   }
 
   if (!(table= create_table_from_items(thd, create_info, create_table,
-                                       extra_fields, keys, &values,
+                                       alter_info, &values,
                                        &thd->extra_lock, hook_ptr)))
     DBUG_RETURN(-1);				// abort() deletes table
 

--- 1.243/sql/sql_lex.cc	2007-05-25 00:41:18 +04:00
+++ 1.244/sql/sql_lex.cc	2007-05-28 15:29:56 +04:00
@@ -1118,6 +1118,34 @@ int MYSQLlex(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),
+  partition_names(rhs.partition_names, mem_root),
+  no_parts(rhs.no_parts)
+{
+  /*
+    Make deep copies of used objects.
+    This is not a fully deep copy - clone() implementations
+    of Alter_drop, Alter_column, Key, foreign_key, key_part_spec
+    do not copy string constants. At the same length the only
+    reason we make a copy currently is that ALTER/CREATE TABLE
+    code changes input Alter_info definitions, but string
+    constants never change.
+  */
+  list_copy_and_replace_each_value(drop_list, mem_root);
+  list_copy_and_replace_each_value(alter_list, mem_root);
+  list_copy_and_replace_each_value(key_list, mem_root);
+  list_copy_and_replace_each_value(create_list, mem_root);
+  /* partition_names are not deeply copied currently */
+}
+
+
 /*
   Skip comment in the end of statement.
 
@@ -2379,3 +2407,22 @@ bool st_select_lex::add_index_hint (THD 
                                             current_index_hint_clause,
                                             str, length));
 }
+
+/**
+  A routine used by the parser to decide whether we are specifying a full
+  partitioning or if only partitions to add or to split.
+
+  @note  This needs to be outside of WITH_PARTITION_STORAGE_ENGINE since it
+  is used from the sql parser that doesn't have any #ifdef's
+
+  @retval  TRUE    Yes, it is part of a management partition command
+  @retval  FALSE          No, not a management partition command
+*/
+
+bool st_lex::is_partition_management() const
+{
+  return (sql_command == SQLCOM_ALTER_TABLE &&
+          (alter_info.flags == ALTER_ADD_PARTITION ||
+           alter_info.flags == ALTER_REORGANIZE_PARTITION));
+}
+

--- 1.277/sql/sql_lex.h	2007-05-23 15:26:10 +04:00
+++ 1.278/sql/sql_lex.h	2007-05-28 15:29:56 +04:00
@@ -830,26 +830,60 @@ inline bool st_select_lex_unit::is_union
 #define ALTER_REMOVE_PARTITIONING (1L << 25)
 #define ALTER_FOREIGN_KEY         (1L << 26)
 
-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;
   List<char>                  partition_names;
   uint                        no_parts;
 
-  st_alter_info(){clear();}
-  void clear()
-  {
+  Alter_info() :
+    flags(0),
+    keys_onoff(LEAVE_AS_IS),
+    tablespace_op(NO_TABLESPACE_OP),
+    no_parts(0)
+  {}
+
+  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;
     no_parts= 0;
     partition_names.empty();
   }
-  void reset(){drop_list.empty();alter_list.empty();clear();}
-} ALTER_INFO;
+  /**
+    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.
+
+    @return You need to use check the error in THD 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
+};
 
 struct st_sp_chistics
 {
@@ -1103,8 +1137,6 @@ typedef struct st_lex : public Query_tab
   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;
@@ -1202,7 +1234,7 @@ typedef struct st_lex : public Query_tab
   bool safe_to_cache_query;
   bool subqueries, ignore;
   st_parsing_options parsing_options;
-  ALTER_INFO alter_info;
+  Alter_info alter_info;
   /* Prepared statements SQL syntax:*/
   LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
   /*
@@ -1361,6 +1393,7 @@ typedef struct st_lex : public Query_tab
   void restore_backup_query_tables_list(Query_tables_list *backup);
 
   bool table_or_sp_used();
+  bool is_partition_management() const;
 } LEX;
 
 struct st_lex_local: public st_lex

--- 1.12/sql/sql_list.cc	2006-12-30 23:02:07 +03:00
+++ 1.13/sql/sql_list.cc	2007-05-28 15:29:56 +04:00
@@ -36,3 +36,37 @@ void free_list(I_List <i_string> *list)
   while ((tmp= list->get()))
     delete tmp;
 }
+
+
+base_list::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;
+}

--- 1.46/sql/sql_list.h	2007-03-29 12:21:59 +04:00
+++ 1.47/sql/sql_list.h	2007-05-28 15:29:56 +04:00
@@ -1,3 +1,5 @@
+#ifndef INCLUDES_MYSQL_SQL_LIST_H
+#define INCLUDES_MYSQL_SQL_LIST_H
 /* Copyright (C) 2000-2003 MySQL AB
 
    This program is free software; you can redistribute it and/or modify
@@ -63,21 +65,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;
+  }
 };
 
 
@@ -93,12 +98,28 @@ 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= elements ? tmp.last : &first;
   }
+  /**
+    Construct a deep copy of the argument in memory root mem_root.
+    The elements themselves are copied by pointer. If you also
+    need to copy elements by value, you should employ
+    list_copy_and_replace_each_value after creating a copy.
+  */
+  base_list(const base_list &rhs, MEM_ROOT *mem_root);
   inline base_list(bool error) { }
   inline bool push_back(void *info)
   {
@@ -185,6 +206,15 @@ public:
       elements+= list->elements;
     }
   }
+  /**
+    Swap two lists.
+  */
+  inline void swap(base_list &rhs)
+  {
+    swap_variables(list_node *, first, rhs.first);
+    swap_variables(list_node **, last, rhs.last);
+    swap_variables(uint, elements, rhs.elements);
+  }
   inline list_node* last_node() { return *last; }
   inline list_node* first_node() { return first;}
   inline void *head() { return first->info; }
@@ -349,6 +379,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_back(T *a, MEM_ROOT *mem_root)
   { return base_list::push_back(a, mem_root); }
@@ -547,3 +579,32 @@ public:
   I_List_iterator(I_List<T> &a) : base_ilist_iterator(a) {}
   inline T* operator++(int) { return (T*) base_ilist_iterator::next(); }
 };
+
+/**
+  Make a deep copy of each list element.
+
+  @note A template function and not a template method of class List
+  is employed because of explicit template instantiation:
+  in server code there are explicit instantiations of List<T> and
+  an explicit instantiation of a template requires that any method
+  of the instantiated class used in the template can be resolved.
+  Evidently not all template arguments have clone() method with
+  the right signature.
+
+  @return You must query the error state in THD for out-of-memory
+  situation after calling this function.
+*/
+
+template <typename T>
+inline
+void
+list_copy_and_replace_each_value(List<T> &list, MEM_ROOT *mem_root)
+{
+  /* Make a deep copy of each element */
+  List_iterator<T> it(list);
+  T *el;
+  while ((el= it++))
+    it.replace(el->clone(mem_root));
+}
+
+#endif // INCLUDES_MYSQL_SQL_LIST_H

--- 1.671/sql/sql_parse.cc	2007-05-23 15:51:38 +04:00
+++ 1.672/sql/sql_parse.cc	2007-05-28 15:29:56 +04:00
@@ -2086,23 +2086,47 @@ mysql_execute_command(THD *thd)
     // Skip first table, which is the table we are creating
     TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local);
     TABLE_LIST *select_tables= lex->query_tables;
+    /*
+      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);
+    /*
+      We need to copy alter_info for the same reasons of re-execution
+      safety, only in case of Alter_info we have to do (almost) a deep
+      copy.
+    */
+    Alter_info alter_info(lex->alter_info, thd->mem_root);
+
+    if (thd->is_fatal_error)
+    {
+      /* If out of memory when creating a copy of alter_info. */
+      res= 1;
+      goto end_with_restore_list;
+    }
 
     if ((res= create_table_precheck(thd, select_tables, create_table)))
       goto end_with_restore_list;
 
+    /* Might have been updated in create_table_precheck */
+    create_info.alias= create_table->alias;
+
 #ifndef HAVE_READLINK
-    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;
 #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->table_name) ||
-	append_file_to_dir(thd, &lex->create_info.index_file_name,
+	append_file_to_dir(thd, &create_info.index_file_name,
 			   create_table->table_name))
       goto end_with_restore_list;
 #endif
@@ -2110,14 +2134,14 @@ mysql_execute_command(THD *thd)
       If we are using SET CHARSET without DEFAULT, add an implicit
       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
@@ -2156,7 +2180,7 @@ mysql_execute_command(THD *thd)
       select_lex->options|= SELECT_NO_UNLOCK;
       unit->set_limit(select_lex);
 
-      if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
+      if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
       {
         lex->link_first_table_back(create_table, link_to_local);
         create_table->create= TRUE;
@@ -2168,7 +2192,7 @@ mysql_execute_command(THD *thd)
           Is table which we are changing used somewhere in other parts
           of query
         */
-        if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
+        if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
         {
           TABLE_LIST *duplicate;
           create_table= lex->unlink_first_table(&link_to_local);
@@ -2180,10 +2204,10 @@ mysql_execute_command(THD *thd)
           }
         }
         /* If we create merge table, we have to test tables in merge, too */
-        if (lex->create_info.used_fields & HA_CREATE_USED_UNION)
+        if (create_info.used_fields & HA_CREATE_USED_UNION)
         {
           TABLE_LIST *tab;
-          for (tab= (TABLE_LIST*) lex->create_info.merge_list.first;
+          for (tab= (TABLE_LIST*) create_info.merge_list.first;
                tab;
                tab= tab->next_local)
           {
@@ -2198,18 +2222,15 @@ mysql_execute_command(THD *thd)
         }
 
         /*
-          FIXME Temporary hack which will go away once Kostja pushes
-                his uber-fix for ALTER/CREATE TABLE.
+          select_create is currently not re-execution friendly and
+          needs to be created for every execution of a PS/SP.
         */
-        lex->create_info.table_existed= 0;
-
         if ((result= new select_create(create_table,
-				       &lex->create_info,
-				       lex->create_list,
-				       lex->key_list,
-				       select_lex->item_list,
-				       lex->duplicates,
-				       lex->ignore)))
+                                       &create_info,
+                                       &alter_info,
+                                       select_lex->item_list,
+                                       lex->duplicates,
+                                       lex->ignore)))
         {
           /*
             CREATE from SELECT give its SELECT_LEX for SELECT,
@@ -2218,29 +2239,25 @@ mysql_execute_command(THD *thd)
           res= handle_select(thd, lex, result, 0);
           delete result;
         }
-	/* reset for PS */
-	lex->create_list.empty();
-	lex->key_list.empty();
       }
-      else if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
+      else if (!(create_info.options & HA_LEX_CREATE_TMP_TABLE))
         create_table= lex->unlink_first_table(&link_to_local);
 
     }
     else
     {
       /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */
-      if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)
+      if (create_info.options & HA_LEX_CREATE_TMP_TABLE)
         thd->options|= OPTION_KEEP_LOG;
       /* regular create */
-      if (lex->create_info.options & HA_LEX_CREATE_TABLE_LIKE)
+      if (create_info.options & HA_LEX_CREATE_TABLE_LIKE)
         res= mysql_create_like_table(thd, create_table, select_tables,
-                                     &lex->create_info);
+                                     &create_info);
       else
       {
         res= mysql_create_table(thd, create_table->db,
-				create_table->table_name, &lex->create_info,
-				lex->create_list,
-				lex->key_list, 0, 0, 1);
+                                create_table->table_name, &create_info,
+                                &alter_info, 0, 0);
       }
       if (!res)
 	send_ok(thd);
@@ -2252,15 +2269,46 @@ end_with_restore_list:
     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.
+
+    In the future ALTER TABLE will notice that the request is to
+    only add indexes and create these one by one for the existing
+    table without having to do a full rebuild.
+  */
+  {
+    /* Prepare stack copies to be re-execution safe */
+    HA_CREATE_INFO 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;
+
     DBUG_ASSERT(first_table == all_tables && first_table != 0);
     if (check_one_table_access(thd, INDEX_ACL, all_tables))
       goto error; /* purecov: inspected */
-    thd->enable_slow_log= opt_log_slow_admin_statements;
     if (end_active_trans(thd))
       goto error;
-    res= mysql_create_index(thd, first_table, lex->key_list);
-    break;
+    /*
+      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= 0;
+    create_info.row_type= ROW_TYPE_NOT_USED;
+    create_info.default_table_charset= thd->variables.collation_database;
+
+    res= mysql_alter_table(thd, first_table->db, first_table->table_name,
+                           &create_info, first_table, &alter_info,
+                           0, (ORDER*) 0, 0);
+    break;
+  }
 #ifdef HAVE_REPLICATION
   case SQLCOM_SLAVE_START:
   {
@@ -2303,10 +2351,21 @@ end_with_restore_list:
       ulong priv=0;
       ulong priv_needed= ALTER_ACL;
       /*
+        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;
+      /*
         We also require DROP priv for ALTER TABLE ... DROP PARTITION, as well
         as for RENAME TO, as being done by SQLCOM_RENAME_TABLE
       */
-      if (lex->alter_info.flags & (ALTER_DROP_PARTITION | ALTER_RENAME))
+      if (alter_info.flags & (ALTER_DROP_PARTITION | ALTER_RENAME))
         priv_needed|= DROP_ACL;
 
       /* Must be set in the parser */
@@ -2318,7 +2377,7 @@ end_with_restore_list:
                        is_schema_db(select_lex->db))||
 	  check_merge_table_access(thd, first_table->db,
 				   (TABLE_LIST *)
-				   lex->create_info.merge_list.first))
+				   create_info.merge_list.first))
 	goto error;				/* purecov: inspected */
       if (grant_option)
       {
@@ -2337,13 +2396,13 @@ end_with_restore_list:
 	}
       }
       /* 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))
 	goto error;
@@ -2357,12 +2416,12 @@ end_with_restore_list:
 
       thd->enable_slow_log= opt_log_slow_admin_statements;
       res= mysql_alter_table(thd, select_lex->db, lex->name.str,
-                             &lex->create_info,
-                             first_table, lex->create_list,
-                             lex->key_list,
+                             &create_info,
+                             first_table,
+                             &alter_info,
                              select_lex->order_list.elements,
                              (ORDER *) select_lex->order_list.first,
-                             lex->ignore, &lex->alter_info, 1);
+                             lex->ignore);
       break;
     }
   case SQLCOM_RENAME_TABLE:
@@ -2505,7 +2564,7 @@ end_with_restore_list:
       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, first_table, 1) :
+      mysql_recreate_table(thd, first_table) :
       mysql_optimize_table(thd, first_table, &lex->check_opt);
     /* ! we write after unlocking the table */
     if (!res && !lex->no_write_to_binlog)
@@ -2858,14 +2917,6 @@ end_with_restore_list:
 			lex->drop_temporary);
   }
   break;
-  case SQLCOM_DROP_INDEX:
-    DBUG_ASSERT(first_table == all_tables && first_table != 0);
-    if (check_one_table_access(thd, INDEX_ACL, all_tables))
-      goto error;				/* purecov: inspected */
-    if (end_active_trans(thd))
-      goto error;
-    res= mysql_drop_index(thd, first_table, &lex->alter_info);
-    break;
   case SQLCOM_SHOW_PROCESSLIST:
     if (!thd->security_ctx->priv_user[0] &&
         check_global_access(thd,PROCESS_ACL))
@@ -3005,6 +3056,12 @@ end_with_restore_list:
     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;
@@ -3037,7 +3094,7 @@ end_with_restore_list:
                      is_schema_db(lex->name.str)))
       break;
     res= mysql_create_db(thd,(lower_case_table_names == 2 ? alias :
-                              lex->name.str), &lex->create_info, 0);
+                              lex->name.str), &create_info, 0);
     break;
   }
   case SQLCOM_DROP_DB:
@@ -3130,6 +3187,7 @@ end_with_restore_list:
   case SQLCOM_ALTER_DB:
   {
     LEX_STRING *db= &lex->name;
+    HA_CREATE_INFO create_info(lex->create_info);
     if (check_db_name(db))
     {
       my_error(ER_WRONG_DB_NAME, MYF(0), db->str);
@@ -3159,7 +3217,7 @@ end_with_restore_list:
                  ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
       goto error;
     }
-    res= mysql_alter_db(thd, db->str, &lex->create_info);
+    res= mysql_alter_db(thd, db->str, &create_info);
     break;
   }
   case SQLCOM_SHOW_CREATE_DB:
@@ -3750,7 +3808,7 @@ create_sp_error:
             goto error;
         }
 
-	my_bool nsok= thd->net.no_send_ok;
+	my_bool save_no_send_ok= thd->net.no_send_ok;
 	thd->net.no_send_ok= TRUE;
 	if (sp->m_flags & sp_head::MULTI_RESULTS)
 	{
@@ -3761,7 +3819,7 @@ create_sp_error:
               back
             */
 	    my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
-	    thd->net.no_send_ok= nsok;
+	    thd->net.no_send_ok= save_no_send_ok;
 	    goto error;
 	  }
           /*
@@ -3777,7 +3835,7 @@ create_sp_error:
 	if (check_routine_access(thd, EXECUTE_ACL,
 				 sp->m_db.str, sp->m_name.str, TRUE, FALSE))
 	{
-	  thd->net.no_send_ok= nsok;
+	  thd->net.no_send_ok= save_no_send_ok;
 	  goto error;
 	}
 #endif
@@ -3802,7 +3860,7 @@ create_sp_error:
 
 	thd->variables.select_limit= select_limit;
 
-	thd->net.no_send_ok= nsok;
+	thd->net.no_send_ok= save_no_send_ok;
         thd->server_status&= ~bits_to_be_cleared;
 
 	if (!res)
@@ -5432,18 +5490,22 @@ bool add_field_to_list(THD *thd, LEX_STR
   }
   if (type_modifier & PRI_KEY_FLAG)
   {
+    Key *key;
     lex->col_list.push_back(new key_part_spec(field_name->str, 0));
-    lex->key_list.push_back(new Key(Key::PRIMARY, NullS,
-                                    &default_key_create_info,
-				    0, lex->col_list));
+    key= new Key(Key::PRIMARY, NullS,
+                      &default_key_create_info,
+                      0, lex->col_list);
+    lex->alter_info.key_list.push_back(key);
     lex->col_list.empty();
   }
   if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
   {
+    Key *key;
     lex->col_list.push_back(new key_part_spec(field_name->str, 0));
-    lex->key_list.push_back(new Key(Key::UNIQUE, NullS,
-                                    &default_key_create_info, 0,
-				    lex->col_list));
+    key= new Key(Key::UNIQUE, NullS,
+                 &default_key_create_info, 0,
+                 lex->col_list);
+    lex->alter_info.key_list.push_back(key);
     lex->col_list.empty();
   }
 
@@ -5503,7 +5565,7 @@ bool add_field_to_list(THD *thd, LEX_STR
                       interval_list, cs, uint_geom_type))
     DBUG_RETURN(1);
 
-  lex->create_list.push_back(new_field);
+  lex->alter_info.create_list.push_back(new_field);
   lex->last_field=new_field;
   DBUG_RETURN(0);
 }
@@ -6526,55 +6588,6 @@ Item * all_any_subquery_creator(Item *le
 
 
 /*
-  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.
-*/
-
-bool 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;
-  HA_CREATE_INFO create_info;
-  DBUG_ENTER("mysql_create_index");
-  bzero((char*) &create_info,sizeof(create_info));
-  create_info.db_type= 0;
-  create_info.default_table_charset= thd->variables.collation_database;
-  create_info.row_type= ROW_TYPE_NOT_USED;
-  DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name,
-				&create_info, table_list,
-				fields, keys, 0, (ORDER*)0,
-                                0, &alter_info, 1));
-}
-
-
-bool 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= 0;
-  create_info.default_table_charset= thd->variables.collation_database;
-  create_info.row_type= ROW_TYPE_NOT_USED;
-  alter_info->clear();
-  alter_info->flags= ALTER_DROP_INDEX;
-  DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->table_name,
-				&create_info, table_list,
-				fields, keys, 0, (ORDER*)0,
-                                0, alter_info, 1));
-}
-
-
-/*
   Multi update query pre-check
 
   SYNOPSIS
@@ -6885,7 +6898,6 @@ bool create_table_precheck(THD *thd, TAB
 
   want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ?
               CREATE_TMP_ACL : CREATE_ACL);
-  lex->create_info.alias= create_table->alias;
   if (check_access(thd, want_priv, create_table->db,
 		   &create_table->grant.privilege, 0, 0,
                    test(create_table->schema_table)) ||

--- 1.420/sql/sql_table.cc	2007-05-24 19:33:58 +04:00
+++ 1.421/sql/sql_table.cc	2007-05-28 15:29:57 +04:00
@@ -41,12 +41,16 @@ static int copy_data_between_tables(TABL
 
 static bool prepare_blob_field(THD *thd, create_field *sql_field);
 static bool check_engine(THD *, const char *, HA_CREATE_INFO *);
-static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
-                               List<create_field> *fields,
-                               List<Key> *keys, bool tmp_table,
-                               uint *db_options,
-                               handler *file, KEY **key_info_buffer,
-                               uint *key_count, int select_field_count);
+static bool
+mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
+                           Alter_info *alter_info,
+                           bool tmp_table,
+                           uint *db_options,
+                           handler *file, KEY **key_info_buffer,
+                           uint *key_count, int select_field_count);
+static bool
+mysql_prepare_alter_table(THD *thd, HA_CREATE_INFO *create_info,
+                          Alter_info *alter_info);
 
 #define MYSQL50_TABLE_NAME_PREFIX         "#mysql50#"
 #define MYSQL50_TABLE_NAME_PREFIX_LENGTH  9
@@ -230,107 +234,13 @@ uint build_tmptable_filename(THD* thd, c
   Return values for compare_tables().
   If you make compare_tables() non-static, move them to a header file.
 */
-#define ALTER_TABLE_DATA_CHANGED  1
-#define ALTER_TABLE_INDEX_CHANGED 2
 
-
-/*
-  SYNOPSIS
-    mysql_copy_create_list()
-    orig_create_list          Original list of created fields
-    inout::new_create_list    Copy of original list
-
-  RETURN VALUES
-    FALSE                     Success
-    TRUE                      Memory allocation error
-
-  DESCRIPTION
-    mysql_prepare_table destroys the create_list and in some cases we need
-    this lists for more purposes. Thus we copy it specifically for use
-    by mysql_prepare_table
-*/
-
-static int mysql_copy_create_list(List<create_field> *orig_create_list,
-                                  List<create_field> *new_create_list)
+enum enum_compare_tables_result
 {
-  List_iterator<create_field> prep_field_it(*orig_create_list);
-  create_field *prep_field;
-  DBUG_ENTER("mysql_copy_create_list");
-
-  while ((prep_field= prep_field_it++))
-  {
-    create_field *field= new create_field(*prep_field);
-    if (!field || new_create_list->push_back(field))
-    {
-      mem_alloc_error(2);
-      DBUG_RETURN(TRUE);
-    }
-  }
-  DBUG_RETURN(FALSE);
-}
-
-
-/*
-  SYNOPSIS
-    mysql_copy_key_list()
-    orig_key                  Original list of keys
-    inout::new_key            Copy of original list
-
-  RETURN VALUES
-    FALSE                     Success
-    TRUE                      Memory allocation error
-
-  DESCRIPTION
-    mysql_prepare_table destroys the key list and in some cases we need
-    this lists for more purposes. Thus we copy it specifically for use
-    by mysql_prepare_table
-*/
-
-static int mysql_copy_key_list(List<Key> *orig_key,
-                               List<Key> *new_key)
-{
-  List_iterator<Key> prep_key_it(*orig_key);
-  Key *prep_key;
-  DBUG_ENTER("mysql_copy_key_list");
-
-  while ((prep_key= prep_key_it++))
-  {
-    List<key_part_spec> prep_columns;
-    List_iterator<key_part_spec> prep_col_it(prep_key->columns);
-    key_part_spec *prep_col;
-    Key *temp_key;
-
-    while ((prep_col= prep_col_it++))
-    {
-      key_part_spec *prep_key_part;
-
-      if (!(prep_key_part= new key_part_spec(*prep_col)))
-      {
-        mem_alloc_error(sizeof(key_part_spec));
-        DBUG_RETURN(TRUE);
-      }
-      if (prep_columns.push_back(prep_key_part))
-      {
-        mem_alloc_error(2);
-        DBUG_RETURN(TRUE);
-      }
-    }
-    if (!(temp_key= new Key(prep_key->type, prep_key->name,
-                            &prep_key->key_create_info,
-                            prep_key->generated,
-                            prep_columns)))
-    {
-      mem_alloc_error(sizeof(Key));
-      DBUG_RETURN(TRUE);
-    }
-    if (new_key->push_back(temp_key))
-    {
-      mem_alloc_error(2);
-      DBUG_RETURN(TRUE);
-    }
-  }
-  DBUG_RETURN(FALSE);
-}
+  ALTER_TABLE_METADATA_ONLY= 0,
+  ALTER_TABLE_DATA_CHANGED= 1,
+  ALTER_TABLE_INDEX_CHANGED= 2
+};
 
 /*
 --------------------------------------------------------------------------
@@ -1304,19 +1214,14 @@ bool mysql_write_frm(ALTER_PARTITION_PAR
   strxmov(shadow_frm_name, shadow_path, reg_ext, NullS);
   if (flags & WFRM_WRITE_SHADOW)
   {
-    if (mysql_copy_create_list(lpt->create_list,
-                               &lpt->new_create_list) ||
-        mysql_copy_key_list(lpt->key_list,
-                            &lpt->new_key_list) ||
-        mysql_prepare_table(lpt->thd, lpt->create_info,
-                            &lpt->new_create_list,
-                            &lpt->new_key_list,
-                            /*tmp_table*/ 1,
-                            &lpt->db_options,
-                            lpt->table->file,
-                            &lpt->key_info_buffer,
-                            &lpt->key_count,
-                            /*select_field_count*/ 0))
+    if (mysql_prepare_create_table(lpt->thd, lpt->create_info,
+                                   lpt->alter_info,
+                                   /*tmp_table*/ 1,
+                                   &lpt->db_options,
+                                   lpt->table->file,
+                                   &lpt->key_info_buffer,
+                                   &lpt->key_count,
+                                   /*select_field_count*/ 0))
     {
       DBUG_RETURN(TRUE);
     }
@@ -1343,7 +1248,7 @@ bool mysql_write_frm(ALTER_PARTITION_PAR
     lpt->create_info->table_options= lpt->db_options;
     if ((mysql_create_frm(lpt->thd, shadow_frm_name, lpt->db,
                           lpt->table_name, lpt->create_info,
-                          lpt->new_create_list, lpt->key_count,
+                          lpt->alter_info->create_list, lpt->key_count,
                           lpt->key_info_buffer, lpt->table->file)) ||
          lpt->table->file->create_handler_files(shadow_path, NULL,
                                                 CHF_CREATE_FLAG,
@@ -2029,7 +1934,7 @@ int prepare_create_field(create_field *s
   DBUG_ENTER("prepare_field");
 
   /*
-    This code came from mysql_prepare_table.
+    This code came from mysql_prepare_create_table.
     Indent preserved to make patching easier
   */
   DBUG_ASSERT(sql_field->charset);
@@ -2130,7 +2035,8 @@ int prepare_create_field(create_field *s
     break;
   case MYSQL_TYPE_BIT:
     /* 
-      We have sql_field->pack_flag already set here, see mysql_prepare_table().
+      We have sql_field->pack_flag already set here, see
+      mysql_prepare_create_table().
     */
     break;
   case MYSQL_TYPE_NEWDECIMAL:
@@ -2179,11 +2085,10 @@ int prepare_create_field(create_field *s
   Preparation for table creation
 
   SYNOPSIS
-    mysql_prepare_table()
+    mysql_prepare_create_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
       tmp_table                 If a temporary table is to be created.
       db_options          INOUT Table options (like HA_OPTION_PACK_RECORD).
       file                      The handler for the new table.
@@ -2198,16 +2103,17 @@ int prepare_create_field(create_field *s
     sets create_info->varchar if the table has a varchar
 
   RETURN VALUES
-    0	ok
-    -1	error
+    FALSE    OK
+    TRUE     error
 */
 
-static int mysql_prepare_table(THD *thd, HA_CREATE_INFO *create_info,
-                               List<create_field> *fields,
-                               List<Key> *keys, bool tmp_table,
-                               uint *db_options,
-                               handler *file, KEY **key_info_buffer,
-                               uint *key_count, int select_field_count)
+static bool
+mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
+                           Alter_info *alter_info,
+                           bool tmp_table,
+                           uint *db_options,
+                           handler *file, KEY **key_info_buffer,
+                           uint *key_count, int select_field_count)
 {
   const char	*key_name;
   create_field	*sql_field,*dup_field;
@@ -2218,11 +2124,12 @@ static int mysql_prepare_table(THD *thd,
   int		timestamps= 0, timestamps_with_niladic= 0;
   int		field_no,dup_no;
   int		select_field_pos,auto_increment=0;
-  List_iterator<create_field> it(*fields),it2(*fields);
+  List_iterator<create_field> it(alter_info->create_list);
+  List_iterator<create_field> it2(alter_info->create_list);
   uint total_uneven_bit_length= 0;
-  DBUG_ENTER("mysql_prepare_table");
+  DBUG_ENTER("mysql_prepare_create_table");
 
-  select_field_pos= fields->elements - select_field_count;
+  select_field_pos= alter_info->create_list.elements - select_field_count;
   null_fields=blob_columns=0;
   create_info->varchar= 0;
   max_key_length= file->max_key_length();
@@ -2257,7 +2164,7 @@ static int mysql_prepare_table(THD *thd,
       strmake(strmake(tmp, save_cs->csname, sizeof(tmp)-4),
               STRING_WITH_LEN("_bin"));
       my_error(ER_UNKNOWN_COLLATION, MYF(0), tmp);
-      DBUG_RETURN(-1);
+      DBUG_RETURN(TRUE);
     }
 
     /*
@@ -2271,26 +2178,23 @@ static int mysql_prepare_table(THD *thd,
          sql_field->sql_type == MYSQL_TYPE_SET ||
          sql_field->sql_type == MYSQL_TYPE_ENUM))
     {
-      Query_arena backup_arena;
-      bool need_to_change_arena= !thd->stmt_arena->is_conventional();
-      if (need_to_change_arena)
-      {
-        /* Asser that we don't do that at every PS execute */
-        DBUG_ASSERT(thd->stmt_arena->is_first_stmt_execute() ||
-                    thd->stmt_arena->is_first_sp_execute());
-        thd->set_n_backup_active_arena(thd->stmt_arena, &backup_arena);
-      }
-
+      /*
+        Starting from 5.1 we work here with a copy of create_field
+        created by the caller, not with the instance that was
+        originally created during parsing. It's OK to create
+        a temporary item and initialize with it a member of the
+        copy -- this item will be thrown away along with the copy
+        at the end of execution, and thus not introduce a dangling
+        pointer in the parsed tree of a prepared statement or a
+        stored procedure statement.
+      */
       sql_field->def= sql_field->def->safe_charset_converter(save_cs);
 
-      if (need_to_change_arena)
-        thd->restore_active_arena(thd->stmt_arena, &backup_arena);
-
       if (sql_field->def == NULL)
       {
         /* Could not convert */
         my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
-        DBUG_RETURN(-1);
+        DBUG_RETURN(TRUE);
       }
     }
 
@@ -2309,12 +2213,11 @@ static int mysql_prepare_table(THD *thd,
       if (!interval)
       {
         /*
-          Create the typelib in prepared statement memory if we're
-          executing one.
+          Create the typelib in runtime memory - we will free the
+          occupied memory at the same time when we free this
+          sql_field -- at the end of execution.
         */
-        MEM_ROOT *stmt_root= thd->stmt_arena->mem_root;
-
-        interval= sql_field->interval= typelib(stmt_root,
+        interval= sql_field->interval= typelib(thd->mem_root,
                                                sql_field->interval_list);
         List_iterator<String> int_it(sql_field->interval_list);
         String conv, *tmp;
@@ -2331,7 +2234,7 @@ static int mysql_prepare_table(THD *thd,
           {
             uint cnv_errs;
             conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs);
-            interval->type_names[i]= strmake_root(stmt_root, conv.ptr(),
+            interval->type_names[i]= strmake_root(thd->mem_root, conv.ptr(),
                                                   conv.length());
             interval->type_lengths[i]= conv.length();
           }
@@ -2348,7 +2251,7 @@ static int mysql_prepare_table(THD *thd,
                                 comma_buf, comma_length, NULL, 0))
             {
               my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", tmp->ptr());
-              DBUG_RETURN(-1);
+              DBUG_RETURN(TRUE);
             }
           }
         }
@@ -2369,7 +2272,7 @@ static int mysql_prepare_table(THD *thd,
             if ((sql_field->flags & NOT_NULL_FLAG) != 0)
             {
               my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
-              DBUG_RETURN(-1);
+              DBUG_RETURN(TRUE);
             }
 
             /* else, NULL is an allowed value */
@@ -2385,7 +2288,7 @@ static int mysql_prepare_table(THD *thd,
           if (not_found)
           {
             my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
-            DBUG_RETURN(-1);
+            DBUG_RETURN(TRUE);
           }
         }
         calculate_interval_lengths(cs, interval, &dummy, &field_length);
@@ -2403,7 +2306,7 @@ static int mysql_prepare_table(THD *thd,
             if ((sql_field->flags & NOT_NULL_FLAG) != 0)
             {
               my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
-              DBUG_RETURN(-1);
+              DBUG_RETURN(TRUE);
             }
 
             /* else, the defaults yield the correct length for NULLs. */
@@ -2414,7 +2317,7 @@ static int mysql_prepare_table(THD *thd,
             if (find_type2(interval, def->ptr(), def->length(), cs) == 0) /* not found */
             {
               my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name);
-              DBUG_RETURN(-1);
+              DBUG_RETURN(TRUE);
             }
           }
         }
@@ -2435,7 +2338,7 @@ static int mysql_prepare_table(THD *thd,
 
     sql_field->create_length_to_internal_length();
     if (prepare_blob_field(thd, sql_field))
-      DBUG_RETURN(-1);
+      DBUG_RETURN(TRUE);
 
     if (!(sql_field->flags & NOT_NULL_FLAG))
       null_fields++;
@@ -2443,7 +2346,7 @@ static int mysql_prepare_table(THD *thd,
     if (check_column_name(sql_field->field_name))
     {
       my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name);
-      DBUG_RETURN(-1);
+      DBUG_RETURN(TRUE);
     }
 
     /* Check if we have used the same field name before */
@@ -2460,7 +2363,7 @@ static int mysql_prepare_table(THD *thd,
 	if (field_no < select_field_pos || dup_no >= select_field_pos)
 	{
 	  my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name);
-	  DBUG_RETURN(-1);
+	  DBUG_RETURN(TRUE);
 	}
 	else
 	{
@@ -2511,9 +2414,9 @@ static int mysql_prepare_table(THD *thd,
     if (prepare_create_field(sql_field, &blob_columns, 
 			     &timestamps, &timestamps_with_niladic,
 			     file->ha_table_flags()))
-      DBUG_RETURN(-1);
+      DBUG_RETURN(TRUE);
     if (sql_field->sql_type == MYSQL_TYPE_VARCHAR)
-      create_info->varchar= 1;
+      create_info->varchar= TRUE;
     sql_field->offset= record_offset;
     if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
       auto_increment++;
@@ -2523,31 +2426,32 @@ static int mysql_prepare_table(THD *thd,
   {
     my_message(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS,
                ER(ER_TOO_MUCH_AUTO_TIMESTAMP_COLS), MYF(0));
-    DBUG_RETURN(-1);
+    DBUG_RETURN(TRUE);
   }
   if (auto_increment > 1)
   {
     my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
-    DBUG_RETURN(-1);
+    DBUG_RETURN(TRUE);
   }
   if (auto_increment &&
       (file->ha_table_flags() & HA_NO_AUTO_INCREMENT))
   {
     my_message(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,
                ER(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT), MYF(0));
-    DBUG_RETURN(-1);
+    DBUG_RETURN(TRUE);
   }
 
   if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS))
   {
     my_message(ER_TABLE_CANT_HANDLE_BLOB, ER(ER_TABLE_CANT_HANDLE_BLOB),
                MYF(0));
-    DBUG_RETURN(-1);
+    DBUG_RETURN(TRUE);
   }
 
   /* 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;
@@ -2573,7 +2477,7 @@ static int mysql_prepare_table(THD *thd,
         my_error(ER_WRONG_FK_DEF, MYF(0),
                  (fk_key->name ?  fk_key->name : "foreign key without name"),
                  ER(ER_KEY_REF_DO_NOT_MATCH_TABLE_REF));
-	DBUG_RETURN(-1);
+	DBUG_RETURN(TRUE);
       }
       continue;
     }
@@ -2582,7 +2486,7 @@ static int mysql_prepare_table(THD *thd,
     if (key->columns.elements > tmp)
     {
       my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
-      DBUG_RETURN(-1);
+      DBUG_RETURN(TRUE);
     }
     key_name_str.str= (char*) key->name;
     key_name_str.length= key->name ? strlen(key->name) : 0;
@@ -2590,7 +2494,7 @@ static int mysql_prepare_table(THD *thd,
                                  system_charset_info, 1))
     {
       my_error(ER_TOO_LONG_IDENT, MYF(0), key->name);
-      DBUG_RETURN(-1);
+      DBUG_RETURN(TRUE);
     }
     key_iterator2.rewind ();
     if (key->type != Key::FOREIGN_KEY)
@@ -2630,20 +2534,20 @@ static int mysql_prepare_table(THD *thd,
 	!my_strcasecmp(system_charset_info,key->name,primary_key_name))
     {
       my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
-      DBUG_RETURN(-1);
+      DBUG_RETURN(TRUE);
     }
   }
   tmp=file->max_keys();
   if (*key_count > tmp)
   {
     my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
-    DBUG_RETURN(-1);
+    DBUG_RETURN(TRUE);
   }
 
   (*key_info_buffer)= key_info= (KEY*) sql_calloc(sizeof(KEY) * (*key_count));
   key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
   if (!*key_info_buffer || ! key_part_info)
-    DBUG_RETURN(-1);				// Out of memory
+    DBUG_RETURN(TRUE);				// Out of memory
 
   key_iterator.rewind();
   key_number=0;
@@ -2680,7 +2584,7 @@ static int mysql_prepare_table(THD *thd,
 #else
 	my_error(ER_FEATURE_DISABLED, MYF(0),
                  sym_group_geom.name, sym_group_geom.needed_define);
-	DBUG_RETURN(-1);
+	DBUG_RETURN(TRUE);
 #endif
     case Key::FOREIGN_KEY:
       key_number--;				// Skip this key
@@ -2703,7 +2607,7 @@ static int mysql_prepare_table(THD *thd,
       {
 	my_message(ER_TABLE_CANT_HANDLE_FT, ER(ER_TABLE_CANT_HANDLE_FT),
                    MYF(0));
-	DBUG_RETURN(-1);
+	DBUG_RETURN(TRUE);
       }
     }
     /*
@@ -2721,12 +2625,12 @@ static int mysql_prepare_table(THD *thd,
       {
         my_message(ER_TABLE_CANT_HANDLE_SPKEYS, ER(ER_TABLE_CANT_HANDLE_SPKEYS),
                    MYF(0));
-        DBUG_RETURN(-1);
+        DBUG_RETURN(TRUE);
       }
       if (key_info->key_parts != 1)
       {
 	my_error(ER_WRONG_ARGUMENTS, MYF(0), "SPATIAL INDEX");
-	DBUG_RETURN(-1);
+	DBUG_RETURN(TRUE);
       }
     }
     else if (key_info->algorithm == HA_KEY_ALG_RTREE)
@@ -2735,15 +2639,15 @@ static int mysql_prepare_table(THD *thd,
       if ((key_info->key_parts & 1) == 1)
       {
 	my_error(ER_WRONG_ARGUMENTS, MYF(0), "RTREE INDEX");
-	DBUG_RETURN(-1);
+	DBUG_RETURN(TRUE);
       }
       /* TODO: To be deleted */
       my_error(ER_NOT_SUPPORTED_YET, MYF(0), "RTREE INDEX");
-      DBUG_RETURN(-1);
+      DBUG_RETURN(TRUE);
 #else
       my_error(ER_FEATURE_DISABLED, MYF(0),
                sym_group_rtree.name, sym_group_rtree.needed_define);
-      DBUG_RETURN(-1);
+      DBUG_RETURN(TRUE);
 #endif
     }
 
@@ -2776,7 +2680,7 @@ static int mysql_prepare_table(THD *thd,
       if (!sql_field)
       {
 	my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name);
-	DBUG_RETURN(-1);
+	DBUG_RETURN(TRUE);
       }
       while ((dup_column= cols2++) != column)
       {
@@ -2786,7 +2690,7 @@ static int mysql_prepare_table(THD *thd,
 	  my_printf_error(ER_DUP_FIELDNAME,
 			  ER(ER_DUP_FIELDNAME),MYF(0),
 			  column->field_name);
-	  DBUG_RETURN(-1);
+	  DBUG_RETURN(TRUE);
 	}
       }
       cols2.rewind();
@@ -2819,7 +2723,7 @@ static int mysql_prepare_table(THD *thd,
         if (key->type == Key::SPATIAL && column->length)
         {
           my_error(ER_WRONG_SUB_KEY, MYF(0));
-	  DBUG_RETURN(-1);
+	  DBUG_RETURN(TRUE);
 	}
 
 	if (f_is_blob(sql_field->pack_flag) ||
@@ -2828,7 +2732,7 @@ static int mysql_prepare_table(THD *thd,
 	  if (!(file->ha_table_flags() & HA_CAN_INDEX_BLOBS))
 	  {
 	    my_error(ER_BLOB_USED_AS_KEY, MYF(0), column->field_name);
-	    DBUG_RETURN(-1);
+	    DBUG_RETURN(TRUE);
 	  }
           if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type ==
               Field::GEOM_POINT)
@@ -2836,7 +2740,7 @@ static int mysql_prepare_table(THD *thd,
 	  if (!column->length)
 	  {
 	    my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name);
-	    DBUG_RETURN(-1);
+	    DBUG_RETURN(TRUE);
 	  }
 	}
 #ifdef HAVE_SPATIAL
@@ -2867,13 +2771,13 @@ static int mysql_prepare_table(THD *thd,
             if (!(file->ha_table_flags() & HA_NULL_IN_KEY))
             {
               my_error(ER_NULL_COLUMN_IN_INDEX, MYF(0), column->field_name);
-              DBUG_RETURN(-1);
+              DBUG_RETURN(TRUE);
             }
             if (key->type == Key::SPATIAL)
             {
               my_message(ER_SPATIAL_CANT_HAVE_NULL,
                          ER(ER_SPATIAL_CANT_HAVE_NULL), MYF(0));
-              DBUG_RETURN(-1);
+              DBUG_RETURN(TRUE);
             }
           }
 	}
@@ -2909,7 +2813,7 @@ static int mysql_prepare_table(THD *thd,
 	    else
 	    {
 	      my_error(ER_TOO_LONG_KEY,MYF(0),length);
-	      DBUG_RETURN(-1);
+	      DBUG_RETURN(TRUE);
 	    }
 	  }
 	}
@@ -2922,7 +2826,7 @@ static int mysql_prepare_table(THD *thd,
 		    column->length != length)))
 	{
 	  my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0));
-	  DBUG_RETURN(-1);
+	  DBUG_RETURN(TRUE);
 	}
 	else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS))
 	  length=column->length;
@@ -2930,7 +2834,7 @@ static int mysql_prepare_table(THD *thd,
       else if (length == 0)
       {
 	my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name);
-	  DBUG_RETURN(-1);
+	  DBUG_RETURN(TRUE);
       }
       if (length > file->max_key_part_length() && key->type != Key::FULLTEXT)
       {
@@ -2949,7 +2853,7 @@ static int mysql_prepare_table(THD *thd,
 	else
 	{
 	  my_error(ER_TOO_LONG_KEY,MYF(0),length);
-	  DBUG_RETURN(-1);
+	  DBUG_RETURN(TRUE);
 	}
       }
       key_part_info->length=(uint16) length;
@@ -2978,7 +2882,7 @@ static int mysql_prepare_table(THD *thd,
 	  {
 	    my_message(ER_MULTIPLE_PRI_KEY, ER(ER_MULTIPLE_PRI_KEY),
                        MYF(0));
-	    DBUG_RETURN(-1);
+	    DBUG_RETURN(TRUE);
 	  }
 	  key_name=primary_key_name;
 	  primary_key=1;
@@ -2989,7 +2893,7 @@ static int mysql_prepare_table(THD *thd,
 	if (check_if_keyname_exists(key_name, *key_info_buffer, key_info))
 	{
 	  my_error(ER_DUP_KEYNAME, MYF(0), key_name);
-	  DBUG_RETURN(-1);
+	  DBUG_RETURN(TRUE);
 	}
 	key_info->name=(char*) key_name;
       }
@@ -2997,7 +2901,7 @@ static int mysql_prepare_table(THD *thd,
     if (!key_info->name || check_column_name(key_info->name))
     {
       my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key_info->name);
-      DBUG_RETURN(-1);
+      DBUG_RETURN(TRUE);
     }
     if (!(key_info->flags & HA_NULL_PART_KEY))
       unique_key=1;
@@ -3005,7 +2909,7 @@ static int mysql_prepare_table(THD *thd,
     if (key_length > max_key_length && key->type != Key::FULLTEXT)
     {
       my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length);
-      DBUG_RETURN(-1);
+      DBUG_RETURN(TRUE);
     }
     key_info++;
   }
@@ -3013,19 +2917,19 @@ static int mysql_prepare_table(THD *thd,
       (file->ha_table_flags() & HA_REQUIRE_PRIMARY_KEY))
   {
     my_message(ER_REQUIRES_PRIMARY_KEY, ER(ER_REQUIRES_PRIMARY_KEY), MYF(0));
-    DBUG_RETURN(-1);
+    DBUG_RETURN(TRUE);
   }
   if (auto_increment > 0)
   {
     my_message(ER_WRONG_AUTO_KEY, ER(ER_WRONG_AUTO_KEY), MYF(0));
-    DBUG_RETURN(-1);
+    DBUG_RETURN(TRUE);
   }
   /* Sort keys in optimized order */
   qsort((gptr) *key_info_buffer, *key_count, sizeof(KEY),
 	(qsort_cmp) sort_keys);
   create_info->null_bits= null_fields;
 
-  DBUG_RETURN(0);
+  DBUG_RETURN(FALSE);
 }
 
 
@@ -3116,7 +3020,8 @@ static bool prepare_blob_field(THD *thd,
 
 /*
   Preparation of create_field for SP function return values.
-  Based on code used in the inner loop of mysql_prepare_table() above
+  Based on code used in the inner loop of mysql_prepare_create_table()
+  above.
 
   SYNOPSIS
     sp_prepare_create_field()
@@ -3165,31 +3070,6 @@ void sp_prepare_create_field(THD *thd, c
 
 
 /*
-  Copy HA_CREATE_INFO struct
-  SYNOPSIS
-    copy_create_info()
-    lex_create_info         The create_info struct setup by parser
-  RETURN VALUES
-    > 0                     A pointer to a copy of the lex_create_info
-    0                       Memory allocation error
-  DESCRIPTION
-  Allocate memory for copy of HA_CREATE_INFO structure from parser
-  to ensure we can reuse the parser struct in stored procedures
-  and prepared statements.
-*/
-
-static HA_CREATE_INFO *copy_create_info(HA_CREATE_INFO *lex_create_info)
-{
-  HA_CREATE_INFO *create_info;
-  if (!(create_info= (HA_CREATE_INFO*)sql_alloc(sizeof(HA_CREATE_INFO))))
-    mem_alloc_error(sizeof(HA_CREATE_INFO));
-  else
-    memcpy((void*)create_info, (void*)lex_create_info, sizeof(HA_CREATE_INFO));
-  return create_info;
-}
-
-
-/*
   Create a table
 
   SYNOPSIS
@@ -3197,15 +3077,12 @@ static HA_CREATE_INFO *copy_create_info(
     thd			Thread object
     db			Database
     table_name		Table name
-    lex_create_info	Create information (like MAX_ROWS)
+    create_info	        Create information (like MAX_ROWS)
     fields		List of fields to create
     keys		List of keys to create
     internal_tmp_table  Set to 1 if this is an internal temporary table
 			(From ALTER TABLE)
-    select_field_count  
-    use_copy_create_info Should we make a copy of create info (we do this
-                         when this is called from sql_parse.cc where we
-                         want to ensure lex object isn't manipulated.
+    select_field_count
 
   DESCRIPTION
     If one creates a temporary table, this is automatically opened
@@ -3227,36 +3104,25 @@ static HA_CREATE_INFO *copy_create_info(
 
 bool mysql_create_table_no_lock(THD *thd,
                                 const char *db, const char *table_name,
-                                HA_CREATE_INFO *lex_create_info,
-                                List<create_field> &fields,
-                                List<Key> &keys,bool internal_tmp_table,
-                                uint select_field_count,
-                                bool use_copy_create_info)
+                                HA_CREATE_INFO *create_info,
+                                Alter_info *alter_info,
+                                bool internal_tmp_table,
+                                uint select_field_count)
 {
   char		path[FN_REFLEN];
   uint          path_length;
   const char	*alias;
   uint		db_options, key_count;
   KEY		*key_info_buffer;
-  HA_CREATE_INFO *create_info;
   handler	*file;
   bool		error= TRUE;
   DBUG_ENTER("mysql_create_table_no_lock");
   DBUG_PRINT("enter", ("db: '%s'  table: '%s'  tmp: %d",
                        db, table_name, internal_tmp_table));
 
-  if (use_copy_create_info)
-  {
-    if (!(create_info= copy_create_info(lex_create_info)))
-    {
-      DBUG_RETURN(TRUE);
-    }
-  }
-  else
-    create_info= lex_create_info;
- 
+
   /* Check for duplicate fields and check type of table to create */
-  if (!fields.elements)
+  if (!alter_info->create_list.elements)
   {
     my_message(ER_TABLE_MUST_HAVE_COLUMNS, ER(ER_TABLE_MUST_HAVE_COLUMNS),
                MYF(0));
@@ -3308,7 +3174,7 @@ bool mysql_create_table_no_lock(THD *thd
       Check that we don't use foreign keys in the table since it won't
       work even with InnoDB beneath it.
     */
-    List_iterator<Key> key_iterator(keys);
+    List_iterator<Key> key_iterator(alter_info->key_list);
     Key *key;
     handlerton *part_engine_type= create_info->db_type;
     char *part_syntax_buf;
@@ -3435,10 +3301,11 @@ bool mysql_create_table_no_lock(THD *thd
 
   set_table_default_charset(thd, create_info, (char*) db);
 
-  if (mysql_prepare_table(thd, create_info, &fields,
-			  &keys, internal_tmp_table, &db_options, file,
-			  &key_info_buffer, &key_count,
-			  select_field_count))
+  if (mysql_prepare_create_table(thd, create_info, alter_info,
+                                 internal_tmp_table,
+                                 &db_options, file,
+                                 &key_info_buffer, &key_count,
+                                 select_field_count))
     goto err;
 
       /* Check if table exists */
@@ -3546,7 +3413,8 @@ bool mysql_create_table_no_lock(THD *thd
   create_info->table_options=db_options;
 
   path[path_length - reg_ext_length]= '\0'; // Remove .frm extension
-  if (rea_create_table(thd, path, db, table_name, create_info, fields,
+  if (rea_create_table(thd, path, db, table_name,
+                       create_info, alter_info->create_list,
                        key_count, key_info_buffer, file))
     goto unlock_and_end;
 
@@ -3598,10 +3466,9 @@ warn:
 
 bool 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 internal_tmp_table,
-                        uint select_field_count,
-                        bool use_copy_create_info)
+                        Alter_info *alter_info,
+                        bool internal_tmp_table,
+                        uint select_field_count)
 {
   TABLE *name_lock= 0;
   bool result;
@@ -3651,9 +3518,9 @@ bool mysql_create_table(THD *thd, const 
   }
 
   result= mysql_create_table_no_lock(thd, db, table_name, create_info,
-                                     fields, keys, internal_tmp_table,
-                                     select_field_count,
-                                     use_copy_create_info);
+                                     alter_info,
+                                     internal_tmp_table,
+                                     select_field_count);
 
 unlock:
   if (name_lock)
@@ -4307,10 +4174,13 @@ static bool mysql_admin_table(THD* thd, 
           (table->table->file->ha_check_for_upgrade(check_opt) ==
            HA_ADMIN_NEEDS_ALTER))
       {
+        my_bool save_no_send_ok= thd->net.no_send_ok;
         ha_autocommit_or_rollback(thd, 1);
         close_thread_tables(thd);
         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);
         goto send_result;
       }
@@ -4389,6 +4259,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,
@@ -4400,7 +4271,9 @@ send_result_message:
                  *save_next_global= table->next_global;
       table->next_local= table->next_global= 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);
       ha_autocommit_or_rollback(thd, 0);
       close_thread_tables(thd);
@@ -4680,7 +4553,7 @@ bool mysql_preload_keys(THD* thd, TABLE_
 */
 
 bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
-                             HA_CREATE_INFO *lex_create_info)
+                             HA_CREATE_INFO *create_info)
 {
   TABLE *name_lock= 0;
   char src_path[FN_REFLEN], dst_path[FN_REFLEN];
@@ -4690,17 +4563,12 @@ bool mysql_create_like_table(THD* thd, T
   int  err;
   bool res= TRUE;
   uint not_used;
-  HA_CREATE_INFO *create_info;
 #ifdef WITH_PARTITION_STORAGE_ENGINE
   char tmp_path[FN_REFLEN];
 #endif
   char ts_name[FN_LEN];
   DBUG_ENTER("mysql_create_like_table");
 
-  if (!(create_info= copy_create_info(lex_create_info)))
-  {
-    DBUG_RETURN(TRUE);
-  }
 
   /* CREATE TABLE ... LIKE is not allowed for views. */
   src_table->required_type= FRMTYPE_TABLE;
@@ -5006,12 +4874,18 @@ err:
   SYNOPSIS
     compare_tables()
       table                     The original table.
-      create_list               The fields for the new table.
-      key_info_buffer           An array of KEY structs for the new indexes.
-      key_count                 The number of elements in the array.
+      alter_info                Alter options, fields and keys for the new
+                                table.
       create_info               Create options for the new table.
-      alter_info                Alter options.
       order_num                 Number of order list elements.
+      need_copy_table     OUT   Result of the comparison. Undefined if error.
+                                Otherwise is one of:
+                                ALTER_TABLE_METADATA_ONLY  No copy needed
+                                ALTER_TABLE_DATA_CHANGED   Data changes,
+                                                           copy needed
+                                ALTER_TABLE_INDEX_CHANGED  Index changes,
+                                                           copy might be needed
+      key_info_buffer     OUT   An array of KEY structs for new indexes
       index_drop_buffer   OUT   An array of offsets into table->key_info.
       index_drop_count    OUT   The number of elements in the array.
       index_add_buffer    OUT   An array of offsets into key_info_buffer.
@@ -5033,27 +4907,69 @@ err:
     that need to be dropped and/or (re-)created.
 
   RETURN VALUES
-    0                           No copy needed
-    ALTER_TABLE_DATA_CHANGED    Data changes, copy needed
-    ALTER_TABLE_INDEX_CHANGED   Index changes, copy might be needed
+    TRUE   error
+    FALSE  success
 */
 
-static uint compare_tables(TABLE *table, List<create_field> *create_list,
-                           KEY *key_info_buffer, uint key_count,
-                           HA_CREATE_INFO *create_info,
-                           ALTER_INFO *alter_info, uint order_num,
-                           uint *index_drop_buffer, uint *index_drop_count,
-                           uint *index_add_buffer, uint *index_add_count,
-                           bool varchar)
+static
+bool
+compare_tables(TABLE *table,
+               Alter_info *alter_info,
+               HA_CREATE_INFO *create_info,
+               uint order_num,
+               enum enum_compare_tables_result *need_copy_table,
+               KEY **key_info_buffer,
+               uint **index_drop_buffer, uint *index_drop_count,
+               uint **index_add_buffer, uint *index_add_count)
 {
   Field **f_ptr, *field;
   uint changes= 0, tmp;
-  List_iterator_fast<create_field> new_field_it(*create_list);
+  uint key_count;
+  List_iterator_fast<create_field> new_field_it(alter_info->create_list);
   create_field *new_field;
   KEY_PART_INFO *key_part;
   KEY_PART_INFO *end;
+  /*
+    Remember if the new definition has new VARCHAR column;
+    create_info->varchar will be reset in mysql_prepare_create_table.
+  */
+  bool varchar= create_info->varchar;
   DBUG_ENTER("compare_tables");
 
+  {
+    THD *thd= table->in_use;
+    /*
+      Create a copy of alter_info.
+      To compare the new and old table definitions, we need to "prepare"
+      the new definition - transform it from parser output to a format
+      that describes the final table layout (all column defaults are
+      initialized, duplicate columns are removed). This is done by
+      mysql_prepare_create_table.  Unfortunately,
+      mysql_prepare_create_table performs its transformations
+      "in-place", that is, modifies the argument.  Since we would
+      like to keep compare_tables() idempotent (not altering any
+      of the arguments) we create a copy of alter_info here and
+      pass it to mysql_prepare_create_table, then use the result
+      to evaluate possibility of fast ALTER TABLE, and then
+      destroy the copy.
+    */
+    Alter_info tmp_alter_info(*alter_info, thd->mem_root);
+    uint db_options= 0; /* not used */
+    /* Create the prepared information. */
+    if (mysql_prepare_create_table(thd, create_info,
+                                   &tmp_alter_info,
+                                   (table->s->tmp_table != NO_TMP_TABLE),
+                                   &db_options,
+                                   table->file, key_info_buffer,
+                                   &key_count, 0))
+      DBUG_RETURN(1);
+    /* Allocate result buffers. */
+    if (! (*index_drop_buffer=
+           (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
+        ! (*index_add_buffer=
+           (uint*) thd->alloc(sizeof(uint) * tmp_alter_info.key_list.elements)))
+      DBUG_RETURN(1);
+  }
   /*
     Some very basic checks. If number of fields changes, or the
     handler, we need to run full ALTER TABLE. In the future
@@ -5080,7 +4996,7 @@ static uint compare_tables(TABLE *table,
     prior to 5.0 branch.
     See BUG#6236.
   */
-  if (table->s->fields != create_list->elements ||
+  if (table->s->fields != alter_info->create_list.elements ||
       table->s->db_type() != create_info->db_type ||
       table->s->tmp_table ||
       create_info->used_fields & HA_CREATE_USED_ENGINE ||
@@ -5090,7 +5006,10 @@ static uint compare_tables(TABLE *table,
       order_num ||
       !table->s->mysql_version ||
       (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
-    DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
+  {
+    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+    DBUG_RETURN(0);
+  }
 
   /*
     Go through fields and check if the original ones are compatible
@@ -5106,7 +5025,10 @@ static uint compare_tables(TABLE *table,
     /* Check that NULL behavior is same for old and new fields */
     if ((new_field->flags & NOT_NULL_FLAG) !=
 	(uint) (field->flags & NOT_NULL_FLAG))
-      DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
+    {
+      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+      DBUG_RETURN(0);
+    }
 
     /* Don't pack rows in old tables if the user has requested this. */
     if (create_info->row_type == ROW_TYPE_DYNAMIC ||
@@ -5124,7 +5046,10 @@ static uint compare_tables(TABLE *table,
 
     /* Evaluate changes bitmap and send to check_if_incompatible_data() */
     if (!(tmp= field->is_equal(new_field)))
-      DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
+    {
+      *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+      DBUG_RETURN(0);
+    }
     // Clear indexed marker
     field->flags&= ~FIELD_IN_ADD_INDEX;
     changes|= tmp;
@@ -5137,7 +5062,7 @@ static uint compare_tables(TABLE *table,
   KEY *table_key;
   KEY *table_key_end= table->key_info + table->s->keys;
   KEY *new_key;
-  KEY *new_key_end= key_info_buffer + key_count;
+  KEY *new_key_end= *key_info_buffer + key_count;
 
   DBUG_PRINT("info", ("index count old: %d  new: %d",
                       table->s->keys, key_count));
@@ -5153,7 +5078,7 @@ static uint compare_tables(TABLE *table,
     KEY_PART_INFO *new_part;
 
     /* Search a new key with the same name. */
-    for (new_key= key_info_buffer; new_key < new_key_end; new_key++)
+    for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
     {
       if (! strcmp(table_key->name, new_key->name))
         break;
@@ -5161,7 +5086,7 @@ static uint compare_tables(TABLE *table,
     if (new_key >= new_key_end)
     {
       /* Key not found. Add the offset of the key to the drop buffer. */
-      index_drop_buffer[(*index_drop_count)++]= table_key - table->key_info;
+      (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
       DBUG_PRINT("info", ("index dropped: '%s'", table_key->name));
       continue;
     }
@@ -5194,8 +5119,8 @@ static uint compare_tables(TABLE *table,
 
   index_changed:
     /* Key modified. Add the offset of the key to both buffers. */
-    index_drop_buffer[(*index_drop_count)++]= table_key - table->key_info;
-    index_add_buffer[(*index_add_count)++]= new_key - key_info_buffer;
+    (*index_drop_buffer)[(*index_drop_count)++]= table_key - table->key_info;
+    (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
     key_part= new_key->key_part;
     end= key_part + new_key->key_parts;
     for(; key_part != end; key_part++)
@@ -5211,7 +5136,7 @@ static uint compare_tables(TABLE *table,
   /*
     Step through all keys of the new table and find matching old keys.
   */
-  for (new_key= key_info_buffer; new_key < new_key_end; new_key++)
+  for (new_key= *key_info_buffer; new_key < new_key_end; new_key++)
   {
     /* Search an old key with the same name. */
     for (table_key= table->key_info; table_key < table_key_end; table_key++)
@@ -5222,7 +5147,7 @@ static uint compare_tables(TABLE *table,
     if (table_key >= table_key_end)
     {
       /* Key not found. Add the offset of the key to the add buffer. */
-      index_add_buffer[(*index_add_count)++]= new_key - key_info_buffer;
+      (*index_add_buffer)[(*index_add_count)++]= new_key - *key_info_buffer;
       key_part= new_key->key_part;
       end= key_part + new_key->key_parts;
       for(; key_part != end; key_part++)
@@ -5237,12 +5162,19 @@ static uint compare_tables(TABLE *table,
 
   /* Check if changes are compatible with current handler without a copy */
   if (table->file->check_if_incompatible_data(create_info, changes))
-    DBUG_RETURN(ALTER_TABLE_DATA_CHANGED);
+  {
+    *need_copy_table= ALTER_TABLE_DATA_CHANGED;
+    DBUG_RETURN(0);
+  }
 
   if (*index_drop_count || *index_add_count)
-    DBUG_RETURN(ALTER_TABLE_INDEX_CHANGED);
+  {
+    *need_copy_table= ALTER_TABLE_INDEX_CHANGED;
+    DBUG_RETURN(0);
+  }
 
-  DBUG_RETURN(0); // Tables are compatible
+  *need_copy_table= ALTER_TABLE_METADATA_ONLY; // Tables are compatible
+  DBUG_RETURN(0);
 }
 
 
@@ -5294,6 +5226,391 @@ bool alter_table_manage_keys(TABLE *tabl
 }
 
 
+/**
+  Prepare column and key definitions for CREATE TABLE in ALTER TABLE.
+
+  This function transforms parse output of ALTER TABLE - lists of
+  columns and keys to add, drop or modify into, essentially,
+  CREATE TABLE definition - a list of columns and keys of the new
+  table. While doing so, it also performs some (bug not all)
+  semantic checks.
+
+  This function is invoked when we know that we're going to
+  perform ALTER TABLE via a temporary table -- i.e. fast ALTER TABLE
+  is not possible, perhaps because the ALTER statement contains
+  instructions that require change in table data, not only in
+  table definition or indexes.
+
+  @param[in,out]  thd         thread handle. Used as a memory pool
+                              and source of environment information.
+  @param[in]      table       the source table, open and locked
+                              Used as an interface to the storage engine
+                              to acquire additional information about
+                              the original table.
+  @param[in,out]  create_info A blob with CREATE/ALTER TABLE
+                              parameters
+  @param[in,out]  alter_info  Another blob with ALTER/CREATE parameters.
+                              Originally create_info was used only in
+                              CREATE TABLE and alter_info only in ALTER TABLE.
+                              But since ALTER might end-up doing CREATE,
+                              this distinction is gone and we just carry
+                              around two structures.
+
+  @return
+    Fills various create_info members based on information retrieved
+    from the storage engine.
+    Sets create_info->varchar if the table has a VARCHAR column.
+    Prepares alter_info->create_list and alter_info->key_list with
+    columns and keys of the new table.
+  @retval TRUE   error, out of memory or a semantical error in ALTER
+                 TABLE instructions
+  @retval FALSE  success
+*/
+
+static bool
+mysql_prepare_alter_table(THD *thd, TABLE *table,
+                          HA_CREATE_INFO *create_info,
+                          Alter_info *alter_info)
+{
+  /* New column definitions are added here */
+  List<create_field> new_create_list;
+  /* New key definitions are added here */
+  List<Key> new_key_list;
+  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
+  List_iterator<create_field> def_it(alter_info->create_list);
+  List_iterator<Alter_column> alter_it(alter_info->alter_list);
+  List_iterator<Key> key_it(alter_info->key_list);
+  List_iterator<create_field> find_it(new_create_list);
+  List_iterator<create_field> field_it(new_create_list);
+  List<key_part_spec> key_parts;
+  uint db_create_options= (table->s->db_create_options
+                           & ~(HA_OPTION_PACK_RECORD));
+  uint used_fields= create_info->used_fields;
+  KEY *key_info=table->key_info;
+  bool rc= TRUE;
+
+  DBUG_ENTER("mysql_prepare_alter_table");
+
+  create_info->varchar= FALSE;
+  /* Let new create options override the old ones */
+  if (!(used_fields & HA_CREATE_USED_MIN_ROWS))
+    create_info->min_rows= table->s->min_rows;
+  if (!(used_fields & HA_CREATE_USED_MAX_ROWS))
+    create_info->max_rows= table->s->max_rows;
+  if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH))
+    create_info->avg_row_length= table->s->avg_row_length;
+  if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
+    create_info->default_table_charset= table->s->table_charset;
+  if (!(used_fields & HA_CREATE_USED_AUTO) && table->found_next_number_field)
+  {
+    /* Table has an autoincrement, copy value to new table */
+    table->file->info(HA_STATUS_AUTO);
+    create_info->auto_increment_value= table->file->stats.auto_increment_value;
+  }
+  if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
+    create_info->key_block_size= table->s->key_block_size;
+
+  if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
+  {
+    char *tablespace= thd->alloc(FN_LEN);
+    /*
+       Regular alter table of disk stored table (no tablespace/storage change)
+       Copy tablespace name
+    */
+    if (tablespace &&
+        (table->file->get_tablespace_name(thd, tablespace, FN_LEN)))
+      create_info->tablespace= tablespace;
+  }
+  restore_record(table, s->default_values);     // Empty record for DEFAULT
+  create_field *def;
+
+  /*
+    First collect all fields from table which isn't in drop_list
+  */
+  Field **f_ptr,*field;
+  for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
+  {
+    if (field->type() == MYSQL_TYPE_STRING)
+      create_info->varchar= TRUE;
+    /* Check if field should be dropped */
+    Alter_drop *drop;
+    drop_it.rewind();
+    while ((drop=drop_it++))
+    {
+      if (drop->type == Alter_drop::COLUMN &&
+	  !my_strcasecmp(system_charset_info,field->field_name, drop->name))
+      {
+	/* Reset auto_increment value if it was dropped */
+	if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER &&
+	    !(used_fields & HA_CREATE_USED_AUTO))
+	{
+	  create_info->auto_increment_value=0;
+	  create_info->used_fields|=HA_CREATE_USED_AUTO;
+	}
+	break;
+      }
+    }
+    if (drop)
+    {
+      drop_it.remove();
+      continue;
+    }
+    /* Check if field is changed */
+    def_it.rewind();
+    while ((def=def_it++))
+    {
+      if (def->change &&
+	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
+	break;
+    }
+    if (def)
+    {						// Field is changed
+      def->field=field;
+      if (!def->after)
+      {
+	new_create_list.push_back(def);
+	def_it.remove();
+      }
+    }
+    else
+    {
+      /*
+        This field was not dropped and not changed, add it to the list
+        for the new table.
+      */
+      def= new create_field(field, field);
+      new_create_list.push_back(def);
+      alter_it.rewind();			// Change default if ALTER
+      Alter_column *alter;
+      while ((alter=alter_it++))
+      {
+	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
+	  break;
+      }
+      if (alter)
+      {
+	if (def->sql_type == MYSQL_TYPE_BLOB)
+	{
+	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
+          goto err;
+	}
+	if ((def->def=alter->def))              // Use new default
+          def->flags&= ~NO_DEFAULT_VALUE_FLAG;
+        else
+          def->flags|= NO_DEFAULT_VALUE_FLAG;
+	alter_it.remove();
+      }
+    }
+  }
+  def_it.rewind();
+  while ((def=def_it++))			// Add new columns
+  {
+    if (def->change && ! def->field)
+    {
+      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table->s->table_name);
+      goto err;
+    }
+    if (!def->after)
+      new_create_list.push_back(def);
+    else if (def->after == first_keyword)
+      new_create_list.push_front(def);
+    else
+    {
+      create_field *find;
+      find_it.rewind();
+      while ((find=find_it++))			// Add new columns
+      {
+	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
+	  break;
+      }
+      if (!find)
+      {
+	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name);
+        goto err;
+      }
+      find_it.after(def);			// Put element after this
+    }
+  }
+  if (alter_info->alter_list.elements)
+  {
+    my_error(ER_BAD_FIELD_ERROR, MYF(0),
+             alter_info->alter_list.head()->name, table->s->table_name);
+    goto err;
+  }
+  if (!new_create_list.elements)
+  {
+    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
+               MYF(0));
+    goto err;
+  }
+
+  /*
+    Collect all keys which isn't in drop list. Add only those
+    for which some fields exists.
+  */
+
+  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
+  {
+    char *key_name= key_info->name;
+    Alter_drop *drop;
+    drop_it.rewind();
+    while ((drop=drop_it++))
+    {
+      if (drop->type == Alter_drop::KEY &&
+	  !my_strcasecmp(system_charset_info,key_name, drop->name))
+	break;
+    }
+    if (drop)
+    {
+      drop_it.remove();
+      continue;
+    }
+
+    KEY_PART_INFO *key_part= key_info->key_part;
+    key_parts.empty();
+    for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
+    {
+      if (!key_part->field)
+	continue;				// Wrong field (from UNIREG)
+      const char *key_part_name=key_part->field->field_name;
+      create_field *cfield;
+      field_it.rewind();
+      while ((cfield=field_it++))
+      {
+	if (cfield->change)
+	{
+	  if (!my_strcasecmp(system_charset_info, key_part_name,
+			     cfield->change))
+	    break;
+	}
+	else if (!my_strcasecmp(system_charset_info,
+				key_part_name, cfield->field_name))
+	  break;
+      }
+      if (!cfield)
+	continue;				// Field is removed
+      uint key_part_length=key_part->length;
+      if (cfield->field)			// Not new field
+      {
+        /*
+          If the field can't have only a part used in a key according to its
+          new type, or should not be used partially according to its
+          previous type, or the field length is less than the key part
+          length, unset the key part length.
+
+          We also unset the key part length if it is the same as the
+          old field's length, so the whole new field will be used.
+
+          BLOBs may have cfield->length == 0, which is why we test it before
+          checking whether cfield->length < key_part_length (in chars).
+         */
+        if (!Field::type_can_have_key_part(cfield->field->type()) ||
+            !Field::type_can_have_key_part(cfield->sql_type) ||
+            /* spatial keys can't have sub-key length */
+            (key_info->flags & HA_SPATIAL) ||
+            (cfield->field->field_length == key_part_length &&
+             !f_is_blob(key_part->key_type)) ||
+	    (cfield->length && (cfield->length < key_part_length /
+                                key_part->field->charset()->mbmaxlen)))
+	  key_part_length= 0;			// Use whole field
+      }
+      key_part_length /= key_part->field->charset()->mbmaxlen;
+      key_parts.push_back(new key_part_spec(cfield->field_name,
+					    key_part_length));
+    }
+    if (key_parts.elements)
+    {
+      KEY_CREATE_INFO key_create_info;
+      Key *key;
+      enum Key::Keytype key_type;
+      bzero((char*) &key_create_info, sizeof(key_create_info));
+
+      key_create_info.algorithm= key_info->algorithm;
+      if (key_info->flags & HA_USES_BLOCK_SIZE)
+        key_create_info.block_size= key_info->block_size;
+      if (key_info->flags & HA_USES_PARSER)
+        key_create_info.parser_name= *key_info->parser_name;
+
+      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_create_info,
+                   test(key_info->flags & HA_GENERATED_KEY),
+                   key_parts);
+      new_key_list.push_back(key);
+    }
+  }
+  {
+    Key *key;
+    while ((key=key_it++))			// Add new keys
+    {
+      if (key->type != Key::FOREIGN_KEY)
+        new_key_list.push_back(key);
+      if (key->name &&
+	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
+      {
+	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
+        goto err;
+      }
+    }
+  }
+
+  if (alter_info->drop_list.elements)
+  {
+    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
+             alter_info->drop_list.head()->name);
+    goto err;
+  }
+  if (alter_info->alter_list.elements)
+  {
+    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
+             alter_info->alter_list.head()->name);
+    goto err;
+  }
+
+  if (!create_info->comment.str)
+  {
+    create_info->comment.str= table->s->comment.str;
+    create_info->comment.length= table->s->comment.length;
+  }
+
+  table->file->update_create_info(create_info);
+  if ((create_info->table_options &
+       (HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) ||
+      (used_fields & HA_CREATE_USED_PACK_KEYS))
+    db_create_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS);
+  if (create_info->table_options &
+      (HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM))
+    db_create_options&= ~(HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM);
+  if (create_info->table_options &
+      (HA_OPTION_DELAY_KEY_WRITE | HA_OPTION_NO_DELAY_KEY_WRITE))
+    db_create_options&= ~(HA_OPTION_DELAY_KEY_WRITE |
+			  HA_OPTION_NO_DELAY_KEY_WRITE);
+  create_info->table_options|= db_create_options;
+
+  if (table->s->tmp_table)
+    create_info->options|=HA_LEX_CREATE_TMP_TABLE;
+
+  rc= FALSE;
+  alter_info->create_list.swap(new_create_list);
+  alter_info->key_list.swap(new_key_list);
+err:
+  DBUG_RETURN(rc);
+}
+
+
 /*
   Alter table
 
@@ -5302,21 +5619,14 @@ bool alter_table_manage_keys(TABLE *tabl
       thd              Thread handle
       new_db           If there is a RENAME clause
       new_name         If there is a RENAME clause
-      lex_create_info  Information from the parsing phase. Since some
-                       clauses are common to CREATE and ALTER TABLE, the
-                       data is stored in lex->create_info. The non-common
-                       is stored in lex->alter_info.
+      create_info      Information from the parsing phase about new
+                       table properties.
       table_list       The table to change.
-      fields           lex->create_list - List of fields to be changed,
-                       added or dropped.
-      keys             lex->key_list - List of keys to be changed, added or
-                       dropped.
+      alter_info       Lists of fields, keys to be changed, added
+                       or dropped.
       order_num        How many ORDER BY fields has been specified.
       order            List of fields to ORDER BY.
       ignore           Whether we have ALTER IGNORE TABLE
-      alter_info       Information from the parsing phase specific to ALTER
-                       TABLE and not shared with CREATE TABLE.
-      do_send_ok       Whether to call send_ok() on success.
 
   DESCRIPTION
     This is a veery long function and is everything but the kitchen sink :)
@@ -5328,15 +5638,15 @@ bool alter_table_manage_keys(TABLE *tabl
     the table and/or enabling/disabling the keys. In this case, the FRM is
     not changed, directly by mysql_alter_table. However, if there is a
     RENAME + change of a field, or an index, the short cut is not used.
-    See how `fields` is used to generate the new FRM regarding the structure
-    of the fields. The same is done for the indices of the table.
+    See how `create_list` is used to generate the new FRM regarding the
+    structure of the fields. The same is done for the indices of the table.
 
     Important is the fact, that this function tries to do as little work as
     possible, by finding out whether a intermediate table is needed to copy
     data into and when finishing the altering to use it as the original table.
     For this reason the function compare_tables() is called, which decides
     based on all kind of data how similar are the new and the original
-    tables. 
+    tables.
 
   RETURN VALUES
     FALSE  OK
@@ -5344,36 +5654,28 @@ bool alter_table_manage_keys(TABLE *tabl
 */
 
 bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
-                       HA_CREATE_INFO *lex_create_info,
+                       HA_CREATE_INFO *create_info,
                        TABLE_LIST *table_list,
-                       List<create_field> &fields, List<Key> &keys,
-                       uint order_num, ORDER *order, bool ignore,
-                       ALTER_INFO *alter_info, bool do_send_ok)
+                       Alter_info *alter_info,
+                       uint order_num, ORDER *order, bool ignore)
 {
   TABLE *table, *new_table= 0, *name_lock= 0;
   int error= 0;
   char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
   char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
-  char index_file[FN_REFLEN], data_file[FN_REFLEN], tablespace[FN_LEN];
+  char index_file[FN_REFLEN], data_file[FN_REFLEN];
   char path[FN_REFLEN];
   char reg_path[FN_REFLEN+1];
   ha_rows copied,deleted;
-  uint db_create_options, used_fields;
   handlerton *old_db_type, *new_db_type, *save_old_db_type;
   legacy_db_type table_type;
-  HA_CREATE_INFO *create_info;
   frm_type_enum frm_type;
-  uint need_copy_table= 0;
-  bool varchar= FALSE;
+  enum_compare_tables_result need_copy_table= ALTER_TABLE_METADATA_ONLY;
 #ifdef WITH_PARTITION_STORAGE_ENGINE
   uint fast_alter_partition= 0;
   bool partition_changed= FALSE;
 #endif
-  List<create_field> prepared_create_list;
-  List<Key>          prepared_key_list;
   bool need_lock_for_indexes= TRUE;
-  uint db_options= 0;
-  uint key_count;
   KEY  *key_info_buffer;
   uint index_drop_count;
   uint *index_drop_buffer;
@@ -5387,6 +5689,12 @@ bool mysql_alter_table(THD *thd,char *ne
   LINT_INIT(index_add_buffer);
   LINT_INIT(index_drop_buffer);
 
+  /*
+    Check if we attempt to alter mysql.slow_log or
+    mysql.general_log table and return an error if
+    it is the case.
+    TODO: this design is obsolete and will be removed.
+  */
   if (table_list && table_list->db && table_list->table_name)
   {
     int table_kind= 0;
@@ -5404,20 +5712,21 @@ bool mysql_alter_table(THD *thd,char *ne
 
     /* Disable alter of log tables to unsupported engine */
     if (table_kind &&
-        (lex_create_info->used_fields & HA_CREATE_USED_ENGINE) &&
-        (!lex_create_info->db_type || /* unknown engine */
-        !(lex_create_info->db_type->flags & HTON_SUPPORT_LOG_TABLES)))
+        (create_info->used_fields & HA_CREATE_USED_ENGINE) &&
+        (!create_info->db_type || /* unknown engine */
+        !(create_info->db_type->flags & HTON_SUPPORT_LOG_TABLES)))
     {
       my_error(ER_UNSUPORTED_LOG_ENGINE, MYF(0));
       DBUG_RETURN(TRUE);
     }
   }
 
+  /*
+    Assign variables table_name, new_name, db, new_db, path, reg_path
+    to simplify further comparisions: we want to see if it's a RENAME
+    later just by comparing the pointers, avoiding the need for strcmp.
+  */
   thd->proc_info="init";
-  if (!(create_info= copy_create_info(lex_create_info)))
-  {
-    DBUG_RETURN(TRUE);
-  }
   table_name=table_list->table_name;
   alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
   db=table_list->db;
@@ -5426,7 +5735,6 @@ bool mysql_alter_table(THD *thd,char *ne
   build_table_filename(reg_path, sizeof(reg_path), db, table_name, reg_ext, 0);
   build_table_filename(path, sizeof(path), db, table_name, "", 0);
 
-  used_fields=create_info->used_fields;
 
   mysql_ha_flush(thd, table_list, MYSQL_HA_CLOSE_FINAL, FALSE);
 
@@ -5440,8 +5748,22 @@ bool mysql_alter_table(THD *thd,char *ne
   (void) unpack_filename(new_name_buff, new_name_buff);
   if (lower_case_table_names != 2)
     my_casedn_str(files_charset_info, new_name_buff);
+  /*
+    If this is just a rename of a view, short cut to the
+    following scenario: 1) lock LOCK_open 2) do a RENAME
+    2) unlock LOCK_open.
+    This is a copy-paste added to make sure
+    ALTER (sic:) TABLE .. RENAME works for views. ALTER VIEW is handled
+    as an independent branch in mysql_execute_command. The need
+    for a copy-paste arose because the main code flow of ALTER TABLE
+    ... RENAME tries to use open_ltable, which does not work for views
+    (open_ltable was never modified to merge table lists of child tables
+    into the main table list, like open_tables does).
+    This code is wrong and will be removed, please do not copy.
+  */
   frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
   /* Rename a view */
+  /* Sic: there is a race here */
   if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME))
   {
     /*
@@ -5487,18 +5809,6 @@ view_err:
     DBUG_RETURN(TRUE);
   table->use_all_columns();
 
-  List_iterator<Alter_drop> drop_it(alter_info->drop_list);
-  List_iterator<create_field> def_it(fields);
-  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
-  List_iterator<create_field> find_it(create_list);
-  List_iterator<Key> key_it(keys);
-  List_iterator<create_field> field_it(create_list);
-  List<key_part_spec> key_parts;
-
-  KEY *key_info=table->key_info;
-
   /* Check that we are not trying to rename to an existing table */
   if (new_name)
   {
@@ -5581,14 +5891,17 @@ view_err:
       create_info->db_type= old_db_type;
   }
 
-#ifdef WITH_PARTITION_STORAGE_ENGINE
-  if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
-                            &partition_changed, &fast_alter_partition))
-    goto err;
-#endif
   if (check_engine(thd, new_name, create_info))
     goto err;
   new_db_type= create_info->db_type;
+
+  if (new_db_type != old_db_type &&
+      !table->file->can_switch_engines())
+  {
+    my_error(ER_ROW_IS_REFERENCED, MYF(0));
+    goto err;
+  }
+
   if (create_info->row_type == ROW_TYPE_NOT_USED)
     create_info->row_type= table->s->row_type;
 
@@ -5704,8 +6017,7 @@ view_err:
     if (!error)
     {
       write_bin_log(thd, TRUE, thd->query, thd->query_length);
-      if (do_send_ok)
-        send_ok(thd);
+      send_ok(thd);
     }
     else if (error > 0)
     {
@@ -5720,387 +6032,48 @@ view_err:
     DBUG_RETURN(error);
   }
 
-  /* We have to do full alter table */
-
-  /* Let new create options override the old ones */
-  if (!(used_fields & HA_CREATE_USED_MIN_ROWS))
-    create_info->min_rows= table->s->min_rows;
-  if (!(used_fields & HA_CREATE_USED_MAX_ROWS))
-    create_info->max_rows= table->s->max_rows;
-  if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH))
-    create_info->avg_row_length= table->s->avg_row_length;
-  if (!(used_fields & HA_CREATE_USED_DEFAULT_CHARSET))
-    create_info->default_table_charset= table->s->table_charset;
-  if (!(used_fields & HA_CREATE_USED_AUTO) && table->found_next_number_field)
-  {
-    /* Table has an autoincrement, copy value to new table */
-    table->file->info(HA_STATUS_AUTO);
-    create_info->auto_increment_value= table->file->stats.auto_increment_value;
-  }
-  if (!(used_fields & HA_CREATE_USED_KEY_BLOCK_SIZE))
-    create_info->key_block_size= table->s->key_block_size;
-
-  if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY)
-  {
-    /* 
-       Regular alter table of disk stored table (no tablespace/storage change)
-       Copy tablespace name
-    */
-    if ((table->file->get_tablespace_name(thd, tablespace, FN_LEN)))
-      create_info->tablespace= tablespace;
-  }
-  restore_record(table, s->default_values);     // Empty record for DEFAULT
-  create_field *def;
-
-  /*
-    First collect all fields from table which isn't in drop_list
-  */
+  /* We have to do full alter table. */
 
-  Field **f_ptr,*field;
-  for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
-  {
-    if (field->type() == MYSQL_TYPE_STRING)
-      varchar= TRUE;
-    /* Check if field should be dropped */
-    Alter_drop *drop;
-    drop_it.rewind();
-    while ((drop=drop_it++))
-    {
-      if (drop->type == Alter_drop::COLUMN &&
-	  !my_strcasecmp(system_charset_info,field->field_name, drop->name))
-      {
-	/* Reset auto_increment value if it was dropped */
-	if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER &&
-	    !(used_fields & HA_CREATE_USED_AUTO))
-	{
-	  create_info->auto_increment_value=0;
-	  create_info->used_fields|=HA_CREATE_USED_AUTO;
-	}
-	break;
-      }
-    }
-    if (drop)
-    {
-      drop_it.remove();
-      continue;
-    }
-    /* Check if field is changed */
-    def_it.rewind();
-    while ((def=def_it++))
-    {
-      if (def->change &&
-	  !my_strcasecmp(system_charset_info,field->field_name, def->change))
-	break;
-    }
-    if (def)
-    {						// Field is changed
-      def->field=field;
-      if (!def->after)
-      {
-	create_list.push_back(def);
-	def_it.remove();
-      }
-    }
-    else
-    {
-      /*
-        This field was not dropped and not changed, add it to the list
-        for the new table.
-      */
-      create_list.push_back(def=new create_field(field,field));
-      alter_it.rewind();			// Change default if ALTER
-      Alter_column *alter;
-      while ((alter=alter_it++))
-      {
-	if (!my_strcasecmp(system_charset_info,field->field_name, alter->name))
-	  break;
-      }
-      if (alter)
-      {
-	if (def->sql_type == MYSQL_TYPE_BLOB)
-	{
-	  my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change);
-          goto err;
-	}
-	if ((def->def=alter->def))              // Use new default
-          def->flags&= ~NO_DEFAULT_VALUE_FLAG;
-        else
-          def->flags|= NO_DEFAULT_VALUE_FLAG;
-	alter_it.remove();
-      }
-    }
-  }
-  def_it.rewind();
-  while ((def=def_it++))			// Add new columns
-  {
-    if (def->change && ! def->field)
-    {
-      my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, table_name);
-      goto err;
-    }
-    if (!def->after)
-      create_list.push_back(def);
-    else if (def->after == first_keyword)
-      create_list.push_front(def);
-    else
-    {
-      create_field *find;
-      find_it.rewind();
-      while ((find=find_it++))			// Add new columns
-      {
-	if (!my_strcasecmp(system_charset_info,def->after, find->field_name))
-	  break;
-      }
-      if (!find)
-      {
-	my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table_name);
-        goto err;
-      }
-      find_it.after(def);			// Put element after this
-    }
-  }
-  if (alter_info->alter_list.elements)
-  {
-    my_error(ER_BAD_FIELD_ERROR, MYF(0),
-             alter_info->alter_list.head()->name, table_name);
-    goto err;
-  }
-  if (!create_list.elements)
-  {
-    my_message(ER_CANT_REMOVE_ALL_FIELDS, ER(ER_CANT_REMOVE_ALL_FIELDS),
-               MYF(0));
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+  if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
+                            &partition_changed, &fast_alter_partition))
     goto err;
-  }
-
+#endif
   /*
-    Collect all keys which isn't in drop list. Add only those
-    for which some fields exists.
+    If the old table had partitions and we are doing ALTER TABLE ...
+    engine= <new_engine>, the new table must preserve the original
+    partitioning. That means that the new engine is still the
+    partitioning engine, not the engine specified in the parser.
+    This is discovered  in prep_alter_part_table, which in such case
+    updates create_info->db_type.
+    Now we need to update the stack copy of create_info->db_type,
+    as otherwise we won't be able to correctly move the files of the
+    temporary table to the result table files.
   */
+  new_db_type= create_info->db_type;
 
-  for (uint i=0 ; i < table->s->keys ; i++,key_info++)
-  {
-    char *key_name= key_info->name;
-    Alter_drop *drop;
-    drop_it.rewind();
-    while ((drop=drop_it++))
-    {
-      if (drop->type == Alter_drop::KEY &&
-	  !my_strcasecmp(system_charset_info,key_name, drop->name))
-	break;
-    }
-    if (drop)
-    {
-      drop_it.remove();
-      continue;
-    }
-
-    KEY_PART_INFO *key_part= key_info->key_part;
-    key_parts.empty();
-    for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
-    {
-      if (!key_part->field)
-	continue;				// Wrong field (from UNIREG)
-      const char *key_part_name=key_part->field->field_name;
-      create_field *cfield;
-      field_it.rewind();
-      while ((cfield=field_it++))
-      {
-	if (cfield->change)
-	{
-	  if (!my_strcasecmp(system_charset_info, key_part_name,
-			     cfield->change))
-	    break;
-	}
-	else if (!my_strcasecmp(system_charset_info,
-				key_part_name, cfield->field_name))
-	  break;
-      }
-      if (!cfield)
-	continue;				// Field is removed
-      uint key_part_length=key_part->length;
-      if (cfield->field)			// Not new field
-      {
-        /*
-          If the field can't have only a part used in a key according to its
-          new type, or should not be used partially according to its
-          previous type, or the field length is less than the key part
-          length, unset the key part length.
-
-          We also unset the key part length if it is the same as the
-          old field's length, so the whole new field will be used.
-
-          BLOBs may have cfield->length == 0, which is why we test it before
-          checking whether cfield->length < key_part_length (in chars).
-         */
-        if (!Field::type_can_have_key_part(cfield->field->type()) ||
-            !Field::type_can_have_key_part(cfield->sql_type) ||
-            /* spatial keys can't have sub-key length */
-            (key_info->flags & HA_SPATIAL) ||
-            (cfield->field->field_length == key_part_length &&
-             !f_is_blob(key_part->key_type)) ||
-	    (cfield->length && (cfield->length < key_part_length /
-                                key_part->field->charset()->mbmaxlen)))
-	  key_part_length= 0;			// Use whole field
-      }
-      key_part_length /= key_part->field->charset()->mbmaxlen;
-      key_parts.push_back(new key_part_spec(cfield->field_name,
-					    key_part_length));
-    }
-    if (key_parts.elements)
-    {
-      KEY_CREATE_INFO key_create_info;
-      bzero((char*) &key_create_info, sizeof(key_create_info));
-
-      key_create_info.algorithm= key_info->algorithm;
-      if (key_info->flags & HA_USES_BLOCK_SIZE)
-        key_create_info.block_size= key_info->block_size;
-      if (key_info->flags & HA_USES_PARSER)
-        key_create_info.parser_name= *key_info->parser_name;
-
-      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_create_info,
-                                 test(key_info->flags & HA_GENERATED_KEY),
-				 key_parts));
-    }
-  }
-  {
-    Key *key;
-    while ((key=key_it++))			// Add new keys
-    {
-      if (key->type != Key::FOREIGN_KEY)
-	key_list.push_back(key);
-      if (key->name &&
-	  !my_strcasecmp(system_charset_info,key->name,primary_key_name))
-      {
-	my_error(ER_WRONG_NAME_FOR_INDEX, MYF(0), key->name);
-        goto err;
-      }
-    }
-  }
-
-  if (alter_info->drop_list.elements)
-  {
-    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
-             alter_info->drop_list.head()->name);
+  if (mysql_prepare_alter_table(thd, table, create_info, alter_info))
     goto err;
-  }
-  if (alter_info->alter_list.elements)
-  {
-    my_error(ER_CANT_DROP_FIELD_OR_KEY, MYF(0),
-             alter_info->alter_list.head()->name);
-    goto err;
-  }
-
-  db_create_options= table->s->db_create_options & ~(HA_OPTION_PACK_RECORD);
-  my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
-	      current_pid, thd->thread_id);
-  /* Safety fix for innodb */
-  if (lower_case_table_names)
-    my_casedn_str(files_charset_info, tmp_name);
-  if (new_db_type != old_db_type && !table->file->can_switch_engines()) {
-    my_error(ER_ROW_IS_REFERENCED, MYF(0));
-    goto err;
-  }
-  create_info->db_type=new_db_type;
-  if (!create_info->comment.str)
-  {
-    create_info->comment.str= table->s->comment.str;
-    create_info->comment.length= table->s->comment.length;
-  }
-
-  table->file->update_create_info(create_info);
-  if ((create_info->table_options &
-       (HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) ||
-      (used_fields & HA_CREATE_USED_PACK_KEYS))
-    db_create_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS);
-  if (create_info->table_options &
-      (HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM))
-    db_create_options&= ~(HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM);
-  if (create_info->table_options &
-      (HA_OPTION_DELAY_KEY_WRITE | HA_OPTION_NO_DELAY_KEY_WRITE))
-    db_create_options&= ~(HA_OPTION_DELAY_KEY_WRITE |
-			  HA_OPTION_NO_DELAY_KEY_WRITE);
-  create_info->table_options|= db_create_options;
-
-  if (table->s->tmp_table)
-    create_info->options|=HA_LEX_CREATE_TMP_TABLE;
 
   set_table_default_charset(thd, create_info, db);
 
-  {
-    /*
-      For some purposes we need prepared table structures and translated
-      key descriptions with proper default key name assignment.
-
-      Unfortunately, mysql_prepare_table() modifies the field and key
-      lists. mysql_create_table() needs the unmodified lists. Hence, we
-      need to copy the lists and all their elements. The lists contain
-      pointers to the elements only.
-
-      We cannot copy conditionally because the partition code always
-      needs prepared lists and compare_tables() needs them and is almost
-      always called.
-    */
-
-    /* Copy fields. */
-    List_iterator<create_field> prep_field_it(create_list);
-    create_field *prep_field;
-    while ((prep_field= prep_field_it++))
-      prepared_create_list.push_back(new create_field(*prep_field));
-
-    /* Copy keys and key parts. */
-    List_iterator<Key> prep_key_it(key_list);
-    Key *prep_key;
-    while ((prep_key= prep_key_it++))
-    {
-      List<key_part_spec> prep_columns;
-      List_iterator<key_part_spec> prep_col_it(prep_key->columns);
-      key_part_spec *prep_col;
-
-      while ((prep_col= prep_col_it++))
-        prep_columns.push_back(new key_part_spec(*prep_col));
-      prepared_key_list.push_back(new Key(prep_key->type, prep_key->name,
-                                          &prep_key->key_create_info,
-                                          prep_key->generated, prep_columns));
-    }
-
-    /* Create the prepared information. */
-    if (mysql_prepare_table(thd, create_info, &prepared_create_list,
-                            &prepared_key_list,
-                            (table->s->tmp_table != NO_TMP_TABLE), &db_options,
-                            table->file, &key_info_buffer, &key_count, 0))
-      goto err;
-  }
-
   if (thd->variables.old_alter_table
       || (table->s->db_type() != create_info->db_type)
 #ifdef WITH_PARTITION_STORAGE_ENGINE
       || partition_changed
 #endif
      )
-    need_copy_table= 1;
+    need_copy_table= ALTER_TABLE_DATA_CHANGED;
   else
   {
-    /* Try to optimize ALTER TABLE. Allocate result buffers. */
-    if (! (index_drop_buffer=
-           (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
-        ! (index_add_buffer=
-           (uint*) thd->alloc(sizeof(uint) * prepared_key_list.elements)))
-      goto err;
     /* Check how much the tables differ. */
-    need_copy_table= compare_tables(table, &prepared_create_list,
-                                    key_info_buffer, key_count,
-                                    create_info, alter_info, order_num,
-                                    index_drop_buffer, &index_drop_count,
-                                    index_add_buffer, &index_add_count,
-                                    varchar);
+    if (compare_tables(table, alter_info,
+                       create_info, order_num,
+                       &need_copy_table,
+                       &key_info_buffer,
+                       &index_drop_buffer, &index_drop_count,
+                       &index_add_buffer, &index_add_count))
+      goto err;
   }
 
   /*
@@ -6198,13 +6171,13 @@ view_err:
       if ((alter_flags & needed_online_flags) == needed_online_flags)
       {
         /* All required online flags are present. */
-        need_copy_table= 0;
+        need_copy_table= ALTER_TABLE_METADATA_ONLY;
         need_lock_for_indexes= FALSE;
       }
       else if ((alter_flags & needed_fast_flags) == needed_fast_flags)
       {
         /* All required fast flags are present. */
-        need_copy_table= 0;
+        need_copy_table= ALTER_TABLE_METADATA_ONLY;
       }
     }
     DBUG_PRINT("info", ("need_copy_table: %u  need_lock: %d",
@@ -6216,7 +6189,7 @@ view_err:
     alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|...
     so that ALTER TABLE won't break when somebody will add new flag
   */
-  if (!need_copy_table)
+  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
     create_info->frm_only= 1;
 
 #ifdef WITH_PARTITION_STORAGE_ENGINE
@@ -6225,12 +6198,17 @@ view_err:
     DBUG_ASSERT(!name_lock);
     DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
                                            create_info, table_list,
-                                           &create_list, &key_list,
                                            db, table_name,
                                            fast_alter_partition));
   }
 #endif
 
+  my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix,
+	      current_pid, thd->thread_id);
+  /* Safety fix for innodb */
+  if (lower_case_table_names)
+    my_casedn_str(files_charset_info, tmp_name);
+
   /*
     Handling of symlinked tables:
     If no rename:
@@ -6284,14 +6262,15 @@ view_err:
   */
   tmp_disable_binlog(thd);
   error= mysql_create_table_no_lock(thd, new_db, tmp_name,
-                                    create_info, create_list,
-                                    key_list, 1, 0, 0);
+                                    create_info,
+                                    alter_info,
+                                    1, 0);
   reenable_binlog(thd);
   if (error)
     goto err;
 
   /* Open the table if we need to copy the data. */
-  if (need_copy_table)
+  if (need_copy_table != ALTER_TABLE_METADATA_ONLY)
   {
     if (table->s->tmp_table)
     {
@@ -6326,9 +6305,10 @@ view_err:
     /* We don't want update TIMESTAMP fields during ALTER TABLE. */
     new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
     new_table->next_number_field=new_table->found_next_number_field;
-    error=copy_data_between_tables(table, new_table, create_list, ignore,
-                                   order_num, order, &copied, &deleted,
-                                   alter_info->keys_onoff);
+    error= copy_data_between_tables(table, new_table,
+                                    alter_info->create_list, ignore,
+                                    order_num, order, &copied, &deleted,
+                                    alter_info->keys_onoff);
   }
   else
   {
@@ -6358,26 +6338,6 @@ view_err:
     table->file->prepare_for_alter();
     if (index_add_count)
     {
-#ifdef XXX_TO_BE_DONE_LATER_BY_WL3020_AND_WL1892
-      if (! need_lock_for_indexes)
-      {
-        /* Downgrade the write lock. */
-        mysql_lock_downgrade_write(thd, table, TL_WRITE_ALLOW_WRITE);
-      }
-
-      /* Create a new .frm file for crash recovery. */
-      /* TODO: Must set INDEX_TO_BE_ADDED flags in the frm file. */
-      VOID(pthread_mutex_lock(&LOCK_open));
-      error= (mysql_create_frm(thd, reg_path, db, table_name,
-                               create_info, prepared_create_list, key_count,
-                               key_info_buffer, table->file) ||
-              table->file->create_handler_files(reg_path, NULL, CHF_INDEX_FLAG,
-                                                create_info));
-      VOID(pthread_mutex_unlock(&LOCK_open));
-      if (error)
-        goto err1;
-#endif
-
       /* The add_index() method takes an array of KEY structs. */
       key_info= (KEY*) thd->alloc(sizeof(KEY) * index_add_count);
       key= key_info;
@@ -6410,36 +6370,6 @@ view_err:
 
     if (index_drop_count)
     {
-#ifdef XXX_TO_BE_DONE_LATER_BY_WL3020_AND_WL1892
-      /* Create a new .frm file for crash recovery. */
-      /* TODO: Must set INDEX_IS_ADDED in the frm file. */
-      /* TODO: Must set INDEX_TO_BE_DROPPED in the frm file. */
-      VOID(pthread_mutex_lock(&LOCK_open));
-      error= (mysql_create_frm(thd, reg_path, db, table_name,
-                               create_info, prepared_create_list, key_count,
-                               key_info_buffer, table->file) ||
-              table->file->create_handler_files(reg_path, NULL, CHF_INDEX_FLAG,
-                                                create_info));
-      VOID(pthread_mutex_unlock(&LOCK_open));
-      if (error)
-        goto err1;
-
-      if (! need_lock_for_indexes)
-      {
-        LOCK_PARAM_TYPE lpt;
-
-        lpt.thd= thd;
-        lpt.table= table;
-        lpt.db= db;
-        lpt.table_name= table_name;
-        lpt.create_info= create_info;
-        lpt.create_list= &create_list;
-        lpt.key_count= key_count;
-        lpt.key_info_buffer= key_info_buffer;
-        abort_and_upgrade_lock(lpt);
-      }
-#endif
-
       /* The prepare_drop_index() method takes an array of key numbers. */
       key_numbers= (uint*) thd->alloc(sizeof(uint) * index_drop_count);
       keyno_p= key_numbers;
@@ -6459,27 +6389,6 @@ view_err:
         goto err1;
       }
 
-#ifdef XXX_TO_BE_DONE_LATER_BY_WL3020
-      if (! need_lock_for_indexes)
-      {
-        /* Downgrade the lock again. */
-        if (table->reginfo.lock_type == TL_WRITE_ALLOW_READ)
-        {
-          LOCK_PARAM_TYPE lpt;
-
-          lpt.thd= thd;
-          lpt.table= table;
-          lpt.db= db;
-          lpt.table_name= table_name;
-          lpt.create_info= create_info;
-          lpt.create_list= &create_list;
-          lpt.key_count= key_count;
-          lpt.key_info_buffer= key_info_buffer;
-          close_open_tables_and_downgrade(lpt);
-        }
-      }
-#endif
-
       /* Tell the handler to finally drop the indexes. */
       if ((error= table->file->final_drop_index(table)))
       {
@@ -6579,18 +6488,22 @@ view_err:
     table is renamed and the SE is also changed, then an intermediate table
     is created and the additional call will not take place.
   */
-  if (!need_copy_table)
-    new_db_type=old_db_type= NULL; // this type cannot happen in regular ALTER
+  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
+  {
+    DBUG_ASSERT(new_db_type == old_db_type);
+    /* This type cannot happen in regular ALTER. */
+    new_db_type= old_db_type= NULL;
+  }
   if (mysql_rename_table(old_db_type, db, table_name, db, old_name,
                          FN_TO_IS_TMP))
   {
     error=1;
     VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
   }
-  else if (mysql_rename_table(new_db_type,new_db,tmp_name,new_db,
+  else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db,
                               new_alias, FN_FROM_IS_TMP) ||
            (new_name != table_name || new_db != db) && // we also do rename
-           (need_copy_table ||
+           (need_copy_table != ALTER_TABLE_METADATA_ONLY ||
             mysql_rename_table(save_old_db_type, db, table_name, new_db,
                                new_alias, NO_FRM_RENAME)) &&
            Table_triggers_list::change_table_name(thd, db, table_name,
@@ -6610,7 +6523,7 @@ view_err:
     goto err_with_placeholders;
   }
 
-  if (! need_copy_table)
+  if (need_copy_table == ALTER_TABLE_METADATA_ONLY)
   {
     /*
       Now we have to inform handler that new .FRM file is in place.
@@ -6691,7 +6604,7 @@ view_err:
                 (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
   write_bin_log(thd, TRUE, thd->query, thd->query_length);
 
-  if (ha_check_storage_engine_flag(old_db_type,HTON_FLUSH_AFTER_RENAME))
+  if (ha_check_storage_engine_flag(old_db_type, HTON_FLUSH_AFTER_RENAME))
   {
     /*
       For the alter table to be properly flushed to the logs, we
@@ -6733,8 +6646,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(FALSE);
 
@@ -6975,31 +6887,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().
 */
-bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
-                          bool do_send_ok)
+bool 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();
-  bzero((char*) &create_info,sizeof(create_info));
+  Alter_info alter_info;
+
+  DBUG_ENTER("mysql_recreate_table");
+
+  bzero((char*) &create_info, sizeof(create_info));
   create_info.db_type= 0;
   create_info.row_type=ROW_TYPE_NOT_USED;
   create_info.default_table_charset=default_charset_info;
   /* Force alter table to recreate table */
-  lex->alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
+  alter_info.flags= (ALTER_CHANGE_COLUMN | ALTER_RECREATE);
   DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
-                                table_list, lex->create_list,
-                                lex->key_list, 0, (ORDER *) 0,
-                                0, &lex->alter_info, do_send_ok));
+                                table_list, &alter_info, 0,
+                                (ORDER *) 0, 0));
 }
 
 

--- 1.571/sql/sql_yacc.yy	2007-05-23 15:54:35 +04:00
+++ 1.572/sql/sql_yacc.yy	2007-05-28 15:29:57 +04:00
@@ -491,7 +491,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 
 %pure_parser					/* We have threads */
 /*
-  Currently there is 287 shift/reduce conflict. We should not introduce
+  Currently there is 286 shift/reduce conflict. We should not introduce
   new conflicts any more.
 */
 %expect 286
@@ -1569,8 +1569,7 @@ create:
 						 TL_OPTION_UPDATING,
 						 TL_WRITE))
 	    MYSQL_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));
@@ -1603,21 +1602,23 @@ create:
 							NULL,
 							TL_OPTION_UPDATING))
 	      MYSQL_YYABORT;
-	    lex->create_list.empty();
-	    lex->key_list.empty();
+            lex->alter_info.reset();
+            lex->alter_info.flags= ALTER_ADD_INDEX;
 	    lex->col_list.empty();
 	    lex->change=NullS;
 	  }
 	   '(' key_list ')' key_options
 	  {
 	    LEX *lex=Lex;
+            Key *key;
 	    if ($2 != Key::FULLTEXT && lex->key_create_info.parser_name.str)
 	    {
 	      my_parse_error(ER(ER_SYNTAX_ERROR));
 	      MYSQL_YYABORT;
 	    }
-	    lex->key_list.push_back(new Key($2, $4.str, &lex->key_create_info, 0,
-					   lex->col_list));
+            key= new Key($2, $4.str, &lex->key_create_info, 0,
+                         lex->col_list);
+            lex->alter_info.key_list.push_back(key);
 	    lex->col_list.empty();
 	  }
 	| CREATE DATABASE opt_if_not_exists ident
@@ -3931,7 +3932,7 @@ opt_part_values:
         /* empty */
         {
           LEX *lex= Lex;
-          if (!is_partition_management(lex))
+          if (! lex->is_partition_management())
           {
             if (lex->part_info->part_type == RANGE_PARTITION)
             {
@@ -3952,7 +3953,7 @@ opt_part_values:
         | VALUES LESS_SYM THAN_SYM part_func_max
         {
           LEX *lex= Lex;
-          if (!is_partition_management(lex))
+          if (! lex->is_partition_management())
           {
             if (Lex->part_info->part_type != RANGE_PARTITION)
             {
@@ -3967,7 +3968,7 @@ opt_part_values:
         | VALUES IN_SYM '(' part_list_func ')'
         {
           LEX *lex= Lex;
-          if (!is_partition_management(lex))
+          if (! lex->is_partition_management())
           {
             if (Lex->part_info->part_type != LIST_PARTITION)
             {
@@ -4478,8 +4479,9 @@ key_def:
 	      my_parse_error(ER(ER_SYNTAX_ERROR));
 	      MYSQL_YYABORT;
 	    }
-	    lex->key_list.push_back(new Key($1,$2, &lex->key_create_info, 0,
-					   lex->col_list));
+            Key *key= new Key($1, $2, &lex->key_create_info, 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
@@ -4487,24 +4489,27 @@ key_def:
 	  {
 	    LEX *lex=Lex;
 	    const char *key_name= $3 ? $3 : $1;
-	    lex->key_list.push_back(new Key($2, key_name, &lex->key_create_info, 0,
-					    lex->col_list));
+            Key *key= new Key($2, key_name, &lex->key_create_info, 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,
-					    &default_key_create_info, 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,
+                         &default_key_create_info, 1,
+                         lex->col_list);
+            lex->alter_info.key_list.push_back(key);
 	    lex->col_list.empty();		/* Alloced by sql_alloc */
-
             /* Only used for ALTER TABLE. Ignored otherwise. */
             lex->alter_info.flags|= ALTER_FOREIGN_KEY;
 	  }
@@ -5095,8 +5100,7 @@ alter:
 	  if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
 						 TL_OPTION_UPDATING))
 	    MYSQL_YYABORT;
-	  lex->create_list.empty();
-	  lex->key_list.empty();
+          lex->alter_info.reset();
 	  lex->col_list.empty();
           lex->select_lex.init_order();
 	  lex->select_lex.db=
@@ -5105,8 +5109,7 @@ alter:
 	  lex->create_info.db_type= 0;
 	  lex->create_info.default_table_charset= NULL;
 	  lex->create_info.row_type= ROW_TYPE_NOT_USED;
-	  lex->alter_info.reset();
-	  lex->alter_info.flags= 0;
+          lex->alter_info.reset();
           lex->no_write_to_binlog= 0;
           lex->create_info.storage_media= HA_SM_DEFAULT;	
 	}
@@ -8141,7 +8144,8 @@ drop:
 	  {
 	     LEX *lex=Lex;
 	     lex->sql_command= SQLCOM_DROP_INDEX;
-	     lex->alter_info.drop_list.empty();
+	     lex->alter_info.reset();
+             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.104/sql/sql_partition.cc	2007-05-23 15:26:11 +04:00
+++ 1.105/sql/sql_partition.cc	2007-05-28 15:29:57 +04:00
@@ -141,30 +141,6 @@ int get_part_iter_for_interval_via_walki
                                            PARTITION_ITERATOR *part_iter);
 static void set_up_range_analysis_info(partition_info *part_info);
 
-/*
-  A routine used by the parser to decide whether we are specifying a full
-  partitioning or if only partitions to add or to split.
-
-  SYNOPSIS
-    is_partition_management()
-    lex                    Reference to the lex object
-
-  RETURN VALUE
-    TRUE                   Yes, it is part of a management partition command
-    FALSE                  No, not a management partition command
-
-  DESCRIPTION
-    This needs to be outside of WITH_PARTITION_STORAGE_ENGINE since it is
-    used from the sql parser that doesn't have any #ifdef's
-*/
-
-my_bool is_partition_management(LEX *lex)
-{
-  return (lex->sql_command == SQLCOM_ALTER_TABLE &&
-          (lex->alter_info.flags == ALTER_ADD_PARTITION ||
-           lex->alter_info.flags == ALTER_REORGANIZE_PARTITION));
-}
-
 #ifdef WITH_PARTITION_STORAGE_ENGINE
 /*
   A support function to check if a name is in a list of strings
@@ -4125,7 +4101,7 @@ error:
     change patterns.
 */
 
-uint prep_alter_part_table(THD *thd, TABLE *table, ALTER_INFO *alter_info,
+uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
                            HA_CREATE_INFO *create_info,
                            handlerton *old_db_type,
                            bool *partition_changed,
@@ -5991,8 +5967,6 @@ void handle_alter_part_error(ALTER_PARTI
     alter_info                    ALTER TABLE info
     create_info                   Create info for CREATE TABLE
     table_list                    List of the table involved
-    create_list                   The fields in the resulting table
-    key_list                      The keys in the resulting table
     db                            Database name of new table
     table_name                    Table name of new table
 
@@ -6006,11 +5980,10 @@ void handle_alter_part_error(ALTER_PARTI
 */
 
 uint fast_alter_partition_table(THD *thd, TABLE *table,
-                                ALTER_INFO *alter_info,
+                                Alter_info *alter_info,
                                 HA_CREATE_INFO *create_info,
                                 TABLE_LIST *table_list,
-                                List<create_field> *create_list,
-                                List<Key> *key_list, char *db,
+                                char *db,
                                 const char *table_name,
                                 uint fast_alter_partition)
 {
@@ -6027,8 +6000,6 @@ uint fast_alter_partition_table(THD *thd
   lpt->part_info= part_info;
   lpt->alter_info= alter_info;
   lpt->create_info= create_info;
-  lpt->create_list= create_list;
-  lpt->key_list= key_list;
   lpt->db_options= create_info->table_options;
   if (create_info->row_type == ROW_TYPE_DYNAMIC)
     lpt->db_options|= HA_OPTION_PACK_RECORD;
@@ -6106,7 +6077,7 @@ uint fast_alter_partition_table(THD *thd
  
       The first approach here was to downgrade locks. Now a different approach
       is decided upon. The idea is that the handler will have access to the
-      ALTER_INFO when store_lock arrives with TL_WRITE_ALLOW_READ. So if the
+      Alter_info when store_lock arrives with TL_WRITE_ALLOW_READ. So if the
       handler knows that this functionality can be handled with a lower lock
       level it will set the lock level to TL_WRITE_ALLOW_WRITE immediately.
       Thus the need to downgrade the lock disappears.
@@ -6379,7 +6350,7 @@ uint fast_alter_partition_table(THD *thd
     user
   */
   DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted,
-                                 table, table_list, FALSE, lpt,
+                                 table, table_list, FALSE, NULL,
                                  written_bin_log));
 }
 #endif

--- 1.270/mysql-test/r/sp.result	2007-05-02 22:11:18 +04:00
+++ 1.271/mysql-test/r/sp.result	2007-05-28 15:29:55 +04:00
@@ -5617,6 +5617,23 @@ Called B
 Called B
 drop procedure proc_21462_a|
 drop procedure proc_21462_b|
+drop table if exists t3|
+drop procedure if exists proc_bug19733|
+create table t3 (s1 int)|
+create procedure proc_bug19733()
+begin
+declare v int default 0;
+while v < 100 do
+create index i on t3 (s1);
+drop index i on t3;
+set v = v + 1;
+end while;
+end|
+call proc_bug19733()|
+call proc_bug19733()|
+call proc_bug19733()|
+drop procedure proc_bug19733|
+drop table t3|
 DROP PROCEDURE IF EXISTS p1|
 DROP VIEW IF EXISTS v1, v2|
 DROP TABLE IF EXISTS t3, t4|

--- 1.237/mysql-test/t/sp.test	2007-05-10 16:27:53 +04:00
+++ 1.238/mysql-test/t/sp.test	2007-05-28 15:29:55 +04:00
@@ -6570,6 +6570,34 @@ drop procedure proc_21462_b|
 
 
 #
+# Bug#19733 "Repeated alter, or repeated create/drop, fails"
+# Check that CREATE/DROP INDEX is re-execution friendly.
+# 
+--disable_warnings
+drop table if exists t3|
+drop procedure if exists proc_bug19733|
+--enable_warnings
+create table t3 (s1 int)|
+
+create procedure proc_bug19733()
+begin
+  declare v int default 0;
+  while v < 100 do
+    create index i on t3 (s1);
+    drop index i on t3;
+    set v = v + 1;
+  end while;
+end|
+
+call proc_bug19733()|
+call proc_bug19733()|
+call proc_bug19733()|
+
+drop procedure proc_bug19733|
+drop table t3|
+
+
+#
 # BUG#20492: Subsequent calls to stored procedure yeild incorrect
 # result if join is used 
 #

--- 1.111/mysql-test/r/ps.result	2007-05-18 22:21:32 +04:00
+++ 1.112/mysql-test/r/ps.result	2007-05-28 15:29:55 +04:00
@@ -1062,6 +1062,87 @@ 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` varchar(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) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+show create table mysqltest.t2;
+Table	Create Table
+t2	CREATE TABLE `t2` (
+  `test` varchar(4) NOT NULL DEFAULT ''
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+drop database mysqltest;
+deallocate prepare stmt1;
+deallocate prepare stmt2;
+execute stmt;
+show create table t1;
+drop table t1;
+execute stmt;
+show create table t1;
+drop table t1;
+deallocate prepare stmt;
+CREATE TABLE t1(a int);
+INSERT INTO t1 VALUES (2), (3), (1);
+PREPARE st1 FROM
+'(SELECT a FROM t1) UNION (SELECT a+10 FROM t1) ORDER BY RAND()*0+a';
+EXECUTE st1;
+a
+1
+2
+3
+11
+12
+13
+EXECUTE st1;
+a
+1
+2
+3
+11
+12
+13
+DEALLOCATE PREPARE st1;
+DROP TABLE t1;
 End of 4.1 tests.
 create table t1 (a varchar(20));
 insert into t1 values ('foo');
@@ -1544,6 +1625,72 @@ a
 2
 DEALLOCATE PREPARE stmt;
 DROP TABLE t1,t2;
+drop table if exists t1;
+create table t1 (s1 char(20));
+prepare stmt from "alter table t1 modify s1 int";
+execute stmt;
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+drop table if exists t1;
+create table t1 (a int, b int);
+prepare s_6895 from "alter table t1 drop column b";
+execute s_6895;
+show columns from t1;
+Field	Type	Null	Key	Default	Extra
+a	int(11)	YES		NULL	
+drop table t1;
+create table t1 (a int, b int);
+execute s_6895;
+show columns from t1;
+Field	Type	Null	Key	Default	Extra
+a	int(11)	YES		NULL	
+drop table t1;
+create table t1 (a int, b int);
+execute s_6895;
+show columns from t1;
+Field	Type	Null	Key	Default	Extra
+a	int(11)	YES		NULL	
+deallocate prepare s_6895;
+drop table t1;
+create table t1 (i int primary key auto_increment) comment='comment for table t1';
+create table t2 (i int, j int, k int);
+prepare stmt from "alter table t1 auto_increment=100";
+execute stmt;
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `i` int(11) NOT NULL AUTO_INCREMENT,
+  PRIMARY KEY (`i`)
+) ENGINE=MyISAM AUTO_INCREMENT=100 DEFAULT CHARSET=latin1 COMMENT='comment for table t1'
+flush tables;
+select * from t2;
+i	j	k
+execute stmt;
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `i` int(11) NOT NULL AUTO_INCREMENT,
+  PRIMARY KEY (`i`)
+) ENGINE=MyISAM AUTO_INCREMENT=100 DEFAULT CHARSET=latin1 COMMENT='comment for table t1'
+deallocate prepare stmt;
+drop table t1, t2;
+set @old_character_set_server= @@character_set_server;
+set @@character_set_server= latin1;
+prepare stmt from "create database mysqltest_1";
+execute stmt;
+show create database mysqltest_1;
+Database	Create Database
+mysqltest_1	CREATE DATABASE `mysqltest_1` /*!40100 DEFAULT CHARACTER SET latin1 */
+drop database mysqltest_1;
+set @@character_set_server= utf8;
+execute stmt;
+show create database mysqltest_1;
+Database	Create Database
+mysqltest_1	CREATE DATABASE `mysqltest_1` /*!40100 DEFAULT CHARACTER SET utf8 */
+drop database mysqltest_1;
+deallocate prepare stmt;
+set @@character_set_server= @old_character_set_server;
 drop tables if exists t1;
 create table t1 (id int primary key auto_increment, value varchar(10));
 insert into t1 (id, value) values (1, 'FIRST'), (2, 'SECOND'), (3, 'THIRD');
@@ -2524,4 +2671,25 @@ i	j
 4	5
 3	NULL
 DROP TABLE t1, t2;
+drop table if exists t1;
+Warnings:
+Note	1051	Unknown table 't1'
+prepare stmt
+from "create table t1 (c char(100) character set utf8, key (c(10)))";
+execute stmt;
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `c` char(100) CHARACTER SET utf8 DEFAULT NULL,
+  KEY `c` (`c`(10))
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+drop table t1;
+execute stmt;
+show create table t1;
+Table	Create Table
+t1	CREATE TABLE `t1` (
+  `c` char(100) CHARACTER SET utf8 DEFAULT NULL,
+  KEY `c` (`c`(10))
+) ENGINE=MyISAM DEFAULT CHARSET=latin1
+drop table t1;
 End of 5.1 tests.

--- 1.114/mysql-test/t/ps.test	2007-05-18 22:21:32 +04:00
+++ 1.115/mysql-test/t/ps.test	2007-05-28 15:29:55 +04:00
@@ -1118,6 +1118,113 @@ 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
+#
+# Protect ourselves from data left in tmp/ by a previos possibly failed
+# test
+--system rm -f $MYSQLTEST_VARDIR/tmp/t1.*
+--disable_warnings
+--disable_query_log
+eval prepare stmt from "create table t1 (c char(10)) data directory='$MYSQLTEST_VARDIR/tmp'";
+--enable_query_log
+execute stmt;
+#
+# DATA DIRECTORY option does not always work: if the operating
+# system does not support symlinks, have_symlinks option is automatically
+# disabled.
+# In this case DATA DIRECTORY is silently ignored when
+# creating a table, and is not output by SHOW CREATE TABLE.
+#
+--disable_result_log
+show create table t1;
+--enable_result_log
+drop table t1;
+execute stmt;
+--disable_result_log
+show create table t1;
+--enable_result_log
+--enable_warnings
+drop table t1;
+deallocate prepare stmt;
+#
+
+#
+# Bug #27937: crash on the second execution for prepared statement 
+#             from UNION with ORDER BY an expression containing RAND()
+#
+
+CREATE TABLE t1(a int);
+INSERT INTO t1 VALUES (2), (3), (1);
+
+PREPARE st1 FROM
+  '(SELECT a FROM t1) UNION (SELECT a+10 FROM t1) ORDER BY RAND()*0+a';
+
+EXECUTE st1;
+EXECUTE st1;
+
+DEALLOCATE PREPARE st1;
+DROP TABLE t1;
+
 --echo End of 4.1 tests.
 
 ############################# 5.0 tests start ################################
@@ -1596,6 +1703,77 @@ EXECUTE stmt USING @arg;
 DEALLOCATE PREPARE stmt;
 
 DROP TABLE t1,t2;
+#
+# Bug#4968 "Stored procedure crash if cursor opened on altered table"
+# The bug is not repeatable any more after the fix for
+# Bug#15217 "Bug #15217   Using a SP cursor on a table created with PREPARE
+# fails with weird error", however ALTER TABLE is not re-execution friendly
+# and that caused a valgrind warning. Check that the warning is gone.
+#
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (s1 char(20));
+prepare stmt from "alter table t1 modify s1 int";
+execute stmt;
+execute stmt;
+drop table t1;
+deallocate prepare stmt;
+
+#
+# Bug#6895 "Prepared Statements: ALTER TABLE DROP COLUMN does nothing"
+#
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int, b int);
+prepare s_6895 from "alter table t1 drop column b";
+execute s_6895;
+show columns from t1;
+drop table t1;
+create table t1 (a int, b int);
+execute s_6895;
+show columns from t1;
+drop table t1;
+create table t1 (a int, b int);
+execute s_6895;
+show columns from t1;
+deallocate prepare s_6895;
+drop table t1;
+
+#
+# Bug #22060 "ALTER TABLE x AUTO_INCREMENT=y in SP crashes server"
+#
+# 5.0 part of the test.
+#
+
+# ALTER TABLE
+create table t1 (i int primary key auto_increment) comment='comment for table t1';
+create table t2 (i int, j int, k int);
+prepare stmt from "alter table t1 auto_increment=100";
+execute stmt;
+show create table t1;
+# Let us trash table-cache's memory
+flush tables;
+select * from t2;
+execute stmt;
+show create table t1;
+deallocate prepare stmt;
+drop table t1, t2;
+# 5.1 part of the test.
+# CREATE DATABASE
+set @old_character_set_server= @@character_set_server;
+set @@character_set_server= latin1; 
+prepare stmt from "create database mysqltest_1";
+execute stmt;
+show create database mysqltest_1;
+drop database mysqltest_1;
+set @@character_set_server= utf8; 
+execute stmt;
+show create database mysqltest_1;
+drop database mysqltest_1;
+deallocate prepare stmt;
+set @@character_set_server= @old_character_set_server;
 
 
 #
@@ -2570,5 +2748,21 @@ connection default;
 
 DROP TABLE t1, t2;
 
+#
+# Bug #24879 Prepared Statements: CREATE TABLE (UTF8 KEY) produces a growing
+# key length
+#
+# Test that parse information is not altered by subsequent executions of a
+# prepared statement
+#
+drop table if exists t1;
+prepare stmt
+from "create table t1 (c char(100) character set utf8, key (c(10)))";
+execute stmt;
+show create table t1;
+drop table t1;
+execute stmt;
+show create table t1;
+drop table t1;
 
 --echo End of 5.1 tests.
Thread
bk commit into 5.1 tree (kostja:1.2515) BUG#4968konstantin28 May