MySQL Lists are EOL. Please join:

List:Commits« Previous MessageNext Message »
From:Konstantin Osipov Date:December 1 2009 7:13pm
Subject:bzr commit into mysql-5.6-next-mr branch (kostja:2966) WL#3726
View as plain text  
#At file:///opt/local/work/next-4284/ based on revid:kostja@stripped

 2966 Konstantin Osipov	2009-12-01
      Backport of:
      ------------------------------------------------------------
      revno: 2630.9.3
      committer: Dmitry Lenev <dlenev@stripped>
      branch nick: mysql-6.0-3726-w3
      timestamp: Wed 2008-06-11 08:33:36 +0400
      message:
        WL#3726 "DDL locking for all metadata objects".
      
        After review fixes in progress.
      
        Changed close_cached_tables() not to flush all unused TABLE
        instances when flushing individual table.
        Renamed expel_table_from_cache() to tdc_remove_table() and
        added enum parameter to be able more explicitly specify type
        of removal, rewrote its code to be more efficient.
      
      ******
      Backport of:
      ------------------------------------------------------------
      revno: 2630.9.4
      committer: Dmitry Lenev <dlenev@stripped>
      branch nick: mysql-6.0-3726-w3
      timestamp: Wed 2008-06-11 15:53:53 +0400
      message:
        WL#3726 "DDL locking for all metadata objects".
      
        After-review fixes in progress.
      
        Minor changes in order to improve code readability
        and simplify debugging.
     @ mysql-test/r/ps_ddl.result
        Restore the original (correct) behaviour,
        now that the patch that fixes the regression
        introduced by the original patch for WL#3726 is fixed.
     @ mysql-test/t/ps_ddl.test
        Restore the original (correct) behaviour,
        now that the patch that fixes the regression
        introduced by the original patch for WL#3726 is fixed
     @ sql/mysql_priv.h
        Renamed expel_table_from_cache() to tdc_remove_table() 
        and added enum parameter to be able more explicitly specify 
        type of removal.
     @ sql/sql_base.cc
        Changed close_cached_tables() not to flush all unused TABLE
        instances when flushing individual table.
        Renamed expel_table_from_cache() to tdc_remove_table() and added
        enum parameter to be able more explicitly specify type of
        removal, rewrote its code to be more efficient. As result removed
        relink_unused() function which is no longer used.
        ******
        Improved code in close_cached_tables() according to reviewer's
        comments.
     @ sql/sql_delete.cc
        Renamed expel_table_from_cache() to tdc_remove_table() 
        and added enum parameter to be able more explicitly 
        specify type of removal.
     @ sql/sql_rename.cc
        Renamed expel_table_from_cache() to tdc_remove_table() 
        and added enum parameter to be able more explicitly 
        specify type of removal.
     @ sql/sql_show.cc
        Moved acquisition of high-prio shared metadata lock in which
        happens in fill_schema_table_from_frm() to separate function.
     @ sql/sql_table.cc
        Renamed expel_table_from_cache() to tdc_remove_table() 
        and added enum parameter to be able more explicitly 
        specify type of removal.
     @ sql/sql_trigger.cc
        Renamed expel_table_from_cache() to tdc_remove_table() 
        and added enum parameter to be able more explicitly 
        specify type of removal.

    modified:
      mysql-test/r/ps_ddl.result
      mysql-test/t/ps_ddl.test
      sql/mysql_priv.h
      sql/sql_base.cc
      sql/sql_delete.cc
      sql/sql_rename.cc
      sql/sql_show.cc
      sql/sql_table.cc
      sql/sql_trigger.cc
=== modified file 'mysql-test/r/ps_ddl.result'
--- a/mysql-test/r/ps_ddl.result	2009-11-30 15:55:03 +0000
+++ b/mysql-test/r/ps_ddl.result	2009-12-01 19:13:01 +0000
@@ -850,7 +850,7 @@ flush table t1;
 execute stmt;
 f1()
 6
-call p_verify_reprepare_count(1);
+call p_verify_reprepare_count(0);
 SUCCESS
 
 execute stmt;

