List:Commits« Previous MessageNext Message »
From:Dmitry Lenev Date:November 3 2010 2:31pm
Subject:bzr push into mysql-5.5-runtime branch (Dmitry.Lenev:3186 to 3187)
View as plain text  
 3187 Dmitry Lenev	2010-11-03
      More changes to draft patch refactoring global read
      lock implementation. Makes GRL yet another type of
      metadata lock and thus exposes it to deadlock detector
      in MDL subsystem.
      
      Solves bugs #54673 "It takes too long to get readlock for
      'FLUSH TABLES WITH READ LOCK'" and #57006 "Deadlock between
      HANDLER and FLUSH TABLES WITH READ LOCK".
      
      Work-in-progress. Introduce concept of duration in MDL
      subsystem. Replace notion of transactional sentinel
      with locks with explicit duration.

    modified:
      sql/event_db_repository.cc
      sql/handler.cc
      sql/lock.cc
      sql/log_event.cc
      sql/mdl.cc
      sql/mdl.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_parse.cc
      sql/sql_prepare.cc
      sql/sql_show.cc
      sql/sql_table.cc
      sql/table.cc
      sql/table.h
      sql/transaction.cc
 3186 Konstantin Osipov	2010-10-28
      Review comments.

    modified:
      sql/event_db_repository.cc
      sql/handler.cc
      sql/sp_head.cc
      sql/transaction.cc
=== modified file 'sql/event_db_repository.cc'
--- a/sql/event_db_repository.cc	2010-10-28 19:35:28 +0000
+++ b/sql/event_db_repository.cc	2010-11-03 14:30:33 +0000
@@ -627,7 +627,7 @@ Event_db_repository::create_event(THD *t
     table at the end but keep the global read lock and
     possible other locks taken by the caller.
   */
-  MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
 
   DBUG_ENTER("Event_db_repository::create_event");
 
@@ -745,7 +745,7 @@ Event_db_repository::update_event(THD *t
     table at the end but keep the global read lock and
     possible other locks taken by the caller.
   */
-  MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
   int ret= 1;
 
   DBUG_ENTER("Event_db_repository::update_event");
@@ -855,7 +855,7 @@ Event_db_repository::drop_event(THD *thd
     table at the end but keep the global read lock and
     possible other locks taken by the caller.
   */
-  MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+  MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
   int ret= 1;
 
   DBUG_ENTER("Event_db_repository::drop_event");
@@ -958,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));
 

=== modified file 'sql/handler.cc'
--- a/sql/handler.cc	2010-10-28 19:35:28 +0000
+++ b/sql/handler.cc	2010-11-03 14:30:33 +0000
@@ -1176,7 +1176,8 @@ int ha_commit_trans(THD *thd, bool all)
         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_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE,
+                       MDL_EXPLICIT);
 
       if (thd->mdl_context.acquire_lock(&mdl_request,
                                         thd->variables.lock_wait_timeout))

=== modified file 'sql/lock.cc'
--- a/sql/lock.cc	2010-10-26 04:25:15 +0000
+++ b/sql/lock.cc	2010-11-03 14:30:33 +0000
@@ -790,7 +790,7 @@ bool lock_schema_name(THD *thd, const ch
 
   thd->global_read_lock.init_protection_request(&global_request);
 
-  mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE);
+  mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE, MDL_TRANSACTION);
 
   mdl_requests.push_front(&mdl_request);
   mdl_requests.push_front(&global_request);
@@ -857,8 +857,9 @@ bool lock_object_name(THD *thd, MDL_key:
   DBUG_ASSERT(stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END));
 
   thd->global_read_lock.init_protection_request(&global_request);
-  schema_request.init(MDL_key::SCHEMA, db, "", MDL_INTENTION_EXCLUSIVE);
-  mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE);
+  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);
@@ -982,13 +983,12 @@ bool Global_read_lock::lock_global_read_
 
     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))
       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;
   }
@@ -1060,33 +1060,8 @@ bool Global_read_lock::can_acquire_prote
 
 void Global_read_lock::init_protection_request(MDL_request *request)
 {
-  request->init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
-}
-
-
-/**
-  Release protection against global read lock if it is set.
-
-  See also "Handling of global read locks" above.
-
-  @param thd     Reference to thread.
-*/
-
-void Global_read_lock::release_protection_if_set(THD *thd)
-{
-  MDL_request mdl_request;
-  MDL_ticket *ticket;
-
-  DBUG_ENTER("release_protection_if_set");
-
-  mdl_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
-
-  if ((ticket= thd->mdl_context.find_ticket_at_front(&mdl_request)))
-  {
-    thd->mdl_context.release_lock(ticket);
-  }
-
-  DBUG_VOID_RETURN;
+  request->init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE,
+                MDL_STATEMENT);
 }
 
 
