Index: include/my_base.h
===================================================================
--- include/my_base.h	(revision 23869)
+++ include/my_base.h	(revision 24227)
@@ -98,8 +98,15 @@
   HA_KEY_ALG_FULLTEXT=	4		/* FULLTEXT (MyISAM tables) */
 };
 
-        /* Storage media types */ 
+/* Index and table build methods */
 
+enum ha_build_method { 
+  HA_BUILD_DEFAULT, 
+  HA_BUILD_ONLINE,
+  HA_BUILD_OFFLINE 
+};
+/* Storage media types */ 
+
 enum ha_storage_media {
   HA_SM_DEFAULT=        0,		/* Not specified (engine default) */
   HA_SM_DISK=           1,		/* DISK storage */
Index: sql/sql_delete.cc
===================================================================
--- sql/sql_delete.cc	(revision 23869)
+++ sql/sql_delete.cc	(revision 24227)
@@ -1115,6 +1115,7 @@
     is_temporary_table= true;
     handlerton *table_type= table->s->db_type();
     TABLE_SHARE *share= table->s;
+    bool frm_only= (share->tmp_table == TMP_TABLE_FRM_FILE_ONLY);
     if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE))
       goto trunc_by_del;
 
@@ -1127,7 +1128,8 @@
     if ((error= (int) !(open_temporary_table(thd, share->path.str,
                                              share->db.str,
 					     share->table_name.str, 1))))
-      (void) rm_temporary_table(table_type, path);
+      (void) rm_temporary_table(table_type, path,
+                             frm_only);
     else
       thd->thread_specific_used= TRUE;
     
Index: sql/table.cc
===================================================================
--- sql/table.cc	(revision 23869)
+++ sql/table.cc	(revision 24227)
@@ -1619,6 +1619,8 @@
 } /* open_binary_frm */
 
 
+
+
 /*
   Open a table based on a TABLE_SHARE
 
@@ -1633,6 +1635,9 @@
     prgflag   		READ_ALL etc..
     ha_open_flags	HA_OPEN_ABORT_IF_LOCKED etc..
     outparam       	result table
+    open_mode           One of OTM_OPEN|OTM_CREATE|OTM_ALTER
+                        if OTM_CREATE some errors are ignore
+                        if OTM_ALTER HA_OPEN is not called
 
   RETURN VALUES
    0	ok
@@ -1644,9 +1649,9 @@
    7    Table definition has changed in engine
 */
 
-int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
+int open_table_from_share_with_mode(THD *thd, TABLE_SHARE *share, const char *alias,
                           uint db_stat, uint prgflag, uint ha_open_flags,
-                          TABLE *outparam, bool is_create_table)
+                          TABLE *outparam, open_table_mode open_mode)
 {
   int error;
   uint records, i, bitmap_size;
@@ -1654,8 +1659,12 @@
   uchar *record, *bitmaps;
   Field **field_ptr;
   DBUG_ENTER("open_table_from_share");
-  DBUG_PRINT("enter",("name: '%s.%s'  form: 0x%lx", share->db.str,
-                      share->table_name.str, (long) outparam));
+  DBUG_PRINT("enter",("name: '%s.%s'  form: 0x%lx, open mode:%s",
+                      share->db.str,
+                      share->table_name.str,
+                      (long) outparam,
+                      (open_mode == OTM_OPEN)?"open":
+                      ((open_mode == OTM_CREATE)?"create":"alter")));
 
   /* Parsing of partitioning information from .frm needs thd->lex set up. */
   DBUG_ASSERT(thd->lex->is_lex_started);
@@ -1832,7 +1841,7 @@
                                 share->partition_info_len,
                                 share->part_state,
                                 share->part_state_len,
-                                outparam, is_create_table,
+                                outparam, (open_mode != OTM_OPEN),
                                 share->default_part_db_type,
                                 &work_part_info_used);
     if (tmp)
@@ -1847,19 +1856,19 @@
        caller's arena depending on work_part_info_used value
     */
     if (!work_part_info_used)
