diff --git a/mysql-test/include/wait_show_pattern.inc b/mysql-test/include/wait_show_pattern.inc
new file mode 100644
index 0000000000000000000000000000000000000000..c9f84ce7f08fdbc6569053f9c08f65efd65dea7f
--- /dev/null
+++ b/mysql-test/include/wait_show_pattern.inc
@@ -0,0 +1,51 @@
+# include/wait_show_pattern.inc
+#
+# SUMMARY
+#
+#   Waits until output produced by SHOW statement which particular type is
+#   specified as parameter matches certain pattern or maximum time reached.
+#
+# NOTES
+#
+#   Only the first row produced by the parameter statement is checked.
+#
+# USAGE
+#
+#   let $show_type= <Tail of SHOW statement>;
+#   let $show_pattern= 'Pattern to be used for LIKE matching';
+#   --source wait_show_pattern.inc
+#
+# EXAMPLES
+# 
+#   alter_table-big.test, wait_slave_status.inc
+#
+# SEE ALSO
+#
+#   wait_slave_status.inc, wait_condition.inc (>=5.1)
+#
+###############################################################################
+
+--disable_query_log
+
+# We accept to wait maximum 30 seconds (0.2 sec/loop).
+let $wait_counter= 150;
+while ($wait_counter)
+{
+  let $result= `SHOW $show_type`;
+  let $success= `SELECT '$result' LIKE $show_pattern`;
+  if ($success)
+  {
+    let $wait_counter= 0;
+  }
+  if (!$success)
+  {
+    real_sleep 0.2;
+    dec $wait_counter;
+  }
+}
+if (!$success)
+{
+  echo Timeout in wait_show_pattern.inc \$show_type= $show_type \$show_pattern= $show_pattern (\$result= '$result');
+}
+
+--enable_query_log
diff --git a/mysql-test/include/wait_slave_status.inc b/mysql-test/include/wait_slave_status.inc
index 7d3636e673cf04840fd0fa3476f69b27294de370..d8d048527cf7ce075ee42536504a8939b3ceeba2 100644
--- a/mysql-test/include/wait_slave_status.inc
+++ b/mysql-test/include/wait_slave_status.inc
@@ -104,50 +104,21 @@
 eval SELECT "let \$result_pattern= $result_pattern ;" AS "";
 SELECT '--source include/wait_slave_status.inc' AS "";
 