@@ -1116,13 +1091,12 @@ bool Global_read_lock::make_global_read_
   if (m_state != GRL_ACQUIRED)
     DBUG_RETURN(0);
 
-  mdl_request.init(MDL_key::COMMIT, "", "", MDL_SHARED);
+  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);
 
-  thd->mdl_context.move_ticket_after_trans_sentinel(mdl_request.ticket);
   m_mdl_blocks_commits_lock= mdl_request.ticket;
   m_state= GRL_ACQUIRED_AND_BLOCKS_COMMIT;
 
@@ -1131,18 +1105,17 @@ bool Global_read_lock::make_global_read_
 
 
 /**
-  Move tickets for metadata locks which are used to implement GRL after
-  transactional sentinel.
+  Set explicit duration for metadata locks which are used to implement GRL.
 
   @param thd     Reference to thread.
 */
 
-void Global_read_lock::move_tickets_after_trans_sentinel(THD *thd)
+void Global_read_lock::set_explicit_lock_duration(THD *thd)
 {
   if (m_mdl_global_shared_lock)
-    thd->mdl_context.move_ticket_after_trans_sentinel(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.move_ticket_after_trans_sentinel(m_mdl_blocks_commits_lock);
+    thd->mdl_context.set_lock_duration(m_mdl_blocks_commits_lock, MDL_EXPLICIT);
 }
 
 /**

=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc	2010-10-18 12:33:49 +0000
+++ b/sql/log_event.cc	2010-11-03 14:30:33 +0000
@@ -4960,7 +4960,7 @@ error:
   if (! thd->in_multi_stmt_transaction_mode())
     thd->mdl_context.release_transactional_locks();
   else
-    thd->global_read_lock.release_protection_if_set(thd);
+    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-10-27 09:55:52 +0000
+++ b/sql/mdl.cc	2010-11-03 14:30:33 +0000
@@ -731,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)
 {
@@ -754,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);
 }
@@ -783,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;
 }
 
@@ -801,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.
@@ -880,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)
-{
-  return new MDL_ticket(ctx_arg, 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
+#ifndef DBUG_OFF
+                        , duration_arg
+#endif
+                        );
 }
 
 
@@ -1451,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.
@@ -1467,48 +1444,27 @@ 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;
-}
-
-
-/**
-  Check whether the ticket at the front of context's ticket list
-  is compatible with request.
-
-  @param mdl_request  Lock request object for ticket to find.
-
-  @note Tickets which correspond to lock types "stronger" than one
-        being requested are also considered compatible.
-
-  @return A pointer to the lock ticket for the object or NULL otherwise.
-*/
-
-MDL_ticket * MDL_context::find_ticket_at_front(MDL_request *mdl_request)
-{
-  MDL_ticket *ticket= m_tickets.front();
-
-  if (ticket &&
-      mdl_request->key.is_equal(&ticket->m_lock->key) &&
-      ticket->has_stronger_or_equal_type(mdl_request->type))
-    return ticket;
-
   return NULL;
 }
 
@@ -1587,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));
@@ -1601,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));
@@ -1623,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;
@@ -1632,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. */
@@ -1650,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;
   }
@@ -1685,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. */
@@ -1698,7 +1664,7 @@ 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;
 }
@@ -1894,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;
 
@@ -1935,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)
@@ -2004,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");
@@ -2021,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);
@@ -2045,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);
   }
 
@@ -2306,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");
@@ -2319,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;
@@ -2332,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.
@@ -2388,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);
   }
 }
 
@@ -2453,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);
 
@@ -2484,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;
 }
@@ -2513,78 +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
 }
 
 
 /**
-  Move ticket to the front of the context's ticket list.
-
-  @param mdl_ticket  Ticket to move.
+  Set transactional duration for all locks in the context.
 */
 
