diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index 6ff468cdbb8d14e24d8c4154824f851141fb5c8c..483ada5364c169b476614254cd4ba29fcbf88ea0 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -4331,9 +4331,8 @@ ha_innobase::analyze(
 }
 
 /**************************************************************************
-This is currently mapped to ::analyze. A better option would be to map this
-to "ALTER TABLE tablename TYPE=InnoDB", which seems to rebuild the table in
-MySQL. */
+This is mapped to "ALTER TABLE tablename TYPE=InnoDB", which rebuilds
+the table in MySQL. */
 
 int
 ha_innobase::optimize(
@@ -4341,7 +4340,7 @@ ha_innobase::optimize(
 	THD*		thd,		/* in: connection thread handle */
 	HA_CHECK_OPT*	check_opt)	/* in: currently ignored */
 {
-	return(ha_innobase::analyze(thd, check_opt));
+        return(HA_ADMIN_TRY_ALTER);
 }
 
 /***********************************************************************
diff --git a/sql/handler.h b/sql/handler.h
index e7dcb97f0e8667117cd9dcb2ac4a587b1e0fb320..c8d3d30aa23b3c292c364a6868f468a5c2957b5a 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -43,6 +43,7 @@
 #define HA_ADMIN_INTERNAL_ERROR  -4
 #define HA_ADMIN_INVALID         -5
 #define HA_ADMIN_REJECT          -6
+#define HA_ADMIN_TRY_ALTER       -7
 
 /* Bits in table_flags() to show what database can do */
 #define HA_READ_RND_SAME        1       /* Read RND-record to KEY-record
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index f04979255236739d3c840e4c1db6430218686a23..587fdf5d42b5491fbbb3cb96980850f1271b4c4a 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -531,7 +531,8 @@ int mysql_alter_table(THD *thd, char *new_db, char *new_name,
 		      List<Key> &keys,
 		      uint order_num, ORDER *order,
 		      enum enum_duplicates handle_duplicates,
-		      ALTER_INFO *alter_info);
+		      ALTER_INFO *alter_info, bool do_send_ok=1);
+int mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool do_send_ok);
 int mysql_create_like_table(THD *thd, TABLE_LIST *table,
                             HA_CREATE_INFO *create_info,
                             Table_ident *src_table);
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 384d05ad94ec87e94dca97998215eefb4f4431f3..f11bc9c34756767e44eee2640effba851ece3836 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2598,24 +2598,9 @@ unsent_create_error:
 	check_table_access(thd,SELECT_ACL | INSERT_ACL, tables,0))
       goto error; /* purecov: inspected */
     thd->slow_command=TRUE;
