List:Commits« Previous MessageNext Message »
From:dlenev Date:March 28 2008 5:47pm
Subject:bk commit into 6.0 tree (dlenev:1.2564) WL#3726
View as plain text  
Below is the list of changes that have just been committed into a local
6.0 repository of dlenev.  When dlenev does a push these changes
will be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2008-03-28 20:47:06+03:00, dlenev@stripped +12 -0
  WL#3726 "DDL locking for all metadata objects".
  
  Work in progress.
  
  Fixed metadata locking in I_S implementation. To avoid deadlocks this
  and other similar code has to use high-priority metadata lock requests
  which ignore pending exclusive metadata lock requests. This patch
  implements support for this attribute. It also cleans up MDL API by
  separating type of metadata lock request (shared/exclusive) from its
  state (pending/satisfied).
  
  Added assert which catches situations in which we do something with
  table without obtaining metadata lock on it and fixed metadata locking
  in places exposed by this assert. Particularly, now we disallow opening
  of views under LOCK TABLES if they were not explicitly locked (to access
  view without problems we need to have metadata lock on it and obtaining
  such lock under LOCK TABLES might lead to deadlocks).

  BitKeeper/etc/ignore@stripped, 2008-03-28 20:46:59+03:00, dlenev@stripped +1 -0
    Added libmysqld/meta_lock.cc to the ignore list

  mysql-test/r/information_schema.result@stripped, 2008-03-28 20:46:58+03:00, dlenev@stripped +17 -0
    Additional test for WL#3726 "DDL locking for all metadata objects".
    Check that we use high-priority metadata lock requests when filling
    I_S tables.

  mysql-test/r/sp.result@stripped, 2008-03-28 20:46:58+03:00, dlenev@stripped +2 -4
    Under LOCK TABLES we no longer allow accessing views which were not
    explicitly locked. To access view we need to obtain metadata lock
    on it and doing this under LOCK TABLES may lead to deadlocks.

  mysql-test/t/information_schema.test@stripped, 2008-03-28 20:46:58+03:00, dlenev@stripped +36 -0
    Additional test for WL#3726 "DDL locking for all metadata objects".
    Check that we use high-priority metadata lock requests when filling
    I_S tables.

  mysql-test/t/sp.test@stripped, 2008-03-28 20:46:58+03:00, dlenev@stripped +3 -1
    Under LOCK TABLES we no longer allow accessing views which were not
    explicitly locked. To access view we need to obtain metadata lock
    on it and doing this under LOCK TABLES may lead to deadlocks.

  sql/lock.cc@stripped, 2008-03-28 20:46:58+03:00, dlenev@stripped +2 -1
    Moved setting of type lock from mdl_add_lock() to separate routine.

  sql/meta_lock.cc@stripped, 2008-03-28 20:46:58+03:00, dlenev@stripped +108 -41
    Separated type of metadata lock request (shared/exclusive) from its
    state (pending/satisfied). Moved setting of type lock request from
    mdl_add_lock() to separate routine.
    Added new attribute for metadata lock requests - priority. The idea is
    that high priority requests for shared lock should ignore existing
    pending requests for exclusive locks. This should allows us to avoid
    deadlocks in certain cases (e.g. in I_S implementation).
    Also MDL_SHARED and MDL_NORMAL_PRIO is now default values of attributes
    of lock request. Lock requests are created with those values, operations
    which release locks reset attributes to these values.
    Added yet another auxiliary function for checking that we have at least
    some kind of metadata lock on the object.

  sql/meta_lock.h@stripped, 2008-03-28 20:46:58+03:00, dlenev@stripped +44 -8
    Separated type of metadata lock request (shared/exclusive) from its
    state (pending/satisfied). Moved setting of type of lock request from
    mdl_add_lock() to separate routine.
    Added new attribute for metadata lock requests - priority. The idea is
    that high priority requests for shared lock should ignore existing
    pending requests for exclusive locks. This should allows us to avoid
    deadlocks in certain cases (e.g. in I_S implementation).
    Added routine to set lock request priority.
    Added yet another auxiliary function for checking that we have at least
    some kind of metadata lock on the object.

  sql/sql_base.cc@stripped, 2008-03-28 20:46:58+03:00, dlenev@stripped +37 -7
    get_table_share():
      Added assertion for catching situations in which we do something with
      table without obtaining metadata lock on it.
    open_table():
      Under LOCK TABLES disallow opening of views which were not explicitly
      locked. To access view without problems we need to have metadata lock
      on it and obtaining this lock under LOCK TABLES might lead to deadlocks.
      Ignore pending exclusive metadata locks by using high-priority lock
      requests when MYSQL_LOCK_IGNORE_FLUSH is in effect. This is needed to
      avoid deadlocks in I_S implementation and other places where we use
      this flag.
    open_tables():
      When we open view which should be processed via derived table on the
      second execution of prepared statement or stored routine we still
      should call open_table() for it in order to obtain metadata lock
      on it and prepare its security context.
    Finally adjusted code to the fact that type of metadata lock request is
    now set by separate call and not in mdl_add_lock().

  sql/sql_delete.cc@stripped, 2008-03-28 20:46:58+03:00, dlenev@stripped +2 -1
    Moved setting of type lock from mdl_add_lock() to separate routine.

  sql/sql_show.cc@stripped, 2008-03-28 20:46:58+03:00, dlenev@stripped +25 -9
    Acquire shared metadata lock when we are getting information for I_S
    table directly from TABLE_SHARE without doing full-blown table open.
    We use high priority lock request in this situation in order to avoid
    deadlocks.

  sql/sql_table.cc@stripped, 2008-03-28 20:46:58+03:00, dlenev@stripped +4 -2
    Moved setting of type lock from mdl_add_lock() to separate routine.

