MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Jon Olav Hauglid Date:September 23 2009 6:32am
Subject:bzr commit into mysql-6.0-bugfixing branch (jon.hauglid:2834) Bug#47249
View as plain text  
#At file:///export/home/z/mysql-6.0-codebase-bugfixing-bug47249/ based on revid:guilhem@stripped

 2834 Jon Olav Hauglid	2009-09-23
      Bug #47249 assert in MDL_global_lock::is_lock_type_compatible
      
      This assert could be triggered if LOCK TABLES were used to lock
      both a table and a view that used the same table. The table would have
      to be first WRITE locked and then READ locked. So "LOCK TABLES v1
      WRITE, t1 READ" would eventually trigger the assert, "LOCK TABLES
      v1 READ, t1 WRITE" would not. The reason is that the ordering of locks
      in the interal representation made a difference when executing 
      FLUSH TABLE on the table.
      
      During FLUSH TABLE, a lock was upgraded to exclusive. If this lock
      was of type MDL_SHARED and not MDL_SHARED_UPGRADABLE, an internal
      counter in the MDL subsystem would get out of sync. This would happen
      if the *last* mention of the table in LOCK TABLES was a READ lock.
      
      The counter in question is the number exclusive locks (active or intention). 
      This is used to make sure a global metadata lock is only taken when the 
      counter is zero (= no conflicts). The counter is increased when a
      MDL_EXCLUSIVE or MDL_SHARED_UPGRADABLE lock is taken, but not when 
      upgrade_shared_lock_to_exclusive() is used to upgrade directly
      from MDL_SHARED to MDL_EXCLUSIVE. 
      
      This patch fixes the problem by searching for a TABLE instance locked
      with MDL_SHARED_UPGRADABLE or MDL_EXCLUSIVE before calling 
      upgrade_shared_lock_to_exclusive(). The patch also adds an assert checking
      that only MDL_SHARED_UPGRADABLE locks are upgraded to exclusive.
      
      Test case added to lock_multi.test.

    modified:
      mysql-test/r/lock_multi.result
      mysql-test/t/lock_multi.test
      sql/mdl.cc
      sql/mdl.h
      sql/sql_base.cc
=== modified file 'mysql-test/r/lock_multi.result'
--- a/mysql-test/r/lock_multi.result	2009-08-24 09:56:29 +0000
+++ b/mysql-test/r/lock_multi.result	2009-09-23 06:32:02 +0000
@@ -220,3 +220,34 @@ flush tables with read lock;;
 connection: default
 flush tables;
 drop table t1;
+#
+# Bug#47249 assert in MDL_global_lock::is_lock_type_compatible
+#
+DROP TABLE IF EXISTS t1;
+DROP VIEW  IF EXISTS v1;
+#
+# Test 1: LOCK TABLES v1 WRITE, t1 READ;
+#
+CREATE TABLE t1 ( f1 integer );
+CREATE VIEW v1 AS SELECT f1 FROM t1 ;
+# Connection 2
+LOCK TABLES v1 WRITE, t1 READ;
+FLUSH TABLE t1;
+# Connection 1
+LOCK TABLES t1 WRITE;
+FLUSH TABLE t1;
+DROP TABLE t1;
+DROP VIEW v1;
+#
+# Test 2: LOCK TABLES t1 WRITE, v1 READ;
+#
+CREATE TABLE t1 ( f1 integer );
+CREATE VIEW v1 AS SELECT f1 FROM t1 ;
+# Connection 2
+LOCK TABLES t1 WRITE, v1 READ;
+FLUSH TABLE t1;
+# Connection 1
+LOCK TABLES t1 WRITE;
+FLUSH TABLE t1;
+DROP TABLE t1;
+DROP VIEW v1;

=== modified file 'mysql-test/t/lock_multi.test'
--- a/mysql-test/t/lock_multi.test	2009-08-24 09:56:29 +0000
+++ b/mysql-test/t/lock_multi.test	2009-09-23 06:32:02 +0000
@@ -668,6 +668,63 @@ connection flush;
 connection default;
 disconnect flush;
 
