List:Commits« Previous MessageNext Message »
From:Dmitry Lenev Date:October 18 2010 12:35pm
Subject:bzr push into mysql-5.5-runtime branch (Dmitry.Lenev:3166 to 3167)
View as plain text  
 3167 Dmitry Lenev	2010-10-18
      Draft patch refactoring global read lock implementation.
      Makes GRL yet another type of metadata lock and thus
      exposes it to deadlock detector in MDL subsystem.
      
      Solves bugs #54673 "It takes too long to get readlock for
      'FLUSH TABLES WITH READ LOCK'" and #57006 "Deadlock between
      HANDLER and FLUSH TABLES WITH READ LOCK".
      
      Work-in-progress.

    removed:
      mysql-test/t/flush_read_lock_kill-master.opt
    added:
      mysql-test/include/check_ftwrl_compatible.inc
      mysql-test/include/check_ftwrl_incompatible.inc
      mysql-test/r/flush_read_lock.result
      mysql-test/t/flush_read_lock.test
    modified:
      mysql-test/include/handler.inc
      mysql-test/include/wait_show_condition.inc
      mysql-test/r/flush.result
      mysql-test/r/flush_read_lock_kill.result
      mysql-test/r/handler_innodb.result
      mysql-test/r/handler_myisam.result
      mysql-test/r/mdl_sync.result
      mysql-test/suite/perfschema/r/dml_setup_instruments.result
      mysql-test/suite/perfschema/r/global_read_lock.result
      mysql-test/suite/perfschema/r/server_init.result
      mysql-test/suite/perfschema/t/global_read_lock.test
      mysql-test/suite/perfschema/t/server_init.test
      mysql-test/t/flush.test
      mysql-test/t/flush_block_commit.test
      mysql-test/t/flush_block_commit_notembedded.test
      mysql-test/t/flush_read_lock_kill.test
      mysql-test/t/lock_multi.test
      mysql-test/t/mdl_sync.test
      mysql-test/t/trigger_notembedded.test
      sql/ha_ndbcluster.cc
      sql/handler.cc
      sql/lock.cc
      sql/lock.h
      sql/log_event.cc
      sql/mdl.cc
      sql/mdl.h
      sql/mysqld.cc
      sql/mysqld.h
      sql/rpl_rli.cc
      sql/sp_head.cc
      sql/sql_base.cc
      sql/sql_base.h
      sql/sql_class.cc
      sql/sql_class.h
      sql/sql_handler.cc
      sql/sql_insert.cc
      sql/sql_lex.cc
      sql/sql_lex.h
      sql/sql_parse.cc
      sql/sql_parse.h
      sql/sql_rename.cc
      sql/sql_table.cc
      sql/sql_trigger.cc
      sql/sql_view.cc
      sql/sql_yacc.yy
      sql/transaction.cc
 3166 Konstantin Osipov	2010-10-14
      A fix and a test case for Bug#56540 "Exception (crash) in 
      sql_show.cc during rqg_info_schema test on Windows".
      
      Ensure we do not access freed memory when filling
      information_schema.views when one of the views
      could not be properly opened.
     @ mysql-test/r/information_schema.result
        Update results - a fix for Bug#56540.
     @ mysql-test/t/information_schema.test
        Add a test case for Bug#56540
     @ sql/sql_base.cc
        Push an error into the Diagnostics area
        when we return an error.
        This directs get_all_tables() to the execution
        branch which doesn't involve 'process_table()'
        when no table/view was opened.
     @ sql/sql_show.cc
        Do not try to access underlying table fields
        when opening of a view failed. The underlying
        table is closed in that case, and accessing
        its fields may lead to dereferencing a damaged 
        pointer.

    modified:
      mysql-test/r/information_schema.result
      mysql-test/t/information_schema.test
      sql/sql_base.cc
      sql/sql_show.cc
=== added file 'mysql-test/include/check_ftwrl_compatible.inc'
--- a/mysql-test/include/check_ftwrl_compatible.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/include/check_ftwrl_compatible.inc	2010-10-18 12:33:49 +0000
@@ -0,0 +1,147 @@
+#
+# SUMMARY
+#   Check that statement is compatible with FLUSH TABLES WITH READ LOCK.
+#
+# PARAMETERS 
+#   $con_aux1     Name of the 1st aux connection to be used by this script.
+#   $con_aux2     Name of the 1st aux connection to be used by this script.
+#   $statement    Statement to be checked.
+#   $cleanup_stmt Statement to be run in order to revert effects of
+#                 statement to be checked.
+#   $skip_3rd_chk Skip the 3rd stage of checking.
+#
+#
+# EXAMPLE
+#    flush.test
+#
+#
+--disable_result_log
+--disable_query_log
+
+# Reset DEBUG_SYNC facility for safety.
+set debug_sync= "RESET";
+
+#
+# First, check that statement can be run under FTWRL.
+#
+flush tables with read lock;
+--disable_abort_on_error
+--eval $statement
+--enable_abort_on_error
+let $err= $mysql_errno;
+if (!$err)
+{
+--echo Success: Was able to run '$statement' under FTWRL.
+unlock tables;
+if (`SELECT "$cleanup_stmt" <> ""`)
+{
+--eval $cleanup_stmt;
+}
+}
+if ($err)
+{
+--echo Error: Wasn't able to run '$statement' under FTWRL!
+unlock tables;
+}
+
+#
+# Then check that this statement won't be blocked by FTWRL
+# that is active in another connection
+connection $con_aux1;
+flush tables with read lock;
+
+connection default;
+--send_eval $statement;
+
+connection $con_aux1;
+
+--enable_result_log
+--enable_query_log
+let $wait_condition=
+  select count(*) = 0 from information_schema.processlist
+  where info = "$statement";
+--source include/wait_condition.inc
+--disable_result_log
+--disable_query_log
+
+if ($success)
+{
+--echo Success: Was able to run '$statement' with FTWRL active in another connection.
+
+connection default;
+--reap
+
+connection $con_aux1;
+unlock tables;
+
+connection default;
+}
+if (!$success)
+{
+--echo Error: Wasn't able to run '$statement' with FTWRL active in another connection!
+unlock tables;
+connection default;
+--reap
+}
+
+if (`SELECT "$cleanup_stmt" <> ""`)
+{
+--eval $cleanup_stmt;
+}
+
+if (`SELECT '$skip_3rd_check' = ''`)
+{
+#
+# Finally, let us check that FTWRL will succeed if this statement
+# is active but has already closed it.
+#
+connection default;
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send_eval $statement;
+
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+--send flush tables with read lock
+
+connection $con_aux2;
+--enable_result_log
+--enable_query_log
+let $wait_condition=
+  select count(*) = 0 from information_schema.processlist
+  where info = "flush tables with read lock";
+--source include/wait_condition.inc
+--disable_result_log
+--disable_query_log
+
+if ($success)
+{
+--echo Success: Was able to run FTWRL while '$statement' was active in another connection.
+connection $con_aux1;
+--reap
+unlock tables;
+set debug_sync='now SIGNAL go';
+connection default;
+--reap
+}
+if (!$success)
+{
+--echo Error: Wasn't able to run FTWRL while '$statement' was active in another connection!
+set debug_sync='now SIGNAL go';
+connection default;
+--reap
+connection $con_aux1;
+--reap
+unlock tables;
+connection default;
+}
+
+set debug_sync= "RESET";
+if (`SELECT "$cleanup_stmt" <> ""`)
+{
+--eval $cleanup_stmt;
+}
+
+}
+
+--enable_result_log
+--enable_query_log

=== added file 'mysql-test/include/check_ftwrl_incompatible.inc'
--- a/mysql-test/include/check_ftwrl_incompatible.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/include/check_ftwrl_incompatible.inc	2010-10-18 12:33:49 +0000
@@ -0,0 +1,150 @@
+#
+# SUMMARY
+#   Check that statement is incompatible with FLUSH TABLES WITH READ LOCK.
+#
+# PARAMETERS 
+#   $con_aux1      Name of the 1st aux connection to be used by this script.
+#   $con_aux2      Name of the 2nd aux connection to be used by this script.
+#   $statement     Statement to be checked.
+#   $cleanup_stmt1 The 1st statement to be run in order to revert effects
+#                  of statement to be checked.
+#   $cleanup_stmt2 The 2nd statement to be run in order to revert effects
+#                  of statement to be checked.
+#   $skip_3rd_chk  Skip the 3rd stage of checking.
+#
+# EXAMPLE
+#    flush.test
+#
+#
+--disable_result_log
+--disable_query_log
+
+# Reset DEBUG_SYNC facility for safety.
+set debug_sync= "RESET";
+
+#
+# First, check that statement cannot be run under FTWRL.
+#
+flush tables with read lock;
+--disable_abort_on_error
+--eval $statement
+--enable_abort_on_error
+let $err= $mysql_errno;
+if ($err)
+{
+--echo Success: Was not able to run '$statement' under FTWRL.
+unlock tables;
+}
+if (!$err)
+{
+--echo Error: Was able to run '$statement' under FTWRL!
+unlock tables;
+if (`SELECT "$cleanup_stmt1" <> ""`)
+{
+--eval $cleanup_stmt1;
+}
+if (`SELECT "$cleanup_stmt2" <> ""`)
+{
+--eval $cleanup_stmt2;
+}
+}
+
+
+#
+# Then check that this statement is blocked by FTWRL
+# that is active in another connection
+connection $con_aux1;
+flush tables with read lock;
+
+connection default;
+--send_eval $statement;
+
+connection $con_aux1;
+
+--enable_result_log
+--enable_query_log
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "$statement";
+--source include/wait_condition.inc
+--disable_result_log
+--disable_query_log
+
+if ($success)
+{
+--echo Success: '$statement' is blocked by FTWRL active in another connection.
+}
+if (!$success)
+{
+--echo Error: '$statement' wasn't blocked by FTWRL active in another connection!
+}
+unlock tables;
+
+connection default;
+--reap
+
+if (`SELECT "$cleanup_stmt1" <> ""`)
+{
+--eval $cleanup_stmt1;
+}
+if (`SELECT "$cleanup_stmt2" <> ""`)
+{
+--eval $cleanup_stmt2;
+}
+
+if (`SELECT '$skip_3rd_check' = ''`)
+{
+#
+# Finally, let us check that FTWRL will succeed if this statement
+# is active but has already closed it.
+#
+connection default;
+--eval set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send_eval $statement;
+
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+--send flush tables with read lock
+
+connection $con_aux2;
+--enable_result_log
+--enable_query_log
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "flush tables with read lock";
+--source include/wait_condition.inc
+--disable_result_log
+--disable_query_log
+
+if ($success)
+{
+--echo Success: FTWRL is blocked when '$statement' is active in another connection.
+}
+if (!$success)
+{
+--echo Error: FTWRL isn't blocked when '$statement' is active in another connection!
+}
+set debug_sync='now SIGNAL go';
+connection default;
+--reap
+connection $con_aux1;
+--reap
+unlock tables;
+connection default;
+
+set debug_sync= "RESET";
+
+if (`SELECT "$cleanup_stmt1" <> ""`)
+{
+--eval $cleanup_stmt1;
+}
+if (`SELECT "$cleanup_stmt2" <> ""`)
+{
+--eval $cleanup_stmt2;
+}
+}
+
+--enable_result_log
+--enable_query_log

=== modified file 'mysql-test/include/handler.inc'
--- a/mysql-test/include/handler.inc	2010-09-24 07:18:16 +0000
+++ b/mysql-test/include/handler.inc	2010-10-18 12:33:49 +0000
@@ -1545,8 +1545,6 @@ lock table not_exists_write read;
 --echo # We still have the read lock.
 --error ER_CANT_UPDATE_WITH_READLOCK
 drop table t1;
-handler t1 read next;
-handler t1 close;
 handler t1 open;
 select a from t2;
 handler t1 read next;

