List:Commits« Previous MessageNext Message »
From:Dmitry Lenev Date:November 11 2010 5:08pm
Subject:bzr commit into mysql-5.5-runtime branch (Dmitry.Lenev:3179) Bug#54673
Bug#57006
View as plain text  
#At file:///home/dlenev/src/bzr/mysql-5.5-rt-grl-2/ based on revid:jon.hauglid@stripped

 3179 Dmitry Lenev	2010-11-11
      Patch that refactors global read lock implementation and fixes
      bug #57006 "Deadlock between HANDLER and FLUSH TABLES WITH READ
      LOCK" and bug #54673 "It takes too long to get readlock for
      'FLUSH TABLES WITH READ LOCK'".
      
      The first bug manifested itself as a deadlock which occurred
      when a connection, which had some table open through HANDLER
      statement, tried to update some data through DML statement
      while another connection tried to execute FLUSH TABLES WITH
      READ LOCK concurrently.
      
      What happened was that FTWRL in the second connection managed
      to perform first step of GRL acquisition and thus blocked all
      upcoming DML. After that it started to wait for table open
      through HANDLER statement to be flushed. When the first connection
      tried to execute DML it has started to wait for GRL/the second
      connection creating deadlock.
      
      The second bug manifested itself as starvation of FLUSH TABLES
      WITH READ LOCK statements in cases when there was a constant
      stream of concurrent DML statements (in two or more
      connections).
      
      This has happened because requests for protection against GRL
      which were acquired by DML statements were ignoring presence of
      pending GRL and thus the latter was starved.
      
      This patch solves both these problems by re-implementing GRL
      using metadata locks.
      
      Similar to the old implementation acquisition of GRL in new
      implementation is two-step. During the first step we block
      all concurrent DML and DDL statements by acquiring global S
      metadata lock (each DML and DDL statement acquires global IX
      lock for its duration). During the second step we block commits
      by acquiring global S lock in COMMIT namespace (commit code
      acquires global IX lock in this namespace).
      
      Note that unlike in old implementation acquisition of
      protection against GRL in DML and DDL is semi-automatic.
      We assume that any statement which should be blocked by GRL
      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.
      
      The first problem is solved because waits for GRL become
      visible to deadlock detector in metadata locking subsystem
      and thus deadlocks like one in the first bug become impossible.
      
      The second problem is solved because global S locks which
      are used for GRL implementation are given preference over
      IX locks which are acquired by concurrent DML (and we can
      switch to fair scheduling in future if needed).
      
      Important change:
      FTWRL/GRL no longer blocks DML and DDL on temporary tables.
      Before this patch behavior was not consistent in this respect:
      in some cases DML/DDL statements on temporary tables were
      blocked while in others they were not. Since the main use cases
      for FTWRL are various forms of backups and temporary tables are
      not preserved during backups we have opted for consistently
      allowing DML/DDL on temporary tables during FTWRL/GRL.
      
      Important change:
      This patch changes thread state names which are used when
      DML/DDL of FTWRL is waiting for global read lock. It is now
      either "Waiting for global read lock" or "Waiting for commit
      lock" depending on the stage on which FTWRL is.
      
      Incompatible change:
      To solve deadlock in events code which was exposed by this
      patch we have to replace LOCK_event_metadata mutex with
      metadata locks on events. As result we have to prohibit
      DDL on events under LOCK TABLES.
      
      This patch also adds extensive test coverage for interaction
      of DML/DDL and FTWRL.
      
      Performance of new and old global read lock implementations
      in sysbench tests were compared. There were no significant
      difference between new and old implementations.
     @ mysql-test/include/check_ftwrl_compatible.inc
        Added helper script which allows to check that a statement is
        compatible with FLUSH TABLES WITH READ LOCK.
     @ mysql-test/include/check_ftwrl_incompatible.inc
        Added helper script which allows to check that a statement is
        incompatible with FLUSH TABLES WITH READ LOCK.
     @ mysql-test/include/handler.inc
        Adjusted test case to the fact that now DROP TABLE closes
        open HANDLERs for the table to be dropped before checking
        if there active FTWRL in this connection.
     @ mysql-test/include/wait_show_condition.inc
        Fixed small error in the timeout message. The correct name
        of variable used as parameter for this script is "$condition"
        and not "$wait_condition".
     @ mysql-test/r/delayed.result
        Added test coverage for scenario which triggered assert in
        metadata locking subsystem.
     @ mysql-test/r/events_2.result
        Updated test results after prohibiting event DDL operations
        under LOCK TABLES.
     @ mysql-test/r/flush.result
        Added test coverage for bug #57006 "Deadlock between HANDLER
        and FLUSH TABLES WITH READ LOCK".
     @ mysql-test/r/flush_read_lock.result
        Added test coverage for various aspects of FLUSH TABLES WITH
        READ LOCK functionality.
     @ mysql-test/r/flush_read_lock_kill.result
        Adjusted test case after replacing custom global read lock
        implementation with one based on metadata locks. Use new
        debug_sync point. Do not disable concurrent inserts as now
        InnoDB we always use InnoDB table.
     @ mysql-test/r/handler_innodb.result
        Adjusted test case to the fact that now DROP TABLE closes
        open HANDLERs for the table to be dropped before checking
        if there active FTWRL in this connection.
     @ mysql-test/r/handler_myisam.result
        Adjusted test case to the fact that now DROP TABLE closes
        open HANDLERs for the table to be dropped before checking
        if there active FTWRL in this connection.
     @ mysql-test/r/mdl_sync.result
        Adjusted test case after replacing custom global read lock
        implementation with one based on metadata locks. Replaced
        usage of GRL-specific debug_sync's with appropriate sync
        points in MDL subsystem.
     @ mysql-test/suite/perfschema/r/dml_setup_instruments.result
        Updated test results after removing global
        COND_global_read_lock condition variable.
     @ mysql-test/suite/perfschema/r/func_file_io.result
        Ensure that this test doesn't affect subsequent tests.
        At the end of its execution enable back P_S instrumentation
        which this test disables at some point.
     @ mysql-test/suite/perfschema/r/func_mutex.result
        Ensure that this test doesn't affect subsequent tests.
        At the end of its execution enable back P_S instrumentation
        which this test disables at some point.
     @ mysql-test/suite/perfschema/r/global_read_lock.result
        Adjusted test case to take into account that new GRL
        implementation is based on MDL.
     @ mysql-test/suite/perfschema/r/server_init.result
        Adjusted test case after replacing custom global read
        lock implementation with one based on MDL and replacing
        LOCK_event_metadata mutex with metadata lock.
     @ mysql-test/suite/perfschema/t/func_file_io.test
        Ensure that this test doesn't affect subsequent tests.
        At the end of its execution enable back P_S instrumentation
        which this test disables at some point.
     @ mysql-test/suite/perfschema/t/func_mutex.test
        Ensure that this test doesn't affect subsequent tests.
        At the end of its execution enable back P_S instrumentation
        which this test disables at some point.
     @ mysql-test/suite/perfschema/t/global_read_lock.test
        Adjusted test case to take into account that new GRL
        implementation is based on MDL.
     @ mysql-test/suite/perfschema/t/server_init.test
        Adjusted test case after replacing custom global read
        lock implementation with one based on MDL and replacing
        LOCK_event_metadata mutex with metadata lock.
     @ mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result
        Updated test results after prohibiting event DDL under
        LOCK TABLES.
     @ mysql-test/t/delayed.test
        Added test coverage for scenario which triggered assert in
        metadata locking subsystem.
     @ mysql-test/t/events_2.test
        Updated test case after prohibiting event DDL operations
        under LOCK TABLES.
     @ mysql-test/t/flush.test
        Added test coverage for bug #57006 "Deadlock between HANDLER
        and FLUSH TABLES WITH READ LOCK".
     @ mysql-test/t/flush_block_commit.test
        Adjusted test case after changing thread state name which
        is used when COMMIT waits for FLUSH TABLES WITH READ LOCK
        from "Waiting for release of readlock" to "Waiting for commit
        lock".
     @ mysql-test/t/flush_block_commit_notembedded.test
        Adjusted test case after changing thread state name which is
        used when DML waits for FLUSH TABLES WITH READ LOCK. Now we
        use "Waiting for global read lock" in this case.
     @ mysql-test/t/flush_read_lock.test
        Added test coverage for various aspects of FLUSH TABLES WITH
        READ LOCK functionality.
     @ mysql-test/t/flush_read_lock_kill-master.opt
        We no longer need to use make_global_read_lock_block_commit_loop
        debug tag in this test. Instead we rely on an appropriate
        debug_sync point in MDL code.
     @ mysql-test/t/flush_read_lock_kill.test
        Adjusted test case after replacing custom global read lock
        implementation with one based on metadata locks. Use new
        debug_sync point. Do not disable concurrent inserts as now
        InnoDB we always use InnoDB table.
     @ mysql-test/t/lock_multi.test
        Adjusted test case after changing thread state names which
        are used when DML or DDL waits for FLUSH TABLES WITH READ
        LOCK to "Waiting for global read lock".
     @ mysql-test/t/mdl_sync.test
        Adjusted test case after replacing custom global read lock
        implementation with one based on metadata locks. Replaced
        usage of GRL-specific debug_sync's with appropriate sync
        points in MDL subsystem. Updated thread state names which
        are used when DDL waits for FTWRL.
     @ mysql-test/t/trigger_notembedded.test
        Adjusted test case after changing thread state names which
        are used when DML or DDL waits for FLUSH TABLES WITH READ
        LOCK to "Waiting for global read lock".
     @ sql/event_data_objects.cc
        Removed Event_queue_element::status/last_executed_changed
        members and Event_queue_element::update_timing_fields()
        method. We no longer use this class for updating mysql.events
        once event is chosen for execution. Accesses to instances of
        this class in scheduler thread require protection by
        Event_queue::LOCK_event_queue mutex and we try to avoid
        updating table while holding this lock.
     @ sql/event_data_objects.h
        Removed Event_queue_element::status/last_executed_changed
        members and Event_queue_element::update_timing_fields()
        method. We no longer use this class for updating mysql.events
        once event is chosen for execution. Accesses to instances of
        this class in scheduler thread require protection by
        Event_queue::LOCK_event_queue mutex and we try to avoid
        updating table while holding this lock.
     @ sql/event_db_repository.cc
        - Changed Event_db_repository methods to not release all
          metadata locks once they are done updating mysql.events
          table. This allows to keep metadata lock protecting
          against GRL and lock protecting particular event around
          until corresponding DDL statement is written to the binary
          log.
        - Removed logic for conditional update of "status" and
          "last_executed" fields from update_timing_fields_for_event()
          method. In the only case when this method is called now
          "last_executed" is always modified and tracking change
          of "status" is too much hassle.
     @ sql/event_db_repository.h
        Removed logic for conditional update of "status" and
        "last_executed" fields from Event_db_repository::
        update_timing_fields_for_event() method.
        In the only case when this method is called now "last_executed"
        is always modified and tracking change of "status" field is
        too much hassle.
     @ sql/event_queue.cc
        Changed event scheduler code not to update mysql.events
        table while holding Event_queue::LOCK_event_queue mutex.
        Doing so led to a deadlock with a new GRL implementation.
        This deadlock didn't occur with old implementation due to
        fact that code acquiring protection against GRL ignored
        pending GRL requests (which lead to GRL starvation).
        One of goals of new implementation is to disallow GRL
        starvation and so we have to solve problem with this
        deadlock in a different way.
     @ sql/events.cc
        Changed methods of Events class to acquire protection
        against GRL while perfoming DDL statement and keep it
        until statement is written to the binary log.
        Unfortunately this step together with new GRL implementation
        exposed deadlock involving Events::LOCK_event_metadata
        and GRL. To solve it Events::LOCK_event_metadata mutex was
        replaced with a metadata lock on event. As a side-effect
        events DDL has to be prohibited under LOCK TABLES even in
        cases when mysql.events table was explicitly locked for
        write.
     @ sql/events.h
        Replaced Events::LOCK_event_metadata mutex with a metadata
        lock on event.
     @ sql/ha_ndbcluster.cc
        Updated code after replacing custom global read lock
        implementation with one based on MDL. Since MDL subsystem
        should now be able to detect deadlocks involving metadata
        locks and GRL there is no need for special handling of
        active GRL.
     @ sql/handler.cc
        Replaced custom implementation of global read lock with
        one based on metadata locks. Consequently when doing
        commit instead of calling method of Global_read_lock
        class to acquire protection against GRL we simply acquire
        IX in COMMIT namespace.
     @ sql/lock.cc
        Replaced custom implementation of global read lock with
        one based on metadata locks. This step allows to expose
        wait for GRL to deadlock detector of MDL subsystem and
        thus succesfully resolve deadlocks similar to one behind
        bug #57006 "Deadlock between HANDLER and FLUSH TABLES
        WITH READ LOCK". It also solves problem with GRL starvation
        described in bug #54673 "It takes too long to get readlock
        for 'FLUSH TABLES WITH READ LOCK'" since metadata locks used
        by GRL give preference to FTWRL statement instead of DML
        statements (if needed in future this can be changed to
        fair scheduling).
        
        Similar to old implementation of acquisition of GRL is
        two-step. During the first step we block all concurrent
        DML and DDL statements by acquiring global S metadata lock
        (each DML and DDL statement acquires global IX lock for
        its duration). During the second step we block commits by
        acquiring global S lock in COMMIT namespace (commit code
        acquires global IX lock in this namespace).
        
        Note that unlike in old implementation acquisition of
        protection against GRL in DML and DDL is semi-automatic.
        We assume that any statement which should be blocked by GRL
        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.
        
        To support this change:
        - Global_read_lock::lock/unlock_global_read_lock and
          make_global_read_lock_block_commit methods were changed
          accordingly.
        - Global_read_lock::wait_if_global_read_lock() and
          start_waiting_global_read_lock() methods were dropped.
          It is now responsibility of code acquiring metadata locks
          opening tables to acquire protection against GRL by
          explicitly taking global IX lock with statement duration.
        - Global variables, mutex and condition variable used by
          old implementation was removed.
        - lock_routine_name() was changed to use statement duration for
          its global IX lock. It was also renamed to lock_object_name()
          as it now also used to take metadata locks on events.
        - Global_read_lock::set_explicit_lock_duration() was added which
          allows not to release locks used for GRL when leaving prelocked
          mode.
     @ sql/lock.h
        - Renamed lock_routine_name() to lock_object_name() and changed
          its signature to allow its usage for events.
        - Removed broadcast_refresh() function. It is no longer needed
          with new GRL implementation.
     @ sql/log_event.cc
        Release metadata locks with statement duration at the end
        of processing legacy event for LOAD DATA. This ensures that
        replication thread processing such event properly releases
        its protection against global read lock.
     @ sql/mdl.cc
        Changed MDL subsystem to support new MDL-based implementation
        of global read lock.
        
        Added COMMIT and EVENTS namespaces for metadata locks. Changed
        thread state name for GLOBAL namespace to "Waiting for global
        read lock".
        
        Optimized MDL_map::find_or_insert() method to avoid taking
        m_mutex mutex when looking up MDL_lock objects for GLOBAL
        or COMMIT namespaces. We keep pre-created MDL_lock objects
        for these namespaces around and simply return pointers to
        these global objects when needed.
        
        Changed MDL_lock/MDL_scoped_lock to properly handle
        notification of insert delayed handler threads when FTWRL
        takes global S lock.
        
        Introduced concept of lock duration. In addition to locks with
        transaction duration which work in the way which is similar to
        how locks worked before (i.e. they are released at the end of
        transaction), locks with statement and explicit duration were
        introduced.
        Locks with statement duration are automatically released at the
        end of statement. Locks with explicit duration require explicit
        release and obsolete concept of transactional sentinel.
        
        * Changed MDL_request and MDL_ticket classes to support notion
          of duration.
        * Changed MDL_context to keep locks with different duration in
          different lists. Changed code handling ticket list to take
          this into account.
        * Changed methods responsible for releasing locks to take into
          account duration of tickets. Particularly public
          MDL_context::release_lock() method now only can release
          tickets with explicit duration (there is still internal
          method which allows to specify duration). To release locks
          with statement or transaction duration one have to use
          release_statement/transactional_locks() methods.
        * Concept of savepoint for MDL subsystem now has to take into
          account locks with statement duration. Consequently
          MDL_savepoint class was introduced and methods working with
          savepoints were updated accordingly.
        * Added methods which allow to set duration for one or all
          locks in the context.
     @ sql/mdl.h
        Changed MDL subsystem to support new MDL-based implementation
        of global read lock.
        
        Added COMMIT and EVENTS namespaces for metadata locks.
        
        Introduced concept of lock duration. In addition to locks with
        transaction duration which work in the way which is similar to
        how locks worked before (i.e. they are released at the end of
        transaction), locks with statement and explicit duration were
        introduced.
        Locks with statement duration are automatically released at the
        end of statement. Locks with explicit duration require explicit
        release and obsolete concept of transactional sentinel.
        
        * Changed MDL_request and MDL_ticket classes to support notion
          of duration.
        * Changed MDL_context to keep locks with different duration in
          different lists. Changed code handling ticket list to take
          this into account.
        * Changed methods responsible for releasing locks to take into
          account duration of tickets. Particularly public
          MDL_context::release_lock() method now only can release
          tickets with explicit duration (there is still internal
          method which allows to specify duration). To release locks
          with statement or transaction duration one have to use
          release_statement/transactional_locks() methods.
        * Concept of savepoint for MDL subsystem now has to take into
          account locks with statement duration. Consequently
          MDL_savepoint class was introduced and methods working with
          savepoints were updated accordingly.
        * Added methods which allow to set duration for one or all
          locks in the context.
     @ sql/mysqld.cc
        Removed global mutex and condition variables which were used
        by old implementation of GRL.
        Also we no longer need to initialize Events::LOCK_event_metadata
        mutex as it was replaced with metadata locks on events.
     @ sql/mysqld.h
        Removed global variable, mutex and condition variables which
        were used by old implementation of GRL.
     @ sql/rpl_rli.cc
        When slave thread closes tables which were open for handling
        of RBR events ensure that it releases global IX lock which
        was acquired as protection against GRL.
     @ sql/sp.cc
        Adjusted code to the new signature of lock_object/routine_name(),
        to the fact that one now needs specify duration of lock when
        initializing MDL_request and to the fact that savepoints for MDL
        subsystem are now represented by MDL_savepoint class.
     @ sql/sp_head.cc
        Ensure that statements in stored procedures release statement
        metadata locks and thus release their protectiong against GRL
        in proper moment in time.
        Adjusted code to the fact that one now needs specify duration
        of lock when initializing MDL_request.
     @ sql/sql_admin.cc
        Adjusted code to the fact that one now needs specify duration
        of lock when initializing MDL_request.
     @ sql/sql_base.cc
        - Implemented support for new approach to acquiring protection
          against global read lock. We no longer acquire such protection
          explicitly on the basis of statement flags. Instead we always
          rely on code which is responsible for acquiring metadata locks
          on object to be changed acquiring this protection. This is
          achieved by acquiring global IX metadata lock with statement
          duration. Code doing this also responsible for checking that
          current connection has no active GRL by calling an
          Global_read_lock::can_acquire_protection() method.
          Changed code in open_table() and lock_table_names()
          accordingly.
          Note that as result of this change DDL and DML on temporary
          tables is always compatible with GRL (before it was
          incompatible in some cases and compatible in other cases).
        - To speed-up code acquiring protection against GRL introduced
          m_has_protection_against_grl member in Open_table_context
          class. It indicates that protection was already acquired
          sometime during open_tables() execution and new attempts
          can be skipped.
        - Thanks to new GRL implementation calls to broadcast_refresh()
          became unnecessary and were removed.
        - Adjusted code to the fact that one now needs specify duration
          of lock when initializing MDL_request and to the fact that
          savepoints for MDL subsystem are now represented by
          MDL_savepoint class.
     @ sql/sql_base.h
        Adjusted code to the fact that savepoints for MDL subsystem are
        now represented by MDL_savepoint class.
        Also introduced Open_table_context::m_has_protection_against_grl
        member which allows to avoid acquiring protection against GRL
        while opening tables if such protection was already acquired.
     @ sql/sql_class.cc
        Changed THD::leave_locked_tables_mode() after transactional
        sentinel for metadata locks was obsoleted by introduction of
        locks with explicit duration.
     @ sql/sql_class.h
        - Adjusted code to the fact that savepoints for MDL subsystem
          are now represented by MDL_savepoint class.
        - Changed Global_read_lock class according to changes in
          global read lock implementation:
          * wait_if_global_read_lock and start_waiting_global_read_lock
            are now gone. Instead code needing protection against GRL
            has to acquire global IX metadata lock with statement
            duration itself. To help it new can_acquire_protection()
            was introduced. Also as result of the above change
            m_protection_count member is gone too.
          * Added m_mdl_blocks_commits_lock member to store metadata
            lock blocking commits.
          * Adjusted code to the fact that concept of transactional
            sentinel was obsoleted by concept of lock duration.
        - Removed CF_PROTECT_AGAINST_GRL flag as it is no longer
          necessary. New GRL implementation acquires protection
          against global read lock automagically when statement
          acquires metadata locks on tables or other objects it
          is going to change.
     @ sql/sql_db.cc
        Adjusted code to the fact that one now needs specify duration
        of lock when initializing MDL_request.
     @ sql/sql_handler.cc
        Removed call to broadcast_refresh() function. It is no longer
        needed with new GRL implementation.
        Adjusted code after introducing duration concept for metadata
        locks. Particularly to the fact transactional sentinel was
        replaced with explicit duration.
     @ sql/sql_handler.h
        Renamed mysql_ha_move_tickets_after_trans_sentinel() to
        mysql_ha_set_explicit_lock_duration() after transactional
        sentinel was obsoleted by locks with explicit duration.
     @ sql/sql_insert.cc
        Adjusted code handling delaying inserts after switching to
        new GRL implementation. Now connection thread initiating
        delayed insert has to acquire global IX lock in addition
        to metadata lock on table being inserted into. This IX lock
        protects against GRL and similarly to SW lock on table being
        inserted into has to be passed to handler thread in order to
        avoid deadlocks.
     @ sql/sql_lex.cc
        LEX::protect_against_global_read_lock member is no longer
        necessary since protection against GRL is automatically
        taken by code acquiring metadata locks/opening tables.
     @ sql/sql_lex.h
        LEX::protect_against_global_read_lock member is no longer
        necessary since protection against GRL is automatically
        taken by code acquiring metadata locks/opening tables.
     @ sql/sql_parse.cc
        - Implemented support for new approach to acquiring protection
          against global read lock. We no longer acquire such protection
          explicitly on the basis of statement flags. Instead we always
          rely on code which is responsible for acquiring metadata locks
          on object to be changed acquiring this protection. This is
          achieved by acquiring global IX metadata lock with statement
          duration. This lock is automatically released at the end of
          statement execution.
        - Changed implementation of CREATE/DROP PROCEDURE/FUNCTION not
          to release metadata locks and thus protection against of GRL
          in the middle of statement execution.
        - Adjusted code to the fact that one now needs specify duration
          of lock when initializing MDL_request and to the fact that
          savepoints for MDL subsystem are now represented by
          MDL_savepoint class.
     @ sql/sql_prepare.cc
        Adjusted code to the to the fact that savepoints for MDL
        subsystem are now represented by MDL_savepoint class.
     @ sql/sql_rename.cc
        With new GRL implementation there is no need to explicitly
        acquire protection against GRL before renaming tables.
        This happens automatically in code which acquires metadata
        locks on tables being renamed.
     @ sql/sql_show.cc
        Adjusted code to the fact that one now needs specify duration
        of lock when initializing MDL_request and to the fact that
        savepoints for MDL subsystem are now represented by
        MDL_savepoint class.
     @ sql/sql_table.cc
        - With new GRL implementation there is no need to explicitly
          acquire protection against GRL before dropping tables.
          This happens automatically in code which acquires metadata
          locks on tables being dropped.
        - Changed mysql_alter_table() not to release lock on new table
          name explicitly and to rely on automatic release of locks
          at the end of statement instead. This was necessary since
          now MDL_context::release_lock() is supported only for locks
          for explicit duration.
     @ sql/sql_trigger.cc
        With new GRL implementation there is no need to explicitly
        acquire protection against GRL before changing table triggers.
        This happens automatically in code which acquires metadata
        locks on tables which triggers are to be changed.
     @ sql/sql_update.cc
        Fix bug exposed by GRL testing. During prepare phase acquire
        only S metadata locks instead of SW locks to keep prepare of
        multi-UPDATE compatible with concurrent LOCK TABLES WRITE
        and global read lock.
     @ sql/sql_view.cc
        With new GRL implementation there is no need to explicitly
        acquire protection against GRL before creating view.
        This happens automatically in code which acquires metadata
        lock on view to be created.
     @ sql/sql_yacc.yy
        LEX::protect_against_global_read_lock member is no longer
        necessary since protection against GRL is automatically
        taken by code acquiring metadata locks/opening tables.
     @ sql/table.cc
        Adjusted code to the fact that one now needs specify duration
        of lock when initializing MDL_request.
     @ sql/table.h
        Adjusted code to the fact that one now needs specify duration
        of lock when initializing MDL_request.
     @ sql/transaction.cc
        Replaced custom implementation of global read lock with
        one based on metadata locks. Consequently when doing
        commit instead of calling method of Global_read_lock
        class to acquire protection against GRL we simply acquire
        IX in COMMIT namespace.
        Also adjusted code to the fact that MDL savepoint is now
        represented by MDL_savepoint class.

    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/delayed.result
      mysql-test/r/events_2.result
      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/func_file_io.result
      mysql-test/suite/perfschema/r/func_mutex.result
      mysql-test/suite/perfschema/r/global_read_lock.result
      mysql-test/suite/perfschema/r/server_init.result
      mysql-test/suite/perfschema/t/func_file_io.test
      mysql-test/suite/perfschema/t/func_mutex.test
      mysql-test/suite/perfschema/t/global_read_lock.test
      mysql-test/suite/perfschema/t/server_init.test
      mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result
      mysql-test/t/delayed.test
      mysql-test/t/events_2.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/event_data_objects.cc
      sql/event_data_objects.h
      sql/event_db_repository.cc
      sql/event_db_repository.h
      sql/event_queue.cc
      sql/events.cc
      sql/events.h
      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.cc
      sql/sp_head.cc
      sql/sql_admin.cc
      sql/sql_base.cc
      sql/sql_base.h
      sql/sql_class.cc
      sql/sql_class.h
      sql/sql_db.cc
      sql/sql_handler.cc
      sql/sql_handler.h
      sql/sql_insert.cc
      sql/sql_lex.cc
      sql/sql_lex.h
      sql/sql_parse.cc
      sql/sql_prepare.cc
      sql/sql_rename.cc
      sql/sql_show.cc
      sql/sql_table.cc
      sql/sql_trigger.cc
      sql/sql_update.cc
      sql/sql_view.cc
      sql/sql_yacc.yy
      sql/table.cc
      sql/table.h
      sql/transaction.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-11-11 17:08:24 +0000
