List:Commits« Previous MessageNext Message »
From:Dmitry Lenev Date:May 25 2010 12:36pm
Subject:bzr push into mysql-trunk-runtime branch (dlenev:3021 to 3022)
Bug#51263
View as plain text  
 3022 Dmitry Lenev	2010-05-25
      Pre-requisite patch for bug #51263 "Deadlock between
      transactional SELECT and ALTER TABLE ... REBUILD PARTITION".
      
      The goal of this patch is to decouple type of metadata
      lock acquired for table by open_tables() from type of
      table-level lock to be acquired on it.
      
      To achieve this we change approach to how we determine what
      type of metadata lock should be acquired on table to be open.
      Now instead of inferring it at open_tables() time from flags
      and type of table-level lock we rely on that type of metadata
      lock is properly set at parsing time and is not changed
      further.
     @ sql/ha_ndbcluster.cc
        Now one needs to properly initialize table list element's
        MDL_request object before calling mysql_rm_table_part2().
     @ sql/lock.cc
        lock_table_names() no longer initializes table list elements'
        MDL_request objects. Now proper initialization of these
        requests is a responsibility of the caller.
     @ sql/lock.h
        Removed MYSQL_OPEN_TAKE_UPGRADABLE_MDL flag which became
        unnecessary. Thanks to the fact that we don't reset type of
        requests for metadata locks between re-executions we now can
        figure out that upgradable locks are requested by simply
        looking at their type which were set in the parser. As result
        this flag became redundant.
     @ sql/mdl.h
        Added version of new operator which simplifies allocation of
        MDL_request objects on a MEM_ROOT.
     @ sql/sp_head.cc
        Added comment explaining why it is OK to infer type of
        metadata lock to request from type of table-level lock
        for prelocking.
        Added enum_mdl_type argument to sp_add_to_query_tables()
        to simplify its usage in trigger implementation.
     @ sql/sp_head.h
        Added enum_mdl_type argument to sp_add_to_query_tables()
        to simplify its usage in trigger implementation.
     @ sql/sql_base.cc
        - open_table_get_mdl_lock():
          Preserve type of MDL_request for table list element which
          was set in the parser by creating MDL_request objects on
          memory root if MYSQL_OPEN_FORCE_SHARED_MDL or
          MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL flag were specified.
          Thanks to this and to the fact that we no longer reset
          type of requests for metadata locks between re-executions
          we no longer need to acquire exclusive metadata lock on
          table to be created in a special way. This lock is acquired
          by code handling acquiring of upgradable locks.
          Also changed signature/calling convention for this function
          to simplify its usage.
        - Accordingly special lock strategy for table list elements
          which was used for such locks became unnecessary and was
          removed. Other strategies were renamed.
        - Since we no longer have guarantee that MDL_request object
          which were not satisfied due to lock conflict belongs to
          table list element Open_table_context class and its methods
          were extended to remember pointer to MDL_request which has
          caused problem at request_backoff_action() time and use it
          in recover_from_failed_open(). Similar approach is used
          for cases when problem from which we need to recover is
          not related to MDL but to the table itself. In this case
          we store pointer to the element of table list.
        - Changed open_tables()/open_tables_check_upgradable_mdl()/
          open_tables_acquire_upgradable_mdl() not to rely on
          MYSQL_OPEN_TAKE_UPGRADABLE_MDL flag to understand when
          upgradable metadata locks should be acquired and not to
          infer type of MDL lock from type of table-level lock.
          Instead we assume that type of MDL to be acquired was set
          in the parser (we can do this as type of MDL_request is
          no longer reset between re-executions).
     @ sql/sql_class.h
        Since we no longer have guarantee that MDL_request object
        which were not satisfied due to lock conflict belongs to
        table list element Open_table_context class and its methods
        were extended to remember pointer to MDL_request which has
        caused problem at request_backoff_action() time and use it
        in recover_from_failed_open(). Similar approach is used
        for cases when problem from which we need to recover is
        not related to MDL but to the table itself. In this case
        we store pointer to the element of table list.
     @ sql/sql_db.cc
        Now one needs to properly initialize table list element's
        MDL_request object before calling mysql_rm_table_part2()
        or mysql_rename_tables().
     @ sql/sql_lex.cc
        st_select_lex/st_select_lex_node::add_table_to_list() method
        now has argument which allows specify type of metadata lock
        to be requested for table list element being added.
     @ sql/sql_lex.h
        - st_select_lex/st_select_lex_node::add_table_to_list()
          method now has argument which specifies type of metadata
          lock to be requested for table list element being added.
          This allows to explicitly set type of MDL lock to be
          acquired for a DDL statement in parser. It is also more
          future-proof than inferring type of MDL request from type
          of table-level lock.
        - Added Yacc_state::m_mdl_type member which specifies which
          type of metadata lock should be requested for tables to be
          added to table list by a grammar rule in cases when the same
          rule is used in several statements requiring different kinds
          of metadata locks.
     @ sql/sql_parse.cc
        - st_select_lex::add_table_to_list() method now has argument
          which specifies type of metadata lock to be requested for
          table list element being added. This allows to explicitly
          set type of MDL lock to be acquired for a DDL statement in
          parser. It is also more future-proof than inferring type of
          MDL request from type of table-level lock.
        - EXCLUSIVE_DOWNGRADABLE_MDL lock strategy has a new name -
          OTLS_DOWNGRADE_IF_EXISTS.
        - Adjusted LOCK TABLES implementation to the fact that we no
          longer infer type of metadata lock to be acquired from table
          level lock and that type of MDL request is set at parsing.
          And thus MYSQL_OPEN_TAKE_UPGRADABLE_MDL flag became
          unnecessary.
     @ sql/sql_prepare.cc
        TABLE_LIST's lock strategy SHARED_MDL was renamed to OTLS_NONE
        as now it means that metadata lock should not be changed during
        call to open_table() (if it has been already acquired) and is
        also used for exclusive metadata lock.
     @ sql/sql_show.cc
        st_select_lex::add_table_to_list() method now has argument
        which specifies type of metadata lock to be requested for
        table list element being added.
     @ sql/sql_table.cc
        - Adjusted mysql_admin_table()'s code to the fact that
          open_tables() no longer determines what kind of metadata
          lock should be obtained basing on type of table-level
          lock and flags. Instead type of metadata lock for table
          to be open should be set before calling open_tables().
        - Changed mysql_alter_table() code to the facts:
          a) that now it is responsibility of caller to properly
          initalize MDL_request in table list elements before calling
          lock_table_names()
          b) and that MYSQL_OPEN_TAKE_UPGRADABLE_MDL is no longer
          necessary since type of metadata lock to be obtained
          at open_tables() time is set during parsing.
        - Changed code of mysql_recreate_table() to properly set
          type of metadata and table-level lock to be obtained
          by mysql_alter_table() which it calls.
     @ sql/sql_trigger.cc
        Instead of relying on MYSQL_OPEN_TAKE_UPGRADABLE_MDL flag to
        force open_tables() to take an upgradable lock we now specify
        exact type of lock to be taken when constructing table list
        element for table to be open for CREATE/DROP TRIGGER.
     @ sql/sql_view.cc
        We no longer use TABLE_LIST::EXCLUSIVE_MDL strategy to force
        open_tables() to take an exclusive metadata lock on view to
        be created. Instead we rely on parser setting proper type of
        metadata lock to request and open_tables() acquiring it.
        This became possible thanks to the fact that we no longer
        reset type of MDL_request between statement re-executions.
     @ sql/sql_yacc.yy
        Instead of inferring type of MDL_request for table to be
        open from type of table-level lock and flags passed to
        open_tables() we now explicitly specify them at parsing.
        This became possible thanks to the fact that we no longer
        reset type of MDL_request between statement re-executions.
        In future this should allow to decouple type of metadata
        lock from type of table-level lock.
        The only exception to this approach is statements implemented
        through mysql_admin_table() which re-uses same table list
        element several times with different types of table-level
        and metadata locks.
        We now also properly initialize MDL_request objects for table
        list elements which are later passed to lock_table_names()
        function.
     @ sql/table.cc
        Do not reset type of MDL_request between statement
        re-executions. This became unnecessesary as we no longer
        change type of MDL_request residing in table list element.
        In its turn this change allows to set type of MDL_request
        only once - at parsing time.
     @ sql/table.h
        Got rid of TABLE_LIST::EXCLUSIVE_MDL lock strategy.
        Now we can specify that we need to acquire exclusive lock
        on table to be processed by open_tables() through setting
        an appropriate type of MDL_request at parsing time (this
        became possible thanks to the fact that we no longer reset
        types of MDL_request's belonging to table list elements
        between statement re-execution).
        Strategy SHARED_MDL was renamed to OTLS_NONE as now it
        means that metadata lock should not be changed during call
        to open_table() (if it has been already acquired) and is
        also used for exclusive metadata lock.
        Strategy EXCLUSIVE_DOWNGRADABLE_MDL was renamed to
        OTLS_DOWNGRADE_IF_EXISTS.

    modified:
      sql/ha_ndbcluster.cc
      sql/lock.cc
      sql/lock.h
      sql/mdl.h
      sql/sp_head.cc
      sql/sp_head.h
      sql/sql_base.cc
      sql/sql_class.h
      sql/sql_db.cc
      sql/sql_lex.cc
      sql/sql_lex.h
      sql/sql_parse.cc
      sql/sql_prepare.cc
      sql/sql_show.cc
      sql/sql_table.cc
      sql/sql_trigger.cc
      sql/sql_view.cc
      sql/sql_yacc.yy
      sql/table.cc
      sql/table.h
 3021 Dmitry Lenev	2010-05-21
      Follow-up for the fix for bug #46947 "Embedded SELECT without 
      FOR UPDATE is causing a lock".
       
      This patch tries to address problems which were exposed 
      during backporting of original patch to 5.1 tree.
       
      - It ensures that we don't change locking behavior of simple
        SELECT statements on InnoDB tables when they are executed
        under LOCK TABLES ... READ and with @@innodb_table_locks=0.
        Also we no longer pass TL_READ_DEFAULT/TL_WRITE_DEFAULT 
        lock types, which are supposed to be parser-only, to 
        handler::start_stmt() method.
      - It makes check_/no_concurrent_insert.inc auxiliary scripts 
        more robust against changes in test cases that use them
        and also ensures that they don't unnecessarily change 
        environment of caller.
     @ mysql-test/include/check_concurrent_insert.inc
        Reset DEBUG_SYNC facility before and after using it in
        auxiliary script. This makes this script more robust against
        changes in test cases calling it. It also ensures that script
        does not unnecessarily change environment of caller.
     @ mysql-test/include/check_no_concurrent_insert.inc
        Reset DEBUG_SYNC facility before and after using it in
        auxiliary script. This makes this script more robust against
        changes in test cases calling it. It also ensures that script
        does not unnecessarily change environment of caller.
     @ mysql-test/r/innodb-lock.result
        Added coverage for LOCK TABLES ... READ behavior in
        @@innodb_table_locks = 0 mode. This test also checks
        that an appropriate type of lock is passed to
        handler::start_stmt() method.
     @ mysql-test/t/innodb-lock.test
        Added coverage for LOCK TABLES ... READ behavior in
        @@innodb_table_locks = 0 mode. This test also checks
        that an appropriate type of lock is passed to
        handler::start_stmt() method.
     @ sql/sql_base.cc
        Since we no longer set TL_READ as lock type for tables used  
        in simple SELECT right in the parser, in order to preserve  
        behavior for such statements on InnoDB tables when in  
        LOCK TABLES mode with @innodb_table_locks = 0,  
        check_lock_and_start_stmt() had to be changed to convert  
        TL_READ_DEFAULT to an appropriate type of read lock before  
        passing it to handler::start_stmt() method.  
        We do similar thing for TL_WRITE_DEFAULT as this lock type  
        is also supposed to be parser-only type.  
        As consequence read_lock_type_for_table() had to be  
        adjusted to behave properly when it is called from  
        check_lock_and_start_stmt() in prelocked mode.

    modified:
      mysql-test/include/check_concurrent_insert.inc
      mysql-test/include/check_no_concurrent_insert.inc
      mysql-test/r/innodb-lock.result
      mysql-test/t/innodb-lock.test
      sql/sql_base.cc