=== modified file 'mysql-test/t/ps_ddl.test'
--- a/mysql-test/t/ps_ddl.test	2009-11-30 15:55:03 +0000
+++ b/mysql-test/t/ps_ddl.test	2009-12-01 19:13:01 +0000
@@ -745,7 +745,7 @@ execute stmt;
 call p_verify_reprepare_count(1);
 flush table t1;
 execute stmt;
-call p_verify_reprepare_count(1);
+call p_verify_reprepare_count(0);
 execute stmt;
 
 --echo # Test 18-c: dependent VIEW has changed

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2009-12-01 14:58:31 +0000
+++ b/sql/mysql_priv.h	2009-12-01 19:13:01 +0000
@@ -1543,8 +1543,11 @@ char *generate_partition_syntax(partitio
 #endif
 
 bool notify_thread_having_shared_lock(THD *thd, THD *in_use);
-void expel_table_from_cache(THD *leave_thd, const char *db,
-                            const char *table_name);
+
+enum enum_tdc_remove_table_type {TDC_RT_REMOVE_ALL, TDC_RT_REMOVE_NOT_OWN,
+                                 TDC_RT_REMOVE_UNUSED};
+void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
+                      const char *db, const char *table_name);
 
 #define NORMAL_PART_NAME 0
 #define TEMP_PART_NAME 1

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2009-12-01 15:20:43 +0000
+++ b/sql/sql_base.cc	2009-12-01 19:13:01 +0000
@@ -921,12 +921,17 @@ void free_io_cache(TABLE *table)
    particular table identified by its share.
 
    @param share Table share.
+
+   @pre Caller should have LOCK_open mutex acquired.
 */
 
 static void kill_delayed_threads_for_table(TABLE_SHARE *share)
 {
   I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables);
   TABLE *tab;
+
+  safe_mutex_assert_owner(&LOCK_open);
+
   while ((tab= it++))
   {
     THD *in_use= tab->in_use;
@@ -983,6 +988,15 @@ bool close_cached_tables(THD *thd, TABLE
     DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu",
                           refresh_version));
     kill_delayed_threads();
+    /*
+      Get rid of all unused TABLE and TABLE_SHARE instances. By doing
+      this we automatically close all tables which were marked as "old".
+    */
+    while (unused_tables)
+      free_cache_entry(unused_tables);
+    /* Free table shares which were not freed implicitly by loop above. */
+    while (oldest_unused_share->next)
+      (void) my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
   }
   else
   {
@@ -993,8 +1007,10 @@ bool close_cached_tables(THD *thd, TABLE
 
       if (share)
       {
-        share->version= 0;
         kill_delayed_threads_for_table(share);
+        /* tdc_remove_table() also sets TABLE_SHARE::version to 0. */
+        tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db,
+                         table->table_name);
 	found=1;
       }
     }
@@ -1002,28 +1018,14 @@ bool close_cached_tables(THD *thd, TABLE
       wait_for_refresh=0;			// Nothing to wait for
   }
 
-  /*
-    Get rid of all unused TABLE and TABLE_SHARE instances. By doing
-    this we automatically close all tables which were marked as "old".
-
-    FIXME: Do not close all unused TABLE instances when flushing
-           particular table.
-  */
-  while (unused_tables)
-    free_cache_entry(unused_tables);
-  /* Free table shares */
-  while (oldest_unused_share->next)
-    (void) my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
+  if (!have_lock)
+    pthread_mutex_unlock(&LOCK_open);
 
   if (!wait_for_refresh)
-  {
-    if (!have_lock)
-      pthread_mutex_unlock(&LOCK_open);
     DBUG_RETURN(result);
-  }
 
+  /* Code below assume that LOCK_open is released. */
   DBUG_ASSERT(!have_lock);