diff -Nrup a/BitKeeper/etc/ignore b/BitKeeper/etc/ignore
--- a/BitKeeper/etc/ignore	2008-01-25 20:40:41 +03:00
+++ b/BitKeeper/etc/ignore	2008-03-28 20:46:59 +03:00
@@ -3027,3 +3027,4 @@ win/vs8cache.txt
 ylwrap
 zlib/*.ds?
 zlib/*.vcproj
+libmysqld/meta_lock.cc
diff -Nrup a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result
--- a/mysql-test/r/information_schema.result	2008-02-07 21:21:13 +03:00
+++ b/mysql-test/r/information_schema.result	2008-03-28 20:46:58 +03:00
@@ -1673,3 +1673,20 @@ char	10	10	NULL	NULL	latin1	latin1_swedi
 drop procedure p1;
 drop procedure p2;
 drop function f1;
+drop tables if exists t1, t2, t3;
+create table t1 (i int);
+create table t2 (j int primary key auto_increment);
+lock table t2 read;
+rename table t2 to t3;;
+select table_name, column_name, data_type from information_schema.columns
+where table_schema = 'test' and table_name in ('t1', 't2');
+table_name	column_name	data_type
+t1	i	int
+t2	j	int
+select table_name, auto_increment from information_schema.tables
+where table_schema = 'test' and table_name in ('t1', 't2');
+table_name	auto_increment
+t1	NULL
+t2	1
+unlock tables;
+drop tables t1, t3;
diff -Nrup a/mysql-test/r/sp.result b/mysql-test/r/sp.result
--- a/mysql-test/r/sp.result	2008-02-07 21:21:13 +03:00
+++ b/mysql-test/r/sp.result	2008-03-28 20:46:58 +03:00
@@ -1085,11 +1085,9 @@ select f0()|
 f0()
 100
 select * from v0|
-f0()
-100
+ERROR HY000: Table 'v0' was not locked with LOCK TABLES
 select *, f0() from v0, (select 123) as d1|
-f0()	123	f0()
-100	123	100
+ERROR HY000: Table 'v0' was not locked with LOCK TABLES
 select id, f3() from t1|
 ERROR HY000: Table 't1' was not locked with LOCK TABLES
 select f4()|
diff -Nrup a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test
--- a/mysql-test/t/information_schema.test	2008-02-02 15:19:46 +03:00
+++ b/mysql-test/t/information_schema.test	2008-03-28 20:46:58 +03:00
@@ -1289,3 +1289,39 @@ from information_schema.routines;
 drop procedure p1;
 drop procedure p2;
 drop function f1;
+
+
+#
+# Additional test for WL#3726 "DDL locking for all metadata objects"
+# To avoid possible deadlocks process of filling of I_S tables should
+# use high-priority metadata lock requests when opening tables.
+# Below we just test that we really use high-priority lock request
+# since reproducing a deadlock will require much more complex test.
+--disable_warnings
+drop tables if exists t1, t2, t3;
+--enable_warnings
+create table t1 (i int);
+create table t2 (j int primary key auto_increment);
+connect (con3726_1,localhost,root,,test);
+connection con3726_1;
+lock table t2 read;
+connect (con3726_2,localhost,root,,test);
+connection con3726_2;
+# RENAME below will add two pending requests for exclusive metadata locks.
+--send rename table t2 to t3;
+connection default;
+let $wait_condition=
+  select count(*) = 1 from information_schema.processlist
+  where state = "Waiting for table" and info like "rename table t2 to t3";
+--source include/wait_condition.inc
+# These statements should not be blocked by pending lock requests
+select table_name, column_name, data_type from information_schema.columns
+  where table_schema = 'test' and table_name in ('t1', 't2');
+select table_name, auto_increment from information_schema.tables
+  where table_schema = 'test' and table_name in ('t1', 't2');
+connection con3726_1;
+unlock tables;
+connection con3726_2;
+--reap
+connection default;
+drop tables t1, t3;
diff -Nrup a/mysql-test/t/sp.test b/mysql-test/t/sp.test
--- a/mysql-test/t/sp.test	2008-02-07 21:21:13 +03:00
+++ b/mysql-test/t/sp.test	2008-03-28 20:46:58 +03:00
@@ -1308,9 +1308,11 @@ select f3()|
 select id, f3() from t1 as t11 order by id|
 # Degenerate cases work too :)
 select f0()|
+# But these should not (particularly views should be locked explicitly).
+--error 1100
 select * from v0|
+--error 1100
 select *, f0() from v0, (select 123) as d1|
-# But these should not !
 --error 1100
 select id, f3() from t1|
 --error 1100
diff -Nrup a/sql/lock.cc b/sql/lock.cc
--- a/sql/lock.cc	2008-03-04 18:21:05 +03:00
+++ b/sql/lock.cc	2008-03-28 20:46:58 +03:00
@@ -965,7 +965,8 @@ bool lock_table_names(THD *thd, TABLE_LI
     if (!(mdl= mdl_alloc_lock(0, lock_table->db, lock_table->table_name,
                               thd->mem_root)))
       goto end;
-    mdl_add_lock(&thd->mdl_context, mdl, TRUE);
+    mdl_set_lock_type(mdl, MDL_EXCLUSIVE);
+    mdl_add_lock(&thd->mdl_context, mdl);
   }
   mdl_acquire_exclusive_locks(&thd->mdl_context);
   return 0;
diff -Nrup a/sql/meta_lock.cc b/sql/meta_lock.cc
--- a/sql/meta_lock.cc	2008-03-25 14:08:22 +03:00
+++ b/sql/meta_lock.cc	2008-03-28 20:46:58 +03:00
@@ -242,6 +242,9 @@ void mdl_context_merge(MDL_CONTEXT *dst,
    by-pointer because of the underlying HASH implementation
    requires the key to be a contiguous buffer.
 
+   The initialized lock request will have MDL_SHARED type and
+   normal priority.
+
    Suggested lock types: TABLE - 0 PROCEDURE - 1 FUNCTION - 2
    Note that tables and views have the same lock type, since
    they share the same name space in the SQL standard.
@@ -253,6 +256,9 @@ void mdl_init_lock(MDL_EL *mdl, char *ke
   int4store(key, type);
   mdl->key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1;
   mdl->key= key;
+  mdl->type= MDL_SHARED;
+  mdl->state= MDL_PENDING;
+  mdl->prio= MDL_NORMAL_PRIO;
 #ifndef DBUG_OFF
   mdl->ctx= 0;
   mdl->lock= 0;
@@ -272,6 +278,9 @@ void mdl_init_lock(MDL_EL *mdl, char *ke
    @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 and
+         normal priority.
+
    @retval 0      Error
    @retval non-0  Pointer to an object representing a lock request
 */