-      tmp= fix_partition_func(thd, outparam, is_create_table);
+      tmp= fix_partition_func(thd, outparam, (open_mode != OTM_OPEN));
     thd->stmt_arena= backup_stmt_arena_ptr;
     thd->restore_active_arena(&part_func_arena, &backup_arena);
     if (!tmp)
     {
       if (work_part_info_used)
-        tmp= fix_partition_func(thd, outparam, is_create_table);
+        tmp= fix_partition_func(thd, outparam, (open_mode != OTM_OPEN));
       outparam->part_info->item_free_list= part_func_arena.free_list;
     }
 partititon_err:
     if (tmp)
     {
-      if (is_create_table)
+      if (open_mode == OTM_CREATE)
       {
         /*
           During CREATE/ALTER TABLE it is ok to receive errors here.
@@ -1888,7 +1897,7 @@
 
   /* The table struct is now initialized;  Open the table */
   error= 2;
-  if (db_stat)
+  if (db_stat && open_mode != OTM_ALTER)
   {
     int ha_err;
     if ((ha_err= (outparam->file->
@@ -1966,6 +1975,48 @@
 
 
 /*
+  Open a table based on a TABLE_SHARE
+
+  SYNOPSIS
+    open_table_from_share()
+    thd			Thread handler
+    share		Table definition
+    alias       	Alias for table
+    db_stat		open flags (for example HA_OPEN_KEYFILE|
+    			HA_OPEN_RNDFILE..) can be 0 (example in
+                        ha_example_table)
+    prgflag   		READ_ALL etc..
+    ha_open_flags	HA_OPEN_ABORT_IF_LOCKED etc..
+    outparam       	result table
+
+  RETURN VALUES
+   0	ok
+   1	Error (see open_table_error)
+   2    Error (see open_table_error)
+   3    Wrong data in .frm file
+   4    Error (see open_table_error)
+   5    Error (see open_table_error: charset unavailable)
+   7    Table definition has changed in engine
+*/
+
+int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
+                          uint db_stat, uint prgflag, uint ha_open_flags,
+                          TABLE *outparam, bool is_create_table)
+{
+    return open_table_from_share_with_mode(
+        thd,
+        share,
+        alias,
+        db_stat,
+        prgflag,
+        ha_open_flags,
+        outparam,
+        (is_create_table==0) ? OTM_OPEN : OTM_CREATE
+        );
+}
+
+
+/*
   Free information allocated by openfrm
 
   SYNOPSIS
Index: sql/sql_lex.h
===================================================================
--- sql/sql_lex.h	(revision 23869)
+++ sql/sql_lex.h	(revision 24227)
@@ -899,6 +899,7 @@
   enum tablespace_op_type       tablespace_op;
   List<char>                    partition_names;
   uint                          no_parts;
+  enum ha_build_method          build_method;
   enum_alter_table_change_level change_level;
   Create_field                 *datetime_field;
   bool                          error_if_not_empty;
@@ -909,6 +910,7 @@
     keys_onoff(LEAVE_AS_IS),
     tablespace_op(NO_TABLESPACE_OP),
     no_parts(0),
+    build_method(HA_BUILD_DEFAULT),
     change_level(ALTER_TABLE_METADATA_ONLY),
     datetime_field(NULL),
     error_if_not_empty(FALSE)
@@ -925,6 +927,7 @@
     tablespace_op= NO_TABLESPACE_OP;
     no_parts= 0;
     partition_names.empty();
+    build_method= HA_BUILD_DEFAULT;
     change_level= ALTER_TABLE_METADATA_ONLY;
     datetime_field= 0;
     error_if_not_empty= FALSE;
Index: sql/handler.h
===================================================================
--- sql/handler.h	(revision 23869)
+++ sql/handler.h	(revision 24227)
@@ -47,6 +47,62 @@
 #define HA_ADMIN_NEEDS_ALTER    -11
 #define HA_ADMIN_NEEDS_CHECK    -12
 
+/* Bits to show what an alter table will do */
+//#include <sql_bitmap.h>
+
+#define HA_MAX_ALTER_FLAGS 40
+typedef Bitmap<HA_MAX_ALTER_FLAGS> HA_ALTER_FLAGS;
+
+#define HA_ADD_INDEX                  (0)
+#define HA_DROP_INDEX                 (1)
+#define HA_ALTER_INDEX                (2)
+#define HA_RENAME_INDEX               (3)
+#define HA_ADD_UNIQUE_INDEX           (4)
+#define HA_DROP_UNIQUE_INDEX          (5)
+#define HA_ALTER_UNIQUE_INDEX         (6)
+#define HA_RENAME_UNIQUE_INDEX        (7)
+#define HA_ADD_PK_INDEX               (8)
+#define HA_DROP_PK_INDEX              (9)
+#define HA_ALTER_PK_INDEX             (10)
+#define HA_ADD_COLUMN                 (11)
+#define HA_DROP_COLUMN                (12)
+#define HA_CHANGE_COLUMN              (13)
+#define HA_ALTER_COLUMN_NAME          (14)
+#define HA_ALTER_COLUMN_TYPE          (15)
+#define HA_ALTER_COLUMN_ORDER         (16)
+#define HA_ALTER_COLUMN_NULLABLE      (17)
+#define HA_COLUMN_DEFAULT_VALUE       (18)
+#define HA_COLUMN_STORAGE             (19)
+#define HA_COLUMN_FORMAT              (20)
+#define HA_ADD_FOREIGN_KEY            (21)
+#define HA_DROP_FOREIGN_KEY           (22)
+#define HA_ALTER_FOREIGN_KEY          (23)
+#define HA_ADD_CONSTRAINT             (24)
+#define HA_ADD_PARTITION              (25)
+#define HA_DROP_PARTITION             (26)
+#define HA_ALTER_PARTITION            (27)
+#define HA_COALESCE_PARTITION         (28)
+#define HA_REORGANIZE_PARTITION       (29)
+#define HA_CHANGE_CHARACTER_SET       (30)
+#define HA_SET_DEFAULT_CHARACTER_SET  (31)
+#define HA_CHANGE_AUTOINCREMENT_VALUE (32)
+#define HA_ALTER_STORAGE              (33)
+#define HA_ALTER_TABLESPACE           (34)
+#define HA_ALTER_ROW_FORMAT           (35)
+#define HA_RENAME_TABLE               (36)
+#define HA_ALTER_STORAGE_ENGINE       (37)
+#define HA_RECREATE                   (38)
+#define HA_ALTER_TABLE_REORG          (39)
+/* Remember to increase HA_MAX_ALTER_FLAGS when adding more flags! */
+
+/* Return values for check_if_supported_alter */
+
+#define HA_ALTER_ERROR               -1
+#define HA_ALTER_SUPPORTED_WAIT_LOCK  0
+#define HA_ALTER_SUPPORTED_NO_LOCK    1
+#define HA_ALTER_NOT_SUPPORTED        2
+
+
 /* Bits in table_flags() to show what database can do */
 
 #define HA_NO_TRANSACTIONS     (1 << 0) /* Doesn't support transactions */
@@ -125,6 +181,9 @@
 #define HA_BINLOG_ROW_CAPABLE  (LL(1) << 34)
 #define HA_BINLOG_STMT_CAPABLE (LL(1) << 35)
 
+
+#define HA_ONLINE_ALTER        (LL(1) << 36)
+
 /*
   Set of all binlog flags. Currently only contain the capabilities
   flags.
@@ -195,6 +254,7 @@
 #define HA_PARTITION_FUNCTION_SUPPORTED         (1L << 12)
 #define HA_FAST_CHANGE_PARTITION                (1L << 13)
 #define HA_PARTITION_ONE_PHASE                  (1L << 14)
+#define HA_GENERAL_ONLINE                       (1L << 15)
 
 /*
   Index scan will not return records in rowid order. Not guaranteed to be
@@ -938,7 +998,18 @@
   enum enum_ha_unused unused2;
 } HA_CREATE_INFO;
 
+typedef struct st_ha_alter_information
+{
+  KEY  *key_info_buffer;
+  uint key_count;
+  uint index_drop_count;
+  uint *index_drop_buffer;
+  uint index_add_count;
+  uint *index_add_buffer;
+  void *data;
+} HA_ALTER_INFO;
 
+
 typedef struct st_key_create_information
 {
   enum ha_key_alg algorithm;
@@ -1751,7 +1822,94 @@
 					 uint table_changes)
  { return COMPATIBLE_DATA_NO; }
 
+  /* On-line ALTER TABLE interface */
+  
   /**
+     Check if a storage engine supports a particular alter table on-line
+  
+     @param    altered_table     A temporary table show what table is to
+                                 change to
+     @param    create_info       Information from the parsing phase about new
+                                 table properties.
+     @param    alter_flags       Bitmask that shows what will be changed
+     @param    table_changes     Shows if table layout has changed (for
+                                 backwards compatibility with
+                                 check_if_incompatible_data
+  
+     @retval   HA_ALTER_ERROR                Unexpected error
+     @retval   HA_ALTER_SUPPORTED_WAIT_LOCK  Supported, but requires DDL lock
+     @retval   HA_ALTER_SUPPORTED_NO_LOCK    Supported
+     @retval   HA_ALTER_NOT_SUPPORTED        Not supported
+  
+     @note
+       The default implementation is implemented to support fast
+       alter table (storage engines that support some changes by
+       just changing the frm file) without any change in the handler
+       implementation.    
+  */
+  virtual int check_if_supported_alter(TABLE *altered_table,
+                                       HA_CREATE_INFO *create_info,
+                                       HA_ALTER_FLAGS *alter_flags,
+                                       uint table_changes);
+  /**
+    Tell storage engine to prepare for the on-line alter table (pre-alter)
+  
+    @param     thd               The thread handle
+    @param     altered_table     A temporary table show what table is to
+                                 change to
+    @param     alter_info        Storage place for data used during phase1
+                                 and phase2
+    @param     alter_flags       Bitmask that shows what will be changed
+  
+    @retval   0      OK
+    @retval   error  error code passed from storage engine
+  */
+  virtual int alter_table_phase1(THD *thd,
+                                 TABLE *altered_table,
+                                 HA_CREATE_INFO *create_info,
+                                 HA_ALTER_INFO *alter_info,
+                                 HA_ALTER_FLAGS *alter_flags)
+  {
+    return HA_ERR_UNSUPPORTED;
+  }
+   /**
+     Tell storage engine to perform the on-line alter table (alter)
+  
+     @param    thd               The thread handle
+     @param    altered_table     A temporary table show what table is to
+                                 change to
+     @param    alter_info        Storage place for data used during phase1
+                                 and phase2
+     @param    alter_flags       Bitmask that shows what will be changed
+  
+     @retval  0      OK
+     @retval  error  error code passed from storage engine
+  
+     @note
+       If check_if_supported_alter returns HA_ALTER_SUPPORTED_WAIT_LOCK
+       this call is to be wrapped with a DDL lock. This is currently NOT
+       supported.
+  */
+  virtual int alter_table_phase2(THD *thd,
+                                 TABLE *altered_table,
+                                 HA_CREATE_INFO *create_info,
+                                 HA_ALTER_INFO *alter_info,
+                                 HA_ALTER_FLAGS *alter_flags)
+  {
+    return HA_ERR_UNSUPPORTED;
+  }
+  /**
+     Tell storage engine that changed frm file is now on disk and table
+     has been re-opened (post-alter)
+  
+     @param    thd               The thread handle
+     @param    table             The altered table, re-opened
+  */
+  virtual int alter_table_phase3(THD *thd, TABLE *table)
+  {
+    return HA_ERR_UNSUPPORTED;
+  }
+  /**
     use_hidden_primary_key() is called in case of an update/delete when
     (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined
     but we don't have a primary key
Index: sql/sql_base.cc
===================================================================
--- sql/sql_base.cc	(revision 23869)
+++ sql/sql_base.cc	(revision 24227)
@@ -1918,8 +1918,13 @@
 
   free_io_cache(table);
   closefrm(table, 0);
+  /*
+     Check that temporary table has not been created with
+     frm_only because it has then not been created in any storage engine
+   */
   if (delete_table)
-    rm_temporary_table(table_type, table->s->path.str);
+    rm_temporary_table(table_type, table->s->path.str,
+                           table->s->tmp_table == TMP_TABLE_FRM_FILE_ONLY);
   if (free_share)
   {
     free_table_share(table->s);
@@ -5507,8 +5512,9 @@
    #  TABLE object
 */
 
-TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
-			    const char *table_name, bool link_in_list)
+TABLE *open_temporary_table_with_mode(THD *thd, const char *path, const char *db,
+			    const char *table_name, bool link_in_list,
+                            open_table_mode open_mode)
 {
   TABLE *tmp_table;
   TABLE_SHARE *share;
@@ -5530,7 +5536,7 @@
   if (!(tmp_table= (TABLE*) my_malloc(sizeof(*tmp_table) + sizeof(*share) +
                                       strlen(path)+1 + key_length,
                                       MYF(MY_WME))))
-    DBUG_RETURN(0);				/* purecov: inspected */
+    DBUG_RETURN(0);             /* purecov: inspected */
 
   share= (TABLE_SHARE*) (tmp_table+1);
   tmp_path= (char*) (share+1);
@@ -5541,12 +5547,16 @@
                        strend(saved_cache_key)+1, tmp_path);
 
   if (open_table_def(thd, share, 0) ||
-      open_table_from_share(thd, share, table_name,
+      open_table_from_share_with_mode(thd, share, table_name,
+                            (open_mode == OTM_ALTER) ? 0 :
                             (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
                                     HA_GET_INDEX),
-                            READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
+                            (open_mode == OTM_ALTER) ?
+                              (READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD |
+                               OPEN_FRM_FILE_ONLY)
+                            : (READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD),
                             ha_open_options,
-                            tmp_table, FALSE))
+                            tmp_table, open_mode))
   {
     /* No need to lock share->mutex as this is not needed for tmp tables */
     free_table_share(share);
@@ -5554,9 +5564,18 @@
     DBUG_RETURN(0);
   }
 
-  tmp_table->reginfo.lock_type= TL_WRITE;	 // Simulate locked
-  share->tmp_table= (tmp_table->file->has_transactions() ? 
-                     TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
+  tmp_table->reginfo.lock_type= TL_WRITE;    // Simulate locked
+  if (open_mode == OTM_ALTER)
+  {
+    /*
+       Temporary table has been created with frm_only
+       and has not been created in any storage engine
+    */
+    share->tmp_table= TMP_TABLE_FRM_FILE_ONLY;
+  }
+  else
+    share->tmp_table= (tmp_table->file->has_transactions() ?
+                       TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE);
 
   if (link_in_list)
   {
@@ -5575,9 +5594,42 @@
   DBUG_RETURN(tmp_table);
 }
 
+/*
+  Open a single table without table caching and don't set it in open_list
 
-bool rm_temporary_table(handlerton *base, char *path)
+  SYNPOSIS
+    open_temporary_table()
+    thd		  Thread object
+    path	  Path (without .frm)
+    db		  database
+    table_name	  Table name
+    link_in_list  1 if table should be linked into thd->temporary_tables
+
+ NOTES:
+    Used by alter_table to open a temporary table and when creating
+    a temporary table with CREATE TEMPORARY ...
+
+ RETURN
+   0  Error
+   #  TABLE object
+*/
+
+TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
+			    const char *table_name, bool link_in_list)
 {
+    return open_temporary_table_with_mode(
+        thd,
+        path,
+        db,
+        table_name,
+        link_in_list,
+        OTM_OPEN
+        );
+}
+
+
+bool rm_temporary_table(handlerton *base, char *path, bool frm_only)
+{
   bool error=0;
   handler *file;
   char *ext;
@@ -5588,7 +5640,7 @@
     error=1; /* purecov: inspected */
   *ext= 0;				// remove extension
   file= get_new_handler((TABLE_SHARE*) 0, current_thd->mem_root, base);
-  if (file && file->ha_delete_table(path))
+  if (!frm_only && file && file->ha_delete_table(path))
   {
     error=1;
     sql_print_warning("Could not remove temporary table: '%s', error: %d",
Index: sql/handler.cc
===================================================================
--- sql/handler.cc	(revision 23869)
+++ sql/handler.cc	(revision 24227)
@@ -2158,6 +2158,66 @@
   DBUG_RETURN(error);
 }
 
+inline uint get_null_offset(TABLE* table, Field* field) {
+    return (uint) ((uchar*) field->null_ptr - (uchar*) table->record[0]);
+}
+
+int handler::check_if_supported_alter(TABLE *altered_table,
+                                     HA_CREATE_INFO *create_info,
+                                     HA_ALTER_FLAGS *alter_flags,
+                                     uint table_changes)
+{
+  DBUG_ENTER("check_if_supported_alter");
+  printf("***alter flags set ***\n");
+  for (uint i = 0; i < HA_MAX_ALTER_FLAGS; i++) {
+      if (alter_flags->is_set(i)) {
+          printf("flag: %d\n", i);
+      }
+  }
+  //
+  // everyone calculates data by doing some default_values - record[0], but I do not see why
+  // that is necessary
+  //
+  printf("******\n");
+  printf("***orig table***\n");
+  for (uint i = 0; i < table->s->fields; i++) {
+      //
+      // make sure to use table->field, and NOT table->s->field
+      //
+      Field* curr_field = table->field[i];
+      uint null_offset = get_null_offset(table, curr_field);
+      printf(
+        "name: %s\n, nullable: %d, null_offset: %d, is_null_field: %d, is_null %d, \n", 
+        curr_field->field_name, 
+        curr_field->null_bit,
+        null_offset,
+        (curr_field->null_ptr != NULL),
+          (curr_field->null_ptr != NULL) ? altered_table->s->default_values[null_offset] & curr_field->null_bit : 0xffffffff
+        );
+  }
+  printf("******\n");
+  printf("***altered table***\n");
+  for (uint i = 0; i < altered_table->s->fields; i++) {
+      Field* curr_field = altered_table->field[i];
+      uint null_offset = get_null_offset(altered_table, curr_field);
+      printf(
+        "name: %s\n, nullable: %d, null_offset: %d, is_null_field: %d, is_null %d, \n", 
+        curr_field->field_name, 
+        curr_field->null_bit,
+        null_offset,
+        (curr_field->null_ptr != NULL),
+        (curr_field->null_ptr != NULL) ? altered_table->s->default_values[null_offset] & curr_field->null_bit : 0xffffffff
+        );
+  }
+  printf("******\n");
+  if (this->check_if_incompatible_data(create_info, table_changes)
+      == COMPATIBLE_DATA_NO)
+    DBUG_RETURN(HA_ALTER_NOT_SUPPORTED);
+  else
+    DBUG_RETURN(HA_ALTER_SUPPORTED_WAIT_LOCK);
+}
+
+
 /**
   Generate the next auto-increment number based on increment and offset.
   computes the lowest number
Index: sql/table.h
===================================================================
--- sql/table.h	(revision 23869)
+++ sql/table.h	(revision 24227)
@@ -133,7 +133,7 @@
 enum tmp_table_type
 {
   NO_TMP_TABLE, NON_TRANSACTIONAL_TMP_TABLE, TRANSACTIONAL_TMP_TABLE,
-  INTERNAL_TMP_TABLE, SYSTEM_TMP_TABLE
+  INTERNAL_TMP_TABLE, SYSTEM_TMP_TABLE, TMP_TABLE_FRM_FILE_ONLY
 };
 
 /** Event on which trigger is invoked. */
Index: sql/sql_table.cc
===================================================================
--- sql/sql_table.cc	(revision 23869)
+++ sql/sql_table.cc	(revision 24227)
@@ -3957,7 +3957,7 @@
     /* Open table and put in temporary table list */
     if (!(open_temporary_table(thd, path, db, table_name, 1)))
     {
-      (void) rm_temporary_table(create_info->db_type, path);
+      (void) rm_temporary_table(create_info->db_type, path, false);
       goto unlock_and_end;
     }
     thd->thread_specific_used= TRUE;
@@ -5364,7 +5364,7 @@
     if (err || !open_temporary_table(thd, dst_path, db, table_name, 1))
     {
       (void) rm_temporary_table(create_info->db_type,
-				dst_path); /* purecov: inspected */
+				dst_path, false); /* purecov: inspected */
       goto err;     /* purecov: inspected */
     }
     thd->thread_specific_used= TRUE;
@@ -5611,7 +5611,7 @@
 
 /*
   SYNOPSIS
-    compare_tables()
+    old_compare_tables()
       table                     The original table.
       alter_info                Alter options, fields and keys for the new
                                 table.
@@ -5653,7 +5653,7 @@
 
 static
 bool
-compare_tables(TABLE *table,
+old_compare_tables(TABLE *table,
                Alter_info *alter_info,
                HA_CREATE_INFO *create_info,
                uint order_num,
@@ -5677,7 +5677,7 @@
   */
   bool varchar= create_info->varchar;
   bool not_nullable= true;
-  DBUG_ENTER("compare_tables");
+  DBUG_ENTER("old_compare_tables");
 
   /*
     Create a copy of alter_info.
@@ -5949,7 +5949,538 @@
   DBUG_RETURN(0);
 }
 
+/**
+  Copy all changes detected by parser to the HA_ALTER_FLAGS
+*/
 
+void setup_ha_alter_flags(TABLE *table,
+                          HA_CREATE_INFO *create_info,
+                          Alter_info *alter_info,
+                          HA_ALTER_FLAGS *alter_flags)
+{
+  uint flags= alter_info->flags;
+
+  if (ALTER_ADD_COLUMN & flags)
+      alter_flags->set_bit(HA_ADD_COLUMN);
+  if (ALTER_DROP_COLUMN & flags)
+      alter_flags->set_bit(HA_DROP_COLUMN);
+  if (ALTER_RENAME & flags)
+      alter_flags->set_bit(HA_RENAME_TABLE);
+  if (ALTER_CHANGE_COLUMN & flags)
+      alter_flags->set_bit(HA_CHANGE_COLUMN);
+  if (ALTER_CHANGE_COLUMN_DEFAULT & flags)
+      alter_flags->set_bit(HA_COLUMN_DEFAULT_VALUE);
+  if (ALTER_RECREATE & flags)
+      alter_flags->set_bit(HA_RECREATE);
+  if (ALTER_ADD_PARTITION & flags)
+      alter_flags->set_bit(HA_ADD_PARTITION);
+  if (ALTER_DROP_PARTITION & flags)
+      alter_flags->set_bit(HA_DROP_PARTITION);
+  if (ALTER_COALESCE_PARTITION & flags)
+      alter_flags->set_bit(HA_COALESCE_PARTITION);
+  if (ALTER_REORGANIZE_PARTITION & flags)
+      alter_flags->set_bit(HA_REORGANIZE_PARTITION);
+  if (ALTER_PARTITION & flags)
+      alter_flags->set_bit(HA_ALTER_PARTITION);
+  if (ALTER_FOREIGN_KEY & flags)
+      alter_flags->set_bit(HA_ALTER_FOREIGN_KEY);
+  if (create_info->auto_increment_value !=
+      table->file->stats.auto_increment_value)
+      alter_flags->set_bit(HA_CHANGE_AUTOINCREMENT_VALUE);
+  if (ALTER_TABLE_REORG & flags)
+      alter_flags->set_bit(HA_ALTER_TABLE_REORG);
+}
+
+/**
+   @param       thd                Thread
+   @param       table              The original table.
+   @param       alter_info         Alter options, fields and keys for the new
+                                   table.
+   @param       create_info        Create options for the new table.
+   @param       order_num          Number of order list elements.
+   @param[out]  ha_alter_flags  Flags that indicate what will be changed
+   @param[out]  ha_alter_info      Data structures needed for on-line alter
+   @param[out]  table_changes      Information about particular change
+
+   First argument 'table' contains information of the original
+   table, which includes all corresponding parts that the new
+   table has in arguments create_list, key_list and create_info.
+
+   By comparing the changes between the original and new table
+   we can determine how much it has changed after ALTER TABLE
+   and whether we need to make a copy of the table, or just change
+   the .frm file.
+
+   Mark any changes detected in the ha_alter_flags.
+
+   If there are no data changes, but index changes, 'index_drop_buffer'
+   and/or 'index_add_buffer' are populated with offsets into
+   table->key_info or key_info_buffer respectively for the indexes
+   that need to be dropped and/or (re-)created.
+
+   @retval TRUE  error
+   @retval FALSE success
+*/
+
+static
+bool
+new_compare_tables(THD *thd,
+               TABLE *table,
+               Alter_info *alter_info,
+               HA_CREATE_INFO *create_info,
+               uint order_num,
+               HA_ALTER_FLAGS *alter_flags,
+               HA_ALTER_INFO *ha_alter_info,
+               uint *table_changes)
+{
+  Field **f_ptr, *field;
+  uint table_changes_local= 0;
+  List_iterator_fast<Create_field> new_field_it, tmp_new_field_it;
+  Create_field *new_field, *tmp_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;
+  uint candidate_key_count= 0;
+  bool not_nullable= true;
+  DBUG_ENTER("compare_tables");
+
+  /*
+    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,
+                                 &ha_alter_info->key_info_buffer,
+                                 &ha_alter_info->key_count,
+                                 /* select_field_count */ 0))
+    DBUG_RETURN(TRUE);
+  /* Allocate result buffers. */
+  if (! (ha_alter_info->index_drop_buffer=
+          (uint*) thd->alloc(sizeof(uint) * table->s->keys)) ||
+      ! (ha_alter_info->index_add_buffer=
+          (uint*) thd->alloc(sizeof(uint) *
+                            tmp_alter_info.key_list.elements)))
+    DBUG_RETURN(TRUE);
+
+  /*
+    First we setup ha_alter_flags based on what was detected
+    by parser
+  */
+  setup_ha_alter_flags(table, create_info, alter_info, alter_flags);
+
+#ifndef DBUG_OFF
+  {
+    char dbug_string[HA_MAX_ALTER_FLAGS+1];
+    alter_flags->print(dbug_string);
+    DBUG_PRINT("info", ("alter_flags:  %s", (char *) dbug_string));
+  }
+#endif
+
+  /*
+    Some very basic checks. If number of fields changes, or the
+    handler, we need to run full ALTER TABLE. In the future
+    new fields can be added and old dropped without copy, but
+    not yet.
+
+    Test also that engine was not given during ALTER TABLE, or
+    we are force to run regular alter table (copy).
+    E.g. ALTER TABLE tbl_name ENGINE=MyISAM.
+
+    For the following ones we also want to run regular alter table:
+    ALTER TABLE tbl_name ORDER BY ..
+    ALTER TABLE tbl_name CONVERT TO CHARACTER SET ..
+
+    At the moment we can't handle altering temporary tables without a copy.
+    We also test if OPTIMIZE TABLE was given and was mapped to alter table.
+    In that case we always do full copy.
+
+    There was a bug prior to mysql-4.0.25. Number of null fields was
+    calculated incorrectly. As a result frm and data files gets out of
+    sync after fast alter table. There is no way to determine by which
+    mysql version (in 4.0 and 4.1 branches) table was created, thus we
+    disable fast alter table for all tables created by mysql versions
+    prior to 5.0 branch.
+    See BUG#6236.
+  */
+  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 ||
+      create_info->used_fields & HA_CREATE_USED_CHARSET ||
+      create_info->used_fields & HA_CREATE_USED_DEFAULT_CHARSET ||
+      (table->s->row_type != create_info->row_type) ||
+      create_info->used_fields & HA_CREATE_USED_PACK_KEYS ||
+      create_info->used_fields & HA_CREATE_USED_MAX_ROWS ||
+      (alter_info->flags & (ALTER_RECREATE | ALTER_FOREIGN_KEY)) ||
+      order_num ||
+      !table->s->mysql_version ||
+      (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
+  {
+    *table_changes= IS_EQUAL_NO;
+    /*
+      Check what has changed and set alter_flags
+    */
+    if (table->s->fields < alter_info->create_list.elements)
+      alter_flags->set_bit(HA_ADD_COLUMN);
+    else if (table->s->fields > alter_info->create_list.elements)
+      alter_flags->set_bit(HA_DROP_COLUMN);
+    if (create_info->db_type != table->s->db_type() ||
+        create_info->used_fields & HA_CREATE_USED_ENGINE)
+        alter_flags->set_bit(HA_ALTER_STORAGE_ENGINE);
+    if (create_info->used_fields & HA_CREATE_USED_CHARSET)
+      alter_flags->set_bit(HA_CHANGE_CHARACTER_SET);
+    if (create_info->used_fields & HA_CREATE_USED_DEFAULT_CHARSET)
+      alter_flags->set_bit(HA_SET_DEFAULT_CHARACTER_SET);
+    if (alter_info->flags & ALTER_RECREATE)
+      alter_flags->set_bit(HA_RECREATE);
+    /* TODO check for ADD/DROP FOREIGN KEY */
+    if (alter_info->flags & ALTER_FOREIGN_KEY)
+      alter_flags->set_bit(HA_ALTER_FOREIGN_KEY);
+    if (!table->s->mysql_version ||
+        (table->s->frm_version < FRM_VER_TRUE_VARCHAR && varchar))
+      alter_flags->set_bit(HA_ALTER_COLUMN_TYPE);
+  }
+  /*
+    Use transformed info to evaluate possibility of fast ALTER TABLE
+    but use the preserved field to persist modifications.
+  */
+  new_field_it.init(alter_info->create_list);
+  tmp_new_field_it.init(tmp_alter_info.create_list);
+
+  /*
+    Go through fields and check if the original ones are compatible
+    with new table.
+  */
+  for (f_ptr= table->field, new_field= new_field_it++,
+       tmp_new_field= tmp_new_field_it++;
+       (new_field && (field= *f_ptr));
+       f_ptr++, new_field= new_field_it++,
+       tmp_new_field= tmp_new_field_it++)
+  {
+    /* Make sure we have at least the default charset in use. */
+    if (!new_field->charset)
+      new_field->charset= create_info->default_table_charset;
+
+    /* Don't pack rows in old tables if the user has requested this. */
+    if (create_info->row_type == ROW_TYPE_DYNAMIC ||
+    (tmp_new_field->flags & BLOB_FLAG) ||
+    (tmp_new_field->sql_type == MYSQL_TYPE_VARCHAR &&
+    create_info->row_type != ROW_TYPE_FIXED))
+      create_info->table_options|= HA_OPTION_PACK_RECORD;
+
+    /* Check how fields have been modified */
+    if (alter_info->flags & ALTER_CHANGE_COLUMN)
+    {
+      /* Evaluate changes bitmap and send to check_if_incompatible_data() */
+      if (!(table_changes_local= field->is_equal(tmp_new_field)))
+        alter_flags->set_bit(HA_ALTER_COLUMN_TYPE);
+
+      /* Check if field was renamed */
+      field->flags&= ~FIELD_IS_RENAMED;
+      if (my_strcasecmp(system_charset_info,
+                        field->field_name,
+                        tmp_new_field->field_name))
+      {
+        field->flags|= FIELD_IS_RENAMED;
+        alter_flags->set_bit(HA_ALTER_COLUMN_NAME);
+      }
+
+      *table_changes&= table_changes_local;
+      if (table_changes_local == IS_EQUAL_PACK_LENGTH)
+        alter_flags->set_bit(HA_ALTER_COLUMN_TYPE);
+
+      /* Check that NULL behavior is same for old and new fields */
+      if ((tmp_new_field->flags & NOT_NULL_FLAG) !=
+          (uint) (field->flags & NOT_NULL_FLAG))
+      {
+        *table_changes= IS_EQUAL_NO;
+        alter_flags->set_bit(HA_ALTER_COLUMN_NULLABLE);
+      }
+    }
+
+    /* Clear indexed marker */
+    field->flags&= ~FIELD_IN_ADD_INDEX;
+  }
+
+  /*
+    Go through keys and check if the original ones are compatible
+    with new table.
+  */
+  KEY *table_key;
+  KEY *table_key_end= table->key_info + table->s->keys;
+  KEY *new_key;
+  KEY *new_key_end=
+       ha_alter_info->key_info_buffer + ha_alter_info->key_count;
+
+  DBUG_PRINT("info", ("index count old: %d  new: %d",
+                      table->s->keys, ha_alter_info->key_count));
+
+  /* Count all candidate keys. */
+
+  for (table_key= table->key_info; table_key < table_key_end; table_key++)
+  {
+    KEY_PART_INFO *table_part;
+    KEY_PART_INFO *table_part_end= table_key->key_part + table_key->key_parts;
+
+    /*
+      Check if key is a candidate key, i.e. a unique index with no index
+      fields nullable, then key is either already primary key or could
+      be promoted to primary key if the original primary key is dropped.
+    */
+    not_nullable= true;
+    for (table_part= table_key->key_part;
+         table_part < table_part_end;
+         table_part++)
+    {
+      not_nullable= not_nullable && (! table_part->field->maybe_null());
+    }
+    if ((table_key->flags & HA_NOSAME) && not_nullable)
+      candidate_key_count++;
+  }
+
+  /*
+    Step through all keys of the old table and search matching new keys.
+  */
+  ha_alter_info->index_drop_count= 0;
+  ha_alter_info->index_add_count= 0;
+  for (table_key= table->key_info; table_key < table_key_end; table_key++)
+  {
+    KEY_PART_INFO *table_part;
+    KEY_PART_INFO *table_part_end= table_key->key_part + table_key->key_parts;
+    KEY_PART_INFO *new_part;
+
+    /* Search a new key with the same name. */
+    for (new_key= ha_alter_info->key_info_buffer;
+         new_key < new_key_end;
+         new_key++)
+    {
+      if (! strcmp(table_key->name, new_key->name))
+        break;
+    }
+    if (new_key >= new_key_end)
+    {
+      /* Key not found. Add the offset of the key to the drop buffer. */
+      ha_alter_info->index_drop_buffer
+           [ha_alter_info->index_drop_count++]=
+           table_key - table->key_info;
+      if (table_key->flags & HA_NOSAME)
+      {
+        /* Unique key. Check for "PRIMARY". */
+        if ((uint) (table_key - table->key_info) == table->s->primary_key)
+        {
+          alter_flags->set_bit(HA_DROP_PK_INDEX);
+          candidate_key_count--;
+        }
+        else
+        {
+          bool is_not_null= true;
+
+          alter_flags->set_bit(HA_DROP_UNIQUE_INDEX);
+          key_part= table_key->key_part;
+          end= key_part + table_key->key_parts;
+
+         /*
+            Check if all fields in key are declared
+            NOT NULL and adjust candidate_key_count
+          */
+          for(; key_part != end; key_part++)
+          {
+            is_not_null=
+              (is_not_null && 
+               (!table->field[key_part->fieldnr-1]->maybe_null()));
+          }
+          if (is_not_null)
+            candidate_key_count--;
+        }
+      }
+      else
+        alter_flags->set_bit(HA_DROP_INDEX);
+      *table_changes= IS_EQUAL_NO;
+      DBUG_PRINT("info", ("index dropped: '%s'", table_key->name));
+      continue;
+    }
+
+    /* Check that the key types are compatible between old and new tables. */
+    if ((table_key->algorithm != new_key->algorithm) ||
+        ((table_key->flags & HA_KEYFLAG_MASK) !=
+         (new_key->flags & HA_KEYFLAG_MASK)) ||
+        (table_key->key_parts != new_key->key_parts))
+    {
+      if (table_key->flags & HA_NOSAME)
+      {
+        /* Unique key. Check for "PRIMARY". */
+        if ((uint) (table_key - table->key_info) == table->s->primary_key)
+          alter_flags->set_bit(HA_ALTER_PK_INDEX);
+        else
+          alter_flags->set_bit(HA_ALTER_UNIQUE_INDEX);
+      }
+      else
+        alter_flags->set_bit(HA_ALTER_INDEX);
+      goto index_changed;
+    }
+
+    /*
+      Check that the key parts remain compatible between the old and
+      new tables.
+    */
+    for (table_part= table_key->key_part, new_part= new_key->key_part;
+         table_part < table_part_end;
+         table_part++, new_part++)
+    {
+      /*
+        Key definition has changed if we are using a different field or
+    if the used key part length is different. We know that the fields
+        did not change. Comparing field numbers is sufficient.
+      */
+      if ((table_part->length != new_part->length) ||
+          (table_part->fieldnr - 1 != new_part->fieldnr))
+      {
+        if (table_key->flags & HA_NOSAME)
+        {
+          /* Unique key. Check for "PRIMARY" */
+          if ((uint) (table_key - table->key_info) ==  table->s->primary_key)
+            alter_flags->set_bit(HA_ALTER_PK_INDEX);
+          else
+            alter_flags->set_bit(HA_ALTER_UNIQUE_INDEX);
+        }
+        else
+          alter_flags->set_bit(HA_ALTER_INDEX);
+        goto index_changed;
+      }
+    }
+    continue;
+
+  index_changed:
+    /* Key modified. Add the offset of the key to both buffers. */
+    ha_alter_info->index_drop_buffer
+         [ha_alter_info->index_drop_count++]=
+         table_key - table->key_info;
+    ha_alter_info->index_add_buffer
+         [ha_alter_info->index_add_count++]=
+         new_key - ha_alter_info->key_info_buffer;
+    key_part= new_key->key_part;
+    end= key_part + new_key->key_parts;
+    for(; key_part != end; key_part++)
+    {
+      /* Mark field to be part of new key */
+      if ((field= table->field[key_part->fieldnr]))
+        field->flags|= FIELD_IN_ADD_INDEX;
+    }
+    *table_changes= IS_EQUAL_NO;
+    DBUG_PRINT("info", ("index changed: '%s'", table_key->name));
+  }
+  /*end of for (; table_key < table_key_end;) */
+
+  /*
+    Step through all keys of the new table and find matching old keys.
+  */
+  for (new_key= ha_alter_info->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++)
+    {
+      if (! strcmp(table_key->name, new_key->name))
+        break;
+    }
+    if (table_key >= table_key_end)
+    {
+      bool is_not_null= true;
+      bool no_pk= ((table->s->primary_key == MAX_KEY) ||
+                   alter_flags->is_set(HA_DROP_PK_INDEX));
+
+      /* Key not found. Add the offset of the key to the add buffer. */
+      ha_alter_info->index_add_buffer
+           [ha_alter_info->index_add_count++]=
+           new_key - ha_alter_info->key_info_buffer;
+      key_part= new_key->key_part;
+      end= key_part + new_key->key_parts;
+      for(; key_part != end; key_part++)
+      {
+        /* Mark field to be part of new key */
+        if ((field= table->field[key_part->fieldnr]))
+          field->flags|= FIELD_IN_ADD_INDEX;
+        /*
+          Check if all fields in key are declared
+          NOT NULL
+         */
+        if (key_part->fieldnr < table->s->fields)
+        {
+          is_not_null=
+            (is_not_null && 
+             (!table->field[key_part->fieldnr]->maybe_null()));
+        }
+        else
+        {
+          /* Index is defined over a newly added column */
+          List_iterator_fast<Create_field>
+            new_field_it(alter_info->create_list);
+          Create_field *new_field;
+          uint fieldnr;
+
+          for (fieldnr= 0, new_field= new_field_it++;
+               fieldnr != key_part->fieldnr;
+               fieldnr++, new_field= new_field_it++);
+          is_not_null=
+            (is_not_null && (new_field->flags & NOT_NULL_FLAG));
+        }
+      }
+      if (new_key->flags & HA_NOSAME)
+      {
+        /* Unique key. Check for "PRIMARY" 
+           or if adding first unique key
+           defined on non-nullable 
+        */
+        DBUG_PRINT("info",("no_pk %s, candidate_key_count %u, is_not_null %s", (no_pk)?"yes":"no", candidate_key_count, (is_not_null)?"yes":"no"));
+        if ((!my_strcasecmp(system_charset_info,
+                            new_key->name, primary_key_name)) ||
+            (no_pk && candidate_key_count == 0 && is_not_null))
+          alter_flags->set_bit(HA_ADD_PK_INDEX);
+        else
+        alter_flags->set_bit(HA_ADD_UNIQUE_INDEX);
+      }
+      else
+        alter_flags->set_bit(HA_ADD_INDEX);
+      *table_changes= IS_EQUAL_NO;
+      DBUG_PRINT("info", ("index added: '%s'", new_key->name));
+    }
+  }
+#ifndef DBUG_OFF
+  {
+    char dbug_string[HA_MAX_ALTER_FLAGS+1];
+    alter_flags->print(dbug_string);
+    DBUG_PRINT("info", ("alter_flags:  %s", (char *) dbug_string));
+  }
+#endif
+
+  DBUG_RETURN(FALSE);
+}
+
+
+
 /*
   Manages enabling/disabling of indexes for ALTER TABLE
 
@@ -5998,7 +6529,283 @@
   DBUG_RETURN(error);
 }
 
+int create_temporary_table(THD *thd,
+                           TABLE *table,
+                           char *new_db,
+                           char *tmp_name,
+                           HA_CREATE_INFO *create_info,
+                           Alter_info *alter_info,
+                           bool db_changed)
+{
+  int error;
+  char index_file[FN_REFLEN], data_file[FN_REFLEN];
+  handlerton *old_db_type, *new_db_type;
+  DBUG_ENTER("create_temporary_table");
+  old_db_type= table->s->db_type();
+  new_db_type= create_info->db_type;
+  /*
+    Handling of symlinked tables:
+    If no rename:
+      Create new data file and index file on the same disk as the
+      old data and index files.
+      Copy data.
+      Rename new data file over old data file and new index file over
+      old index file.
+      Symlinks are not changed.
 
+   If rename:
+      Create new data file and index file on the same disk as the
+      old data and index files.  Create also symlinks to point at
+      the new tables.
+      Copy data.
+      At end, rename intermediate tables, and symlinks to intermediate
+      table, to final table name.
+      Remove old table and old symlinks
+
+    If rename is made to another database:
+      Create new tables in new database.
+      Copy data.
+      Remove old table and symlinks.
+  */
+  if (db_changed)		// Ignore symlink if db changed
+  {
+    if (create_info->index_file_name)
+    {
+      /* Fix index_file_name to have 'tmp_name' as basename */
+      strmov(index_file, tmp_name);
+      create_info->index_file_name=fn_same(index_file,
+                                           create_info->index_file_name,
+                                           1);
+    }
+    if (create_info->data_file_name)
+    {
+      /* Fix data_file_name to have 'tmp_name' as basename */
+      strmov(data_file, tmp_name);
+      create_info->data_file_name=fn_same(data_file,
+                                          create_info->data_file_name,
+                                          1);
+    }
+  }
+  else
+    create_info->data_file_name=create_info->index_file_name=0;
+
+  DEBUG_SYNC(thd, "alter_table_before_create_table_no_lock");
+  /*
+    Create a table with a temporary name.
+    With create_info->frm_only == 1 this creates a .frm file only.
+    We don't log the statement, it will be logged later.
+  */
+  tmp_disable_binlog(thd);
+  error= mysql_create_table_no_lock(thd, new_db, tmp_name,
+                                    create_info, alter_info, 1, 0);
+  reenable_binlog(thd);
+
+  DBUG_RETURN(error);
+}
+
+/*
+  Create a temporary table that reflects what an alter table operation
+  will accomplish.
+
+  SYNOPSIS
+    create_altered_table()
+      thd              Thread handle
+      table            The original table
+      create_info      Information from the parsing phase about new
+                       table properties.
+      alter_info       Lists of fields, keys to be changed, added
+                       or dropped.
+      db_change        Specifies if the table is moved to another database
+  RETURN
+    A temporary table with all changes
+    NULL if error
+  NOTES
+    The temporary table is created without storing it in any storage engine
+    and is opened only to get the table struct and frm file reference.
+*/
+TABLE *create_altered_table(THD *thd,
+                            TABLE *table,
+                            char *new_db,
+                            HA_CREATE_INFO *create_info,
+                            Alter_info *alter_info,
+                            bool db_change)
+{
+  int error;
+  HA_CREATE_INFO altered_create_info(*create_info);
+  TABLE *altered_table;
+  char tmp_name[80];
+  char path[FN_REFLEN];
+  DBUG_ENTER("create_altered_table");
+
+  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);
+  altered_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE;
+  altered_create_info.frm_only= 1;
+  if ((error= create_temporary_table(thd, table, new_db, tmp_name,
+                                     &altered_create_info,
+                                     alter_info, db_change)))
+  {
+    DBUG_PRINT("info", ("Error %u while creating temporary table", error));
+    DBUG_RETURN(NULL);
+  };
+
+  build_table_filename(path, sizeof(path), new_db, tmp_name, "",
+                       FN_IS_TMP);
+  altered_table= open_temporary_table_with_mode(thd, path, new_db, tmp_name, 1,
+                                      OTM_ALTER);
+  DBUG_RETURN(altered_table);
+
+  DBUG_RETURN(NULL);
+}
+
+
+
+/*
+  Perform a fast or on-line alter table
+
+  SYNOPSIS
+    mysql_fast_or_online_alter_table()
+      thd              Thread handle
+      table            The original table
+      altered_table    A temporary table showing how we will change table
+      create_info      Information from the parsing phase about new
+                       table properties.
+      alter_info       Storage place for data used during different phases
+      ha_alter_flags   Bitmask that shows what will be changed
+      keys_onoff       Specifies if keys are to be enabled/disabled
+  RETURN
+     0  OK
+    >0  An error occured during the on-line alter table operation
+    -1  Error when re-opening table
+  NOTES
+    If mysql_alter_table does not need to copy the table, it is
+    either a fast alter table where the storage engine does not
+    need to know about the change, only the frm will change,
+    or the storage engine supports performing the alter table
+    operation directly, on-line without mysql having to copy
+    the table.
+*/
+int mysql_fast_or_online_alter_table(THD *thd,
+                                     TABLE *table,
+                                     TABLE *altered_table,
+                                     HA_CREATE_INFO *create_info,
+                                     HA_ALTER_INFO *alter_info,
+                                     HA_ALTER_FLAGS *ha_alter_flags,
+                                     enum enum_enable_or_disable keys_onoff)
+{
+  int error= 0;
+  bool online= (table->file->ha_table_flags() & HA_ONLINE_ALTER)?true:false;
+  TABLE *t_table;
+
+  DBUG_ENTER(" mysql_fast_or_online_alter_table");
+  if (online)
+  {
+   /*
+      Tell the handler to prepare for the online alter
+    */
+    if ((error= table->file->alter_table_phase1(thd,
+                                                altered_table,
+                                                create_info,
+                                                alter_info,
+                                                ha_alter_flags)))
+    {
+      goto err;
+    }
+
+    /*
+       Tell the storage engine to perform the online alter table
+       TODO: 
+       if check_if_supported_alter() returned HA_ALTER_SUPPORTED_WAIT_LOCK
+       we need to wrap the next call with a DDL lock.
+     */
+    if ((error= table->file->alter_table_phase2(thd,
+                                                altered_table,
+                                                create_info,
+                                                alter_info,
+                                                ha_alter_flags)))
+    {
+      goto err;
+    }
+  }
+  /*
+    The final .frm file is already created as a temporary file
+    and will be renamed to the original table name.
+  */
+  VOID(pthread_mutex_lock(&LOCK_open));
+  wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
+  alter_table_manage_keys(table, table->file->indexes_are_disabled(),
+                          keys_onoff);
+  close_data_files_and_morph_locks(thd,
+                                   table->pos_in_table_list->db,
+                                   table->pos_in_table_list->table_name);
+  if (mysql_rename_table(NULL,
+			 altered_table->s->db.str,
+                         altered_table->s->table_name.str,
+			 table->s->db.str,
+                         table->s->table_name.str, FN_FROM_IS_TMP))
+  {
+    error= 1;
+    VOID(pthread_mutex_unlock(&LOCK_open));
+    goto err;
+  }
+  broadcast_refresh();
+  VOID(pthread_mutex_unlock(&LOCK_open));
+
+  /*
+    The ALTER TABLE is always in its own transaction.
+    Commit must not be called while LOCK_open is locked. It could call
+    wait_if_global_read_lock(), which could create a deadlock if called
+    with LOCK_open.
+  */
+  error= ha_autocommit_or_rollback(thd, 0);
+
+  if (end_active_trans(thd))
+    error=1;
+  if (error)
+    goto err;
+  if (online)
+  {
+    VOID(pthread_mutex_lock(&LOCK_open));
+    if (reopen_table(table))
+    {
+      error= -1;
+      goto err;
+    }
+    VOID(pthread_mutex_unlock(&LOCK_open));
+    t_table= table;
+
+   /*
+      Tell the handler that the changed frm is on disk and table
+      has been re-opened
+   */
+    if ((error= t_table->file->alter_table_phase3(thd, t_table)))
+    {
+      goto err;
+    }
+
+    /*
+      We are going to reopen table down on the road, so we have to restore
+      state of the TABLE object which we used for obtaining of handler
+      object to make it suitable for reopening.
+    */
+    DBUG_ASSERT(t_table == table);
+    table->open_placeholder= 1;
+    VOID(pthread_mutex_lock(&LOCK_open));
+    close_handle_and_leave_table_as_lock(table);
+    VOID(pthread_mutex_unlock(&LOCK_open));
+  }
+
+ err:
+  if (error)
+    DBUG_PRINT("info", ("Got error %u", error));
+  DBUG_RETURN(error);
+}
+
+
+
 /**
   Prepare column and key definitions for CREATE TABLE in ALTER TABLE.
 
@@ -6219,6 +7026,22 @@
         goto err;
       }
       find_it.after(def);			// Put element after this
+      /*
+        XXX: hack for Bug#28427.
+        If column order has changed, force OFFLINE ALTER TABLE
+        without querying engine capabilities.  If we ever have an
+        engine that supports online ALTER TABLE CHANGE COLUMN
+        <name> AFTER <name1> (Falcon?), this fix will effectively
+        disable the capability.
+        TODO: detect the situation in compare_tables, behave based
+        on engine capabilities.
+      */
+      if (alter_info->build_method == HA_BUILD_ONLINE)
+      {
+        my_error(ER_NOT_SUPPORTED_YET, MYF(0), thd->query());
+        goto err;
+      }
+      alter_info->build_method= HA_BUILD_OFFLINE;
       alter_info->change_level= ALTER_TABLE_DATA_CHANGED;
     }
   }