-void MDL_context::move_ticket_to_front(MDL_ticket *mdl_ticket)
+void MDL_context::set_transaction_duration_for_all_locks()
 {
-  m_tickets.remove(mdl_ticket);
-  m_tickets.push_front(mdl_ticket);
+  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-10-25 15:16:12 +0000
+++ b/sql/mdl.h	2010-11-03 14:30:33 +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)
 
@@ -310,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.
@@ -332,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
@@ -368,6 +378,7 @@ public:
 
   MDL_request(const MDL_request *rhs)
     :type(rhs->type),
+    duration(rhs->duration),
     ticket(NULL),
     key(&rhs->key)
   {}
@@ -489,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.
   */
@@ -517,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.
 */
 
@@ -564,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;
@@ -589,38 +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();
+    return !(m_tickets[MDL_STATEMENT].is_empty() &&
+             m_tickets[MDL_TRANSACTION].is_empty() &&
+             m_tickets[MDL_EXPLICIT].is_empty());
   }
 
-  MDL_ticket *mdl_savepoint()
+  MDL_savepoint 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 MDL_savepoint(m_tickets[MDL_STATEMENT].front(),
+                         m_tickets[MDL_TRANSACTION].front());
   }
 
-  void set_trans_sentinel()
-  {
-    m_trans_sentinel= m_tickets.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 move_ticket_to_front(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; }
 
@@ -661,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
@@ -715,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.
+      involved schemas and global intention exclusive lock.
   */
-  Ticket_list m_tickets;
-  /**
-    Separates transactional and non-transactional locks
-    in m_tickets list, @sa m_tickets.
-  */
-  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
@@ -751,13 +793,11 @@ private:
     readily available to the wait-for graph iterator.
    */
   MDL_wait_for_subgraph *m_waiting_for;
-public:
-  MDL_ticket *find_ticket(MDL_request *mdl_req,
-                          bool *is_transactional);
-
-  MDL_ticket *find_ticket_at_front(MDL_request *mdl_req);
 private:
-  void release_locks_stored_before(MDL_ticket *sentinel);
+  MDL_ticket *find_ticket(MDL_request *mdl_req,
+                          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/rpl_rli.cc'
--- a/sql/rpl_rli.cc	2010-10-18 12:33:49 +0000
+++ b/sql/rpl_rli.cc	2010-11-03 14:30:33 +0000
@@ -1275,7 +1275,7 @@ void Relay_log_info::slave_close_thread_
   if (! thd->in_multi_stmt_transaction_mode())
     thd->mdl_context.release_transactional_locks();
   else
-    thd->global_read_lock.release_protection_if_set(thd);
+    thd->mdl_context.release_statement_locks();
 
   clear_tables_to_lock();
 }

=== modified file 'sql/sp.cc'
--- a/sql/sp.cc	2010-10-25 15:16:12 +0000
+++ b/sql/sp.cc	2010-11-03 14:30:33 +0000
@@ -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);
@@ -1370,7 +1370,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));
 
@@ -1694,7 +1694,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-28 19:35:28 +0000
+++ b/sql/sp_head.cc	2010-11-03 14:30:33 +0000
@@ -2129,7 +2129,7 @@ sp_head::execute_procedure(THD *thd, Lis
     else if (! thd->locked_tables_mode)
     {
       DBUG_ASSERT(! thd->in_sub_stmt);
-      thd->global_read_lock.release_protection_if_set(thd);
+      thd->mdl_context.release_statement_locks();
     }
 
     thd->rollback_item_tree_changes();
@@ -2967,7 +2967,7 @@ sp_lex_keeper::reset_lex_and_exec_core(T
     if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode())
       thd->mdl_context.release_transactional_locks();
     else if (! thd->in_sub_stmt && !thd->locked_tables_mode)
-      thd->global_read_lock.release_protection_if_set(thd);
+      thd->mdl_context.release_statement_locks();
   }
 
   if (m_lex->query_tables_own_last)
@@ -4163,7 +4163,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 */
 
@@ -4207,7 +4208,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-03 14:30:33 +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-22 13:26:29 +0000
+++ b/sql/sql_base.cc	2010-11-03 14:30:33 +0000
@@ -2450,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;
   }
 
@@ -2812,7 +2813,8 @@ bool open_table(THD *thd, TABLE_LIST *ta
                   MYSQL_OPEN_FORCE_SHARED_MDL |
                   MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
                   MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) &&