=== modified file 'sql/ha_ndbcluster.cc'
--- a/sql/ha_ndbcluster.cc	2010-05-05 22:02:08 +0000
+++ b/sql/ha_ndbcluster.cc	2010-05-25 12:35:01 +0000
@@ -7408,9 +7408,10 @@ int ndbcluster_find_files(handlerton *ht
       DBUG_PRINT("info", ("Remove table %s/%s", db, file_name_str));
       // Delete the table and all related files
       TABLE_LIST table_list;
-      bzero((char*) &table_list,sizeof(table_list));
-      table_list.db= (char*) db;
-      table_list.alias= table_list.table_name= (char*)file_name_str;
+      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_tpye(MDL_EXCLUSIVE);
       (void)mysql_rm_table_part2(thd, &table_list,
                                  FALSE,   /* if_exists */
                                  FALSE,   /* drop_temporary */ 

=== modified file 'sql/lock.cc'
--- a/sql/lock.cc	2010-03-31 14:05:33 +0000
+++ b/sql/lock.cc	2010-05-25 12:35:01 +0000
@@ -880,6 +880,8 @@ static MYSQL_LOCK *get_lock_data(THD *th
          before calling it. Also it cannot be called while holding
          LOCK_open mutex. Both these invariants are enforced by asserts
          in MDL_context::acquire_locks().
+   @note Initialization of MDL_request members of TABLE_LIST elements
+         is a responsibility of the caller.
 
    @retval FALSE  Success.
    @retval TRUE   Failure (OOM or thread was killed).
@@ -894,12 +896,7 @@ bool lock_table_names(THD *thd, TABLE_LI
   global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
 
   for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
-  {
-    lock_table->mdl_request.init(MDL_key::TABLE,
-                                 lock_table->db, lock_table->table_name,
-                                 MDL_EXCLUSIVE);
     mdl_requests.push_front(&lock_table->mdl_request);
-  }
 
   mdl_requests.push_front(&global_request);
 

=== modified file 'sql/lock.h'
--- a/sql/lock.h	2010-04-12 13:17:37 +0000
+++ b/sql/lock.h	2010-05-25 12:35:01 +0000
@@ -15,33 +15,32 @@ typedef struct st_mysql_lock MYSQL_LOCK;
 #define MYSQL_OPEN_TEMPORARY_ONLY               0x0004
 #define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY      0x0008
 #define MYSQL_LOCK_LOG_TABLE                    0x0010
-#define MYSQL_OPEN_TAKE_UPGRADABLE_MDL          0x0020
 /**
   Do not try to acquire a metadata lock on the table: we
   already have one.
 */
-#define MYSQL_OPEN_HAS_MDL_LOCK                 0x0040
+#define MYSQL_OPEN_HAS_MDL_LOCK                 0x0020
 /**
   If in locked tables mode, ignore the locked tables and get
   a new instance of the table.
 */
-#define MYSQL_OPEN_GET_NEW_TABLE                0x0080
+#define MYSQL_OPEN_GET_NEW_TABLE                0x0040
 /** Don't look up the table in the list of temporary tables. */
-#define MYSQL_OPEN_SKIP_TEMPORARY               0x0100
+#define MYSQL_OPEN_SKIP_TEMPORARY               0x0080
 /** Fail instead of waiting when conficting metadata lock is discovered. */
-#define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT         0x0200
+#define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT         0x0100
 /** Open tables using MDL_SHARED lock instead of one specified in parser. */
-#define MYSQL_OPEN_FORCE_SHARED_MDL             0x0400
+#define MYSQL_OPEN_FORCE_SHARED_MDL             0x0200
 /**
   Open tables using MDL_SHARED_HIGH_PRIO lock instead of one specified
   in parser.
 */
-#define MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL   0x0800
+#define MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL   0x0400
 /**
   When opening or locking the table, use the maximum timeout
   (LONG_TIMEOUT = 1 year) rather than the user-supplied timeout value.
 */
-#define MYSQL_LOCK_IGNORE_TIMEOUT               0x1000
+#define MYSQL_LOCK_IGNORE_TIMEOUT               0x0800
 
 /** Please refer to the internals manual. */
 #define MYSQL_OPEN_REOPEN  (MYSQL_OPEN_IGNORE_FLUSH |\

=== modified file 'sql/mdl.h'
--- a/sql/mdl.h	2010-03-24 19:15:06 +0000
+++ b/sql/mdl.h	2010-05-25 12:35:01 +0000
@@ -308,6 +308,10 @@ public:
   MDL_key key;
 
 public:
+  static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
+  { return alloc_root(mem_root, size); }
+  static void operator delete(void *ptr, MEM_ROOT *mem_root) {}
+
   void init(MDL_key::enum_mdl_namespace namespace_arg,
             const char *db_arg, const char *name_arg,
             enum_mdl_type mdl_type_arg);

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2010-04-28 10:04:11 +0000
+++ b/sql/sp_head.cc	2010-05-25 12:35:01 +0000
@@ -4010,6 +4010,11 @@ sp_head::add_used_tables_to_table_list(T
       table->prelocking_placeholder= 1;
       table->belong_to_view= belong_to_view;
       table->trg_event_map= stab->trg_event_map;
+      /*
+        Since we don't allow DDL on base tables in prelocked mode it
+        is safe to infer the type of metadata lock from the type of
+        table lock.
+      */
       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);
@@ -4040,7 +4045,8 @@ sp_head::add_used_tables_to_table_list(T
 TABLE_LIST *
 sp_add_to_query_tables(THD *thd, LEX *lex,
 		       const char *db, const char *name,
-		       thr_lock_type locktype)
+                       thr_lock_type locktype,
+                       enum_mdl_type mdl_type)
 {
   TABLE_LIST *table;
 
@@ -4055,8 +4061,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,
-                          table->lock_type >= TL_WRITE_ALLOW_WRITE ?
-                          MDL_SHARED_WRITE : MDL_SHARED_READ);
+                          mdl_type);
 
   lex->add_to_query_tables(table);
   return table;

=== modified file 'sql/sp_head.h'
--- a/sql/sp_head.h	2010-04-07 11:58:40 +0000
+++ b/sql/sp_head.h	2010-05-25 12:35:01 +0000
@@ -1346,7 +1346,9 @@ set_routine_security_ctx(THD *thd, sp_he
 TABLE_LIST *
 sp_add_to_query_tables(THD *thd, LEX *lex,
 		       const char *db, const char *name,
-		       thr_lock_type locktype);
+                       thr_lock_type locktype,
+                       enum_mdl_type mdl_type);
+
 Item *
 sp_prepare_func_item(THD* thd, Item **it_addr);
 

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2010-05-21 12:41:24 +0000
+++ b/sql/sql_base.cc	2010-05-25 12:35:01 +0000
@@ -2358,82 +2358,90 @@ void table_share_release_hook(void *shar
 
 
 /**
-   A helper function that acquires an MDL lock for a table
-   being opened.
+  Try to acquire an MDL lock for a table being opened.
+
+  @param[in,out] thd      Session context, to report errors.
+  @param[out]    ot_ctx   Open table context, to hold the back off
+                          state. If we failed to acquire a lock
+                          due to a lock conflict, we add the
+                          failed request to the open table context.
+  @param[in,out] mdl_request A request for an MDL lock.
+                          If we managed to acquire a ticket
+                          (no errors or lock conflicts occurred),
+                          contains a reference to it on
+                          return. However, is not modified if MDL
+                          lock type- modifying flags were provided.
+  @param[in]    flags flags MYSQL_OPEN_FORCE_SHARED_MDL,
+                          MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL or
+                          MYSQL_OPEN_FAIL_ON_MDL_CONFLICT
+                          @sa open_table().
+  @param[out]   mdl_ticket Only modified if there was no error.
+                          If we managed to acquire an MDL
+                          lock, contains a reference to the
+                          ticket, otherwise is set to NULL.
+
+  @retval TRUE  An error occurred.
+  @retval FALSE No error, but perhaps a lock conflict, check mdl_ticket.
 */
 
 static bool
-open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
+open_table_get_mdl_lock(THD *thd, Open_table_context *ot_ctx,
                         MDL_request *mdl_request,
-                        Open_table_context *ot_ctx,
-                        uint flags)
+                        uint flags,
+                        MDL_ticket **mdl_ticket)
 {
-  if (table_list->lock_strategy)
+  if (flags & (MYSQL_OPEN_FORCE_SHARED_MDL |
+               MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL))
   {
-    MDL_request_list mdl_requests;
-    MDL_request *global_request;
     /*
-      In case of CREATE TABLE .. If NOT EXISTS .. SELECT, the table
-      may not yet exist. Let's acquire an exclusive lock for that
-      case. If later it turns out the table existsed, we will
-      downgrade the lock to shared. Note that, according to the
-      locking protocol, all exclusive locks must be acquired before
-      shared locks. This invariant is preserved here and is also
-      enforced by asserts in metadata locking subsystem.
-    */
-
-    mdl_request->set_type(MDL_EXCLUSIVE);
-    DBUG_ASSERT(! thd->mdl_context.has_locks() ||
-                thd->handler_tables_hash.records ||
-                thd->global_read_lock.is_acquired());
+      MYSQL_OPEN_FORCE_SHARED_MDL flag means that we are executing
+      PREPARE for a prepared statement and want to override
+      the type-of-operation aware metadata lock which was set
+      in the parser/during view opening with a simple shared
+      metadata lock.
+      This is necessary to allow concurrent execution of PREPARE
+      and LOCK TABLES WRITE statement against the same table.
+
+      MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL flag means that we open
+      the table in order to get information about it for one of I_S
+      queries and also want to override the type-of-operation aware
+      shared metadata lock which was set earlier (e.g. during view
+      opening) with a high-priority shared metadata lock.
+      This is necessary to avoid unnecessary waiting and extra
+      ER_WARN_I_S_SKIPPED_TABLE warnings when accessing I_S tables.
 
-    if (!(global_request= ot_ctx->get_global_mdl_request(thd)))
-      return 1;
+      These two flags are mutually exclusive.
+    */
+    DBUG_ASSERT(!(flags & MYSQL_OPEN_FORCE_SHARED_MDL) ||
+                !(flags & MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL));
 
-    mdl_requests.push_front(mdl_request);
-    mdl_requests.push_front(global_request);
+    mdl_request= new (thd->mem_root) MDL_request(mdl_request);
+    if (mdl_request == NULL)
+      return TRUE;
 
-    if (thd->mdl_context.acquire_locks(&mdl_requests, ot_ctx->get_timeout()))
-      return 1;
+    mdl_request->set_type((flags & MYSQL_OPEN_FORCE_SHARED_MDL) ?
+                          MDL_SHARED : MDL_SHARED_HIGH_PRIO);
   }
-  else
-  {
-    if (flags & MYSQL_OPEN_FORCE_SHARED_MDL)
-    {
-      /*
-        While executing PREPARE for prepared statement we override
-        type-of-operation aware type of shared metadata lock which
-        was set in the parser with simple shared metadata lock.
-        This is necessary to allow concurrent execution of PREPARE
-        and LOCK TABLES WRITE statement which locks one of the tables
-        used in the statement being prepared.
-      */
-      DBUG_ASSERT(!(flags & (MYSQL_OPEN_TAKE_UPGRADABLE_MDL |
-                             MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)));
 
-      mdl_request->set_type(MDL_SHARED);
-    }
-    else if (flags & MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)
-    {
-      DBUG_ASSERT(!(flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL));
-      mdl_request->set_type(MDL_SHARED_HIGH_PRIO);
-    }
-
-    ot_ctx->add_request(mdl_request);
+  ot_ctx->add_request(mdl_request);
 
-    if (thd->mdl_context.try_acquire_lock(mdl_request))
-      return 1;
+  if (thd->mdl_context.try_acquire_lock(mdl_request))
+    return TRUE;
 
-    if (mdl_request->ticket == NULL)
+  if (mdl_request->ticket == NULL)
+  {
+    if (flags & MYSQL_OPEN_FAIL_ON_MDL_CONFLICT)
     {
-      if (flags & MYSQL_OPEN_FAIL_ON_MDL_CONFLICT)
-        my_error(ER_WARN_I_S_SKIPPED_TABLE, MYF(0), table_list->db, table_list->table_name);
-      else
-        ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_MDL_LOCK);
-      return 1;
+      my_error(ER_WARN_I_S_SKIPPED_TABLE, MYF(0),
+               mdl_request->key.db_name(), mdl_request->key.name());
+      return TRUE;
     }
+    if (ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_MDL_LOCK,
+                                   mdl_request, NULL))
+      return TRUE;
   }
-  return 0;
+  *mdl_ticket= mdl_request->ticket;
+  return FALSE;
 }
 
 
@@ -2468,11 +2476,9 @@ open_table_get_mdl_lock(THD *thd, TABLE_
     is never opened. In both cases, metadata locks are always taken according
     to the lock strategy.
 
-    This function will take a exclusive metadata lock on the table if
-    TABLE_LIST::lock_strategy is EXCLUSIVE_DOWNGRADABLE_MDL or EXCLUSIVE_MDL.
-    If the lock strategy is EXCLUSIVE_DOWNGRADABLE_MDL and opening the table
-    is successful, the exclusive metadata lock is downgraded to a shared
-    lock.
+    If the lock strategy is OTLS_DOWNGRADE_IF_EXISTS and opening the table
+    is successful, the exclusive metadata lock acquired by the caller
+    is downgraded to a shared lock.
 
   RETURN
     TRUE  Open failed. "action" parameter may contain type of action
@@ -2490,7 +2496,6 @@ bool open_table(THD *thd, TABLE_LIST *ta
   char	key[MAX_DBKEY_LENGTH];
   uint	key_length;
   char	*alias= table_list->alias;
-  MDL_request *mdl_request;
   MDL_ticket *mdl_ticket;
   int error;
   TABLE_SHARE *share;
@@ -2528,7 +2533,8 @@ bool open_table(THD *thd, TABLE_LIST *ta
 
     if (thd->version != refresh_version)
     {
-      (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC);
+      (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC,
+                                            NULL, NULL);
       DBUG_RETURN(TRUE);
     }
   }
@@ -2701,23 +2707,25 @@ bool open_table(THD *thd, TABLE_LIST *ta
     This is the normal use case.
   */
 
-  mdl_request= &table_list->mdl_request;
   if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
   {
-    if (open_table_get_mdl_lock(thd, table_list, mdl_request, ot_ctx, flags))
+    if (open_table_get_mdl_lock(thd, ot_ctx, &table_list->mdl_request,
+                                flags, &mdl_ticket) ||
+        mdl_ticket == NULL)
     {
       DEBUG_SYNC(thd, "before_open_table_wait_refresh");
       DBUG_RETURN(TRUE);
     }
     DEBUG_SYNC(thd, "after_open_table_mdl_shared");
   }
-
-  /*
-    Grab reference to the granted MDL lock ticket. Must be done after
-    open_table_get_mdl_lock as the lock on the table might have been
-    acquired previously (MYSQL_OPEN_HAS_MDL_LOCK).
-  */
-  mdl_ticket= mdl_request->ticket;
+  else
+  {
+    /*
+      Grab reference to the MDL lock ticket that was acquired
+      by the caller.
+    */
+    mdl_ticket= table_list->mdl_request.ticket;
+  }
 
   hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
   mysql_mutex_lock(&LOCK_open);
@@ -2737,7 +2745,8 @@ bool open_table(THD *thd, TABLE_LIST *ta
   {
     /* Someone did a refresh while thread was opening tables */
     mysql_mutex_unlock(&LOCK_open);
-    (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC);
+    (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC,
+                                          NULL, NULL);
     DBUG_RETURN(TRUE);
   }
 
@@ -2878,7 +2887,8 @@ bool open_table(THD *thd, TABLE_LIST *ta
        */
       release_table_share(share);
       mysql_mutex_unlock(&LOCK_open);
-      (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC);
+      (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC,
+                                            NULL, NULL);
       DBUG_RETURN(TRUE);
     }
     /* Force close at once after usage */
@@ -2918,12 +2928,14 @@ bool open_table(THD *thd, TABLE_LIST *ta
       if (error == 7)
       {
         share->version= 0;
-        (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER);
+        (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER,
+                                              NULL, table_list);
       }
       else if (share->crashed)
       {
         share->version= 0;
-        (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR);
+        (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR,
+                                              NULL, table_list);
       }
 
       goto err_unlock;
@@ -2947,7 +2959,7 @@ bool open_table(THD *thd, TABLE_LIST *ta
     table exists now we should downgrade our exclusive metadata
     lock on this table to SW metadata lock.
   */
-  if (table_list->lock_strategy == TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL &&
+  if (table_list->lock_strategy == TABLE_LIST::OTLS_DOWNGRADE_IF_EXISTS &&
       !(flags & MYSQL_OPEN_HAS_MDL_LOCK))
     mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_WRITE);
 
@@ -3782,6 +3794,8 @@ end_with_lock_open:
 
 Open_table_context::Open_table_context(THD *thd, ulong timeout)
   :m_action(OT_NO_ACTION),
+   m_failed_mdl_request(NULL),
+   m_failed_table(NULL),
    m_start_of_statement_svp(thd->mdl_context.mdl_savepoint()),
    m_has_locks((thd->in_multi_stmt_transaction_mode() &&
                 thd->mdl_context.has_locks()) ||
@@ -3801,10 +3815,8 @@ MDL_request *Open_table_context::get_glo
 {
   if (! m_global_mdl_request)
   {
-    char *buff;
-    if ((buff= (char*)thd->alloc(sizeof(MDL_request))))
+    if ((m_global_mdl_request= new (thd->mem_root) MDL_request()))
     {
-      m_global_mdl_request= new (buff) MDL_request();
       m_global_mdl_request->init(MDL_key::GLOBAL, "", "",
                                  MDL_INTENTION_EXCLUSIVE);
     }
@@ -3823,7 +3835,8 @@ MDL_request *Open_table_context::get_glo
 
 bool
 Open_table_context::
-request_backoff_action(enum_open_table_action action_arg)
+request_backoff_action(enum_open_table_action action_arg,
+                       MDL_request *mdl_request, TABLE_LIST *table)
 {
   /*
     We are inside a transaction that already holds locks and have
@@ -3847,6 +3860,19 @@ request_backoff_action(enum_open_table_a
     return TRUE;
   }
   m_action= action_arg;
+  /*
+    If waiting for metadata lock is requested, a pointer to
+    MDL_request object for which we were unable to acquire the
+    lock is required.
+  */
+  DBUG_ASSERT(m_action != OT_WAIT_MDL_LOCK || mdl_request);
+  m_failed_mdl_request= mdl_request;
+  /*
+    If auto-repair or discovery are requested, a pointer to table
+    list element must be provided.
+  */
+  DBUG_ASSERT((m_action != OT_DISCOVER && m_action != OT_REPAIR) || table);
+  m_failed_table= table;
   return FALSE;
 }
 
@@ -3855,10 +3881,6 @@ request_backoff_action(enum_open_table_a
    Recover from failed attempt of open table by performing requested action.
 
    @param  thd     Thread context
-   @param  mdl_request MDL_request of the object that caused the problem.
-   @param  table   Optional (can be NULL). Used only if action is OT_REPAIR.
-                   In that case a TABLE_LIST for the table to be repaired.
-                   @todo: It's unnecessary and should be removed.
 
    @pre This function should be called only with "action" != OT_NO_ACTION
         and after having called @sa close_tables_for_reopen().
@@ -3869,8 +3891,7 @@ request_backoff_action(enum_open_table_a
 
 bool
 Open_table_context::
-recover_from_failed_open(THD *thd, MDL_request *mdl_request,
-                         TABLE_LIST *table)
+recover_from_failed_open(THD *thd)
 {
   bool result= FALSE;
   /*
@@ -3882,7 +3903,8 @@ recover_from_failed_open(THD *thd, MDL_r
   switch (m_action)
   {
     case OT_WAIT_MDL_LOCK:
-      result= thd->mdl_context.wait_for_lock(mdl_request, get_timeout());
+      result= thd->mdl_context.wait_for_lock(m_failed_mdl_request,
+                                             get_timeout());
       break;
     case OT_WAIT_TDC:
       result= tdc_wait_for_old_versions(thd, &m_mdl_requests, get_timeout());
@@ -3891,7 +3913,7 @@ recover_from_failed_open(THD *thd, MDL_r
     case OT_DISCOVER:
       {
         MDL_request mdl_global_request;
-        MDL_request mdl_xlock_request(mdl_request);
+        MDL_request mdl_xlock_request(&m_failed_table->mdl_request);
         MDL_request_list mdl_requests;
 
         mdl_global_request.init(MDL_key::GLOBAL, "", "",
@@ -3905,14 +3927,11 @@ recover_from_failed_open(THD *thd, MDL_r
              thd->mdl_context.acquire_locks(&mdl_requests, get_timeout())))
           break;
 
-        DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE);
         mysql_mutex_lock(&LOCK_open);
-        tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
-                         mdl_request->key.db_name(),
-                         mdl_request->key.name());
-        ha_create_table_from_engine(thd,
-                                    mdl_request->key.db_name(),
-                                    mdl_request->key.name());
+        tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
+                         m_failed_table->table_name);
+        ha_create_table_from_engine(thd, m_failed_table->db,
+                                    m_failed_table->table_name);
         mysql_mutex_unlock(&LOCK_open);
 
         thd->warning_info->clear_warning_info(thd->query_id);
@@ -3923,7 +3942,7 @@ recover_from_failed_open(THD *thd, MDL_r
     case OT_REPAIR:
       {
         MDL_request mdl_global_request;
-        MDL_request mdl_xlock_request(mdl_request);
+        MDL_request mdl_xlock_request(&m_failed_table->mdl_request);
         MDL_request_list mdl_requests;
 
         mdl_global_request.init(MDL_key::GLOBAL, "", "",
@@ -3937,14 +3956,12 @@ recover_from_failed_open(THD *thd, MDL_r
              thd->mdl_context.acquire_locks(&mdl_requests, get_timeout())))
           break;
 
-        DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE);
         mysql_mutex_lock(&LOCK_open);
-        tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
-                         mdl_request->key.db_name(),
-                         mdl_request->key.name());
+        tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
+                         m_failed_table->table_name);
         mysql_mutex_unlock(&LOCK_open);
 
-        result= auto_repair_table(thd, table);
+        result= auto_repair_table(thd, m_failed_table);
         thd->mdl_context.release_transactional_locks();
         break;
       }
@@ -3953,6 +3970,13 @@ recover_from_failed_open(THD *thd, MDL_r
   }
   /* Remove all old requests, they will be re-added. */
   m_mdl_requests.empty();
+  /*
+    Reset the pointers to conflicting MDL request and the
+    TABLE_LIST element, set when we need auto-discovery or repair,
+    for safety.
+  */
+  m_failed_mdl_request= NULL;
+  m_failed_table= NULL;
   /* Prepare for possible another back-off. */
   m_action= OT_NO_ACTION;
   return result;
@@ -4081,7 +4105,8 @@ open_and_process_routine(THD *thd, Query
         if (rt->mdl_request.ticket == NULL)
         {
           /* A lock conflict. Someone's trying to modify SP metadata. */
-          ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_MDL_LOCK);
+          ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_MDL_LOCK,
+                                         &rt->mdl_request, NULL);
           DBUG_RETURN(TRUE);
         }
         DEBUG_SYNC(thd, "after_shared_lock_pname");
@@ -4228,12 +4253,14 @@ open_and_process_table(THD *thd, LEX *le
     */
     if (tables->view)
     {
+      MDL_ticket *mdl_ticket;
       /*
         We still need to take a MDL lock on the merged view to protect
         it from concurrent changes.
       */
-      if (!open_table_get_mdl_lock(thd, tables, &tables->mdl_request,
-                                   ot_ctx, flags))
+      if (!open_table_get_mdl_lock(thd, ot_ctx, &tables->mdl_request,
+                                   flags, &mdl_ticket) &&
+          mdl_ticket != NULL)
         goto process_view_routines;
       /* Fall-through to return error. */
     }
@@ -4423,6 +4450,8 @@ end:
                        should be acquired.
   @param tables_end    End of list of tables.
   @param ot_ctx        Context of open_tables() operation.
+  @param flags         Bitmap of flags to modify how the tables will be
+                       open, see open_table() description for details.
 
   @retval FALSE  Success.
   @retval TRUE   Failure (e.g. connection was killed)
@@ -4431,31 +4460,30 @@ end:
 static bool
 open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
                                    TABLE_LIST *tables_end,
-                                   Open_table_context *ot_ctx)
+                                   Open_table_context *ot_ctx,
+                                   uint flags)
 {
   MDL_request_list mdl_requests;
   TABLE_LIST *table;
 
   DBUG_ASSERT(!thd->locked_tables_mode);
-  DEBUG_SYNC(thd, "open_tables_acquire_upgradable_mdl");
 
   for (table= tables_start; table && table != tables_end;
        table= table->next_global)
   {
-    if (table->lock_type >= TL_WRITE_ALLOW_WRITE &&
+    if (table->mdl_request.type >= MDL_SHARED_NO_WRITE &&
         !(table->open_type == OT_TEMPORARY_ONLY ||
+          (flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
           (table->open_type != OT_BASE_ONLY &&
+           ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
            find_temporary_table(thd, table))))
-    {
-      table->mdl_request.set_type(table->lock_type > TL_WRITE_ALLOW_READ ?
-                                  MDL_SHARED_NO_READ_WRITE :
-                                  MDL_SHARED_NO_WRITE);
       mdl_requests.push_front(&table->mdl_request);
-    }
   }
 
   if (! mdl_requests.is_empty())
   {
+    DEBUG_SYNC(thd, "open_tables_acquire_upgradable_mdl");
+
     MDL_request *global_request= ot_ctx->get_global_mdl_request(thd);
 
     if (global_request == NULL)
@@ -4469,11 +4497,8 @@ open_tables_acquire_upgradable_mdl(THD *
   for (table= tables_start; table && table != tables_end;
        table= table->next_global)
   {
-    if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
-    {
+    if (table->mdl_request.type >= MDL_SHARED_NO_WRITE)
       table->mdl_request.ticket= NULL;
-      table->mdl_request.set_type(MDL_SHARED_WRITE);
-    }
   }
 
   return FALSE;
@@ -4489,6 +4514,8 @@ open_tables_acquire_upgradable_mdl(THD *
   @param tables_start  Start of list of tables on which upgradable locks
                        should be searched for.
   @param tables_end    End of list of tables.
+  @param flags         Bitmap of flags to modify how the tables will be
+                       open, see open_table() description for details.
 
   @retval FALSE  Success.
   @retval TRUE   Failure (e.g. connection was killed)
@@ -4496,7 +4523,7 @@ open_tables_acquire_upgradable_mdl(THD *
 
 static bool
 open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
-                                 TABLE_LIST *tables_end)
+                                 TABLE_LIST *tables_end, uint flags)
 {
   TABLE_LIST *table;
 
@@ -4505,9 +4532,11 @@ open_tables_check_upgradable_mdl(THD *th
   for (table= tables_start; table && table != tables_end;
        table= table->next_global)
   {
-    if (table->lock_type >= TL_WRITE_ALLOW_WRITE &&
+    if (table->mdl_request.type >= MDL_SHARED_NO_WRITE &&
         !(table->open_type == OT_TEMPORARY_ONLY ||
+          (flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
           (table->open_type != OT_BASE_ONLY &&
+           ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
            find_temporary_table(thd, table))))
     {
       /*
@@ -4519,8 +4548,14 @@ open_tables_check_upgradable_mdl(THD *th
         lock, all other instances of TABLE for the same table will have the
         same ticket.
 
-        Note that find_table_for_mdl_upgrade() will report an error if a
-        ticket is not found.
+        Note that this works OK even for CREATE TABLE statements which
+        request X type of metadata lock. This is because under LOCK TABLES
+        such statements don't create the table but only check if it exists
+        or, in most complex case, only insert into it.
+        Thus SNRW lock should be enough.
+
+        Note that find_table_for_mdl_upgrade() will report an error if
+        no suitable ticket is found.
       */
       if (!find_table_for_mdl_upgrade(thd->open_tables, table->db,
                                       table->table_name, FALSE))
@@ -4612,21 +4647,19 @@ restart:
     (in non-LOCK TABLES mode) we might have to acquire upgradable
     semi-exclusive metadata locks (SNW or SNRW) on some of the
     tables to be opened.
-    So we acquire all such locks at once here as doing this in one
+    When executing CREATE TABLE .. If NOT EXISTS .. SELECT, the
+    table may not yet exist, in which case we acquire an exclusive
+    lock.
+    We acquire all such locks at once here as doing this in one
     by one fashion may lead to deadlocks or starvation. Later when
     we will be opening corresponding table pre-acquired metadata
     lock will be reused (thanks to the fact that in recursive case
     metadata locks are acquired without waiting).
   */
-  if (flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL)
+  if (! (flags & (MYSQL_OPEN_HAS_MDL_LOCK |
+                  MYSQL_OPEN_FORCE_SHARED_MDL |
+                  MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)))
   {
-    /*
-      open_tables_acquire_upgradable_mdl() does not currenly handle
-      these two flags. At this point, that does not matter as they
-      are not used together with MYSQL_OPEN_TAKE_UPGRADABLE_MDL.
-    */
-    DBUG_ASSERT(!(flags & (MYSQL_OPEN_SKIP_TEMPORARY |
-                           MYSQL_OPEN_TEMPORARY_ONLY)));
     if (thd->locked_tables_mode)
     {
       /*
@@ -4634,7 +4667,8 @@ restart:
         need to check if appropriate locks were pre-acquired.
       */
       if (open_tables_check_upgradable_mdl(thd, *start,
-                                           thd->lex->first_not_own_table()))
+                                           thd->lex->first_not_own_table(),
+                                           flags))
       {
         error= TRUE;
         goto err;
@@ -4642,7 +4676,7 @@ restart:
     }
     else if (open_tables_acquire_upgradable_mdl(thd, *start,
                                                 thd->lex->first_not_own_table(),
-                                                &ot_ctx))
+                                                &ot_ctx, flags))
     {
       error= TRUE;
       goto err;
@@ -4688,7 +4722,6 @@ restart:
             have failed to open since closing tables can trigger removal of
             elements from the table list (if MERGE tables are involved),
           */
-          TABLE_LIST *failed_table= *table_to_open;
           close_tables_for_reopen(thd, start, ot_ctx.start_of_statement_svp());
 
           /*
@@ -4696,8 +4729,7 @@ restart:
             TABLE_LIST element. Altough currently this assumption is valid
             it may change in future.
           */
-          if (ot_ctx.recover_from_failed_open(thd, &failed_table->mdl_request,
-                                              failed_table))
+          if (ot_ctx.recover_from_failed_open(thd))
             goto err;
 
           error= FALSE;
@@ -4741,7 +4773,7 @@ restart:
           {
             close_tables_for_reopen(thd, start,
                                     ot_ctx.start_of_statement_svp());
-            if (ot_ctx.recover_from_failed_open(thd, &rt->mdl_request, NULL))
+            if (ot_ctx.recover_from_failed_open(thd))
               goto err;
 
             error= FALSE;
@@ -5163,6 +5195,9 @@ TABLE *open_ltable(THD *thd, TABLE_LIST 
   /* open_ltable can be used only for BASIC TABLEs */
   table_list->required_type= FRMTYPE_TABLE;
 
+  /* This function can't properly handle requests for such metadata locks. */
+  DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_NO_WRITE);
+
   while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx, lock_flags)) &&
          ot_ctx.can_recover_from_failed_open())
   {
@@ -5173,8 +5208,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST 
     */
     thd->mdl_context.rollback_to_savepoint(ot_ctx.start_of_statement_svp());
     table_list->mdl_request.ticket= 0;
-    if (ot_ctx.recover_from_failed_open(thd, &table_list->mdl_request,
-                                        table_list))
+    if (ot_ctx.recover_from_failed_open(thd))
       break;
   }
 

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2010-05-18 12:52:51 +0000
+++ b/sql/sql_class.h	2010-05-25 12:35:01 +0000
@@ -1329,9 +1329,9 @@ public:
   };
   Open_table_context(THD *thd, ulong timeout);
 
-  bool recover_from_failed_open(THD *thd, MDL_request *mdl_request,
-                                TABLE_LIST *table);
-  bool request_backoff_action(enum_open_table_action action_arg);
+  bool recover_from_failed_open(THD *thd);
+  bool request_backoff_action(enum_open_table_action action_arg,
+                              MDL_request *mdl_request, TABLE_LIST *table);
 
   void add_request(MDL_request *request)
   { m_mdl_requests.push_front(request); }
@@ -1362,6 +1362,14 @@ private:
   MDL_request_list m_mdl_requests;
   /** Back off action. */
   enum enum_open_table_action m_action;
+  /** For OT_WAIT_MDL_LOCK action, the request for which we should wait. */
+  MDL_request *m_failed_mdl_request;
+  /**
+    For OT_DISCOVER and OT_REPAIR actions, the table list element for
+    the table which definition should be re-discovered or which
+    should be repaired.
+  */
+  TABLE_LIST *m_failed_table;
   MDL_ticket *m_start_of_statement_svp;
   /**
     Whether we had any locks when this context was created.

=== modified file 'sql/sql_db.cc'
--- a/sql/sql_db.cc	2010-04-07 11:58:40 +0000
+++ b/sql/sql_db.cc	2010-05-25 12:35:01 +0000
@@ -1203,6 +1203,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);
       /* Link into list */
       (*tot_list_next)= table_list;
       tot_list_next= &table_list->next_local;
@@ -1918,9 +1920,11 @@ bool mysql_upgrade_db(THD *thd, LEX_STRI
       Table_ident *new_ident= new Table_ident(thd, new_db, table_str, 0);
       if (!old_ident || !new_ident ||
           !sl->add_table_to_list(thd, old_ident, NULL,
-                                 TL_OPTION_UPDATING, TL_IGNORE) ||
+                                 TL_OPTION_UPDATING, TL_IGNORE,
+                                 MDL_EXCLUSIVE) ||
           !sl->add_table_to_list(thd, new_ident, NULL,
-                                 TL_OPTION_UPDATING, TL_IGNORE))
+                                 TL_OPTION_UPDATING, TL_IGNORE,
+                                 MDL_EXCLUSIVE))
       {
         error= 1;
         my_dirend(dirp);

=== modified file 'sql/sql_lex.cc'
--- a/sql/sql_lex.cc	2010-05-18 12:52:51 +0000
+++ b/sql/sql_lex.cc	2010-05-25 12:35:01 +0000
@@ -1978,6 +1978,7 @@ TABLE_LIST *st_select_lex_node::add_tabl
 						  LEX_STRING *alias,
 						  ulong table_join_options,
 						  thr_lock_type flags,
+                                                  enum_mdl_type mdl_type,
 						  List<Index_hint> *hints,
                                                   LEX_STRING *option)
 {

=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h	2010-05-18 12:52:51 +0000
+++ b/sql/sql_lex.h	2010-05-25 12:35:01 +0000
@@ -502,6 +502,7 @@ public:
 					LEX_STRING *alias,
 					ulong table_options,
 					thr_lock_type flags= TL_UNLOCK,
+                                        enum_mdl_type mdl_type= MDL_SHARED_READ,
 					List<Index_hint> *hints= 0,
                                         LEX_STRING *option= 0);
   virtual void set_lock_for_tables(thr_lock_type lock_type) {}
@@ -799,6 +800,7 @@ public:
 				LEX_STRING *alias,
 				ulong table_options,
 				thr_lock_type flags= TL_UNLOCK,
+                                enum_mdl_type mdl_type= MDL_SHARED_READ,
 				List<Index_hint> *hints= 0,
                                 LEX_STRING *option= 0);
   TABLE_LIST* get_table_list();
@@ -2249,6 +2251,7 @@ public:
     yacc_yyvs= NULL;
     m_set_signal_info.clear();
     m_lock_type= TL_READ_DEFAULT;
+    m_mdl_type= MDL_SHARED_READ;
   }
 
   ~Yacc_state();
@@ -2260,6 +2263,7 @@ public:
   void reset_before_substatement()
   {
     m_lock_type= TL_READ_DEFAULT;
+    m_mdl_type= MDL_SHARED_READ;
   }
 
   /**
@@ -2299,6 +2303,12 @@ public:
   */
   thr_lock_type m_lock_type;
 
+  /**
+    The type of requested metadata lock for tables added to
+    the statement table list.
+  */
+  enum_mdl_type m_mdl_type;
+
   /*
     TODO: move more attributes from the LEX structure here.
   */

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2010-05-18 12:52:51 +0000
+++ b/sql/sql_parse.cc	2010-05-25 12:35:01 +0000
@@ -1606,7 +1606,8 @@ int prepare_schema_table(THD *thd, LEX *
     /* 'parent_lex' is used in init_query() so it must be before it. */
     schema_select_lex->parent_lex= lex;
     schema_select_lex->init_query();
-    if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ))
+    if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
+                                              MDL_SHARED_READ))
       DBUG_RETURN(1);
     lex->query_tables_last= query_tables_last;
     break;
@@ -2544,7 +2545,7 @@ case SQLCOM_PREPARE:
 
     /* Set strategies: reset default or 'prepared' values. */
     create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
-    create_table->lock_strategy= TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL;
+    create_table->lock_strategy= TABLE_LIST::OTLS_DOWNGRADE_IF_EXISTS;
 
     /*
       Close any open handlers for the table
@@ -3502,16 +3503,13 @@ end_with_restore_list:
         thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
       goto error;
 
-    init_mdl_requests(all_tables);
-
     thd->variables.option_bits|= OPTION_TABLE_LOCK;
     thd->in_lock_tables=1;
 
     {
       Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
 
-      res= (open_and_lock_tables(thd, all_tables, FALSE,
-                                 MYSQL_OPEN_TAKE_UPGRADABLE_MDL,
+      res= (open_and_lock_tables(thd, all_tables, FALSE, 0,
                                  &lock_tables_prelocking_strategy) ||
             thd->locked_tables_list.init_locked_tables(thd));
     }
@@ -6014,6 +6012,7 @@ bool add_to_list(THD *thd, SQL_LIST &lis
                          - TL_OPTION_FORCE_INDEX : Force usage of index
                          - TL_OPTION_ALIAS : an alias in multi table DELETE
   @param lock_type	How table should be locked
+  @param mdl_type       Type of metadata lock to acquire on the table.
   @param use_index	List of indexed used in USE INDEX
   @param ignore_index	List of indexed used in IGNORE INDEX
 
@@ -6028,6 +6027,7 @@ TABLE_LIST *st_select_lex::add_table_to_
 					     LEX_STRING *alias,
 					     ulong table_options,
 					     thr_lock_type lock_type,
+					     enum_mdl_type mdl_type,
 					     List<Index_hint> *index_hints_arg,
                                              LEX_STRING *option)
 {
@@ -6175,9 +6175,7 @@ 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,
-                        (ptr->lock_type >= TL_WRITE_ALLOW_WRITE) ?
-                        MDL_SHARED_WRITE : MDL_SHARED_READ);
+  ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type);
   DBUG_RETURN(ptr);
 }
 

=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc	2010-05-05 22:02:08 +0000
+++ b/sql/sql_prepare.cc	2010-05-25 12:35:01 +0000
@@ -1690,7 +1690,7 @@ static bool mysql_test_create_table(Prep
      for the prepare phase.
    */
   create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
-  create_table->lock_strategy= TABLE_LIST::SHARED_MDL;
+  create_table->lock_strategy= TABLE_LIST::OTLS_NONE;
 
   if (select_lex->item_list.elements)
   {

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2010-04-28 10:04:11 +0000
+++ b/sql/sql_show.cc	2010-05-25 12:35:01 +0000
@@ -2358,7 +2358,7 @@ int make_table_list(THD *thd, SELECT_LEX
   Table_ident *table_ident;
   table_ident= new Table_ident(thd, *db_name, *table_name, 1);
   sel->init_query();
-  if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ))
+  if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, MDL_SHARED_READ))
     return 1;
   return 0;
 }
@@ -6582,7 +6582,7 @@ int make_schema_select(THD *thd, SELECT_
                        strlen(schema_table->table_name), 0);
   if (schema_table->old_format(thd, schema_table) ||   /* Handle old syntax */
       !sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0),
-                              0, 0, TL_READ))
+                              0, 0, TL_READ, MDL_SHARED_READ))
   {
     DBUG_RETURN(1);
   }

=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc	2010-05-13 09:24:59 +0000
+++ b/sql/sql_table.cc	2010-05-25 12:35:01 +0000
@@ -4656,6 +4656,14 @@ static bool mysql_admin_table(THD* thd, 
     strxmov(table_name, db, ".", table->table_name, NullS);
     thd->open_options|= extra_open_options;
     table->lock_type= lock_type;
+    /*
+      To make code safe for re-execution we need to reset type of MDL
+      request as code below may change it.
+      To allow concurrent execution of read-only operations we acquire
+      weak metadata lock for them.
+    */
+    table->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_READ) ?
+                                MDL_SHARED_NO_READ_WRITE : MDL_SHARED_READ);
     /* open only one table from local list of command */
     {
       TABLE_LIST *save_next_global, *save_next_local;
@@ -4677,8 +4685,7 @@ static bool mysql_admin_table(THD* thd, 
       if (view_operator_func == NULL)
         table->required_type=FRMTYPE_TABLE;
 
-      open_error= open_and_lock_tables(thd, table, TRUE,
-                                       MYSQL_OPEN_TAKE_UPGRADABLE_MDL);
+      open_error= open_and_lock_tables(thd, table, TRUE, 0);
       thd->no_warnings_for_error= 0;
       table->next_global= save_next_global;
       table->next_local= save_next_local;
@@ -5024,6 +5031,7 @@ send_result_message:
         /* Clear the ticket released in close_thread_tables(). */
         table->mdl_request.ticket= NULL;
         DEBUG_SYNC(thd, "ha_admin_open_ltable");
+        table->mdl_request.set_type(MDL_SHARED_WRITE);
         if ((table->table= open_ltable(thd, table, lock_type, 0)))
         {
           result_code= table->table->file->ha_analyze(thd, check_opt);
@@ -5461,6 +5469,7 @@ mysql_discard_or_import_tablespace(THD *
    not complain when we lock the table
  */
   thd->tablespace_op= TRUE;
+  table_list->mdl_request.set_type(MDL_SHARED_WRITE);
   if (!(table=open_ltable(thd, table_list, TL_WRITE, 0)))
   {
     thd->tablespace_op=FALSE;
@@ -6568,6 +6577,12 @@ bool mysql_alter_table(THD *thd,char *ne
 
     if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
       DBUG_RETURN(TRUE);
+
+    /*
+      TODO/FIXME: Get rid of this code branch if possible. To add insult
+                  to injury it breaks locking protocol.
+    */
+    table_list->mdl_request.set_type(MDL_EXCLUSIVE);
     if (lock_table_names(thd, table_list))
     {
       error= 1;
@@ -6608,8 +6623,7 @@ view_err:
 
   Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info);
 
-  error= open_and_lock_tables(thd, table_list, FALSE,
-                              MYSQL_OPEN_TAKE_UPGRADABLE_MDL,
+  error= open_and_lock_tables(thd, table_list, FALSE, 0,
                               &alter_prelocking_strategy);
 
   if (error)
@@ -7904,6 +7918,10 @@ bool mysql_recreate_table(THD *thd, TABL
   table_list->table= NULL;
   /* Same applies to MDL ticket. */
   table_list->mdl_request.ticket= NULL;
+  /* Set lock type which is appropriate for ALTER TABLE. */
+  table_list->lock_type= TL_WRITE_ALLOW_READ;
+  /* Same applies to MDL request. */
+  table_list->mdl_request.set_type(MDL_SHARED_NO_WRITE);
 
   bzero((char*) &create_info, sizeof(create_info));
   create_info.row_type=ROW_TYPE_NOT_USED;

=== modified file 'sql/sql_trigger.cc'
--- a/sql/sql_trigger.cc	2010-04-28 10:04:11 +0000
+++ b/sql/sql_trigger.cc	2010-05-25 12:35:01 +0000
@@ -489,8 +489,7 @@ bool mysql_create_or_drop_trigger(THD *t
   else
   {
     tables->table= open_n_lock_single_table(thd, tables,
-                                            TL_WRITE_ALLOW_READ,
-                                            MYSQL_OPEN_TAKE_UPGRADABLE_MDL);
+                                            TL_WRITE_ALLOW_READ, 0);
     if (! tables->table)
       goto end;
     tables->table->use_all_columns();
@@ -1667,7 +1666,8 @@ bool add_table_for_trigger(THD *thd,
     DBUG_RETURN(TRUE);
 
   *table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str,
-                                 tbl_name.str, TL_IGNORE);
+                                 tbl_name.str, TL_IGNORE,
+                                 MDL_SHARED_NO_WRITE);
 
   DBUG_RETURN(*table ? FALSE : TRUE);
 }

=== modified file 'sql/sql_view.cc'
--- a/sql/sql_view.cc	2010-04-07 11:58:40 +0000
+++ b/sql/sql_view.cc	2010-05-25 12:35:01 +0000
@@ -433,7 +433,7 @@ bool mysql_create_view(THD *thd, TABLE_L
 
   lex->link_first_table_back(view, link_to_local);
   view->open_strategy= TABLE_LIST::OPEN_STUB;
-  view->lock_strategy= TABLE_LIST::EXCLUSIVE_MDL;
+  view->lock_strategy= TABLE_LIST::OTLS_NONE;
   view->open_type= OT_BASE_ONLY;
 
   if (open_and_lock_tables(thd, lex->query_tables, TRUE, 0))

=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy	2010-04-28 10:04:11 +0000
+++ b/sql/sql_yacc.yy	2010-05-25 12:35:01 +0000
@@ -697,7 +697,8 @@ static bool add_create_index_prepare (LE
   lex->sql_command= SQLCOM_CREATE_INDEX;
   if (!lex->current_select->add_table_to_list(lex->thd, table, NULL,
                                               TL_OPTION_UPDATING,
-                                              TL_WRITE_ALLOW_READ))
+                                              TL_WRITE_ALLOW_READ,
+                                              MDL_SHARED_NO_WRITE))
     return TRUE;
   lex->alter_info.reset();
   lex->alter_info.flags= ALTER_ADD_INDEX;
@@ -2023,7 +2024,7 @@ create:
             lex->sql_command= SQLCOM_CREATE_TABLE;
             if (!lex->select_lex.add_table_to_list(thd, $5, NULL,
                                                    TL_OPTION_UPDATING,
-                                                   TL_WRITE))
+                                                   TL_WRITE, MDL_EXCLUSIVE))
               MYSQL_YYABORT;
             lex->alter_info.reset();
             lex->col_list.empty();
@@ -4213,7 +4214,8 @@ create2:
 
             lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
             src_table= lex->select_lex.add_table_to_list(thd, $2, NULL, 0,
-                                                         TL_READ);
+                                                         TL_READ,
+                                                         MDL_SHARED_READ);
             if (! src_table)
               MYSQL_YYABORT;
             /* CREATE TABLE ... LIKE is not allowed for views. */
@@ -4227,7 +4229,8 @@ create2:
 
             lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
             src_table= lex->select_lex.add_table_to_list(thd, $3, NULL, 0,
-                                                         TL_READ);
+                                                         TL_READ,
+                                                         MDL_SHARED_READ);
             if (! src_table)
               MYSQL_YYABORT;
             /* CREATE TABLE ... LIKE is not allowed for views. */
@@ -6154,7 +6157,8 @@ alter:
             lex->duplicates= DUP_ERROR; 
             if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
                                                    TL_OPTION_UPDATING,
-                                                   TL_WRITE_ALLOW_READ))
+                                                   TL_WRITE_ALLOW_READ,
+                                                   MDL_SHARED_NO_WRITE))
               MYSQL_YYABORT;
             lex->col_list.empty();
             lex->select_lex.init_order();
@@ -6847,6 +6851,8 @@ checksum:
           {
             LEX *lex=Lex;
             lex->sql_command = SQLCOM_CHECKSUM;
+            /* Will be overriden during execution. */
+            YYPS->m_lock_type= TL_UNLOCK;
           }
           table_list opt_checksum_type
           {}
@@ -6866,6 +6872,8 @@ repair:
             lex->no_write_to_binlog= $2;
             lex->check_opt.init();
             lex->alter_info.reset();
+            /* Will be overriden during execution. */
+            YYPS->m_lock_type= TL_UNLOCK;
           }
           table_list opt_mi_repair_type
           {}
@@ -6895,6 +6903,8 @@ analyze:
             lex->no_write_to_binlog= $2;
             lex->check_opt.init();
             lex->alter_info.reset();
+            /* Will be overriden during execution. */
+            YYPS->m_lock_type= TL_UNLOCK;
           }
           table_list
           {}
@@ -6921,6 +6931,8 @@ check:
             lex->sql_command = SQLCOM_CHECK;
             lex->check_opt.init();
             lex->alter_info.reset();
+            /* Will be overriden during execution. */
+            YYPS->m_lock_type= TL_UNLOCK;
           }
           table_list opt_mi_check_type
           {}