@@ -0,0 +1,158 @@
+#
+# SUMMARY
+#   Check that a 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 2nd aux connection to be used by this script.
+#   $statement    The statement to be checked.
+#   $cleanup_stmt The statement to be run in order to revert effects of
+#                 the statement to be checked.
+#   $skip_3rd_chk Skip the 3rd stage of checking. The purpose of the third
+#                 stage is to check that metadata locks taken by this
+#                 statement are compatible with metadata locks taken
+#                 by FTWRL.
+#
+# EXAMPLE
+#   flush_read_lock.test
+#
+--disable_result_log
+--disable_query_log
+
+# Reset DEBUG_SYNC facility for safety.
+set debug_sync= "RESET";
+
+#
+# First, check that the 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;
+# Apparently statement was successfully executed and so
+# was not blocked by FTWRL.
+# To be safe against wait_condition.inc succeeding due to
+# races let us first reap the statement being checked to
+# ensure that it has been successfully executed.
+--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 its tables.
+#
+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;
+# Apparently FTWRL was successfully executed and so was not blocked by
+# the statement being checked. To be safe against wait_condition.inc
+# succeeding due to races let us first reap the FTWRL to ensure that it
+# has been successfully executed.
+--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-11-11 17:08:24 +0000
@@ -0,0 +1,155 @@
+#
+# SUMMARY
+#   Check that a 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     The 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. The purpose of the third
+#                  stage is to check that metadata locks taken by this
+#                  statement are incompatible with metadata locks taken
+#                  by FTWRL.
+#
+# EXAMPLE
+#   flush_read_lock.test
+#
+--disable_result_log
+--disable_query_log
+
+# Reset DEBUG_SYNC facility for safety.
+set debug_sync= "RESET";
+
+#
+# First, check that the 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" or
+         state = "Waiting for commit 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 not succeed if this
+# statement is active but has already closed its tables.
+#
+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" or
+         state = "Waiting for commit 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-11-11 17:08:24 +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-11-11 17:08:24 +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/delayed.result'
--- a/mysql-test/r/delayed.result	2010-09-16 14:06:46 +0000
+++ b/mysql-test/r/delayed.result	2010-11-11 17:08:24 +0000
@@ -418,6 +418,18 @@ COMMIT;
 UNLOCK TABLES;
 # Connection con1
 # Reaping: INSERT DELAYED INTO t1 VALUES (5)
+# Connection default
+# Test 5: LOCK TABLES + INSERT DELAYED in one connection.
+# This test has triggered some asserts in metadata locking
+# subsystem at some point in time..
+LOCK TABLE t1 WRITE;
+INSERT DELAYED INTO t2 VALUES (7);
+UNLOCK TABLES;
+SET AUTOCOMMIT= 0;
+LOCK TABLE t1 WRITE;
+INSERT DELAYED INTO t2 VALUES (8);
+UNLOCK TABLES;
+SET AUTOCOMMIT= 1;
 # Connection con2
 # Connection con1
 # Connection default

=== modified file 'mysql-test/r/events_2.result'
--- a/mysql-test/r/events_2.result	2009-12-22 09:35:56 +0000
+++ b/mysql-test/r/events_2.result	2010-11-11 17:08:24 +0000
@@ -133,15 +133,15 @@ select event_name from information_schem
 event_name
 e1
 create event e2 on schedule every 10 hour do select 1;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 alter event e2 disable;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 alter event e2 rename to e3;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 drop event e2;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 drop event e1;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 unlock tables;
 lock table t1 write;
 show create event e1;
@@ -151,15 +151,15 @@ select event_name from information_schem
 event_name
 e1
 create event e2 on schedule every 10 hour do select 1;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 alter event e2 disable;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 alter event e2 rename to e3;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 drop event e2;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 drop event e1;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 unlock tables;
 lock table t1 read, mysql.event read;
 show create event e1;
@@ -169,15 +169,15 @@ select event_name from information_schem
 event_name
 e1
 create event e2 on schedule every 10 hour do select 1;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 alter event e2 disable;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 alter event e2 rename to e3;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 drop event e2;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 drop event e1;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 unlock tables;
 lock table t1 write, mysql.event read;
 show create event e1;
@@ -187,15 +187,15 @@ select event_name from information_schem
 event_name
 e1
 create event e2 on schedule every 10 hour do select 1;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 alter event e2 disable;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 alter event e2 rename to e3;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 drop event e2;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 drop event e1;
-ERROR HY000: Table 'event' was locked with a READ lock and can't be updated
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 unlock tables;
 lock table t1 read, mysql.event write;
 ERROR HY000: You can't combine write-locking of system tables with other tables or lock types
@@ -209,11 +209,17 @@ select event_name from information_schem
 event_name
 e1
 create event e2 on schedule every 10 hour do select 1;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 alter event e2 disable;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 alter event e2 rename to e3;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 drop event e3;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 drop event e1;
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 unlock tables;
+drop event e1;
 Make sure we have left no events 
 select event_name from information_schema.events;
 event_name

=== 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-11-11 17:08:24 +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-11-11 17:08:24 +0000
@@ -0,0 +1,1683 @@
+# FTWRL takes two global metadata locks -- a global shared
+# metadata lock and the commit blocker lock.
+# The first lock prevents DDL from taking place.
+# Let's say that all DDL statements that take metadata
+# locks form class #1 -- incompatible with FTWRL because
+# take incompatible MDL table locks.
+# The first global lock doesn't, however, prevent standalone
+# COMMITs (or implicit COMMITs) from taking place, since a
+# COMMIT doesn't take table locks. It doesn't prevent
+# DDL on temporary tables either, since they don't
+# take any table locks either.
+# Most DDL statements do not perform an implicit commit
+# if operate on a temporary table. Examples are CREATE
+# TEMPORARY TABLE and DROP TEMPORARY TABLE. 
+# Thus, these DDL statements can go through in presence
+# of FTWRL. This is class #2 -- compatible because
+# do not take incompatible MDL locks and do not issue
+# implicit commit..
+# (Although these operations do not commit, their effects
+# cannot be rolled back either.)
+# ALTER TABLE, ANALYZE, OPTIMIZE and some others always
+# issue an implicit commit, even if its argument is a
+# temporary table. 
+# *Howewer* an implicit commit is a no-op if all engines
+# used since the start of transactiona are non-
+# transactional. Thus, for non-transactional engines,
+# these operations are not blocked by FTWRL.
+# This is class #3 -- compatible because do not take
+# MDL table locks and are non-transactional. 
+# On the contrary, for transactional engines, there
+# is always a commit, regardless of whether a table
+# is temporary or not. Thus, for example, ALTER TABLE
+# for a transactional engine will wait for FTWRL, 
+# even if the subject table is temporary.
+# Thus ALTER TABLE <temporary> is incompatible
+# with FTWRL. This is class #4 -- incompatible
+# becuase issue implicit COMMIT which is not a no-op.
+# Finally, there are administrative statements (such as 
+# RESET SLAVE) that do not take any locks and do not
+# issue COMMIT.
+# This is class #5.
+# The goal of this coverage is to test statements
+# of all classes.
+# @todo: documents the effects of @@autocommit,
+# DML and temporary transactional tables.
+# Use MyISAM engine for the most of the tables
+# used in this test in order to be able to 
+# check that DDL statements on temporary tables
+# are compatible with FTRWL. 
+drop tables if exists t1_base, t2_base, t3_trans;
+drop tables if exists tm_base, tm_base_temp;
+drop database if exists mysqltest1;
+# We're going to test ALTER DATABASE UPGRADE
+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;
+drop event if exists e1;
+drop event if exists e2;
+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 temporary table t3_temp_trans(i int) engine=innodb;
+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|
+create event e1 on schedule every 1 minute do begin 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 a temporary 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.
+#
+Success: Was not able to run 'alter event e1 comment 'test'' under FTWRL.
+Success: 'alter event e1 comment 'test'' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter event e1 comment 'test'' is active in another connection.
+#
+# 1.x) The rest of ALTER statements (ALTER TABLESPACE,
+#      ALTER LOGFILE GROUP and ALTER SERVER) are too
+#      special to be tested here.
+#
+#
+# 2) ANALYZE TABLE statement is compatible with FTWRL.
+# See Bug#43336 ANALYZE and OPTIMIZE do not honour
+#  --read-only for a discussion why.
+#
+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 blocked 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 cases a 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 an 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 INDEX 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 INDEX 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.
+#
+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.
+Success: FTWRL is blocked when 'create function f2() returns int return 0' is active in another connection.
+#
+# 8.7) CREATE PROCEDURE is incompatible with FTWRL.
+#
+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.
+Success: FTWRL is blocked when 'create procedure p3() begin end' is active in another connection.
+#
+# 8.8) CREATE EVENT should be incompatible with FTWRL.
+#
+Success: Was not able to run 'create event e2 on schedule every 1 minute do begin end' under FTWRL.
+Success: 'create event e2 on schedule every 1 minute do begin end' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'create event e2 on schedule every 1 minute do begin end' is active in another connection.
+#
+# 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 (CREATE LOGFILE GROUP,
+#      CREATE TABLESPACE and CREATE SERVER) 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.
+# 
+# 9.1.a) Prepare of simple INSERT statement.
+#
+# 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.1.b) Prepare of multi-UPDATE. At some point such statements
+#        tried to acquire thr_lock.c locks during prepare phase.
+#        This no longer happens and thus it 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 'prepare stmt1 from 'update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j'' under FTWRL.
+Success: Was able to run 'prepare stmt1 from 'update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j'' with FTWRL active in another connection.
+#
+# 9.1.c) Prepare of multi-DELETE. Again PREPARE of such
+#        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 'prepare stmt1 from 'delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j'' under FTWRL.
+Success: Was able to run 'prepare stmt1 from 'delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j'' 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 change 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 be compatible 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 on a 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 on a 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.
+#
+Success: Was not able to run 'drop function f1' under FTWRL.
+Success: 'drop function f1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop function f1' is active in another connection.
+#
+# 13.5) DROP PROCEDURE is incompatible with FTWRL.
+#
+Success: Was not able to run 'drop procedure p1' under FTWRL.
+Success: 'drop procedure p1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop procedure p1' is 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.
+#
+Success: Was not able to run 'drop event e1' under FTWRL.
+Success: 'drop event e1' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'drop event e1' is active in another connection.
+#
+# 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 (DROP TABLESPACE, DROP LOGFILE
+#       GROUP and DROP SERVER) 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 change 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 have different compatibility with FTWRL.
+#
+# 31.d.I) SET PASSWORD is incompatible with FTWRL as it changes data.
+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;
+#
+# 31.d.II) SET READ_ONLY is compatible with FTWRL (but has no
+#          effect when executed under it).
+# Skip last part of compatibility testing as our helper debug
+# sync-point doesn't work for SET statements.
+Success: Was able to run 'set global read_only= 1' under FTWRL.
+Success: Was able to run 'set global read_only= 1' with FTWRL active in another connection.
+#
+# 31.d.III) Situation with SET AUTOCOMMIT is complex.
+#           Turning auto-commit off is always compatible with FTWRL.
+#           Turning auto-commit on causes implicit commit and so
+#           is incompatible with FTWRL if there are changes to be
+#           committed.
+flush tables with read lock;
+set autocommit= 0;
+# Turning auto-commit on causes implicit commit so can
+# be incompatible with FTWRL if there is something to
+# commit. But since even in this case we allow commits
+# under active FTWRL such statement should always succeed.
+insert into t3_temp_trans values (1);
+set autocommit= 1;
+unlock tables;
+delete from t3_temp_trans;
+# Check that SET AUTOCOMMIT=0 is not blocked and 
+# SET AUTOCOMMIT=1 is blocked by active FTWRL in
+# another connection.
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+set autocommit= 0;
+# Switching to connection 'con1'.
+unlock tables;
+# Switching to connection 'default'.
+# Do some work so implicit commit in SET AUTOCOMMIT=1 
+# 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:
+set autocommit= 1;
+# Switching to connection 'con1'.
+# Wait until SET AUTOCOMMIT=1 is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap SET AUTOCOMMIT=1.
+delete from t3_trans;
+#
+# Check that SET AUTOCOMMIT=1 blocks FTWRL in another connection.
+set autocommit= 0;
+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';
+set autocommit= 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.
+set debug_sync='now SIGNAL go';
+# Switching to connection 'default'.
+# Reap SET AUTOCOMMIT=1.
+# Switching to connection 'con1'.
+# Reap FTWRL.
+unlock tables;
+# Switching to connection 'default'.
+delete from t3_trans;
+set debug_sync= "RESET";
+#
+# 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";
+#
+# 38) Test effect of auto-commit mode for DML on transactional
+#     temporary tables.
+# 
+# 38.1) When auto-commit is on each such a statement ends with commit
+#       of changes to temporary tables. But since transactions doing
+#       such changes are considered read only [sic!/QQ] this commit
+#       is compatible with FTWRL.
+#
+#       Let us demostrate this fact for some common DML statements.
+Success: Was able to run 'delete from t3_temp_trans' under FTWRL.
+Success: Was able to run 'delete from t3_temp_trans' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'delete from t3_temp_trans' was active in another connection.
+Success: Was able to run 'insert into t3_temp_trans values (1)' under FTWRL.
+Success: Was able to run 'insert into t3_temp_trans values (1)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'insert into t3_temp_trans values (1)' was active in another connection.
+Success: Was able to run 'update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j' under FTWRL.
+Success: Was able to run 'update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j' was active in another connection.
+# 
+# 38.2) When auto-commit is off DML on transaction temporary tables
+#       is compatible with FTWRL.
+#
+set autocommit= 0;
+Success: Was able to run 'delete from t3_temp_trans' under FTWRL.
+Success: Was able to run 'delete from t3_temp_trans' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'delete from t3_temp_trans' was active in another connection.
+Success: Was able to run 'insert into t3_temp_trans values (1)' under FTWRL.
+Success: Was able to run 'insert into t3_temp_trans values (1)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'insert into t3_temp_trans values (1)' was active in another connection.
+Success: Was able to run 'update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j' under FTWRL.
+Success: Was able to run 'update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j' was active in another connection.
+set autocommit= 1;
+#
+# 39) Test effect of DDL on transactional tables.
+#
+# 39.1) Due to implicit commit at the end of statement some of DDL
+#       statements which are compatible with FTWRL in non-transactional
+#       case are not compatible in case of transactional tables.
+#
+# 39.1.a) ANALYZE TABLE for transactional table is incompatible with
+#         FTWRL.
+flush tables with read lock;
+# Implicit commits are allowed under FTWRL.
+analyze table t3_trans;
+Table	Op	Msg_type	Msg_text
+test.t3_trans	analyze	status	OK
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+analyze table t3_trans;
+# Switching to connection 'con1'.
+# Check that ANALYZE TABLE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap ANALYZE TABLE
+Table	Op	Msg_type	Msg_text
+test.t3_trans	analyze	status	OK
+#
+# 39.1.b) CHECK TABLE for transactional table is compatible with FTWRL.
+#         Although it does implicit commit at the end of statement it
+#         is considered to be read-only operation.
+# Skip last part of compatibility testing as this statement
+# releases metadata locks in non-standard place.
+Success: Was able to run 'check table t3_trans' under FTWRL.
+Success: Was able to run 'check table t3_trans' with FTWRL active in another connection.
+#
+# 39.2) Situation with DDL on temporary transactional tables is
+#       complex.
+#
+# 39.2.a) Some statements compatible with FTWRL since they don't
+#         do implicit commit.
+#
+# For example, CREATE TEMPORARY TABLE:
+Success: Was able to run 'create temporary table t4_temp_trans(i int) engine=innodb' under FTWRL.
+Success: Was able to run 'create temporary table t4_temp_trans(i int) engine=innodb' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'create temporary table t4_temp_trans(i int) engine=innodb' was active in another connection.
+#
+# Or DROP TEMPORARY TABLE:
+Success: Was able to run 'drop temporary tables t3_temp_trans' under FTWRL.
+Success: Was able to run 'drop temporary tables t3_temp_trans' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'drop temporary tables t3_temp_trans' was active in another connection.
+#
+# 39.2.b) Some statements do implicit commit but are considered
+#         read-only and so are compatible with FTWRL.
+#
+# For example, REPAIR TABLE:
+Success: Was able to run 'repair table t3_temp_trans' under FTWRL.
+Success: Was able to run 'repair table t3_temp_trans' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'repair table t3_temp_trans' was active in another connection.
+#
+# And ANALYZE TABLE:
+Success: Was able to run 'analyze table t3_temp_trans' under FTWRL.
+Success: Was able to run 'analyze table t3_temp_trans' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'analyze table t3_temp_trans' was active in another connection.
+#
+# 39.2.c) Some statements do implicit commit and not
+#         considered read-only. As result they are 
+#         not compatible with FTWRL.
+#
+flush tables with read lock;
+# Implicit commits are allowed under FTWRL.
+alter table t3_temp_trans add column c1 int;
+unlock tables;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+alter table t3_temp_trans drop column c1;
+# Switching to connection 'con1'.
+# Check that ALTER TABLE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap ALTER TABLE
+#
+# 40) Test effect of implicit commit for DDL which is otherwise
+#     compatible with FTWRL. Implicit commit at the start of DDL
+#     statement can make it incompatible with FTWRL if there are
+#     some changes to be commited even in case when DDL statement
+#     itself is compatible with FTWRL.
+#
+# For example CHECK TABLE for base non-transactional tables and
+# ALTER TABLE for temporary non-transactional tables are affected.
+begin;
+insert into t3_trans values (1);
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+check table t1_base;
+# Switching to connection 'con1'.
+# Check that CHECK TABLE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap CHECK TABLE
+Table	Op	Msg_type	Msg_text
+test.t1_base	check	status	OK
+begin;
+delete from t3_trans;
+#
+# Switching to connection 'con1'.
+flush tables with read lock;
+# Switching to connection 'default'.
+alter table t1_temp add column c1 int;
+# Switching to connection 'con1'.
+# Check that ALTER TABLE is blocked.
+unlock tables;
+# Switching to connection 'default'.
+# Reap ALTER TABLE
+alter table t1_temp drop column c1;
+#
+# 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;
+
+# Check that COMMIT thas is issued after
+# FLUSH TABLES WITH READ LOCK is not blocked by
+# FLUSH TABLES WITH READ LOCK from another connection.
+# This scenario is used in innobackup.pl. The COMMIT goes
+# through because the transaction started by FTWRL does
+# not modify any tables, and the commit blocker lock is
+# only taken when there were such modifications.
+
+flush tables with read lock;
+# Switching to connection 'con1'.
+# The below FTWRL should not be blocked by transaction in 'default'.
+flush tables with read lock;
+# Switching to connection 'default'.
+select * from t1_base;
+i
+select * from t3_trans;
+i
+commit;
+# Switching to connection 'con1'.
+select * from t1_base;
+i
+select * from t3_trans;
+i
+commit;
+unlock tables;
+# Switching to connection 'default'.
+unlock tables;
+#
+# Check how FLUSH TABLE WITH READ LOCK is handled for MERGE tables.
+# As usual there are tricky cases related to this type of tables.
+#
+#
+# 1) Most typical case - base MERGE table with base underlying tables.
+#    
+# 1.a) DML statements which change data should be incompatible with FTWRL.
+create table tm_base (i int) engine=merge union=(t1_base) insert_method=last;
+Success: Was not able to run 'insert into tm_base values (1)' under FTWRL.
+Success: 'insert into tm_base values (1)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'insert into tm_base values (1)' is active in another connection.
+#
+# 1.b) DDL statement on such table should be incompatible with FTWRL as well.
+Success: Was not able to run 'alter table tm_base insert_method=first' under FTWRL.
+Success: 'alter table tm_base insert_method=first' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter table tm_base insert_method=first' is active in another connection.
+drop table tm_base;
+#
+# 2) Temporary MERGE table with base underlying tables.
+#
+# 2.a) DML statements which change data should be incompatible with FTWRL
+#      as they affect base tables.
+create temporary table tm_temp_base (i int) engine=merge union=(t1_base) insert_method=last;
+Success: Was not able to run 'insert into tm_temp_base values (1)' under FTWRL.
+Success: 'insert into tm_temp_base values (1)' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'insert into tm_temp_base values (1)' is active in another connection.
+#
+# 2.b) Some of DDL statements on such table can be compatible with FTWRL
+#      as they don't affect base tables.
+Success: Was able to run 'drop temporary tables tm_temp_base' under FTWRL.
+Success: Was able to run 'drop temporary tables tm_temp_base' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'drop temporary tables tm_temp_base' was active in another connection.
+#
+# 2.c) ALTER statement is incompatible with FTWRL. Even though it does
+#      not change data in base table it still acquires strong metadata
+#      locks on them.
+Success: Was not able to run 'alter table tm_temp_base insert_method=first' under FTWRL.
+Success: 'alter table tm_temp_base insert_method=first' is blocked by FTWRL active in another connection.
+Success: FTWRL is blocked when 'alter table tm_temp_base insert_method=first' is active in another connection.
+drop table tm_temp_base;
+#
+# 3) Temporary MERGE table with temporary underlying tables.
+#
+# 3.a) DML statements should be compatible with FTWRL as
+#      no base table is going to be affected.
+create temporary table tm_temp_temp (i int) engine=merge union=(t1_temp) insert_method=last;
+Success: Was able to run 'insert into tm_temp_temp values (1)' under FTWRL.
+Success: Was able to run 'insert into tm_temp_temp values (1)' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'insert into tm_temp_temp values (1)' was active in another connection.
+#
+# 3.b) DDL statements should be compatible with FTWRL as well
+#      as no base table is going to be affected too.
+Success: Was able to run 'alter table tm_temp_temp union=(t1_temp) insert_method=first' under FTWRL.
+Success: Was able to run 'alter table tm_temp_temp union=(t1_temp) insert_method=first' with FTWRL active in another connection.
+Success: Was able to run FTWRL while 'alter table tm_temp_temp union=(t1_temp) insert_method=first' was active in another connection.
+drop table tm_temp_temp;
+#
+# 4) For the sake of completeness let us check that base MERGE tables
+#    with temporary underlying tables are not functional.
+create table tm_base_temp (i int) engine=merge union=(t1_temp) insert_method=last;
+select * from tm_base_temp;
+ERROR HY000: Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
+drop table tm_base_temp;
+#
+# Clean-up.
+#
+drop event e1;
+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-11-11 17:08:24 +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-11-11 17:08:24 +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-11-11 17:08:24 +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-11-11 17:08:24 +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,10 +2486,17 @@ 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
+# 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 statement ends.
+#
+# Reaping DROP PROCEDURE.
 # Connection 2