@@ -319,16 +328,12 @@ void mdl_alloc_locks(TABLE_LIST *table_l
                       There should be no more than one context per
                       connection, to avoid deadlocks.
    @param  lock       The lock request to be added.
-   @param  exclusive  Type of the lock requested
 */
 
-void mdl_add_lock(MDL_CONTEXT *context, MDL_EL *lock, bool exclusive)
+void mdl_add_lock(MDL_CONTEXT *context, MDL_EL *lock)
 {
   DBUG_ENTER("mdl_add_lock");
-  if (exclusive)
-    lock->type= EXCLUSIVE_PENDING;
-  else
-    lock->type= SHARED_PENDING;
+  DBUG_ASSERT(lock->state == MDL_PENDING);
   DBUG_ASSERT(!lock->ctx);
   lock->ctx= context;
   context->locks.push_front(lock);
@@ -413,7 +418,7 @@ bool mdl_acquire_shared_lock(MDL_EL *l)
   MDL_LOCK *lock;
   bool result= FALSE;
 
-  DBUG_ASSERT(l->type == SHARED_PENDING);
+  DBUG_ASSERT(l->type == MDL_SHARED && l->state == MDL_PENDING);
 
   safe_mutex_assert_not_owner(&LOCK_open);
 
@@ -425,13 +430,15 @@ bool mdl_acquire_shared_lock(MDL_EL *l)
     lock->active_readers.push_front(l);
     lock->users= 1;
     my_hash_insert(&mdl_locks, (uchar*)lock);
-    l->type= SHARED;
+    l->state= MDL_ACQUIRED;
     l->lock= lock;
   }
   else
   {
-    if ((lock->active_writers.is_empty() && lock->waiting_writers.is_empty() &&
-         lock->active_readers_waiting_upgrade.is_empty()) ||
+    if ((lock->active_writers.is_empty() &&
+          (l->prio == MDL_HIGH_PRIO ||
+          lock->waiting_writers.is_empty() &&
+          lock->active_readers_waiting_upgrade.is_empty())) ||
         (!lock->active_writers.is_empty() &&
          lock->active_writers.head()->ctx == l->ctx))
     {
@@ -444,7 +451,7 @@ bool mdl_acquire_shared_lock(MDL_EL *l)
       */
       lock->active_readers.push_front(l);
       lock->users++;
-      l->type= SHARED;
+      l->state= MDL_ACQUIRED;
       l->lock= lock;
     }
     else
@@ -490,7 +497,7 @@ void mdl_acquire_exclusive_locks(MDL_CON
   pthread_mutex_lock(&LOCK_mdl);
   while ((l= it++))
   {
-    DBUG_ASSERT(l->type == EXCLUSIVE_PENDING);
+    DBUG_ASSERT(l->type == MDL_EXCLUSIVE && l->state == MDL_PENDING);
     if (!(lock= (MDL_LOCK *)hash_search(&mdl_locks, (uchar*)l->key, l->key_length)))
     {
       lock= get_lock_object();
@@ -555,7 +562,7 @@ void mdl_acquire_exclusive_locks(MDL_CON
     lock= l->lock;
     lock->waiting_writers.remove(l);
     lock->active_writers.push_front(l);
-    l->type= EXCLUSIVE;
+    l->state= MDL_ACQUIRED;
     if (lock->cached_object)
       (*lock->cached_object_release_hook)(l->lock->cached_object);
     lock->cached_object= NULL;
@@ -600,10 +607,11 @@ void mdl_upgrade_shared_lock_to_exclusiv
   pthread_mutex_lock(&LOCK_mdl);
   while ((l= it++))
     if (l->key_length == key_length && !memcmp(l->key, key, key_length) &&
-        l->type == SHARED)
+        l->type == MDL_SHARED)
     {
       DBUG_PRINT("info", ("found shared lock for upgrade"));
-      l->type= SHARED_PENDING_UPGRADE;
+      DBUG_ASSERT(l->state == MDL_ACQUIRED);
+      l->state= MDL_PENDING_UPGRADE;
       lock= l->lock;
       lock->active_readers.remove(l);
       lock->active_readers_waiting_upgrade.push_front(l);
@@ -615,8 +623,10 @@ void mdl_upgrade_shared_lock_to_exclusiv
     it.rewind();
     while ((l= it++))
     {
-      if (l->type == SHARED_PENDING_UPGRADE)
+      if (l->state == MDL_PENDING_UPGRADE)
       {
+        DBUG_ASSERT(l->type == MDL_SHARED);
+
         lock= l->lock;
 
         if ((lh= lock->active_readers.head()))
@@ -655,12 +665,14 @@ void mdl_upgrade_shared_lock_to_exclusiv
 
   it.rewind();
   while ((l= it++))
-    if (l->type == SHARED_PENDING_UPGRADE)
+    if (l->state == MDL_PENDING_UPGRADE)
     {
+      DBUG_ASSERT(l->type == MDL_SHARED);
       lock= l->lock;
       lock->active_readers_waiting_upgrade.remove(l);
       lock->active_writers.push_front(l);
-      l->type= EXCLUSIVE;
+      l->type= MDL_EXCLUSIVE;
+      l->state= MDL_ACQUIRED;
       if (lock->cached_object)
         (*lock->cached_object_release_hook)(l->lock->cached_object);
       lock->cached_object= 0;
@@ -697,7 +709,7 @@ bool mdl_try_acquire_exclusive_lock(MDL_
 {
   MDL_LOCK *lock;
 
-  DBUG_ASSERT(l->type == EXCLUSIVE_PENDING);
+  DBUG_ASSERT(l->type == MDL_EXCLUSIVE && l->state == MDL_PENDING);
 
   safe_mutex_assert_not_owner(&LOCK_open);
 
@@ -708,15 +720,15 @@ bool mdl_try_acquire_exclusive_lock(MDL_
     lock->active_writers.push_front(l);
     lock->users= 1;
     my_hash_insert(&mdl_locks, (uchar*)lock);
-    l->type= EXCLUSIVE;
+    l->state= MDL_ACQUIRED;
     l->lock= lock;
     lock= 0;
   }
   pthread_mutex_unlock(&LOCK_mdl);
 
   /*
-    FIXME: We can't leave EXCLUSIVE_PENDING locks in the list since for such
-           locks we assume that they have MDL_EL::lock properly set.
+    FIXME: We can't leave pending MDL_EXCLUSIVE lock request in the list since
+           for such locks we assume that they have MDL_EL::lock properly set.
            Long term we should clearly define relation between lock types,
            presence in the context lists and MDL_EL::lock values.
   */
@@ -773,7 +785,7 @@ void mdl_wait_for_locks(MDL_CONTEXT *con
     it.rewind();
     while ((l= it++))
     {
-      DBUG_ASSERT(l->type == SHARED_PENDING);
+      DBUG_ASSERT(l->type == MDL_SHARED && l->state == MDL_PENDING);
       if ((lock= (MDL_LOCK *)hash_search(&mdl_locks, (uchar*)l->key, l->key_length)) &&
           !(lock->active_writers.is_empty() &&
             lock->active_readers_waiting_upgrade.is_empty() &&
@@ -819,14 +831,14 @@ static void release_lock(MDL_EL *l)
   {
     switch (l->type)
     {
-      case SHARED:
+      case MDL_SHARED:
         lock->active_readers.remove(l);
         break;
-      case EXCLUSIVE_PENDING:
-        lock->waiting_writers.remove(l);
-        break;
-      case EXCLUSIVE:
-        lock->active_writers.remove(l);
+      case MDL_EXCLUSIVE:
+        if (l->state == MDL_PENDING)
+          lock->waiting_writers.remove(l);
+        else
+          lock->active_writers.remove(l);
         break;
       default:
         /* TODO Really? How about problems during lock upgrade ? */
@@ -847,6 +859,9 @@ static void release_lock(MDL_EL *l)
    It is also used to release shared locks in the end of an SQL
    statement.
 
+   Also resets lock requests back to their initial state (i.e.
+   sets type and priority to MDL_SHARED and MDL_NORMAL_PRIO).
+
    @param context The context with which the locks to be released
                   are associated.
 */
@@ -864,17 +879,22 @@ void mdl_release_locks(MDL_CONTEXT *cont
   {
     DBUG_PRINT("info", ("found lock to release l=%p", l));
     /*
-      We should not release locks which we have not obtained. Allows us
-      to avoid problems in open_tables() in case of back-off
+      We should not release locks which pending shared locks as these
+      are not associated with lock object and don't present in its
+      lists. Allows us to avoid problems in open_tables() in case of
+      back-off
     */
-    if (l->type != SHARED_PENDING)
+    if (!(l->type == MDL_SHARED && l->state == MDL_PENDING))
     {
       release_lock(l);
-      l->type= SHARED_PENDING;
+      l->state= MDL_PENDING;
 #ifndef DBUG_OFF
       l->lock= 0;
 #endif
     }
+    /* Return lock request to its initial state. */
+    l->type= MDL_SHARED;
+    l->prio= MDL_NORMAL_PRIO;
   }
   /* Inefficient but will do for a while */
   pthread_cond_broadcast(&COND_mdl);
@@ -890,6 +910,9 @@ void mdl_release_locks(MDL_CONTEXT *cont
    @param context Context with exclusive locks.
 
    @note Shared locks are left intact.
+   @note Resets lock requests for locks released back to their
+         initial state (i.e.sets type and priority to MDL_SHARED
+         and MDL_NORMAL_PRIO).
 */
 
 void mdl_release_exclusive_locks(MDL_CONTEXT *context)
@@ -902,13 +925,18 @@ void mdl_release_exclusive_locks(MDL_CON
   pthread_mutex_lock(&LOCK_mdl);
   while ((l= it++))
   {
-    if (l->type == EXCLUSIVE)
+    if (l->type == MDL_EXCLUSIVE)
     {
+      DBUG_ASSERT(l->state == MDL_ACQUIRED);
       release_lock(l);
 #ifndef DBUG_OFF
       l->ctx= 0;
       l->lock= 0;
 #endif
+      l->state= MDL_PENDING;
+      /* Return lock request to its initial state. */
+      l->type= MDL_SHARED;
+      l->prio= MDL_NORMAL_PRIO;
       context->locks.remove(l);
     }
   }
@@ -923,6 +951,9 @@ void mdl_release_exclusive_locks(MDL_CON
 
    @param context Context containing lock in question
    @param lock    Lock to be released
+
+   @note Resets lock request for lock released back to its initial state
+         (i.e.sets type and priority to MDL_SHARED and MDL_NORMAL_PRIO).
 */
 
 void mdl_release_lock(MDL_CONTEXT *context, MDL_EL *lr)
@@ -935,6 +966,10 @@ void mdl_release_lock(MDL_CONTEXT *conte
   lr->ctx= 0;
   lr->lock= 0;
 #endif
+  lr->state= MDL_PENDING;
+  /* Return lock request to its initial state. */
+  lr->type= MDL_SHARED;
+  lr->prio= MDL_NORMAL_PRIO;
   context->locks.remove(lr);
   pthread_cond_broadcast(&COND_mdl);
   pthread_mutex_unlock(&LOCK_mdl);
@@ -958,11 +993,12 @@ void mdl_downgrade_exclusive_locks(MDL_C
 
   pthread_mutex_lock(&LOCK_mdl);
   while ((l= it++))
-    if (l->type == EXCLUSIVE)
+    if (l->type == MDL_EXCLUSIVE)
     {
+      DBUG_ASSERT(l->state == MDL_ACQUIRED);
       lock= l->lock;
       lock->active_writers.remove(l);
-      l->type= SHARED;
+      l->type= MDL_SHARED;
       lock->active_readers.push_front(l);
     }
   pthread_cond_broadcast(&COND_mdl);
@@ -996,7 +1032,40 @@ bool mdl_is_exclusive_lock_owner(MDL_CON
 
   while ((l= it++) && (l->key_length != key_length || memcmp(l->key, key, key_length)))
     continue;
-  return (l && l->type == EXCLUSIVE);
+  return (l && l->type == MDL_EXCLUSIVE && l->state == MDL_ACQUIRED);
+}
+
+
+/**
+   Auxiliary function which allows to check if we some kind of lock on
+   the object.
+
+   @param context Current context
+   @param type    Id of object type
+   @param db      Name of the database
+   @param name    Name of the object
+
+   @return TRUE if current context contains satisfied lock for the object,
+           FALSE otherwise.
+*/
+
+bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db,
+                        const char *name)
+{
+  char key[MAX_DBKEY_LENGTH];
+  uint key_length;
+  MDL_EL *l;
+  I_P_List_iterator<MDL_EL, MDL_EL_context> it(context->locks);
+
+  int4store(key, type);
+  key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1;
+
+  while ((l= it++) && (l->key_length != key_length ||
+                       memcmp(l->key, key, key_length) ||
+                       l->state == MDL_PENDING))
+    continue;
+
+  return l;
 }
 
 
@@ -1053,8 +1122,7 @@ void mdl_set_cached_object(MDL_EL *l, vo
                        l->key + 4 + strlen(l->key + 4) + 1,
                        cached_object));
 
-  DBUG_ASSERT(l->type == SHARED || l->type == EXCLUSIVE ||
-              l->type == SHARED_PENDING_UPGRADE);
+  DBUG_ASSERT(l->state == MDL_ACQUIRED || l->state == MDL_PENDING_UPGRADE);
 
   /*
     TODO: This assumption works now since we do mdl_get_cached_object()
@@ -1082,7 +1150,6 @@ void mdl_set_cached_object(MDL_EL *l, vo
 
 void* mdl_get_cached_object(MDL_EL *l)
 {
-  DBUG_ASSERT(l->type == SHARED || l->type == EXCLUSIVE ||
-              l->type == SHARED_PENDING_UPGRADE);
+  DBUG_ASSERT(l->state == MDL_ACQUIRED || l->state == MDL_PENDING_UPGRADE);
   return l->lock->cached_object;
 }
diff -Nrup a/sql/meta_lock.h b/sql/meta_lock.h
--- a/sql/meta_lock.h	2008-03-25 14:08:22 +03:00
+++ b/sql/meta_lock.h	2008-03-28 20:46:58 +03:00
@@ -20,14 +20,24 @@ struct MDL_LOCK;
 struct MDL_CONTEXT;
 
 
-/*
-  Type of meta-data lock. The only types that concern users are SHARED
-  and EXCLUSIVE. Other types are just an implementation detail.
-*/
+/** Type of metadata lock request. */
+
+enum enum_mdl_type {MDL_SHARED=0, MDL_EXCLUSIVE};
+
 
-enum enum_mdl_type {SHARED_PENDING=0, SHARED, EXCLUSIVE_PENDING,
-                    SHARED_PENDING_UPGRADE, EXCLUSIVE};
+/** States which metadata lock request can have. */
 
+enum enum_mdl_state {MDL_PENDING=0, MDL_ACQUIRED, MDL_PENDING_UPGRADE};
+
+
+/**
+   Priority of metadata lock requests. High priority attribute is
+   applicable only to requests for shared locks and indicates that
+   such request should ignore pending requests for exclusive locks
+   and for upgrading of shared locks to exclusive.
+*/
+
+enum enum_mdl_prio {MDL_NORMAL_PRIO=0, MDL_HIGH_PRIO};
 
 
 /**
@@ -43,6 +53,8 @@ struct MDL_EL
   char        *key;
   uint        key_length;
   enum        enum_mdl_type type;
+  enum        enum_mdl_state state;
+  enum        enum_mdl_prio prio;
 
 private:
   /**
@@ -133,9 +145,30 @@ void mdl_init_lock(MDL_EL *mdl, char *ke
 MDL_EL *mdl_alloc_lock(int type, const char *db, const char *name,
                        MEM_ROOT *root);
 void mdl_alloc_locks(TABLE_LIST *table_list, MEM_ROOT *root);
-void mdl_add_lock(MDL_CONTEXT *context, MDL_EL *lock, bool exclusive);
+void mdl_add_lock(MDL_CONTEXT *context, MDL_EL *lock);
 void mdl_free_locks(MDL_CONTEXT *context);
 
+/**
+   Set type of lock request. Can be only applied to pending locks.
+*/
+
+inline void mdl_set_lock_type(MDL_EL *lock, enum_mdl_type lock_type)
+{
+  DBUG_ASSERT(lock->state == MDL_PENDING);
+  lock->type= lock_type;
+}
+
+/**
+   Set priority for lock request. High priority can be only set
+   for shared locks.
+*/
+
+inline void mdl_set_lock_priority(MDL_EL *lock, enum_mdl_prio prio)
+{
+  DBUG_ASSERT(lock->type == MDL_SHARED && lock->state == MDL_PENDING);
+  lock->prio= prio;
+}
+
 bool mdl_acquire_shared_lock(MDL_EL *lock);
 void mdl_acquire_exclusive_locks(MDL_CONTEXT *context);
 void mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type,
@@ -151,6 +184,9 @@ void mdl_downgrade_exclusive_locks(MDL_C
 
 bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, const char *db,
                                  const char *name);
+bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db,
+                       const char *name);
+
 bool mdl_has_pending_conflicting_lock(MDL_EL *l);
 
 inline bool mdl_has_non_freed_locks(MDL_CONTEXT *context)
@@ -160,7 +196,7 @@ inline bool mdl_has_non_freed_locks(MDL_
 
 
 /**
-   Get iterator for walkin through all lock requests in the context.
+   Get iterator for walking through all lock requests in the context.
 */
 
 inline I_P_List_iterator<MDL_EL, MDL_EL_context> mdl_get_locks(MDL_CONTEXT *ctx)
diff -Nrup a/sql/sql_base.cc b/sql/sql_base.cc
--- a/sql/sql_base.cc	2008-03-25 14:08:22 +03:00
+++ b/sql/sql_base.cc	2008-03-28 20:46:58 +03:00
@@ -343,6 +343,13 @@ TABLE_SHARE *get_table_share(THD *thd, T
 
   *error= 0;
 
+  /*
+    To be able perform any operation on table we should own
+    some kind of metadata lock on it.
+  */
+  DBUG_ASSERT(mdl_is_lock_owner(&thd->mdl_context, 0, table_list->db,
+                                table_list->table_name));
+
   /* Read table definition from cache */
   if ((share= (TABLE_SHARE*) hash_search(&table_def_cache,(uchar*) key,
                                          key_length)))
@@ -2515,8 +2522,11 @@ void table_share_release_hook(void *shar
                         put in the thread-open-list.
     flags               Bitmap of flags to modify how open works:
                           MYSQL_LOCK_IGNORE_FLUSH - Open table even if
-                          someone has done a flush or namelock on it.
+                          someone has done a flush or there is a pending
+                          exclusive metadata lock requests against it.
                           No version number checking is done.
+                          QQ: Do we need a separate flag for metadata
+                              lock part ?
                           MYSQL_OPEN_TEMPORARY_ONLY - Open only temporary
                           table not the base table or view.
 
@@ -2694,7 +2704,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *
       Is this table a view and not a base table?
       (it is work around to allow to open view with locked tables,
       real fix will be made after definition cache will be made)
+
+      Since opening of view which was not explicitly locked by LOCK
+      TABLES breaks metadata locking protocol (potentially can lead
+      to deadlocks) it should be disallowed.
     */
+    if (mdl_is_lock_owner(&thd->mdl_context, 0, table_list->db,
+                          table_list->table_name))
     {
       char path[FN_REFLEN];
       enum legacy_db_type not_used;
@@ -2741,15 +2757,18 @@ TABLE *open_table(THD *thd, TABLE_LIST *
   */
 
   mdl= table_list->mdl;
-  mdl_add_lock(&thd->mdl_context, mdl, table_list->open_table_type);
+  mdl_add_lock(&thd->mdl_context, mdl);
 
   if (table_list->open_table_type)
   {
+    mdl_set_lock_type(mdl, MDL_EXCLUSIVE);
     // This case can be significantly optimized
     mdl_acquire_exclusive_locks(&thd->mdl_context);
   }
   else
   {
+    mdl_set_lock_priority(mdl, (flags & MYSQL_LOCK_IGNORE_FLUSH) ?
+                               MDL_HIGH_PRIO : MDL_NORMAL_PRIO);
     if (mdl_acquire_shared_lock(mdl))
     {
       if (action)
@@ -4117,7 +4136,8 @@ static bool handle_failed_open_table_att
       mdl_free_locks(&thd->mdl_context);
       break;
     case OT_DISCOVER:
-      mdl_add_lock(&thd->mdl_context, table->mdl, TRUE);
+      mdl_set_lock_type(table->mdl, MDL_EXCLUSIVE);
+      mdl_add_lock(&thd->mdl_context, table->mdl);
       mdl_acquire_exclusive_locks(&thd->mdl_context);
 
       pthread_mutex_lock(&LOCK_open);
@@ -4130,7 +4150,8 @@ static bool handle_failed_open_table_att
       mdl_release_exclusive_locks(&thd->mdl_context);
       break;
     case OT_REPAIR:
-      mdl_add_lock(&thd->mdl_context, table->mdl, TRUE);
+      mdl_set_lock_type(table->mdl, MDL_EXCLUSIVE);
+      mdl_add_lock(&thd->mdl_context, table->mdl);
       mdl_acquire_exclusive_locks(&thd->mdl_context);
 
       pthread_mutex_lock(&LOCK_open);
@@ -4584,9 +4605,18 @@ int open_tables(THD *thd, TABLE_LIST **s
      */
     if (tables->derived)
     {
-      if (tables->view)
-        goto process_view_routines;
-      continue;
+      if (!tables->view)
+        continue;
+      /*
+        We restore view's name and database wiped out by derived tables
+        processing and fall back to standard open process in order to
+        obtain proper metadata locks and do other necessary steps like
+        stored routine processing.
+      */
+      tables->db= tables->view_db.str;
+      tables->db_length= tables->view_db.length;
+      tables->table_name= tables->view_name.str;
+      tables->table_name_length= tables->view_name.length;
     }
     /*
       If this TABLE_LIST object is a placeholder for an information_schema
diff -Nrup a/sql/sql_delete.cc b/sql/sql_delete.cc
--- a/sql/sql_delete.cc	2008-02-18 17:31:35 +03:00
+++ b/sql/sql_delete.cc	2008-03-28 20:46:58 +03:00
@@ -1022,7 +1022,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST
              without holding any kind of meta-data lock.
     */
     mdl= mdl_alloc_lock(0, table_list->db, table_list->table_name, thd->mem_root);
-    mdl_add_lock(&thd->mdl_context, mdl, TRUE);
+    mdl_set_lock_type(mdl, MDL_EXCLUSIVE);
+    mdl_add_lock(&thd->mdl_context, mdl);
     mdl_acquire_exclusive_locks(&thd->mdl_context);
     VOID(pthread_mutex_lock(&LOCK_open));
     expel_table_from_cache(0, table_list->db, table_list->table_name);
diff -Nrup a/sql/sql_show.cc b/sql/sql_show.cc
--- a/sql/sql_show.cc	2008-03-04 18:21:06 +03:00
+++ b/sql/sql_show.cc	2008-03-28 20:46:58 +03:00
@@ -3042,12 +3042,26 @@ static int fill_schema_table_from_frm(TH
   int error;
   char key[MAX_DBKEY_LENGTH];
   uint key_length;
+  MDL_EL mdl;
+  char mdlkey[MAX_DBKEY_LENGTH];
 
   bzero((char*) &table_list, sizeof(TABLE_LIST));
   bzero((char*) &tbl, sizeof(TABLE));
 
   table_list.table_name= table_name->str;
   table_list.db= db_name->str;
+  mdl_init_lock(&mdl, mdlkey, 0, db_name->str, table_name->str);
+  table_list.mdl= &mdl;
+  mdl_add_lock(&thd->mdl_context, &mdl);
+  mdl_set_lock_priority(&mdl, MDL_HIGH_PRIO);
+
+  /*
+    TODO: investigate if in this particular situation we can get by
+          simply obtaining internal lock of data-dictionary (ATM it
+          is LOCK_open) instead of obtaning full-blown metadata lock.
+  */
+  while (mdl_acquire_shared_lock(&mdl))
+    mdl_wait_for_locks(&thd->mdl_context);
 
   if (schema_table->i_s_requested_object & OPEN_TRIGGER_ONLY)
   {
@@ -3061,8 +3075,7 @@ static int fill_schema_table_from_frm(TH
       delete tbl.triggers;
     }
     free_root(&tbl.mem_root, MYF(0));
-    thd->clear_error();
-    return res;
+    goto err;
   }
 
   key_length= create_table_def_key(thd, key, &table_list, 0);
@@ -3072,7 +3085,7 @@ static int fill_schema_table_from_frm(TH
   if (!share)
   {
     res= 0;
-    goto err;
+    goto err_unlock;
   }
  
   if (share->is_view)
@@ -3081,7 +3094,7 @@ static int fill_schema_table_from_frm(TH
     {
       /* skip view processing */
       res= 0;
-      goto err1;
+      goto err_share;
     }
     else if (schema_table->i_s_requested_object & OPEN_VIEW_FULL)
     {
@@ -3090,7 +3103,7 @@ static int fill_schema_table_from_frm(TH
         open_normal_and_derived_tables()
       */
       res= 1;
-      goto err1;
+      goto err_share;
     }
   }
 
@@ -3102,11 +3115,11 @@ static int fill_schema_table_from_frm(TH
                      READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD |
                      OPEN_VIEW_NO_PARSE,
                      thd->open_options, &tbl, &table_list, thd->mem_root))
-      goto err1;
+      goto err_share;
     table_list.view= (st_lex*) share->is_view;
     res= schema_table->process_table(thd, &table_list, table,
                                      res, db_name, table_name);
-    goto err1;
+    goto err_share;
   }
 
   {
@@ -3117,11 +3130,14 @@ static int fill_schema_table_from_frm(TH
                                      res, db_name, table_name);
   }
 
-err1:
+err_share:
   release_table_share(share, RELEASE_NORMAL);
 
-err:
+err_unlock:
   pthread_mutex_unlock(&LOCK_open);
+
+err:
+  mdl_release_lock(&thd->mdl_context, &mdl);
   thd->clear_error();
   return res;
 }
diff -Nrup a/sql/sql_table.cc b/sql/sql_table.cc
--- a/sql/sql_table.cc	2008-03-06 21:40:08 +03:00
+++ b/sql/sql_table.cc	2008-03-28 20:46:58 +03:00
@@ -3627,7 +3627,8 @@ static bool lock_table_name_if_not_cache
 {
   if (!(*lock= mdl_alloc_lock(0, db, table_name, thd->mem_root)))
     return TRUE;
-  mdl_add_lock(&thd->mdl_context, *lock, TRUE);
+  mdl_set_lock_type(*lock, MDL_EXCLUSIVE);
+  mdl_add_lock(&thd->mdl_context, *lock);
   if (mdl_try_acquire_exclusive_lock(&thd->mdl_context, *lock))
   {
     *lock= 0;
@@ -3942,7 +3943,8 @@ static int prepare_for_repair(THD *thd, 
     */
     mdl= mdl_alloc_lock(0, table_list->db, table_list->table_name,
                         thd->mem_root);
-    mdl_add_lock(&thd->mdl_context, mdl, TRUE);
+    mdl_set_lock_type(mdl, MDL_EXCLUSIVE);
+    mdl_add_lock(&thd->mdl_context, mdl);
     mdl_acquire_exclusive_locks(&thd->mdl_context);
 
     pthread_mutex_lock(&LOCK_open);
Thread
bk commit into 6.0 tree (dlenev:1.2564) WL#3726dlenev28 Mar