@@ -6953,6 +6965,8 @@ optimize:
             lex->no_write_to_binlog= $2;
             lex->check_opt.init();
             lex->alter_info.reset();
+            /* Will be overriden during execution. */
+            YYPS->m_lock_type= TL_UNLOCK;
           }
           table_list
           {}
@@ -7001,9 +7015,9 @@ table_to_table:
             LEX *lex=Lex;
             SELECT_LEX *sl= lex->current_select;
             if (!sl->add_table_to_list(lex->thd, $1,NULL,TL_OPTION_UPDATING,
-                                       TL_IGNORE) ||
+                                       TL_IGNORE, MDL_EXCLUSIVE) ||
                 !sl->add_table_to_list(lex->thd, $3,NULL,TL_OPTION_UPDATING,
-                                       TL_IGNORE))
+                                       TL_IGNORE, MDL_EXCLUSIVE))
               MYSQL_YYABORT;
           }
         ;
@@ -7034,7 +7048,8 @@ keycache_list:
 assign_to_keycache:
           table_ident cache_keys_spec
           {
-            if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ, 
+            if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ,
+                                           MDL_SHARED_READ,
                                            Select->pop_index_hints()))
               MYSQL_YYABORT;
           }
@@ -7044,6 +7059,7 @@ assign_to_keycache_parts:
           table_ident adm_partition cache_keys_spec
           {
             if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ, 
+                                           MDL_SHARED_READ,
                                            Select->pop_index_hints()))
               MYSQL_YYABORT;
           }