-  pthread_mutex_unlock(&LOCK_open);
 
   if (thd->locked_tables_mode)
   {
@@ -2109,27 +2111,6 @@ bool rename_temporary_table(THD* thd, TA
 }
 
 
-	/* move table first in unused links */
-
-static void relink_unused(TABLE *table)
-{
-  /* Assert that MERGE children are not attached in unused_tables. */
-  DBUG_ASSERT(!table->is_children_attached());
-
-  if (table != unused_tables)
-  {
-    table->prev->next=table->next;		/* Remove from unused list */
-    table->next->prev=table->prev;
-    table->next=unused_tables;			/* Link in unused tables */
-    table->prev=unused_tables->prev;
-    unused_tables->prev->next=table;
-    unused_tables->prev=table;
-    unused_tables=table;
-    check_unused();
-  }
-}
-
-
 /**
   Prepare an open merge table for close.
 
@@ -2241,7 +2222,8 @@ bool wait_while_table_is_used(THD *thd, 
   }
 
   pthread_mutex_lock(&LOCK_open);
-  expel_table_from_cache(thd, table->s->db.str, table->s->table_name.str);
+  tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN,
+                   table->s->db.str, table->s->table_name.str);
   pthread_mutex_unlock(&LOCK_open);
   DBUG_RETURN(FALSE);
 }
@@ -4075,7 +4057,7 @@ recover_from_failed_open_table_attempt(T
       if (mdl_acquire_exclusive_locks(&thd->mdl_context))
         return TRUE;
       pthread_mutex_lock(&LOCK_open);
-      expel_table_from_cache(0, table->db, table->table_name);
+      tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
       ha_create_table_from_engine(thd, table->db, table->table_name);
       pthread_mutex_unlock(&LOCK_open);
 
@@ -4089,7 +4071,7 @@ recover_from_failed_open_table_attempt(T
       if (mdl_acquire_exclusive_locks(&thd->mdl_context))
         return TRUE;
       pthread_mutex_lock(&LOCK_open);
-      expel_table_from_cache(0, table->db, table->table_name);
+      tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
       pthread_mutex_unlock(&LOCK_open);
 
       result= auto_repair_table(thd, table);
@@ -8549,50 +8531,82 @@ bool notify_thread_having_shared_lock(TH
 
 
 /**
-   Remove all instances of the table from cache assuming that current thread
-   has exclusive meta-data lock on it (optionally leave instances belonging
-   to the current thread in cache).
-
-   @param  leave_thd  0      If we should remove all instances
-                      non-0  Pointer to current thread context if we should
-                      leave instances belonging to this thread.
-   @param  db         Name of database
-   @param  table_name Name of table
+   Remove all or some (depending on parameter) instances of TABLE and
+   TABLE_SHARE from the table definition cache.
+
+   @param  thd          Thread context
+   @param  remove_type  Type of removal:
+                        TDC_RT_REMOVE_ALL     - remove all TABLE instances and
+                                                TABLE_SHARE instance. There
+                                                should be no used TABLE objects
+                                                and caller should have exclusive
+                                                metadata lock on the table.
+                        TDC_RT_REMOVE_NOT_OWN - remove all TABLE instances
+                                                except those that belong to
+                                                this thread. There should be
+                                                no TABLE objects used by other
+                                                threads and caller should have
+                                                exclusive metadata lock on the
+                                                table.
+                        TDC_RT_REMOVE_UNUSED  - remove all unused TABLE
+                                                instances (if there are no
+                                                used instances will also
+                                                remove TABLE_SHARE).
+   @param  db           Name of database
+   @param  table_name   Name of table
 
    @note Unlike remove_table_from_cache() it assumes that table instances
          are already not used by any (other) thread (this should be achieved
          by using meta-data locks).
 */
 
-void expel_table_from_cache(THD *leave_thd, const char *db, const char *table_name)
+void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
+                      const char *db, const char *table_name)
 {
   char key[MAX_DBKEY_LENGTH];
   uint key_length;
   TABLE *table;
   TABLE_SHARE *share;
 
-  key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
-
-  if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key,
-                                            key_length)))
-  {
-    I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables);
-    share->version= 0;
+  safe_mutex_assert_owner(&LOCK_open);
 
-    while ((table= it++))
-      relink_unused(table);
-  }
+  DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED ||
+              mdl_is_exclusive_lock_owner(&thd->mdl_context, 0,
+                                          db, table_name));
 