+# Reaping FTWRL.
 UNLOCK TABLES;
 # Connection 1
 SET DEBUG_SYNC= 'RESET';

=== 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-11-11 17:08:24 +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/func_file_io.result'
--- a/mysql-test/suite/perfschema/r/func_file_io.result	2010-10-07 00:03:27 +0000
+++ b/mysql-test/suite/perfschema/r/func_file_io.result	2010-11-11 17:08:24 +0000
@@ -100,3 +100,4 @@ INNER JOIN performance_schema.THREADS p 
 WHERE p.PROCESSLIST_ID = 1
 GROUP BY h.EVENT_NAME
 HAVING TOTAL_WAIT > 0;
+UPDATE performance_schema.SETUP_INSTRUMENTS SET enabled = 'YES';

=== modified file 'mysql-test/suite/perfschema/r/func_mutex.result'
--- a/mysql-test/suite/perfschema/r/func_mutex.result	2010-01-12 01:47:27 +0000
+++ b/mysql-test/suite/perfschema/r/func_mutex.result	2010-11-11 17:08:24 +0000
@@ -110,4 +110,5 @@ WHERE (EVENT_NAME = 'wait/synch/rwlock/s
 SELECT IF((COALESCE(@after_count, 0) - COALESCE(@before_count, 0)) = 0, 'Success', 'Failure') test_fm2_rw_timed;
 test_fm2_rw_timed
 Success
+UPDATE performance_schema.SETUP_INSTRUMENTS SET enabled = 'YES';
 DROP TABLE t1;

=== 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-11-11 17:08:24 +0000
@@ -1,4 +1,5 @@
 use performance_schema;
+update performance_schema.SETUP_INSTRUMENTS set enabled='YES';
 grant SELECT, UPDATE, LOCK TABLES on performance_schema.* to pfsuser@localhost;
 flush privileges;
 connect (con1, localhost, pfsuser, , test);
@@ -21,9 +22,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-11-11 17:08:24 +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
@@ -120,10 +116,6 @@ where name like "wait/synch/mutex/sql/Qu
 count(name)
 1
 select count(name) from MUTEX_INSTANCES
-where name like "wait/synch/mutex/sql/LOCK_event_metadata";
-count(name)
-1
-select count(name) from MUTEX_INSTANCES
 where name like "wait/synch/mutex/sql/LOCK_event_queue";
 count(name)
 1
@@ -184,10 +176,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/func_file_io.test'
--- a/mysql-test/suite/perfschema/t/func_file_io.test	2010-10-07 00:03:27 +0000
+++ b/mysql-test/suite/perfschema/t/func_file_io.test	2010-11-11 17:08:24 +0000
@@ -190,3 +190,6 @@ HAVING TOTAL_WAIT > 0;
 ## HAVING BYTES > 0
 ## ORDER BY i.user, h.operation;
 ## --enable_result_log
+
+# Clean-up.
+UPDATE performance_schema.SETUP_INSTRUMENTS SET enabled = 'YES';

=== modified file 'mysql-test/suite/perfschema/t/func_mutex.test'
--- a/mysql-test/suite/perfschema/t/func_mutex.test	2010-01-12 01:47:27 +0000
+++ b/mysql-test/suite/perfschema/t/func_mutex.test	2010-11-11 17:08:24 +0000
@@ -128,4 +128,6 @@ SET @after_count = (SELECT SUM(TIMER_WAI
 
 SELECT IF((COALESCE(@after_count, 0) - COALESCE(@before_count, 0)) = 0, 'Success', 'Failure') test_fm2_rw_timed;
 
+# Clean-up.
+UPDATE performance_schema.SETUP_INSTRUMENTS SET enabled = 'YES';
 DROP TABLE t1;

=== 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-11-11 17:08:24 +0000
@@ -22,6 +22,10 @@
 
 use performance_schema;
 
+# Make test robust against errors in other tests.
+# Ensure that instrumentation is turned on when we create new connection.
+update performance_schema.SETUP_INSTRUMENTS set enabled='YES';
+
 grant SELECT, UPDATE, LOCK TABLES on performance_schema.* to pfsuser@localhost;
 flush privileges;
 
@@ -60,7 +64,7 @@ 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
 
@@ -69,7 +73,7 @@ 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-11-11 17:08:24 +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
@@ -133,9 +130,6 @@ select count(name) from MUTEX_INSTANCES
 #  where name like "wait/synch/mutex/sql/Event_scheduler::LOCK_scheduler_state";
 
 select count(name) from MUTEX_INSTANCES
- where name like "wait/synch/mutex/sql/LOCK_event_metadata";
-
-select count(name) from MUTEX_INSTANCES
  where name like "wait/synch/mutex/sql/LOCK_event_queue";
 
 select count(name) from MUTEX_INSTANCES
@@ -189,9 +183,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/suite/rpl/r/rpl_tmp_table_and_DDL.result'
--- a/mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result	2010-04-28 03:26:47 +0000
+++ b/mysql-test/suite/rpl/r/rpl_tmp_table_and_DDL.result	2010-11-11 17:08:24 +0000
@@ -123,16 +123,16 @@ DROP PROCEDURE p2;
 ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 INSERT INTO t2 VALUES ("DROP PROCEDURE p2 with table locked");
 CREATE EVENT e1 ON SCHEDULE EVERY 10 HOUR DO SELECT 1;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 INSERT INTO t2 VALUES ("CREATE EVENT e1 with table locked");
 UNLOCK TABLE;
 CREATE EVENT e2 ON SCHEDULE EVERY 10 HOUR DO SELECT 1;
 LOCK TABLE t1 WRITE;
 ALTER EVENT e2 ON SCHEDULE EVERY 20 HOUR DO SELECT 1;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 INSERT INTO t2 VALUES ("ALTER EVENT e2 with table locked");
 DROP EVENT e2;
-ERROR HY000: Table 'event' was not locked with LOCK TABLES
+ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction
 INSERT INTO t2 VALUES ("DROP EVENT e2 with table locked");
 CREATE DATABASE mysqltest1;
 ERROR HY000: Can't execute the given command because you have active locked tables or an active transaction

=== modified file 'mysql-test/t/delayed.test'
--- a/mysql-test/t/delayed.test	2010-09-16 14:06:46 +0000
+++ b/mysql-test/t/delayed.test	2010-11-11 17:08:24 +0000
@@ -539,6 +539,21 @@ connection con1;
 --echo # Reaping: INSERT DELAYED INTO t1 VALUES (5)
 --reap
 
+--echo # Connection default
+connection default;
+
+--echo # Test 5: LOCK TABLES + INSERT DELAYED in one connection.
+--echo # This test has triggered some asserts in metadata locking
+--echo # subsystem at some point in time..
+LOCK TABLE t1 WRITE;
+INSERT DELAYED INTO t2 VALUES (7);
+UNLOCK TABLES;
+SET AUTOCOMMIT= 0;
+LOCK TABLE t1 WRITE;
+INSERT DELAYED INTO t2 VALUES (8);
+UNLOCK TABLES;
+SET AUTOCOMMIT= 1;
+
 --echo # Connection con2
 connection con2;
 disconnect con2;

=== modified file 'mysql-test/t/events_2.test'
--- a/mysql-test/t/events_2.test	2009-12-22 09:35:56 +0000
+++ b/mysql-test/t/events_2.test	2010-11-11 17:08:24 +0000
@@ -212,15 +212,15 @@ lock table t1 read;
 --replace_regex /STARTS '[^']+'/STARTS '#'/
 show create event e1;
 select event_name from information_schema.events;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 create event e2 on schedule every 10 hour do select 1;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 alter event e2 disable;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 alter event e2 rename to e3;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 drop event e2;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 drop event e1;
 unlock tables;
 #
@@ -229,15 +229,15 @@ lock table t1 write;
 --replace_regex /STARTS '[^']+'/STARTS '#'/
 show create event e1;
 select event_name from information_schema.events;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 create event e2 on schedule every 10 hour do select 1;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 alter event e2 disable;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 alter event e2 rename to e3;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 drop event e2;
---error ER_TABLE_NOT_LOCKED
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 drop event e1;
 unlock tables;
 #
@@ -246,15 +246,15 @@ lock table t1 read, mysql.event read;
 --replace_regex /STARTS '[^']+'/STARTS '#'/
 show create event e1;
 select event_name from information_schema.events;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 create event e2 on schedule every 10 hour do select 1;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 alter event e2 disable;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 alter event e2 rename to e3;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 drop event e2;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 drop event e1;
 unlock tables;
 #
@@ -263,15 +263,15 @@ lock table t1 write, mysql.event read;
 --replace_regex /STARTS '[^']+'/STARTS '#'/
 show create event e1;
 select event_name from information_schema.events;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 create event e2 on schedule every 10 hour do select 1;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 alter event e2 disable;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 alter event e2 rename to e3;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 drop event e2;
---error ER_TABLE_NOT_LOCKED_FOR_WRITE
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 drop event e1;
 unlock tables;
 #
@@ -285,12 +285,18 @@ lock table mysql.event write;
 --replace_regex /STARTS '[^']+'/STARTS '#'/
 show create event e1;
 select event_name from information_schema.events;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 create event e2 on schedule every 10 hour do select 1;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 alter event e2 disable;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 alter event e2 rename to e3;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 drop event e3;
+--error ER_LOCK_OR_ACTIVE_TRANSACTION
 drop event e1;
 unlock tables;
+drop event e1;
 --echo Make sure we have left no events 
 select event_name from information_schema.events;
 --echo