@@ -7079,6 +7095,7 @@ preload_keys:
           table_ident cache_keys_spec opt_ignore_leaves
           {
             if (!Select->add_table_to_list(YYTHD, $1, NULL, $3, TL_READ,
+                                           MDL_SHARED_READ,
                                            Select->pop_index_hints()))
               MYSQL_YYABORT;
           }
@@ -7088,6 +7105,7 @@ preload_keys_parts:
           table_ident adm_partition cache_keys_spec opt_ignore_leaves
           {
             if (!Select->add_table_to_list(YYTHD, $1, NULL, $4, TL_READ,
+                                           MDL_SHARED_READ,
                                            Select->pop_index_hints()))
               MYSQL_YYABORT;
           }
@@ -9220,6 +9238,7 @@ table_factor:
             if (!($$= Select->add_table_to_list(YYTHD, $2, $3,
                                                 Select->get_table_join_options(),
                                                 YYPS->m_lock_type,
+                                                YYPS->m_mdl_type,
                                                 Select->pop_index_hints())))
               MYSQL_YYABORT;
             Select->add_joined_table($$);
@@ -9291,7 +9310,7 @@ table_factor:
                 MYSQL_YYABORT;
               if (!($$= sel->add_table_to_list(lex->thd,
                                                new Table_ident(unit), $5, 0,
-                                               TL_READ)))
+                                               TL_READ, MDL_SHARED_READ)))
 
                 MYSQL_YYABORT;
               sel->add_joined_table($$);