-  /* This may destroy share so we have to do new look-up later. */
-  while (unused_tables && !unused_tables->s->version)
-    free_cache_entry(unused_tables);
+  key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
 
   if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key,
                                             key_length)))
   {
-    DBUG_ASSERT(leave_thd || share->ref_count == 0);
-    if (share->ref_count == 0)
-      my_hash_delete(&table_def_cache, (uchar*) share);
+    if (share->ref_count)
+    {
+      I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables);
+#ifndef DBUG_OFF
+      if (remove_type == TDC_RT_REMOVE_ALL)
+      {
+        DBUG_ASSERT(share->used_tables.is_empty());
+      }
+      else if (remove_type == TDC_RT_REMOVE_NOT_OWN)
+      {
+        I_P_List_iterator<TABLE, TABLE_share> it2(share->used_tables);
+        while ((table= it2++))
+          if (table->in_use != thd)
+          {
+            DBUG_ASSERT(0);
+          }
+      }
+#endif
+      /*
+        Set share's version to zero in order to ensure that it gets
+        automatically deleted once it is no longer referenced.
+      */
+      share->version= 0;
+      while ((table= it++))
+        free_cache_entry(table);
+    }
+    else
+      (void) my_hash_delete(&table_def_cache, (uchar*) share);
   }
 }
 
@@ -8793,7 +8807,7 @@ int abort_and_upgrade_lock(ALTER_PARTITI
     DBUG_RETURN(1);
   }
   pthread_mutex_lock(&LOCK_open);
-  expel_table_from_cache(lpt->thd, lpt->db, lpt->table_name);
+  tdc_remove_table(lpt->thd, TDC_RT_REMOVE_NOT_OWN, lpt->db, lpt->table_name);
   pthread_mutex_unlock(&LOCK_open);
   DBUG_RETURN(0);
 }

=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc	2009-12-01 13:51:50 +0000
+++ b/sql/sql_delete.cc	2009-12-01 19:13:01 +0000
@@ -1172,7 +1172,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST
     if (mdl_acquire_exclusive_locks(&thd->mdl_context))
       DBUG_RETURN(TRUE);
     pthread_mutex_lock(&LOCK_open);
-    expel_table_from_cache(0, table_list->db, table_list->table_name);
+    tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db,
+                     table_list->table_name);
     pthread_mutex_unlock(&LOCK_open);
   }
 

=== modified file 'sql/sql_rename.cc'
--- a/sql/sql_rename.cc	2009-12-01 14:39:03 +0000
+++ b/sql/sql_rename.cc	2009-12-01 19:13:01 +0000
@@ -139,7 +139,8 @@ bool mysql_rename_tables(THD *thd, TABLE
   pthread_mutex_lock(&LOCK_open);
 
   for (ren_table= table_list; ren_table; ren_table= ren_table->next_local)
-    expel_table_from_cache(0, ren_table->db, ren_table->table_name);
+    tdc_remove_table(thd, TDC_RT_REMOVE_ALL, ren_table->db,
+                     ren_table->table_name);
 
   error=0;
   if ((ren_table=rename_tables(thd,table_list,0)))

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2009-12-01 15:20:43 +0000
+++ b/sql/sql_show.cc	2009-12-01 19:13:01 +0000
@@ -3076,6 +3076,56 @@ uint get_table_open_method(TABLE_LIST *t
 
 
 /**
+   Acquire high priority share metadata lock on a table.
+
+   @param thd            Thread context.
+   @param mdl_lock_data  Pointer to memory to be used for MDL_LOCK_DATA
+                         object for a lock request.
+   @param mdlkey         Pointer to the buffer for key for the lock request
+                         (should be at least strlen(db) + strlen(name) + 2
+                         bytes, or, if the lengths are not known,
+                         MAX_DBNAME_LENGTH)
+   @param table          Table list element for the table
+
+   @note This is an auxiliary function to be used in cases when we want to
+         access table's description by looking up info in TABLE_SHARE without
+         going through full-blown table open.
+   @note This function assumes that there are no other metadata lock requests
+         in the current metadata locking context.
+
+   @retval FALSE  Success
+   @retval TRUE   Some error occured (probably thread was killed).
+*/
+
+static bool
+acquire_high_prio_shared_mdl_lock(THD *thd, MDL_LOCK_DATA *mdl_lock_data,
+                                  char *mdlkey, TABLE_LIST *table)
+{
+  bool retry;
+
+  mdl_init_lock(mdl_lock_data, mdlkey, 0, table->db, table->table_name);
+  table->mdl_lock_data= mdl_lock_data;
+  mdl_add_lock(&thd->mdl_context, mdl_lock_data);
+  mdl_set_lock_type(mdl_lock_data, MDL_SHARED_HIGH_PRIO);
+
+  while (1)
+  {
+    if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_data, &retry))
+    {
+      if (!retry || mdl_wait_for_locks(&thd->mdl_context))
+      {
+        mdl_remove_all_locks(&thd->mdl_context);
+        return TRUE;
+      }
+      continue;
+    }
+    break;
+  }
+  return FALSE;
+}
+
+
+/**
   @brief          Fill I_S table with data from FRM file only
 
   @param[in]      thd                      thread handler
@@ -3108,7 +3158,6 @@ static int fill_schema_table_from_frm(TH
   char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1];
   MDL_LOCK_DATA mdl_lock_data;
   char mdlkey[MAX_DBKEY_LENGTH];
-  bool retry;
 
   bzero((char*) &table_list, sizeof(TABLE_LIST));
   bzero((char*) &tbl, sizeof(TABLE));
@@ -3133,32 +3182,20 @@ static int fill_schema_table_from_frm(TH
     table_list.db= db_name->str;
   }
 
-  mdl_init_lock(&mdl_lock_data, mdlkey, 0, db_name->str, table_name->str);
-  table_list.mdl_lock_data= &mdl_lock_data;
-  mdl_add_lock(&thd->mdl_context, &mdl_lock_data);
-  mdl_set_lock_type(&mdl_lock_data, MDL_SHARED_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 (1)
+  if (acquire_high_prio_shared_mdl_lock(thd, &mdl_lock_data, mdlkey,
+                                        &table_list))
   {
-    if (mdl_acquire_shared_lock(&thd->mdl_context, &mdl_lock_data, &retry))
-    {
-      if (!retry || mdl_wait_for_locks(&thd->mdl_context))
-      {
-        /*
-          Some error occured or we have been killed while waiting
-          for conflicting locks to go away, let the caller to handle
-          the situation.
-        */
-        return 1;
-      }
-      continue;
-    }
-    break;
+    /*
+      Some error occured (most probably we have been killed while
+      waiting for conflicting locks to go away), let the caller to
+      handle the situation.
+    */
+    return 1;
   }
 
   key_length= create_table_def_key(thd, key, &table_list, 0);