=== 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-11-11 17:08:24 +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-11-11 17:08:24 +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 commit 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-11-11 17:08:24 +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-11-11 17:08:24 +0000
@@ -0,0 +1,2187 @@
+#
+# 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 # FTWRL takes two global metadata locks -- a global shared
+--echo # metadata lock and the commit blocker lock.
+--echo # The first lock prevents DDL from taking place.
+--echo # Let's say that all DDL statements that take metadata
+--echo # locks form class #1 -- incompatible with FTWRL because
+--echo # take incompatible MDL table locks.
+--echo # The first global lock doesn't, however, prevent standalone
+--echo # COMMITs (or implicit COMMITs) from taking place, since a
+--echo # COMMIT doesn't take table locks. It doesn't prevent
+--echo # DDL on temporary tables either, since they don't
+--echo # take any table locks either.
+--echo # Most DDL statements do not perform an implicit commit
+--echo # if operate on a temporary table. Examples are CREATE
+--echo # TEMPORARY TABLE and DROP TEMPORARY TABLE. 
+--echo # Thus, these DDL statements can go through in presence
+--echo # of FTWRL. This is class #2 -- compatible because
+--echo # do not take incompatible MDL locks and do not issue
+--echo # implicit commit..
+--echo # (Although these operations do not commit, their effects
+--echo # cannot be rolled back either.)
+--echo # ALTER TABLE, ANALYZE, OPTIMIZE and some others always
+--echo # issue an implicit commit, even if its argument is a
+--echo # temporary table. 
+--echo # *Howewer* an implicit commit is a no-op if all engines
+--echo # used since the start of transactiona are non-
+--echo # transactional. Thus, for non-transactional engines,
+--echo # these operations are not blocked by FTWRL.
+--echo # This is class #3 -- compatible because do not take
+--echo # MDL table locks and are non-transactional. 
+--echo # On the contrary, for transactional engines, there
+--echo # is always a commit, regardless of whether a table
+--echo # is temporary or not. Thus, for example, ALTER TABLE
+--echo # for a transactional engine will wait for FTWRL, 
+--echo # even if the subject table is temporary.
+--echo # Thus ALTER TABLE <temporary> is incompatible
+--echo # with FTWRL. This is class #4 -- incompatible
+--echo # becuase issue implicit COMMIT which is not a no-op.
+--echo # Finally, there are administrative statements (such as 
+--echo # RESET SLAVE) that do not take any locks and do not
+--echo # issue COMMIT.
+--echo # This is class #5.
+--echo # The goal of this coverage is to test statements
+--echo # of all classes.
+--echo # @todo: documents the effects of @@autocommit,
+--echo # DML and temporary transactional tables.
+
+--echo # Use MyISAM engine for the most of the tables
+--echo # used in this test in order to be able to 
+--echo # check that DDL statements on temporary tables
+--echo # are compatible with FTRWL. 
+--disable_warnings
+drop tables if exists t1_base, t2_base, t3_trans;
+drop tables if exists tm_base, tm_base_temp;
+drop database if exists mysqltest1;
+--echo # We're going to test ALTER DATABASE UPGRADE
+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;
+drop event if exists e1;
+drop event if exists e2;
+--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 temporary table t3_temp_trans(i int) engine=innodb;
+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 ;|
+create event e1 on schedule every 1 minute do begin end;
+
+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 a temporary 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 #
+let $statement= alter event e1 comment 'test';
+let $cleanup_stmt1= alter event e1 comment '';
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 1.x) The rest of ALTER statements (ALTER TABLESPACE,
+--echo #      ALTER LOGFILE GROUP and ALTER SERVER) are too
+--echo #      special to be tested here.
+--echo #
+
+
+--echo #
+--echo # 2) ANALYZE TABLE statement is compatible with FTWRL.
+--echo # See Bug#43336 ANALYZE and OPTIMIZE do not honour
+--echo #  --read-only for a discussion why.
+--echo #
+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 blocked and 
+--echo # COMMIT is blocked by active FTWRL in another
+--echo # 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 commit 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 commit 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 cases a parameter expression should be
+--echo #      compatible 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 an 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 INDEX 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 INDEX 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;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 8.7) CREATE PROCEDURE is incompatible with FTWRL.
+--echo #
+let $statement= create procedure p3() begin end;
+let $cleanup_stmt1= drop procedure p3;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 8.8) CREATE EVENT should be incompatible with FTWRL.
+--echo #
+let $statement= create event e2 on schedule every 1 minute do begin end;
+let $cleanup_stmt1= drop event e2;
+--source include/check_ftwrl_incompatible.inc
+
+--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 (CREATE LOGFILE GROUP,
+--echo #      CREATE TABLESPACE and CREATE SERVER) are too special
+--echo #      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.
+--echo # 
+--echo # 9.1.a) Prepare of simple INSERT statement.
+--echo #
+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.1.b) Prepare of multi-UPDATE. At some point such statements
+--echo #        tried to acquire thr_lock.c locks during prepare phase.
+--echo #        This no longer happens and thus it is compatible with
+--echo #        FTWRL.
+let $statement= prepare stmt1 from 'update t1_base, t2_base set t1_base.i= 1 where t1_base.i = t2_base.j';
+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.1.c) Prepare of multi-DELETE. Again PREPARE of such
+--echo #        statement should be compatible with FTWRL.
+let $statement= prepare stmt1 from 'delete t1_base from t1_base, t2_base where t1_base.i = t2_base.j';
+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 change 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 be compatible 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 on a 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 on a 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;
+--source include/check_ftwrl_incompatible.inc
+
+--echo #
+--echo # 13.5) DROP PROCEDURE is incompatible with FTWRL.
+--echo #
+let $statement= drop procedure p1;
+let $cleanup_stmt1= create procedure p1() begin end;
+--source include/check_ftwrl_incompatible.inc
+
+--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 #
+let $statement= drop event e1;
+let $cleanup_stmt1= create event e1 on schedule every 1 minute do begin end;
+--source include/check_ftwrl_incompatible.inc
+
+--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 (DROP TABLESPACE, DROP LOGFILE
+--echo #       GROUP and DROP SERVER) 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 change 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 have different compatibility with FTWRL.
+--echo #
+--echo # 31.d.I) SET PASSWORD is incompatible with FTWRL as it changes data.
+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 # 31.d.II) SET READ_ONLY is compatible with FTWRL (but has no
+--echo #          effect when executed under it).
+let $statement= set global read_only= 1;
+let $cleanup_stmt= set global read_only= 0;
+--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.III) Situation with SET AUTOCOMMIT is complex.
+--echo #           Turning auto-commit off is always compatible with FTWRL.
+--echo #           Turning auto-commit on causes implicit commit and so
+--echo #           is incompatible with FTWRL if there are changes to be
+--echo #           committed.
+flush tables with read lock;
+set autocommit= 0;
+--echo # Turning auto-commit on causes implicit commit so can
+--echo # be incompatible with FTWRL if there is something to
+--echo # commit. But since even in this case we allow commits
+--echo # under active FTWRL such statement should always succeed.
+insert into t3_temp_trans values (1);
+set autocommit= 1;
+unlock tables;
+delete from t3_temp_trans;
+--echo # Check that SET AUTOCOMMIT=0 is not blocked and 
+--echo # SET AUTOCOMMIT=1 is blocked by active FTWRL in
+--echo # another connection.
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+set autocommit= 0;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Do some work so implicit commit in SET AUTOCOMMIT=1 
+--echo # 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 set autocommit= 1
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Wait until SET AUTOCOMMIT=1 is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for commit lock" and
+        info = "set autocommit= 1";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap SET AUTOCOMMIT=1.
+--reap
+delete from t3_trans;
+--echo #
+--echo # Check that SET AUTOCOMMIT=1 blocks FTWRL in another connection.
+set autocommit= 0;
+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 set autocommit= 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 commit 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 SET AUTOCOMMIT=1.
+--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 # 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 commit 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 commit 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 # 38) Test effect of auto-commit mode for DML on transactional
+--echo #     temporary tables.
+--echo # 
+--echo # 38.1) When auto-commit is on each such a statement ends with commit
+--echo #       of changes to temporary tables. But since transactions doing
+--echo #       such changes are considered read only [sic!/QQ] this commit
+--echo #       is compatible with FTWRL.
+--echo #
+--echo #       Let us demostrate this fact for some common DML statements.
+let $statement= delete from t3_temp_trans;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+let $statement= insert into t3_temp_trans values (1);
+let $cleanup_stmt= delete from t3_temp_trans limit 1;
+--source include/check_ftwrl_compatible.inc
+
+let $statement= update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+--echo # 
+--echo # 38.2) When auto-commit is off DML on transaction temporary tables
+--echo #       is compatible with FTWRL.
+--echo #
+set autocommit= 0;
+let $statement= delete from t3_temp_trans;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+
+let $statement= insert into t3_temp_trans values (1);
+let $cleanup_stmt= delete from t3_temp_trans limit 1;
+--source include/check_ftwrl_compatible.inc
+
+let $statement= update t3_temp_trans, t2_temp set t3_temp_trans.i= 1 where t3_temp_trans.i = t2_temp.j;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+set autocommit= 1;
+
+
+--echo #
+--echo # 39) Test effect of DDL on transactional tables.
+--echo #
+--echo # 39.1) Due to implicit commit at the end of statement some of DDL
+--echo #       statements which are compatible with FTWRL in non-transactional
+--echo #       case are not compatible in case of transactional tables.
+--echo #
+--echo # 39.1.a) ANALYZE TABLE for transactional table is incompatible with
+--echo #         FTWRL.
+flush tables with read lock;
+--echo # Implicit commits are allowed under FTWRL.
+analyze table t3_trans;
+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 analyze table t3_trans
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that ANALYZE TABLE is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for commit lock" and
+        info = "analyze table t3_trans";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap ANALYZE TABLE
+--reap
+
+--echo #
+--echo # 39.1.b) CHECK TABLE for transactional table is compatible with FTWRL.
+--echo #         Although it does implicit commit at the end of statement it
+--echo #         is considered to be read-only operation.
+let $statement= check table t3_trans;
+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 # 39.2) Situation with DDL on temporary transactional tables is
+--echo #       complex.
+--echo #
+--echo # 39.2.a) Some statements compatible with FTWRL since they don't
+--echo #         do implicit commit.
+--echo #
+--echo # For example, CREATE TEMPORARY TABLE:
+let $statement= create temporary table t4_temp_trans(i int) engine=innodb;
+let $cleanup_stmt= drop temporary tables t4_temp_trans;
+--source include/check_ftwrl_compatible.inc
+--echo #
+--echo # Or DROP TEMPORARY TABLE:
+let $statement= drop temporary tables t3_temp_trans;
+let $cleanup_stmt= create temporary table t3_temp_trans(i int) engine=innodb;
+--source include/check_ftwrl_compatible.inc
+--echo #
+--echo # 39.2.b) Some statements do implicit commit but are considered
+--echo #         read-only and so are compatible with FTWRL.
+--echo #
+--echo # For example, REPAIR TABLE:
+let $statement= repair table t3_temp_trans;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+--echo #
+--echo # And ANALYZE TABLE:
+let $statement= analyze table t3_temp_trans;
+let $cleanup_stmt= ;
+--source include/check_ftwrl_compatible.inc
+--echo #
+--echo # 39.2.c) Some statements do implicit commit and not
+--echo #         considered read-only. As result they are 
+--echo #         not compatible with FTWRL.
+--echo #
+flush tables with read lock;
+--echo # Implicit commits are allowed under FTWRL.
+alter table t3_temp_trans add column c1 int;
+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 alter table t3_temp_trans drop column c1
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that ALTER TABLE is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for commit lock" and
+        info = "alter table t3_temp_trans drop column c1";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap ALTER TABLE
+--reap
+
+
+--echo #
+--echo # 40) Test effect of implicit commit for DDL which is otherwise
+--echo #     compatible with FTWRL. Implicit commit at the start of DDL
+--echo #     statement can make it incompatible with FTWRL if there are
+--echo #     some changes to be commited even in case when DDL statement
+--echo #     itself is compatible with FTWRL.
+--echo #
+--echo # For example CHECK TABLE for base non-transactional tables and
+--echo # ALTER TABLE for temporary non-transactional tables are affected.
+begin;
+insert into t3_trans values (1);
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send check table t1_base
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that CHECK TABLE is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for commit lock" and
+        info = "check table t1_base";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap CHECK TABLE
+--reap
+begin;
+delete from t3_trans;
+--echo #
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+flush tables with read lock;
+--echo # Switching to connection 'default'.
+connection default;
+--send alter table t1_temp add column c1 int
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+--echo # Check that ALTER TABLE is blocked.
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for commit lock" and
+        info = "alter table t1_temp add column c1 int";
+--source include/wait_condition.inc
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+--echo # Reap ALTER TABLE
+--reap
+alter table t1_temp drop column c1;
+
+
+--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 # Check that COMMIT thas is issued after
+--echo # FLUSH TABLES WITH READ LOCK is not blocked by
+--echo # FLUSH TABLES WITH READ LOCK from another connection.
+--echo # This scenario is used in innobackup.pl. The COMMIT goes
+--echo # through because the transaction started by FTWRL does
+--echo # not modify any tables, and the commit blocker lock is
+--echo # only taken when there were such modifications.
+--echo 
+flush tables with read lock;
+--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;
+select * from t1_base;
+select * from t3_trans;
+commit;
+--echo # Switching to connection '$con_aux1'.
+connection $con_aux1;
+select * from t1_base;
+select * from t3_trans;
+commit;
+unlock tables;
+--echo # Switching to connection 'default'.
+connection default;
+unlock tables;
+
+
+--echo #
+--echo # Check how FLUSH TABLE WITH READ LOCK is handled for MERGE tables.
+--echo # As usual there are tricky cases related to this type of tables.
+--echo #
+--echo #
+--echo # 1) Most typical case - base MERGE table with base underlying tables.
+--echo #    
+--echo # 1.a) DML statements which change data should be incompatible with FTWRL.
+create table tm_base (i int) engine=merge union=(t1_base) insert_method=last;
+let $statement= insert into tm_base values (1);
+let $cleanup_stmt1= delete from tm_base;
+--source include/check_ftwrl_incompatible.inc
+--echo #
+--echo # 1.b) DDL statement on such table should be incompatible with FTWRL as well.
+let $statement= alter table tm_base insert_method=first;
+let $cleanup_stmt1= alter table tm_base insert_method=last;
+--source include/check_ftwrl_incompatible.inc
+drop table tm_base;
+
+--echo #
+--echo # 2) Temporary MERGE table with base underlying tables.
+--echo #
+--echo # 2.a) DML statements which change data should be incompatible with FTWRL
+--echo #      as they affect base tables.
+create temporary table tm_temp_base (i int) engine=merge union=(t1_base) insert_method=last;
+let $statement= insert into tm_temp_base values (1);
+let $cleanup_stmt1= delete from tm_temp_base;
+--source include/check_ftwrl_incompatible.inc
+--echo #
+--echo # 2.b) Some of DDL statements on such table can be compatible with FTWRL
+--echo #      as they don't affect base tables.
+let $statement= drop temporary tables tm_temp_base;
+let $cleanup_stmt= create temporary table tm_temp_base (i int) engine=merge union=(t1_base) insert_method=last;
+--source include/check_ftwrl_compatible.inc
+--echo #
+--echo # 2.c) ALTER statement is incompatible with FTWRL. Even though it does
+--echo #      not change data in base table it still acquires strong metadata
+--echo #      locks on them.
+let $statement= alter table tm_temp_base insert_method=first;
+let $cleanup_stmt1= alter table tm_temp_base insert_method=last;
+--source include/check_ftwrl_incompatible.inc
+drop table tm_temp_base;
+
+--echo #
+--echo # 3) Temporary MERGE table with temporary underlying tables.
+--echo #
+--echo # 3.a) DML statements should be compatible with FTWRL as
+--echo #      no base table is going to be affected.
+create temporary table tm_temp_temp (i int) engine=merge union=(t1_temp) insert_method=last;
+let $statement= insert into tm_temp_temp values (1);
+let $cleanup_stmt= delete from tm_temp_temp;
+--source include/check_ftwrl_compatible.inc
+--echo #
+--echo # 3.b) DDL statements should be compatible with FTWRL as well
+--echo #      as no base table is going to be affected too.
+let $statement= alter table tm_temp_temp union=(t1_temp) insert_method=first;
+let $cleanup_stmt= alter table tm_temp_temp union=(t1_temp) insert_method=last;
+--source include/check_ftwrl_compatible.inc
+drop table tm_temp_temp;
+
+--echo #
+--echo # 4) For the sake of completeness let us check that base MERGE tables
+--echo #    with temporary underlying tables are not functional.
+create table tm_base_temp (i int) engine=merge union=(t1_temp) insert_method=last;
+--error ER_WRONG_MRG_TABLE
+select * from tm_base_temp;
+drop table tm_base_temp;
+
+
+--echo #
+--echo # Clean-up.
+--echo #
+drop event e1;
+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-11-11 17:08:24 +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 commit 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-11-11 17:08:24 +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-11-11 17:08:24 +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,15 +3689,22 @@ 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;
+--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 statement ends.
+--echo #
+--echo # Reaping DROP PROCEDURE.
 --reap
 
 --echo # Connection 2
 connection con2;
+--echo # Reaping FTWRL.
 --reap
 UNLOCK TABLES;
 
@@ -4485,7 +4492,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 +4514,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-11-11 17:08:24 +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/event_data_objects.cc'
--- a/sql/event_data_objects.cc	2010-07-27 10:25:53 +0000
+++ b/sql/event_data_objects.cc	2010-11-11 17:08:24 +0000
@@ -290,7 +290,6 @@ Event_basic::load_time_zone(THD *thd, co
 */
 
 Event_queue_element::Event_queue_element():
-  status_changed(FALSE), last_executed_changed(FALSE),
   on_completion(Event_parse_data::ON_COMPLETION_DROP),
   status(Event_parse_data::ENABLED), expression(0), dropped(FALSE),
   execution_count(0)
@@ -539,7 +538,6 @@ Event_queue_element::load_from_row(THD *
                                                    TIME_NO_ZERO_DATE);
     last_executed= my_tz_OFFSET0->TIME_to_gmt_sec(&time,&not_used);
   }
-  last_executed_changed= FALSE;
 
   if ((ptr= get_field(&mem_root, table->field[ET_FIELD_STATUS])) == NullS)
     DBUG_RETURN(TRUE);
@@ -935,7 +933,6 @@ Event_queue_element::compute_next_execut
       DBUG_PRINT("info",("One-time event will be dropped: %d.", dropped));
 
       status= Event_parse_data::DISABLED;
-      status_changed= TRUE;
     }
     goto ret;
   }
@@ -955,7 +952,6 @@ Event_queue_element::compute_next_execut
       dropped= TRUE;
     DBUG_PRINT("info", ("Dropped: %d", dropped));
     status= Event_parse_data::DISABLED;
-    status_changed= TRUE;
 
     goto ret;
   }
@@ -1018,7 +1014,6 @@ Event_queue_element::compute_next_execut
         if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
           dropped= TRUE;
         status= Event_parse_data::DISABLED;
-        status_changed= TRUE;
       }
       else
       {
@@ -1108,7 +1103,6 @@ Event_queue_element::compute_next_execut
           execute_at= 0;
           execute_at_null= TRUE;
           status= Event_parse_data::DISABLED;
-          status_changed= TRUE;
           if (on_completion == Event_parse_data::ON_COMPLETION_DROP)
             dropped= TRUE;
         }
@@ -1144,50 +1138,11 @@ void
 Event_queue_element::mark_last_executed(THD *thd)
 {
   last_executed= (my_time_t) thd->query_start();
-  last_executed_changed= TRUE;
 
   execution_count++;
 }
 
 
-/*
-  Saves status and last_executed_at to the disk if changed.
-
-  SYNOPSIS
-    Event_queue_element::update_timing_fields()
-      thd - thread context
-
-  RETURN VALUE
-    FALSE   OK
-    TRUE    Error while opening mysql.event for writing or during
-            write on disk
-*/
-
-bool
-Event_queue_element::update_timing_fields(THD *thd)
-{
-  Event_db_repository *db_repository= Events::get_db_repository();
-  int ret;
-
-  DBUG_ENTER("Event_queue_element::update_timing_fields");
-
-  DBUG_PRINT("enter", ("name: %*s", (int) name.length, name.str));
-
-  /* No need to update if nothing has changed */
-  if (!(status_changed || last_executed_changed))
-    DBUG_RETURN(0);
-
-  ret= db_repository->update_timing_fields_for_event(thd,
-                                                     dbname, name,
-                                                     last_executed_changed,
-                                                     last_executed,
-                                                     status_changed,
-                                                     (ulonglong) status);
-  last_executed_changed= status_changed= FALSE;
-  DBUG_RETURN(ret);
-}
-
-
 static
 void
 append_datetime(String *buf, Time_zone *time_zone, my_time_t secs,

=== modified file 'sql/event_data_objects.h'
--- a/sql/event_data_objects.h	2010-03-31 14:05:33 +0000
+++ b/sql/event_data_objects.h	2010-11-11 17:08:24 +0000
@@ -82,10 +82,6 @@ protected:
 
 class Event_queue_element : public Event_basic
 {
-protected:
-  bool status_changed;
-  bool last_executed_changed;
-
 public:
   int on_completion;
   int status;
@@ -117,9 +113,6 @@ public:
 
   void
   mark_last_executed(THD *thd);
-
-  bool
-  update_timing_fields(THD *thd);
 };
 
 

=== modified file 'sql/event_db_repository.cc'
--- a/sql/event_db_repository.cc	2010-08-13 08:02:37 +0000
+++ b/sql/event_db_repository.cc	2010-11-11 17:08:24 +0000
@@ -622,6 +622,12 @@ Event_db_repository::create_event(THD *t
   TABLE *table= NULL;
   sp_head *sp= thd->lex->sphead;
   ulong saved_mode= thd->variables.sql_mode;
+  /*
+    Take a savepoint to release only the lock on mysql.event
+    table at the end but keep the global read lock and
+    possible other locks taken by the caller.
+  */
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
 
   DBUG_ENTER("Event_db_repository::create_event");
 
@@ -699,8 +705,8 @@ Event_db_repository::create_event(THD *t
   ret= 0;
 
 end:
-  if (table)
-    close_mysql_tables(thd);
+  close_thread_tables(thd);
+  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
 
   thd->variables.sql_mode= saved_mode;
   DBUG_RETURN(test(ret));
@@ -734,6 +740,12 @@ Event_db_repository::update_event(THD *t
   TABLE *table= NULL;
   sp_head *sp= thd->lex->sphead;
   ulong saved_mode= thd->variables.sql_mode;
+  /*
+    Take a savepoint to release only the lock on mysql.event
+    table at the end but keep the global read lock and
+    possible other locks taken by the caller.
+  */
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
   int ret= 1;
 
   DBUG_ENTER("Event_db_repository::update_event");
@@ -811,8 +823,8 @@ Event_db_repository::update_event(THD *t
   ret= 0;
 
 end:
-  if (table)
-    close_mysql_tables(thd);
+  close_thread_tables(thd);
+  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
 
   thd->variables.sql_mode= saved_mode;
   DBUG_RETURN(test(ret));
@@ -838,6 +850,12 @@ Event_db_repository::drop_event(THD *thd
                                 bool drop_if_exists)
 {
   TABLE *table= NULL;
+  /*
+    Take a savepoint to release only the lock on mysql.event
+    table at the end but keep the global read lock and
+    possible other locks taken by the caller.
+  */
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
   int ret= 1;
 
   DBUG_ENTER("Event_db_repository::drop_event");
@@ -866,8 +884,8 @@ Event_db_repository::drop_event(THD *thd
   ret= 0;
 
 end:
-  if (table)
-    close_mysql_tables(thd);
+  close_thread_tables(thd);
+  thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
 
   DBUG_RETURN(test(ret));
 }
@@ -940,7 +958,7 @@ Event_db_repository::drop_schema_events(
   TABLE *table= NULL;
   READ_RECORD read_record_info;
   enum enum_events_table_field field= ET_FIELD_DB;
-  MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
   DBUG_ENTER("Event_db_repository::drop_schema_events");
   DBUG_PRINT("enter", ("field=%d schema=%s", field, schema.str));
 
@@ -1042,15 +1060,14 @@ Event_db_repository::
 update_timing_fields_for_event(THD *thd,
                                LEX_STRING event_db_name,
                                LEX_STRING event_name,
-                               bool update_last_executed,
                                my_time_t last_executed,
-                               bool update_status,
                                ulonglong status)
 {
   TABLE *table= NULL;
   Field **fields;
   int ret= 1;
   bool save_binlog_row_based;
+  MYSQL_TIME time;
 
   DBUG_ENTER("Event_db_repository::update_timing_fields_for_event");
 
@@ -1075,20 +1092,12 @@ update_timing_fields_for_event(THD *thd,
   /* Don't update create on row update. */
   table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
 
-  if (update_last_executed)
-  {
-    MYSQL_TIME time;
-    my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed);
+  my_tz_OFFSET0->gmt_sec_to_TIME(&time, last_executed);
+  fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
+  fields[ET_FIELD_LAST_EXECUTED]->store_time(&time, MYSQL_TIMESTAMP_DATETIME);
 
-    fields[ET_FIELD_LAST_EXECUTED]->set_notnull();
-    fields[ET_FIELD_LAST_EXECUTED]->store_time(&time,
-                                               MYSQL_TIMESTAMP_DATETIME);
-  }
-  if (update_status)
-  {
-    fields[ET_FIELD_STATUS]->set_notnull();
-    fields[ET_FIELD_STATUS]->store(status, TRUE);
-  }
+  fields[ET_FIELD_STATUS]->set_notnull();
+  fields[ET_FIELD_STATUS]->store(status, TRUE);
 
   if ((ret= table->file->ha_update_row(table->record[1], table->record[0])))
   {

=== modified file 'sql/event_db_repository.h'
--- a/sql/event_db_repository.h	2010-07-27 10:25:53 +0000
+++ b/sql/event_db_repository.h	2010-11-11 17:08:24 +0000
@@ -101,9 +101,7 @@ public:
   update_timing_fields_for_event(THD *thd,
                                  LEX_STRING event_db_name,
                                  LEX_STRING event_name,
-                                 bool update_last_executed,
                                  my_time_t last_executed,
-                                 bool update_status,
                                  ulonglong status);
 public:
   static bool

=== modified file 'sql/event_queue.cc'
--- a/sql/event_queue.cc	2010-10-22 11:58:09 +0000
+++ b/sql/event_queue.cc	2010-11-11 17:08:24 +0000
@@ -17,6 +17,8 @@
 #include "unireg.h"
 #include "event_queue.h"
 #include "event_data_objects.h"
+#include "event_db_repository.h"
+#include "events.h"
 #include "sql_audit.h"
 #include "tztime.h"     // my_tz_find, my_tz_OFFSET0, struct Time_zone
 #include "log.h"        // sql_print_error
@@ -444,7 +446,6 @@ Event_queue::recalculate_activation_time
   for (i= 0; i < queue.elements; i++)
   {
     ((Event_queue_element*)queue_element(&queue, i))->compute_next_execution_time();
-    ((Event_queue_element*)queue_element(&queue, i))->update_timing_fields(thd);
   }
   queue_fix(&queue);
   /*
@@ -567,6 +568,8 @@ Event_queue::get_top_for_execution_if_ti
 {
   bool ret= FALSE;
   *event_name= NULL;
+  my_time_t last_executed;
+  int status;
   DBUG_ENTER("Event_queue::get_top_for_execution_if_time");
 
   LOCK_QUEUE_DATA();
@@ -632,8 +635,14 @@ Event_queue::get_top_for_execution_if_ti
 
     top->execution_count++;
     (*event_name)->dropped= top->dropped;
+    /*
+      Save new values of last_executed timestamp and event status on stack
+      in order to be able to update event description in system table once
+      QUEUE_DATA lock is released.
+    */
+    last_executed= top->last_executed;
+    status= top->status;
 
-    top->update_timing_fields(thd);
     if (top->status == Event_parse_data::DISABLED)
     {
       DBUG_PRINT("info", ("removing from the queue"));
@@ -656,9 +665,16 @@ end:
                       ret, (long) *event_name));
 
   if (*event_name)
+  {
     DBUG_PRINT("info", ("db: %s  name: %s",
                         (*event_name)->dbname.str, (*event_name)->name.str));
 
+    Event_db_repository *db_repository= Events::get_db_repository();
+    (void) db_repository->update_timing_fields_for_event(thd,
+                            (*event_name)->dbname, (*event_name)->name,
+                            last_executed, (ulonglong) status);
+  }
+
   DBUG_RETURN(ret);
 }
 

=== modified file 'sql/events.cc'
--- a/sql/events.cc	2010-08-26 10:01:43 +0000
+++ b/sql/events.cc	2010-11-11 17:08:24 +0000
@@ -30,6 +30,7 @@
 #include "event_scheduler.h"
 #include "sp_head.h" // for Stored_program_creation_ctx
 #include "set_var.h"
+#include "lock.h"   // lock_object_name
 
 /**
   @addtogroup Event_Scheduler
@@ -77,7 +78,6 @@ Event_queue *Events::event_queue;
 Event_scheduler *Events::scheduler;
 Event_db_repository *Events::db_repository;
 ulong Events::opt_event_scheduler= Events::EVENTS_OFF;
-mysql_mutex_t Events::LOCK_event_metadata;
 bool Events::check_system_tables_error= FALSE;
 
 
@@ -340,7 +340,9 @@ Events::create_event(THD *thd, Event_par
   if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
     thd->clear_current_stmt_binlog_format_row();
 
-  mysql_mutex_lock(&LOCK_event_metadata);
+  if (lock_object_name(thd, MDL_key::EVENT,
+                       parse_data->dbname.str, parse_data->name.str))
+    DBUG_RETURN(TRUE);
 
   /* On error conditions my_error() is called so no need to handle here */
   if (!(ret= db_repository->create_event(thd, parse_data, if_not_exists)))
@@ -388,7 +390,6 @@ Events::create_event(THD *thd, Event_par
       }
     }
   }
-  mysql_mutex_unlock(&LOCK_event_metadata);
   /* Restore the state of binlog format */
   DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
   if (save_binlog_row_based)
@@ -472,7 +473,9 @@ Events::update_event(THD *thd, Event_par
   if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
     thd->clear_current_stmt_binlog_format_row();
 
-  mysql_mutex_lock(&LOCK_event_metadata);
+  if (lock_object_name(thd, MDL_key::EVENT,
+                       parse_data->dbname.str, parse_data->name.str))
+    DBUG_RETURN(TRUE);
 
   /* On error conditions my_error() is called so no need to handle here */
   if (!(ret= db_repository->update_event(thd, parse_data,
@@ -502,7 +505,6 @@ Events::update_event(THD *thd, Event_par
       ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
     }
   }
-  mysql_mutex_unlock(&LOCK_event_metadata);
   /* Restore the state of binlog format */
   DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
   if (save_binlog_row_based)
@@ -556,7 +558,9 @@ Events::drop_event(THD *thd, LEX_STRING 
   if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
     thd->clear_current_stmt_binlog_format_row();
 
-  mysql_mutex_lock(&LOCK_event_metadata);
+  if (lock_object_name(thd, MDL_key::EVENT,
+                       dbname.str, name.str))
+    DBUG_RETURN(TRUE);
   /* On error conditions my_error() is called so no need to handle here */
   if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists)))
   {
@@ -566,7 +570,6 @@ Events::drop_event(THD *thd, LEX_STRING 
     DBUG_ASSERT(thd->query() && thd->query_length());
     ret= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
   }
-  mysql_mutex_unlock(&LOCK_event_metadata);
   /* Restore the state of binlog format */
   DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
   if (save_binlog_row_based)
@@ -595,15 +598,12 @@ Events::drop_schema_events(THD *thd, cha
   DBUG_PRINT("enter", ("dropping events from %s", db));
 
   /*
-    sic: no check if the scheduler is disabled or system tables
+    Sic: no check if the scheduler is disabled or system tables
     are damaged, as intended.
   */
-
-  mysql_mutex_lock(&LOCK_event_metadata);
   if (event_queue)
     event_queue->drop_schema_events(thd, db_lex);
   db_repository->drop_schema_events(thd, db_lex);
-  mysql_mutex_unlock(&LOCK_event_metadata);
 
   DBUG_VOID_RETURN;
 }
@@ -915,12 +915,11 @@ Events::deinit()
 }
 
 #ifdef HAVE_PSI_INTERFACE
-PSI_mutex_key key_LOCK_event_metadata, key_LOCK_event_queue,
+PSI_mutex_key key_LOCK_event_queue,
               key_event_scheduler_LOCK_scheduler_state;
 
 static PSI_mutex_info all_events_mutexes[]=
 {
-  { &key_LOCK_event_metadata, "LOCK_event_metadata", PSI_FLAG_GLOBAL},
   { &key_LOCK_event_queue, "LOCK_event_queue", PSI_FLAG_GLOBAL},
   { &key_event_scheduler_LOCK_scheduler_state, "Event_scheduler::LOCK_scheduler_state", PSI_FLAG_GLOBAL}
 };
@@ -974,23 +973,6 @@ Events::init_mutexes()
 #ifdef HAVE_PSI_INTERFACE
   init_events_psi_keys();
 #endif
-
-  mysql_mutex_init(key_LOCK_event_metadata,
-                   &LOCK_event_metadata, MY_MUTEX_INIT_FAST);
-}
-
-
-/*
-  Destroys Events mutexes
-
-  SYNOPSIS
-    Events::destroy_mutexes()
-*/
-
-void
-Events::destroy_mutexes()
-{
-  mysql_mutex_destroy(&LOCK_event_metadata);
 }
 
 

=== modified file 'sql/events.h'
--- a/sql/events.h	2010-08-26 10:01:43 +0000
+++ b/sql/events.h	2010-11-11 17:08:24 +0000
@@ -26,8 +26,7 @@
 */
 
 #ifdef HAVE_PSI_INTERFACE
-extern PSI_mutex_key key_LOCK_event_metadata,
-                     key_event_scheduler_LOCK_scheduler_state;
+extern PSI_mutex_key key_event_scheduler_LOCK_scheduler_state;
 extern PSI_cond_key key_event_scheduler_COND_state;
 extern PSI_thread_key key_thread_event_scheduler, key_thread_event_worker;
 #endif /* HAVE_PSI_INTERFACE */
@@ -79,7 +78,6 @@ public:
   enum enum_opt_event_scheduler { EVENTS_OFF, EVENTS_ON, EVENTS_DISABLED };
   /* Protected using LOCK_global_system_variables only. */
   static ulong opt_event_scheduler;
-  static mysql_mutex_t LOCK_event_metadata;
   static bool check_if_system_tables_error();
   static bool start();
   static bool stop();

=== modified file 'sql/ha_ndbcluster.cc'
--- a/sql/ha_ndbcluster.cc	2010-10-19 22:51:34 +0000
+++ b/sql/ha_ndbcluster.cc	2010-11-11 17:08:24 +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-18 11:27:52 +0000
+++ b/sql/handler.cc	2010-11-11 17:08:24 +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_SUICIDE(););
 
@@ -1166,11 +1166,27 @@ 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 a metadata lock which will ensure that COMMIT is blocked
+        by an active FLUSH TABLES WITH READ LOCK (and vice versa:
+        COMMIT in progress blocks FTWRL).
+
+        We allow the owner of FTWRL to COMMIT; we assume that it knows
+        what it does.
+      */
+      mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE,
+                       MDL_EXPLICIT);
+
+      if (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 +1241,16 @@ int ha_commit_trans(THD *thd, bool all)
     DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE(););
     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)
+    {
+      /*
+        We do not always immediately release transactional locks
+        after ha_commit_trans() (see uses of ha_enable_transaction()),
+        thus we release the commit blocker lock as soon as it's
+        not needed.
+      */
+      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-10-22 11:58:09 +0000
+++ b/sql/lock.cc	2010-11-11 17:08:24 +0000
@@ -777,8 +777,11 @@ bool lock_schema_name(THD *thd, const ch
     return TRUE;
   }
 
-  global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
-  mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE);
+  if (thd->global_read_lock.can_acquire_protection())
+    return TRUE;
+  global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+                      MDL_STATEMENT);
+  mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE, MDL_TRANSACTION);
 
   mdl_requests.push_front(&mdl_request);
   mdl_requests.push_front(&global_request);