=== modified file 'mysql-test/include/wait_show_condition.inc'
--- a/mysql-test/include/wait_show_condition.inc	2009-10-20 06:30:15 +0000
+++ b/mysql-test/include/wait_show_condition.inc	2010-10-18 12:33:49 +0000
@@ -101,7 +101,7 @@ if (`SELECT '$wait_for_all' = '1'`)
 
 if (!$found)
 {
-  echo # Timeout in include/wait_show_condition.inc for $wait_condition;
+  echo # Timeout in include/wait_show_condition.inc for $condition;
   echo #         show_statement : $show_statement;
   echo #         field          : $field;
   echo #         condition      : $condition;

=== modified file 'mysql-test/r/flush.result'
--- a/mysql-test/r/flush.result	2010-09-09 14:29:14 +0000
+++ b/mysql-test/r/flush.result	2010-10-18 12:33:49 +0000
@@ -423,3 +423,31 @@ i
 4
 unlock tables;
 drop tables tm, t1, t2;
+#
+# Test for bug #57006 "Deadlock between HANDLER and
+#                      FLUSH TABLES WITH READ LOCK".
+#
+drop table if exists t1, t2;
+create table t1 (i int);
+create table t2 (i int);
+handler t1 open;
+# Switching to connection 'con1'.
+# Sending:
+flush tables with read lock;
+# Switching to connection 'con2'.
+# Wait until FTWRL starts waiting for 't1' to be closed.
+# Switching to connection 'default'.
+# The below statement should not cause deadlock.
+# Sending:
+insert into t2 values (1);
+# Switching to connection 'con2'.
+# Wait until INSERT starts to wait for FTWRL to go away.
+# Switching to connection 'con1'.
+# FTWRL should be able to continue now.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'default'.
+# Reap INSERT.
+handler t1 close;
+# Cleanup.
+drop tables t1, t2;

=== added file 'mysql-test/r/flush_read_lock.result'
--- a/mysql-test/r/flush_read_lock.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/flush_read_lock.result	2010-10-18 12:33:49 +0000
@@ -0,0 +1,1295 @@
+# Use MyISAM engine for the most of test tables
+# in order to avoid implicit commit affecting
+# tests for DDL statements.
+drop tables if exists t1_base, t2_base, t3_trans;
+drop database if exists mysqltest1;
+drop database if exists `#mysql50#mysqltest-2`;
+drop procedure if exists p1;
+drop function if exists f1;
+drop view if exists v1;
+drop procedure if exists p2;
+drop function if exists f2_base;
+drop function if exists f2_temp;
+create table t1_base(i int) engine=myisam;
+create table t2_base(j int) engine=myisam;
+create table t3_trans(i int) engine=innodb;
+create temporary table t1_temp(i int) engine=myisam;
+create temporary table t2_temp(j int) engine=myisam;
+create database mysqltest1;
+create database `#mysql50#mysqltest-2`;
+create procedure p1() begin end;
+create function f1() returns int return 0;
+create view v1 as select 1 as i;
+create procedure p2(i int) begin end;
+create function f2_base() returns int
+begin
+insert into t1_base values (1);
+return 0;
+end|
+create function f2_temp() returns int
+begin
+insert into t1_temp values (1);
+return 0;
+end|
+#
+# Test compatibility of FLUSH TABLES WITH READ LOCK 
+# with various statements.
+#
+# These tests don't cover some classes of statements:
+# - Replication-related - CHANGE MASTER TO, START/STOP SLAVE and etc
+#   (all compatible with FTWRL).
+# - Plugin-related - INSTALL/UNINSTALL (incompatible with FTWRL,
+#   require plugin support).
+#
+# 1) ALTER variants.
+#
+# 1.1) ALTER TABLE
+# 
+# 1.1.a) For base table should be incompatible with FTWRL.
+#
+Success: Was not able to run 'alter table t1_base add column c1 int' under FTWRL.
+Success: 'alter table t1_base add column c1 int' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter table t1_base add column c1 int' is active in another connection.
+#
+# 1.1.b) For temporaty table should be compatible with FTWRL.
+#
+Success: Was able to run 'alter table t1_temp add column c1 int' under FTWRL.
+Success: Was able to run 'alter table t1_temp add column c1 int' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'alter table t1_temp add column c1 int' was active in another connection.
+#
+# 1.2) ALTER DATABASE should be incompatible with FTWRL.
+#
+Success: Was not able to run 'alter database mysqltest1 default character set utf8' under FTWRL.
+Success: 'alter database mysqltest1 default character set utf8' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter database mysqltest1 default character set utf8' is active in another connection.
+#
+# 1.3) ALTER DATABASE UPGRADE DATA DIRECTORY NAME should be
+#      incompatible with FTWRL.
+#
+Success: Was not able to run 'alter database `#mysql50#mysqltest-2` upgrade data directory name' under FTWRL.
+Success: 'alter database `#mysql50#mysqltest-2` upgrade data directory name' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter database `#mysql50#mysqltest-2` upgrade data directory name' is active in another connection.
+#
+# 1.4) ALTER PROCEDURE should be incompatible with FTWRL.
+#
+Success: Was not able to run 'alter procedure p1 comment 'a'' under FTWRL.
+Success: 'alter procedure p1 comment 'a'' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter procedure p1 comment 'a'' is active in another connection.
+#
+# 1.5) ALTER FUNCTION should be incompatible with FTWRL.
+#
+Success: Was not able to run 'alter function f1 comment 'a'' under FTWRL.
+Success: 'alter function f1 comment 'a'' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter function f1 comment 'a'' is active in another connection.
+#
+# 1.6) ALTER VIEW should be incompatible with FTWRL.
+#
+Success: Was not able to run 'alter view v1 as select 2 as j' under FTWRL.
+Success: 'alter view v1 as select 2 as j' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter view v1 as select 2 as j' is active in another connection.
+#
+# 1.7) ALTER EVENT should be incompatible with FTWRL.
+#
+# TODO/FIXME: Unfortunately it releases protection against
+#             GRL a bit too early at this point.
+#
+# 1.x) The rest of ALTER statements are too special to
+#      to be tested here.
+#
+#
+# 2) ANALYZE TABLE statement is compatible with FTWRL.
+#
+# QQ: Is this a correct behavior?
+Success: Was able to run 'analyze table t1_base' under FTWRL.
+Success: Was able to run 'analyze table t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'analyze table t1_base' was active in another connection.
+#
+# 3) BEGIN, ROLLBACK and COMMIT statements.
+#    BEGIN and ROLLBACK are compatible with FTWRL.
+#    COMMIT is not.
+# 
+# We need a special test for these statements as
+# FTWRL commits a transaction and because COMMIT
+# is handled in a special way.
+flush tables with read lock;
+begin;
+# ROLLBACK is allowed under FTWRL although there
+# no much sense in it. FTWRL commits any previous
+# changes and doesn't allows any DML after it.
+# So such a ROLLBACK is always a no-op.
+rollback;
+# Although COMMIT is incompatible with FTWRL in
+# other senses it is still allowed under FTWRL.
+# This fact relied upon by some versions of
+# innobackup tool.
+# Similarly to ROLLBACK it is a no-op in this situation.
+commit;
+unlock tables;
+# Check that BEGIN/ROLLBACK are not and COMMIT is
+# blocked by active FTWRL in another connection
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+begin;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+# Do some work so ROLLBACK is not a no-op.
+insert into t3_trans values (1);
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+rollback;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+begin;
+# Do some work so COMMIT is not a no-op.
+insert into t3_trans values (1);
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+# Send:
+commit;
+# Switching to connection 'con1'.
+# Wait until COMMIT is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap COMMIT.
+delete from t3_trans;
+#
+# Check that COMMIT blocks FTWRL in another connection.
+begin;
+insert into t3_trans values (1);
+set debug_sync='RESET';
+set debug_sync='ha_commit_trans_after_acquire_commit_lock SIGNAL parked WAIT_FOR go';
+commit;
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+# Switching to connection 'con2'.
+# Wait until FTWRL is blocked.
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap COMMIT.
+# Switching to connection 'con1'.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'default'.
+delete from t3_trans;
+set debug_sync= "RESET";
+# We don't run similar test for BEGIN and ROLLBACK as
+# they release metadata locks in non-standard place.
+#
+# 4) BINLOG statement should be incompatible with FTWRL.
+#
+#
+# Provide format description BINLOG statement first.
+BINLOG '
+MfmqTA8BAAAAZwAAAGsAAAABAAQANS41LjctbTMtZGVidWctbG9nAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAx+apMEzgNAAgAEgAEBAQEEgAAVAAEGggAAAAICAgCAA==
+';
+# Now test compatibility for BINLOG statement which is 
+# equivalent to INSERT INTO t1_base VALUES (1).
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was not able to run 'BINLOG '
+MfmqTBMBAAAALgAAAN0AAAAAACgAAAAAAAEABHRlc3QAB3QxX2Jhc2UAAQMAAQ==
+MfmqTBcBAAAAIgAAAP8AAAAAACgAAAAAAAEAAf/+AQAAAA==
+'' under FTWRL.
+Success: 'BINLOG '
+MfmqTBMBAAAALgAAAN0AAAAAACgAAAAAAAEABHRlc3QAB3QxX2Jhc2UAAQMAAQ==
+MfmqTBcBAAAAIgAAAP8AAAAAACgAAAAAAAEAAf/+AQAAAA==
+'' is blocked by FTWRL active in another connection.
+#
+# 5) CALL statement. This statement uses resources in two
+#    ways through expressions used as parameters and through
+#    sub-statements. This test covers only usage through
+#    parameters as sub-statements do locking individually.
+#
+# 5.a) In simple case parameter expression should be compatible
+#      with FTWRL.
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'call p2((select count(*) from t1_base))' under FTWRL.
+Success: Was able to run 'call p2((select count(*) from t1_base))' with FTWRL active in another connection.
+#
+# 5.b) In case when expression uses function which updates
+#      base tables CALL should be incompatible with FTWRL.
+#
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was not able to run 'call p2(f2_base())' under FTWRL.
+Success: 'call p2(f2_base())' is blocked by FTWRL active in another connection.
+#
+# 5.c) If function used as argument updates temporary tables
+#      CALL statement should be compatible with FTWRL.
+#
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'call p2(f2_temp())' under FTWRL.
+Success: Was able to run 'call p2(f2_temp())' with FTWRL active in another connection.
+#
+# 6) CHECK TABLE statement is compatible with FTWRL.
+#
+Success: Was able to run 'check table t1_base' under FTWRL.
+Success: Was able to run 'check table t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'check table t1_base' was active in another connection.
+#
+# 7) CHECKSUM TABLE statement is compatible with FTWRL.
+#
+Success: Was able to run 'checksum table t1_base' under FTWRL.
+Success: Was able to run 'checksum table t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'checksum table t1_base' was active in another connection.
+#
+# 8) CREATE variants.
+#
+# 8.1) CREATE TABLE statement.
+#
+# 8.1.a) CREATE TABLE is incompatible with FTWRL when
+#        base table is created.
+Success: Was not able to run 'create table t3_base(i int)' under FTWRL.
+Success: 'create table t3_base(i int)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create table t3_base(i int)' is active in another connection.
+# 8.1.b) CREATE TABLE is compatible with FTWRL when
+#        temporary table is created.
+Success: Was able to run 'create temporary table t3_temp(i int)' under FTWRL.
+Success: Was able to run 'create temporary table t3_temp(i int)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'create temporary table t3_temp(i int)' was active in another connection.
+# 8.1.c) CREATE TABLE LIKE is incompatible with FTWRL when
+#        base table is created.
+Success: Was not able to run 'create table t3_base like t1_temp' under FTWRL.
+Success: 'create table t3_base like t1_temp' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create table t3_base like t1_temp' is active in another connection.
+# 8.1.d) CREATE TABLE LIKE is compatible with FTWRL when
+#        temporary table is created.
+Success: Was able to run 'create temporary table t3_temp like t1_base' under FTWRL.
+Success: Was able to run 'create temporary table t3_temp like t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'create temporary table t3_temp like t1_base' was active in another connection.
+# 8.1.e) CREATE TABLE SELECT is incompatible with FTWRL when
+#        base table is created.
+Success: Was not able to run 'create table t3_base select 1 as i' under FTWRL.
+Success: 'create table t3_base select 1 as i' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create table t3_base select 1 as i' is active in another connection.
+# 8.1.f) CREATE TABLE SELECT is compatible with FTWRL when
+#        temporary table is created.
+Success: Was able to run 'create temporary table t3_temp select 1 as i' under FTWRL.
+Success: Was able to run 'create temporary table t3_temp select 1 as i' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'create temporary table t3_temp select 1 as i' was active in another connection.
+# 8.2) CREATE INDEX statement.
+#
+# 8.2.a) CREATE TABLE is incompatible with FTWRL when
+#        applied to base table.
+Success: Was not able to run 'create index i on t1_base (i)' under FTWRL.
+Success: 'create index i on t1_base (i)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create index i on t1_base (i)' is active in another connection.
+# 8.2.b) CREATE TABLE is compatible with FTWRL when
+#        applied to temporary table.
+Success: Was able to run 'create index i on t1_temp (i)' under FTWRL.
+Success: Was able to run 'create index i on t1_temp (i)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'create index i on t1_temp (i)' was active in another connection.
+#
+# 8.3) CREATE DATABASE is incompatible with FTWRL.
+#
+Success: Was not able to run 'create database mysqltest2' under FTWRL.
+Success: 'create database mysqltest2' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create database mysqltest2' is active in another connection.
+#
+# 8.4) CREATE VIEW is incompatible with FTWRL.
+#
+Success: Was not able to run 'create view v2 as select 1 as j' under FTWRL.
+Success: 'create view v2 as select 1 as j' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create view v2 as select 1 as j' is active in another connection.
+#
+# 8.5) CREATE TRIGGER is incompatible with FTWRL.
+#
+Success: Was not able to run 'create trigger t1_bi before insert on t1_base for each row begin end' under FTWRL.
+Success: 'create trigger t1_bi before insert on t1_base for each row begin end' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create trigger t1_bi before insert on t1_base for each row begin end' is active in another connection.
+#
+# 8.6) CREATE FUNCTION is incompatible with FTWRL.
+#
+# Skip last part of compatibility testing as this statement
+# is internally consists of two statements.
+# QQ: Is this a problem?
+Success: Was not able to run 'create function f2() returns int return 0' under FTWRL.
+Success: 'create function f2() returns int return 0' is blocked by FTWRL active in another connection.
+#
+# 8.7) CREATE PROCEDURE is incompatible with FTWRL.
+#
+# Skip last part of compatibility testing as this statement
+# is internally consists of two statements.
+# QQ: Is this a problem?
+Success: Was not able to run 'create procedure p3() begin end' under FTWRL.
+Success: 'create procedure p3() begin end' is blocked by FTWRL active in another connection.
+#
+# 8.8) CREATE EVENT should be incompatible with FTWRL.
+#
+# TODO/FIXME: Unfortunately it releases protection against
+#             GRL a bit too early at this point.
+#
+# 8.9) CREATE USER should be incompatible with FTWRL.
+#
+Success: Was not able to run 'create user mysqltest_u1' under FTWRL.
+Success: 'create user mysqltest_u1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create user mysqltest_u1' is active in another connection.
+#
+# 8.x) The rest of CREATE variants are too special to test here.
+#
+#
+# 9) PREPARE, EXECUTE and DEALLOCATE PREPARE statements.
+#
+# 9.1) PREPARE statement is compatible with FTWRL as it
+#      doesn't change any data.
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'prepare stmt1 from 'insert into t1_base values (1)'' under FTWRL.
+Success: Was able to run 'prepare stmt1 from 'insert into t1_base values (1)'' with FTWRL active in another connection.
+#
+# 9.2) Compatibility of EXECUTE statement depends on statement
+#     to be executed.
+#
+# 9.2.a) EXECUTE for statement which is itself compatible with
+#       FTWRL should be compatible.
+prepare stmt1 from 'select * from t1_base';
+Success: Was able to run 'execute stmt1' under FTWRL.
+Success: Was able to run 'execute stmt1' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'execute stmt1' was active in another connection.
+deallocate prepare stmt1;
+#
+# 9.2.b) EXECUTE for statement which is incompatible with FTWRL
+#        should be also incompatible.
+#
+# Check that EXECUTE is not allowed under FTWRL.
+prepare stmt1 from 'insert into t1_base values (1)';
+flush tables with read lock;
+execute stmt1;
+ERROR HY000: Can't execute the query because you have a conflicting read lock
+unlock tables;
+# Check that active FTWRL in another connection
+# blocks EXECUTE which changes data.
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+execute stmt1 ;
+# Switching to connection 'con1'.
+# Check that EXECUTE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap EXECUTE.
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+execute stmt1; ;
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+# Switching to connection 'con2'.
+# Wait until FTWRL is blocked.
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap EXECUTE.
+# Switching to connection 'con1'.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'default'.
+set debug_sync= "RESET";
+delete from t1_base;
+deallocate prepare stmt1;
+#
+# 9.3) DEALLOCATE PREPARE is compatible with FTWRL.
+#
+prepare stmt1 from 'insert into t1_base values (1)';
+Success: Was able to run 'deallocate prepare stmt1' under FTWRL.
+Success: Was able to run 'deallocate prepare stmt1' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'deallocate prepare stmt1' was active in another connection.
+deallocate prepare stmt1;
+#
+# 10) DELETE variations.
+#
+# 10.1) Simple DELETE.
+# 
+# 10.1.a) Simple DELETE on base table is incompatible with FTWRL.
+Success: Was not able to run 'delete from t1_base' under FTWRL.
+Success: 'delete from t1_base' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'delete from t1_base' is active in another connection.
+# 
+# 10.1.b) Simple DELETE on temporary table is compatible with FTWRL.
+Success: Was able to run 'delete from t1_temp' under FTWRL.
+Success: Was able to run 'delete from t1_temp' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'delete from t1_temp' was active in another connection.
+#
+# 10.2) Multi DELETE.
+# 
+# 10.2.a) Multi DELETE on base tables is incompatible with FTWRL.
+Success: Was not able to run 'delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j' under FTWRL.
+Success: 'delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j' is active in another connection.
+# 
+# 10.2.b) Multi DELETE on temporary tables is compatible with FTWRL.
+Success: Was able to run 'delete t1_temp from t1_temp, t2_temp where t1_temp.i = t2_temp.j' under FTWRL.
+Success: Was able to run 'delete t1_temp from t1_temp, t2_temp where t1_temp.i = t2_temp.j' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'delete t1_temp from t1_temp, t2_temp where t1_temp.i = t2_temp.j' was active in another connection.
+#
+# 11) DESCRIBE should be compatible with FTWRL.
+#
+Success: Was able to run 'describe t1_base' under FTWRL.
+Success: Was able to run 'describe t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'describe t1_base' was active in another connection.
+#
+# 12) Compatibility of DO statement with FTWRL depends on its
+#     expression.
+#
+# 12.a) DO with expression which does not changes base table
+#       should be compatible with FTWRL.
+Success: Was able to run 'do (select count(*) from t1_base)' under FTWRL.
+Success: Was able to run 'do (select count(*) from t1_base)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'do (select count(*) from t1_base)' was active in another connection.
+#
+# 12.b) DO which calls SF updating base table should be
+#       incompatible with FTWRL.
+Success: Was not able to run 'do f2_base()' under FTWRL.
+Success: 'do f2_base()' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'do f2_base()' is active in another connection.
+#
+# 12.c) DO which calls SF updating temporary table should be
+#       compatible with FTWRL.
+Success: Was able to run 'do f2_temp()' under FTWRL.
+Success: Was able to run 'do f2_temp()' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'do f2_temp()' was active in another connection.
+#
+# 13) DROP variants.
+#
+# 13.1) DROP TABLES.
+#
+# 13.1.a) DROP TABLES which affects base tables is incompatible
+#         with FTWRL.
+Success: Was not able to run 'drop table t2_base' under FTWRL.
+Success: 'drop table t2_base' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop table t2_base' is active in another connection.
+# 13.1.b) DROP TABLES which affects only temporary tables
+#         in theory can be compatible with FTWRL.
+#         In practice it is not yet.
+Success: Was not able to run 'drop table t2_temp' under FTWRL.
+Success: 'drop table t2_temp' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop table t2_temp' is active in another connection.
+#
+# 13.1.c) DROP TEMPORARY TABLES should becompatible with FTWRL.
+Success: Was able to run 'drop temporary table t2_temp' under FTWRL.
+Success: Was able to run 'drop temporary table t2_temp' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'drop temporary table t2_temp' was active in another connection.
+#
+# 13.2) DROP INDEX.
+#
+# 13.2.a) DROP INDEX for base table is incompatible with FTWRL.
+create index i on t1_base (i);
+Success: Was not able to run 'drop index i on t1_base' under FTWRL.
+Success: 'drop index i on t1_base' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop index i on t1_base' is active in another connection.
+drop index i on t1_base;
+#
+# 13.2.b) DROP INDEX for temporary table is compatible with FTWRL.
+create index i on t1_temp (i);
+Success: Was able to run 'drop index i on t1_temp' under FTWRL.
+Success: Was able to run 'drop index i on t1_temp' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'drop index i on t1_temp' was active in another connection.
+drop index i on t1_temp;
+#
+# 13.3) DROP DATABASE is incompatible with FTWRL
+#
+Success: Was not able to run 'drop database mysqltest1' under FTWRL.
+Success: 'drop database mysqltest1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop database mysqltest1' is active in another connection.
+#
+# 13.4) DROP FUNCTION is incompatible with FTWRL.
+#
+# Skip last part of compatibility testing as this statement
+# is internally consists of two statements.
+# QQ: Is this a problem?
+Success: Was not able to run 'drop function f1' under FTWRL.
+Success: 'drop function f1' is blocked by FTWRL active in another connection.
+#
+# 13.5) DROP PROCEDURE is incompatible with FTWRL.
+#
+# Skip last part of compatibility testing as this statement
+# is internally consists of two statements.
+# QQ: Is this a problem?
+Success: Was not able to run 'drop procedure p1' under FTWRL.
+Success: 'drop procedure p1' is blocked by FTWRL active in another connection.
+#
+# 13.6) DROP USER should be incompatible with FTWRL.
+#
+create user mysqltest_u1;
+Success: Was not able to run 'drop user mysqltest_u1' under FTWRL.
+Success: 'drop user mysqltest_u1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop user mysqltest_u1' is active in another connection.
+drop user mysqltest_u1;
+#
+# 13.7) DROP VIEW should be incompatible with FTWRL.
+#
+Success: Was not able to run 'drop view v1' under FTWRL.
+Success: 'drop view v1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop view v1' is active in another connection.
+#
+# 13.8) DROP EVENT should be incompatible with FTWRL.
+#
+# TODO/FIXME: Unfortunately it releases protection against
+#             GRL a bit too early at this point.
+#
+# 13.9) DROP TRIGGER is incompatible with FTWRL.
+#
+create trigger t1_bi before insert on t1_base for each row begin end;
+Success: Was not able to run 'drop trigger t1_bi' under FTWRL.
+Success: 'drop trigger t1_bi' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop trigger t1_bi' is active in another connection.
+drop trigger t1_bi;
+#
+# 13.x) The rest of DROP variants are too special to test here.
+#
+#
+# 14) FLUSH variants.
+#
+# Test compatibility of _some_ important FLUSH variants with FTWRL.
+#
+# 14.1) FLUSH TABLES WITH READ LOCK is compatible with itself.
+# 
+# Check that FTWRL statements can be run while FTWRL
+# is active in another connection.
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# The second FTWRL in a row is allowed at the moment.
+# It does not make much sense as it does only flush.
+flush tables with read lock;
+unlock tables;
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+flush tables with read lock;
+unlock tables;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+#
+# 14.2) FLUSH TABLES <list> WITH READ LOCK is not blocked by
+#       active FTWRL. But since the latter keeps tables open
+#       FTWRL is blocked by FLUSH TABLES <list> WITH READ LOCK.
+flush tables with read lock;
+# FT <list> WRL is allowed under FTWRL at the moment.
+# It does not make much sense though.
+flush tables t1_base, t2_base with read lock;
+unlock tables;
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+flush tables t1_base, t2_base with read lock;
+unlock tables;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+flush tables t1_base, t2_base with read lock;
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'con2'.
+# Wait until FTWRL is blocked.
+# Switching to connection 'default'.
+unlock tables;
+# Switching to connection 'con1'.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'default'.
+#
+# 14.3) FLUSH TABLES is compatible with FTWRL.
+Success: Was able to run 'flush tables' under FTWRL.
+Success: Was able to run 'flush tables' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'flush tables' was active in another connection.
+#
+# 14.4) FLUSH TABLES <list> is compatible with FTWRL.
+Success: Was able to run 'flush table t1_base, t2_base' under FTWRL.
+Success: Was able to run 'flush table t1_base, t2_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'flush table t1_base, t2_base' was active in another connection.
+#
+# 14.5) FLUSH PRIVILEGES is compatible with FTWRL.
+Success: Was able to run 'flush privileges' under FTWRL.
+Success: Was able to run 'flush privileges' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'flush privileges' was active in another connection.
+#
+# 15) GRANT statement should be incompatible with FTWRL.
+#
+Success: Was not able to run 'grant all privileges on t1_base to mysqltest_u1' under FTWRL.
+Success: 'grant all privileges on t1_base to mysqltest_u1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'grant all privileges on t1_base to mysqltest_u1' is active in another connection.
+drop user mysqltest_u1;
+#
+# 16) All HANDLER variants are half-compatible with FTWRL.
+#     I.e. they are not blocked by active FTWRL. But since open
+#     HANDLER means open table instance FTWRL is blocked while
+#     HANDLER is not closed.
+#
+# Check that HANDLER statements succeed under FTWRL.
+flush tables with read lock;
+handler t1_base open;
+handler t1_base read first;
+i
+handler t1_base close;
+unlock tables;
+# Check that HANDLER statements can be run while FTWRL
+# is active in another connection.
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+handler t1_base open;
+handler t1_base read first;
+i
+handler t1_base close;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+#
+# 17) HELP statement is compatible with FTWRL.
+#
+Success: Was able to run 'help no_such_topic' under FTWRL.
+Success: Was able to run 'help no_such_topic' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'help no_such_topic' was active in another connection.
+#
+# 18) INSERT statement.
+# 
+# 18.a) Ordinary INSERT into base table is incompatible with FTWRL.
+Success: Was not able to run 'insert into t1_base values (1)' under FTWRL.
+Success: 'insert into t1_base values (1)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'insert into t1_base values (1)' is active in another connection.
+#
+# 18.b) Ordinary INSERT into temp table is compatible with FTWRL.
+Success: Was able to run 'insert into t1_temp values (1)' under FTWRL.
+Success: Was able to run 'insert into t1_temp values (1)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'insert into t1_temp values (1)' was active in another connection.
+#
+# 18.c) INSERT DELAYED is incompatible with FTWRL.
+Success: Was not able to run 'insert delayed into t1_base values (1)' under FTWRL.
+Success: 'insert delayed into t1_base values (1)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'insert delayed into t1_base values (1)' is active in another connection.
+delete from t1_base;
+# 
+# 18.d) INSERT SELECT into base table is incompatible with FTWRL.
+Success: Was not able to run 'insert into t1_base select * from t1_temp' under FTWRL.
+Success: 'insert into t1_base select * from t1_temp' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'insert into t1_base select * from t1_temp' is active in another connection.
+#
+# 18.e) INSERT SELECT into temp table is compatible with FTWRL.
+Success: Was able to run 'insert into t1_temp select * from t1_base' under FTWRL.
+Success: Was able to run 'insert into t1_temp select * from t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'insert into t1_temp select * from t1_base' was active in another connection.
+#
+# 19) KILL statement is compatible with FTWRL.
+#
+# Check that KILL can be run under FTWRL.
+flush tables with read lock;
+set @id:= connection_id();
+kill query @id;
+ERROR 70100: Query execution was interrupted
+unlock tables;
+# Check that KILL statements can be run while FTWRL
+# is active in another connection.
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+kill query @id;
+ERROR 70100: Query execution was interrupted
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+# Finally check that KILL doesn't block FTWRL
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+kill query @id;
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap KILL. 
+ERROR 70100: Query execution was interrupted
+set debug_sync='RESET';
+#
+# 20) LOAD DATA statement.
+# 
+# 20.a) LOAD DATA into base table is incompatible with FTWRL.
+Success: Was not able to run 'load data infile '../../std_data/rpl_loaddata.dat' into table t1_base (@dummy, i)' under FTWRL.
+Success: 'load data infile '../../std_data/rpl_loaddata.dat' into table t1_base (@dummy, i)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'load data infile '../../std_data/rpl_loaddata.dat' into table t1_base (@dummy, i)' is active in another connection.
+# 
+# 20.b) LOAD DATA into temporary table is compatible with FTWRL.
+Success: Was able to run 'load data infile '../../std_data/rpl_loaddata.dat' into table t1_temp (@dummy, i)' under FTWRL.
+Success: Was able to run 'load data infile '../../std_data/rpl_loaddata.dat' into table t1_temp (@dummy, i)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'load data infile '../../std_data/rpl_loaddata.dat' into table t1_temp (@dummy, i)' was active in another connection.
+#
+# 21) LOCK/UNLOCK TABLES statements.
+#
+# LOCK TABLES statement always (almost) blocks FTWRL as it
+# keeps tables open until UNLOCK TABLES.
+# Active FTWRL on the other hand blocks only those
+# LOCK TABLES which allow updating of base tables.
+#
+# 21.a) LOCK TABLES READ is allowed under FTWRL and
+#       is not blocked by active FTWRL.
+flush tables with read lock;
+lock tables t1_base read;
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+lock tables t1_base read;
+unlock tables;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+#
+# 21.b) LOCK TABLES WRITE on a base table is disallowed
+#       under FTWRL and should be blocked by active FTWRL.
+flush tables with read lock;
+lock tables t1_base write;
+ERROR HY000: Can't execute the query because you have a conflicting read lock
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+lock tables t1_base write ;
+# Switching to connection 'con1'.
+# Check that LOCK TABLES WRITE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap LOCK TABLES WRITE
+unlock tables;
+#
+# 21.c) LOCK TABLES WRITE on temporary table doesn't
+#       make much sense but is allowed under FTWRL
+#       and should not be blocked by active FTWRL.
+flush tables with read lock;
+lock tables t1_temp write;
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+lock tables t1_temp write;
+unlock tables;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+#
+# 22) OPTIMIZE TABLE statement.
+#
+# 22.a) OPTIMIZE TABLE of base table is incompatible with FTWRL.
+flush tables with read lock;
+# OPTIMIZE statement returns errors as part of result-set.
+optimize table t1_base;
+Table	Op	Msg_type	Msg_text
+test.t1_base	optimize	Error	Can't execute the query because you have a conflicting read lock
+test.t1_base	optimize	error	Corrupt
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+optimize table t1_base;
+# Switching to connection 'con1'.
+# Check that OPTIMIZE TABLE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap OPTIMIZE TABLE
+Table	Op	Msg_type	Msg_text
+test.t1_base	optimize	status	OK
+# We don't check that active OPTIMIZE TABLE blocks
+# FTWRL as this one of statements releasing metadata
+# locks in non-standard place.
+#
+# 22.b) OPTIMIZE TABLE of temporary table is compatible with FTWRL.
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'optimize table t1_temp' under FTWRL.
+Success: Was able to run 'optimize table t1_temp' with FTWRL active in another connection.
+#
+# 23) CACHE statement is compatible with FTWRL.
+#
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'cache index t1_base in default' under FTWRL.
+Success: Was able to run 'cache index t1_base in default' with FTWRL active in another connection.
+#
+# 24) LOAD INDEX statement is compatible with FTWRL.
+#
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'load index into cache t1_base' under FTWRL.
+Success: Was able to run 'load index into cache t1_base' with FTWRL active in another connection.
+#
+# 25) SAVEPOINT/RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT are
+#     compatible with FTWRL.
+#
+# Since manipulations on savepoint have to be done
+# inside transaction and FTWRL commits transaction we
+# need a special test for these statements.
+flush tables with read lock;
+begin;
+savepoint sv1;
+rollback to savepoint sv1;
+release savepoint sv1;
+unlock tables;
+commit;
+# Check that these statements are not blocked by
+# active FTWRL in another connection.
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+begin;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+# Do some changes to avoid SAVEPOINT and friends
+# being almost no-ops.
+insert into t3_trans values (1);
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+savepoint sv1;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+insert into t3_trans values (2);
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+rollback to savepoint sv1;
+release savepoint sv1;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+rollback;
+# Check that these statements don't block FTWRL in
+# another connection.
+begin;
+# Do some changes to avoid SAVEPOINT and friends
+# being almost no-ops.
+insert into t3_trans values (1);
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+savepoint sv1;
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap SAVEPOINT
+insert into t3_trans values (2);
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+rollback to savepoint sv1;
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap ROLLBACK TO SAVEPOINT
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+release savepoint sv1;
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap RELEASE SAVEPOINT
+rollback;
+set debug_sync= "RESET";
+#
+# 26) RENAME variants.
+#
+# 26.1) RENAME TABLES is incompatible with FTWRL.
+Success: Was not able to run 'rename table t1_base to t3_base' under FTWRL.
+Success: 'rename table t1_base to t3_base' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'rename table t1_base to t3_base' is active in another connection.
+#
+# 26.2) RENAME USER is incompatible with FTWRL.
+create user mysqltest_u1;
+Success: Was not able to run 'rename user mysqltest_u1 to mysqltest_u2' under FTWRL.
+Success: 'rename user mysqltest_u1 to mysqltest_u2' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'rename user mysqltest_u1 to mysqltest_u2' is active in another connection.
+drop user mysqltest_u1;
+#
+# 27) REPAIR TABLE statement.
+#
+# 27.a) REPAIR TABLE of base table is incompatible with FTWRL.
+flush tables with read lock;
+# REPAIR statement returns errors as part of result-set.
+repair table t1_base;
+Table	Op	Msg_type	Msg_text
+test.t1_base	repair	Error	Can't execute the query because you have a conflicting read lock
+test.t1_base	repair	error	Corrupt
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+repair table t1_base;
+# Switching to connection 'con1'.
+# Check that REPAIR TABLE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap REPAIR TABLE
+Table	Op	Msg_type	Msg_text
+test.t1_base	repair	status	OK
+# We don't check that active REPAIR TABLE blocks
+# FTWRL as this one of statements releasing metadata
+# locks in non-standard place.
+#
+# 27.b) REPAIR TABLE of temporary table is compatible with FTWRL.
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'repair table t1_temp' under FTWRL.
+Success: Was able to run 'repair table t1_temp' with FTWRL active in another connection.
+#
+# 28) REPLACE statement.
+# 
+# 28.a) Ordinary REPLACE into base table is incompatible with FTWRL.
+Success: Was not able to run 'replace into t1_base values (1)' under FTWRL.
+Success: 'replace into t1_base values (1)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'replace into t1_base values (1)' is active in another connection.
+#
+# 28.b) Ordinary REPLACE into temp table is compatible with FTWRL.
+Success: Was able to run 'replace into t1_temp values (1)' under FTWRL.
+Success: Was able to run 'replace into t1_temp values (1)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'replace into t1_temp values (1)' was active in another connection.
+# 
+# 28.c) REPLACE SELECT into base table is incompatible with FTWRL.
+Success: Was not able to run 'replace into t1_base select * from t1_temp' under FTWRL.
+Success: 'replace into t1_base select * from t1_temp' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'replace into t1_base select * from t1_temp' is active in another connection.
+#
+# 28.d) REPLACE SELECT into temp table is compatible with FTWRL.
+Success: Was able to run 'replace into t1_temp select * from t1_base' under FTWRL.
+Success: Was able to run 'replace into t1_temp select * from t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'replace into t1_temp select * from t1_base' was active in another connection.
+#
+# 29) REVOKE variants.
+# 
+# 29.1) REVOKE privileges is incompatible with FTWRL. 
+grant all privileges on t1_base to mysqltest_u1;
+Success: Was not able to run 'revoke all privileges on t1_base from mysqltest_u1' under FTWRL.
+Success: 'revoke all privileges on t1_base from mysqltest_u1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'revoke all privileges on t1_base from mysqltest_u1' is active in another connection.
+# 
+# 29.2) REVOKE ALL PRIVILEGES, GRANT OPTION is incompatible with FTWRL.
+Success: Was not able to run 'revoke all privileges, grant option from mysqltest_u1' under FTWRL.
+Success: 'revoke all privileges, grant option from mysqltest_u1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'revoke all privileges, grant option from mysqltest_u1' is active in another connection.
+drop user mysqltest_u1;
+#
+# 30) Compatibility of SELECT statement with FTWRL depends on
+#     locking mode used and on functions being invoked by it.
+#
+# 30.a) Simple SELECT which does not changes tables should be
+#       compatible with FTWRL.
+Success: Was able to run 'select count(*) from t1_base' under FTWRL.
+Success: Was able to run 'select count(*) from t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'select count(*) from t1_base' was active in another connection.
+# 30.b) SELECT ... FOR UPDATE is incompatible with FTWRL.
+Success: Was not able to run 'select count(*) from t1_base for update' under FTWRL.
+Success: 'select count(*) from t1_base for update' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'select count(*) from t1_base for update' is active in another connection.
+# 30.c) SELECT ... LOCK IN SHARE MODE is compatible with FTWRL.
+Success: Was able to run 'select count(*) from t1_base lock in share mode' under FTWRL.
+Success: Was able to run 'select count(*) from t1_base lock in share mode' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'select count(*) from t1_base lock in share mode' was active in another connection.
+#
+# 30.d) SELECT which calls SF updating base table should be
+#       incompatible with FTWRL.
+Success: Was not able to run 'select f2_base()' under FTWRL.
+Success: 'select f2_base()' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'select f2_base()' is active in another connection.
+#
+# 30.e) SELECT which calls SF updating temporary table should be
+#       compatible with FTWRL.
+Success: Was able to run 'select f2_temp()' under FTWRL.
+Success: Was able to run 'select f2_temp()' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'select f2_temp()' was active in another connection.
+#
+# 31) Compatibility of SET statement with FTWRL depends on its
+#     expression and on whether it is a special SET statement.
+#
+# 31.a) Ordinary SET with expression which does not
+#       changes base table should be compatible with FTWRL.
+# Skip last part of compatibility testing as our helper debug
+# sync-point doesn't work for SET statements.
+Success: Was able to run 'set @a:= (select count(*) from t1_base)' under FTWRL.
+Success: Was able to run 'set @a:= (select count(*) from t1_base)' with FTWRL active in another connection.
+#
+# 31.b) Ordinary SET which calls SF updating base table should
+#       be incompatible with FTWRL.
+# Skip last part of compatibility testing as our helper debug
+# sync-point doesn't work for SET statements.
+Success: Was not able to run 'set @a:= f2_base()' under FTWRL.
+Success: 'set @a:= f2_base()' is blocked by FTWRL active in another connection.
+#
+# 31.c) Ordinary SET which calls SF updating temporary table
+#       should be compatible with FTWRL.
+# Skip last part of compatibility testing as our helper debug
+# sync-point doesn't work for SET statements.
+Success: Was able to run 'set @a:= f2_temp()' under FTWRL.
+Success: Was able to run 'set @a:= f2_temp()' with FTWRL active in another connection.
+#
+# 31.d) Special SET variants that cause implicit commit or change
+#       data are incompatible with GRL.
+create user mysqltest_u1;
+# Skip last part of compatibility testing as our helper debug
+# sync-point doesn't work for SET statements.
+Success: Was not able to run 'set password for 'mysqltest_u1' = password('')' under FTWRL.
+Success: 'set password for 'mysqltest_u1' = password('')' is blocked by FTWRL active in another connection.
+drop user mysqltest_u1;
+#
+# 32) SHOW statements are compatible with FTWRL.
+#     Let us test _some_ of them.
+#
+# 32.1) SHOW TABLES.
+Success: Was able to run 'show tables from test' under FTWRL.
+Success: Was able to run 'show tables from test' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'show tables from test' was active in another connection.
+#
+# 32.1) SHOW TABLES.
+Success: Was able to run 'show tables from test' under FTWRL.
+Success: Was able to run 'show tables from test' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'show tables from test' was active in another connection.
+#
+# 32.2) SHOW EVENTS.
+Success: Was able to run 'show events from test' under FTWRL.
+Success: Was able to run 'show events from test' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'show events from test' was active in another connection.
+#
+# 32.3) SHOW GRANTS.
+create user mysqltest_u1;
+Success: Was able to run 'show grants for mysqltest_u1' under FTWRL.
+Success: Was able to run 'show grants for mysqltest_u1' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'show grants for mysqltest_u1' was active in another connection.
+drop user mysqltest_u1;
+#
+# 32.4) SHOW CREATE TABLE.
+Success: Was able to run 'show create table t1_base' under FTWRL.
+Success: Was able to run 'show create table t1_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'show create table t1_base' was active in another connection.
+#
+# 32.5) SHOW CREATE FUNCTION.
+Success: Was able to run 'show create function f1' under FTWRL.
+Success: Was able to run 'show create function f1' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'show create function f1' was active in another connection.
+#
+# 33) SIGNAL statement is compatible with FTWRL.
+#
+# Note that we don't cover RESIGNAL as it requires
+# active handler context. 
+Success: Was able to run 'signal sqlstate '01000'' under FTWRL.
+Success: Was able to run 'signal sqlstate '01000'' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'signal sqlstate '01000'' was active in another connection.
+#
+# 34) TRUNCATE TABLE statement.
+# 
+# 34.a) TRUNCATE of base table is incompatible with FTWRL.
+Success: Was not able to run 'truncate table t1_base' under FTWRL.
+Success: 'truncate table t1_base' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'truncate table t1_base' is active in another connection.
+# 
+# 34.b) TRUNCATE of temporary table is compatible with FTWRL.
+Success: Was able to run 'truncate table t1_temp' under FTWRL.
+Success: Was able to run 'truncate table t1_temp' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'truncate table t1_temp' was active in another connection.
+#
+# 35) UPDATE variants.
+#
+# 35.1) Simple UPDATE.
+# 
+# 35.1.a) Simple UPDATE on base table is incompatible with FTWRL.
+Success: Was not able to run 'update t1_base set i= 1 where i = 0' under FTWRL.
+Success: 'update t1_base set i= 1 where i = 0' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'update t1_base set i= 1 where i = 0' is active in another connection.
+# 
+# 35.1.b) Simple UPDATE on temporary table is compatible with FTWRL.
+Success: Was able to run 'update t1_temp set i= 1 where i = 0' under FTWRL.
+Success: Was able to run 'update t1_temp set i= 1 where i = 0' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'update t1_temp set i= 1 where i = 0' was active in another connection.
+#
+# 35.2) Multi UPDATE.
+# 
+# 35.2.a) Multi UPDATE on base tables is incompatible with FTWRL.
+Success: Was not able to run 'update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j' under FTWRL.
+Success: 'update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j' is active in another connection.
+# 
+# 35.2.b) Multi UPDATE on temporary tables is compatible with FTWRL.
+Success: Was able to run 'update t1_temp, t2_temp set t1_temp.i= 1 where t1_temp.i = t2_temp.j' under FTWRL.
+Success: Was able to run 'update t1_temp, t2_temp set t1_temp.i= 1 where t1_temp.i = t2_temp.j' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'update t1_temp, t2_temp set t1_temp.i= 1 where t1_temp.i = t2_temp.j' was active in another connection.
+#
+# 36) USE statement is compatible with FTWRL.
+#
+Success: Was able to run 'use mysqltest1' under FTWRL.
+Success: Was able to run 'use mysqltest1' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'use mysqltest1' was active in another connection.
+#
+# 37) XA statements.
+#     
+# XA statements are similar to BEGIN/COMMIT/ROLLBACK.
+#
+# XA BEGIN, END, PREPARE, ROLLBACK and RECOVER are compatible
+# with FTWRL. XA COMMIT is not.
+flush tables with read lock;
+# Although all below statements are allowed under FTWRL they
+# are almost no-ops as FTWRL does commit and does not allows
+# any non-temporary DML under it.
+xa start 'test1';
+xa end 'test1';
+xa prepare 'test1';
+xa rollback 'test1';
+xa start 'test1';
+xa end 'test1';
+xa prepare 'test1';
+xa commit 'test1';
+xa recover;
+unlock tables;
+# Check that XA non-COMMIT statements are not and COMMIT is
+# blocked by active FTWRL in another connection
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+xa start 'test1';
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+insert into t3_trans values (1);
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+xa end 'test1';
+xa prepare 'test1';
+xa rollback 'test1';
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+xa start 'test1';
+insert into t3_trans values (1);
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+xa end 'test1';
+xa prepare 'test1';
+# Send:
+xa commit 'test1';;
+# Switching to connection 'con1'.
+# Wait until XA COMMIT is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap XA COMMIT.
+delete from t3_trans;
+#
+# Check that XA COMMIT blocks FTWRL in another connection.
+xa start 'test1';
+insert into t3_trans values (1);
+xa end 'test1';
+xa prepare 'test1';
+set debug_sync='RESET';
+set debug_sync='trans_xa_commit_after_acquire_commit_lock SIGNAL parked WAIT_FOR go';
+xa commit 'test1';
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+# Switching to connection 'con2'.
+# Wait until FTWRL is blocked.
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap XA COMMIT.
+# Switching to connection 'con1'.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'default'.
+delete from t3_trans;
+set debug_sync= "RESET";
+#
+# Check that FLUSH TABLES WITH READ LOCK is blocked by individual
+# statements and is not blocked in the presence of transaction which
+# has done some changes earlier but is idle now (or does only reads).
+# This allows to use this statement even on systems which has long
+# running transactions.
+#
+begin;
+insert into t1_base values (1);
+insert into t3_trans values (1);
+# Switching to connection 'con1'.
+# The below FTWRL should not be blocked by transaction in 'default'.
+flush tables with read lock;
+# Switching to connection 'default'.
+# Transaction still is able to read even with FTWRL active in another
+# connection.
+select * from t1_base;
+i
+1
+select * from t2_base;
+j
+select * from t3_trans;
+i
+1
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+commit;
+delete from t1_base;
+delete from t3_trans;
+#
+# Check that impending FTWRL blocks new DML statements and
+# so can't be starved by a constant flow of DML.
+# (a.k.a. test for bug #54673 "It takes too long to get
+# readlock for 'FLUSH TABLES WITH READ LOCK'").
+#
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+insert into t1_base values (1);
+# Switching to connection 'con1'.
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+# Switching to connection 'con2'.
+# Wait until FTWRL is blocked.
+# Try to run another INSERT and see that it is blocked.
+insert into t2_base values (1);;
+# Switching to connection 'con3'.
+# Wait until new INSERT is blocked.
+# Unblock INSERT in the first connection.
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap first INSERT.
+# Switching to connection 'con1'.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'con2'.
+# Reap second INSERT.
+# Switching to connection 'default'.
+set debug_sync= "RESET";
+delete from t1_base;
+delete from t2_base;
+#
+# Clean-up.
+#
+drop function f2_temp;
+drop function f2_base;
+drop procedure p2;
+drop view v1;
+drop function f1;
+drop procedure p1;
+drop database `#mysql50#mysqltest-2`;
+drop database mysqltest1;
+drop temporary tables t1_temp, t2_temp;
+drop tables t1_base, t2_base, t3_trans;

=== modified file 'mysql-test/r/flush_read_lock_kill.result'
--- a/mysql-test/r/flush_read_lock_kill.result	2009-03-06 14:56:17 +0000
+++ b/mysql-test/r/flush_read_lock_kill.result	2010-10-18 12:33:49 +0000
@@ -1,12 +1,38 @@
-SET @old_concurrent_insert= @@global.concurrent_insert;
-SET @@global.concurrent_insert= 0;
 DROP TABLE IF EXISTS t1;
-CREATE TABLE t1 (kill_id INT);
+SET DEBUG_SYNC= 'RESET';
+CREATE TABLE t1 (kill_id INT) engine = InnoDB;
 INSERT INTO t1 VALUES(connection_id());
+# Switching to connection 'default'.
+# Start transaction.
+BEGIN;
+INSERT INTO t1 VALUES(connection_id());
+# Ensure that COMMIT will pause once it acquires protection
+# against its global read lock.
+SET DEBUG_SYNC='ha_commit_trans_after_acquire_commit_lock SIGNAL acquired WAIT_FOR go';
+# Sending:
+COMMIT;
+# Switching to 'con1'.
+# Wait till COMMIT acquires protection against global read
+# lock and pauses.
+SET DEBUG_SYNC='now WAIT_FOR acquired';
+# Sending:
 FLUSH TABLES WITH READ LOCK;
-SELECT ((@id := kill_id) - kill_id) FROM t1;
+# Switching to 'con2'.
+SELECT ((@id := kill_id) - kill_id) FROM t1 LIMIT 1;
 ((@id := kill_id) - kill_id)
 0
+# Wait till FLUSH TABLES WITH READ LOCK blocks due
+# to active COMMIT
+# Kill connection 'con1'.
 KILL CONNECTION @id;
+# Switching to 'con1'.
+# Try to reap FLUSH TABLES WITH READ LOCK, 
+# it fail due to killed statement and connection.
+Got one of the listed errors
+# Switching to 'con2'.
+# Resume COMMIT.
+SET DEBUG_SYNC='now SIGNAL go';
+# Switching to 'default'.
+# Reaping COMMIT.
 DROP TABLE t1;
-SET @@global.concurrent_insert= @old_concurrent_insert;
+SET DEBUG_SYNC= 'RESET';

=== modified file 'mysql-test/r/handler_innodb.result'
--- a/mysql-test/r/handler_innodb.result	2010-09-24 07:18:16 +0000
+++ b/mysql-test/r/handler_innodb.result	2010-10-18 12:33:49 +0000
@@ -1485,10 +1485,6 @@ ERROR 42S02: Table 'test.not_exists_writ
 # We still have the read lock.
 drop table t1;
 ERROR HY000: Can't execute the query because you have a conflicting read lock
-handler t1 read next;
-a	b
-1	1
-handler t1 close;
 handler t1 open;
 select a from t2;
 a

=== modified file 'mysql-test/r/handler_myisam.result'
--- a/mysql-test/r/handler_myisam.result	2010-09-24 07:18:16 +0000
+++ b/mysql-test/r/handler_myisam.result	2010-10-18 12:33:49 +0000
@@ -1481,10 +1481,6 @@ ERROR 42S02: Table 'test.not_exists_writ
 # We still have the read lock.
 drop table t1;
 ERROR HY000: Can't execute the query because you have a conflicting read lock
-handler t1 read next;
-a	b
-1	1
-handler t1 close;
 handler t1 open;
 select a from t2;
 a

=== modified file 'mysql-test/r/mdl_sync.result'
--- a/mysql-test/r/mdl_sync.result	2010-10-06 14:34:28 +0000
+++ b/mysql-test/r/mdl_sync.result	2010-10-18 12:33:49 +0000
@@ -2471,7 +2471,7 @@ CREATE PROCEDURE p1() SELECT 1;
 SET DEBUG_SYNC= 'now WAIT_FOR table_opened';
 # Check that FLUSH must wait to get the GRL
 # and let CREATE PROCEDURE continue
-SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait';
+SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL grlwait';
 FLUSH TABLES WITH READ LOCK;
 # Connection 1
 # Connection 2
@@ -2486,12 +2486,23 @@ DROP PROCEDURE p1;
 SET DEBUG_SYNC= 'now WAIT_FOR table_opened';
 # Check that FLUSH must wait to get the GRL
 # and let DROP PROCEDURE continue
-SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait';
+SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL grlwait';
 FLUSH TABLES WITH READ LOCK;
-# Connection 1
 # Connection 2
+# Once FLUSH TABLES WITH READ LOCK starts waiting
+# DROP PROCEDURE will be waked up and will drop
+# procedure. Global read lock will be granted after
+# this point. After that DROP PROCEDURE will block
+# due to GRL during attempt to auto-magically revoke
+# privileges. This is expected as we treat these two 
+# steps as a separate statements.
+#
+# QQ: This doesn't seem right and IMO breaks binary
+#     logging!
+# Ublock second part of DROP PROCEDURE.
 UNLOCK TABLES;
 # Connection 1
+# Connection 1
 SET DEBUG_SYNC= 'RESET';
 #
 # Bug#50786 Assertion `thd->mdl_context.trans_sentinel() == __null' 

=== modified file 'mysql-test/suite/perfschema/r/dml_setup_instruments.result'
--- a/mysql-test/suite/perfschema/r/dml_setup_instruments.result	2010-09-30 13:29:12 +0000
+++ b/mysql-test/suite/perfschema/r/dml_setup_instruments.result	2010-10-18 12:33:49 +0000
@@ -37,7 +37,6 @@ where name like 'Wait/Synch/Cond/sql/%'
 order by name limit 10;
 NAME	ENABLED	TIMED
 wait/synch/cond/sql/COND_flush_thread_cache	YES	YES
-wait/synch/cond/sql/COND_global_read_lock	YES	YES
 wait/synch/cond/sql/COND_manager	YES	YES
 wait/synch/cond/sql/COND_queue_state	YES	YES
 wait/synch/cond/sql/COND_rpl_status	YES	YES
@@ -46,6 +45,7 @@ wait/synch/cond/sql/COND_thread_cache	YE
 wait/synch/cond/sql/COND_thread_count	YES	YES
 wait/synch/cond/sql/Delayed_insert::cond	YES	YES
 wait/synch/cond/sql/Delayed_insert::cond_client	YES	YES
+wait/synch/cond/sql/Event_scheduler::COND_state	YES	YES
 select * from performance_schema.SETUP_INSTRUMENTS
 where name='Wait';
 select * from performance_schema.SETUP_INSTRUMENTS

=== modified file 'mysql-test/suite/perfschema/r/global_read_lock.result'
--- a/mysql-test/suite/perfschema/r/global_read_lock.result	2010-01-12 01:47:27 +0000
+++ b/mysql-test/suite/perfschema/r/global_read_lock.result	2010-10-18 12:33:49 +0000
@@ -21,9 +21,9 @@ select event_name,
 left(source, locate(":", source)) as short_source,
 timer_end, timer_wait, operation
 from performance_schema.EVENTS_WAITS_CURRENT
-where event_name like "wait/synch/cond/sql/COND_global_read_lock";
+where event_name like "wait/synch/cond/sql/MDL_context::COND_wait_status";
 event_name	short_source	timer_end	timer_wait	operation
-wait/synch/cond/sql/COND_global_read_lock	lock.cc:	NULL	NULL	wait
+wait/synch/cond/sql/MDL_context::COND_wait_status	mdl.cc:	NULL	NULL	timed_wait
 unlock tables;
 update performance_schema.SETUP_INSTRUMENTS set enabled='NO';
 update performance_schema.SETUP_INSTRUMENTS set enabled='YES';

=== modified file 'mysql-test/suite/perfschema/r/server_init.result'
--- a/mysql-test/suite/perfschema/r/server_init.result	2010-09-17 01:04:34 +0000
+++ b/mysql-test/suite/perfschema/r/server_init.result	2010-10-18 12:33:49 +0000
@@ -88,10 +88,6 @@ where name like "wait/synch/mutex/sql/LO
 count(name)
 1
 select count(name) from MUTEX_INSTANCES
-where name like "wait/synch/mutex/sql/LOCK_global_read_lock";
-count(name)
-1
-select count(name) from MUTEX_INSTANCES
 where name like "wait/synch/mutex/sql/LOCK_global_system_variables";
 count(name)
 1
@@ -184,10 +180,6 @@ where name like "wait/synch/cond/sql/CON
 count(name)
 1
 select count(name) from COND_INSTANCES
-where name like "wait/synch/cond/sql/COND_global_read_lock";
-count(name)
-1
-select count(name) from COND_INSTANCES
 where name like "wait/synch/cond/sql/COND_thread_cache";
 count(name)
 1

=== modified file 'mysql-test/suite/perfschema/t/global_read_lock.test'
--- a/mysql-test/suite/perfschema/t/global_read_lock.test	2010-07-20 19:15:29 +0000
+++ b/mysql-test/suite/perfschema/t/global_read_lock.test	2010-10-18 12:33:49 +0000
@@ -60,16 +60,25 @@ lock tables performance_schema.SETUP_INS
 --echo connection default;
 connection default;
 
-let $wait_condition= select 1 from performance_schema.EVENTS_WAITS_CURRENT where event_name like "wait/synch/cond/sql/COND_global_read_lock";
+let $wait_condition= select 1 from performance_schema.EVENTS_WAITS_CURRENT where event_name like "wait/synch/cond/sql/MDL_context::COND_wait_status";
 
 --source include/wait_condition.inc
 
+if (!$success)
+{
+select * from performance_schema.EVENTS_WAITS_CURRENT;
+show status like '%perf%';
+show processlist;
+select * from performance_schema.THREADS;
+set session debug="+d,execute_command_crash";
+}
+
 # Observe the blocked thread in the performance schema :)
 select event_name,
   left(source, locate(":", source)) as short_source,
   timer_end, timer_wait, operation
   from performance_schema.EVENTS_WAITS_CURRENT
-  where event_name like "wait/synch/cond/sql/COND_global_read_lock";
+  where event_name like "wait/synch/cond/sql/MDL_context::COND_wait_status";
 
 unlock tables;
 

=== modified file 'mysql-test/suite/perfschema/t/server_init.test'
--- a/mysql-test/suite/perfschema/t/server_init.test	2010-09-17 01:04:34 +0000
+++ b/mysql-test/suite/perfschema/t/server_init.test	2010-10-18 12:33:49 +0000
@@ -101,9 +101,6 @@ select count(name) from MUTEX_INSTANCES
  where name like "wait/synch/mutex/sql/LOCK_manager";
 
 select count(name) from MUTEX_INSTANCES
- where name like "wait/synch/mutex/sql/LOCK_global_read_lock";
-
-select count(name) from MUTEX_INSTANCES
  where name like "wait/synch/mutex/sql/LOCK_global_system_variables";
 
 select count(name) from MUTEX_INSTANCES
@@ -189,9 +186,6 @@ select count(name) from COND_INSTANCES
  where name like "wait/synch/cond/sql/COND_manager";
 
 select count(name) from COND_INSTANCES
- where name like "wait/synch/cond/sql/COND_global_read_lock";
-
-select count(name) from COND_INSTANCES
  where name like "wait/synch/cond/sql/COND_thread_cache";
 
 select count(name) from COND_INSTANCES

=== modified file 'mysql-test/t/flush.test'
--- a/mysql-test/t/flush.test	2010-09-09 14:29:14 +0000
+++ b/mysql-test/t/flush.test	2010-10-18 12:33:49 +0000
@@ -577,3 +577,70 @@ select * from t1;
 select * from t2;
 unlock tables;
 drop tables tm, t1, t2;
+
+
+--echo #
+--echo # Test for bug #57006 "Deadlock between HANDLER and
+--echo #                      FLUSH TABLES WITH READ LOCK".
+--echo #
+--disable_warnings
+drop table if exists t1, t2;
+--enable_warnings
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+connection default;
+create table t1 (i int);
+create table t2 (i int);
+handler t1 open;
+
+--echo # Switching to connection 'con1'.
+connection con1;
+--echo # Sending:
+--send flush tables with read lock
+
+--echo # Switching to connection 'con2'.
+connection con2;
+--echo # Wait until FTWRL starts waiting for 't1' to be closed.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for table flush"
+  and info = "flush tables with read lock";
+--source include/wait_condition.inc
+
+--echo # Switching to connection 'default'.
+connection default;
+--echo # The below statement should not cause deadlock.
+--echo # Sending:
+--send insert into t2 values (1)
+
+--echo # Switching to connection 'con2'.
+connection con2;
+--echo # Wait until INSERT starts to wait for FTWRL to go away.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock"
+  and info = "insert into t2 values (1)";
+--source include/wait_condition.inc
+
+--echo # Switching to connection 'con1'.
+connection con1;
+--echo # FTWRL should be able to continue now.
+--echo # Reap FTWRL.
+--reap
+unlock tables;
+
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap INSERT.
+--reap
+handler t1 close;
+
+--echo # Cleanup.
+connection con1;
+disconnect con1;
+--source include/wait_until_disconnected.inc
+connection con2;
+disconnect con2;
+--source include/wait_until_disconnected.inc
+connection default;
+drop tables t1, t2;

=== modified file 'mysql-test/t/flush_block_commit.test'
--- a/mysql-test/t/flush_block_commit.test	2009-12-09 15:56:34 +0000
+++ b/mysql-test/t/flush_block_commit.test	2010-10-18 12:33:49 +0000
@@ -39,7 +39,7 @@ connection con2;
 --echo # Wait until COMMIT gets blocked.
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for release of readlock" and info = "COMMIT";
+  where state = "Waiting for global read lock" and info = "COMMIT";
 --source include/wait_condition.inc
 --echo # Verify that 'con1' was blocked and data did not move.
 SELECT * FROM t1;

=== modified file 'mysql-test/t/flush_block_commit_notembedded.test'
--- a/mysql-test/t/flush_block_commit_notembedded.test	2010-05-26 14:34:25 +0000
+++ b/mysql-test/t/flush_block_commit_notembedded.test	2010-10-18 12:33:49 +0000
@@ -53,7 +53,7 @@ begin;
 connection con1;
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for release of readlock" and
+  where state = "Waiting for global read lock" and
         info = "insert into t1 values (1)";
 --source include/wait_condition.inc
 unlock tables;

=== added file 'mysql-test/t/flush_read_lock.test'
--- a/mysql-test/t/flush_read_lock.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/flush_read_lock.test	2010-10-18 12:33:49 +0000
@@ -0,0 +1,1717 @@
+#
+# Test coverage for various aspects of FLUSH TABLES WITH READ LOCK
+# functionality.
+#
+
+# We need InnoDB for COMMIT/ROLLBACK related tests.
+--source include/have_innodb.inc
+# We need the Debug Sync Facility.
+--source include/have_debug_sync.inc
+# Save the initial number of concurrent sessions.
+--source include/count_sessions.inc
+
+--echo # Use MyISAM engine for the most of test tables
+--echo # in order to avoid implicit commit affecting
+--echo # tests for DDL statements.
+--disable_warnings
+drop tables if exists t1_base, t2_base, t3_trans;
+drop database if exists mysqltest1;
+drop database if exists `#mysql50#mysqltest-2`;
+drop procedure if exists p1;
+drop function if exists f1;
+drop view if exists v1;
+drop procedure if exists p2;
+drop function if exists f2_base;
+drop function if exists f2_temp;
+--enable_warnings
+create table t1_base(i int) engine=myisam;
+create table t2_base(j int) engine=myisam;
+create table t3_trans(i int) engine=innodb;
+create temporary table t1_temp(i int) engine=myisam;
+create temporary table t2_temp(j int) engine=myisam;
+create database mysqltest1;
+create database `#mysql50#mysqltest-2`;
+create procedure p1() begin end;
+create function f1() returns int return 0;
+create view v1 as select 1 as i;
+create procedure p2(i int) begin end;
+delimiter |;
+create function f2_base() returns int
+begin
+  insert into t1_base values (1);
+  return 0;
+end|
+create function f2_temp() returns int
+begin
+  insert into t1_temp values (1);
+  return 0;
+end|
+delimiter ;|
+
+connect (con1,localhost,root,,);
+connect (con2,localhost,root,,);
+connect (con3,localhost,root,,);
+connection default;
+
+--echo #
+--echo # Test compatibility of FLUSH TABLES WITH READ LOCK 
+--echo # with various statements.
+--echo #
+--echo # These tests don't cover some classes of statements:
+--echo # - Replication-related - CHANGE MASTER TO, START/STOP SLAVE and etc
+--echo #   (all compatible with FTWRL).
+--echo # - Plugin-related - INSTALL/UNINSTALL (incompatible with FTWRL,
+--echo #   require plugin support).
+
+let $con_aux1=con1;
+let $con_aux2=con2;
+let $cleanup_stmt2= ;
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 1) ALTER variants.
+--echo #
+--echo # 1.1) ALTER TABLE
+--echo # 
+--echo # 1.1.a) For base table should be incompatible with FTWRL.
+--echo #
+let $statement= alter table t1_base add column c1 int;
+let $cleanup_stmt1= alter table t1_base drop column c1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 1.1.b) For temporaty table should be compatible with FTWRL.
+--echo #
+let $statement= alter table t1_temp add column c1 int;
+let $cleanup_stmt= alter table t1_temp drop column c1;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 1.2) ALTER DATABASE should be incompatible with FTWRL.
+--echo #
+let $statement= alter database mysqltest1 default character set utf8;
+let $cleanup_stmt1= alter database mysqltest1 default character set latin1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 1.3) ALTER DATABASE UPGRADE DATA DIRECTORY NAME should be
+--echo #      incompatible with FTWRL.
+--echo #
+let $statement= alter database `#mysql50#mysqltest-2` upgrade data directory name;
+let $cleanup_stmt1= drop database `mysqltest-2`;
+let $cleanup_stmt2= create database `#mysql50#mysqltest-2`;
+--source include/check_ftwrl_incompatible.inc
+let $cleanup_stmt2= ;
+
+--echo #
+--echo # 1.4) ALTER PROCEDURE should be incompatible with FTWRL.
+--echo #
+let $statement= alter procedure p1 comment 'a';
+let $cleanup_stmt1= alter procedure p1 comment '';
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 1.5) ALTER FUNCTION should be incompatible with FTWRL.
+--echo #
+let $statement= alter function f1 comment 'a';
+let $cleanup_stmt1= alter function f1 comment '';
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 1.6) ALTER VIEW should be incompatible with FTWRL.
+--echo #
+let $statement= alter view v1 as select 2 as j;
+let $cleanup_stmt1= alter view v1 as select 1 as i;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 1.7) ALTER EVENT should be incompatible with FTWRL.
+--echo #
+--echo # TODO/FIXME: Unfortunately it releases protection against
+--echo #             GRL a bit too early at this point.
+
+--echo #
+--echo # 1.x) The rest of ALTER statements are too special to
+--echo #      to be tested here.
+--echo #
+
+
+--echo #
+--echo # 2) ANALYZE TABLE statement is compatible with FTWRL.
+--echo #
+--echo # QQ: Is this a correct behavior?
+let $statement= analyze table t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 3) BEGIN, ROLLBACK and COMMIT statements.
+--echo #    BEGIN and ROLLBACK are compatible with FTWRL.
+--echo #    COMMIT is not.
+--echo # 
+--echo # We need a special test for these statements as
+--echo # FTWRL commits a transaction and because COMMIT
+--echo # is handled in a special way.
+flush tables with read lock;
+begin;
+--echo # ROLLBACK is allowed under FTWRL although there
+--echo # no much sense in it. FTWRL commits any previous
+--echo # changes and doesn't allows any DML after it.
+--echo # So such a ROLLBACK is always a no-op.
+rollback;
+--echo # Although COMMIT is incompatible with FTWRL in
+--echo # other senses it is still allowed under FTWRL.
+--echo # This fact relied upon by some versions of
+--echo # innobackup tool.
+--echo # Similarly to ROLLBACK it is a no-op in this situation.
+commit;
+unlock tables;
+--echo # Check that BEGIN/ROLLBACK are not and COMMIT is
+--echo # blocked by active FTWRL in another connection
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+begin;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Do some work so ROLLBACK is not a no-op.
+insert into t3_trans values (1);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+rollback;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+begin;
+--echo # Do some work so COMMIT is not a no-op.
+insert into t3_trans values (1);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Send:
+--send commit
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until COMMIT is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "commit";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap COMMIT.
+--reap
+delete from t3_trans;
+--echo #
+--echo # Check that COMMIT blocks FTWRL in another connection.
+begin;
+insert into t3_trans values (1);
+set debug_sync='RESET';
+set debug_sync='ha_commit_trans_after_acquire_commit_lock SIGNAL parked WAIT_FOR go';
+--send commit
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+--send flush tables with read lock
+--echo # Switching to connection '$con_aux2'.
+connection $con_aux2;
+--echo # Wait until FTWRL is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "flush tables with read lock";
+--source include/wait_condition.inc
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap COMMIT.
+--reap
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Reap FTWRL.
+--reap 
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+delete from t3_trans;
+set debug_sync= "RESET";
+--echo # We don't run similar test for BEGIN and ROLLBACK as
+--echo # they release metadata locks in non-standard place.
+
+
+--echo #
+--echo # 4) BINLOG statement should be incompatible with FTWRL.
+--echo #
+--echo #
+--echo # Provide format description BINLOG statement first.
+BINLOG '
+MfmqTA8BAAAAZwAAAGsAAAABAAQANS41LjctbTMtZGVidWctbG9nAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAx+apMEzgNAAgAEgAEBAQEEgAAVAAEGggAAAAICAgCAA==
+';
+--echo # Now test compatibility for BINLOG statement which is 
+--echo # equivalent to INSERT INTO t1_base VALUES (1).
+let $statement= BINLOG '
+MfmqTBMBAAAALgAAAN0AAAAAACgAAAAAAAEABHRlc3QAB3QxX2Jhc2UAAQMAAQ==
+MfmqTBcBAAAAIgAAAP8AAAAAACgAAAAAAAEAAf/+AQAAAA==
+';
+let $cleanup_stmt1= delete from t1_base where i = 1 limit 1;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_incompatible.inc
+let $skip_3rd_check= ;
+
+
+--echo #
+--echo # 5) CALL statement. This statement uses resources in two
+--echo #    ways through expressions used as parameters and through
+--echo #    sub-statements. This test covers only usage through
+--echo #    parameters as sub-statements do locking individually.
+--echo #
+--echo # 5.a) In simple case parameter expression should be compatible
+--echo #      with FTWRL.
+let $statement= call p2((select count(*) from t1_base));
+let $cleanup_stmt1= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 5.b) In case when expression uses function which updates
+--echo #      base tables CALL should be incompatible with FTWRL.
+--echo #
+let $statement= call p2(f2_base());
+let $cleanup_stmt1= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_incompatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 5.c) If function used as argument updates temporary tables
+--echo #      CALL statement should be compatible with FTWRL.
+--echo #
+let $statement= call p2(f2_temp());
+let $cleanup_stmt1= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+
+--echo #
+--echo # 6) CHECK TABLE statement is compatible with FTWRL.
+--echo #
+let $statement= check table t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 7) CHECKSUM TABLE statement is compatible with FTWRL.
+--echo #
+let $statement= checksum table t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 8) CREATE variants.
+--echo #
+--echo # 8.1) CREATE TABLE statement.
+--echo #
+--echo # 8.1.a) CREATE TABLE is incompatible with FTWRL when
+--echo #        base table is created.
+let $statement= create table t3_base(i int);
+let $cleanup_stmt1= drop table t3_base;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 8.1.b) CREATE TABLE is compatible with FTWRL when
+--echo #        temporary table is created.
+let $statement= create temporary table t3_temp(i int);
+let $cleanup_stmt= drop temporary tables t3_temp;
+--source include/check_ftwrl_compatible.inc
+
+--echo # 8.1.c) CREATE TABLE LIKE is incompatible with FTWRL when
+--echo #        base table is created.
+let $statement= create table t3_base like t1_temp;
+let $cleanup_stmt1= drop table t3_base;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 8.1.d) CREATE TABLE LIKE is compatible with FTWRL when
+--echo #        temporary table is created.
+let $statement= create temporary table t3_temp like t1_base;
+let $cleanup_stmt= drop temporary table t3_temp;
+--source include/check_ftwrl_compatible.inc
+
+--echo # 8.1.e) CREATE TABLE SELECT is incompatible with FTWRL when
+--echo #        base table is created.
+let $statement= create table t3_base select 1 as i;
+let $cleanup_stmt1= drop table t3_base;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 8.1.f) CREATE TABLE SELECT is compatible with FTWRL when
+--echo #        temporary table is created.
+let $statement= create temporary table t3_temp select 1 as i;
+let $cleanup_stmt= drop temporary table t3_temp;
+--source include/check_ftwrl_compatible.inc
+
+--echo # 8.2) CREATE INDEX statement.
+--echo #
+--echo # 8.2.a) CREATE TABLE is incompatible with FTWRL when
+--echo #        applied to base table.
+let $statement= create index i on t1_base (i);
+let $cleanup_stmt1= drop index i on t1_base;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 8.2.b) CREATE TABLE is compatible with FTWRL when
+--echo #        applied to temporary table.
+let $statement= create index i on t1_temp (i);
+let $cleanup_stmt= drop index i on t1_temp;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 8.3) CREATE DATABASE is incompatible with FTWRL.
+--echo #
+let $statement= create database mysqltest2;
+let $cleanup_stmt1= drop database mysqltest2;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 8.4) CREATE VIEW is incompatible with FTWRL.
+--echo #
+let $statement= create view v2 as select 1 as j;
+let $cleanup_stmt1= drop view v2;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 8.5) CREATE TRIGGER is incompatible with FTWRL.
+--echo #
+let $statement= create trigger t1_bi before insert on t1_base for each row begin end;
+let $cleanup_stmt1= drop trigger t1_bi;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 8.6) CREATE FUNCTION is incompatible with FTWRL.
+--echo #
+let $statement= create function f2() returns int return 0;
+let $cleanup_stmt1= drop function f2;
+--echo # Skip last part of compatibility testing as this statement
+--echo # is internally consists of two statements.
+--echo # QQ: Is this a problem?
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_incompatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 8.7) CREATE PROCEDURE is incompatible with FTWRL.
+--echo #
+let $statement= create procedure p3() begin end;
+let $cleanup_stmt1= drop procedure p3;
+--echo # Skip last part of compatibility testing as this statement
+--echo # is internally consists of two statements.
+--echo # QQ: Is this a problem?
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_incompatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 8.8) CREATE EVENT should be incompatible with FTWRL.
+--echo #
+--echo # TODO/FIXME: Unfortunately it releases protection against
+--echo #             GRL a bit too early at this point.
+
+--echo #
+--echo # 8.9) CREATE USER should be incompatible with FTWRL.
+--echo #
+let $statement= create user mysqltest_u1;
+let $cleanup_stmt1= drop user mysqltest_u1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 8.x) The rest of CREATE variants are too special to test here.
+--echo #
+
+
+--echo #
+--echo # 9) PREPARE, EXECUTE and DEALLOCATE PREPARE statements.
+--echo #
+--echo # 9.1) PREPARE statement is compatible with FTWRL as it
+--echo #      doesn't change any data.
+let $statement= prepare stmt1 from 'insert into t1_base values (1)';
+let $cleanup_stmt= deallocate prepare stmt1;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 9.2) Compatibility of EXECUTE statement depends on statement
+--echo #     to be executed.
+--echo #
+--echo # 9.2.a) EXECUTE for statement which is itself compatible with
+--echo #       FTWRL should be compatible.
+prepare stmt1 from 'select * from t1_base';
+let $statement= execute stmt1;
+let $cleanup_stmt= ; 
+--source include/check_ftwrl_compatible.inc
+deallocate prepare stmt1;
+
+--echo #
+--echo # 9.2.b) EXECUTE for statement which is incompatible with FTWRL
+--echo #        should be also incompatible.
+--echo #
+--echo # Check that EXECUTE is not allowed under FTWRL.
+prepare stmt1 from 'insert into t1_base values (1)';
+flush tables with read lock;
+--error ER_CANT_UPDATE_WITH_READLOCK
+execute stmt1;
+unlock tables;
+--echo # Check that active FTWRL in another connection
+--echo # blocks EXECUTE which changes data.
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send execute stmt1 
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that EXECUTE is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "insert into t1_base values (1)";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap EXECUTE.
+--reap
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send execute stmt1; 
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+--send flush tables with read lock
+--echo # Switching to connection '$con_aux2'.
+connection $con_aux2;
+--echo # Wait until FTWRL is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "flush tables with read lock";
+--source include/wait_condition.inc
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap EXECUTE.
+--reap
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Reap FTWRL.
+--reap 
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+set debug_sync= "RESET";
+delete from t1_base;
+deallocate prepare stmt1;
+
+--echo #
+--echo # 9.3) DEALLOCATE PREPARE is compatible with FTWRL.
+--echo #
+prepare stmt1 from 'insert into t1_base values (1)';
+let $statement= deallocate prepare stmt1;
+let $cleanup_stmt= prepare stmt1 from 'insert into t1_base values (1)';
+--source include/check_ftwrl_compatible.inc
+deallocate prepare stmt1;
+
+
+--echo #
+--echo # 10) DELETE variations.
+--echo #
+--echo # 10.1) Simple DELETE.
+--echo # 
+--echo # 10.1.a) Simple DELETE on base table is incompatible with FTWRL.
+let $statement= delete from t1_base;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 
+--echo # 10.1.b) Simple DELETE on temporary table is compatible with FTWRL.
+let $statement= delete from t1_temp;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 10.2) Multi DELETE.
+--echo # 
+--echo # 10.2.a) Multi DELETE on base tables is incompatible with FTWRL.
+let $statement= delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 
+--echo # 10.2.b) Multi DELETE on temporary tables is compatible with FTWRL.
+let $statement= delete t1_temp from t1_temp, t2_temp where t1_temp.i = t2_temp.j;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 11) DESCRIBE should be compatible with FTWRL.
+--echo #
+let $statement= describe t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 12) Compatibility of DO statement with FTWRL depends on its
+--echo #     expression.
+--echo #
+--echo # 12.a) DO with expression which does not changes base table
+--echo #       should be compatible with FTWRL.
+let $statement= do (select count(*) from t1_base);
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 12.b) DO which calls SF updating base table should be
+--echo #       incompatible with FTWRL.
+let $statement= do f2_base();
+let $cleanup_stmt1= delete from t1_base limit 1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 12.c) DO which calls SF updating temporary table should be
+--echo #       compatible with FTWRL.
+let $statement= do f2_temp();
+let $cleanup_stmt= delete from t1_temp limit 1;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 13) DROP variants.
+--echo #
+--echo # 13.1) DROP TABLES.
+--echo #
+--echo # 13.1.a) DROP TABLES which affects base tables is incompatible
+--echo #         with FTWRL.
+let $statement= drop table t2_base;
+let $cleanup_stmt1= create table t2_base(j int);
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 13.1.b) DROP TABLES which affects only temporary tables
+--echo #         in theory can be compatible with FTWRL.
+--echo #         In practice it is not yet.
+let $statement= drop table t2_temp;
+let $cleanup_stmt1= create temporary table t2_temp(j int);
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 13.1.c) DROP TEMPORARY TABLES should becompatible with FTWRL.
+let $statement= drop temporary table t2_temp;
+let $cleanup_stmt= create temporary table t2_temp(j int);
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 13.2) DROP INDEX.
+--echo #
+--echo # 13.2.a) DROP INDEX for base table is incompatible with FTWRL.
+create index i on t1_base (i);
+let $statement= drop index i on t1_base;
+let $cleanup_stmt1= create index i on t1_base (i);
+--source include/check_ftwrl_incompatible.inc
+drop index i on t1_base;
+
+--echo #
+--echo # 13.2.b) DROP INDEX for temporary table is compatible with FTWRL.
+create index i on t1_temp (i);
+let $statement= drop index i on t1_temp;
+let $cleanup_stmt= create index i on t1_temp (i);
+--source include/check_ftwrl_compatible.inc
+drop index i on t1_temp;
+
+--echo #
+--echo # 13.3) DROP DATABASE is incompatible with FTWRL
+--echo #
+let $statement= drop database mysqltest1;
+let $cleanup_stmt1= create database mysqltest1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 13.4) DROP FUNCTION is incompatible with FTWRL.
+--echo #
+let $statement= drop function f1;
+let $cleanup_stmt1= create function f1() returns int return 0;
+--echo # Skip last part of compatibility testing as this statement
+--echo # is internally consists of two statements.
+--echo # QQ: Is this a problem?
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_incompatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 13.5) DROP PROCEDURE is incompatible with FTWRL.
+--echo #
+let $statement= drop procedure p1;
+let $cleanup_stmt1= create procedure p1() begin end;
+--echo # Skip last part of compatibility testing as this statement
+--echo # is internally consists of two statements.
+--echo # QQ: Is this a problem?
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_incompatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 13.6) DROP USER should be incompatible with FTWRL.
+--echo #
+create user mysqltest_u1;
+let $statement= drop user mysqltest_u1;
+let $cleanup_stmt1= create user mysqltest_u1;
+--source include/check_ftwrl_incompatible.inc
+drop user mysqltest_u1;
+
+--echo #
+--echo # 13.7) DROP VIEW should be incompatible with FTWRL.
+--echo #
+let $statement= drop view v1;
+let $cleanup_stmt1= create view v1 as select 1 as i;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 13.8) DROP EVENT should be incompatible with FTWRL.
+--echo #
+--echo # TODO/FIXME: Unfortunately it releases protection against
+--echo #             GRL a bit too early at this point.
+
+--echo #
+--echo # 13.9) DROP TRIGGER is incompatible with FTWRL.
+--echo #
+create trigger t1_bi before insert on t1_base for each row begin end;
+let $statement= drop trigger t1_bi;
+let $cleanup_stmt1= create trigger t1_bi before insert on t1_base for each row begin end;
+--source include/check_ftwrl_incompatible.inc
+drop trigger t1_bi;
+
+--echo #
+--echo # 13.x) The rest of DROP variants are too special to test here.
+--echo #
+
+
+--echo #
+--echo # 14) FLUSH variants.
+--echo #
+--echo # Test compatibility of _some_ important FLUSH variants with FTWRL.
+--echo #
+--echo # 14.1) FLUSH TABLES WITH READ LOCK is compatible with itself.
+--echo # 
+--echo # Check that FTWRL statements can be run while FTWRL
+--echo # is active in another connection.
+--echo #
+--echo # Switching to connection '$con_aux1'.
+flush tables with read lock;
+--echo # The second FTWRL in a row is allowed at the moment.
+--echo # It does not make much sense as it does only flush.
+flush tables with read lock;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+flush tables with read lock;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+
+--echo #
+--echo # 14.2) FLUSH TABLES <list> WITH READ LOCK is not blocked by
+--echo #       active FTWRL. But since the latter keeps tables open
+--echo #       FTWRL is blocked by FLUSH TABLES <list> WITH READ LOCK.
+flush tables with read lock;
+--echo # FT <list> WRL is allowed under FTWRL at the moment.
+--echo # It does not make much sense though.
+flush tables t1_base, t2_base with read lock;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+flush tables t1_base, t2_base with read lock;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+flush tables t1_base, t2_base with read lock;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--send flush tables with read lock
+--echo # Switching to connection '$con_aux2'.
+connection $con_aux2;
+--echo # Wait until FTWRL is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for table flush" and
+        info = "flush tables with read lock";
+--source include/wait_condition.inc
+--echo # Switching to connection 'default'.
+connection default;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Reap FTWRL.
+--reap
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+
+
+--echo #
+--echo # 14.3) FLUSH TABLES is compatible with FTWRL.
+let $statement= flush tables;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 14.4) FLUSH TABLES <list> is compatible with FTWRL.
+let $statement= flush table t1_base, t2_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 14.5) FLUSH PRIVILEGES is compatible with FTWRL.
+let $statement= flush privileges;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 15) GRANT statement should be incompatible with FTWRL.
+--echo #
+let $statement= grant all privileges on t1_base to mysqltest_u1;
+let $cleanup_stmt1= revoke all privileges on t1_base from mysqltest_u1;
+--source include/check_ftwrl_incompatible.inc
+drop user mysqltest_u1;
+
+
+--echo #
+--echo # 16) All HANDLER variants are half-compatible with FTWRL.
+--echo #     I.e. they are not blocked by active FTWRL. But since open
+--echo #     HANDLER means open table instance FTWRL is blocked while
+--echo #     HANDLER is not closed.
+--echo #
+--echo # Check that HANDLER statements succeed under FTWRL.
+flush tables with read lock;
+handler t1_base open;
+handler t1_base read first;
+handler t1_base close;
+unlock tables;
+--echo # Check that HANDLER statements can be run while FTWRL
+--echo # is active in another connection.
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+handler t1_base open;
+handler t1_base read first;
+handler t1_base close;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+
+
+--echo #
+--echo # 17) HELP statement is compatible with FTWRL.
+--echo #
+let $statement= help no_such_topic;
+let $cleanup_stmt= ; 
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 18) INSERT statement.
+--echo # 
+--echo # 18.a) Ordinary INSERT into base table is incompatible with FTWRL.
+let $statement= insert into t1_base values (1);
+let $cleanup_stmt1= delete from t1_base limit 1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 18.b) Ordinary INSERT into temp table is compatible with FTWRL.
+let $statement= insert into t1_temp values (1);
+let $cleanup_stmt= delete from t1_temp limit 1;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 18.c) INSERT DELAYED is incompatible with FTWRL.
+let $statement= insert delayed into t1_base values (1);
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+delete from t1_base;
+
+--echo # 
+--echo # 18.d) INSERT SELECT into base table is incompatible with FTWRL.
+let $statement= insert into t1_base select * from t1_temp;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 18.e) INSERT SELECT into temp table is compatible with FTWRL.
+let $statement= insert into t1_temp select * from t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 19) KILL statement is compatible with FTWRL.
+--echo #
+--echo # Check that KILL can be run under FTWRL.
+flush tables with read lock;
+set @id:= connection_id();
+--error ER_QUERY_INTERRUPTED
+kill query @id;
+unlock tables;
+--echo # Check that KILL statements can be run while FTWRL
+--echo # is active in another connection.
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--error ER_QUERY_INTERRUPTED
+kill query @id;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Finally check that KILL doesn't block FTWRL
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send kill query @id
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap KILL. 
+--error ER_QUERY_INTERRUPTED
+--reap
+set debug_sync='RESET';
+
+
+--echo #
+--echo # 20) LOAD DATA statement.
+--echo # 
+--echo # 20.a) LOAD DATA into base table is incompatible with FTWRL.
+let $statement= load data infile '../../std_data/rpl_loaddata.dat' into table t1_base (@dummy, i);
+let $cleanup_stmt1= delete from t1_base;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 
+--echo # 20.b) LOAD DATA into temporary table is compatible with FTWRL.
+let $statement= load data infile '../../std_data/rpl_loaddata.dat' into table t1_temp (@dummy, i);
+let $cleanup_stmt= delete from t1_temp;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 21) LOCK/UNLOCK TABLES statements.
+--echo #
+--echo # LOCK TABLES statement always (almost) blocks FTWRL as it
+--echo # keeps tables open until UNLOCK TABLES.
+--echo # Active FTWRL on the other hand blocks only those
+--echo # LOCK TABLES which allow updating of base tables.
+--echo #
+--echo # 21.a) LOCK TABLES READ is allowed under FTWRL and
+--echo #       is not blocked by active FTWRL.
+flush tables with read lock;
+lock tables t1_base read;
+unlock tables;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+lock tables t1_base read;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+
+--echo #
+--echo # 21.b) LOCK TABLES WRITE on a base table is disallowed
+--echo #       under FTWRL and should be blocked by active FTWRL.
+flush tables with read lock;
+--error ER_CANT_UPDATE_WITH_READLOCK
+lock tables t1_base write;
+unlock tables;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send lock tables t1_base write 
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that LOCK TABLES WRITE is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "lock tables t1_base write";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap LOCK TABLES WRITE
+--reap
+unlock tables;
+
+--echo #
+--echo # 21.c) LOCK TABLES WRITE on temporary table doesn't
+--echo #       make much sense but is allowed under FTWRL
+--echo #       and should not be blocked by active FTWRL.
+flush tables with read lock;
+lock tables t1_temp write;
+unlock tables;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+lock tables t1_temp write;
+unlock tables;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+
+
+--echo #
+--echo # 22) OPTIMIZE TABLE statement.
+--echo #
+--echo # 22.a) OPTIMIZE TABLE of base table is incompatible with FTWRL.
+flush tables with read lock;
+--echo # OPTIMIZE statement returns errors as part of result-set.
+optimize table t1_base;
+unlock tables;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send optimize table t1_base
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that OPTIMIZE TABLE is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "optimize table t1_base";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap OPTIMIZE TABLE
+--reap
+--echo # We don't check that active OPTIMIZE TABLE blocks
+--echo # FTWRL as this one of statements releasing metadata
+--echo # locks in non-standard place.
+
+--echo #
+--echo # 22.b) OPTIMIZE TABLE of temporary table is compatible with FTWRL.
+let $statement= optimize table t1_temp;
+let $cleanup_stmt= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+
+--echo #
+--echo # 23) CACHE statement is compatible with FTWRL.
+--echo #
+let $statement= cache index t1_base in default;
+let $cleanup_stmt= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+
+--echo #
+--echo # 24) LOAD INDEX statement is compatible with FTWRL.
+--echo #
+let $statement= load index into cache t1_base;
+let $cleanup_stmt= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+
+--echo #
+--echo # 25) SAVEPOINT/RELEASE SAVEPOINT/ROLLBACK TO SAVEPOINT are
+--echo #     compatible with FTWRL.
+--echo #
+--echo # Since manipulations on savepoint have to be done
+--echo # inside transaction and FTWRL commits transaction we
+--echo # need a special test for these statements.
+flush tables with read lock;
+begin;
+savepoint sv1;
+rollback to savepoint sv1;
+release savepoint sv1;
+unlock tables;
+commit;
+--echo # Check that these statements are not blocked by
+--echo # active FTWRL in another connection.
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+begin;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Do some changes to avoid SAVEPOINT and friends
+--echo # being almost no-ops.
+insert into t3_trans values (1);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+savepoint sv1;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+insert into t3_trans values (2);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+rollback to savepoint sv1;
+release savepoint sv1;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+rollback;
+--echo # Check that these statements don't block FTWRL in
+--echo # another connection.
+begin;
+--echo # Do some changes to avoid SAVEPOINT and friends
+--echo # being almost no-ops.
+insert into t3_trans values (1);
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send savepoint sv1
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap SAVEPOINT
+--reap
+insert into t3_trans values (2);
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send rollback to savepoint sv1
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap ROLLBACK TO SAVEPOINT
+--reap
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send release savepoint sv1
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+flush tables with read lock;
+unlock tables;
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap RELEASE SAVEPOINT
+--reap
+rollback;
+set debug_sync= "RESET";
+
+
+--echo #
+--echo # 26) RENAME variants.
+--echo #
+--echo # 26.1) RENAME TABLES is incompatible with FTWRL.
+let $statement= rename table t1_base to t3_base;
+let $cleanup_stmt1= rename table t3_base to t1_base;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 26.2) RENAME USER is incompatible with FTWRL.
+create user mysqltest_u1;
+let $statement= rename user mysqltest_u1 to mysqltest_u2;
+let $cleanup_stmt1= rename user mysqltest_u2 to mysqltest_u1;
+--source include/check_ftwrl_incompatible.inc
+drop user mysqltest_u1;
+
+
+--echo #
+--echo # 27) REPAIR TABLE statement.
+--echo #
+--echo # 27.a) REPAIR TABLE of base table is incompatible with FTWRL.
+flush tables with read lock;
+--echo # REPAIR statement returns errors as part of result-set.
+repair table t1_base;
+unlock tables;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send repair table t1_base
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that REPAIR TABLE is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "repair table t1_base";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap REPAIR TABLE
+--reap
+--echo # We don't check that active REPAIR TABLE blocks
+--echo # FTWRL as this one of statements releasing metadata
+--echo # locks in non-standard place.
+
+--echo #
+--echo # 27.b) REPAIR TABLE of temporary table is compatible with FTWRL.
+let $statement= repair table t1_temp;
+let $cleanup_stmt= ;
+--echo # Skip last part of compatibility testing as this statement
+--echo # releases metadata locks in non-standard place.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+
+--echo #
+--echo # 28) REPLACE statement.
+--echo # 
+--echo # 28.a) Ordinary REPLACE into base table is incompatible with FTWRL.
+let $statement= replace into t1_base values (1);
+let $cleanup_stmt1= delete from t1_base limit 1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 28.b) Ordinary REPLACE into temp table is compatible with FTWRL.
+let $statement= replace into t1_temp values (1);
+let $cleanup_stmt= delete from t1_temp limit 1;
+--source include/check_ftwrl_compatible.inc
+
+--echo # 
+--echo # 28.c) REPLACE SELECT into base table is incompatible with FTWRL.
+let $statement= replace into t1_base select * from t1_temp;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 28.d) REPLACE SELECT into temp table is compatible with FTWRL.
+let $statement= replace into t1_temp select * from t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 29) REVOKE variants.
+--echo # 
+--echo # 29.1) REVOKE privileges is incompatible with FTWRL. 
+grant all privileges on t1_base to mysqltest_u1;
+let $statement= revoke all privileges on t1_base from mysqltest_u1;
+let $cleanup_stmt1= grant all privileges on t1_base to mysqltest_u1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 
+--echo # 29.2) REVOKE ALL PRIVILEGES, GRANT OPTION is incompatible with FTWRL.
+let $statement= revoke all privileges, grant option from mysqltest_u1;
+let $cleanup_stmt1= grant all privileges on t1_base to mysqltest_u1;
+--source include/check_ftwrl_incompatible.inc
+drop user mysqltest_u1;
+
+
+--echo #
+--echo # 30) Compatibility of SELECT statement with FTWRL depends on
+--echo #     locking mode used and on functions being invoked by it.
+--echo #
+--echo # 30.a) Simple SELECT which does not changes tables should be
+--echo #       compatible with FTWRL.
+let $statement= select count(*) from t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo # 30.b) SELECT ... FOR UPDATE is incompatible with FTWRL.
+let $statement= select count(*) from t1_base for update;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 30.c) SELECT ... LOCK IN SHARE MODE is compatible with FTWRL.
+let $statement= select count(*) from t1_base lock in share mode;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 30.d) SELECT which calls SF updating base table should be
+--echo #       incompatible with FTWRL.
+let $statement= select f2_base();
+let $cleanup_stmt1= delete from t1_base limit 1;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 30.e) SELECT which calls SF updating temporary table should be
+--echo #       compatible with FTWRL.
+let $statement= select f2_temp();
+let $cleanup_stmt= delete from t1_temp limit 1;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 31) Compatibility of SET statement with FTWRL depends on its
+--echo #     expression and on whether it is a special SET statement.
+--echo #
+--echo # 31.a) Ordinary SET with expression which does not
+--echo #       changes base table should be compatible with FTWRL.
+let $statement= set @a:= (select count(*) from t1_base);
+let $cleanup_stmt= ;
+--echo # Skip last part of compatibility testing as our helper debug
+--echo # sync-point doesn't work for SET statements.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 31.b) Ordinary SET which calls SF updating base table should
+--echo #       be incompatible with FTWRL.
+let $statement= set @a:= f2_base();
+let $cleanup_stmt1= delete from t1_base limit 1;
+--echo # Skip last part of compatibility testing as our helper debug
+--echo # sync-point doesn't work for SET statements.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_incompatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 31.c) Ordinary SET which calls SF updating temporary table
+--echo #       should be compatible with FTWRL.
+let $statement= set @a:= f2_temp();
+let $cleanup_stmt= delete from t1_temp limit 1;
+--echo # Skip last part of compatibility testing as our helper debug
+--echo # sync-point doesn't work for SET statements.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_compatible.inc
+let $skip_3rd_check= ;
+
+--echo #
+--echo # 31.d) Special SET variants that cause implicit commit or change
+--echo #       data are incompatible with GRL.
+create user mysqltest_u1;
+let $statement= set password for 'mysqltest_u1' = password('');
+let $cleanup_stmt1= ;
+--echo # Skip last part of compatibility testing as our helper debug
+--echo # sync-point doesn't work for SET statements.
+let $skip_3rd_check= 1;
+--source include/check_ftwrl_incompatible.inc
+let $skip_3rd_check= ;
+drop user mysqltest_u1;
+
+
+--echo #
+--echo # 32) SHOW statements are compatible with FTWRL.
+--echo #     Let us test _some_ of them.
+--echo #
+--echo # 32.1) SHOW TABLES.
+let $statement= show tables from test;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 32.1) SHOW TABLES.
+let $statement= show tables from test;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 32.2) SHOW EVENTS.
+let $statement= show events from test;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 32.3) SHOW GRANTS.
+create user mysqltest_u1;
+let $statement= show grants for mysqltest_u1;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+drop user mysqltest_u1;
+
+--echo #
+--echo # 32.4) SHOW CREATE TABLE.
+let $statement= show create table t1_base;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 32.5) SHOW CREATE FUNCTION.
+let $statement= show create function f1;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 33) SIGNAL statement is compatible with FTWRL.
+--echo #
+--echo # Note that we don't cover RESIGNAL as it requires
+--echo # active handler context. 
+let $statement= signal sqlstate '01000';
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 34) TRUNCATE TABLE statement.
+--echo # 
+--echo # 34.a) TRUNCATE of base table is incompatible with FTWRL.
+let $statement= truncate table t1_base;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 
+--echo # 34.b) TRUNCATE of temporary table is compatible with FTWRL.
+let $statement= truncate table t1_temp;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 35) UPDATE variants.
+--echo #
+--echo # 35.1) Simple UPDATE.
+--echo # 
+--echo # 35.1.a) Simple UPDATE on base table is incompatible with FTWRL.
+let $statement= update t1_base set i= 1 where i = 0;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 
+--echo # 35.1.b) Simple UPDATE on temporary table is compatible with FTWRL.
+let $statement= update t1_temp set i= 1 where i = 0;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo #
+--echo # 35.2) Multi UPDATE.
+--echo # 
+--echo # 35.2.a) Multi UPDATE on base tables is incompatible with FTWRL.
+let $statement= update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j;
+let $cleanup_stmt1= ;
+--source include/check_ftwrl_incompatible.inc
+
+--echo # 
+--echo # 35.2.b) Multi UPDATE on temporary tables is compatible with FTWRL.
+let $statement= update t1_temp, t2_temp set t1_temp.i= 1 where t1_temp.i = t2_temp.j;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 36) USE statement is compatible with FTWRL.
+--echo #
+let $statement= use mysqltest1;
+let $cleanup_stmt= use test;
+--source include/check_ftwrl_compatible.inc
+
+
+--echo #
+--echo # 37) XA statements.
+--echo #     
+--echo # XA statements are similar to BEGIN/COMMIT/ROLLBACK.
+--echo #
+--echo # XA BEGIN, END, PREPARE, ROLLBACK and RECOVER are compatible
+--echo # with FTWRL. XA COMMIT is not.
+flush tables with read lock;
+--echo # Although all below statements are allowed under FTWRL they
+--echo # are almost no-ops as FTWRL does commit and does not allows
+--echo # any non-temporary DML under it.
+xa start 'test1';
+xa end 'test1';
+xa prepare 'test1';
+xa rollback 'test1';
+xa start 'test1';
+xa end 'test1';
+xa prepare 'test1';
+xa commit 'test1';
+--disable_result_log
+xa recover;
+--enable_result_log
+unlock tables;
+--echo # Check that XA non-COMMIT statements are not and COMMIT is
+--echo # blocked by active FTWRL in another connection
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+xa start 'test1';
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+insert into t3_trans values (1);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+xa end 'test1';
+xa prepare 'test1';
+xa rollback 'test1';
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+xa start 'test1';
+insert into t3_trans values (1);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+connection default;
+xa end 'test1';
+xa prepare 'test1';
+--echo # Send:
+--send xa commit 'test1';
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until XA COMMIT is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "xa commit 'test1'";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap XA COMMIT.
+--reap
+delete from t3_trans;
+--echo #
+--echo # Check that XA COMMIT blocks FTWRL in another connection.
+xa start 'test1';
+insert into t3_trans values (1);
+xa end 'test1';
+xa prepare 'test1';
+set debug_sync='RESET';
+set debug_sync='trans_xa_commit_after_acquire_commit_lock SIGNAL parked WAIT_FOR go';
+--send xa commit 'test1'
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+--send flush tables with read lock
+--echo # Switching to connection '$con_aux2'.
+connection $con_aux2;
+--echo # Wait until FTWRL is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "flush tables with read lock";
+--source include/wait_condition.inc
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap XA COMMIT.
+--reap
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Reap FTWRL.
+--reap 
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+delete from t3_trans;
+set debug_sync= "RESET";
+
+
+--echo #
+--echo # Check that FLUSH TABLES WITH READ LOCK is blocked by individual
+--echo # statements and is not blocked in the presence of transaction which
+--echo # has done some changes earlier but is idle now (or does only reads).
+--echo # This allows to use this statement even on systems which has long
+--echo # running transactions.
+--echo #
+begin;
+insert into t1_base values (1);
+insert into t3_trans values (1);
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # The below FTWRL should not be blocked by transaction in 'default'.
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Transaction still is able to read even with FTWRL active in another
+--echo # connection.
+select * from t1_base;
+select * from t2_base;
+select * from t3_trans;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+commit;
+delete from t1_base;
+delete from t3_trans;
+
+
+--echo #
+--echo # Check that impending FTWRL blocks new DML statements and
+--echo # so can't be starved by a constant flow of DML.
+--echo # (a.k.a. test for bug #54673 "It takes too long to get
+--echo # readlock for 'FLUSH TABLES WITH READ LOCK'").
+--echo #
+set debug_sync='RESET';
+set debug_sync='execute_command_after_close_tables SIGNAL parked WAIT_FOR go';
+--send insert into t1_base values (1)
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+set debug_sync='now WAIT_FOR parked';
+--send flush tables with read lock
+--echo # Switching to connection '$con_aux2'.
+connection $con_aux2;
+--echo # Wait until FTWRL is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "flush tables with read lock";
+--source include/wait_condition.inc
+--echo # Try to run another INSERT and see that it is blocked.
+--send insert into t2_base values (1);
+--echo # Switching to connection 'con3'.
+connection con3;
+--echo # Wait until new INSERT is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "insert into t2_base values (1)";
+--echo # Unblock INSERT in the first connection.
+set debug_sync='now SIGNAL go';
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap first INSERT.
+--reap
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Reap FTWRL.
+--reap 
+unlock tables;
+--echo # Switching to connection '$con_aux2'.
+connection $con_aux2;
+--echo # Reap second INSERT.
+--reap
+--echo # Switching to connection 'default'.
+connection default;
+set debug_sync= "RESET";
+delete from t1_base;
+delete from t2_base;
+
+
+--echo #
+--echo # Clean-up.
+--echo #
+drop function f2_temp;
+drop function f2_base;
+drop procedure p2;
+drop view v1;
+drop function f1;
+drop procedure p1;
+drop database `#mysql50#mysqltest-2`;
+drop database mysqltest1;
+drop temporary tables t1_temp, t2_temp;
+drop tables t1_base, t2_base, t3_trans;
+disconnect con1;
+disconnect con2;
+disconnect con3;
+
+# Check that all connections opened by test cases in this file are really
+# gone so execution of other tests won't be affected by their presence.
+--source include/wait_until_count_sessions.inc