-# We accept to wait maximum 30 seconds (0.2 sec/loop).
-let $max_wait= 150;
-while ($max_wait)
-{
-    let $my_val= `SHOW SLAVE STATUS`;
-    # Now we have the first record of the SHOW result set as one fat string
-    # within the variable  $my_val.
-
-    eval SET @my_val = '$my_val';
-    # DEBUG eval SELECT @my_val AS "response to SHOW SLAVE STATUS";
+let $show_type= SLAVE STATUS;
+let $show_pattern= $result_pattern;
+--enable_query_log
 
-    eval SELECT @my_val LIKE $result_pattern INTO @success;
-    # @success is     '1' if we have a match
-    #                 '0' if we have no match
-    # DEBUG SELECT @success;
+--source include/wait_show_pattern.inc
 
-    let $success= `SELECT @success`;
-    let $no_success= `SELECT @success = 0`;
-    if ($success)
-    {
-       # We reached the expected result and want to jump out of the loop
-       # without unneeded sleeps.
-       # Attention: Do not set $max_wait to 0, because "while" with negative value
-       #            does not work.
-       let $max_wait= 1;
-    }
-    if ($no_success)
-    {
-       # We did not reach the expected result and will have to sleep again
-       # or jump out of the loop, when max_wait is exhausted.
-       real_sleep 0.2;
-    }
-    dec $max_wait;
-}
---enable_query_log
-if ($no_success)
+if (!$success)
 {
 let $message= ! Attention: Timeout in wait_slave_status.inc.
               |       Possible reasons with decreasing probability:
-              |       - The LIKE pattern ($result_pattern) is wrong, because the
+              |       - The LIKE pattern is wrong, because the
               |         testcase was altered or the layout of the
               |         SHOW SLAVE STATUS result set changed.
               |       - There is a new bug within the replication.
-              |       - We met an extreme testing environment and $max_wait is
+              |       - We met an extreme testing environment and timeout is
               |         too small.;
 --source include/show_msg80.inc
 --echo DEBUG INFO START (wait_slave_status.inc):
diff --git a/mysql-test/r/alter_table-big.result b/mysql-test/r/alter_table-big.result
new file mode 100644
index 0000000000000000000000000000000000000000..873978c60dea449b06fb79ddc893443996b82a9a
--- /dev/null
+++ b/mysql-test/r/alter_table-big.result
@@ -0,0 +1,18 @@
+drop table if exists t1, t2;
+create table t1 (n1 int, n2 int, n3 int,
+key (n1, n2, n3),
+key (n2, n3, n1),
+key (n3, n1, n2));
+create table t2 (i int);
+alter table t1 disable keys;
+reset master;
+alter table t1 enable keys;;
+insert into t2 values (1);
+insert into t1 values (1, 1, 1);
+show binlog events in 'master-bin.000001' from 98;
+Log_name	Pos	Event_type	Server_id	End_log_pos	Info
+master-bin.000001	#	Query	1	#	use `test`; insert into t2 values (1)
+master-bin.000001	#	Query	1	#	use `test`; alter table t1 enable keys
+master-bin.000001	#	Query	1	#	use `test`; insert into t1 values (1, 1, 1)
+drop tables t1, t2;
+End of 5.0 tests
diff --git a/mysql-test/t/alter_table-big.test b/mysql-test/t/alter_table-big.test
new file mode 100644
index 0000000000000000000000000000000000000000..9a773f48a9c9784d04c4f5709ed4144ab39af6bb
--- /dev/null
+++ b/mysql-test/t/alter_table-big.test
@@ -0,0 +1,62 @@
+# In order to be more or less robust test for bug#25044 has to take
+# significant time (e.g. about 9 seconds on my (Dmitri's) computer)
+# so we probably want execute it only in --big-test mode.
+# Also in 5.1 this test will require statement-based binlog.
+--source include/big_test.inc
+
+
+#
+# Test for bug #25044 "ALTER TABLE ... ENABLE KEYS acquires global
+# 'opening tables' lock".
+#
+# ALTER TABLE ... ENABLE KEYS should not acquire LOCK_open mutex for
+# the whole its duration as it prevents other queries from execution.
+--disable_warnings
+drop table if exists t1, t2;
+--enable_warnings
+connect (addconroot, localhost, root,,);
+connection default;
+create table t1 (n1 int, n2 int, n3 int,
+                key (n1, n2, n3),
+                key (n2, n3, n1),
+                key (n3, n1, n2));
+create table t2 (i int);
+
+# Populating 't1' table with keys disabled, so ALTER TABLE .. ENABLE KEYS
+# will run for some time
+alter table t1 disable keys;
+--disable_query_log
+insert into t1 values (RAND()*1000,RAND()*1000,RAND()*1000);
+let $1=19;
+while ($1)
+{
+ eval insert into t1 select RAND()*1000,RAND()*1000,RAND()*1000 from t1;
+ dec $1;
+}
+--enable_query_log
+
+# Later we use binlog to check the order in which statements are
+# executed so let us reset it first.
+reset master;
+--send alter table t1 enable keys;
+connection addconroot;
+let $show_type= PROCESSLIST;
+let $show_pattern= '%Repair by sorting%alter table t1 enable keys%';
+--source include/wait_show_pattern.inc
+# This statement should not be blocked by in-flight ALTER and therefore
+# should be executed and written to binlog before ALTER TABLE ... ENABLE KEYS
+# finishes.
+insert into t2 values (1);
+# And this should wait until the end of ALTER TABLE ... ENABLE KEYS.
+insert into t1 values (1, 1, 1);
+connection default;
+--reap
+# Check that statements were executed/binlogged in correct order.
+--replace_column 2 # 5 #
+show binlog events in 'master-bin.000001' from 98;
+
+# Clean up
+drop tables t1, t2;
+
+
+--echo End of 5.0 tests
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 4f5f4c29854b1896fba3461512575f9fdb1c4ae5..d9dadfbfd81137f2dcf3bc7972b1ead5bb4408b8 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -3163,19 +3163,30 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
   if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) &&
       !table->s->tmp_table) // no need to touch frm
   {
-    VOID(pthread_mutex_lock(&LOCK_open));
-
     switch (alter_info->keys_onoff) {
     case LEAVE_AS_IS:
       error= 0;
       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));
       error= table->file->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->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
       /* COND_refresh will be signaled in close_thread_tables() */
       break;
@@ -3188,6 +3199,16 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
       error= 0;
     }
 
+    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="rename";