@@ -10126,13 +10145,17 @@ do:
 */
 
 drop:
-          DROP opt_temporary table_or_tables if_exists table_list opt_restrict
+          DROP opt_temporary table_or_tables if_exists
           {
             LEX *lex=Lex;
             lex->sql_command = SQLCOM_DROP_TABLE;
             lex->drop_temporary= $2;
             lex->drop_if_exists= $4;
+            YYPS->m_lock_type= TL_IGNORE;
+            YYPS->m_mdl_type= MDL_EXCLUSIVE;
           }
+          table_list opt_restrict
+          {}
         | DROP INDEX_SYM ident ON table_ident {}
           {
             LEX *lex=Lex;
@@ -10145,7 +10168,8 @@ drop:
             lex->alter_info.drop_list.push_back(ad);
             if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,
                                                         TL_OPTION_UPDATING,
-                                                        TL_WRITE_ALLOW_READ))
+                                                        TL_WRITE_ALLOW_READ,
+                                                        MDL_SHARED_NO_WRITE))
               MYSQL_YYABORT;
           }
         | DROP DATABASE if_exists ident
@@ -10215,12 +10239,16 @@ drop:
           {
             Lex->sql_command = SQLCOM_DROP_USER;
           }
-        | DROP VIEW_SYM if_exists table_list opt_restrict
+        | DROP VIEW_SYM if_exists
           {
             LEX *lex= Lex;
             lex->sql_command= SQLCOM_DROP_VIEW;
             lex->drop_if_exists= $3;
+            YYPS->m_lock_type= TL_IGNORE;
+            YYPS->m_mdl_type= MDL_EXCLUSIVE;
           }