@@ -6405,80 +7228,25 @@
 }
 
 
-/*
-  Alter table
 
-  SYNOPSIS
-    mysql_alter_table()
-      thd              Thread handle
-      new_db           If there is a RENAME clause
-      new_name         If there is a RENAME clause
-      create_info      Information from the parsing phase about new
-                       table properties.
-      table_list       The table to change.
-      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
 
-  DESCRIPTION
-    This is a veery long function and is everything but the kitchen sink :)
-    It is used to alter a table and not only by ALTER TABLE but also
-    CREATE|DROP INDEX are mapped on this function.
-
-    When the ALTER TABLE statement just does a RENAME or ENABLE|DISABLE KEYS,
-    or both, then this function short cuts its operation by renaming
-    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 `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.
-
-  RETURN VALUES
-    FALSE  OK
-    TRUE   Error
-*/
-
-bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
+bool extract_and_maybe_alter_table(TABLE** out_table, bool* continue_alter, THD *thd,char *new_db, char *new_name,
                        HA_CREATE_INFO *create_info,
                        TABLE_LIST *table_list,
                        Alter_info *alter_info,
                        uint order_num, ORDER *order, bool ignore)
+
 {
-  TABLE *table, *new_table= 0, *name_lock= 0;
+  TABLE *table;
   int error= 0;
-  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
-  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
-  char index_file[FN_REFLEN], data_file[FN_REFLEN];
-  char path[FN_REFLEN + 1];
+  char new_name_buff[FN_REFLEN + 1];
+  char *table_name, *db, *alias;
+  char path[FN_REFLEN+1];
   char reg_path[FN_REFLEN+1];
-  ha_rows copied,deleted;
-  handlerton *old_db_type, *new_db_type, *save_old_db_type;
   legacy_db_type table_type;
   frm_type_enum frm_type;
-  enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
-#ifdef WITH_PARTITION_STORAGE_ENGINE
-  uint fast_alter_partition= 0;
-  bool partition_changed= FALSE;
-#endif
-  bool need_lock_for_indexes= TRUE;
-  KEY  *key_info_buffer;
-  uint index_drop_count= 0;
-  uint *index_drop_buffer= NULL;
-  uint index_add_count= 0;
-  uint *index_add_buffer= NULL;
-  uint candidate_key_count= 0;
-  bool committed= 0;
-  bool no_pk;
-  DBUG_ENTER("mysql_alter_table");
+  DBUG_ENTER("extract_table");
+  *continue_alter = FALSE;
 
   /*
     Check if we attempt to alter mysql.slow_log or
@@ -6523,8 +7291,8 @@
   }
 
   /*
-    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
+    Assign variables table_name, new_name, db, new_db, path
+    to simplify further comparisons: we want to see if it's a RENAME
     later just by comparing the pointers, avoiding the need for strcmp.
   */
   thd_proc_info(thd, "init");
@@ -6559,7 +7327,7 @@
     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);