-    if (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC))
-    {
-      /* Use ALTER TABLE */
-      lex->create_list.empty();
-      lex->key_list.empty();
-      lex->col_list.empty();
-      lex->alter_info.reset();
-      bzero((char*) &create_info,sizeof(create_info));
-      create_info.db_type=DB_TYPE_DEFAULT;
-      create_info.row_type=ROW_TYPE_DEFAULT;
-      create_info.default_table_charset=default_charset_info;
-      res= mysql_alter_table(thd, NullS, NullS, &create_info,
-			     tables, lex->create_list,
-			     lex->key_list, 0, (ORDER *) 0,
-			     DUP_ERROR, &lex->alter_info);
-    }
-    else
-      res = mysql_optimize_table(thd, tables, &lex->check_opt);
+    res= (specialflag & (SPECIAL_SAFE_MODE | SPECIAL_NO_NEW_FUNC)) ?
+      mysql_recreate_table(thd, tables, 1) :
+      mysql_optimize_table(thd, tables, &lex->check_opt);
     /* ! we write after unlocking the table */
     if (!res && !lex->no_write_to_binlog)
     {
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index bdff1f52d04c251042950434185561742c2ad88e..4dd4b85c9fc0a374fe48bc6d70143ce025434fa7 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1804,6 +1804,9 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
     protocol->store(table_name, system_charset_info);
     protocol->store(operator_name, system_charset_info);
 
+send_result_message:
+
+    DBUG_PRINT("info", ("result_code: %d", result_code));
     switch (result_code) {
     case HA_ADMIN_NOT_IMPLEMENTED:
       {
@@ -1847,6 +1850,28 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
       protocol->store("Invalid argument",16, system_charset_info);
       break;
 
+    case HA_ADMIN_TRY_ALTER:
+    {
+      /*
+        This is currently used only by InnoDB. ha_innobase::optimize() answers
+        "try with alter", so here we close the table, do an ALTER TABLE,
+        reopen the table and do ha_innobase::analyze() on it.
+      */
+      close_thread_tables(thd);
+      TABLE_LIST *save_next= table->next;
+      table->next= 0;
+      result_code= mysql_recreate_table(thd, table, 0);
+      if (!result_code) // recreation went ok
+      {
+        if ((table->table= open_ltable(thd, table, lock_type)) &&
+            ((result_code= table->table->file->analyze(thd, check_opt)) > 0))
+          result_code= 0; // analyze went ok
+      }
+      result_code= result_code ? HA_ADMIN_FAILED : HA_ADMIN_OK;
+      table->next= save_next;
+      goto send_result_message;
+    }
+
     default:				// Probably HA_ADMIN_INTERNAL_ERROR
       protocol->store("error", 5, system_charset_info);
       protocol->store("Unknown - internal error during operation", 41
@@ -2476,7 +2501,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
 		      List<create_field> &fields, List<Key> &keys,
 		      uint order_num, ORDER *order,
 		      enum enum_duplicates handle_duplicates,
-		      ALTER_INFO *alter_info)
+		      ALTER_INFO *alter_info, bool do_send_ok)
 {
   TABLE *table,*new_table;
   int error;
@@ -2633,7 +2658,8 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
 	Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
 	mysql_bin_log.write(&qinfo);
       }
-      send_ok(thd);
+      if (do_send_ok)
+        send_ok(thd);
     }
     else
     {
@@ -3196,7 +3222,8 @@ end_temporary:
   my_snprintf(tmp_name, sizeof(tmp_name), ER(ER_INSERT_INFO),
 	      (ulong) (copied + deleted), (ulong) deleted,
 	      (ulong) thd->cuted_fields);
-  send_ok(thd,copied+deleted,0L,tmp_name);
+  if (do_send_ok)
+    send_ok(thd,copied+deleted,0L,tmp_name);
   thd->some_tables_deleted=0;
   DBUG_RETURN(0);
 
@@ -3346,6 +3373,39 @@ copy_data_between_tables(TABLE *from,TABLE *to,
 }
 
 
+/*
+  Recreates tables by calling mysql_alter_table().
+
+  SYNOPSIS
+    mysql_recreate_table()
+    thd			Thread handler
+    tables		Tables to recreate
+    do_send_ok          If we should send_ok() or leave it to caller
+
+ RETURN
+    Like mysql_alter_table().
+*/
+int mysql_recreate_table(THD *thd, TABLE_LIST *table_list,
+                         bool do_send_ok)
+{
+  DBUG_ENTER("mysql_recreate_table");
+  LEX *lex= thd->lex;
+  HA_CREATE_INFO create_info;
+  lex->create_list.empty();
+  lex->key_list.empty();
+  lex->col_list.empty();
+  lex->alter_info.reset();
+  bzero((char*) &create_info,sizeof(create_info));
+  create_info.db_type=DB_TYPE_DEFAULT;
+  create_info.row_type=ROW_TYPE_DEFAULT;
+  create_info.default_table_charset=default_charset_info;
+  DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
+                                table_list, lex->create_list,
+                                lex->key_list, 0, (ORDER *) 0,
+                                DUP_ERROR, &lex->alter_info, do_send_ok));
+}
+
+
 int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
 {
   TABLE_LIST *table;