-      ! thd->locked_tables_mode)
+      ! thd->locked_tables_mode &&
+      ! ot_ctx->has_protection_against_grl())
   {
     MDL_request protection_request;
     MDL_deadlock_handler mdl_deadlock_handler(ot_ctx);
@@ -2833,8 +2835,7 @@ bool open_table(THD *thd, TABLE_LIST *ta
     if (result)
       DBUG_RETURN(TRUE);
 
-    ot_ctx->set_protection_against_grl(&thd->mdl_context,
-                                       protection_request.ticket);
+    ot_ctx->set_has_protection_against_grl();
   }
 
 
@@ -3872,7 +3873,7 @@ Open_table_context::Open_table_context(T
    m_flags(flags),
    m_action(OT_NO_ACTION),
    m_has_locks(thd->mdl_context.has_locks()),
-   m_protection_against_grl(NULL)
+   m_has_protection_against_grl(FALSE)
 {}
 
 
@@ -4030,11 +4031,11 @@ recover_from_failed_open(THD *thd)
   */
   m_failed_table= NULL;
   /*
-    Reset pointer to ticket for metadata lock protecting
-    against GRL. It is no longer valid as the lock was
+    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_protection_against_grl= NULL;
+  m_has_protection_against_grl= FALSE;
   /* Prepare for possible another back-off. */
   m_action= OT_NO_ACTION;
   return result;
@@ -4573,7 +4574,8 @@ 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);
     }
 
@@ -4926,16 +4928,6 @@ err:
   thd_proc_info(thd, 0);
   free_root(&new_frm_mem, MYF(0));              // Free pre-alloced block
 
-  if (ot_ctx.get_protection_against_grl())
-  {
-    /*
-      Move ticket for metadata lock which protects this statement from
-      a GRL to the front of ticket list. This allows to find it quickly
-      when this lock has to be released at the end of statement.
-    */
-    thd->mdl_context.move_ticket_to_front(ot_ctx.get_protection_against_grl());
-  }
-
   if (error && *table_to_open)
   {
     (*table_to_open)->table= NULL;
@@ -5376,16 +5368,6 @@ end:
     close_thread_tables(thd);
   }
 
-  if (ot_ctx.get_protection_against_grl())
-  {
-    /*
-      Move ticket for metadata lock which protects this statement from
-      a GRL to the front of ticket list. This allows to find it quickly
-      when this lock has to be released at the end of statement.
-    */
-    thd->mdl_context.move_ticket_to_front(ot_ctx.get_protection_against_grl());
-  }
-
   thd_proc_info(thd, 0);
   DBUG_RETURN(table);
 }
@@ -5416,7 +5398,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));
 
@@ -5473,7 +5455,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) ||
@@ -5730,7 +5712,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-18 12:33:49 +0000
+++ b/sql/sql_base.h	2010-11-03 14:30:33 +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,
@@ -508,7 +508,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;
   }
@@ -521,19 +521,17 @@ public:
   uint get_flags() const { return m_flags; }
 
   /**
-    Set ticket for metadata lock which protects this statement against GRL
-    if it was acquired while opening tables.
+    Set flag indicating that we have already acquired metadata lock
+    protecting this statement against GRL while opening tables.
   */
-  void set_protection_against_grl(MDL_context *mdl_ctx, MDL_ticket *ticket)
+  void set_has_protection_against_grl()
   {
-    if (! m_protection_against_grl &&
-        ! mdl_ctx->has_lock(m_start_of_statement_svp, ticket))
-      m_protection_against_grl= ticket;
+    m_has_protection_against_grl= TRUE;
   }
 
-  MDL_ticket* get_protection_against_grl() const
+  bool has_protection_against_grl() const
   {
-    return m_protection_against_grl;
+    return m_has_protection_against_grl;
   }
 
 private:
@@ -543,7 +541,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.
@@ -560,12 +558,10 @@ private:
   */
   bool m_has_locks;
   /**
-    Metadata lock which protects this statement against global read lock
-    and which might needed to be individually released at the end of
-    statement execution. NULL in cases when such protection is unneeded
-    or when we know that this statement will release all locks at its end.
+    Indicates that in the process of opening tables we have acquired
+    protection against global read lock.
   */