=== removed file 'mysql-test/t/flush_read_lock_kill-master.opt'
--- a/mysql-test/t/flush_read_lock_kill-master.opt	2006-11-29 20:51:09 +0000
+++ b/mysql-test/t/flush_read_lock_kill-master.opt	1970-01-01 00:00:00 +0000
@@ -1 +0,0 @@
---loose-debug=+d,make_global_read_lock_block_commit_loop

=== modified file 'mysql-test/t/flush_read_lock_kill.test'
--- a/mysql-test/t/flush_read_lock_kill.test	2009-10-20 18:00:07 +0000
+++ b/mysql-test/t/flush_read_lock_kill.test	2010-10-18 12:33:49 +0000
@@ -2,24 +2,19 @@
 # for running commits to finish (in the past it could not)
 # This will not be a meaningful test on non-debug servers so will be
 # skipped.
-# If running mysql-test-run --debug, the --debug added by
-# mysql-test-run to the mysqld command line will override the one of
-# -master.opt. But this test is designed to still pass then (though it
-# won't test anything interesting).
 
 # This also won't work with the embedded server test
 --source include/not_embedded.inc
 
 --source include/have_debug.inc
 
+# This test needs transactional engine as otherwise COMMIT
+# won't block FLUSH TABLES WITH GLOBAL READ LOCK.
+--source include/have_innodb.inc
+
 # Save the initial number of concurrent sessions
 --source include/count_sessions.inc
 