+  frm_type= mysql_frm_type(thd, reg_path, &table_type);
   /* Rename a view */
   /* Sic: there is a race here */
   if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME))
@@ -6592,6 +7360,7 @@
 
     if (wait_if_global_read_lock(thd,0,1))
       DBUG_RETURN(TRUE);
+
     VOID(pthread_mutex_lock(&LOCK_open));
     if (lock_table_names(thd, table_list))
     {
@@ -6623,6 +7392,99 @@
 
   if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ)))
     DBUG_RETURN(TRUE);
+  *out_table = table;
+  *continue_alter = TRUE;
+  DBUG_RETURN(FALSE);
+}
+
+/*
+  Alter table
+
+  SYNOPSIS
+    mysql_alter_table()
+      thd              Thread handle
+      new_db           If there is a RENAME clause
+      new_name         If there is a RENAME clause
+      create_info      Information from the parsing phase about new
+                       table properties.
+      table_list       The table to change.
+      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
+
+  DESCRIPTION
+    This is a veery long function and is everything but the kitchen sink :)
+    It is used to alter a table and not only by ALTER TABLE but also
+    CREATE|DROP INDEX are mapped on this function.
+
+    When the ALTER TABLE statement just does a RENAME or ENABLE|DISABLE KEYS,
+    or both, then this function short cuts its operation by renaming
+    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 `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.
+
+  RETURN VALUES
+    FALSE  OK
+    TRUE   Error
+*/
+
+bool old_mysql_alter_table(TABLE* table, THD *thd,char *new_db, char *new_name,
+                       HA_CREATE_INFO *create_info,
+                       TABLE_LIST *table_list,
+                       Alter_info *alter_info,
+                       uint order_num, ORDER *order, bool ignore)
+{
+  TABLE *new_table= 0, *name_lock= 0;
+  int error= 0;
+  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
+  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
+  char index_file[FN_REFLEN], data_file[FN_REFLEN];
+  char path[FN_REFLEN + 1];
+  char reg_path[FN_REFLEN+1];
+  ha_rows copied,deleted;
+  handlerton *old_db_type, *new_db_type, *save_old_db_type;
+  enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+  uint fast_alter_partition= 0;
+  bool partition_changed= FALSE;
+#endif
+  bool need_lock_for_indexes= TRUE;
+  KEY  *key_info_buffer;
+  uint index_drop_count= 0;
+  uint *index_drop_buffer= NULL;
+  uint index_add_count= 0;
+  uint *index_add_buffer= NULL;
+  uint candidate_key_count= 0;
+  bool committed= 0;
+  bool no_pk;
+  DBUG_ENTER("old_mysql_alter_table");
+
+  /*
+    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(thd, "init");
+  table_name=table_list->table_name;
+  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
+  db=table_list->db;
+  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
+    new_db= db;
+  build_table_filename(reg_path, sizeof(reg_path) - 1, db, table_name, reg_ext, 0);
+  build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0);
+
+
   table->use_all_columns();
 
   /*
@@ -6919,7 +7781,7 @@
   {
     enum_alter_table_change_level need_copy_table_res;
     /* Check how much the tables differ. */
-    if (compare_tables(table, alter_info,
+    if (old_compare_tables(table, alter_info,
                        create_info, order_num,
                        &need_copy_table_res,
                        &key_info_buffer,
@@ -7638,6 +8500,837 @@
 }
 /* mysql_alter_table */
 
+bool new_mysql_alter_table(TABLE* table, THD *thd,char *new_db, char *new_name,
+                       HA_CREATE_INFO *create_info,
+                       TABLE_LIST *table_list,
+                       Alter_info *alter_info,
+                       uint order_num, ORDER *order, bool ignore)
+
+{
+  TABLE *new_table=0, *name_lock= 0;;
+  int error= 0;
+  char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
+  char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
+  char path[FN_REFLEN+1];
+  char reg_path[FN_REFLEN+1];
+  ha_rows copied= 0,deleted= 0;
+  handlerton *old_db_type, *new_db_type, *save_old_db_type;
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+  uint fast_alter_partition= 0;
+  bool partition_changed= FALSE;
+#endif
+  DBUG_ENTER("new_mysql_alter_table");
+
+  /*
+    Assign variables table_name, new_name, db, new_db, path
+    to simplify further comparisons: we want to see if it's a RENAME
+    later just by comparing the pointers, avoiding the need for strcmp.
+  */
+  thd_proc_info(thd, "init");
+  table_name=table_list->table_name;
+  alias= (lower_case_table_names == 2) ? table_list->alias : table_name;
+  db=table_list->db;
+  if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db))
+    new_db= db;
+  build_table_filename(reg_path, sizeof(reg_path) - 1, db, table_name, reg_ext, 0);
+  build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0);
+
+
+  table->use_all_columns();
+
+  /*
+    Prohibit changing of the UNION list of a non-temporary MERGE table
+    under LOCK tables. It would be quite difficult to reuse a shrinked
+    set of tables from the old table or to open a new TABLE object for
+    an extended list and verify that they belong to locked tables.
+  */
+  if (thd->locked_tables &&
+      (create_info->used_fields & HA_CREATE_USED_UNION) &&
+      (table->s->tmp_table == NO_TMP_TABLE))
+  {
+    my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+    DBUG_RETURN(TRUE);
+  }
+
+  /* Check that we are not trying to rename to an existing table */
+  if (new_name)
+  {
+    DBUG_PRINT("info", ("new_db.new_name: '%s'.'%s'", new_db, new_name));
+    strmov(new_name_buff,new_name);
+    strmov(new_alias= new_alias_buff, new_name);
+    if (lower_case_table_names)
+    {
+      if (lower_case_table_names != 2)
+      {
+	my_casedn_str(files_charset_info, new_name_buff);
+	new_alias= new_name;			// Create lower case table name
+      }
+      my_casedn_str(files_charset_info, new_name);
+    }
+    if (new_db == db &&
+	!my_strcasecmp(table_alias_charset, new_name_buff, table_name))
+    {
+      /*
+	Source and destination table names are equal: make later check
+	easier.
+      */
+      new_alias= new_name= table_name;
+    }
+    else
+    {
+      if (table->s->tmp_table != NO_TMP_TABLE)
+      {
+	if (find_temporary_table(thd,new_db,new_name_buff))
+	{
+	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff);
+	  DBUG_RETURN(TRUE);
+	}
+      }
+      else
+      {
+        if (lock_table_name_if_not_cached(thd, new_db, new_name, &name_lock))
+          DBUG_RETURN(TRUE);
+        if (!name_lock)
+        {
+	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
+	  DBUG_RETURN(TRUE);
+        }
+
+        build_table_filename(new_name_buff, sizeof(new_name_buff) - 1,
+                             new_db, new_name_buff, reg_ext, 0);
+        if (!access(new_name_buff, F_OK))
+	{
+	  /* Table will be closed in do_command() */
+	  my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
+	  goto err;
+	}
+      }
+    }
+  }
+  else
+  {
+    new_alias= (lower_case_table_names == 2) ? alias : table_name;
+    new_name= table_name;
+  }
+
+  old_db_type= table->s->db_type();
+  if (!create_info->db_type)
+  {
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+    if (table->part_info &&
+        create_info->used_fields & HA_CREATE_USED_ENGINE)
+    {
+      /*
+        This case happens when the user specified
+        ENGINE = x where x is a non-existing storage engine
+        We set create_info->db_type to default_engine_type
+        to ensure we don't change underlying engine type
+        due to a erroneously given engine name.
+      */
+      create_info->db_type= table->part_info->default_engine_type;
+    }
+    else
+#endif
+      create_info->db_type= old_db_type;
+  }
+
+  if (check_engine(thd, new_name, create_info))
+    goto err;
+  new_db_type= create_info->db_type;
+
+  if ((new_db_type != old_db_type ||
+       alter_info->flags & ALTER_PARTITION) &&
+      !table->file->can_switch_engines())
+  {
+    my_error(ER_ROW_IS_REFERENCED, MYF(0));
+    goto err;
+  }
+
+  /*
+   If this is an ALTER TABLE and no explicit row type specified reuse
+   the table's row type.
+   Note : this is the same as if the row type was specified explicitly.
+  */
+  if (create_info->row_type == ROW_TYPE_NOT_USED)
+  {
+    /* ALTER TABLE without explicit row type */
+    create_info->row_type= table->s->row_type;
+  }
+  else
+  {
+    /* ALTER TABLE with specific row type */
+    create_info->used_fields |= HA_CREATE_USED_ROW_FORMAT;
+  }
+
+  DBUG_PRINT("info", ("old type: %s  new type: %s",
+             ha_resolve_storage_engine_name(old_db_type),
+             ha_resolve_storage_engine_name(new_db_type)));
+  if (ha_check_storage_engine_flag(old_db_type, HTON_ALTER_NOT_SUPPORTED) ||
+      ha_check_storage_engine_flag(new_db_type, HTON_ALTER_NOT_SUPPORTED))
+  {
+    DBUG_PRINT("info", ("doesn't support alter"));
+    my_error(ER_ILLEGAL_HA, MYF(0), table_name);
+    goto err;
+  }
+
+  thd_proc_info(thd, "setup");
+  if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
+      !table->s->tmp_table) // no need to touch frm
+  {
+    switch (alter_info->keys_onoff) {
+    case LEAVE_AS_IS:
+      break;
+    case ENABLE:
+      /*
+        wait_while_table_is_used() ensures that table being altered is
+        opened only by this thread and that TABLE::TABLE_SHARE::version
+        of TABLE object corresponding to this table is 0.
+        The latter guarantees that no DML statement will open this table
+        until ALTER TABLE finishes (i.e. until close_thread_tables())
+        while the fact that the table is still open gives us protection
+        from concurrent DDL statements.
+      */
+      VOID(pthread_mutex_lock(&LOCK_open));
+      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
+      VOID(pthread_mutex_unlock(&LOCK_open));
+      DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
+      error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
+      /* COND_refresh will be signaled in close_thread_tables() */
+      break;
+    case DISABLE:
+      VOID(pthread_mutex_lock(&LOCK_open));
+      wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
+      VOID(pthread_mutex_unlock(&LOCK_open));
+      error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
+      /* COND_refresh will be signaled in close_thread_tables() */
+      break;
+    default:
+      DBUG_ASSERT(FALSE);
+      error= 0;
+      break;
+    }
+    if (error == HA_ERR_WRONG_COMMAND)
+    {
+      error= 0;
+      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
+			  table->alias);
+    }
+
+    VOID(pthread_mutex_lock(&LOCK_open));
+    /*
+      Unlike to the above case close_cached_table() below will remove ALL
+      instances of TABLE from table cache (it will also remove table lock
+      held by this thread). So to make actual table renaming and writing
+      to binlog atomic we have to put them into the same critical section
+      protected by LOCK_open mutex. This also removes gap for races between
+      access() and mysql_rename_table() calls.
+    */
+
+    if (!error && (new_name != table_name || new_db != db))
+    {
+      thd_proc_info(thd, "rename");
+      /*
+        Then do a 'simple' rename of the table. First we need to close all
+        instances of 'source' table.
+      */
+      close_cached_table(thd, table);
+      /*
+        Then, we want check once again that target table does not exist.
+        Actually the order of these two steps does not matter since
+        earlier we took name-lock on the target table, so we do them
+        in this particular order only to be consistent with 5.0, in which
+        we don't take this name-lock and where this order really matters.
+        TODO: Investigate if we need this access() check at all.
+      */
+      if (!access(new_name_buff,F_OK))
+      {
+	my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name);
+	error= -1;
+      }
+      else
+      {
+	*fn_ext(new_name)=0;
+	if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
+	  error= -1;
+        else if (Table_triggers_list::change_table_name(thd, db, table_name,
+                                                        new_db, new_alias))
+        {
+          VOID(mysql_rename_table(old_db_type, new_db, new_alias, db,
+                                  table_name, 0));
+          error= -1;
+        }
+      }
+    }
+
+    if (error == HA_ERR_WRONG_COMMAND)
+    {
+      error= 0;
+      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+			  ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
+			  table->alias);
+    }
+
+    if (!error)
+    {
+      error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+      if (!error)
+        my_ok(thd);
+    }
+    else if (error > 0)
+    {
+      table->file->print_error(error, MYF(0));
+      error= -1;
+    }
+    if (name_lock)
+      unlink_open_table(thd, name_lock, FALSE);
+    VOID(pthread_mutex_unlock(&LOCK_open));
+    table_list->table= NULL;                    // For query cache
+    query_cache_invalidate3(thd, table_list, 0);
+    DBUG_RETURN(error);
+  }
+
+  /* We have to do full alter table. */
+
+#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 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;
+
+  if (mysql_prepare_alter_table(thd, table, create_info, alter_info))
+    goto err;
+
+  set_table_default_charset(thd, create_info, db);
+
+
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+  if (fast_alter_partition)
+  {
+    DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
+                                           create_info, table_list,
+                                           db, table_name,
+                                           fast_alter_partition));
+  }
+#endif
+
+  if (thd->variables.old_alter_table
+      || (table->s->db_type() != create_info->db_type)
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+      || (partition_changed && !(create_info->db_type->partition_flags() & HA_USE_AUTO_PARTITION))
+#endif
+     )
+  {
+    if (alter_info->build_method == HA_BUILD_ONLINE)
+    {
+      my_error(ER_NOT_SUPPORTED_YET, MYF(0), thd->query());
+      goto err;
+    }
+    alter_info->build_method= HA_BUILD_OFFLINE;
+  }
+
+  if (alter_info->build_method != HA_BUILD_OFFLINE)
+  {
+    TABLE *altered_table= 0;
+    HA_ALTER_INFO ha_alter_info;
+    HA_ALTER_FLAGS ha_alter_flags;
+    uint table_changes= IS_EQUAL_YES;
+    bool need_copy_table= TRUE;
+    /* Check how much the tables differ. */
+    if (new_compare_tables(thd, table, alter_info,
+                       create_info, order_num,
+                       &ha_alter_flags,
+                       &ha_alter_info,
+                       &table_changes))
+    {
+      DBUG_RETURN(TRUE);
+    }
+
+    /*
+      Check if storage engine supports altering the table
+      on-line.
+    */
+
+#ifndef DBUG_OFF
+    {
+      char dbug_string[HA_MAX_ALTER_FLAGS+1];
+      ha_alter_flags.print(dbug_string);
+      DBUG_PRINT("info", ("need_copy_table: %u, table_changes: %u, Real alter_flags:  %s",
+                          need_copy_table, table_changes,
+                          (char *) dbug_string));
+    }
+#endif
+
+    /*
+      If table is not renamed, changed database and
+      some change was detected then check if engine
+      can do the change on-line
+    */
+    if (new_name == table_name && new_db == db &&
+        !ha_alter_flags.is_clear_all())
+    {
+      Alter_info tmp_alter_info(*alter_info, thd->mem_root);
+
+      /*
+        If no table rename,
+        check if table can be altered on-line
+      */
+      if (!(altered_table= create_altered_table(thd,
+                                                table,
+                                                new_db,
+                                                create_info,
+                                                &tmp_alter_info,
+                                                !strcmp(db, new_db))))
+        goto err;
+
+      switch (table->file->check_if_supported_alter(altered_table,
+                                                    create_info,
+                                                    &ha_alter_flags,
+                                                    table_changes)) {
+      case HA_ALTER_SUPPORTED_WAIT_LOCK:
+      case HA_ALTER_SUPPORTED_NO_LOCK:
+        /*
+          @todo: Currently we always acquire an exclusive name
+          lock on the table metadata when performing fast or online
+          ALTER TABLE. In future we may consider this unnecessary,
+          and narrow the scope of the exclusive name lock to only
+          cover manipulation with .frms. Storage engine API
+          call check_if_supported_alter has provision for this
+          already now.
+        */
+        need_copy_table= FALSE;
+        break;
+      case HA_ALTER_NOT_SUPPORTED:
+        if (alter_info->build_method == HA_BUILD_ONLINE)
+        {
+          my_error(ER_NOT_SUPPORTED_YET, MYF(0), thd->query());
+          close_temporary_table(thd, altered_table, 1, 1);
+          goto err;
+        }
+        need_copy_table= TRUE;
+        break;
+      case HA_ALTER_ERROR:
+      default:
+        close_temporary_table(thd, altered_table, 1, 1);
+        goto err;
+      }
+#ifndef DBUG_OFF
+      {
+        char dbug_string[HA_MAX_ALTER_FLAGS+1];
+        ha_alter_flags.print(dbug_string);
+        DBUG_PRINT("info", ("need_copy_table: %u, table_changes: %u, Real alter_flags:  %s",
+                            need_copy_table, table_changes,
+                            (char *) dbug_string));
+      }
+#endif
+
+    }
+    /* TODO need to check if changes can be handled as fast ALTER TABLE */
+    if (!altered_table)
+      need_copy_table= TRUE;
+
+    if (!need_copy_table)
+    {
+      error= mysql_fast_or_online_alter_table(thd,
+                                              table,
+                                              altered_table,
+                                              create_info,
+                                              &ha_alter_info,
+                                              &ha_alter_flags,
+                                              alter_info->keys_onoff);
+      if (thd->lock)
+      {
+        mysql_unlock_tables(thd, thd->lock);
+        thd->lock=0;
+      }
+      close_temporary_table(thd, altered_table, 1, 1);
+
+      if (error)
+      {
+        switch (error) {
+        case(-1):
+          goto err_with_placeholders;
+        default:
+          goto err;
+        }
+      }
+      else
+      {
+        pthread_mutex_lock(&LOCK_open);
+        goto end_online;
+      }
+    }
+
+    if (altered_table)
+      close_temporary_table(thd, altered_table, 1, 1);
+  }
+
+  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);
+
+
+  /* Create a temporary table with the new format */
+  if ((error= create_temporary_table(thd, table, new_db, tmp_name, 
+                                     create_info, alter_info, 
+                                     !strcmp(db, new_db))))
+  {
+    goto err;
+  }
+
+  /* Open the table so we need to copy the data to it. */
+  if (table->s->tmp_table)
+  {
+    TABLE_LIST tbl;
+    bzero((void*) &tbl, sizeof(tbl));
+    tbl.db= new_db;
+    tbl.table_name= tbl.alias= tmp_name;
+    /* Table is in thd->temporary_tables */
+    new_table= open_table(thd, &tbl, thd->mem_root, (bool*) 0,
+                          MYSQL_LOCK_IGNORE_FLUSH);
+  }
+  else
+  {
+    char path[FN_REFLEN+1];
+    /* table is a normal table: Create temporary table in same directory */
+    build_table_filename(path, sizeof(path)-1, new_db, tmp_name, "",
+                         FN_IS_TMP);
+    /* Open our intermediate table */
+    new_table=open_temporary_table(thd, path, new_db, tmp_name, 0);
+  }
+  if (!new_table)
+    goto err1;
+
+  /* Copy the data if necessary. */
+  thd->count_cuted_fields= CHECK_FIELD_WARN;	// calc cuted fields
+  thd->cuted_fields=0L;
+  copied=deleted=0;
+  /*
+    We do not copy data for MERGE tables. Only the children have data.
+    MERGE tables have HA_NO_COPY_ON_ALTER set.
+  */
+  if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER))
+  {
+    /* 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;
+    thd_proc_info(thd, "copy to tmp table");
+    error= copy_data_between_tables(table, new_table,
+                                    alter_info->create_list, ignore,
+                                    order_num, order, &copied, &deleted,
+                                    alter_info->keys_onoff,
+                                    alter_info->error_if_not_empty);
+  }
+  else
+  {
+    VOID(pthread_mutex_lock(&LOCK_open));
+    wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
+    VOID(pthread_mutex_unlock(&LOCK_open));
+    thd_proc_info(thd, "manage keys");
+    alter_table_manage_keys(table, table->file->indexes_are_disabled(),
+                            alter_info->keys_onoff);
+    error= ha_autocommit_or_rollback(thd, 0);
+    if (end_active_trans(thd))
+      error= 1;
+  }
+  thd->count_cuted_fields= CHECK_FIELD_IGNORE;
+
+  if (table->s->tmp_table != NO_TMP_TABLE)
+  {
+    /* We changed a temporary table */
+    if (error)
+      goto err1;
+    /* Close lock if this is a transactional table */
+    if (thd->lock)
+    {
+      mysql_unlock_tables(thd, thd->lock);
+      thd->lock=0;
+    }
+    /* Remove link to old table and rename the new one */
+    close_temporary_table(thd, table, 1, 1);
+    /* Should pass the 'new_name' as we store table name in the cache */
+    if (rename_temporary_table(thd, new_table, new_db, new_name))
+      goto err1;
+    /* We don't replicate alter table statement on temporary tables */
+    if (!thd->current_stmt_binlog_row_based &&
+        write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
+      DBUG_RETURN(TRUE);
+    goto end_temporary;
+  }
+
+  if (new_table)
+  {
+    /*
+      Close the intermediate table that will be the new table.
+      Note that MERGE tables do not have their children attached here.
+    */
+    intern_close_table(new_table);
+    my_free(new_table,MYF(0));
+  }
+  DEBUG_SYNC(thd, "alter_table_before_rename_result_table");
+  VOID(pthread_mutex_lock(&LOCK_open));
+  if (error)
+  {
+    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
+    VOID(pthread_mutex_unlock(&LOCK_open));
+    goto err;
+  }
+
+  /*
+    Data is copied. Now we:
+    1) Wait until all other threads close old version of table.
+    2) Close instances of table open by this thread and replace them
+       with exclusive name-locks.
+    3) Rename the old table to a temp name, rename the new one to the
+       old name.
+    4) If we are under LOCK TABLES and don't do ALTER TABLE ... RENAME
+       we reopen new version of table.
+    5) Write statement to the binary log.
+    6) If we are under LOCK TABLES and do ALTER TABLE ... RENAME we
+       remove name-locks from list of open tables and table cache.
+    7) If we are not not under LOCK TABLES we rely on close_thread_tables()
+       call to remove name-locks from table cache and list of open table.
+  */
+
+  thd_proc_info(thd, "rename result table");
+  my_snprintf(old_name, sizeof(old_name), "%s2-%lx-%lx", tmp_file_prefix,
+	      current_pid, thd->thread_id);
+  if (lower_case_table_names)
+    my_casedn_str(files_charset_info, old_name);
+
+  wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME);
+  close_data_files_and_morph_locks(thd, db, table_name);
+
+  error=0;
+  save_old_db_type= old_db_type;
+
+  /*
+    This leads to the storage engine (SE) not being notified for renames in
+    mysql_rename_table(), because we just juggle with the FRM and nothing
+    more. If we have an intermediate table, then we notify the SE that
+    it should become the actual table. Later, we will recycle the old table.
+    However, in case of ALTER TABLE RENAME there might be no intermediate
+    table. This is when the old and new tables are compatible, according to
+    compare_table(). Then, we need one additional call to
+    mysql_rename_table() with flag NO_FRM_RENAME, which does nothing else but
+    actual rename in the SE and the FRM is not touched. Note that, if the
+    table is renamed and the SE is also changed, then an intermediate table
+    is created and the additional call will not take place.
+  */
+  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,
+                              new_alias, FN_FROM_IS_TMP) ||
+           ((new_name != table_name || new_db != db) && // we also do rename
+           Table_triggers_list::change_table_name(thd, db, table_name,
+                                                  new_db, new_alias)))
+  {
+    /* Try to get everything back. */
+    error=1;
+    VOID(quick_rm_table(new_db_type,new_db,new_alias, 0));
+    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
+    VOID(mysql_rename_table(old_db_type, db, old_name, db, alias,
+                            FN_FROM_IS_TMP));
+  }
+
+  if (error)
+  {
+    /* This shouldn't happen. But let us play it safe. */
+    goto err_with_placeholders;
+  }
+
+  VOID(quick_rm_table(old_db_type, db, old_name, FN_IS_TMP));
+
+end_online:
+
+  if (thd->locked_tables && new_name == table_name && new_db == db)
+  {
+    thd->in_lock_tables= 1;
+    error= reopen_tables(thd, 1, 1);
+    thd->in_lock_tables= 0;
+    if (error)
+      goto err_with_placeholders;
+  }
+  VOID(pthread_mutex_unlock(&LOCK_open));
+
+  thd_proc_info(thd, "end");
+
+  DBUG_EXECUTE_IF("sleep_alter_before_main_binlog", my_sleep(6000000););
+  DEBUG_SYNC(thd, "alter_table_before_main_binlog");
+
+  ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE,
+                      thd->query(), thd->query_length(),
+                      db, table_name);
+
+  DBUG_ASSERT(!(mysql_bin_log.is_open() &&
+                thd->current_stmt_binlog_row_based &&
+                (create_info->options & HA_LEX_CREATE_TMP_TABLE)));
+  if (write_bin_log(thd, TRUE, thd->query(), thd->query_length()))
+    DBUG_RETURN(TRUE);
+  
+  table_list->table=0;				// For query cache
+  query_cache_invalidate3(thd, table_list, 0);
+
+  if (thd->locked_tables && (new_name != table_name || new_db != db))
+  {
+    /*
+      If are we under LOCK TABLES and did ALTER TABLE with RENAME we need
+      to remove placeholders for the old table and for the target table
+      from the list of open tables and table cache. If we are not under
+      LOCK TABLES we can rely on close_thread_tables() doing this job.
+    */
+    pthread_mutex_lock(&LOCK_open);
+    unlink_open_table(thd, table, FALSE);
+    unlink_open_table(thd, name_lock, FALSE);
+    pthread_mutex_unlock(&LOCK_open);
+  }
+
+end_temporary:
+  my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
+	      (ulong) (copied + deleted), (ulong) deleted,
+	      (ulong) thd->cuted_fields);
+  my_ok(thd, copied + deleted, 0L, tmp_name);
+  thd->some_tables_deleted=0;
+  DBUG_RETURN(FALSE);
+
+err1:
+  if (new_table)
+  {
+    /* close_temporary_table() frees the new_table pointer. */
+    close_temporary_table(thd, new_table, 1, 1);
+  }
+  else
+    VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
+
+err:
+  /*
+    No default value was provided for a DATE/DATETIME field, the
+    current sql_mode doesn't allow the '0000-00-00' value and
+    the table to be altered isn't empty.
+    Report error here.
+  */
+  if (alter_info->error_if_not_empty && thd->row_count)
+  {
+    const char *f_val= 0;
+    enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE;
+    switch (alter_info->datetime_field->sql_type)
+    {
+      case MYSQL_TYPE_DATE:
+      case MYSQL_TYPE_NEWDATE:
+        f_val= "0000-00-00";
+        t_type= MYSQL_TIMESTAMP_DATE;
+        break;
+      case MYSQL_TYPE_DATETIME:
+        f_val= "0000-00-00 00:00:00";
+        t_type= MYSQL_TIMESTAMP_DATETIME;
+        break;
+      default:
+        DBUG_ASSERT(0);
+    }
+    bool save_abort_on_warning= thd->abort_on_warning;
+    thd->abort_on_warning= TRUE;
+    make_truncated_value_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                                 f_val, strlength(f_val), t_type,
+                                 alter_info->datetime_field->field_name);
+    thd->abort_on_warning= save_abort_on_warning;
+  }
+  if (name_lock)
+  {
+    pthread_mutex_lock(&LOCK_open);
+    unlink_open_table(thd, name_lock, FALSE);
+    pthread_mutex_unlock(&LOCK_open);
+  }
+  DBUG_RETURN(TRUE);
+
+err_with_placeholders:
+  /*
+    An error happened while we were holding exclusive name-lock on table
+    being altered. To be safe under LOCK TABLES we should remove placeholders
+    from list of open tables list and table cache.
+  */
+  unlink_open_table(thd, table, FALSE);
+  if (name_lock)
+    unlink_open_table(thd, name_lock, FALSE);
+  VOID(pthread_mutex_unlock(&LOCK_open));
+  DBUG_RETURN(TRUE);
+}
+
+
+
+bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
+                       HA_CREATE_INFO *create_info,
+                       TABLE_LIST *table_list,
+                       Alter_info *alter_info,
+                       uint order_num, ORDER *order, bool ignore)
+{
+    //ulong alter_flags= 0;
+    //alter_flags= table_list->table->s->db_type() ? table_list->table->s->db_type()->alter_table_flags(alter_info->flags) : 0;    
+    //bool use_new = (alter_flags & HA_GENERAL_ONLINE)?true:false;
+    TABLE* table = NULL;
+    bool continue_alter = FALSE;
+    bool ret_val = extract_and_maybe_alter_table(
+        &table,
+        &continue_alter,
+        thd,
+        new_db,
+        new_name,
+        create_info,
+        table_list,
+        alter_info,
+        order_num,
+        order,
+        ignore
+        );
+    if (!continue_alter) return ret_val;
+    
+    if (table->s->db_type() && table->s->db_type()->alter_table_flags && (table->s->db_type()->alter_table_flags(alter_info->flags)&HA_GENERAL_ONLINE) != 0) {
+        return new_mysql_alter_table(
+            table,
+            thd,
+            new_db,
+            new_name,
+            create_info,
+            table_list,
+            alter_info,
+            order_num,
+            order,
+            ignore
+            );
+    }
+    else return old_mysql_alter_table(
+        table,
+        thd,
+        new_db,
+        new_name,
+        create_info,
+        table_list,
+        alter_info,
+        order_num,
+        order,
+        ignore
+        );
+}
+
 static int
 copy_data_between_tables(TABLE *from,TABLE *to,
 			 List<Create_field> &create,
Index: sql/mysql_priv.h
===================================================================
--- sql/mysql_priv.h	(revision 23869)
+++ sql/mysql_priv.h	(revision 24227)
@@ -262,6 +262,17 @@
   CHARSET_INFO *m_connection_cl;
 };
 