@@ -793,13 +796,13 @@ bool lock_schema_name(THD *thd, const ch
 
 
 /**
-  Obtain an exclusive metadata lock on the stored routine name.
+  Obtain an exclusive metadata lock on an object name.
 
   @param thd         Thread handle.
-  @param is_function Stored routine type (only functions or procedures
-                     are name-locked.
-  @param db          The schema the routine belongs to.
-  @param name        Routine name.
+  @param mdl_type    Object type (currently functions, procedures
+                     and events can be name-locked).
+  @param db          The schema the object belongs to.
+  @param name        Object name in the schema.
 
   This function assumes that no metadata locks were acquired
   before calling it. Additionally, it cannot be called while
@@ -815,12 +818,9 @@ bool lock_schema_name(THD *thd, const ch
                  or this connection was killed.
 */
 
-bool lock_routine_name(THD *thd, bool is_function,
+bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
                        const char *db, const char *name)
 {
-  MDL_key::enum_mdl_namespace mdl_type= (is_function ?
-                                         MDL_key::FUNCTION :
-                                         MDL_key::PROCEDURE);
   MDL_request_list mdl_requests;
   MDL_request global_request;
   MDL_request schema_request;
@@ -836,9 +836,13 @@ 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);
-  schema_request.init(MDL_key::SCHEMA, db, "", MDL_INTENTION_EXCLUSIVE);
-  mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE);
+  if (thd->global_read_lock.can_acquire_protection())
+    return TRUE;
+  global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+                      MDL_STATEMENT);
+  schema_request.init(MDL_key::SCHEMA, db, "", MDL_INTENTION_EXCLUSIVE,
+                      MDL_TRANSACTION);
+  mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE, MDL_TRANSACTION);
 
   mdl_requests.push_front(&mdl_request);
   mdl_requests.push_front(&schema_request);
@@ -888,45 +892,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 +943,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,84 +963,17 @@ 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);
-
-    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++;
-
-#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.
-
-      The sync point is after enter_cond() so that proc_info is
-      available immediately after the sync point sends a SIGNAL. This
-      can make tests more reliable.
-
-      The sync point is before the loop so that it is executed only once.
-    */
-    if (protect_against_global_read_lock && !thd->killed)
-    {
-      DEBUG_SYNC(thd, "wait_lock_global_read_lock");
-    }
-#endif /* defined(ENABLED_DEBUG_SYNC) */
-
-    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));
-    mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED);
+    mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED, MDL_EXPLICIT);
 
     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
@@ -1088,166 +999,22 @@ 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.
-
-  @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.
-*/
-
-#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)
-{
-  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 (!abort_on_refresh)
-      m_protection_count++;
-    DBUG_RETURN(FALSE);
-  }
-  /*
-    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);
-
-  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);
-}
-
-
-/**
-  Release protection against global read lock and restart
-  global read lock waiters.
-
-  Should only be called if we have protection against global read lock.
-
-  See also "Handling of global read locks" above.
-
-  @param thd     Reference to thread.
-*/
-
-void Global_read_lock::start_waiting_global_read_lock(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);
-  DBUG_VOID_RETURN;
-}
-
 
 /**
   Make global read lock also block commits.
@@ -1266,8 +1033,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
@@ -1275,42 +1041,32 @@ 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, MDL_EXPLICIT);
+
+  if (thd->mdl_context.acquire_lock(&mdl_request,
+                                    thd->variables.lock_wait_timeout))
+    DBUG_RETURN(TRUE);
+
+  m_mdl_blocks_commits_lock= mdl_request.ticket;
+  m_state= GRL_ACQUIRED_AND_BLOCKS_COMMIT;
+
+  DBUG_RETURN(FALSE);
 }
 
 
 /**
-  Broadcast COND_global_read_lock.
+  Set explicit duration for metadata locks which are used to implement GRL.
 
-  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.
+  @param thd     Reference to thread.
 */
 
-void broadcast_refresh(void)
+void Global_read_lock::set_explicit_lock_duration(THD *thd)
 {
-  mysql_cond_broadcast(&COND_global_read_lock);
+  if (m_mdl_global_shared_lock)
+    thd->mdl_context.set_lock_duration(m_mdl_global_shared_lock, MDL_EXPLICIT);
+  if (m_mdl_blocks_commits_lock)
+    thd->mdl_context.set_lock_duration(m_mdl_blocks_commits_lock, MDL_EXPLICIT);
 }
 
 /**

=== modified file 'sql/lock.h'
--- a/sql/lock.h	2010-08-13 09:51:48 +0000
+++ b/sql/lock.h	2010-11-11 17:08:24 +0000
@@ -2,6 +2,7 @@
 #define LOCK_INCLUDED
 
 #include "thr_lock.h"                           /* thr_lock_type */
+#include "mdl.h"
 
 // Forward declarations
 struct TABLE;
@@ -18,11 +19,10 @@ 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 */
-bool lock_routine_name(THD *thd, bool is_function, const char *db,
-                       const char *name);
+bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type,
+                      const char *db, const char *name);
 
 #endif /* LOCK_INCLUDED */

=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc	2010-10-23 13:09:27 +0000
+++ b/sql/log_event.cc	2010-11-11 17:08:24 +0000
@@ -4961,6 +4961,8 @@ error:
   */
   if (! thd->in_multi_stmt_transaction_mode())
     thd->mdl_context.release_transactional_locks();
+  else
+    thd->mdl_context.release_statement_locks();
 
   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-11-11 17:08:24 +0000
@@ -68,8 +68,6 @@ static void init_mdl_psi_keys(void)
 }
 #endif /* HAVE_PSI_INTERFACE */
 
-void notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket);
-
 
 /**
   Thread state names to be used in case when we have to wait on resource
@@ -78,12 +76,14 @@ 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
+  "Waiting for trigger metadata lock",
+  "Waiting for event metadata lock",
+  "Waiting for commit lock"
 };
 
 static bool mdl_initialized= 0;
@@ -100,7 +100,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 +109,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;
 };
 
 
@@ -354,18 +357,6 @@ public:
 
   inline static MDL_lock *create(const MDL_key *key);
 
-  void notify_shared_locks(MDL_context *ctx)
-  {
-    Ticket_iterator it(m_granted);
-    MDL_ticket *conflicting_ticket;
-
-    while ((conflicting_ticket= it++))
-    {
-      if (conflicting_ticket->get_ctx() != ctx)
-        notify_shared_lock(ctx->get_thd(), conflicting_ticket);
-    }
-  }
-
   void reschedule_waiters();
 
   void remove_ticket(Ticket_list MDL_lock::*queue, MDL_ticket *ticket);
@@ -373,6 +364,9 @@ public:
   bool visit_subgraph(MDL_ticket *waiting_ticket,
                       MDL_wait_for_graph_visitor *gvisitor);
 
+  virtual bool needs_notification(const MDL_ticket *ticket) const = 0;
+  virtual void notify_conflicting_locks(MDL_context *ctx) = 0;
+
   /** List of granted tickets for this lock. */
   Ticket_list m_granted;
   /** Tickets for contexts waiting to acquire a lock. */
@@ -442,6 +436,11 @@ public:
   {
     return m_waiting_incompatible;
   }
+  virtual bool needs_notification(const MDL_ticket *ticket) const
+  {
+    return (ticket->get_type() == MDL_SHARED);
+  }
+  virtual void notify_conflicting_locks(MDL_context *ctx);
 
 private:
   static const bitmap_t m_granted_incompatible[MDL_TYPE_END];
@@ -469,6 +468,11 @@ public:
   {
     return m_waiting_incompatible;
   }
+  virtual bool needs_notification(const MDL_ticket *ticket) const
+  {
+    return ticket->is_upgradable_or_exclusive();
+  }
+  virtual void notify_conflicting_locks(MDL_context *ctx);
 
 private:
   static const bitmap_t m_granted_incompatible[MDL_TYPE_END];
@@ -536,9 +540,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 +561,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 +580,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 +612,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 +685,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
@@ -719,8 +731,7 @@ void MDL_map::remove(MDL_lock *lock)
 */
 
 MDL_context::MDL_context()
-  :m_trans_sentinel(NULL),
-  m_thd(NULL),
+  : m_thd(NULL),
   m_needs_thr_lock_abort(FALSE),
   m_waiting_for(NULL)
 {
@@ -742,7 +753,9 @@ MDL_context::MDL_context()
 
 void MDL_context::destroy()
 {
-  DBUG_ASSERT(m_tickets.is_empty());
+  DBUG_ASSERT(m_tickets[MDL_STATEMENT].is_empty() &&
+              m_tickets[MDL_TRANSACTION].is_empty() &&
+              m_tickets[MDL_EXPLICIT].is_empty());
 
   mysql_prlock_destroy(&m_LOCK_waiting_for);
 }
@@ -771,10 +784,12 @@ void MDL_context::destroy()
 void MDL_request::init(MDL_key::enum_mdl_namespace mdl_namespace,
                        const char *db_arg,
                        const char *name_arg,
-                       enum enum_mdl_type mdl_type_arg)
+                       enum_mdl_type mdl_type_arg,
+                       enum_mdl_duration mdl_duration_arg)
 {
   key.mdl_key_init(mdl_namespace, db_arg, name_arg);
   type= mdl_type_arg;
+  duration= mdl_duration_arg;
   ticket= NULL;
 }
 
@@ -789,49 +804,17 @@ void MDL_request::init(MDL_key::enum_mdl
 */
 
 void MDL_request::init(const MDL_key *key_arg,
-                       enum enum_mdl_type mdl_type_arg)
+                       enum_mdl_type mdl_type_arg,
+                       enum_mdl_duration mdl_duration_arg)
 {
   key.mdl_key_init(key_arg);
   type= mdl_type_arg;
+  duration= mdl_duration_arg;
   ticket= NULL;
 }
 
 
 /**
-  Allocate and initialize one lock request.
-
-  Same as mdl_init_lock(), but allocates the lock and the key buffer
-  on a memory root. Necessary to lock ad-hoc tables, e.g.
-  mysql.* tables of grant and data dictionary subsystems.
-
-  @param  mdl_namespace  Id of namespace of object to be locked
-  @param  db             Name of database to which object belongs
-  @param  name           Name of of object
-  @param  root           MEM_ROOT on which object should be allocated
-
-  @note The allocated lock request will have MDL_SHARED type.
-
-  @retval 0      Error if out of memory
-  @retval non-0  Pointer to an object representing a lock request
-*/
-
-MDL_request *
-MDL_request::create(MDL_key::enum_mdl_namespace mdl_namespace, const char *db,
-                    const char *name, enum_mdl_type mdl_type,
-                    MEM_ROOT *root)
-{
-  MDL_request *mdl_request;
-
-  if (!(mdl_request= (MDL_request*) alloc_root(root, sizeof(MDL_request))))
-    return NULL;
-
-  mdl_request->init(mdl_namespace, db, name, mdl_type);
-
-  return mdl_request;
-}
-
-
-/**
   Auxiliary functions needed for creation/destruction of MDL_lock objects.
 
   @note Also chooses an MDL_lock descendant appropriate for object namespace.
@@ -846,6 +829,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);
@@ -867,9 +851,17 @@ void MDL_lock::destroy(MDL_lock *lock)
         on memory allocation by reusing released objects.
 */
 
-MDL_ticket *MDL_ticket::create(MDL_context *ctx_arg, enum_mdl_type type_arg)
+MDL_ticket *MDL_ticket::create(MDL_context *ctx_arg, enum_mdl_type type_arg
+#ifndef DBUG_OFF
+                               , enum_mdl_duration duration_arg
+#endif
+                               )
 {
-  return new MDL_ticket(ctx_arg, type_arg);
+  return new MDL_ticket(ctx_arg, type_arg
+#ifndef DBUG_OFF
+                        , duration_arg
+#endif
+                        );
 }
 
 
@@ -1438,13 +1430,11 @@ bool MDL_ticket::is_incompatible_when_wa
 /**
   Check whether the context already holds a compatible lock ticket
   on an object.
-  Start searching the transactional locks. If not
-  found in the list of transactional locks, look at LOCK TABLES
-  and HANDLER locks.
+  Start searching from list of locks for the same duration as lock
+  being requested. If not look at lists for other durations.
 
   @param mdl_request  Lock request object for lock to be acquired
-  @param[out] is_transactional FALSE if we pass beyond m_trans_sentinel
-                      while searching for ticket, otherwise TRUE.
+  @param[out] result_duration  Duration of lock which was found.
 
   @note Tickets which correspond to lock types "stronger" than one
         being requested are also considered compatible.
@@ -1454,24 +1444,28 @@ bool MDL_ticket::is_incompatible_when_wa
 
 MDL_ticket *
 MDL_context::find_ticket(MDL_request *mdl_request,
-                         bool *is_transactional)
+                         enum_mdl_duration *result_duration)
 {
   MDL_ticket *ticket;
-  Ticket_iterator it(m_tickets);
-
-  *is_transactional= TRUE;
+  int i;
 
-  while ((ticket= it++))
+  for (i= 0; i < MDL_DURATION_END; i++)
   {
-    if (ticket == m_trans_sentinel)
-      *is_transactional= FALSE;
+    enum_mdl_duration duration= (enum_mdl_duration)((mdl_request->duration+i) %
+                                                    MDL_DURATION_END);
+    Ticket_iterator it(m_tickets[duration]);
 
-    if (mdl_request->key.is_equal(&ticket->m_lock->key) &&
-        ticket->has_stronger_or_equal_type(mdl_request->type))
-      break;
+    while ((ticket= it++))
+    {
+      if (mdl_request->key.is_equal(&ticket->m_lock->key) &&
+          ticket->has_stronger_or_equal_type(mdl_request->type))
+      {
+        *result_duration= duration;
+        return ticket;
+      }
+    }
   }
-
-  return ticket;
+  return NULL;
 }
 
 
@@ -1549,7 +1543,7 @@ MDL_context::try_acquire_lock_impl(MDL_r
   MDL_lock *lock;
   MDL_key *key= &mdl_request->key;
   MDL_ticket *ticket;
-  bool is_transactional;
+  enum_mdl_duration found_duration;
 
   DBUG_ASSERT(mdl_request->type != MDL_EXCLUSIVE ||
               is_lock_owner(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE));
@@ -1563,7 +1557,7 @@ MDL_context::try_acquire_lock_impl(MDL_r
     Check whether the context already holds a shared lock on the object,
     and if so, grant the request.
   */