-# Disable concurrent inserts to avoid test failures when reading the
-# connection id which was inserted into a table by another thread.
-SET @old_concurrent_insert= @@global.concurrent_insert;
-SET @@global.concurrent_insert= 0;
-
 connect (con1,localhost,root,,);
 connect (con2,localhost,root,,);
 connection con1;
@@ -27,47 +22,64 @@ connection con1;
 --disable_warnings
 DROP TABLE IF EXISTS t1;
 --enable_warnings
-CREATE TABLE t1 (kill_id INT);
+SET DEBUG_SYNC= 'RESET';
+CREATE TABLE t1 (kill_id INT) engine = InnoDB;
+INSERT INTO t1 VALUES(connection_id());
+
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Start transaction.
+BEGIN;
 INSERT INTO t1 VALUES(connection_id());
+--echo # Ensure that COMMIT will pause once it acquires protection
+--echo # against its global read lock.
+SET DEBUG_SYNC='ha_commit_trans_after_acquire_commit_lock SIGNAL acquired WAIT_FOR go';
 
-# Thanks to the parameter we passed to --debug, this FLUSH will
-# block on a debug build running with our --debug=make_global... It
-# will block until killed. In other cases (non-debug build or other
-# --debug) it will succeed immediately
+--echo # Sending:
+--send COMMIT
 