=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc	2009-12-01 14:58:31 +0000
+++ b/sql/sql_table.cc	2009-12-01 19:13:01 +0000
@@ -1897,7 +1897,7 @@ int mysql_rm_table_part2(THD *thd, TABLE
         DBUG_RETURN(1);
       pthread_mutex_lock(&LOCK_open);
       for (table= tables; table; table= table->next_local)
-        expel_table_from_cache(0, table->db, table->table_name);
+        tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
       pthread_mutex_unlock(&LOCK_open);
     }
     else
@@ -4345,7 +4345,7 @@ static int prepare_for_restore(THD* thd,
     if (mdl_acquire_exclusive_locks(&thd->mdl_context))
       DBUG_RETURN(TRUE);
     pthread_mutex_lock(&LOCK_open);
-    expel_table_from_cache(0, table->db, table->table_name);
+    tdc_remove_table(0, TDC_RT_REMOVE_UNUSED, table->db, table->table_name);
     pthread_mutex_unlock(&LOCK_open);
 
     if (my_copy(src_path, dst_path, MYF(MY_WME)))

=== modified file 'sql/sql_trigger.cc'
--- a/sql/sql_trigger.cc	2009-12-01 14:39:03 +0000
+++ b/sql/sql_trigger.cc	2009-12-01 19:13:01 +0000
@@ -469,7 +469,7 @@ bool mysql_create_or_drop_trigger(THD *t
       goto end;
 
     pthread_mutex_lock(&LOCK_open);
-    expel_table_from_cache(0, tables->db, tables->table_name);
+    tdc_remove_table(thd, TDC_RT_REMOVE_ALL, tables->db, tables->table_name);
 
     if (reopen_name_locked_table(thd, tables))
       goto end_unlock;


Attachment: [text/bzr-bundle] bzr/kostja@sun.com-20091201191301-h7rzq95t5ivshu83.bundle
Thread
bzr commit into mysql-5.6-next-mr branch (kostja:2966) WL#3726Konstantin Osipov1 Dec