+          table_list opt_restrict
+          {}
         | DROP EVENT_SYM if_exists sp_name
           {
             Lex->drop_if_exists= $3;
@@ -10261,7 +10289,10 @@ table_list:
 table_name:
           table_ident
           {
-            if (!Select->add_table_to_list(YYTHD, $1, NULL, TL_OPTION_UPDATING))
+            if (!Select->add_table_to_list(YYTHD, $1, NULL,
+                                           TL_OPTION_UPDATING,
+                                           YYPS->m_lock_type,
+                                           YYPS->m_mdl_type))
               MYSQL_YYABORT;
           }
         ;
@@ -10276,7 +10307,8 @@ table_alias_ref:
           {
             if (!Select->add_table_to_list(YYTHD, $1, NULL,
                                            TL_OPTION_UPDATING | TL_OPTION_ALIAS,
-                                           YYPS->m_lock_type))
+                                           YYPS->m_lock_type,
+                                           YYPS->m_mdl_type))
               MYSQL_YYABORT;
           }
         ;
@@ -10558,6 +10590,8 @@ delete:
             lex->sql_command= SQLCOM_DELETE;
             mysql_init_select(lex);
             YYPS->m_lock_type= TL_WRITE_DEFAULT;
+            YYPS->m_mdl_type= MDL_SHARED_WRITE;
+
             lex->ignore= 0;
             lex->select_lex.init_order();
           }