+--echo # Switching to 'con1'.
 connection con1;
+--echo # Wait till COMMIT acquires protection against global read
+--echo # lock and pauses.
+SET DEBUG_SYNC='now WAIT_FOR acquired';
+--echo # Sending:
 send FLUSH TABLES WITH READ LOCK;
 
-# kill con1
+--echo # Switching to 'con2'.
 connection con2;
-SELECT ((@id := kill_id) - kill_id) FROM t1;
+SELECT ((@id := kill_id) - kill_id) FROM t1 LIMIT 1;
 
-# Wait for the debug sync point, test won't run on non-debug
-# builds anyway.
+--echo # Wait till FLUSH TABLES WITH READ LOCK blocks due
+--echo # to active COMMIT
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for all running commits to finish"
+  where state = "Waiting for global read lock"
   and info = "flush tables with read lock";
 --source include/wait_condition.inc
 
+--echo # Kill connection 'con1'.
 KILL CONNECTION @id;
 
+--echo # Switching to 'con1'.
 connection con1;
-# On debug builds it will be error 1053 (killed); on non-debug, or
-# debug build running without our --debug=make_global..., will be
-# error 0 (no error). The only important thing to test is that on
-# debug builds with our --debug=make_global... we don't hang forever.
---error 0,1317,2013
+--echo # Try to reap FLUSH TABLES WITH READ LOCK, 
+--echo # it fail due to killed statement and connection.
+--error 1317,2013
 reap;
 
+--echo # Switching to 'con2'.
 connection con2;
-DROP TABLE t1;
+--echo # Resume COMMIT.
+SET DEBUG_SYNC='now SIGNAL go';
 
+--echo # Switching to 'default'.
 connection default;
+--echo # Reaping COMMIT.
+--reap
 disconnect con2;
-
-# Restore global concurrent_insert value
-SET @@global.concurrent_insert= @old_concurrent_insert;
+DROP TABLE t1;
+SET DEBUG_SYNC= 'RESET';
 
 # Wait till all disconnects are completed
 --source include/wait_until_count_sessions.inc

=== modified file 'mysql-test/t/lock_multi.test'
--- a/mysql-test/t/lock_multi.test	2010-08-12 13:50:23 +0000
+++ b/mysql-test/t/lock_multi.test	2010-10-18 12:33:49 +0000
@@ -228,7 +228,7 @@ connection writer;
 # Sleep a bit till the flush of connection locker is in work and hangs
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for global metadata lock" and
+  where state = "Waiting for global read lock" and
         info = "FLUSH TABLES WITH READ LOCK";
 --source include/wait_condition.inc
 # This must not block.
@@ -260,7 +260,7 @@ connection writer;
 # Sleep a bit till the flush of connection locker is in work and hangs
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for global metadata lock" and
+  where state = "Waiting for global read lock" and
         info = "FLUSH TABLES WITH READ LOCK";
 --source include/wait_condition.inc
 --error ER_TABLE_NOT_LOCKED
@@ -296,10 +296,11 @@ DROP DATABASE mysqltest_1;
 # With bug in place: try to acquire LOCK_mysql_create_table...
 # When fixed: Reject dropping db because of the read lock.
 connection con1;
-# Wait a bit so that the session con2 is in state "Waiting for release of readlock"
+# Wait a bit so that the session con2 is in state
+# "Waiting for global read lock"
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for release of readlock"
+  where state = "Waiting for global read lock"
   and info = "DROP DATABASE mysqltest_1";
 --source include/wait_condition.inc
 --error ER_CANT_UPDATE_WITH_READLOCK
@@ -376,7 +377,7 @@ connection con5;
 --echo # con5
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for global metadata lock" and
+  where state = "Waiting for global read lock" and
         info = "flush tables with read lock";
 --source include/wait_condition.inc
 --echo # global read lock is taken
@@ -384,10 +385,11 @@ connection con3;
 --echo # con3
 send select * from t2 for update;
 connection con5;
-let $show_statement= SHOW PROCESSLIST;
-let $field= State;
-let $condition= = 'Waiting for release of readlock';
---source include/wait_show_condition.inc
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "select * from t2 for update";
+--source include/wait_condition.inc
 --echo # waiting for release of read lock
 connection con4;
 --echo # con4
@@ -433,10 +435,11 @@ connection con1;
 send update t2 set a = 1;
 connection default;
 --echo # default
-let $show_statement= SHOW PROCESSLIST;
-let $field= State;
-let $condition= = 'Waiting for release of readlock';
---source include/wait_show_condition.inc
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "update t2 set a = 1";
+--source include/wait_condition.inc
 --echo # statement is waiting for release of read lock
 connection con2;
 --echo # con2
@@ -460,10 +463,11 @@ connection con1;
 send lock tables t2 write;
 connection default;
 --echo # default
-let $show_statement= SHOW PROCESSLIST;
-let $field= State;
-let $condition= = 'Waiting for release of readlock';
---source include/wait_show_condition.inc
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for global read lock" and
+        info = "lock tables t2 write";
+--source include/wait_condition.inc
 --echo # statement is waiting for release of read lock
 connection con2;
 --echo # con2
@@ -571,7 +575,8 @@ connection default;
 --echo connection: default
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for global metadata lock";
+  where state = "Waiting for global read lock" and
+        info = "flush tables with read lock";
 --source include/wait_condition.inc
 alter table t1 add column j int;
 connect (insert,localhost,root,,test,,);
@@ -579,14 +584,16 @@ connection insert;
 --echo connection: insert
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for global metadata lock";
+  where state = "Waiting for global read lock" and
+        info = "flush tables with read lock";
 --source include/wait_condition.inc
 --send insert into t1 values (1,2);
 --echo connection: default
 connection default;
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for release of readlock";
+  where state = "Waiting for global read lock" and
+        info = "insert into t1 values (1,2)";
 --source include/wait_condition.inc
 unlock tables;
 connection flush;
@@ -594,7 +601,8 @@ connection flush;
 --reap
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for release of readlock";
+  where state = "Waiting for global read lock" and
+        info = "insert into t1 values (1,2)";
 --source include/wait_condition.inc
 select * from t1;
 unlock tables;
@@ -629,12 +637,12 @@ connection default;
 --echo connection: default
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for global metadata lock";
+  where state = "Waiting for global read lock";
 --source include/wait_condition.inc
 flush tables;
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for global metadata lock";
+  where state = "Waiting for global read lock";
 --source include/wait_condition.inc
 unlock tables;
 connection flush;
@@ -698,12 +706,12 @@ connection default;
 --echo connection: default
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for global metadata lock";
+  where state = "Waiting for global read lock";
 --source include/wait_condition.inc
 flush tables;
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for global metadata lock";
+  where state = "Waiting for global read lock";
 --source include/wait_condition.inc
 drop table t1;
 connection flush;

=== modified file 'mysql-test/t/mdl_sync.test'
--- a/mysql-test/t/mdl_sync.test	2010-10-06 14:34:28 +0000
+++ b/mysql-test/t/mdl_sync.test	2010-10-18 12:33:49 +0000
@@ -3661,7 +3661,7 @@ connection con2;
 SET DEBUG_SYNC= 'now WAIT_FOR table_opened';
 --echo # Check that FLUSH must wait to get the GRL
 --echo # and let CREATE PROCEDURE continue
-SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait';
+SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL grlwait';
 --send FLUSH TABLES WITH READ LOCK
 
 --echo # Connection 1
@@ -3689,20 +3689,31 @@ connection con2;
 SET DEBUG_SYNC= 'now WAIT_FOR table_opened';
 --echo # Check that FLUSH must wait to get the GRL
 --echo # and let DROP PROCEDURE continue
-SET DEBUG_SYNC= 'wait_lock_global_read_lock SIGNAL grlwait';
+SET DEBUG_SYNC= 'mdl_acquire_lock_wait SIGNAL grlwait';
 --send FLUSH TABLES WITH READ LOCK
 
---echo # Connection 1
-connection default;
---reap
-
 --echo # Connection 2
 connection con2;
+--echo # Once FLUSH TABLES WITH READ LOCK starts waiting
+--echo # DROP PROCEDURE will be waked up and will drop
+--echo # procedure. Global read lock will be granted after
+--echo # this point. After that DROP PROCEDURE will block
+--echo # due to GRL during attempt to auto-magically revoke
+--echo # privileges. This is expected as we treat these two 
+--echo # steps as a separate statements.
+--echo #
+--echo # QQ: This doesn't seem right and IMO breaks binary
+--echo #     logging!
 --reap
+--echo # Ublock second part of DROP PROCEDURE.
 UNLOCK TABLES;
 
 --echo # Connection 1
 connection default;
+--reap
+
+--echo # Connection 1
+connection default;
 SET DEBUG_SYNC= 'RESET';
 
 disconnect con2;
@@ -4485,7 +4496,7 @@ connection con2;
 --echo # Connection default
 connection default;
 let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
-  WHERE state='Waiting for release of readlock'
+  WHERE state='Waiting for global read lock'
   AND info='CREATE TABLE db1.t2(a INT)';
 --source include/wait_condition.inc
 UNLOCK TABLES;
@@ -4507,7 +4518,7 @@ connection con2;
 --echo # Connection default
 connection default;
 let $wait_condition=SELECT COUNT(*)=1 FROM information_schema.processlist
-  WHERE state='Waiting for release of readlock'
+  WHERE state='Waiting for global read lock'
   AND info='ALTER DATABASE db1 DEFAULT CHARACTER SET utf8';
 --source include/wait_condition.inc
 UNLOCK TABLES;

=== modified file 'mysql-test/t/trigger_notembedded.test'
--- a/mysql-test/t/trigger_notembedded.test	2010-09-07 09:00:41 +0000
+++ b/mysql-test/t/trigger_notembedded.test	2010-10-18 12:33:49 +0000
@@ -896,7 +896,7 @@ connection default;
 --echo connection: default
 let $wait_condition=
   select count(*) = 1 from information_schema.processlist
-  where state = "Waiting for global metadata lock";
+  where state = "Waiting for global read lock";
 --source include/wait_condition.inc
 create trigger t1_bi before insert on t1 for each row begin end;
 unlock tables;

=== modified file 'sql/ha_ndbcluster.cc'
--- a/sql/ha_ndbcluster.cc	2010-09-20 14:17:32 +0000
+++ b/sql/ha_ndbcluster.cc	2010-10-18 12:33:49 +0000
@@ -7382,38 +7382,35 @@ int ndbcluster_find_files(handlerton *ht
   }
 
   /*
+    Delete old files.
+
     ndbcluster_find_files() may be called from I_S code and ndbcluster_binlog
     thread in situations when some tables are already open. This means that
     code below will try to obtain exclusive metadata lock on some table
-    while holding shared meta-data lock on other tables. This might lead to
-    a deadlock, and therefore is disallowed by assertions of the metadata
-    locking subsystem. This is violation of metadata
-    locking protocol which has to be closed ASAP.
+    while holding shared meta-data lock on other tables. This might lead to a
+    deadlock but such a deadlock should be detected by MDL deadlock detector.
+
     XXX: the scenario described above is not covered with any test.
   */
-  if (!global_read_lock)
+  List_iterator_fast<char> it3(delete_list);
+  while ((file_name_str= it3++))
   {
-    // Delete old files
-    List_iterator_fast<char> it3(delete_list);
-    while ((file_name_str= it3++))
-    {
-      DBUG_PRINT("info", ("Remove table %s/%s", db, file_name_str));
-      // Delete the table and all related files
-      TABLE_LIST table_list;
-      table_list.init_one_table(db, strlen(db), file_name_str,
-                                strlen(file_name_str), file_name_str,
-                                TL_WRITE);
-      table_list.mdl_request.set_type(MDL_EXCLUSIVE);
-      (void)mysql_rm_table_part2(thd, &table_list,
-                                 FALSE,   /* if_exists */
-                                 FALSE,   /* drop_temporary */ 
-                                 FALSE,   /* drop_view */
-                                 TRUE     /* dont_log_query*/);
-      trans_commit_implicit(thd); /* Safety, should be unnecessary. */
-      thd->mdl_context.release_transactional_locks();
-      /* Clear error message that is returned when table is deleted */
-      thd->clear_error();
-    }
+    DBUG_PRINT("info", ("Remove table %s/%s", db, file_name_str));
+    // Delete the table and all related files
+    TABLE_LIST table_list;
+    table_list.init_one_table(db, strlen(db), file_name_str,
+                              strlen(file_name_str), file_name_str,
+                              TL_WRITE);
+    table_list.mdl_request.set_type(MDL_EXCLUSIVE);
+    (void)mysql_rm_table_part2(thd, &table_list,
+                               FALSE,   /* if_exists */
+                               FALSE,   /* drop_temporary */
+                               FALSE,   /* drop_view */
+                               TRUE     /* dont_log_query*/);
+    trans_commit_implicit(thd); /* Safety, should be unnecessary. */
+    thd->mdl_context.release_transactional_locks();
+    /* Clear error message that is returned when table is deleted */
+    thd->clear_error();
   }
 
   /* Lock mutex before creating .FRM files. */

=== modified file 'sql/handler.cc'
--- a/sql/handler.cc	2010-10-06 14:34:28 +0000
+++ b/sql/handler.cc	2010-10-18 12:33:49 +0000
@@ -29,8 +29,6 @@
 #include "sql_cache.h"                   // query_cache, query_cache_*
 #include "key.h"     // key_copy, key_unpack, key_cmp_if_same, key_cmp
 #include "sql_table.h"                   // build_table_filename
-#include "lock.h"               // wait_if_global_read_lock,
-                                // start_waiting_global_read_lock
 #include "sql_parse.h"                          // check_stack_overrun
 #include "sql_acl.h"            // SUPER_ACL
 #include "sql_base.h"           // free_io_cache
@@ -41,6 +39,7 @@
 #include "transaction.h"
 #include <errno.h>
 #include "probes_mysql.h"
+#include "debug_sync.h"         // DEBUG_SYNC
 
 #ifdef WITH_PARTITION_STORAGE_ENGINE
 #include "ha_partition.h"
@@ -1155,6 +1154,7 @@ int ha_commit_trans(THD *thd, bool all)
   {
     uint rw_ha_count;
     bool rw_trans;
+    MDL_request mdl_request;
 
     DBUG_EXECUTE_IF("crash_commit_before", DBUG_ABORT(););
 
@@ -1166,11 +1166,28 @@ int ha_commit_trans(THD *thd, bool all)
     /* rw_trans is TRUE when we in a transaction changing data */
     rw_trans= is_real_trans && (rw_ha_count > 0);
 
-    if (rw_trans &&
-        thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, FALSE))
+    if (rw_trans)
     {
-      ha_rollback_trans(thd, all);
-      DBUG_RETURN(1);
+      /*
+        Acquire metadata lock which will ensure that COMMIT is blocked
+        by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
+        progress blocks FTWRL).
+
+        We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
+        This allowance is needed to not break existing versions of innobackup
+        which do a BEGIN; INSERT; FLUSH TABLES WITH READ LOCK; COMMIT.
+      */
+      thd->global_read_lock.init_commit_request(&mdl_request);
+
+      if (! thd->global_read_lock.has_read_lock() &&
+          thd->mdl_context.acquire_lock(&mdl_request,
+                                        thd->variables.lock_wait_timeout))
+      {
+        ha_rollback_trans(thd, all);
+        DBUG_RETURN(1);
+      }
+
+      DEBUG_SYNC(thd, "ha_commit_trans_after_acquire_commit_lock");
     }
 
     if (rw_trans &&
@@ -1225,8 +1242,8 @@ int ha_commit_trans(THD *thd, bool all)
     DBUG_EXECUTE_IF("crash_commit_after", DBUG_ABORT(););
     RUN_HOOK(transaction, after_commit, (thd, FALSE));
 end:
-    if (rw_trans)
-      thd->global_read_lock.start_waiting_global_read_lock(thd);
+    if (rw_trans && mdl_request.ticket)
+      thd->mdl_context.release_lock(mdl_request.ticket);
   }
   /* Free resources and perform other cleanup even for 'empty' transactions. */
   else if (is_real_trans)

=== modified file 'sql/lock.cc'
--- a/sql/lock.cc	2010-08-12 13:50:23 +0000
+++ b/sql/lock.cc	2010-10-18 12:33:49 +0000
@@ -777,7 +777,19 @@ bool lock_schema_name(THD *thd, const ch
     return TRUE;
   }
 
-  global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
+  if (thd->global_read_lock.can_acquire_protection())
+    return TRUE;
+
+  /*
+    This function can be only called during execution of a DDL statement.
+    Since such statements release all metadata locks at its end we don't
+    need to do anything special to ensure that protection against GRL is
+    released.
+  */
+  DBUG_ASSERT(stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END));
+
+  thd->global_read_lock.init_protection_request(&global_request);
+
   mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE);
 
   mdl_requests.push_front(&mdl_request);
@@ -836,7 +848,18 @@ bool lock_routine_name(THD *thd, bool is
   DBUG_ASSERT(name);
   DEBUG_SYNC(thd, "before_wait_locked_pname");
 
-  global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
+  if (thd->global_read_lock.can_acquire_protection())
+    return TRUE;
+
+  /*
+    This function can be only called during execution of a DDL statement.
+    Since such statements release all metadata locks at its end we don't
+    need to do anything special to ensure that protection against GRL is
+    released.
+  */
+  DBUG_ASSERT(stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END));
+
+  thd->global_read_lock.init_protection_request(&global_request);
   schema_request.init(MDL_key::SCHEMA, db, "", MDL_INTENTION_EXCLUSIVE);
   mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE);
 