-  MDL_ticket *m_protection_against_grl;
+  bool m_has_protection_against_grl;
 };
 
 

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2010-10-19 13:31:53 +0000
+++ b/sql/sql_class.cc	2010-11-03 14:30:33 +0000
@@ -3458,15 +3458,15 @@ void THD::set_mysys_var(struct st_my_thr
 void THD::leave_locked_tables_mode()
 {
   locked_tables_mode= LTM_NONE;
-  mdl_context.reset_trans_sentinel(NULL);
+  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.move_tickets_after_trans_sentinel(this);
+  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-26 04:25:15 +0000
+++ b/sql/sql_class.h	2010-11-03 14:30:33 +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;
 };
 
 /**
@@ -1346,10 +1346,9 @@ public:
   bool can_acquire_protection();
   bool has_read_lock() const { return (m_state != GRL_NONE); }
   void init_protection_request(MDL_request *request);
-  void release_protection_if_set(THD *thd);
   bool make_global_read_lock_block_commit(THD *thd);
   bool is_acquired() const { return m_state != GRL_NONE; }
-  void move_tickets_after_trans_sentinel(THD *thd);
+  void set_explicit_lock_duration(THD *thd);
 private:
   enum_grl_state m_state;
   /**
@@ -2699,7 +2698,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();

=== modified file 'sql/sql_db.cc'
--- a/sql/sql_db.cc	2010-07-19 08:27:53 +0000
+++ b/sql/sql_db.cc	2010-11-03 14:30:33 +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-10-18 12:33:49 +0000
+++ b/sql/sql_handler.cc	2010-11-03 14:30:33 +0000
@@ -179,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,
@@ -248,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;
 
@@ -328,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);
   }
 
@@ -965,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-03 14:30:33 +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-27 10:39:04 +0000
+++ b/sql/sql_insert.cc	2010-11-03 14:30:33 +0000
@@ -591,12 +591,7 @@ bool open_and_lock_for_insert_delayed(TH
     this or another tables (updating the same table is of course illegal,
     but such an attempt can be discovered only later during statement
     execution).
-
-    Move ticket protecting from a GRL to the front of ticket list.
-    This allows to find it quickly when this lock has to be released
-    at the end of statement.
   */
-  thd->mdl_context.move_ticket_to_front(protection_request.ticket);
 
   /*
     Reset the ticket in case we end up having to use normal insert and
@@ -2120,7 +2115,7 @@ bool delayed_get_table(THD *thd, MDL_req
       di->table_list.db= di->thd.db;
       /* 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_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;

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2010-10-26 04:25:15 +0000
+++ b/sql/sql_parse.cc	2010-11-03 14:30:33 +0000
@@ -1085,7 +1085,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))
@@ -4362,7 +4362,7 @@ finish:
   }
   else if (! thd->in_sub_stmt && !thd->locked_tables_mode)
   {
-    thd->global_read_lock.release_protection_if_set(thd);
+    thd->mdl_context.release_statement_locks();
   }
 
   DBUG_RETURN(res || thd->is_error());
@@ -5826,7 +5826,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-08-18 09:35:41 +0000
+++ b/sql/sql_prepare.cc	2010-11-03 14:30:33 +0000
@@ -3168,7 +3168,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.
@@ -3240,7 +3239,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_show.cc'
--- a/sql/sql_show.cc	2010-10-14 16:56:56 +0000
+++ b/sql/sql_show.cc	2010-11-03 14:30:33 +0000
@@ -675,7 +675,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;
@@ -3190,7 +3190,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)
   {
@@ -7749,7 +7749,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-18 12:33:49 +0000
+++ b/sql/sql_table.cc	2010-11-03 14:30:33 +0000
@@ -5579,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;
@@ -5741,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.
@@ -5759,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.
@@ -5946,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);
     }
@@ -6654,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);
   }
@@ -6718,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);
 
@@ -6731,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/table.cc'
--- a/sql/table.cc	2010-10-04 12:42:16 +0000
+++ b/sql/table.cc	2010-11-03 14:30:33 +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-03 14:30:33 +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-10-28 19:35:28 +0000
+++ b/sql/transaction.cc	2010-11-03 14:30:33 +0000
@@ -392,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);
 }
@@ -655,7 +655,8 @@ bool trans_xa_commit(THD *thd)
 
       We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does.
     */
-    mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE);
+    mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE,
+                     MDL_TRANSACTION);
 
     if (thd->mdl_context.acquire_lock(&mdl_request,
                                       thd->variables.lock_wait_timeout))

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-5.5-runtime branch (Dmitry.Lenev:3186 to 3187) Dmitry Lenev3 Nov