@@ -10568,9 +10602,11 @@ single_multi:
           FROM table_ident
           {
             if (!Select->add_table_to_list(YYTHD, $2, NULL, TL_OPTION_UPDATING,
-                                           YYPS->m_lock_type))
+                                           YYPS->m_lock_type,
+                                           YYPS->m_mdl_type))
               MYSQL_YYABORT;
             YYPS->m_lock_type= TL_READ_DEFAULT;
+            YYPS->m_mdl_type= MDL_SHARED_READ;
           }
           where_clause opt_order_clause
           delete_limit_clause {}
@@ -10578,6 +10614,7 @@ single_multi:
           {
             mysql_init_multi_delete(Lex);
             YYPS->m_lock_type= TL_READ_DEFAULT;
+            YYPS->m_mdl_type= MDL_SHARED_READ;
           }
           FROM join_table_list where_clause
           {
@@ -10588,6 +10625,7 @@ single_multi:
           {
             mysql_init_multi_delete(Lex);
             YYPS->m_lock_type= TL_READ_DEFAULT;
+            YYPS->m_mdl_type= MDL_SHARED_READ;
           }
           USING join_table_list where_clause
           {
@@ -10611,7 +10649,8 @@ table_wild_one:
                                            ti,
                                            NULL,
                                            TL_OPTION_UPDATING | TL_OPTION_ALIAS,
-                                           YYPS->m_lock_type))
+                                           YYPS->m_lock_type,
+                                           YYPS->m_mdl_type))
               MYSQL_YYABORT;
           }
         | ident '.' ident opt_wild