-  if ((ticket= find_ticket(mdl_request, &is_transactional)))
+  if ((ticket= find_ticket(mdl_request, &found_duration)))
   {
     DBUG_ASSERT(ticket->m_lock);
     DBUG_ASSERT(ticket->has_stronger_or_equal_type(mdl_request->type));
@@ -1585,7 +1579,9 @@ MDL_context::try_acquire_lock_impl(MDL_r
       a different alias.
     */
     mdl_request->ticket= ticket;
-    if (!is_transactional && clone_ticket(mdl_request))
+    if ((found_duration != mdl_request->duration ||
+         mdl_request->duration == MDL_EXPLICIT) &&
+        clone_ticket(mdl_request))
     {
       /* Clone failed. */
       mdl_request->ticket= NULL;
@@ -1594,7 +1590,11 @@ MDL_context::try_acquire_lock_impl(MDL_r
     return FALSE;
   }
 
-  if (!(ticket= MDL_ticket::create(this, mdl_request->type)))
+  if (!(ticket= MDL_ticket::create(this, mdl_request->type
+#ifndef DBUG_OFF
+                                   , mdl_request->duration
+#endif
+                                   )))
     return TRUE;
 
   /* The below call implicitly locks MDL_lock::m_rwlock on success. */
@@ -1612,7 +1612,7 @@ MDL_context::try_acquire_lock_impl(MDL_r
 
     mysql_prlock_unlock(&lock->m_rwlock);
 
-    m_tickets.push_front(ticket);
+    m_tickets[mdl_request->duration].push_front(ticket);
 
     mdl_request->ticket= ticket;
   }
@@ -1647,7 +1647,11 @@ MDL_context::clone_ticket(MDL_request *m
     we effectively downgrade the cloned lock to the level of
     the request.
   */
-  if (!(ticket= MDL_ticket::create(this, mdl_request->type)))
+  if (!(ticket= MDL_ticket::create(this, mdl_request->type
+#ifndef DBUG_OFF
+                                   , mdl_request->duration
+#endif
+                                   )))
     return TRUE;
 
   /* clone() is not supposed to be used to get a stronger lock. */
@@ -1660,36 +1664,74 @@ MDL_context::clone_ticket(MDL_request *m
   ticket->m_lock->m_granted.add_ticket(ticket);
   mysql_prlock_unlock(&ticket->m_lock->m_rwlock);
 
-  m_tickets.push_front(ticket);
+  m_tickets[mdl_request->duration].push_front(ticket);
 
   return FALSE;
 }
 
 
 /**
-  Notify a thread holding a shared metadata lock which
-  conflicts with a pending exclusive lock.
+  Notify threads holding a shared metadata locks on object which
+  conflict with a pending X, SNW or SNRW lock.
 
-  @param thd               Current thread context
-  @param conflicting_ticket  Conflicting metadata lock
+  @param  ctx  MDL_context for current thread.
 */
 
-void notify_shared_lock(THD *thd, MDL_ticket *conflicting_ticket)
+void MDL_object_lock::notify_conflicting_locks(MDL_context *ctx)
 {
-  /* Only try to abort locks on which we back off. */
-  if (conflicting_ticket->get_type() < MDL_SHARED_NO_WRITE)
+  Ticket_iterator it(m_granted);
+  MDL_ticket *conflicting_ticket;
+
+  while ((conflicting_ticket= it++))
   {
-    MDL_context *conflicting_ctx= conflicting_ticket->get_ctx();
-    THD *conflicting_thd= conflicting_ctx->get_thd();
-    DBUG_ASSERT(thd != conflicting_thd); /* Self-deadlock */
+    /* Only try to abort locks on which we back off. */
+    if (conflicting_ticket->get_ctx() != ctx &&
+        conflicting_ticket->get_type() < MDL_SHARED_NO_WRITE)
 
-    /*
-      If thread which holds conflicting lock is waiting on table-level
-      lock or some other non-MDL resource we might need to wake it up
-      by calling code outside of MDL.
-    */
-    mysql_notify_thread_having_shared_lock(thd, conflicting_thd,
-                               conflicting_ctx->get_needs_thr_lock_abort());
+    {
+      MDL_context *conflicting_ctx= conflicting_ticket->get_ctx();
+
+      /*
+        If thread which holds conflicting lock is waiting on table-level
+        lock or some other non-MDL resource we might need to wake it up
+        by calling code outside of MDL.
+      */
+      mysql_notify_thread_having_shared_lock(ctx->get_thd(),
+                                 conflicting_ctx->get_thd(),
+                                 conflicting_ctx->get_needs_thr_lock_abort());
+    }
+  }
+}
+
+
+/**
+  Notify threads holding scoped IX locks which conflict with a pending S lock.
+
+  @param  ctx  MDL_context for current thread.
+*/
+
+void MDL_scoped_lock::notify_conflicting_locks(MDL_context *ctx)
+{
+  Ticket_iterator it(m_granted);
+  MDL_ticket *conflicting_ticket;
+
+  while ((conflicting_ticket= it++))
+  {
+    if (conflicting_ticket->get_ctx() != ctx &&
+        conflicting_ticket->get_type() == MDL_INTENTION_EXCLUSIVE)
+
+    {
+      MDL_context *conflicting_ctx= conflicting_ticket->get_ctx();
+
+      /*
+        Thread which holds global IX lock can be a handler thread for
+        insert delayed. We need to kill such threads in order to get
+        global shared lock. We do this my calling code outside of MDL.
+      */
+      mysql_notify_thread_having_shared_lock(ctx->get_thd(),
+                                 conflicting_ctx->get_thd(),
+                                 conflicting_ctx->get_needs_thr_lock_abort());
+    }
   }
 }
 
@@ -1747,8 +1789,8 @@ MDL_context::acquire_lock(MDL_request *m
   */
   m_wait.reset_status();
 
-  if (ticket->is_upgradable_or_exclusive())
-    lock->notify_shared_locks(this);
+  if (lock->needs_notification(ticket))
+    lock->notify_conflicting_locks(this);
 
   mysql_prlock_unlock(&lock->m_rwlock);
 
@@ -1759,7 +1801,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);
@@ -1775,7 +1817,7 @@ MDL_context::acquire_lock(MDL_request *m
         break;
 
       mysql_prlock_wrlock(&lock->m_rwlock);
-      lock->notify_shared_locks(this);
+      lock->notify_conflicting_locks(this);
       mysql_prlock_unlock(&lock->m_rwlock);
       set_timespec(abs_shortwait, 1);
     }
@@ -1818,7 +1860,7 @@ MDL_context::acquire_lock(MDL_request *m
   */
   DBUG_ASSERT(wait_status == MDL_wait::GRANTED);
 
-  m_tickets.push_front(ticket);
+  m_tickets[mdl_request->duration].push_front(ticket);
 
   mdl_request->ticket= ticket;
 
@@ -1859,7 +1901,7 @@ bool MDL_context::acquire_locks(MDL_requ
 {
   MDL_request_list::Iterator it(*mdl_requests);
   MDL_request **sort_buf, **p_req;
-  MDL_ticket *mdl_svp= mdl_savepoint();
+  MDL_savepoint mdl_svp= mdl_savepoint();
   ssize_t req_count= static_cast<ssize_t>(mdl_requests->elements());
 
   if (req_count == 0)
@@ -1928,7 +1970,7 @@ MDL_context::upgrade_shared_lock_to_excl
                                               ulong lock_wait_timeout)
 {
   MDL_request mdl_xlock_request;
-  MDL_ticket *mdl_svp= mdl_savepoint();
+  MDL_savepoint mdl_svp= mdl_savepoint();
   bool is_new_ticket;
 
   DBUG_ENTER("MDL_ticket::upgrade_shared_lock_to_exclusive");
@@ -1945,7 +1987,8 @@ MDL_context::upgrade_shared_lock_to_excl
   DBUG_ASSERT(mdl_ticket->m_type == MDL_SHARED_NO_WRITE ||
               mdl_ticket->m_type == MDL_SHARED_NO_READ_WRITE);
 
-  mdl_xlock_request.init(&mdl_ticket->m_lock->key, MDL_EXCLUSIVE);
+  mdl_xlock_request.init(&mdl_ticket->m_lock->key, MDL_EXCLUSIVE,
+                         MDL_TRANSACTION);
 
   if (acquire_lock(&mdl_xlock_request, lock_wait_timeout))
     DBUG_RETURN(TRUE);
@@ -1969,7 +2012,7 @@ MDL_context::upgrade_shared_lock_to_excl
 
   if (is_new_ticket)
   {
-    m_tickets.remove(mdl_xlock_request.ticket);
+    m_tickets[MDL_TRANSACTION].remove(mdl_xlock_request.ticket);
     MDL_ticket::destroy(mdl_xlock_request.ticket);
   }
 
@@ -2230,10 +2273,12 @@ void MDL_context::find_deadlock()
 /**
   Release lock.
 
-  @param ticket Ticket for lock to be released.
+  @param duration Lock duration.
+  @param ticket   Ticket for lock to be released.
+
 */
 
-void MDL_context::release_lock(MDL_ticket *ticket)
+void MDL_context::release_lock(enum_mdl_duration duration, MDL_ticket *ticket)
 {
   MDL_lock *lock= ticket->m_lock;
   DBUG_ENTER("MDL_context::release_lock");
@@ -2243,12 +2288,9 @@ void MDL_context::release_lock(MDL_ticke
   DBUG_ASSERT(this == ticket->get_ctx());
   mysql_mutex_assert_not_owner(&LOCK_open);
 
-  if (ticket == m_trans_sentinel)
-    m_trans_sentinel= ++Ticket_list::Iterator(m_tickets, ticket);
-
   lock->remove_ticket(&MDL_lock::m_granted, ticket);
 
-  m_tickets.remove(ticket);
+  m_tickets[duration].remove(ticket);
   MDL_ticket::destroy(ticket);
 
   DBUG_VOID_RETURN;
@@ -2256,50 +2298,56 @@ void MDL_context::release_lock(MDL_ticke
 
 
 /**
+  Release lock with explicit duration.
+
+  @param ticket   Ticket for lock to be released.
+
+*/
+
+void MDL_context::release_lock(MDL_ticket *ticket)
+{
+  DBUG_ASSERT(ticket->m_duration == MDL_EXPLICIT);
+
+  release_lock(MDL_EXPLICIT, ticket);
+}
+
+
+/**
   Release all locks associated with the context. If the sentinel
   is not NULL, do not release locks stored in the list after and
   including the sentinel.
 
-  Transactional locks are added to the beginning of the list, i.e.
-  stored in reverse temporal order. This allows to employ this
-  function to:
+  Statement and transactional locks are added to the beginning of
+  the corresponding lists, i.e. stored in reverse temporal order.
+  This allows to employ this function to:
   - back off in case of a lock conflict.
-  - release all locks in the end of a transaction
+  - release all locks in the end of a statment or transaction
   - rollback to a savepoint.
-
-  The sentinel semantics is used to support LOCK TABLES
-  mode and HANDLER statements: locks taken by these statements
-  survive COMMIT, ROLLBACK, ROLLBACK TO SAVEPOINT.
 */
 
-void MDL_context::release_locks_stored_before(MDL_ticket *sentinel)
+void MDL_context::release_locks_stored_before(enum_mdl_duration duration,
+                                              MDL_ticket *sentinel)
 {
   MDL_ticket *ticket;
-  Ticket_iterator it(m_tickets);
+  Ticket_iterator it(m_tickets[duration]);
   DBUG_ENTER("MDL_context::release_locks_stored_before");
 
-  if (m_tickets.is_empty())
+  if (m_tickets[duration].is_empty())
     DBUG_VOID_RETURN;
 
   while ((ticket= it++) && ticket != sentinel)
   {
     DBUG_PRINT("info", ("found lock to release ticket=%p", ticket));
-    release_lock(ticket);
+    release_lock(duration, ticket);
   }
-  /*
-    If all locks were released, then the sentinel was not present
-    in the list. It must never happen because the sentinel was
-    bogus, i.e. pointed to a ticket that no longer exists.
-  */
-  DBUG_ASSERT(! m_tickets.is_empty() || sentinel == NULL);
 
   DBUG_VOID_RETURN;
 }
 
 
 /**
-  Release all locks in the context which correspond to the same name/
-  object as this lock request.
+  Release all explicit locks in the context which correspond to the
+  same name/object as this lock request.
 
   @param ticket    One of the locks for the name/object for which all
                    locks should be released.
@@ -2312,17 +2360,13 @@ void MDL_context::release_all_locks_for_
 
   /* Remove matching lock tickets from the context. */
   MDL_ticket *ticket;
-  Ticket_iterator it_ticket(m_tickets);
+  Ticket_iterator it_ticket(m_tickets[MDL_EXPLICIT]);
 
   while ((ticket= it_ticket++))
   {
     DBUG_ASSERT(ticket->m_lock);
-    /*
-      We rarely have more than one ticket in this loop,
-      let's not bother saving on pthread_cond_broadcast().
-    */
     if (ticket->m_lock == lock)
-      release_lock(ticket);
+      release_lock(MDL_EXPLICIT, ticket);
   }
 }
 
@@ -2377,9 +2421,10 @@ MDL_context::is_lock_owner(MDL_key::enum
                            enum_mdl_type mdl_type)
 {
   MDL_request mdl_request;
-  bool is_transactional_unused;
-  mdl_request.init(mdl_namespace, db, name, mdl_type);
-  MDL_ticket *ticket= find_ticket(&mdl_request, &is_transactional_unused);
+  enum_mdl_duration not_unused;
+  /* We don't care about exact duration of lock here. */
+  mdl_request.init(mdl_namespace, db, name, mdl_type, MDL_TRANSACTION);
+  MDL_ticket *ticket= find_ticket(&mdl_request, &not_unused);
 
   DBUG_ASSERT(ticket == NULL || ticket->m_lock);
 
@@ -2408,18 +2453,15 @@ bool MDL_ticket::has_pending_conflicting
   @note It's safe to iterate and unlock any locks after taken after this
         savepoint because other statements that take other special locks
         cause a implicit commit (ie LOCK TABLES).
-
-  @param mdl_savepont  The last acquired MDL lock when the
-                       savepoint was set.
 */
 
-void MDL_context::rollback_to_savepoint(MDL_ticket *mdl_savepoint)
+void MDL_context::rollback_to_savepoint(const MDL_savepoint &mdl_savepoint)
 {
   DBUG_ENTER("MDL_context::rollback_to_savepoint");
 
   /* If savepoint is NULL, it is from the start of the transaction. */
-  release_locks_stored_before(mdl_savepoint ?
-                              mdl_savepoint : m_trans_sentinel);
+  release_locks_stored_before(MDL_STATEMENT, mdl_savepoint.m_stmt_ticket);
+  release_locks_stored_before(MDL_TRANSACTION, mdl_savepoint.m_trans_ticket);
 
   DBUG_VOID_RETURN;
 }
@@ -2437,65 +2479,150 @@ void MDL_context::rollback_to_savepoint(
 void MDL_context::release_transactional_locks()
 {
   DBUG_ENTER("MDL_context::release_transactional_locks");
-  release_locks_stored_before(m_trans_sentinel);
+  release_locks_stored_before(MDL_STATEMENT, NULL);
+  release_locks_stored_before(MDL_TRANSACTION, NULL);
+  DBUG_VOID_RETURN;
+}
+
+
+void MDL_context::release_statement_locks()
+{
+  DBUG_ENTER("MDL_context::release_transactional_locks");
+  release_locks_stored_before(MDL_STATEMENT, NULL);
   DBUG_VOID_RETURN;
 }
 
 
 /**
   Does this savepoint have this lock?
-  
-  @retval TRUE  The ticket is older than the savepoint and
-                is not LT, HA or GLR ticket. Thus it belongs
-                to the savepoint.
-  @retval FALSE The ticket is newer than the savepoint
-                or is an LT, HA or GLR ticket.
+
+  @retval TRUE  The ticket is older than the savepoint or
+                is an LT, HA or GLR ticket. Thus it belongs
+                to the savepoint or has explicit duration.
+  @retval FALSE The ticket is newer than the savepoint.
+                and is not an LT, HA or GLR ticket.
 */
 
-bool MDL_context::has_lock(MDL_ticket *mdl_savepoint,
+bool MDL_context::has_lock(const MDL_savepoint &mdl_savepoint,
                            MDL_ticket *mdl_ticket)
 {
   MDL_ticket *ticket;
   /* Start from the beginning, most likely mdl_ticket's been just acquired. */
-  MDL_context::Ticket_iterator it(m_tickets);
-  bool found_savepoint= FALSE;
+  MDL_context::Ticket_iterator s_it(m_tickets[MDL_STATEMENT]);
+  MDL_context::Ticket_iterator t_it(m_tickets[MDL_TRANSACTION]);
 
-  while ((ticket= it++) && ticket != m_trans_sentinel)
+  while ((ticket= s_it++) && ticket != mdl_savepoint.m_stmt_ticket)
   {
-    /*
-      First met the savepoint. The ticket must be
-      somewhere after it.
-    */
-    if (ticket == mdl_savepoint)
-      found_savepoint= TRUE;
-    /*
-      Met the ticket. If we haven't yet met the savepoint,
-      the ticket is newer than the savepoint.
-    */
     if (ticket == mdl_ticket)
-      return found_savepoint;
+      return FALSE;
   }
-  /* Reached m_trans_sentinel. The ticket must be LT, HA or GRL ticket. */
-  return FALSE;
+
+  while ((ticket= t_it++) && ticket != mdl_savepoint.m_trans_ticket)
+  {
+    if (ticket == mdl_ticket)
+      return FALSE;
+  }
+  return TRUE;
+}
+
+
+/**
+  Change lock duration for transactional lock.
+
+  @param ticket   Ticket representing lock.
+  @param duration Lock duration to be set.
+
+  @note This method only supports changing duration of
+        transactional lock to some other duration.
+*/
+
+void MDL_context::set_lock_duration(MDL_ticket *mdl_ticket,
+                                    enum_mdl_duration duration)
+{
+  DBUG_ASSERT(mdl_ticket->m_duration == MDL_TRANSACTION &&
+              duration != MDL_TRANSACTION);
+
+  m_tickets[MDL_TRANSACTION].remove(mdl_ticket);
+  m_tickets[duration].push_front(mdl_ticket);
+#ifndef DBUG_OFF
+  mdl_ticket->m_duration= duration;
+#endif
 }
 
 
 /**
-  Rearrange the ticket to reside in the part of the list that's
-  beyond m_trans_sentinel. This effectively changes the ticket
-  life cycle, from automatic to manual: i.e. the ticket is no
-  longer released by MDL_context::release_transactional_locks() or
-  MDL_context::rollback_to_savepoint(), it must be released manually.
+  Set explicit duration for all locks in the context.
 */
 
-void MDL_context::move_ticket_after_trans_sentinel(MDL_ticket *mdl_ticket)
+void MDL_context::set_explicit_duration_for_all_locks()
 {
-  m_tickets.remove(mdl_ticket);
-  if (m_trans_sentinel == NULL)
+  int i;
+  MDL_ticket *ticket;
+
+  /*
+    In the most common case when this function is called list
+    of transactional locks is bigger than list of locks with
+    explicit duration. So we start by swapping these two lists
+    and then move elements from new list of transactional
+    locks and list of statement locks to list of locks with
+    explicit duration.
+  */
+
+  m_tickets[MDL_EXPLICIT].swap(m_tickets[MDL_TRANSACTION]);
+
+  for (i= 0; i < MDL_EXPLICIT; i++)
   {
-    m_trans_sentinel= mdl_ticket;
-    m_tickets.push_back(mdl_ticket);
+    Ticket_iterator it_ticket(m_tickets[i]);
+
+    while ((ticket= it_ticket++))
+    {
+      m_tickets[i].remove(ticket);
+      m_tickets[MDL_EXPLICIT].push_front(ticket);
+    }
   }
-  else
-    m_tickets.insert_after(m_trans_sentinel, mdl_ticket);
+
+#ifndef DBUG_OFF
+  Ticket_iterator exp_it(m_tickets[MDL_EXPLICIT]);
+
+  while ((ticket= exp_it++))
+    ticket->m_duration= MDL_EXPLICIT;
+#endif
+}
+
+
+/**
+  Set transactional duration for all locks in the context.
+*/
+
+void MDL_context::set_transaction_duration_for_all_locks()
+{
+  MDL_ticket *ticket;
+
+  /*
+    In the most common case when this function is called list
+    of explicit locks is bigger than two other lists (in fact,
+    list of statement locks is always empty). So we start by
+    swapping list of explicit and transactional locks and then
+    move contents of new list of explicit locks to list of
+    locks with transactional duration.
+  */
+
+  DBUG_ASSERT(m_tickets[MDL_STATEMENT].is_empty());
+
+  m_tickets[MDL_TRANSACTION].swap(m_tickets[MDL_EXPLICIT]);
+
+  Ticket_iterator it_ticket(m_tickets[MDL_EXPLICIT]);
+
+  while ((ticket= it_ticket++))
+  {
+    m_tickets[MDL_EXPLICIT].remove(ticket);
+    m_tickets[MDL_TRANSACTION].push_front(ticket);
+  }
+
+#ifndef DBUG_OFF
+  Ticket_iterator trans_it(m_tickets[MDL_TRANSACTION]);
+
+  while ((ticket= trans_it++))
+    ticket->m_duration= MDL_TRANSACTION;
+#endif
 }

=== modified file 'sql/mdl.h'
--- a/sql/mdl.h	2010-09-30 13:29:12 +0000
+++ b/sql/mdl.h	2010-11-11 17:08:24 +0000
@@ -150,6 +150,15 @@ enum enum_mdl_type {
   MDL_TYPE_END};
 
 
+/** Duration of metadata lock. */
+
+enum enum_mdl_duration { MDL_STATEMENT= 0,
+                         MDL_TRANSACTION,
+                         MDL_EXPLICIT,
+                         /* This should be the last ! */
+                         MDL_DURATION_END };
+
+
 /** Maximal length of key for metadata locking subsystem. */
 #define MAX_MDLKEY_LENGTH (1 + NAME_LEN + 1 + NAME_LEN + 1)
 
@@ -167,13 +176,16 @@ class MDL_key
 {
 public:
   /**
-    Object namespaces
+    Object namespaces.
+    Sic: when adding a new member to this enum make sure to
+    update m_namespace_to_wait_state_name array in mdl.cc!
 
     Different types of objects exist in different namespaces
      - TABLE is for tables and views.
      - FUNCTION is for stored functions.
      - PROCEDURE is for stored procedures.
      - TRIGGER is for triggers.
+     - EVENT is for event scheduler events
     Note that although there isn't metadata locking on triggers,
     it's necessary to have a separate namespace for them since
     MDL_key is also used outside of the MDL subsystem.
@@ -184,6 +196,8 @@ public:
                             FUNCTION,
                             PROCEDURE,
                             TRIGGER,
+                            EVENT,
+                            COMMIT,
                             /* This should be the last ! */
                             NAMESPACE_END };
 
@@ -305,6 +319,8 @@ class MDL_request
 public:
   /** Type of metadata lock. */
   enum          enum_mdl_type type;
+  /** Duration for requested lock. */
+  enum enum_mdl_duration duration;
 
   /**
     Pointers for participating in the list of lock requests for this context.
@@ -327,17 +343,16 @@ public:
 
   void init(MDL_key::enum_mdl_namespace namespace_arg,
             const char *db_arg, const char *name_arg,
-            enum_mdl_type mdl_type_arg);
-  void init(const MDL_key *key_arg, enum_mdl_type mdl_type_arg);
+            enum_mdl_type mdl_type_arg,
+            enum_mdl_duration mdl_duration_arg);
+  void init(const MDL_key *key_arg, enum_mdl_type mdl_type_arg,
+            enum_mdl_duration mdl_duration_arg);
   /** Set type of lock request. Can be only applied to pending locks. */
   inline void set_type(enum_mdl_type type_arg)
   {
     DBUG_ASSERT(ticket == NULL);
     type= type_arg;
   }
-  static MDL_request *create(MDL_key::enum_mdl_namespace mdl_namespace,
-                             const char *db, const char *name,
-                             enum_mdl_type mdl_type, MEM_ROOT *root);
 
   /*
     This is to work around the ugliness of TABLE_LIST
@@ -363,6 +378,7 @@ public:
 
   MDL_request(const MDL_request *rhs)
     :type(rhs->type),
+    duration(rhs->duration),
     ticket(NULL),
     key(&rhs->key)
   {}
@@ -484,17 +500,35 @@ public:
 private:
   friend class MDL_context;
 
-  MDL_ticket(MDL_context *ctx_arg, enum_mdl_type type_arg)
+  MDL_ticket(MDL_context *ctx_arg, enum_mdl_type type_arg
+#ifndef DBUG_OFF
+             , enum_mdl_duration duration_arg
+#endif
+            )
    : m_type(type_arg),
+#ifndef DBUG_OFF
+     m_duration(duration_arg),
+#endif
      m_ctx(ctx_arg),
      m_lock(NULL)
   {}
 
-  static MDL_ticket *create(MDL_context *ctx_arg, enum_mdl_type type_arg);
+  static MDL_ticket *create(MDL_context *ctx_arg, enum_mdl_type type_arg
+#ifndef DBUG_OFF
+                            , enum_mdl_duration duration_arg
+#endif
+                            );
   static void destroy(MDL_ticket *ticket);
 private:
   /** Type of metadata lock. Externally accessible. */
   enum enum_mdl_type m_type;
+#ifndef DBUG_OFF
+  /**
+    Duration of lock represented by this ticket.
+    Context private. Debug-only.
+  */
+  enum_mdl_duration m_duration;
+#endif
   /**
     Context of the owner of the metadata lock ticket. Externally accessible.
   */
@@ -512,6 +546,39 @@ private:
 
 
 /**
+  Savepoint for MDL context.
+
+  Doesn't include metadata locks with explicit duration as
+  they are not released during rollback to savepoint.
+*/
+
+class MDL_savepoint
+{
+public:
+  MDL_savepoint() {};
+
+private:
+  MDL_savepoint(MDL_ticket *stmt_ticket, MDL_ticket *trans_ticket)
+    : m_stmt_ticket(stmt_ticket), m_trans_ticket(trans_ticket)
+  {}
+
+  friend class MDL_context;
+
+private:
+  /**
+    Pointer to last lock with statement duration which was taken
+    before creation of savepoint.
+  */
+  MDL_ticket *m_stmt_ticket;
+  /**
+    Pointer to last lock with transaction duration which was taken
+    before creation of savepoint.
+  */
+  MDL_ticket *m_trans_ticket;
+};
+
+
+/**
   A reliable way to wait on an MDL lock.
 */
 
@@ -559,9 +626,7 @@ public:
   typedef I_P_List<MDL_ticket,
                    I_P_List_adapter<MDL_ticket,
                                     &MDL_ticket::next_in_context,
-                                    &MDL_ticket::prev_in_context>,
-                   I_P_List_null_counter,
-                   I_P_List_fast_push_back<MDL_ticket> >
+                                    &MDL_ticket::prev_in_context> >
           Ticket_list;
 
   typedef Ticket_list::Iterator Ticket_iterator;
@@ -584,37 +649,28 @@ public:
                      const char *db, const char *name,
                      enum_mdl_type mdl_type);
 
-  bool has_lock(MDL_ticket *mdl_savepoint, MDL_ticket *mdl_ticket);
+  bool has_lock(const MDL_savepoint &mdl_savepoint, MDL_ticket *mdl_ticket);
 
   inline bool has_locks() const
   {
-    return !m_tickets.is_empty();
-  }
-
-  MDL_ticket *mdl_savepoint()
-  {
-    /*
-      NULL savepoint represents the start of the transaction.
-      Checking for m_trans_sentinel also makes sure we never
-      return a pointer to HANDLER ticket as a savepoint.
-    */
-    return m_tickets.front() == m_trans_sentinel ? NULL : m_tickets.front();
+    return !(m_tickets[MDL_STATEMENT].is_empty() &&
+             m_tickets[MDL_TRANSACTION].is_empty() &&
+             m_tickets[MDL_EXPLICIT].is_empty());
   }
 
-  void set_trans_sentinel()
+  MDL_savepoint mdl_savepoint()
   {
-    m_trans_sentinel= m_tickets.front();
+    return MDL_savepoint(m_tickets[MDL_STATEMENT].front(),
+                         m_tickets[MDL_TRANSACTION].front());
   }
-  MDL_ticket *trans_sentinel() const { return m_trans_sentinel; }
 
-  void reset_trans_sentinel(MDL_ticket *sentinel_arg)
-  {
-    m_trans_sentinel= sentinel_arg;
-  }
-  void move_ticket_after_trans_sentinel(MDL_ticket *mdl_ticket);
+  void set_explicit_duration_for_all_locks();
+  void set_transaction_duration_for_all_locks();
+  void set_lock_duration(MDL_ticket *mdl_ticket, enum_mdl_duration duration);
 
+  void release_statement_locks();
   void release_transactional_locks();
-  void rollback_to_savepoint(MDL_ticket *mdl_savepoint);
+  void rollback_to_savepoint(const MDL_savepoint &mdl_savepoint);
 
   inline THD *get_thd() const { return m_thd; }
 
@@ -655,46 +711,43 @@ public:
   MDL_wait m_wait;
 private:
   /**
-    All MDL tickets acquired by this connection.
+    Lists of all MDL tickets acquired by this connection.
 
-    The order of tickets in m_tickets list.
-    ---------------------------------------
-    The entire set of locks acquired by a connection
-    can be separated in two subsets: transactional and
-    non-transactional locks.
-
-    Transactional locks are locks with automatic scope. They
-    are accumulated in the course of a transaction, and
-    released only on COMMIT, ROLLBACK or ROLLBACK TO SAVEPOINT.
-    They must not be (and never are) released manually,
+    Lists of MDL tickets:
+    ---------------------
+    The entire set of locks acquired by a connection can be separated
+    in three subsets according to their: locks released at the end of
+    statement, at the end of transaction and locks are released
+    explicitly.
+
+    Statement and transactional locks are locks with automatic scope.
+    They are accumulated in the course of a transaction, and released
+    either at the end of uppermost statement (for statement locks) or
+    on COMMIT, ROLLBACK or ROLLBACK TO SAVEPOINT (for transactional
+    locks). They must not be (and never are) released manually,
     i.e. with release_lock() call.
 
-    Non-transactional locks are taken for locks that span
+    Locks with explicit duration are taken for locks that span
     multiple transactions or savepoints.
     These are: HANDLER SQL locks (HANDLER SQL is
     transaction-agnostic), LOCK TABLES locks (you can COMMIT/etc
     under LOCK TABLES, and the locked tables stay locked), and
-    SET GLOBAL READ_ONLY=1 global shared lock.
+    locks implementing "global read lock".
 
-    Transactional locks are always prepended to the beginning
-    of the list. In other words, they are stored in reverse
-    temporal order. Thus, when we rollback to a savepoint,
-    we start popping and releasing tickets from the front
-    until we reach the last ticket acquired after the
-    savepoint.
-
-    Non-transactional locks are always stored after
-    transactional ones, and among each other can be
-    split into three sets:
+    Statement/transactional locks are always prepended to the
+    beginning of the appropriate list. In other words, they are
+    stored in reverse temporal order. Thus, when we rollback to
+    a savepoint, we start popping and releasing tickets from the
+    front until we reach the last ticket acquired after the savepoint.
+
+    Locks with explicit duration stored are not stored in any
+    particular order, and among each other can be split into
+    three sets:
 
     [LOCK TABLES locks] [HANDLER locks] [GLOBAL READ LOCK locks]
 
     The following is known about these sets:
 
-    * we can never have both HANDLER and LOCK TABLES locks
-      together -- HANDLER statements are prohibited under LOCK
-      TABLES, entering LOCK TABLES implicitly closes all open
-      HANDLERs.
     * GLOBAL READ LOCK locks are always stored after LOCK TABLES
       locks and after HANDLER locks. This is because one can't say
       SET GLOBAL read_only=1 or FLUSH TABLES WITH READ LOCK
@@ -709,14 +762,9 @@ private:
       However, one can open a few HANDLERs after entering the
       read only mode.
     * LOCK TABLES locks include intention exclusive locks on
-      involved schemas.
-  */
-  Ticket_list m_tickets;
-  /**
-    Separates transactional and non-transactional locks
-    in m_tickets list, @sa m_tickets.
+      involved schemas and global intention exclusive lock.
   */
-  MDL_ticket *m_trans_sentinel;
+  Ticket_list m_tickets[MDL_DURATION_END];
   THD *m_thd;
   /**
     TRUE -  if for this context we will break protocol and try to
@@ -747,8 +795,9 @@ private:
   MDL_wait_for_subgraph *m_waiting_for;
 private:
   MDL_ticket *find_ticket(MDL_request *mdl_req,
-                          bool *is_transactional);
-  void release_locks_stored_before(MDL_ticket *sentinel);
+                          enum_mdl_duration *duration);
+  void release_locks_stored_before(enum_mdl_duration duration, MDL_ticket *sentinel);
+  void release_lock(enum_mdl_duration duration, MDL_ticket *ticket);
   bool try_acquire_lock_impl(MDL_request *mdl_request,
                              MDL_ticket **out_ticket);
 

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2010-11-05 13:16:27 +0000
+++ b/sql/mysqld.cc	2010-11-11 17:08:24 +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;
@@ -1542,7 +1540,6 @@ static void clean_up_mutexes()
   mysql_mutex_destroy(&LOCK_crypt);
   mysql_mutex_destroy(&LOCK_user_conn);
   mysql_mutex_destroy(&LOCK_connection_count);
-  Events::destroy_mutexes();
 #ifdef HAVE_OPENSSL
   mysql_mutex_destroy(&LOCK_des_key_file);
 #ifndef HAVE_YASSL
@@ -1560,12 +1557,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 +3519,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 +3546,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);
@@ -7669,7 +7661,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,
@@ -7707,7 +7699,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},
@@ -7756,7 +7747,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,
@@ -7779,7 +7770,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-11-05 13:16:27 +0000
+++ b/sql/mysqld.h	2010-11-11 17:08:24 +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-11-11 17:08:24 +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->mdl_context.release_statement_locks();
+
   clear_tables_to_lock();
 }
 #endif

=== modified file 'sql/sp.cc'
--- a/sql/sp.cc	2010-10-21 08:41:13 +0000
+++ b/sql/sp.cc	2010-11-11 17:08:24 +0000
@@ -27,7 +27,7 @@
 #include "sql_acl.h"                       // SUPER_ACL
 #include "sp_head.h"
 #include "sp_cache.h"
-#include "lock.h"                               // lock_routine_name
+#include "lock.h"                               // lock_object_name
 
 #include <my_user.h>
 
@@ -440,7 +440,7 @@ static TABLE *open_proc_table_for_update
 {
   TABLE_LIST table_list;
   TABLE *table;
-  MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
   DBUG_ENTER("open_proc_table_for_update");
 
   table_list.init_one_table("mysql", 5, "proc", 4, "proc", TL_WRITE);
@@ -925,6 +925,8 @@ sp_create_routine(THD *thd, int type, sp
   TABLE *table;
   char definer[USER_HOST_BUFF_SIZE];
   ulong saved_mode= thd->variables.sql_mode;
+  MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
+                                        MDL_key::FUNCTION : MDL_key::PROCEDURE;
 
   CHARSET_INFO *db_cs= get_default_db_collation(thd, sp->m_db.str);
 
@@ -944,8 +946,7 @@ sp_create_routine(THD *thd, int type, sp
               type == TYPE_ENUM_FUNCTION);
 
   /* Grab an exclusive MDL lock. */
-  if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION,
-                        sp->m_db.str, sp->m_name.str))
+  if (lock_object_name(thd, mdl_type, sp->m_db.str, sp->m_name.str))
     DBUG_RETURN(SP_OPEN_TABLE_FAILED);
 
   /* Reset sql_mode during data dictionary operations. */
@@ -1193,6 +1194,8 @@ sp_drop_routine(THD *thd, int type, sp_n
   TABLE *table;
   int ret;
   bool save_binlog_row_based;
+  MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
+                                        MDL_key::FUNCTION : MDL_key::PROCEDURE;
   DBUG_ENTER("sp_drop_routine");
   DBUG_PRINT("enter", ("type: %d  name: %.*s",
 		       type, (int) name->m_name.length, name->m_name.str));
@@ -1201,8 +1204,7 @@ sp_drop_routine(THD *thd, int type, sp_n
               type == TYPE_ENUM_FUNCTION);
 
   /* Grab an exclusive MDL lock. */
-  if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION,
-                        name->m_db.str, name->m_name.str))
+  if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str))
     DBUG_RETURN(SP_DELETE_ROW_FAILED);
 
   if (!(table= open_proc_table_for_update(thd)))
@@ -1273,6 +1275,8 @@ sp_update_routine(THD *thd, int type, sp
   TABLE *table;
   int ret;
   bool save_binlog_row_based;
+  MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
+                                        MDL_key::FUNCTION : MDL_key::PROCEDURE;
   DBUG_ENTER("sp_update_routine");
   DBUG_PRINT("enter", ("type: %d  name: %.*s",
 		       type, (int) name->m_name.length, name->m_name.str));
@@ -1281,8 +1285,7 @@ sp_update_routine(THD *thd, int type, sp
               type == TYPE_ENUM_FUNCTION);
 
   /* Grab an exclusive MDL lock. */
-  if (lock_routine_name(thd, type == TYPE_ENUM_FUNCTION,
-                        name->m_db.str, name->m_name.str))
+  if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str))
     DBUG_RETURN(SP_OPEN_TABLE_FAILED);
 
   if (!(table= open_proc_table_for_update(thd)))
@@ -1370,7 +1373,7 @@ sp_drop_db_routines(THD *thd, char *db)
   TABLE *table;
   int ret;
   uint key_len;
-  MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
   DBUG_ENTER("sp_drop_db_routines");
   DBUG_PRINT("enter", ("db: %s", db));
 
@@ -1697,7 +1700,7 @@ bool sp_add_used_routine(Query_tables_li
       (Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry));
     if (!rn)              // OOM. Error will be reported using fatal_error().
       return FALSE;
-    rn->mdl_request.init(key, MDL_SHARED);
+    rn->mdl_request.init(key, MDL_SHARED, MDL_TRANSACTION);
     if (my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn))
       return FALSE;
     prelocking_ctx->sroutines_list.link_in_list(rn, &rn->next);

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2010-10-27 07:32:26 +0000
+++ b/sql/sp_head.cc	2010-11-11 17:08:24 +0000
@@ -2140,6 +2140,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->mdl_context.release_statement_locks();
 
     thd->rollback_item_tree_changes();
 
@@ -2978,6 +2980,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->mdl_context.release_statement_locks();
   }
 
   if (m_lex->query_tables_own_last)
@@ -4176,7 +4180,8 @@ sp_head::add_used_tables_to_table_list(T
       */
       table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
                               table->lock_type >= TL_WRITE_ALLOW_WRITE ?
-                              MDL_SHARED_WRITE : MDL_SHARED_READ);
+                              MDL_SHARED_WRITE : MDL_SHARED_READ,
+                              MDL_TRANSACTION);
 
       /* Everyting else should be zeroed */
 
@@ -4220,7 +4225,7 @@ sp_add_to_query_tables(THD *thd, LEX *le
   table->select_lex= lex->current_select;
   table->cacheable_table= 1;
   table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
-                          mdl_type);
+                          mdl_type, MDL_TRANSACTION);
 
   lex->add_to_query_tables(table);
   return table;

=== modified file 'sql/sql_admin.cc'
--- a/sql/sql_admin.cc	2010-09-22 08:15:41 +0000
+++ b/sql/sql_admin.cc	2010-11-11 17:08:24 +0000
@@ -85,7 +85,7 @@ static int prepare_for_repair(THD *thd, 
     key_length= create_table_def_key(thd, key, table_list, 0);
     table_list->mdl_request.init(MDL_key::TABLE,
                                  table_list->db, table_list->table_name,
-                                 MDL_EXCLUSIVE);
+                                 MDL_EXCLUSIVE, MDL_TRANSACTION);
 
     if (lock_table_names(thd, table_list, table_list->next_global,
                          thd->variables.lock_wait_timeout,

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2010-10-14 16:56:56 +0000
+++ b/sql/sql_base.cc	2010-11-11 17:08:24 +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();
 }
 
 
@@ -2463,7 +2450,8 @@ open_table_get_mdl_lock(THD *thd, Open_t
 
     mdl_request_shared.init(&mdl_request->key,
                             (flags & MYSQL_OPEN_FORCE_SHARED_MDL) ?
-                            MDL_SHARED : MDL_SHARED_HIGH_PRIO);
+                            MDL_SHARED : MDL_SHARED_HIGH_PRIO,
+                            MDL_TRANSACTION);
     mdl_request= &mdl_request_shared;
   }
 
@@ -2628,32 +2616,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
@@ -2824,6 +2786,59 @@ bool open_table(THD *thd, TABLE_LIST *ta
 
   if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
   {
+    /*
+      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.
+    */
+    if (table_list->mdl_request.type >= MDL_SHARED_WRITE &&
+        ! (flags & (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
+                    MYSQL_OPEN_FORCE_SHARED_MDL |
+                    MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
+                    MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) &&
+        ! ot_ctx->has_protection_against_grl())
+    {
+      MDL_request protection_request;
+      MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
+
+      if (thd->global_read_lock.can_acquire_protection())
+        DBUG_RETURN(TRUE);
+
+      protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+                              MDL_STATEMENT);
+
+      /*
+        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_has_protection_against_grl();
+    }
+
     if (open_table_get_mdl_lock(thd, ot_ctx, &table_list->mdl_request,
                                 flags, &mdl_ticket) ||
         mdl_ticket == NULL)
@@ -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_has_protection_against_grl(FALSE)
 {}
 
 
@@ -4013,6 +4028,12 @@ recover_from_failed_open(THD *thd)
     for safety.
   */
   m_failed_table= NULL;
+  /*
+    Reset flag indicating that we have already acquired protection
+    against GRL. It is no longer valid as the corresponding lock was
+    released by close_tables_for_reopen().
+  */
+  m_has_protection_against_grl= FALSE;
   /* Prepare for possible another back-off. */
   m_action= OT_NO_ACTION;
   return result;
@@ -4551,11 +4572,20 @@ lock_table_names(THD *thd,
       if (schema_request == NULL)
         return TRUE;
       schema_request->init(MDL_key::SCHEMA, table->db, "",
-                           MDL_INTENTION_EXCLUSIVE);
+                           MDL_INTENTION_EXCLUSIVE,
+                           MDL_TRANSACTION);
       mdl_requests.push_front(schema_request);
     }
-    /* Take the global intention exclusive lock. */
-    global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
+
+    /*
+      Protect this statement against concurrent global read lock
+      by acquiring global intention exclusive lock with statement
+      duration.
+    */
+    if (thd->global_read_lock.can_acquire_protection())
+      return TRUE;
+    global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+                        MDL_STATEMENT);
     mdl_requests.push_front(&global_request);
   }
 
@@ -5362,7 +5392,7 @@ bool open_and_lock_tables(THD *thd, TABL
                           Prelocking_strategy *prelocking_strategy)
 {
   uint counter;
-  MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
   DBUG_ENTER("open_and_lock_tables");
   DBUG_PRINT("enter", ("derived handling: %d", derived));
 
@@ -5419,7 +5449,7 @@ bool open_normal_and_derived_tables(THD 
 {
   DML_prelocking_strategy prelocking_strategy;
   uint counter;
-  MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
   DBUG_ENTER("open_normal_and_derived_tables");
   DBUG_ASSERT(!thd->fill_derived_tables());
   if (open_tables(thd, &tables, &counter, flags, &prelocking_strategy) ||
@@ -5676,7 +5706,7 @@ bool lock_tables(THD *thd, TABLE_LIST *t
 */
 
 void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
-                             MDL_ticket *start_of_statement_svp)
+                             const MDL_savepoint &start_of_statement_svp)
 {
   TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
   TABLE_LIST *tmp;

=== modified file 'sql/sql_base.h'
--- a/sql/sql_base.h	2010-10-27 18:57:44 +0000
+++ b/sql/sql_base.h	2010-11-11 17:08:24 +0000
@@ -159,7 +159,7 @@ thr_lock_type read_lock_type_for_table(T
 my_bool mysql_rm_tmp_tables(void);
 bool rm_temporary_table(handlerton *base, char *path);
 void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
-                             MDL_ticket *start_of_statement_svp);
+                             const MDL_savepoint &start_of_statement_svp);
 TABLE_LIST *find_table_in_list(TABLE_LIST *table,
                                TABLE_LIST *TABLE_LIST::*link,
                                const char *db_name,
@@ -507,7 +507,7 @@ public:
     the statement, so that we can rollback to it before waiting on
     locks.
   */
-  MDL_ticket *start_of_statement_svp() const
+  const MDL_savepoint &start_of_statement_svp() const
   {
     return m_start_of_statement_svp;
   }
@@ -518,6 +518,21 @@ public:
   }
 
   uint get_flags() const { return m_flags; }
+
+  /**
+    Set flag indicating that we have already acquired metadata lock
+    protecting this statement against GRL while opening tables.
+  */
+  void set_has_protection_against_grl()
+  {
+    m_has_protection_against_grl= TRUE;
+  }
+
+  bool has_protection_against_grl() const
+  {
+    return m_has_protection_against_grl;
+  }
+
 private:
   /**
     For OT_DISCOVER and OT_REPAIR actions, the table list element for
@@ -525,7 +540,7 @@ private:
     should be repaired.
   */
   TABLE_LIST *m_failed_table;
-  MDL_ticket *m_start_of_statement_svp;
+  MDL_savepoint m_start_of_statement_svp;
   /**
     Lock timeout in seconds. Initialized to LONG_TIMEOUT when opening system
     tables or to the "lock_wait_timeout" system variable for regular tables.
@@ -541,6 +556,11 @@ private:
     and we can't safely do back-off (and release them).
   */
   bool m_has_locks;
+  /**
+    Indicates that in the process of opening tables we have acquired
+    protection against global read lock.
+  */
+  bool m_has_protection_against_grl;
 };
 
 

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2010-10-25 12:26:21 +0000
+++ b/sql/sql_class.cc	2010-11-11 17:08:24 +0000
@@ -3497,11 +3497,15 @@ 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. */
-  mdl_context.reset_trans_sentinel(global_read_lock.global_shared_lock());
+  mdl_context.set_transaction_duration_for_all_locks();
+  /*
+    Make sure we don't release the global read lock and commit blocker
+    when leaving LTM.
+  */
+  global_read_lock.set_explicit_lock_duration(this);
   /* Also ensure that we don't release metadata locks for open HANDLERs. */
   if (handler_tables_hash.records)
-    mysql_ha_move_tickets_after_trans_sentinel(this);
+    mysql_ha_set_explicit_lock_duration(this);
 }
 
 void THD::get_definer(LEX_USER *definer)

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2010-10-25 12:26:21 +0000
+++ b/sql/sql_class.h	2010-11-11 17:08:24 +0000
@@ -822,8 +822,8 @@ struct st_savepoint {
   char                *name;
   uint                 length;
   Ha_trx_info         *ha_list;
-  /** Last acquired lock before this savepoint was set. */
-  MDL_ticket     *mdl_savepoint;
+  /** State of metadata locks before this savepoint was set. */
+  MDL_savepoint        mdl_savepoint;
 };
 
 enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY};
@@ -1058,12 +1058,12 @@ class Open_tables_backup: public Open_ta
 public:
   /**
     When we backup the open tables state to open a system
-    table or tables, points at the last metadata lock
-    acquired before the backup. Is used to release
-    metadata locks on system tables after they are
+    table or tables, we want to save state of metadata
+    locks which were acquired before the backup. It is used
+    to release metadata locks on system tables after they are
     no longer used.
   */
-  MDL_ticket *mdl_system_tables_svp;
+  MDL_savepoint mdl_system_tables_svp;
 };
 
 /**
@@ -1336,26 +1336,43 @@ 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);
+  /**
+    Check if this connection can acquire protection against GRL and
+    emit error if otherwise.
+  */
+  bool can_acquire_protection() const
+  {
+    if (m_state)
+    {
+      my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0));
+      return TRUE;
+    }
+    return FALSE;
+  }
   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; }