+
+--echo #
+--echo # Bug#47249 assert in MDL_global_lock::is_lock_type_compatible
+--echo #
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+DROP VIEW  IF EXISTS v1;
+--enable_warnings
+
+--echo #
+--echo # Test 1: LOCK TABLES v1 WRITE, t1 READ;
+--echo #
+
+CREATE TABLE t1 ( f1 integer );
+CREATE VIEW v1 AS SELECT f1 FROM t1 ;
+
+--echo # Connection 2
+connect (con2,localhost,root);
+LOCK TABLES v1 WRITE, t1 READ;
+FLUSH TABLE t1;
+disconnect con2;
+--source include/wait_until_disconnected.inc
+
+--echo # Connection 1
+connection default;
+LOCK TABLES t1 WRITE;
+FLUSH TABLE t1;                                    # Assertion happened here
+
+# Cleanup
+DROP TABLE t1;
+DROP VIEW v1;
+
+--echo #
+--echo # Test 2: LOCK TABLES t1 WRITE, v1 READ;
+--echo #
+
+CREATE TABLE t1 ( f1 integer );
+CREATE VIEW v1 AS SELECT f1 FROM t1 ;
+
+--echo # Connection 2
+connect (con2,localhost,root);
+LOCK TABLES t1 WRITE, v1 READ;
+FLUSH TABLE t1;
+disconnect con2;
+--source include/wait_until_disconnected.inc
+
+--echo # Connection 1
+connection default;
+LOCK TABLES t1 WRITE;
+FLUSH TABLE t1;                                    # Assertion happened here
+
+# Cleanup
+DROP TABLE t1;
+DROP VIEW v1;
+
+
 # Wait till all disconnects are completed
 --source include/wait_until_count_sessions.inc
 

=== modified file 'sql/mdl.cc'
--- a/sql/mdl.cc	2009-09-16 14:26:50 +0000
+++ b/sql/mdl.cc	2009-09-23 06:32:02 +0000
@@ -1023,6 +1023,9 @@ MDL_ticket::upgrade_shared_lock_to_exclu
   if (m_type == MDL_EXCLUSIVE)
     DBUG_RETURN(FALSE);
 
+  /* Only allow upgrades from MDL_SHARED_UPGRADABLE */
+  DBUG_ASSERT(m_type == MDL_SHARED_UPGRADABLE);
+
   pthread_mutex_lock(&LOCK_mdl);
 
   old_msg= MDL_ENTER_COND(thd, mysys_var);

=== modified file 'sql/mdl.h'
--- a/sql/mdl.h	2009-09-16 14:26:50 +0000
+++ b/sql/mdl.h	2009-09-23 06:32:02 +0000
@@ -267,6 +267,10 @@ public:
                          mdl_cached_object_release_hook release_hook);
   const MDL_context *get_ctx() const { return m_ctx; }
   bool is_shared() const { return m_type < MDL_EXCLUSIVE; }
+  bool is_upgradable_or_exclusive() const
+  {
+    return m_type == MDL_SHARED_UPGRADABLE || m_type == MDL_EXCLUSIVE;
+  }
   bool upgrade_shared_lock_to_exclusive();
   void downgrade_exclusive_lock();
 private:

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2009-09-22 11:44:58 +0000
+++ b/sql/sql_base.cc	2009-09-23 06:32:02 +0000
@@ -125,6 +125,8 @@ static bool tdc_wait_for_old_versions(TH
 static bool
 has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables);
 
+TABLE *find_table_for_mdl_upgrade(TABLE *list, const char *db,
+                                  const char *table_name);
 
 uint cached_open_tables(void)
 {
@@ -993,8 +995,8 @@ bool close_cached_tables(THD *thd, TABLE
          table_list= table_list->next_global)
     {
       /* A check that the table was locked for write is done by the caller. */
-      TABLE *table= find_locked_table(thd->open_tables, table_list->db,
-                                      table_list->table_name);
+      TABLE *table= find_table_for_mdl_upgrade(thd->open_tables, table_list->db,
+                                               table_list->table_name);
 
       /* May return NULL if this table has already been closed via an alias. */
       if (! table)
@@ -2971,6 +2973,34 @@ TABLE *find_write_locked_table(TABLE *li
 }
 
 
+/**
+   Find instance of TABLE with MDL_SHARED_UPGRADABLE or
+   MDL_EXCLUSIVE lock from the list of open tables.
+
+   @param list       List of TABLE objects to be searched
+   @param db         Database name.
+   @param table_name Name of table.
+
+   @return Pointer to MDL_SHARED_UPGRADABLE or MDL_EXCLUSIVE
+           TABLE instance, NULL otherwise.
+*/
+
+TABLE *find_table_for_mdl_upgrade(TABLE *list, const char *db,
+                                  const char *table_name)
+{
+  TABLE *tab= find_locked_table(list, db, table_name);
+
+  while (tab != NULL)
+  {
+    if (tab->mdl_ticket != NULL &&
+        tab->mdl_ticket->is_upgradable_or_exclusive())
+      return tab;
+    tab= find_locked_table(tab->next, db, table_name);
+  }
+  return NULL;
+}
+
+
 /***********************************************************************
   class Locked_tables_list implementation. Declared in sql_class.h
 ************************************************************************/


Attachment: [text/bzr-bundle] bzr/jon.hauglid@sun.com-20090923063202-84e7ezkkc7yag9mw.bundle
Thread
bzr commit into mysql-6.0-bugfixing branch (jon.hauglid:2834) Bug#47249Jon Olav Hauglid23 Sep
  • Re: bzr commit into mysql-6.0-bugfixing branch (jon.hauglid:2834)Bug#47249Konstantin Osipov23 Sep