@@ -888,45 +911,24 @@ static void print_lock_error(int error, 
 /****************************************************************************
   Handling of global read locks
 
+  Global read lock is implemented using metadata lock infrastructure.
+
   Taking the global read lock is TWO steps (2nd step is optional; without
   it, COMMIT of existing transactions will be allowed):
   lock_global_read_lock() THEN make_global_read_lock_block_commit().
 
-  The global locks are handled through the global variables:
-  global_read_lock
-    count of threads which have the global read lock (i.e. have completed at
-    least the first step above)
-  global_read_lock_blocks_commit
-    count of threads which have the global read lock and block
-    commits (i.e. are in or have completed the second step above)
-  waiting_for_read_lock
-    count of threads which want to take a global read lock but cannot
-  protect_against_global_read_lock
-    count of threads which have set protection against global read lock.
-
-  access to them is protected with a mutex LOCK_global_read_lock
-
-  (XXX: one should never take LOCK_open if LOCK_global_read_lock is
-  taken, otherwise a deadlock may occur. Other mutexes could be a
-  problem too - grep the code for global_read_lock if you want to use
-  any other mutex here) Also one must not hold LOCK_open when calling
-  wait_if_global_read_lock(). When the thread with the global read lock
-  tries to close its tables, it needs to take LOCK_open in
-  close_thread_table().
-
   How blocking of threads by global read lock is achieved: that's
-  advisory. Any piece of code which should be blocked by global read lock must
-  be designed like this:
-  - call to wait_if_global_read_lock(). When this returns 0, no global read
-  lock is owned; if argument abort_on_refresh was 0, none can be obtained.
-  - job
-  - if abort_on_refresh was 0, call to start_waiting_global_read_lock() to
-  allow other threads to get the global read lock. I.e. removal of the
-  protection.
-  (Note: it's a bit like an implementation of rwlock).
-
-  [ I am sorry to mention some SQL syntaxes below I know I shouldn't but found
-  no better descriptive way ]
+  semi-automatic. We assume that any statement which should be blocked
+  by global read lock will either open and acquires write-lock on tables
+  or acquires metadata locks on objects it is going to modify. For any
+  such statement global IX metadata lock is automatically acquired for
+  its duration (in case of LOCK TABLES until end of LOCK TABLES mode).
+  And lock_global_read_lock() simply acquires global S metadata lock
+  and thus prohibits execution of statements which modify data (unless
+  they modify only temporary tables). If deadlock happens it is detected
+  by MDL subsystem and resolved in the standard fashion (by backing-off
+  metadata locks acquired so far and restarting open tables process
+  if possible).
 
   Why does FLUSH TABLES WITH READ LOCK need to block COMMIT: because it's used
   to read a non-moving SHOW MASTER STATUS, and a COMMIT writes to the binary
@@ -960,11 +962,6 @@ static void print_lock_error(int error, 
 
 ****************************************************************************/
 
-volatile uint global_read_lock=0;
-volatile uint global_read_lock_blocks_commit=0;
-static volatile uint protect_against_global_read_lock=0;
-static volatile uint waiting_for_read_lock=0;
-
 /**
   Take global read lock, wait if there is protection against lock.
 
@@ -985,64 +982,6 @@ bool Global_read_lock::lock_global_read_
   if (!m_state)
   {
     MDL_request mdl_request;
-    const char *old_message;
-    const char *new_message= "Waiting to get readlock";
-    (void) mysql_mutex_lock(&LOCK_global_read_lock);
-
-#if defined(ENABLED_DEBUG_SYNC)
-    /*
-      The below sync point fires if we have to wait for
-      protect_against_global_read_lock.
-
-      WARNING: Beware to use WAIT_FOR with this sync point. We hold
-      LOCK_global_read_lock here.
-
-      Call the sync point before calling enter_cond() as it does use
-      enter_cond() and exit_cond() itself if a WAIT_FOR action is
-      executed in spite of the above warning.
-
-      Pre-set proc_info so that it is available immediately after the
-      sync point sends a SIGNAL. This makes tests more reliable.
-    */
-    if (protect_against_global_read_lock)
-    {
-      thd_proc_info(thd, new_message);
-      DEBUG_SYNC(thd, "wait_lock_global_read_lock");
-    }
-#endif /* defined(ENABLED_DEBUG_SYNC) */
-
-    old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
-                                new_message);
-    DBUG_PRINT("info",
-	       ("waiting_for: %d  protect_against: %d",
-		waiting_for_read_lock, protect_against_global_read_lock));
-
-    waiting_for_read_lock++;
-    while (protect_against_global_read_lock && !thd->killed)
-      mysql_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
-    waiting_for_read_lock--;
-    if (thd->killed)
-    {
-      thd->exit_cond(old_message);
-      DBUG_RETURN(1);
-    }
-    m_state= GRL_ACQUIRED;
-    global_read_lock++;
-    thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
-    /*
-      When we perform FLUSH TABLES or ALTER TABLE under LOCK TABLES,
-      tables being reopened are protected only by meta-data locks at
-      some point. To avoid sneaking in with our global read lock at
-      this moment we have to take global shared meta data lock.
-
-      TODO: We should change this code to acquire global shared metadata
-            lock before acquiring global read lock. But in order to do
-            this we have to get rid of all those places in which
-            wait_if_global_read_lock() is called before acquiring
-            metadata locks first. Also long-term we should get rid of
-            redundancy between metadata locks, global read lock and DDL
-            blocker (see WL#4399 and WL#4400).
-    */
 
     DBUG_ASSERT(! thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "",
                                                  MDL_SHARED));
@@ -1050,20 +989,11 @@ bool Global_read_lock::lock_global_read_
 
     if (thd->mdl_context.acquire_lock(&mdl_request,
                                       thd->variables.lock_wait_timeout))
-    {
-      /* Our thread was killed -- return back to initial state. */
-      mysql_mutex_lock(&LOCK_global_read_lock);
-      if (!(--global_read_lock))
-      {
-        DBUG_PRINT("signal", ("Broadcasting COND_global_read_lock"));
-        mysql_cond_broadcast(&COND_global_read_lock);
-      }
-      mysql_mutex_unlock(&LOCK_global_read_lock);
-      m_state= GRL_NONE;
       DBUG_RETURN(1);
-    }
+
     thd->mdl_context.move_ticket_after_trans_sentinel(mdl_request.ticket);
     m_mdl_global_shared_lock= mdl_request.ticket;
+    m_state= GRL_ACQUIRED;
   }
   /*
     We DON'T set global_read_lock_blocks_commit now, it will be set after
@@ -1089,163 +1019,88 @@ bool Global_read_lock::lock_global_read_
 
 void Global_read_lock::unlock_global_read_lock(THD *thd)
 {
-  uint tmp;
   DBUG_ENTER("unlock_global_read_lock");
-  DBUG_PRINT("info",
-             ("global_read_lock: %u  global_read_lock_blocks_commit: %u",
-              global_read_lock, global_read_lock_blocks_commit));
 
   DBUG_ASSERT(m_mdl_global_shared_lock && m_state);
 
-  thd->mdl_context.release_lock(m_mdl_global_shared_lock);
-  m_mdl_global_shared_lock= NULL;
-
-  mysql_mutex_lock(&LOCK_global_read_lock);
-  tmp= --global_read_lock;
-  if (m_state == GRL_ACQUIRED_AND_BLOCKS_COMMIT)
-    --global_read_lock_blocks_commit;
-  mysql_mutex_unlock(&LOCK_global_read_lock);
-  /* Send the signal outside the mutex to avoid a context switch */
-  if (!tmp)
+  if (m_mdl_blocks_commits_lock)
   {
-    DBUG_PRINT("signal", ("Broadcasting COND_global_read_lock"));
-    mysql_cond_broadcast(&COND_global_read_lock);
+    thd->mdl_context.release_lock(m_mdl_blocks_commits_lock);
+    m_mdl_blocks_commits_lock= NULL;
   }
+  thd->mdl_context.release_lock(m_mdl_global_shared_lock);
+  m_mdl_global_shared_lock= NULL;
   m_state= GRL_NONE;
 
   DBUG_VOID_RETURN;
 }
 
-/**
-  Wait if the global read lock is set, and optionally seek protection against
-  global read lock.
 
-  See also "Handling of global read locks" above.
+/**
+  Check if this connection can acquire protecting against GRL and
+  emit error if otherwise.
 
-  @param thd              Reference to thread.
-  @param abort_on_refresh If True, abort waiting if a refresh occurs,
-                          do NOT seek protection against GRL.
-                          If False, wait until the GRL is released and seek
-                          protection against GRL.
-  @param is_not_commit    If False, called from a commit operation,
-                          wait only if commit blocking is also enabled.
-
-  @retval False  Success, protection against global read lock is set
-                          (if !abort_on_refresh)
-  @retval True   Failure, wait was aborted or thread was killed.
+  QQ: Does it make sense to make it inline?
 */
 
-#define must_wait (global_read_lock &&                             \
-                   (is_not_commit ||                               \
-                    global_read_lock_blocks_commit))
-
-bool Global_read_lock::
-wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
-                         bool is_not_commit)
+bool Global_read_lock::can_acquire_protection()
 {
-  const char *UNINIT_VAR(old_message);
-  bool result= 0, need_exit_cond;
-  DBUG_ENTER("wait_if_global_read_lock");
-
-  /*
-    If we already have protection against global read lock,
-    just increment the counter.
-  */
-  if (unlikely(m_protection_count > 0))
+  if (m_state)
   {
-    if (!abort_on_refresh)
-      m_protection_count++;
-    DBUG_RETURN(FALSE);
+    my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0));
+    return TRUE;
   }
-  /*
-    Assert that we do not own LOCK_open. If we would own it, other
-    threads could not close their tables. This would make a pretty
-    deadlock.
-  */
-  mysql_mutex_assert_not_owner(&LOCK_open);
+  return FALSE;
+}
 
-  mysql_mutex_lock(&LOCK_global_read_lock);
-  if ((need_exit_cond= must_wait))
-  {
-    if (m_state)		// This thread had the read locks
-    {
-      if (is_not_commit)
-        my_message(ER_CANT_UPDATE_WITH_READLOCK,
-                   ER(ER_CANT_UPDATE_WITH_READLOCK), MYF(0));
-      mysql_mutex_unlock(&LOCK_global_read_lock);
-      /*
-        We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
-        This allowance is needed to not break existing versions of innobackup
-        which do a BEGIN; INSERT; FLUSH TABLES WITH READ LOCK; COMMIT.
-      */
-      DBUG_RETURN(is_not_commit);
-    }
-    old_message=thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
-				"Waiting for release of readlock");
-    while (must_wait && ! thd->killed &&
-	   (!abort_on_refresh || !thd->open_tables ||
-            thd->open_tables->s->version == refresh_version))
-    {
-      DBUG_PRINT("signal", ("Waiting for COND_global_read_lock"));
-      mysql_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
-      DBUG_PRINT("signal", ("Got COND_global_read_lock"));
-    }
-    if (thd->killed)
-      result=1;
-  }
-  if (!abort_on_refresh && !result)
-  {
-    m_protection_count++;
-    protect_against_global_read_lock++;
-    DBUG_PRINT("sql_lock", ("protect_against_global_read_lock incr: %u",
-                            protect_against_global_read_lock));
-  }
-  /*
-    The following is only true in case of a global read locks (which is rare)
-    and if old_message is set
-  */
-  if (unlikely(need_exit_cond))
-    thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
-  else
-    mysql_mutex_unlock(&LOCK_global_read_lock);
-  DBUG_RETURN(result);
+
+/**
+  Initialize request for lock protecting normal statement from GRL
+  (and vice versa).
+
+  QQ: Does it make sense to make it inline?
+*/
+
+void Global_read_lock::init_protection_request(MDL_request *request)
+{
+  request->init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
 }
 
 
 /**
-  Release protection against global read lock and restart
-  global read lock waiters.
+  Initialize request for lock protecting commit from GRL (and vice versa).
+
+  QQ: Does it make sense to make it inline?
+*/
+
+void Global_read_lock::init_commit_request(MDL_request *request)
+{
+  request->init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE);
+}
+
 
-  Should only be called if we have protection against global read lock.
+/**
+  Release protection against global read lock if it is set.
 
   See also "Handling of global read locks" above.
 
   @param thd     Reference to thread.
 */
 
-void Global_read_lock::start_waiting_global_read_lock(THD *thd)
+void Global_read_lock::release_protection_if_set(THD *thd)
 {
-  bool tmp;
-  DBUG_ENTER("start_waiting_global_read_lock");
-  /*
-    Ignore request if we do not have protection against global read lock.
-    (Note that this is a violation of the interface contract, hence the assert).
-  */
-  DBUG_ASSERT(m_protection_count > 0);
-  if (unlikely(m_protection_count == 0))
-    DBUG_VOID_RETURN;
-  /* Decrement local read lock protection counter, return if we still have it */
-  if (unlikely(--m_protection_count > 0))
-    DBUG_VOID_RETURN;
-  if (unlikely(m_state))
-    DBUG_VOID_RETURN;
-  mysql_mutex_lock(&LOCK_global_read_lock);
-  DBUG_ASSERT(protect_against_global_read_lock);
-  tmp= (!--protect_against_global_read_lock &&
-        (waiting_for_read_lock || global_read_lock_blocks_commit));
-  mysql_mutex_unlock(&LOCK_global_read_lock);
-  if (tmp)
-    mysql_cond_broadcast(&COND_global_read_lock);
+  MDL_request mdl_request;
+  MDL_ticket *ticket;
+
+  DBUG_ENTER("release_protection_if_set");
+
+  mdl_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
+
+  if ((ticket= thd->mdl_context.find_ticket_at_front(&mdl_request)))
+  {
+    thd->mdl_context.release_lock(ticket);
+  }
+
   DBUG_VOID_RETURN;
 }
 
@@ -1267,8 +1122,7 @@ void Global_read_lock::start_waiting_glo
 
 bool Global_read_lock::make_global_read_lock_block_commit(THD *thd)
 {
-  bool error;
-  const char *old_message;
+  MDL_request mdl_request;
   DBUG_ENTER("make_global_read_lock_block_commit");
   /*
     If we didn't succeed lock_global_read_lock(), or if we already suceeded
@@ -1276,42 +1130,22 @@ bool Global_read_lock::make_global_read_
   */
   if (m_state != GRL_ACQUIRED)
     DBUG_RETURN(0);
-  mysql_mutex_lock(&LOCK_global_read_lock);
-  /* increment this BEFORE waiting on cond (otherwise race cond) */
-  global_read_lock_blocks_commit++;
-  /* For testing we set up some blocking, to see if we can be killed */
-  DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
-                  protect_against_global_read_lock++;);
-  old_message= thd->enter_cond(&COND_global_read_lock, &LOCK_global_read_lock,
-                               "Waiting for all running commits to finish");
-  while (protect_against_global_read_lock && !thd->killed)
-    mysql_cond_wait(&COND_global_read_lock, &LOCK_global_read_lock);
-  DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
-                  protect_against_global_read_lock--;);
-  if ((error= test(thd->killed)))
-    global_read_lock_blocks_commit--; // undo what we did
-  else
-    m_state= GRL_ACQUIRED_AND_BLOCKS_COMMIT;
-  thd->exit_cond(old_message); // this unlocks LOCK_global_read_lock
-  DBUG_RETURN(error);
-}
 
+  mdl_request.init(MDL_key::COMMIT, "", "", MDL_SHARED);
 
-/**
-  Broadcast COND_global_read_lock.
+  if (thd->mdl_context.acquire_lock(&mdl_request,
+                                    thd->variables.lock_wait_timeout))
+    DBUG_RETURN(TRUE);
 
-  TODO/FIXME: Dmitry thinks that we broadcast on COND_global_read_lock
-              when old instance of table is closed to avoid races
-              between incrementing refresh_version and
-              wait_if_global_read_lock(thd, TRUE, FALSE) call.
-              Once global read lock implementation starts using MDL
-              infrastructure this will became unnecessary and should
-              be removed.
-*/
+  /*
+    TODO/FIXME Ensure that this ticket is correctly placed after sentinel
+               when we are leaving LOCK TABLES mode.
+  */
+  thd->mdl_context.move_ticket_after_trans_sentinel(mdl_request.ticket);
+  m_mdl_blocks_commits_lock= mdl_request.ticket;
+  m_state= GRL_ACQUIRED_AND_BLOCKS_COMMIT;
 
-void broadcast_refresh(void)
-{
-  mysql_cond_broadcast(&COND_global_read_lock);
+  DBUG_RETURN(FALSE);
 }
 
 /**

=== modified file 'sql/lock.h'
--- a/sql/lock.h	2010-08-13 09:51:48 +0000
+++ b/sql/lock.h	2010-10-18 12:33:49 +0000
@@ -18,7 +18,6 @@ void mysql_lock_remove(THD *thd, MYSQL_L
 void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock);
 bool mysql_lock_abort_for_thread(THD *thd, TABLE *table);
 MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
-void broadcast_refresh(void);
 /* Lock based on name */
 bool lock_schema_name(THD *thd, const char *db);
 /* Lock based on stored routine name */

=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc	2010-10-09 10:18:16 +0000
+++ b/sql/log_event.cc	2010-10-18 12:33:49 +0000
@@ -4959,6 +4959,8 @@ error:
   */
   if (! thd->in_multi_stmt_transaction_mode())
     thd->mdl_context.release_transactional_locks();
+  else
+    thd->global_read_lock.release_protection_if_set(thd);
 
   DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error",
                   thd->is_slave_error= 0; thd->is_fatal_error= 1;);

=== modified file 'sql/mdl.cc'
--- a/sql/mdl.cc	2010-09-30 13:29:12 +0000
+++ b/sql/mdl.cc	2010-10-18 12:33:49 +0000
@@ -78,12 +78,13 @@ void notify_shared_lock(THD *thd, MDL_ti
 
 const char *MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
 {
-  "Waiting for global metadata lock",
+  "Waiting for global read lock",
   "Waiting for schema metadata lock",
   "Waiting for table metadata lock",
   "Waiting for stored function metadata lock",
   "Waiting for stored procedure metadata lock",
-  NULL
+  NULL,
+  "Waiting for global read lock"
 };
 
 static bool mdl_initialized= 0;
@@ -100,7 +101,6 @@ class MDL_map
 public:
   void init();
   void destroy();
-  MDL_lock *find(const MDL_key *key);
   MDL_lock *find_or_insert(const MDL_key *key);
   void remove(MDL_lock *lock);
 private:
@@ -110,6 +110,10 @@ private:
   HASH m_locks;
   /* Protects access to m_locks hash. */
   mysql_mutex_t m_mutex;
+  /** Pre-allocated MDL_lock object for GLOBAL namespace. */
+  MDL_lock *m_global_lock;
+  /** Pre-allocated MDL_lock object for COMMIT namespace. */
+  MDL_lock *m_commit_lock;
 };
 
 
@@ -373,6 +377,8 @@ public:
   bool visit_subgraph(MDL_ticket *waiting_ticket,
                       MDL_wait_for_graph_visitor *gvisitor);
 
+  virtual bool needs_notification(const MDL_ticket *ticket) const = 0;
+
   /** List of granted tickets for this lock. */
   Ticket_list m_granted;
   /** Tickets for contexts waiting to acquire a lock. */
@@ -442,6 +448,10 @@ public:
   {
     return m_waiting_incompatible;
   }
+  virtual bool needs_notification(const MDL_ticket *ticket) const
+  {
+    return (ticket->get_type() == MDL_SHARED);
+  }
 
 private:
   static const bitmap_t m_granted_incompatible[MDL_TYPE_END];
@@ -469,6 +479,10 @@ public:
   {
     return m_waiting_incompatible;
   }
+  virtual bool needs_notification(const MDL_ticket *ticket) const
+  {
+    return ticket->is_upgradable_or_exclusive();
+  }
 
 private:
   static const bitmap_t m_granted_incompatible[MDL_TYPE_END];
@@ -536,9 +550,14 @@ void mdl_destroy()
 
 void MDL_map::init()
 {
+  MDL_key global_lock_key(MDL_key::GLOBAL, "", "");
+  MDL_key commit_lock_key(MDL_key::COMMIT, "", "");
+
   mysql_mutex_init(key_MDL_map_mutex, &m_mutex, NULL);
   my_hash_init(&m_locks, &my_charset_bin, 16 /* FIXME */, 0, 0,
                mdl_locks_key, 0, 0);
+  m_global_lock= MDL_lock::create(&global_lock_key);
+  m_commit_lock= MDL_lock::create(&commit_lock_key);
 }
 
 
@@ -552,6 +571,8 @@ void MDL_map::destroy()
   DBUG_ASSERT(!m_locks.records);
   mysql_mutex_destroy(&m_mutex);
   my_hash_free(&m_locks);
+  MDL_lock::destroy(m_global_lock);
+  MDL_lock::destroy(m_commit_lock);
 }
 
 
@@ -569,43 +590,28 @@ MDL_lock* MDL_map::find_or_insert(const 
   MDL_lock *lock;
   my_hash_value_type hash_value;
 
-  hash_value= my_calc_hash(&m_locks, mdl_key->ptr(), mdl_key->length());
-
-retry:
-  mysql_mutex_lock(&m_mutex);
-  if (!(lock= (MDL_lock*) my_hash_search_using_hash_value(&m_locks,
-                                                          hash_value,
-                                                          mdl_key->ptr(),
-                                                          mdl_key->length())))
+  if (mdl_key->mdl_namespace() == MDL_key::GLOBAL ||
+      mdl_key->mdl_namespace() == MDL_key::COMMIT)
   {
-    lock= MDL_lock::create(mdl_key);
-    if (!lock || my_hash_insert(&m_locks, (uchar*)lock))
-    {
-      mysql_mutex_unlock(&m_mutex);
-      MDL_lock::destroy(lock);
-      return NULL;
-    }
-  }
-
-  if (move_from_hash_to_lock_mutex(lock))
-    goto retry;
+    /*
+      Avoid locking m_mutex when lock for GLOBAL or COMMIT namespace is
+      requested. Return pointer to pre-allocated MDL_lock instance instead.
+      Such an optimization allows to save one mutex lock/unlock for any
+      statement changing data.
 
-  return lock;
-}
+      It works since these namespaces contain only one element so keys
+      for them look like '<namespace-id>\0\0'.
+    */
+    DBUG_ASSERT(mdl_key->length() == 3);
 
+    lock= (mdl_key->mdl_namespace() == MDL_key::GLOBAL) ? m_global_lock :
+                                                          m_commit_lock;
 
-/**
-  Find MDL_lock object corresponding to the key.
+    mysql_prlock_wrlock(&lock->m_rwlock);
 
-  @retval non-NULL - MDL_lock instance for the key with locked
-                     MDL_lock::m_rwlock.
-  @retval NULL     - There was no MDL_lock for the key.
-*/
+    return lock;
+  }
 
-MDL_lock* MDL_map::find(const MDL_key *mdl_key)
-{
-  MDL_lock *lock;
-  my_hash_value_type hash_value;
 
   hash_value= my_calc_hash(&m_locks, mdl_key->ptr(), mdl_key->length());
 
@@ -616,8 +622,13 @@ retry:
                                                           mdl_key->ptr(),
                                                           mdl_key->length())))
   {
-    mysql_mutex_unlock(&m_mutex);
-    return NULL;
+    lock= MDL_lock::create(mdl_key);
+    if (!lock || my_hash_insert(&m_locks, (uchar*)lock))
+    {
+      mysql_mutex_unlock(&m_mutex);
+      MDL_lock::destroy(lock);
+      return NULL;
+    }
   }
 
   if (move_from_hash_to_lock_mutex(lock))
@@ -684,6 +695,17 @@ void MDL_map::remove(MDL_lock *lock)
 {
   uint ref_usage, ref_release;
 
+  if (lock->key.mdl_namespace() == MDL_key::GLOBAL ||
+      lock->key.mdl_namespace() == MDL_key::COMMIT)
+  {
+    /*
+      Never destroy pre-allocated MDL_lock objects for GLOBAL and
+      COMMIT namespaces.
+    */
+    mysql_prlock_unlock(&lock->m_rwlock);
+    return;
+  }
+
   /*
     Destroy the MDL_lock object, but ensure that anyone that is
     holding a reference to the object is not remaining, if so he
@@ -846,6 +868,7 @@ inline MDL_lock *MDL_lock::create(const 
   {
     case MDL_key::GLOBAL:
     case MDL_key::SCHEMA:
+    case MDL_key::COMMIT:
       return new MDL_scoped_lock(mdl_key);
     default:
       return new MDL_object_lock(mdl_key);
@@ -1476,6 +1499,31 @@ MDL_context::find_ticket(MDL_request *md
 
 
 /**
+  Check whether the ticket at the front of context's ticket list
+  is compatible with request.
+
+  @param mdl_request  Lock request object for ticket to find.
+
+  @note Tickets which correspond to lock types "stronger" than one
+        being requested are also considered compatible.
+
+  @return A pointer to the lock ticket for the object or NULL otherwise.
+*/
+
+MDL_ticket * MDL_context::find_ticket_at_front(MDL_request *mdl_request)
+{
+  MDL_ticket *ticket= m_tickets.front();
+
+  if (ticket &&
+      mdl_request->key.is_equal(&ticket->m_lock->key) &&
+      ticket->has_stronger_or_equal_type(mdl_request->type))
+    return ticket;
+
+  return NULL;
+}
+
+
+/**
   Try to acquire one lock.
 
   Unlike exclusive locks, shared locks are acquired one by
@@ -1747,7 +1795,7 @@ MDL_context::acquire_lock(MDL_request *m
   */
   m_wait.reset_status();
 
-  if (ticket->is_upgradable_or_exclusive())
+  if (lock->needs_notification(ticket))
     lock->notify_shared_locks(this);
 
   mysql_prlock_unlock(&lock->m_rwlock);
@@ -1759,7 +1807,7 @@ MDL_context::acquire_lock(MDL_request *m
 
   find_deadlock();
 
-  if (ticket->is_upgradable_or_exclusive())
+  if (lock->needs_notification(ticket))
   {
     struct timespec abs_shortwait;
     set_timespec(abs_shortwait, 1);
@@ -2499,3 +2547,16 @@ void MDL_context::move_ticket_after_tran
   else
     m_tickets.insert_after(m_trans_sentinel, mdl_ticket);
 }
+
+
+/**
+  Move ticket to the front of the context's ticket list.
+
+  @param mdl_ticket  Ticket to move.
+*/
+
+void MDL_context::move_ticket_to_front(MDL_ticket *mdl_ticket)
+{
+  m_tickets.remove(mdl_ticket);
+  m_tickets.push_front(mdl_ticket);
+}

=== modified file 'sql/mdl.h'
--- a/sql/mdl.h	2010-09-30 13:29:12 +0000
+++ b/sql/mdl.h	2010-10-18 12:33:49 +0000
@@ -184,6 +184,7 @@ public:
                             FUNCTION,
                             PROCEDURE,
                             TRIGGER,
+                            COMMIT,
                             /* This should be the last ! */
                             NAMESPACE_END };
 
@@ -612,6 +613,7 @@ public:
     m_trans_sentinel= sentinel_arg;
   }
   void move_ticket_after_trans_sentinel(MDL_ticket *mdl_ticket);