+/**
+  Opening modes for open_temporary_table and open_table_from_share
+*/
+
+enum open_table_mode
+{
+  OTM_OPEN= 0,
+  OTM_CREATE= 1,
+  OTM_ALTER= 2
+};
+
 /***************************************************************************
   Configuration parameters
 ****************************************************************************/
@@ -1571,7 +1582,10 @@
 int decide_logging_format(THD *thd, TABLE_LIST *tables);
 TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
 			    const char *table_name, bool link_in_list);
-bool rm_temporary_table(handlerton *base, char *path);
+TABLE *open_temporary_table_with_mode(THD *thd, const char *path, const char *db,
+			    const char *table_name, bool link_in_list,
+                            open_table_mode open_mode);
+bool rm_temporary_table(handlerton *base, char *path, bool frm_only);
 void free_io_cache(TABLE *entry);
 void intern_close_table(TABLE *entry);
 bool close_thread_table(THD *thd, TABLE **table_ptr);
@@ -2177,6 +2191,9 @@
 void free_table_share(TABLE_SHARE *share);
 int open_table_def(THD *thd, TABLE_SHARE *share, uint db_flags);
 void open_table_error(TABLE_SHARE *share, int error, int db_errno, int errarg);
+int open_table_from_share_with_mode(THD *thd, TABLE_SHARE *share, const char *alias,
+                          uint db_stat, uint prgflag, uint ha_open_flags,
+                          TABLE *outparam, open_table_mode open_mode);
 int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias,
                           uint db_stat, uint prgflag, uint ha_open_flags,
                           TABLE *outparam, bool is_create_table);