+  void set_explicit_lock_duration(THD *thd);
 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;
 };
 
 
@@ -2697,7 +2714,7 @@ public:
   {
     DBUG_ASSERT(locked_tables_mode == LTM_NONE);
 
-    mdl_context.set_trans_sentinel();
+    mdl_context.set_explicit_duration_for_all_locks();
     locked_tables_mode= mode_arg;
   }
   void leave_locked_tables_mode();
@@ -3503,20 +3520,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_db.cc'
--- a/sql/sql_db.cc	2010-10-19 10:29:21 +0000
+++ b/sql/sql_db.cc	2010-11-11 17:08:24 +0000
@@ -1054,7 +1054,8 @@ static long mysql_rm_known_files(THD *th
       table_list->alias= table_list->table_name;	// If lower_case_table_names=2
       table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix);
       table_list->mdl_request.init(MDL_key::TABLE, table_list->db,
-                                   table_list->table_name, MDL_EXCLUSIVE);
+                                   table_list->table_name, MDL_EXCLUSIVE,
+                                   MDL_TRANSACTION);
       /* Link into list */
       (*tot_list_next_local)= table_list;
       (*tot_list_next_global)= table_list;

=== modified file 'sql/sql_handler.cc'
--- a/sql/sql_handler.cc	2010-08-12 13:50:23 +0000
+++ b/sql/sql_handler.cc	2010-11-11 17:08:24 +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)
@@ -183,7 +179,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST 
   uint          dblen, namelen, aliaslen, counter;
   bool          error;
   TABLE         *backup_open_tables;