+  void move_ticket_to_front(MDL_ticket *mdl_ticket);
 
   void release_transactional_locks();
   void rollback_to_savepoint(MDL_ticket *mdl_savepoint);
@@ -745,9 +747,12 @@ private:
     readily available to the wait-for graph iterator.
    */
   MDL_wait_for_subgraph *m_waiting_for;
-private:
+public:
   MDL_ticket *find_ticket(MDL_request *mdl_req,
                           bool *is_transactional);
+
+  MDL_ticket *find_ticket_at_front(MDL_request *mdl_req);
+private:
   void release_locks_stored_before(MDL_ticket *sentinel);
   bool try_acquire_lock_impl(MDL_request *mdl_request,
                              MDL_ticket **out_ticket);

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2010-10-08 14:52:39 +0000
+++ b/sql/mysqld.cc	2010-10-18 12:33:49 +0000
@@ -604,8 +604,7 @@ pthread_key(MEM_ROOT**,THR_MALLOC);
 pthread_key(THD*, THR_THD);
 mysql_mutex_t LOCK_thread_count;
 mysql_mutex_t
-  LOCK_status, LOCK_global_read_lock,
-  LOCK_error_log, LOCK_uuid_generator,
+  LOCK_status, LOCK_error_log, LOCK_uuid_generator,
   LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
   LOCK_crypt,
   LOCK_global_system_variables,
@@ -625,7 +624,6 @@ mysql_mutex_t LOCK_des_key_file;
 mysql_rwlock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave;
 mysql_rwlock_t LOCK_system_variables_hash;
 mysql_cond_t COND_thread_count;
-mysql_cond_t COND_global_read_lock;
 pthread_t signal_thread;
 pthread_attr_t connection_attrib;
 mysql_mutex_t LOCK_server_started;
@@ -1560,12 +1558,10 @@ static void clean_up_mutexes()
   mysql_rwlock_destroy(&LOCK_sys_init_slave);
   mysql_mutex_destroy(&LOCK_global_system_variables);
   mysql_rwlock_destroy(&LOCK_system_variables_hash);
-  mysql_mutex_destroy(&LOCK_global_read_lock);
   mysql_mutex_destroy(&LOCK_uuid_generator);
   mysql_mutex_destroy(&LOCK_prepared_stmt_count);
   mysql_mutex_destroy(&LOCK_error_messages);
   mysql_cond_destroy(&COND_thread_count);
-  mysql_cond_destroy(&COND_global_read_lock);
   mysql_cond_destroy(&COND_thread_cache);
   mysql_cond_destroy(&COND_flush_thread_cache);
   mysql_cond_destroy(&COND_manager);
@@ -3524,8 +3520,6 @@ static int init_thread_environment()
                    &LOCK_global_system_variables, MY_MUTEX_INIT_FAST);
   mysql_rwlock_init(key_rwlock_LOCK_system_variables_hash,
                     &LOCK_system_variables_hash);
-  mysql_mutex_init(key_LOCK_global_read_lock,
-                   &LOCK_global_read_lock, MY_MUTEX_INIT_FAST);
   mysql_mutex_init(key_LOCK_prepared_stmt_count,
                    &LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST);
   mysql_mutex_init(key_LOCK_error_messages,
@@ -3553,7 +3547,6 @@ static int init_thread_environment()
   mysql_rwlock_init(key_rwlock_LOCK_sys_init_slave, &LOCK_sys_init_slave);
   mysql_rwlock_init(key_rwlock_LOCK_grant, &LOCK_grant);
   mysql_cond_init(key_COND_thread_count, &COND_thread_count, NULL);
-  mysql_cond_init(key_COND_global_read_lock, &COND_global_read_lock, NULL);
   mysql_cond_init(key_COND_thread_cache, &COND_thread_cache, NULL);
   mysql_cond_init(key_COND_flush_thread_cache, &COND_flush_thread_cache, NULL);
   mysql_cond_init(key_COND_manager, &COND_manager, NULL);
@@ -7656,7 +7649,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key
   key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
   key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
   key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
-  key_LOCK_gdl, key_LOCK_global_read_lock, key_LOCK_global_system_variables,
+  key_LOCK_gdl, key_LOCK_global_system_variables,
   key_LOCK_manager,
   key_LOCK_prepared_stmt_count,
   key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
@@ -7694,7 +7687,6 @@ static PSI_mutex_info all_server_mutexes
   { &key_LOCK_delayed_status, "LOCK_delayed_status", PSI_FLAG_GLOBAL},
   { &key_LOCK_error_log, "LOCK_error_log", PSI_FLAG_GLOBAL},
   { &key_LOCK_gdl, "LOCK_gdl", PSI_FLAG_GLOBAL},
-  { &key_LOCK_global_read_lock, "LOCK_global_read_lock", PSI_FLAG_GLOBAL},
   { &key_LOCK_global_system_variables, "LOCK_global_system_variables", PSI_FLAG_GLOBAL},
   { &key_LOCK_manager, "LOCK_manager", PSI_FLAG_GLOBAL},
   { &key_LOCK_prepared_stmt_count, "LOCK_prepared_stmt_count", PSI_FLAG_GLOBAL},
@@ -7743,7 +7735,7 @@ PSI_cond_key key_PAGE_cond, key_COND_act
 #endif /* HAVE_MMAP */
 
 PSI_cond_key key_BINLOG_COND_prep_xids, key_BINLOG_update_cond,
-  key_COND_cache_status_changed, key_COND_global_read_lock, key_COND_manager,
+  key_COND_cache_status_changed, key_COND_manager,
   key_COND_rpl_status, key_COND_server_started,
   key_delayed_insert_cond, key_delayed_insert_cond_client,
   key_item_func_sleep_cond, key_master_info_data_cond,
@@ -7766,7 +7758,6 @@ static PSI_cond_info all_server_conds[]=
   { &key_BINLOG_COND_prep_xids, "MYSQL_BIN_LOG::COND_prep_xids", 0},
   { &key_BINLOG_update_cond, "MYSQL_BIN_LOG::update_cond", 0},
   { &key_COND_cache_status_changed, "Query_cache::COND_cache_status_changed", 0},
-  { &key_COND_global_read_lock, "COND_global_read_lock", PSI_FLAG_GLOBAL},
   { &key_COND_manager, "COND_manager", PSI_FLAG_GLOBAL},
   { &key_COND_rpl_status, "COND_rpl_status", PSI_FLAG_GLOBAL},
   { &key_COND_server_started, "COND_server_started", PSI_FLAG_GLOBAL},

=== modified file 'sql/mysqld.h'
--- a/sql/mysqld.h	2010-08-30 14:07:40 +0000
+++ b/sql/mysqld.h	2010-10-18 12:33:49 +0000
@@ -100,7 +100,7 @@ extern bool opt_ignore_builtin_innodb;
 extern my_bool opt_character_set_client_handshake;
 extern bool volatile abort_loop;
 extern bool in_bootstrap;
-extern uint volatile thread_count, global_read_lock;
+extern uint volatile thread_count;
 extern uint connection_count;
 extern my_bool opt_safe_user_create;
 extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap;
@@ -227,7 +227,7 @@ extern PSI_mutex_key key_BINLOG_LOCK_ind
   key_delayed_insert_mutex, key_hash_filo_lock, key_LOCK_active_mi,
   key_LOCK_connection_count, key_LOCK_crypt, key_LOCK_delayed_create,
   key_LOCK_delayed_insert, key_LOCK_delayed_status, key_LOCK_error_log,
-  key_LOCK_gdl, key_LOCK_global_read_lock, key_LOCK_global_system_variables,
+  key_LOCK_gdl, key_LOCK_global_system_variables,
   key_LOCK_logger, key_LOCK_manager,
   key_LOCK_prepared_stmt_count,
   key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
@@ -248,7 +248,7 @@ extern PSI_cond_key key_PAGE_cond, key_C
 #endif /* HAVE_MMAP */
 
 extern PSI_cond_key key_BINLOG_COND_prep_xids, key_BINLOG_update_cond,
-  key_COND_cache_status_changed, key_COND_global_read_lock, key_COND_manager,
+  key_COND_cache_status_changed, key_COND_manager,
   key_COND_rpl_status, key_COND_server_started,
   key_delayed_insert_cond, key_delayed_insert_cond_client,
   key_item_func_sleep_cond, key_master_info_data_cond,
@@ -320,7 +320,7 @@ extern mysql_mutex_t
        LOCK_user_locks, LOCK_status,
        LOCK_error_log, LOCK_delayed_insert, LOCK_uuid_generator,
        LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
-       LOCK_slave_list, LOCK_active_mi, LOCK_manager, LOCK_global_read_lock,
+       LOCK_slave_list, LOCK_active_mi, LOCK_manager,
        LOCK_global_system_variables, LOCK_user_conn,
        LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count;
 extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
@@ -333,7 +333,6 @@ extern mysql_rwlock_t LOCK_grant, LOCK_s
 extern mysql_rwlock_t LOCK_system_variables_hash;
 extern mysql_cond_t COND_thread_count;
 extern mysql_cond_t COND_manager;
-extern mysql_cond_t COND_global_read_lock;
 extern int32 thread_running;
 extern my_atomic_rwlock_t thread_running_lock;
 

=== modified file 'sql/rpl_rli.cc'
--- a/sql/rpl_rli.cc	2010-07-27 10:25:53 +0000
+++ b/sql/rpl_rli.cc	2010-10-18 12:33:49 +0000
@@ -1274,6 +1274,9 @@ void Relay_log_info::slave_close_thread_
   */
   if (! thd->in_multi_stmt_transaction_mode())
     thd->mdl_context.release_transactional_locks();
+  else
+    thd->global_read_lock.release_protection_if_set(thd);
+
   clear_tables_to_lock();
 }
 #endif

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2010-10-12 12:19:33 +0000
+++ b/sql/sp_head.cc	2010-10-18 12:33:49 +0000
@@ -2126,6 +2126,8 @@ sp_head::execute_procedure(THD *thd, Lis
 
     if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
       thd->mdl_context.release_transactional_locks();
+    else if (! thd->in_sub_stmt && !thd->locked_tables_mode)
+      thd->global_read_lock.release_protection_if_set(thd);
 
     thd->rollback_item_tree_changes();
 
@@ -2961,6 +2963,8 @@ sp_lex_keeper::reset_lex_and_exec_core(T
 
     if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
       thd->mdl_context.release_transactional_locks();
+    else if (! thd->in_sub_stmt && !thd->locked_tables_mode)
+      thd->global_read_lock.release_protection_if_set(thd);
   }
 
   if (m_lex->query_tables_own_last)

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2010-10-14 16:56:56 +0000
+++ b/sql/sql_base.cc	2010-10-18 12:33:49 +0000
@@ -21,7 +21,7 @@
 #include "sql_priv.h"
 #include "unireg.h"
 #include "debug_sync.h"
-#include "lock.h"        // broadcast_refresh, mysql_lock_remove,
+#include "lock.h"        // mysql_lock_remove,
                          // mysql_unlock_tables,
                          // mysql_lock_have_duplicate
 #include "sql_show.h"    // append_identifier
@@ -1285,20 +1285,12 @@ static void mark_used_tables_as_free_for
 
 static void close_open_tables(THD *thd)
 {
-  bool found_old_table= 0;
-
   mysql_mutex_assert_not_owner(&LOCK_open);
 
   DBUG_PRINT("info", ("thd->open_tables: 0x%lx", (long) thd->open_tables));
 
   while (thd->open_tables)
-    found_old_table|= close_thread_table(thd, &thd->open_tables);
-
-  if (found_old_table)
-  {
-    /* Tell threads waiting for refresh that something has happened */
-    broadcast_refresh();
-  }
+    (void) close_thread_table(thd, &thd->open_tables);
 }
 
 
@@ -1364,11 +1356,6 @@ close_all_tables_for_name(THD *thd, TABL
   /* Remove the table share from the cache. */
   tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table_name,
                    FALSE);
-  /*
-    There could be a FLUSH thread waiting
-    on the table to go away. Wake it up.
-  */
-  broadcast_refresh();
 }
 
 
@@ -2628,32 +2615,6 @@ bool open_table(THD *thd, TABLE_LIST *ta
                TMP_TABLE_KEY_EXTRA);
 
   /*
-    We need this to work for all tables, including temporary
-    tables, for backwards compatibility. But not under LOCK
-    TABLES, since under LOCK TABLES one can't use a non-prelocked
-    table.  This code only works for updates done inside DO/SELECT
-    f1() statements, normal DML is handled by means of
-    sql_command_flags.
-  */
-  if (global_read_lock && table_list->lock_type >= TL_WRITE_ALLOW_WRITE &&
-      ! (flags & MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK) &&
-      ! thd->locked_tables_mode)
-  {
-    /*
-      Someone has issued FLUSH TABLES WITH READ LOCK and we want
-      a write lock. Wait until the lock is gone.
-    */
-    if (thd->global_read_lock.wait_if_global_read_lock(thd, 1, 1))
-      DBUG_RETURN(TRUE);
-
-    if (thd->open_tables && thd->open_tables->s->version != refresh_version)
-    {
-      (void)ot_ctx->request_backoff_action(Open_table_context::OT_REOPEN_TABLES,
-                                           NULL);
-      DBUG_RETURN(TRUE);
-    }
-  }
-  /*
     Unless requested otherwise, try to resolve this table in the list
     of temporary tables of this thread. In MySQL temporary tables
     are always thread-local and "shadow" possible base tables with the
@@ -2822,6 +2783,60 @@ bool open_table(THD *thd, TABLE_LIST *ta
     This is the normal use case.
   */
 
+  /*
+    We are not under LOCK TABLES and going to acquire write-lock/
+    modify the base table. We need to acquire protection against
+    global read lock until end of this statement in order to have
+    this statement blocked by active FLUSH TABLES WITH READ LOCK.
+
+    We don't block acquire this protection under LOCK TABLES as
+    such protection already acquired at LOCK TABLES time and
+    not released until UNLOCK TABLES.
+
+    We don't block statements which modify only temporary tables
+    as these tables are not preserved by backup by any form of
+    backup which uses FLUSH TABLES WITH READ LOCK.
+
+    TODO: The fact that we sometimes acquire protection against
+          GRL only when we encounter table to be write-locked
+          slightly increases probability of deadlock.
+          This problem will be solved once Alik pushes his
+          temporary table refactoring patch and we can start
+          pre-acquiring metadata locks at the beggining of
+          open_tables() call.
+
+    QQ: What about MYSQL_OPEN_HAS_MDL_LOCK flag?
+  */
+  if (table_list->lock_type >= TL_WRITE_ALLOW_WRITE &&
+      ! (flags & (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
+                  MYSQL_OPEN_FORCE_SHARED_MDL |
+                  MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)) &&
+      ! thd->locked_tables_mode)
+  {
+    MDL_request protection_request;
+    MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
+
+    if (thd->global_read_lock.can_acquire_protection())
+      DBUG_RETURN(TRUE);
+
+    thd->global_read_lock.init_protection_request(&protection_request);
+    /*
+      Install error handler which if possible will convert deadlock error
+      into request to back-off and restart process of opening tables.
+    */
+    thd->push_internal_handler(&mdl_deadlock_handler);
+    bool result= thd->mdl_context.acquire_lock(&protection_request,
+                                               ot_ctx->get_timeout());
+    thd->pop_internal_handler();
+
+    if (result)
+      DBUG_RETURN(TRUE);
+
+    ot_ctx->set_protection_against_grl(&thd->mdl_context,
+                                       protection_request.ticket);
+  }
+
+
   if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
   {
     if (open_table_get_mdl_lock(thd, ot_ctx, &table_list->mdl_request,
@@ -3379,7 +3394,6 @@ unlink_all_closed_tables(THD *thd, MYSQL
 
       close_thread_table(thd, &thd->open_tables);
     }
-    broadcast_refresh();
   }
   /* Exclude all closed tables from the LOCK TABLES list. */
   for (TABLE_LIST *table_list= m_locked_tables; table_list; table_list=
@@ -3856,7 +3870,8 @@ Open_table_context::Open_table_context(T
              LONG_TIMEOUT : thd->variables.lock_wait_timeout),
    m_flags(flags),
    m_action(OT_NO_ACTION),
-   m_has_locks(thd->mdl_context.has_locks())
+   m_has_locks(thd->mdl_context.has_locks()),
+   m_protection_against_grl(NULL)
 {}
 
 
@@ -4013,6 +4028,12 @@ recover_from_failed_open(THD *thd)
     for safety.
   */
   m_failed_table= NULL;
+  /*
+    Reset pointer to ticket for metadata lock protecting
+    against GRL. It is no longer valid as the lock was
+    released by close_tables_for_reopen().
+  */
+  m_protection_against_grl= NULL;
   /* Prepare for possible another back-off. */
   m_action= OT_NO_ACTION;
   return result;
@@ -4554,8 +4575,19 @@ lock_table_names(THD *thd,
                            MDL_INTENTION_EXCLUSIVE);
       mdl_requests.push_front(schema_request);
     }
-    /* Take the global intention exclusive lock. */
-    global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
+
+    if (thd->global_read_lock.can_acquire_protection())
+      return TRUE;
+
+    /*
+      We can get here only during execution of code which will release
+      all metadata locks by the statement's end. So we don't need to do
+      anything special to ensure that protection against GRL is released.
+
+      QQ: This assumption is kind of ugly and hard to enforce using asserts.
+          Is there any nice way to get rid of it?
+    */
+    thd->global_read_lock.init_protection_request(&global_request);
     mdl_requests.push_front(&global_request);
   }
 
@@ -4893,6 +4925,16 @@ err:
   thd_proc_info(thd, 0);
   free_root(&new_frm_mem, MYF(0));              // Free pre-alloced block
 
+  if (ot_ctx.get_protection_against_grl())
+  {
+    /*
+      Move ticket for metadata lock which protects this statement from
+      a GRL to the front of ticket list. This allows to find it quickly
+      when this lock has to be released at the end of statement.
+    */
+    thd->mdl_context.move_ticket_to_front(ot_ctx.get_protection_against_grl());
+  }
+
   if (error && *table_to_open)
   {
     (*table_to_open)->table= NULL;
@@ -5332,6 +5374,17 @@ end:
       trans_rollback_stmt(thd);
     close_thread_tables(thd);
   }
+
+  if (ot_ctx.get_protection_against_grl())
+  {
+    /*
+      Move ticket for metadata lock which protects this statement from
+      a GRL to the front of ticket list. This allows to find it quickly
+      when this lock has to be released at the end of statement.
+    */
+    thd->mdl_context.move_ticket_to_front(ot_ctx.get_protection_against_grl());
+  }
+
   thd_proc_info(thd, 0);
   DBUG_RETURN(table);
 }

=== modified file 'sql/sql_base.h'
--- a/sql/sql_base.h	2010-09-30 13:29:12 +0000
+++ b/sql/sql_base.h	2010-10-18 12:33:49 +0000
@@ -519,6 +519,23 @@ public:
   }
 
   uint get_flags() const { return m_flags; }
+
+  /**
+    Set ticket for metadata lock which protects this statement against GRL
+    if it was acquired while opening tables.
+  */
+  void set_protection_against_grl(MDL_context *mdl_ctx, MDL_ticket *ticket)
+  {
+    if (! m_protection_against_grl &&
+        ! mdl_ctx->has_lock(m_start_of_statement_svp, ticket))
+      m_protection_against_grl= ticket;
+  }
+
+  MDL_ticket* get_protection_against_grl() const
+  {
+    return m_protection_against_grl;
+  }
+
 private:
   /**
     For OT_DISCOVER and OT_REPAIR actions, the table list element for
@@ -542,6 +559,13 @@ private:
     and we can't safely do back-off (and release them).
   */
   bool m_has_locks;
+  /**
+    Metadata lock which protects this statement against global read lock
+    and which might needed to be individually released at the end of
+    statement execution. NULL in cases when such protection is unneeded
+    or when we know that this statement will release all locks at its end.
+  */
+  MDL_ticket *m_protection_against_grl;
 };
 
 

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2010-10-04 12:42:16 +0000
+++ b/sql/sql_class.cc	2010-10-18 12:33:49 +0000
@@ -3458,7 +3458,10 @@ void THD::set_mysys_var(struct st_my_thr
 void THD::leave_locked_tables_mode()
 {
   locked_tables_mode= LTM_NONE;
-  /* Make sure we don't release the global read lock when leaving LTM. */
+  /*
+    Make sure we don't release the global read lock when leaving LTM.
+    TODO/FIXME: Add handling of commit blocker!
+  */
   mdl_context.reset_trans_sentinel(global_read_lock.global_shared_lock());
   /* Also ensure that we don't release metadata locks for open HANDLERs. */
   if (handler_tables_hash.records)

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2010-09-20 14:17:32 +0000
+++ b/sql/sql_class.h	2010-10-18 12:33:49 +0000
@@ -1336,26 +1336,35 @@ public:
   };
 
   Global_read_lock()
-    :m_protection_count(0), m_state(GRL_NONE), m_mdl_global_shared_lock(NULL)
+    : m_state(GRL_NONE),
+      m_mdl_global_shared_lock(NULL),
+      m_mdl_blocks_commits_lock(NULL)
   {}
 
   bool lock_global_read_lock(THD *thd);
   void unlock_global_read_lock(THD *thd);
-  bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh,
-                                bool is_not_commit);
-  void start_waiting_global_read_lock(THD *thd);
+  bool can_acquire_protection();
+  bool has_read_lock() const { return (m_state != GRL_NONE); }
+  void init_protection_request(MDL_request *request);
+  void init_commit_request(MDL_request *request);
+  void release_protection_if_set(THD *thd);
   bool make_global_read_lock_block_commit(THD *thd);
   bool is_acquired() const { return m_state != GRL_NONE; }
-  bool has_protection() const { return m_protection_count > 0; }
   MDL_ticket *global_shared_lock() const { return m_mdl_global_shared_lock; }
 private:
-  uint           m_protection_count;            // GRL protection count
+  enum_grl_state m_state;
   /**
     In order to acquire the global read lock, the connection must
-    acquire a global shared metadata lock, to prohibit all DDL.
+    acquire shared metadata lock in GLOBAL namespace, to prohibit
+    all DDL.
   */
-  enum_grl_state m_state;
   MDL_ticket *m_mdl_global_shared_lock;
+  /**
+    Also in order to acquire the global read lock, the connection
+    must acquire a shared metadata lock in COMMIT namespace, to
+    prohibit commits.
+  */
+  MDL_ticket *m_mdl_blocks_commits_lock;
 };
 
 
@@ -3498,20 +3507,10 @@ public:
 #define CF_DIAGNOSTIC_STMT        (1U << 8)
 
 /**
-  SQL statements that must be protected against impending global read lock
-  to prevent deadlock. This deadlock could otherwise happen if the statement
-  starts waiting for the GRL to go away inside mysql_lock_tables while at the
-  same time having "old" opened tables. The thread holding the GRL can be
-  waiting for these "old" opened tables to be closed, causing a deadlock
-  (FLUSH TABLES WITH READ LOCK).
- */
-#define CF_PROTECT_AGAINST_GRL  (1U << 10)
-
-/**
   Identifies statements that may generate row events
   and that may end up in the binary log.
 */
-#define CF_CAN_GENERATE_ROW_EVENTS (1U << 11)
+#define CF_CAN_GENERATE_ROW_EVENTS (1U << 9)
 
 /* Bits in server_command_flags */
 

=== modified file 'sql/sql_handler.cc'
--- a/sql/sql_handler.cc	2010-08-12 13:50:23 +0000
+++ b/sql/sql_handler.cc	2010-10-18 12:33:49 +0000
@@ -55,7 +55,7 @@
 #include "sql_handler.h"
 #include "unireg.h"                    // REQUIRED: for other includes
 #include "sql_base.h"                           // close_thread_tables
-#include "lock.h"            // broadcast_refresh, mysql_unlock_tables
+#include "lock.h"                               // mysql_unlock_tables
 #include "key.h"                                // key_copy
 #include "sql_base.h"                           // insert_fields
 #include "sql_select.h"
@@ -131,11 +131,7 @@ static void mysql_ha_close_table(THD *th
     /* Non temporary table. */
     tables->table->file->ha_index_or_rnd_end();
     tables->table->open_by_handler= 0;
-    if (close_thread_table(thd, &tables->table))
-    {
-      /* Tell threads waiting for refresh that something has happened */
-      broadcast_refresh();
-    }
+    (void) close_thread_table(thd, &tables->table);
     thd->mdl_context.release_lock(tables->mdl_request.ticket);
   }
   else if (tables->table)

=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc	2010-10-07 10:01:51 +0000
+++ b/sql/sql_insert.cc	2010-10-18 12:33:49 +0000
@@ -529,32 +529,29 @@ void upgrade_lock_type(THD *thd, thr_loc
 static
 bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
 {
+  MDL_request protection_request;
   DBUG_ENTER("open_and_lock_for_insert_delayed");
 
 #ifndef EMBEDDED_LIBRARY
-  if (thd->locked_tables_mode && thd->global_read_lock.is_acquired())
-  {
-    /*
-      If this connection has the global read lock, the handler thread
-      will not be able to lock the table. It will wait for the global
-      read lock to go away, but this will never happen since the
-      connection thread will be stuck waiting for the handler thread
-      to open and lock the table.
-      If we are not in locked tables mode, INSERT will seek protection
-      against the global read lock (and fail), thus we will only get
-      to this point in locked tables mode.
-    */
-    my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0));
-    DBUG_RETURN(TRUE);
-  }
-
   /*
     In order for the deadlock detector to be able to find any deadlocks
-    caused by the handler thread locking this table, we take the metadata
-    lock inside the connection thread. If this goes ok, the ticket is cloned
-    and added to the list of granted locks held by the handler thread.
+    caused by the handler thread waiting for GRL or this table, we acquire
+    protection against GRL (global IX metadata lock) and metadata lock on
+    table to being inserted into inside the connection thread.
+    If this goes ok, the tickets are cloned and added to the list of granted
+    locks held by the handler thread.
+
+    TODO/FIXME: Handle release of protection against GRL.
   */
-  MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+  if (thd->global_read_lock.can_acquire_protection())
+    DBUG_RETURN(TRUE);
+
+  thd->global_read_lock.init_protection_request(&protection_request);
+
+  if (thd->mdl_context.acquire_lock(&protection_request,
+                                    thd->variables.lock_wait_timeout))
+    DBUG_RETURN(TRUE);
+
   if (thd->mdl_context.acquire_lock(&table_list->mdl_request,
                                     thd->variables.lock_wait_timeout))
     /*
@@ -589,13 +586,13 @@ bool open_and_lock_for_insert_delayed(TH
   }
 
   /*
-    If a lock was acquired above, we should release it after
-    handle_delayed_insert() has cloned the ticket. Note that acquire_lock() can
-    succeed because the connection already has the lock. In this case the ticket
-    will be before the mdl_savepoint and we should not release it here.
+    We can't release protection against GRL and metadata lock on the table
+    being inserted into here. These locks might be required, for example,
+    because this INSERT DELAYED calls functions which may try to update
+    this or another tables (updating the same table is of course illegal,
+    but such an attempt can be discovered only later during statement
+    execution).
   */