@@ -10623,7 +10662,8 @@ table_wild_one:
                                            ti,
                                            NULL,
                                            TL_OPTION_UPDATING | TL_OPTION_ALIAS,
-                                           YYPS->m_lock_type))
+                                           YYPS->m_lock_type,
+                                           YYPS->m_mdl_type))
               MYSQL_YYABORT;
           }
         ;
@@ -11137,7 +11177,15 @@ flush:
 
 flush_options:
           table_or_tables
-          { Lex->type|= REFRESH_TABLES; }
+          {
+            Lex->type|= REFRESH_TABLES;
+            /*
+              Set type of metadata and table locks for
+              FLUSH TABLES table_list WITH READ LOCK.
+            */
+            YYPS->m_lock_type= TL_READ_NO_INSERT;
+            YYPS->m_mdl_type= MDL_EXCLUSIVE;
+          }
           opt_table_list {}
           opt_with_read_lock {}
         | flush_options_list
@@ -11301,7 +11349,7 @@ load:
           {
             LEX *lex=Lex;
             if (!Select->add_table_to_list(YYTHD, $12, NULL, TL_OPTION_UPDATING,
-                                           $4))
+                                           $4, MDL_SHARED_WRITE))
               MYSQL_YYABORT;
             lex->field_list.empty();
             lex->update_list.empty();
@@ -13007,10 +13055,14 @@ table_lock:
           table_ident opt_table_alias lock_option
           {
             thr_lock_type lock_type= (thr_lock_type) $3;
-            if (!Select->add_table_to_list(YYTHD, $1, $2, 0, lock_type))
+            bool lock_for_write= (lock_type >= TL_WRITE_ALLOW_WRITE);
+            if (!Select->add_table_to_list(YYTHD, $1, $2, 0, lock_type,
+                                           (lock_for_write ?
+                                            MDL_SHARED_NO_READ_WRITE :
+                                            MDL_SHARED_READ)))
               MYSQL_YYABORT;
             /* If table is to be write locked, protect from a impending GRL. */
-            if (lock_type >= TL_WRITE_ALLOW_WRITE)
+            if (lock_for_write)
               Lex->protect_against_global_read_lock= TRUE;
           }
         ;
@@ -13765,6 +13817,7 @@ query_expression_option:
             if (check_simple_select())
               MYSQL_YYABORT;
             YYPS->m_lock_type= TL_READ_HIGH_PRIORITY;
+            YYPS->m_mdl_type= MDL_SHARED_READ;
             Select->options|= SELECT_HIGH_PRIORITY;
           }
         | DISTINCT         { Select->options|= SELECT_DISTINCT; }
@@ -13894,7 +13947,10 @@ view_tail:
             LEX *lex= thd->lex;
             lex->sql_command= SQLCOM_CREATE_VIEW;
             /* first table in list is target VIEW name */
-            if (!lex->select_lex.add_table_to_list(thd, $3, NULL, TL_OPTION_UPDATING))
+            if (!lex->select_lex.add_table_to_list(thd, $3, NULL,
+                                                   TL_OPTION_UPDATING,
+                                                   TL_IGNORE,
+                                                   MDL_EXCLUSIVE))
               MYSQL_YYABORT;
           }
           view_list_opt AS view_select
@@ -14034,7 +14090,8 @@ trigger_tail:
             if (!lex->select_lex.add_table_to_list(YYTHD, $9,
                                                    (LEX_STRING*) 0,
                                                    TL_OPTION_UPDATING,
-                                                   TL_IGNORE))
+                                                   TL_WRITE_ALLOW_READ,
+                                                   MDL_SHARED_NO_WRITE))
               MYSQL_YYABORT;
           }
         ;

=== modified file 'sql/table.cc'
--- a/sql/table.cc	2010-04-07 11:58:40 +0000
+++ b/sql/table.cc	2010-05-25 12:35:01 +0000
@@ -4659,13 +4659,6 @@ void TABLE_LIST::reinit_before_use(THD *
          parent_embedding->nested_join->join_list.head() == embedded);
 
   mdl_request.ticket= NULL;
-  /*
-    Since we manipulate with the metadata lock type in open_table(),
-    we need to reset it to the parser default, to restore things back
-    to first-execution state.
-  */
-  mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE) ?
-                       MDL_SHARED_WRITE : MDL_SHARED_READ);
 }
 
 /*

=== modified file 'sql/table.h'
--- a/sql/table.h	2010-04-12 13:17:37 +0000
+++ b/sql/table.h	2010-05-25 12:35:01 +0000
@@ -1581,20 +1581,21 @@ struct TABLE_LIST
     OPEN_STUB
   } open_strategy;
   /**
-    Indicates the locking strategy for the object being opened:
-    whether the associated metadata lock is shared or exclusive.
+    Indicates the locking strategy for the object being opened.
   */
   enum
   {
-    /* Take a shared metadata lock before the object is opened. */
-    SHARED_MDL= 0,
     /*
-       Take a exclusive metadata lock before the object is opened.
-       If opening is successful, downgrade to a shared lock.
+      Take metadata lock specified by 'mdl_request' member before
+      the object is opened. Do nothing after that.
     */
-    EXCLUSIVE_DOWNGRADABLE_MDL,
-    /* Take a exclusive metadata lock before the object is opened. */
-    EXCLUSIVE_MDL
+    OTLS_NONE= 0,
+    /*
+      Take (exclusive) metadata lock specified by 'mdl_request' member
+      before object is opened. If opening is successful, downgrade to
+      a shared lock.
+    */
+    OTLS_DOWNGRADE_IF_EXISTS
   } lock_strategy;
   /* For transactional locking. */
   int           lock_timeout;           /* NOWAIT or WAIT [X]               */


Attachment: [text/bzr-bundle] bzr/dlenev@mysql.com-20100525123501-dx90if0j53ukimom.bundle
Thread
bzr push into mysql-trunk-runtime branch (dlenev:3021 to 3022)Bug#51263Dmitry Lenev25 May