-  MDL_ticket    *mdl_savepoint;
+  MDL_savepoint mdl_savepoint;
   DBUG_ENTER("mysql_ha_open");
   DBUG_PRINT("enter",("'%s'.'%s' as '%s'  reopen: %d",
                       tables->db, tables->table_name, tables->alias,
@@ -252,7 +248,13 @@ bool mysql_ha_open(THD *thd, TABLE_LIST 
     memcpy(hash_tables->db, tables->db, dblen);
     memcpy(hash_tables->table_name, tables->table_name, namelen);
     memcpy(hash_tables->alias, tables->alias, aliaslen);
-    hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED);
+    /*
+      We can't request lock with explicit duration for this table
+      right from the start as open_tables() can't handle properly
+      back-off for such locks.
+    */
+    hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED,
+                                  MDL_TRANSACTION);
     /* for now HANDLER can be used only for real TABLES */
     hash_tables->required_type= FRMTYPE_TABLE;
 
@@ -332,8 +334,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST 
   thd->set_open_tables(backup_open_tables);
   if (hash_tables->mdl_request.ticket)
   {
-    thd->mdl_context.
-      move_ticket_after_trans_sentinel(hash_tables->mdl_request.ticket);
+    thd->mdl_context.set_lock_duration(hash_tables->mdl_request.ticket,
+                                       MDL_EXPLICIT);
     thd->mdl_context.set_needs_thr_lock_abort(TRUE);
   }
 
@@ -969,24 +971,23 @@ void mysql_ha_cleanup(THD *thd)
 
 
 /**
-  Move tickets for metadata locks corresponding to open HANDLERs
-  after transaction sentinel in order to protect them from being
-  released at the end of transaction.
+  Set explicit duration for metadata locks corresponding to open HANDLERs
+  to protect them from being released at the end of transaction.
 
   @param thd Thread identifier.
 */
 
-void mysql_ha_move_tickets_after_trans_sentinel(THD *thd)
+void mysql_ha_set_explicit_lock_duration(THD *thd)
 {
   TABLE_LIST *hash_tables;
-  DBUG_ENTER("mysql_ha_move_tickets_after_trans_sentinel");
+  DBUG_ENTER("mysql_ha_set_explicit_lock_duration");
 
   for (uint i= 0; i < thd->handler_tables_hash.records; i++)
   {
     hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i);
     if (hash_tables->table && hash_tables->table->mdl_ticket)
-      thd->mdl_context.
-             move_ticket_after_trans_sentinel(hash_tables->table->mdl_ticket);
+      thd->mdl_context.set_lock_duration(hash_tables->table->mdl_ticket,
+                                         MDL_EXPLICIT);
   }
   DBUG_VOID_RETURN;
 }

=== modified file 'sql/sql_handler.h'
--- a/sql/sql_handler.h	2010-06-09 08:39:09 +0000
+++ b/sql/sql_handler.h	2010-11-11 17:08:24 +0000
@@ -31,6 +31,6 @@ void mysql_ha_flush(THD *thd);
 void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables);
 void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables);
 void mysql_ha_cleanup(THD *thd);
-void mysql_ha_move_tickets_after_trans_sentinel(THD *thd);
+void mysql_ha_set_explicit_lock_duration(THD *thd);
 
 #endif /* SQL_HANDLER_INCLUDED */

=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc	2010-10-07 10:01:51 +0000
+++ b/sql/sql_insert.cc	2010-11-11 17:08:24 +0000
@@ -77,7 +77,8 @@
 #include "sql_audit.h"
 
 #ifndef EMBEDDED_LIBRARY
-static bool delayed_get_table(THD *thd, TABLE_LIST *table_list);
+static bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
+                              TABLE_LIST *table_list);
 static int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic,
                          LEX_STRING query, bool ignore, bool log_on);
 static void end_delayed_insert(THD *thd);
@@ -529,32 +530,28 @@ 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.
   */
-  MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+  if (thd->global_read_lock.can_acquire_protection())
+    DBUG_RETURN(TRUE);
+
+  protection_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+                          MDL_STATEMENT);
+
+  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))
     /*
@@ -564,7 +561,7 @@ bool open_and_lock_for_insert_delayed(TH
     DBUG_RETURN(TRUE);
 
   bool error= FALSE;
-  if (delayed_get_table(thd, table_list))
+  if (delayed_get_table(thd, &protection_request, table_list))
     error= TRUE;
   else if (table_list->table)
   {
@@ -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),
@@ -2066,7 +2069,8 @@ Delayed_insert *find_handler(THD *thd, T
 */
 
 static
-bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
+bool delayed_get_table(THD *thd, MDL_request *grl_protection_request,
+                       TABLE_LIST *table_list)
 {
   int error;
   Delayed_insert *di;
@@ -2110,7 +2114,10 @@ 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, MDL_STATEMENT);
+      di->grl_protection.ticket= grl_protection_request->ticket;
       init_mdl_requests(&di->table_list);
       di->table_list.mdl_request.ticket= table_list->mdl_request.ticket;
 
@@ -2650,13 +2657,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-11-11 17:08:24 +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-25 15:08:27 +0000
+++ b/sql/sql_lex.h	2010-11-11 17:08:24 +0000
@@ -2394,22 +2394,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-25 12:26:21 +0000
+++ b/sql/sql_parse.cc	2010-11-11 17:08:24 +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
@@ -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 |
@@ -285,26 +281,18 @@ void init_update_queries(void)
   sql_command_flags[SQLCOM_CREATE_EVENT]=   CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
   sql_command_flags[SQLCOM_ALTER_EVENT]=    CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
   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;
@@ -366,20 +354,19 @@ 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;
   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;
+  sql_command_flags[SQLCOM_CREATE_PROCEDURE]=  CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+  sql_command_flags[SQLCOM_CREATE_SPFUNCTION]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+  sql_command_flags[SQLCOM_DROP_PROCEDURE]=    CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+  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;
 
@@ -1111,7 +1098,7 @@ bool dispatch_command(enum enum_server_c
       SHOW statements should not add the used tables to the list of tables
       used in a transaction.
     */
-    MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+    MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
 
     status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS]);
     if (thd->copy_db_to(&db.str, &db.length))
@@ -1754,16 +1741,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
@@ -1797,7 +1774,6 @@ mysql_execute_command(THD *thd)
   /* have table map for update for multi-update statement (BUG#37051) */
   bool have_table_map_for_update= FALSE;
 #endif
-  /* Saved variable value */
   DBUG_ENTER("mysql_execute_command");
 #ifdef WITH_PARTITION_STORAGE_ENGINE
   thd->work_part_info= 0;
@@ -1989,17 +1965,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");
@@ -2074,10 +2039,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;
   }
@@ -2336,20 +2297,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
     {
@@ -3143,9 +3090,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;
@@ -3801,13 +3745,22 @@ end_with_restore_list:
       Security_context *backup= NULL;
       LEX_USER *definer= thd->lex->definer;
       /*
-        We're going to issue an implicit GRANT statement.
-        It takes metadata locks and updates system tables.
-        Make sure that sp_create_routine() did not leave any
-        locks in the MDL context, so there is no risk to
-        deadlock.
+        We're going to issue an implicit GRANT statement so we close all
+        open tables. We have to keep metadata locks as this ensures that
+        this statement is atomic against concurent FLUSH TABLES WITH READ
+        LOCK. Deadlocks which can arise due to fact that this implicit
+        statement takes metadata locks should be detected by a deadlock
+        detector in MDL subsystem and reported as errors.
+
+        No need to commit/rollback statement transaction, it's not started.
+
+        TODO: Long-term we should either ensure that implicit GRANT statement
+              is written into binary log as a separate statement or make both
+              creation of routine and implicit GRANT parts of one fully atomic
+              statement.
       */
-      close_mysql_tables(thd);
+      DBUG_ASSERT(thd->transaction.stmt.is_empty());
+      close_thread_tables(thd);
       /*
         Check if the definer exists on slave, 
         then use definer privilege to insert routine privileges to mysql.procs_priv.
@@ -4067,13 +4020,22 @@ create_sp_error:
 
 #ifndef NO_EMBEDDED_ACCESS_CHECKS
       /*
-        We're going to issue an implicit REVOKE statement.
-        It takes metadata locks and updates system tables.
-        Make sure that sp_create_routine() did not leave any
-        locks in the MDL context, so there is no risk to
-        deadlock.
+        We're going to issue an implicit REVOKE statement so we close all
+        open tables. We have to keep metadata locks as this ensures that
+        this statement is atomic against concurent FLUSH TABLES WITH READ
+        LOCK. Deadlocks which can arise due to fact that this implicit
+        statement takes metadata locks should be detected by a deadlock
+        detector in MDL subsystem and reported as errors.
+
+        No need to commit/rollback statement transaction, it's not started.
+
+        TODO: Long-term we should either ensure that implicit REVOKE statement
+              is written into binary log as a separate statement or make both
+              dropping of routine and implicit REVOKE parts of one fully atomic
+              statement.
       */
-      close_mysql_tables(thd);
+      DBUG_ASSERT(thd->transaction.stmt.is_empty());
+      close_thread_tables(thd);
 
       if (sp_result != SP_KEY_NOT_FOUND &&
           sp_automatic_privileges && !opt_noacl &&
@@ -4362,14 +4324,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());
@@ -4405,6 +4359,11 @@ 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
+
   if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END))
   {
     /* No transaction control allowed in sub-statements. */
@@ -4430,6 +4389,10 @@ finish:
     */
     thd->mdl_context.release_transactional_locks();
   }
+  else if (! thd->in_sub_stmt)
+  {
+    thd->mdl_context.release_statement_locks();
+  }
 
   DBUG_RETURN(res || thd->is_error());
 }
@@ -5899,7 +5862,8 @@ TABLE_LIST *st_select_lex::add_table_to_
   ptr->next_name_resolution_table= NULL;
   /* Link table in global list (all used tables) */
   lex->add_to_query_tables(ptr);
-  ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type);
+  ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type,
+                        MDL_TRANSACTION);
   DBUG_RETURN(ptr);
 }
 

=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc	2010-11-03 14:15:18 +0000
+++ b/sql/sql_prepare.cc	2010-11-11 17:08:24 +0000
@@ -3172,7 +3172,6 @@ bool Prepared_statement::prepare(const c
   bool error;
   Statement stmt_backup;
   Query_arena *old_stmt_arena;
-  MDL_ticket *mdl_savepoint= NULL;
   DBUG_ENTER("Prepared_statement::prepare");
   /*
     If this is an SQLCOM_PREPARE, we also increase Com_prepare_sql.
@@ -3244,7 +3243,7 @@ bool Prepared_statement::prepare(const c
     Marker used to release metadata locks acquired while the prepared
     statement is being checked.
   */
-  mdl_savepoint= thd->mdl_context.mdl_savepoint();
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
 
   /* 
    The only case where we should have items in the thd->free_list is

=== modified file 'sql/sql_rename.cc'
--- a/sql/sql_rename.cc	2010-11-07 18:06:22 +0000
+++ b/sql/sql_rename.cc	2010-11-11 17:08:24 +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_show.cc'
--- a/sql/sql_show.cc	2010-11-02 14:02:16 +0000
+++ b/sql/sql_show.cc	2010-11-11 17:08:24 +0000
@@ -671,7 +671,7 @@ mysqld_show_create(THD *thd, TABLE_LIST 
     Metadata locks taken during SHOW CREATE should be released when
     the statmement completes as it is an information statement.
   */
-  MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
 
   /* We want to preserve the tree for views. */
   thd->lex->view_prepare_mode= TRUE;
@@ -3186,7 +3186,7 @@ try_acquire_high_prio_shared_mdl_lock(TH
 {
   bool error;
   table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
-                          MDL_SHARED_HIGH_PRIO);
+                          MDL_SHARED_HIGH_PRIO, MDL_TRANSACTION);
 
   if (can_deadlock)
   {
@@ -7745,7 +7745,7 @@ bool show_create_trigger(THD *thd, const
     Metadata locks taken during SHOW CREATE TRIGGER should be released when
     the statement completes as it is an information statement.
   */
-  MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
 
   /*
     Open the table by name in order to load Table_triggers_list object.

=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc	2010-10-19 10:29:21 +0000
+++ b/sql/sql_table.cc	2010-11-11 17:08:24 +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);
@@ -5592,7 +5579,6 @@ bool mysql_alter_table(THD *thd,char *ne
   TABLE *table, *new_table= 0;
   MDL_ticket *mdl_ticket;
   MDL_request target_mdl_request;
-  bool has_target_mdl_lock= FALSE;
   int error= 0;
   char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
   char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
@@ -5754,7 +5740,7 @@ bool mysql_alter_table(THD *thd,char *ne
       else
       {
         target_mdl_request.init(MDL_key::TABLE, new_db, new_name,
-                                MDL_EXCLUSIVE);
+                                MDL_EXCLUSIVE, MDL_TRANSACTION);
         /*
           Global intention exclusive lock must have been already acquired when
           table to be altered was open, so there is no need to do it here.
@@ -5772,7 +5758,6 @@ bool mysql_alter_table(THD *thd,char *ne
 	  DBUG_RETURN(TRUE);
         }
         DEBUG_SYNC(thd, "locked_table_name");
-        has_target_mdl_lock= TRUE;
         /*
           Table maybe does not exist, but we got an exclusive lock
           on the name, now we can safely try to find out for sure.
@@ -5959,10 +5944,7 @@ bool mysql_alter_table(THD *thd,char *ne
         along with the implicit commit.
       */
       if (new_name != table_name || new_db != db)
-      {
-        thd->mdl_context.release_lock(target_mdl_request.ticket);
         thd->mdl_context.release_all_locks_for_name(mdl_ticket);
-      }
       else
         mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
     }
@@ -6667,10 +6649,7 @@ bool mysql_alter_table(THD *thd,char *ne
       thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)
   {
     if ((new_name != table_name || new_db != db))
-    {
-      thd->mdl_context.release_lock(target_mdl_request.ticket);
       thd->mdl_context.release_all_locks_for_name(mdl_ticket);
-    }
     else
       mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
   }
@@ -6731,8 +6710,6 @@ err:
                                  alter_info->datetime_field->field_name);
     thd->abort_on_warning= save_abort_on_warning;
   }
-  if (has_target_mdl_lock)
-    thd->mdl_context.release_lock(target_mdl_request.ticket);
 
   DBUG_RETURN(TRUE);
 
@@ -6744,9 +6721,6 @@ err_with_mdl:
     tables and release the exclusive metadata lock.
   */
   thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
-  if (has_target_mdl_lock)
-    thd->mdl_context.release_lock(target_mdl_request.ticket);
-
   thd->mdl_context.release_all_locks_for_name(mdl_ticket);
   DBUG_RETURN(TRUE);
 }

=== modified file 'sql/sql_trigger.cc'
--- a/sql/sql_trigger.cc	2010-09-30 10:43:43 +0000
+++ b/sql/sql_trigger.cc	2010-11-11 17:08:24 +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_update.cc'
--- a/sql/sql_update.cc	2010-10-07 10:01:51 +0000
+++ b/sql/sql_update.cc	2010-11-11 17:08:24 +0000
@@ -1026,9 +1026,17 @@ int mysql_multi_update_prepare(THD *thd)
   /* following need for prepared statements, to run next time multi-update */
   thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
 
-  /* open tables and create derived ones, but do not lock and fill them */
+  /*
+    Open tables and create derived ones, but do not lock and fill them yet.
+
+    During prepare phase acquire only S metadata locks instead of SW locks to
+    keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE
+    and global read lock.
+  */
   if ((original_multiupdate &&
-       open_tables(thd, &table_list, &table_count, 0)) ||
+       open_tables(thd, &table_list, &table_count,
+                   (thd->stmt_arena->is_stmt_prepare() ?
+                    MYSQL_OPEN_FORCE_SHARED_MDL : 0))) ||
       mysql_handle_derived(lex, &mysql_derived_prepare))
     DBUG_RETURN(TRUE);
   /*

=== modified file 'sql/sql_view.cc'
--- a/sql/sql_view.cc	2010-08-18 11:55:37 +0000
+++ b/sql/sql_view.cc	2010-11-11 17:08:24 +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-11-03 14:51:52 +0000
+++ b/sql/sql_yacc.yy	2010-11-11 17:08:24 +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
           {
@@ -13182,9 +13181,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/table.cc'
--- a/sql/table.cc	2010-10-04 12:42:16 +0000
+++ b/sql/table.cc	2010-11-11 17:08:24 +0000
@@ -5219,7 +5219,8 @@ void init_mdl_requests(TABLE_LIST *table
     table_list->mdl_request.init(MDL_key::TABLE,
                                  table_list->db, table_list->table_name,
                                  table_list->lock_type >= TL_WRITE_ALLOW_WRITE ?
-                                 MDL_SHARED_WRITE : MDL_SHARED_READ);
+                                 MDL_SHARED_WRITE : MDL_SHARED_READ,
+                                 MDL_TRANSACTION);
 }
 
 

=== modified file 'sql/table.h'
--- a/sql/table.h	2010-10-06 14:34:28 +0000
+++ b/sql/table.h	2010-11-11 17:08:24 +0000
@@ -1384,7 +1384,8 @@ struct TABLE_LIST
     lock_type= lock_type_arg;
     mdl_request.init(MDL_key::TABLE, db, table_name,
                      (lock_type >= TL_WRITE_ALLOW_WRITE) ?
-                     MDL_SHARED_WRITE : MDL_SHARED_READ);
+                     MDL_SHARED_WRITE : MDL_SHARED_READ,
+                     MDL_TRANSACTION);
   }
 
   /*

=== modified file 'sql/transaction.cc'
--- a/sql/transaction.cc	2010-09-13 11:31:22 +0000
+++ b/sql/transaction.cc	2010-11-11 17:08:24 +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)
@@ -391,15 +392,15 @@ bool trans_savepoint(THD *thd, LEX_STRIN
   thd->transaction.savepoints= newsv;
 
   /*
-    Remember the last acquired lock before the savepoint was set.
-    This is used as a marker to only release locks acquired after
+    Remember locks acquired before the savepoint was set.
+    They are used as a marker to only release locks acquired after
     the setting of this savepoint.
     Note: this works just fine if we're under LOCK TABLES,
     since mdl_savepoint() is guaranteed to be beyond
     the last locked table. This allows to release some
     locks acquired during LOCK TABLES.
   */
-  newsv->mdl_savepoint = thd->mdl_context.mdl_savepoint();
+  newsv->mdl_savepoint= thd->mdl_context.mdl_savepoint();
 
   DBUG_RETURN(FALSE);
 }
@@ -645,17 +646,31 @@ 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.
+    */
+    mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE,
+                     MDL_TRANSACTION);
+
+    if (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);
     }
   }
   else


Attachment: [text/bzr-bundle] bzr/dmitry.lenev@oracle.com-20101111170824-sk93hz822v5weiwl.bundle
Thread
bzr commit into mysql-5.5-runtime branch (Dmitry.Lenev:3179) Bug#54673Bug#57006Dmitry Lenev11 Nov