-  if (!thd->mdl_context.has_lock(mdl_savepoint, table_list->mdl_request.ticket))
-    thd->mdl_context.release_lock(table_list->mdl_request.ticket);
 
   /*
     Reset the ticket in case we end up having to use normal insert and
@@ -1873,10 +1870,11 @@ public:
   mysql_cond_t cond, cond_client;
   volatile uint tables_in_use,stacked_inserts;
   volatile bool status;
-  /*
+  /**
     When the handler thread starts, it clones a metadata lock ticket
-    for the table to be inserted. This is done to allow the deadlock
-    detector to detect deadlocks resulting from this lock.
+    which protects against GRL and ticket for the table to be inserted.
+    This is done to allow the deadlock detector to detect deadlocks
+    resulting from these locks.
     Before this is done, the connection thread cannot safely exit
     without causing problems for clone_ticket().
     Once handler_thread_initialized has been set, it is safe for the
@@ -1888,6 +1886,11 @@ public:
   I_List<delayed_row> rows;
   ulong group_count;
   TABLE_LIST table_list;			// Argument
+  /**
+    Request for IX metadata lock protecting against GRL which is
+    passed from connection thread to the handler thread.
+  */
+  MDL_request grl_protection;
 
   Delayed_insert()
     :locks_in_memory(0), table(0),tables_in_use(0),stacked_inserts(0),
@@ -2092,6 +2095,8 @@ bool delayed_get_table(THD *thd, TABLE_L
     */
     if (! (di= find_handler(thd, table_list)))
     {
+      bool not_used;
+
       if (!(di= new Delayed_insert()))
         goto end_create;
       mysql_mutex_lock(&LOCK_thread_count);
@@ -2110,7 +2115,11 @@ bool delayed_get_table(THD *thd, TABLE_L
       /* Replace volatile strings with local copies */
       di->table_list.alias= di->table_list.table_name= di->thd.query();
       di->table_list.db= di->thd.db;
-      /* We need the ticket so that it can be cloned in handle_delayed_insert */
+      /* We need the tickets so that they can be cloned in handle_delayed_insert */
+      di->grl_protection.init(MDL_key::GLOBAL, "", "",
+                              MDL_INTENTION_EXCLUSIVE);
+      di->grl_protection.ticket= thd->mdl_context.find_ticket(
+                                        &di->grl_protection, &not_used);
       init_mdl_requests(&di->table_list);
       di->table_list.mdl_request.ticket= table_list->mdl_request.ticket;
 
@@ -2650,13 +2659,15 @@ pthread_handler_t handle_delayed_insert(
     thd->set_current_stmt_binlog_format_row_if_mixed();
 
     /*
-      Clone the ticket representing the lock on the target table for
-      the insert and add it to the list of granted metadata locks held by
-      the handler thread. This is safe since the handler thread is
-      not holding nor waiting on any metadata locks.
+      Clone tickets representing protection against GRL and the lock on
+      the target table for the insert and add them to the list of granted
+      metadata locks held by the handler thread. This is safe since the
+      handler thread is not holding nor waiting on any metadata locks.
     */
-    if (thd->mdl_context.clone_ticket(&di->table_list.mdl_request))
+    if (thd->mdl_context.clone_ticket(&di->grl_protection) ||
+        thd->mdl_context.clone_ticket(&di->table_list.mdl_request))
     {
+      thd->mdl_context.release_transactional_locks();
       di->handler_thread_initialized= TRUE;
       goto err;
     }

=== modified file 'sql/sql_lex.cc'
--- a/sql/sql_lex.cc	2010-10-05 14:22:30 +0000
+++ b/sql/sql_lex.cc	2010-10-18 12:33:49 +0000
@@ -417,7 +417,6 @@ void lex_start(THD *thd)
   lex->nest_level=0 ;
   lex->allow_sum_func= 0;
   lex->in_sum_func= NULL;
-  lex->protect_against_global_read_lock= FALSE;
   /*
     ok, there must be a better solution for this, long-term
     I tried "bzero" in the sql_yacc.yy code, but that for

=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h	2010-10-06 14:34:28 +0000
+++ b/sql/sql_lex.h	2010-10-18 12:33:49 +0000
@@ -2390,22 +2390,6 @@ struct LEX: public Query_tables_list
   bool escape_used;
   bool is_lex_started; /* If lex_start() did run. For debugging. */
 
-  /*
-    Special case for SELECT .. FOR UPDATE and LOCK TABLES .. WRITE.
-
-    Protect from a impending GRL as otherwise the thread might deadlock
-    if it starts waiting for the GRL in mysql_lock_tables.
-
-    The protection is needed because there is a race between setting
-    the global read lock and waiting for all open tables to be closed.
-    The problem is a circular wait where a thread holding "old" open
-    tables will wait for the global read lock to be released while the
-    thread holding the global read lock will wait for all "old" open
-    tables to be closed -- the flush part of flush tables with read
-    lock.
-  */
-  bool protect_against_global_read_lock;
-
   LEX();
 
   virtual ~LEX()

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2010-10-07 16:01:17 +0000
+++ b/sql/sql_parse.cc	2010-10-18 12:33:49 +0000
@@ -18,12 +18,9 @@
 #include "sql_priv.h"
 #include "unireg.h"                    // REQUIRED: for other includes
 #include "sql_parse.h"        // sql_kill, *_precheck, *_prepare
-#include "lock.h"             // wait_if_global_read_lock,
-                              // unlock_global_read_lock,
-                              // try_transactional_lock,
+#include "lock.h"             // try_transactional_lock,
                               // check_transactional_lock,
                               // set_handler_table_locks,
-                              // start_waiting_global_read_lock,
                               // lock_global_read_lock,
                               // make_global_read_lock_block_commit
 #include "sql_base.h"         // find_temporary_tablesx
@@ -190,7 +187,7 @@ static bool some_non_temp_table_to_be_up
   @param mask   Bitmask used for the SQL command match.
 
 */
-static bool stmt_causes_implicit_commit(THD *thd, uint mask)
+bool stmt_causes_implicit_commit(THD *thd, uint mask)
 {
   LEX *lex= thd->lex;
   bool skip= FALSE;
@@ -260,21 +257,20 @@ void init_update_queries(void)
     the code, in particular in the Query_log_event's constructor.
   */
   sql_command_flags[SQLCOM_CREATE_TABLE]=   CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
-                                            CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL |
+                                            CF_AUTO_COMMIT_TRANS |
                                             CF_CAN_GENERATE_ROW_EVENTS;
   sql_command_flags[SQLCOM_CREATE_INDEX]=   CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_ALTER_TABLE]=    CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
-                                            CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
+                                            CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_TRUNCATE]=       CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
-                                            CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
+                                            CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_DROP_TABLE]=     CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_LOAD]=           CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
-                                            CF_PROTECT_AGAINST_GRL |
                                             CF_CAN_GENERATE_ROW_EVENTS;
-  sql_command_flags[SQLCOM_CREATE_DB]=      CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
-  sql_command_flags[SQLCOM_DROP_DB]=        CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
-  sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
-  sql_command_flags[SQLCOM_ALTER_DB]=       CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
+  sql_command_flags[SQLCOM_CREATE_DB]=      CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+  sql_command_flags[SQLCOM_DROP_DB]=        CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+  sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS;
+  sql_command_flags[SQLCOM_ALTER_DB]=       CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_RENAME_TABLE]=   CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_DROP_INDEX]=     CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_CREATE_VIEW]=    CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
@@ -282,29 +278,24 @@ void init_update_queries(void)
   sql_command_flags[SQLCOM_DROP_VIEW]=      CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_CREATE_TRIGGER]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_DROP_TRIGGER]=   CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+  // TODO/FIXME Investigate early release of metadata locks and effect on GRL.
   sql_command_flags[SQLCOM_CREATE_EVENT]=   CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+  // TODO/FIXME Investigate early release of metadata locks and effect on GRL.
   sql_command_flags[SQLCOM_ALTER_EVENT]=    CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+  // TODO/FIXME Investigate early release of metadata locks and effect on GRL.
   sql_command_flags[SQLCOM_DROP_EVENT]=     CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
-  sql_command_flags[SQLCOM_CREATE_TRIGGER]= CF_AUTO_COMMIT_TRANS;
-  sql_command_flags[SQLCOM_DROP_TRIGGER]=   CF_AUTO_COMMIT_TRANS;
 
   sql_command_flags[SQLCOM_UPDATE]=	    CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
-                                            CF_PROTECT_AGAINST_GRL |
                                             CF_CAN_GENERATE_ROW_EVENTS;
   sql_command_flags[SQLCOM_UPDATE_MULTI]=   CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
-                                            CF_PROTECT_AGAINST_GRL |
                                             CF_CAN_GENERATE_ROW_EVENTS;
   sql_command_flags[SQLCOM_INSERT]=	    CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
-                                            CF_PROTECT_AGAINST_GRL |
                                             CF_CAN_GENERATE_ROW_EVENTS;
   sql_command_flags[SQLCOM_INSERT_SELECT]=  CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
-                                            CF_PROTECT_AGAINST_GRL |
                                             CF_CAN_GENERATE_ROW_EVENTS;
   sql_command_flags[SQLCOM_DELETE]=         CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
-                                            CF_PROTECT_AGAINST_GRL |
                                             CF_CAN_GENERATE_ROW_EVENTS;
   sql_command_flags[SQLCOM_DELETE_MULTI]=   CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
-                                            CF_PROTECT_AGAINST_GRL |
                                             CF_CAN_GENERATE_ROW_EVENTS;
   sql_command_flags[SQLCOM_REPLACE]=        CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
                                             CF_CAN_GENERATE_ROW_EVENTS;
@@ -356,6 +347,7 @@ void init_update_queries(void)
   sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND;
   sql_command_flags[SQLCOM_SHOW_PROFILES]=    CF_STATUS_COMMAND;
   sql_command_flags[SQLCOM_SHOW_PROFILE]=     CF_STATUS_COMMAND;
+
   sql_command_flags[SQLCOM_BINLOG_BASE64_EVENT]= CF_STATUS_COMMAND;
 
    sql_command_flags[SQLCOM_SHOW_TABLES]=       (CF_STATUS_COMMAND |
@@ -366,20 +358,24 @@ void init_update_queries(void)
                                                 CF_REEXECUTION_FRAGILE);
 
 
-  sql_command_flags[SQLCOM_CREATE_USER]=       CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL;
-  sql_command_flags[SQLCOM_RENAME_USER]=       CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL;
-  sql_command_flags[SQLCOM_DROP_USER]=         CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL;
+  sql_command_flags[SQLCOM_CREATE_USER]=       CF_CHANGES_DATA;
+  sql_command_flags[SQLCOM_RENAME_USER]=       CF_CHANGES_DATA;
+  sql_command_flags[SQLCOM_DROP_USER]=         CF_CHANGES_DATA;
   sql_command_flags[SQLCOM_GRANT]=             CF_CHANGES_DATA;
   sql_command_flags[SQLCOM_REVOKE]=            CF_CHANGES_DATA;
-  sql_command_flags[SQLCOM_REVOKE_ALL]=        CF_PROTECT_AGAINST_GRL;
+  // QQ Looks somewhat strange but is not changed...
   sql_command_flags[SQLCOM_OPTIMIZE]=          CF_CHANGES_DATA;
   sql_command_flags[SQLCOM_CREATE_FUNCTION]=   CF_CHANGES_DATA;
-  sql_command_flags[SQLCOM_CREATE_PROCEDURE]=  CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
-  sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
-  sql_command_flags[SQLCOM_DROP_PROCEDURE]=    CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
-  sql_command_flags[SQLCOM_DROP_FUNCTION]=     CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
-  sql_command_flags[SQLCOM_ALTER_PROCEDURE]=   CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
-  sql_command_flags[SQLCOM_ALTER_FUNCTION]=    CF_CHANGES_DATA | CF_PROTECT_AGAINST_GRL | CF_AUTO_COMMIT_TRANS;
+  // TODO/FIXME Investigate and fix consequences of two statements aproach for GRL.
+  sql_command_flags[SQLCOM_CREATE_PROCEDURE]=  CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+  // TODO/FIXME Investigate and fix consequences of two statements aproach for GRL.
+  sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+  // TODO/FIXME Investigate and fix consequences of two statements aproach for GRL.
+  sql_command_flags[SQLCOM_DROP_PROCEDURE]=    CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+  // TODO/FIXME Investigate and fix consequences of two statements aproach for GRL.
+  sql_command_flags[SQLCOM_DROP_FUNCTION]=     CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+  sql_command_flags[SQLCOM_ALTER_PROCEDURE]=   CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+  sql_command_flags[SQLCOM_ALTER_FUNCTION]=    CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_INSTALL_PLUGIN]=    CF_CHANGES_DATA;
   sql_command_flags[SQLCOM_UNINSTALL_PLUGIN]=  CF_CHANGES_DATA;
 
@@ -415,7 +411,9 @@ void init_update_queries(void)
   sql_command_flags[SQLCOM_FLUSH]=              CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_RESET]=              CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_CREATE_SERVER]=      CF_AUTO_COMMIT_TRANS;
+  // TODO/FIXME Investigate effect of implicit close_cached_tables.
   sql_command_flags[SQLCOM_ALTER_SERVER]=       CF_AUTO_COMMIT_TRANS;
+  // TODO/FIXME Investigate effect of implicit close_cached_tables.
   sql_command_flags[SQLCOM_DROP_SERVER]=        CF_AUTO_COMMIT_TRANS;
 }
 
@@ -1738,16 +1736,6 @@ bool sp_process_definer(THD *thd)
 /**
   Execute command saved in thd and lex->sql_command.
 
-    Before every operation that can request a write lock for a table
-    wait if a global read lock exists. However do not wait if this
-    thread has locked tables already. No new locks can be requested
-    until the other locks are released. The thread that requests the
-    global read lock waits for write locked tables to become unlocked.
-
-    Note that wait_if_global_read_lock() sets a protection against a new
-    global read lock when it succeeds. This needs to be released by
-    start_waiting_global_read_lock() after the operation.
-
   @param thd                       Thread handle
 
   @todo
@@ -1973,17 +1961,6 @@ mysql_execute_command(THD *thd)
     thd->mdl_context.release_transactional_locks();
   }
 
-  /*
-    Check if this command needs protection against the global read lock
-    to avoid deadlock. See CF_PROTECT_AGAINST_GRL.
-    start_waiting_global_read_lock() is called at the end of
-    mysql_execute_command().
-  */
-  if (((sql_command_flags[lex->sql_command] & CF_PROTECT_AGAINST_GRL) != 0) &&
-      !thd->locked_tables_mode)
-    if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
-      goto error;
-
 #ifndef DBUG_OFF
   if (lex->sql_command != SQLCOM_SET_OPTION)
     DEBUG_SYNC(thd,"before_execute_sql_command");
@@ -2058,10 +2035,6 @@ mysql_execute_command(THD *thd)
     if (res)
       break;
 
-    if (!thd->locked_tables_mode && lex->protect_against_global_read_lock &&
-        thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
-      break;
-
     res= execute_sqlcom_select(thd, all_tables);
     break;
   }
@@ -2320,20 +2293,6 @@ case SQLCOM_PREPARE:
       create_info.default_table_charset= create_info.table_charset;
       create_info.table_charset= 0;
     }
-    /*
-      The create-select command will open and read-lock the select table
-      and then create, open and write-lock the new table. If a global
-      read lock steps in, we get a deadlock. The write lock waits for
-      the global read lock, while the global read lock waits for the
-      select table to be closed. So we wait until the global readlock is
-      gone before starting both steps. Note that
-      wait_if_global_read_lock() sets a protection against a new global
-      read lock when it succeeds. This needs to be released by
-      start_waiting_global_read_lock(). We protect the normal CREATE
-      TABLE in the same way. That way we avoid that a new table is
-      created during a global read lock.
-      Protection against grl is covered by the CF_PROTECT_AGAINST_GRL flag.
-    */
 
 #ifdef WITH_PARTITION_STORAGE_ENGINE
     {
@@ -3118,9 +3077,6 @@ end_with_restore_list:
     if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables,
                            FALSE, UINT_MAX, FALSE))
       goto error;
-    if (lex->protect_against_global_read_lock &&
-        thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
-      goto error;
 
     thd->variables.option_bits|= OPTION_TABLE_LOCK;
     thd->in_lock_tables=1;
@@ -4330,14 +4286,6 @@ error:
   res= TRUE;
 
 finish:
-  if (thd->global_read_lock.has_protection())
-  {
-    /*
-      Release the protection against the global read lock and wake
-      everyone, who might want to set a global read lock.
-    */
-    thd->global_read_lock.start_waiting_global_read_lock(thd);
-  }
 
   DBUG_ASSERT(!thd->in_active_multi_stmt_transaction() ||
                thd->in_multi_stmt_transaction_mode());
@@ -4373,6 +4321,13 @@ finish:
   close_thread_tables(thd);
   thd_proc_info(thd, 0);
 
+#ifndef DBUG_OFF
+  if (lex->sql_command != SQLCOM_SET_OPTION && ! thd->in_sub_stmt)
+    DEBUG_SYNC(thd, "execute_command_after_close_tables");
+#endif
+
+  DBUG_EVALUATE_IF("execute_command_crash", (abort(), 0), 0);
+
   if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
   {
     /* No transaction control allowed in sub-statements. */
@@ -4398,6 +4353,10 @@ finish:
     */
     thd->mdl_context.release_transactional_locks();
   }
+  else if (! thd->in_sub_stmt && !thd->locked_tables_mode)
+  {
+    thd->global_read_lock.release_protection_if_set(thd);
+  }
 
   DBUG_RETURN(res || thd->is_error());
 }

=== modified file 'sql/sql_parse.h'
--- a/sql/sql_parse.h	2010-08-31 09:59:51 +0000
+++ b/sql/sql_parse.h	2010-10-18 12:33:49 +0000
@@ -78,6 +78,7 @@ bool check_host_name(LEX_STRING *str);
 bool check_identifier_name(LEX_STRING *str, uint max_char_length,
                            uint err_code, const char *param_for_err_msg);
 bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length);
+bool stmt_causes_implicit_commit(THD *thd, uint mask);
 bool sqlcom_can_generate_row_events(const THD *thd);
 bool is_update_query(enum enum_sql_command command);
 bool is_log_table_write_query(enum enum_sql_command command);

=== modified file 'sql/sql_rename.cc'
--- a/sql/sql_rename.cc	2010-09-16 09:11:13 +0000
+++ b/sql/sql_rename.cc	2010-10-18 12:33:49 +0000
@@ -24,8 +24,7 @@
 #include "sql_table.h"                         // build_table_filename
 #include "sql_view.h"             // mysql_frm_type, mysql_rename_view
 #include "sql_trigger.h"
-#include "lock.h"       // wait_if_global_read_lock
-                        // start_waiting_global_read_lock
+#include "lock.h"       // MYSQL_OPEN_SKIP_TEMPORARY
 #include "sql_base.h"   // tdc_remove_table, lock_table_names,
 #include "sql_handler.h"                        // mysql_ha_rm_tables
 #include "datadict.h"
@@ -63,9 +62,6 @@ bool mysql_rename_tables(THD *thd, TABLE
 
   mysql_ha_rm_tables(thd, table_list);
 
-  if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
-    DBUG_RETURN(1);
-
   if (logger.is_log_table_enabled(QUERY_LOG_GENERAL) ||
       logger.is_log_table_enabled(QUERY_LOG_SLOW))
   {
@@ -189,7 +185,6 @@ bool mysql_rename_tables(THD *thd, TABLE
     query_cache_invalidate3(thd, table_list, 0);
 
 err:
-  thd->global_read_lock.start_waiting_global_read_lock(thd);
   DBUG_RETURN(error || binlog_error);
 }
 

=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc	2010-10-01 13:59:07 +0000
+++ b/sql/sql_table.cc	2010-10-18 12:33:49 +0000
@@ -23,9 +23,7 @@
 #include "sql_parse.h"                        // test_if_data_home_dir
 #include "sql_cache.h"                          // query_cache_*
 #include "sql_base.h"   // open_table_uncached, lock_table_names
-#include "lock.h"       // wait_if_global_read_lock
-                        // start_waiting_global_read_lock,
-                        // mysql_unlock_tables
+#include "lock.h"       // mysql_unlock_tables
 #include "strfunc.h"    // find_type2, find_set
 #include "sql_view.h" // view_checksum 
 #include "sql_truncate.h"                       // regenerate_locked_table 
@@ -1855,21 +1853,10 @@ bool mysql_rm_table(THD *thd,TABLE_LIST 
   DBUG_ENTER("mysql_rm_table");
 
   /* mark for close and remove all cached entries */
-
-  if (!drop_temporary)
-  {
-    if (!thd->locked_tables_mode &&
-       thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
-      DBUG_RETURN(TRUE);
-  }
-
   thd->push_internal_handler(&err_handler);
   error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
   thd->pop_internal_handler();
 
-  if (thd->global_read_lock.has_protection())
-    thd->global_read_lock.start_waiting_global_read_lock(thd);
-
   if (error)
     DBUG_RETURN(TRUE);
   my_ok(thd);

=== modified file 'sql/sql_trigger.cc'
--- a/sql/sql_trigger.cc	2010-09-30 10:43:43 +0000
+++ b/sql/sql_trigger.cc	2010-10-18 12:33:49 +0000
@@ -24,8 +24,6 @@
 #include "parse_file.h"
 #include "sp.h"
 #include "sql_base.h"                          // find_temporary_table
-#include "lock.h"                    // wait_if_global_read_lock,
-                                     // start_waiting_global_read_lock
 #include "sql_show.h"                // append_definer, append_identifier
 #include "sql_table.h"                        // build_table_filename,
                                               // check_n_cut_mysql50_prefix
@@ -391,15 +389,6 @@ bool mysql_create_or_drop_trigger(THD *t
     DBUG_RETURN(TRUE);
   }
 
-  /*
-    We don't want perform our operations while global read lock is held
-    so we have to wait until its end and then prevent it from occurring
-    again until we are done, unless we are under lock tables.
-  */
-  if (!thd->locked_tables_mode &&
-      thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
-    DBUG_RETURN(TRUE);
-
   if (!create)
   {
     bool if_exists= thd->lex->drop_if_exists;
@@ -547,9 +536,6 @@ end:
   if (!create)
     thd->lex->restore_backup_query_tables_list(&backup);
 
-  if (thd->global_read_lock.has_protection())
-    thd->global_read_lock.start_waiting_global_read_lock(thd);
-
   if (!result)
     my_ok(thd);
 

=== modified file 'sql/sql_view.cc'
--- a/sql/sql_view.cc	2010-08-18 11:55:37 +0000
+++ b/sql/sql_view.cc	2010-10-18 12:33:49 +0000
@@ -22,7 +22,7 @@
 #include "sql_base.h"    // find_table_in_global_list, lock_table_names
 #include "sql_parse.h"                          // sql_parse
 #include "sql_cache.h"                          // query_cache_*
-#include "lock.h"        // wait_if_global_read_lock
+#include "lock.h"        // MYSQL_OPEN_SKIP_TEMPORARY 
 #include "sql_show.h"    // append_identifier
 #include "sql_table.h"                         // build_table_filename
 #include "sql_db.h"            // mysql_opt_change_db, mysql_change_db
@@ -649,13 +649,6 @@ bool mysql_create_view(THD *thd, TABLE_L
   }
 #endif
 
-
-  if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
-  {
-    res= TRUE;
-    goto err;
-  }
-
   res= mysql_register_view(thd, view, mode);
 
   if (mysql_bin_log.is_open())
@@ -704,7 +697,6 @@ bool mysql_create_view(THD *thd, TABLE_L
 
   if (mode != VIEW_CREATE_NEW)
     query_cache_invalidate3(thd, view, 0);
-  thd->global_read_lock.start_waiting_global_read_lock(thd);
   if (res)
     goto err;
 

=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy	2010-10-07 23:34:59 +0000
+++ b/sql/sql_yacc.yy	2010-10-18 12:33:49 +0000
@@ -7385,7 +7385,6 @@ select_lock_type:
             LEX *lex=Lex;
             lex->current_select->set_lock_for_tables(TL_WRITE);
             lex->safe_to_cache_query=0;
-            lex->protect_against_global_read_lock= TRUE;
           }
         | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM
           {
@@ -13172,9 +13171,6 @@ table_lock:
                                             MDL_SHARED_NO_READ_WRITE :
                                             MDL_SHARED_READ)))
               MYSQL_YYABORT;
-            /* If table is to be write locked, protect from a impending GRL. */
-            if (lock_for_write)
-              Lex->protect_against_global_read_lock= TRUE;
           }
         ;
 

=== modified file 'sql/transaction.cc'
--- a/sql/transaction.cc	2010-09-13 11:31:22 +0000
+++ b/sql/transaction.cc	2010-10-18 12:33:49 +0000
@@ -21,6 +21,7 @@
 #include "sql_priv.h"
 #include "transaction.h"
 #include "rpl_handler.h"
+#include "debug_sync.h"         // DEBUG_SYNC
 
 /* Conditions under which the transaction state must not change. */
 static bool trans_check(THD *thd)
@@ -645,17 +646,35 @@ bool trans_xa_commit(THD *thd)
   }
   else if (xa_state == XA_PREPARED && thd->lex->xa_opt == XA_NONE)
   {
-    if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, FALSE))
+    MDL_request mdl_request;
+
+    /*
+      Acquire metadata lock which will ensure that COMMIT is blocked
+      by active FLUSH TABLES WITH READ LOCK (and vice versa COMMIT in
+      progress blocks FTWRL).
+
+      We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
+      This allowance is needed to not break existing versions of innobackup
+      which do a BEGIN; INSERT; FLUSH TABLES WITH READ LOCK; COMMIT.
+    */
+    thd->global_read_lock.init_commit_request(&mdl_request);
+
+    if (! thd->global_read_lock.has_read_lock() &&
+        thd->mdl_context.acquire_lock(&mdl_request,
+                                      thd->variables.lock_wait_timeout))
     {
       ha_rollback_trans(thd, TRUE);
       my_error(ER_XAER_RMERR, MYF(0));
     }
     else
     {
+      DEBUG_SYNC(thd, "trans_xa_commit_after_acquire_commit_lock");
+
       res= test(ha_commit_one_phase(thd, 1));
       if (res)
         my_error(ER_XAER_RMERR, MYF(0));
-      thd->global_read_lock.start_waiting_global_read_lock(thd);
+      if (mdl_request.ticket)
+        thd->mdl_context.release_lock(mdl_request.ticket);
     }
   }
   else

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-5.5-runtime branch (Dmitry.Lenev:3166 to 3167) Dmitry Lenev18 Oct