List:Commits« Previous MessageNext Message »
From:Dmitry Lenev Date:May 27 2008 7:32pm
Subject:commit into mysql-6.0 branch (dlenev:2649) WL#3726
View as plain text  
#At file:///home/dlenev/src/bzr/mysql-6.0-3726-w/

 2649 Dmitry Lenev	2008-05-27
      WL#3726 "DDL locking for all metadata objects".
      
      After review fixes in progress.
      
      Changed mysql_lock_tables() to be no longer responsible for
      reopening table if waiting for the lock on it was aborted.
      This allows to get rid of several annoying functions.
modified:
  sql/ha_ndbcluster_binlog.cc
  sql/lock.cc
  sql/log_event_old.cc
  sql/mysql_priv.h
  sql/sql_base.cc
  sql/sql_handler.cc
  sql/sql_insert.cc
  sql/sql_update.cc

per-file comments:
  sql/ha_ndbcluster_binlog.cc
    lock_tables() now also accepts set of options to be passed
    to mysql_lock_tables().
  sql/lock.cc
    Changed mysql_lock_tables() always requests caller to reopen
    table instead doing this on its own when waiting for lock was
    aborted. This allows us to get rid of several functions which
    were used in rare cases and significantly complicated our life.
  sql/log_event_old.cc
    lock_tables() now also accepts set of options to be passed
    to mysql_lock_tables().
  sql/mysql_priv.h
    Now mysql_lock_tables() always requests caller to reopen
    table instead doing this on its own when waiting for lock was
    aborted. So we no longer need wait_for_tables() and
    table_is_used() functions and MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN
    flag.
    open_and_lock_table_derived() and lock_tables() now accept
    options to be passed to open_tables() and mysql_lock_tables()
    calls.
  sql/sql_base.cc
    Since now mysql_lock_tables() always requests caller to
    reopen table instead doing this on its own when waiting for
    lock was aborted we no longer need wait_for_tables(),
    table_is_used() and close_old_data_files() functions.
    open_and_lock_table_derived() and lock_tables() now accept
    options to be passed to open_tables() and mysql_lock_tables()
    calls. This was needed in order to get rid of redundant code
    in open_system_tables_for_read() function.
  sql/sql_handler.cc
    mysql_lock_tables() is now always requests reopen if waiting
    for lock is aborted. MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN flag
    was removed.
  sql/sql_insert.cc
    handle_delayed_insert():
      Since mysql_lock_tables() is no longer responsible for
      reopening tables when waiting for lock was aborted we have
      to handle such situation outside of this function. To simplify
      this extracted code opening table for delayed insert thread to
      separate method of Delayed_insert class.
  sql/sql_update.cc
    lock_tables() now also accepts set of options to be passed
    to mysql_lock_tables().

=== modified file 'sql/ha_ndbcluster_binlog.cc'
--- a/sql/ha_ndbcluster_binlog.cc	2008-05-23 13:54:03 +0000
+++ b/sql/ha_ndbcluster_binlog.cc	2008-05-27 17:31:53 +0000
@@ -2563,7 +2563,7 @@
       goto add_ndb_binlog_index_err;
     }
 
-    if (lock_tables(thd, &binlog_tables, 1, &need_reopen))
+    if (lock_tables(thd, &binlog_tables, 1, 0, &need_reopen))
     {
       if (need_reopen)
       {

=== modified file 'sql/lock.cc'
--- a/sql/lock.cc	2008-05-23 13:54:03 +0000
+++ b/sql/lock.cc	2008-05-27 17:31:53 +0000
@@ -94,31 +94,6 @@
 static int unlock_external(THD *thd, TABLE **table,uint count);
 static void print_lock_error(int error, const char *);
 
-/*
-  Lock tables.
-
-  SYNOPSIS
-    mysql_lock_tables()
-    thd                         The current thread.
-    tables                      An array of pointers to the tables to lock.
-    count                       The number of tables to lock.
-    flags                       Options:
-      MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK      Ignore a global read lock
-      MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY      Ignore SET GLOBAL READ_ONLY
-      MYSQL_LOCK_IGNORE_FLUSH                 Ignore a flush tables.
-      MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN        Instead of reopening altered
-                                              or dropped tables by itself,
-                                              mysql_lock_tables() should
-                                              notify upper level and rely
-                                              on caller doing this.
-    need_reopen                 Out parameter, TRUE if some tables were altered
-                                or deleted and should be reopened by caller.
-
-  RETURN
-    A lock structure pointer on success.
-    NULL on error or if some tables should be reopen.
-*/
-
 /* Map the return value of thr_lock to an error from errmsg.txt */
 static int thr_lock_errno_to_mysql[]=
 { 0, 1, ER_LOCK_WAIT_TIMEOUT, ER_LOCK_DEADLOCK };
@@ -232,6 +207,28 @@
 }
 
 
+/**
+   Lock tables.
+
+   @param thd          The current thread.
+   @param tables       An array of pointers to the tables to lock.
+   @param count        The number of tables to lock.
+   @param flags        Options:
+                 MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK Ignore a global read lock
+                 MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY Ignore SET GLOBAL READ_ONLY
+                 MYSQL_LOCK_IGNORE_FLUSH            Ignore a flush tables.
+   @param need_reopen  Out parameter, TRUE if some tables were altered
+                       or deleted and should be reopened by caller.
+
+   @note Caller of this function should always be ready to handle request to
+         reopen table unless there are external invariants which guarantee
+         that such thing won't be needed (for example we are obtaining lock
+         on table on which we already have exclusive metadata lock).
+
+   @retval  A lock structure pointer on success.
+   @retval  NULL on error or if some tables should be reopen.
+*/
+
 MYSQL_LOCK *mysql_lock_tables(THD *thd, TABLE **tables, uint count,
                               uint flags, bool *need_reopen)
 {
@@ -315,7 +312,7 @@
       my_error(rc, MYF(0));
       break;
     }
-    else if (rc == 1)                           /* aborted */
+    else if (rc == 1)                           /* aborted or killed */
     {
       thd->some_tables_deleted=1;		// Try again
       sql_lock->lock_count= 0;                  // Locks are already freed
@@ -324,8 +321,9 @@
     else if (!thd->some_tables_deleted || (flags & MYSQL_LOCK_IGNORE_FLUSH))
     {
       /*
-        Thread was killed or lock aborted. Let upper level close all
-        used tables and retry or give error.
+        Success and nobody set thd->some_tables_deleted to force reopen
+        or we were called with MYSQL_LOCK_IGNORE_FLUSH so such attempts
+        should be ignored.
       */
       break;
     }
@@ -351,13 +349,9 @@
     */
     reset_lock_data_and_free(&sql_lock);
 retry:
-    if (flags & MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN)
-    {
-      *need_reopen= TRUE;
-      break;
-    }
-    if (wait_for_tables(thd))
-      break;					// Couldn't open tables
+    /* Let upper level close all used tables and retry or give error. */
+    *need_reopen= TRUE;
+    break;
   }
   thd_proc_info(thd, 0);
   if (thd->killed)

=== modified file 'sql/log_event_old.cc'
--- a/sql/log_event_old.cc	2008-05-23 13:54:03 +0000
+++ b/sql/log_event_old.cc	2008-05-27 17:31:53 +0000
@@ -1469,7 +1469,8 @@
     lex_start(thd);
 
     while ((error= lock_tables(thd, rli->tables_to_lock,
-                               rli->tables_to_lock_count, &need_reopen)))
+                               rli->tables_to_lock_count, 0,
+                               &need_reopen)))
     {
       if (!need_reopen)
       {

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2008-05-23 13:54:03 +0000
+++ b/sql/mysql_priv.h	2008-05-27 17:31:53 +0000
@@ -1342,8 +1342,6 @@
                   uint db_stat, uint prgflag,
                   uint ha_open_flags, TABLE *outparam,
                   TABLE_LIST *table_desc, MEM_ROOT *mem_root);
-bool wait_for_tables(THD *thd);
-bool table_is_used(TABLE *table, bool wait_for_name_lock);
 void unlock_locked_tables(THD *thd);
 void execute_init_command(THD *thd, sys_var_str *init_command_var,
 			  rw_lock_t *var_mutex);
@@ -1593,22 +1591,24 @@
                         pthread_cond_t *cond);
 int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
 /* open_and_lock_tables with optional derived handling */
-int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived);
+int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
+                                 uint flags);
 /* simple open_and_lock_tables without derived handling */
 inline int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
 {
-  return open_and_lock_tables_derived(thd, tables, FALSE);
+  return open_and_lock_tables_derived(thd, tables, FALSE, 0);
 }
 /* open_and_lock_tables with derived handling */
 inline int open_and_lock_tables(THD *thd, TABLE_LIST *tables)
 {
-  return open_and_lock_tables_derived(thd, tables, TRUE);
+  return open_and_lock_tables_derived(thd, tables, TRUE, 0);
 }
 /* simple open_and_lock_tables without derived handling for single table */
 TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
                                 thr_lock_type lock_type);
 bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
-int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, bool *need_reopen);
+int lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags,
+                bool *need_reopen);
 int decide_logging_format(THD *thd, TABLE_LIST *tables);
 TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
                             const char *table_name, bool link_in_list,
@@ -2141,10 +2141,9 @@
 /* mysql_lock_tables() and open_table() flags bits */
 #define MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK      0x0001
 #define MYSQL_LOCK_IGNORE_FLUSH                 0x0002
-#define MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN        0x0004
-#define MYSQL_OPEN_TEMPORARY_ONLY               0x0008
-#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY      0x0010
-#define MYSQL_LOCK_PERF_SCHEMA                  0x0020
+#define MYSQL_OPEN_TEMPORARY_ONLY               0x0004
+#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY      0x0008
+#define MYSQL_LOCK_PERF_SCHEMA                  0x0010
 
 void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
 void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock);

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2008-05-24 10:03:45 +0000
+++ b/sql/sql_base.cc	2008-05-27 17:31:53 +0000
@@ -114,8 +114,6 @@
 static bool open_table_entry_fini(THD *thd, TABLE_SHARE *share, TABLE *entry);
 static bool auto_repair_table(THD *thd, TABLE_LIST *table_list);
 static void free_cache_entry(TABLE *entry);
-static void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
-                                 bool send_refresh);
 static bool tdc_wait_for_old_versions(THD *thd, MDL_CONTEXT *context);
 static bool
 has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables);
@@ -3390,9 +3388,6 @@
   TABLE *err_tables= NULL, *err_tab_tmp;
   bool error=0, not_used;
   bool merge_table_found= FALSE;
-  const uint flags= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN |
-                    MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK |
-                    MYSQL_LOCK_IGNORE_FLUSH;
 
   DBUG_ENTER("reopen_tables");
 
@@ -3485,10 +3480,14 @@
   if (tables != tables_ptr)			// Should we get back old locks
   {
     MYSQL_LOCK *lock;
+    const uint flags= MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK |
+                      MYSQL_LOCK_IGNORE_FLUSH;
     /*
-      We should always get these locks. Anyway, we must not go into
-      wait_for_tables() as it tries to acquire LOCK_open, which is
-      already locked.
+      Since we have exclusive metadata locks on tables which we
+      are reopening we should always get these locks (We won't
+      wait on table level locks so can't get aborted and we ignore
+      other threads that set THD::some_tables_deleted by using
+      MYSQL_LOCK_IGNORE_FLUSH flag).
     */
     thd->some_tables_deleted=0;
     if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables),
@@ -3517,165 +3516,6 @@
 
 
 /**
-    Close handlers for tables in list, but leave the TABLE structure
-    intact so that we can re-open these quickly.
-
-    @param thd           Thread context
-    @param table         Head of the list of TABLE objects
-    @param morph_locks   TRUE  - remove locks which we have on tables being closed
-                                 but ensure that no DML or DDL will sneak in before
-                                 we will re-open the table (i.e. temporarily morph
-                                 our table-level locks into name-locks).
-                         FALSE - otherwise
-    @param send_refresh  Should we awake waiters even if we didn't close any tables?
-*/
-
-static void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
-                                 bool send_refresh)
-{
-  bool found= send_refresh;
-  DBUG_ENTER("close_old_data_files");
-
-  for (; table ; table=table->next)
-  {
-    DBUG_PRINT("tcache", ("checking table: '%s'.'%s' %p",
-                          table->s->db.str, table->s->table_name.str,
-                          table));
-    DBUG_PRINT("tcache", ("needs refresh: %d  is open: %u",
-                          table->needs_reopen_or_name_lock(), table->db_stat));
-    /*
-      Reopen marked for flush.
-    */
-    if (table->needs_reopen_or_name_lock())
-    {
-      found=1;
-      if (table->db_stat)
-      {
-	if (morph_locks)
-	{
-          /*
-            Forward lock handling to MERGE parent. But unlock parent
-            once only.
-          */
-          TABLE *ulcktbl= table->parent ? table->parent : table;
-          if (ulcktbl->lock_count)
-          {
-            /*
-              Wake up threads waiting for table-level lock on this table
-              so they won't sneak in when we will temporarily remove our
-              lock on it. This will also give them a chance to close their
-              instances of this table.
-            */
-            mysql_lock_abort(thd, ulcktbl, TRUE);
-            mysql_lock_remove(thd, thd->locked_tables, ulcktbl, TRUE);
-            ulcktbl->lock_count= 0;
-          }
-          if ((ulcktbl != table) && ulcktbl->db_stat)
-          {
-            /*
-              Close the parent too. Note that parent can come later in
-              the list of tables. It will then be noticed as closed and
-              as a placeholder. When this happens, do not clear the
-              placeholder flag. See the branch below ("***").
-            */
-            ulcktbl->open_placeholder= 1;
-            close_handle_and_leave_table_as_lock(ulcktbl);
-          }
-          /*
-            We want to protect the table from concurrent DDL operations
-            (like RENAME TABLE) until we will re-open and re-lock it.
-          */
-	  table->open_placeholder= 1;
-	}
-        close_handle_and_leave_table_as_lock(table);
-      }
-      else if (table->open_placeholder && !morph_locks)
-      {
-        /*
-          We come here only in close-for-back-off scenario. So we have to
-          "close" create placeholder here to avoid deadlocks (for example,
-          in case of concurrent execution of CREATE TABLE t1 SELECT * FROM t2
-          and RENAME TABLE t2 TO t1). In close-for-re-open scenario we will
-          probably want to let it stay.
-
-          Note "***": We must not enter this branch if the placeholder
-          flag has been set because of a former close through a child.
-          See above the comment that refers to this note.
-        */
-        table->open_placeholder= 0;
-      }
-    }
-  }
-  if (found)
-    broadcast_refresh();
-  DBUG_VOID_RETURN;
-}
-
-
-/*
-  Wait until all threads has closed the tables in the list
-  We have also to wait if there is thread that has a lock on this table even
-  if the table is closed
-*/
-
-bool table_is_used(TABLE *table, bool wait_for_name_lock)
-{
-  DBUG_ENTER("table_is_used");
-  do
-  {
-    char *key= table->s->table_cache_key.str;
-    uint key_length= table->s->table_cache_key.length;
-    /* Note that 'table' can use artificial TABLE_SHARE object. */
-    TABLE_SHARE *share= (TABLE_SHARE*)hash_search(&table_def_cache,
-                                                  (uchar*) key, key_length);
-    if (share && !share->used_tables.is_empty() &&
-        share->version != refresh_version)
-      DBUG_RETURN(1);
-  } while ((table=table->next));
-  DBUG_RETURN(0);
-}
-
-
-/*
-  Wait until all used tables are refreshed.
-
-  FIXME We should remove this function since for several functions which
-        are invoked by it new scenarios of usage are introduced, while
-        this function implements optimization useful only in rare cases.
-*/
-
-bool wait_for_tables(THD *thd)
-{
-  bool result;
-  DBUG_ENTER("wait_for_tables");
-
-  thd_proc_info(thd, "Waiting for tables");
-  pthread_mutex_lock(&LOCK_open);
-  while (!thd->killed)
-  {
-    thd->some_tables_deleted=0;
-    close_old_data_files(thd,thd->open_tables,0,dropping_tables != 0);
-    mysql_ha_flush(thd);
-    if (!table_is_used(thd->open_tables,1))
-      break;
-    (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
-  }
-  if (thd->killed)
-    result= 1;					// aborted
-  else
-  {
-    /* Now we can open all tables without any interference */
-    thd_proc_info(thd, "Reopen tables");
-    thd->version= refresh_version;
-    result=reopen_tables(thd, 0);
-  }
-  pthread_mutex_unlock(&LOCK_open);
-  thd_proc_info(thd, 0);
-  DBUG_RETURN(result);
-}
-
-
-/**
    Unlock and close tables open and locked by LOCK TABLES statement.
 
    @param thd Current thread's context.
@@ -5080,14 +4920,8 @@
       DBUG_ASSERT(thd->lock == 0);	// You must lock everything at once
       if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK)
 	if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1,
-                                            (lock_flags |
-                                             MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN),
-                                            &refresh)))
+                                            lock_flags, &refresh)))
         {
-          /*
-            FIXME: Actually we should get rid of MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN option
-                   as all reopening should happen outside of mysql_lock_tables() code.
-          */
           if (refresh)
           {
             close_thread_tables(thd);
@@ -5112,6 +4946,8 @@
     open_and_lock_tables_derived()
     thd		- thread handler
     tables	- list of tables for open&locking
+    flags       - set of options to be used to open and lock tables (see
+                  open_tables() and mysql_lock_tables() for details).
     derived     - if to handle derived tables
 
   RETURN
@@ -5129,7 +4965,8 @@
     the third argument set appropriately.
 */
 
-int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived)
+int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
+                                 uint flags)
 {
   uint counter;
   bool need_reopen;
@@ -5138,7 +4975,7 @@
 
   for ( ; ; ) 
   {
-    if (open_tables(thd, &tables, &counter, 0))
+    if (open_tables(thd, &tables, &counter, flags))
       DBUG_RETURN(-1);
 
     DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", {
@@ -5147,7 +4984,8 @@
       my_sleep(6000000);
       thd->proc_info= old_proc_info;});
 
-    if (!lock_tables(thd, tables, counter, &need_reopen))
+    if (!lock_tables(thd, tables, counter, flags,
+                     &need_reopen))
       break;
     if (!need_reopen)
       DBUG_RETURN(-1);
@@ -5362,6 +5200,7 @@
     thd			Thread handler
     tables		Tables to lock
     count		Number of opened tables
+    flags               Options (see mysql_lock_tables() for details)
     need_reopen         Out parameter which if TRUE indicates that some
                         tables were dropped or altered during this call
                         and therefore invoker should reopen tables and
@@ -5382,7 +5221,8 @@
    -1	Error
 */
 
-int lock_tables(THD *thd, TABLE_LIST *tables, uint count, bool *need_reopen)
+int lock_tables(THD *thd, TABLE_LIST *tables, uint count,
+                uint flags, bool *need_reopen)
 {
   TABLE_LIST *table;
 
@@ -5410,7 +5250,6 @@
   {
     DBUG_ASSERT(thd->lock == 0);	// You must lock everything at once
     TABLE **start,**ptr;
-    uint lock_flag= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN;
 
     if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count)))
       DBUG_RETURN(-1);
@@ -5439,7 +5278,7 @@
     }
 
     if (! (thd->lock= mysql_lock_tables(thd, start, (uint) (ptr - start),
-                                        lock_flag, need_reopen)))
+                                        flags, need_reopen)))
     {
       if (thd->lex->requires_prelocking())
       {
@@ -8989,41 +8828,38 @@
 open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
                             Open_tables_state *backup)
 {
+  Query_tables_list query_tables_list_backup;
+  LEX *lex= thd->lex;
+
   DBUG_ENTER("open_system_tables_for_read");
 
   alloc_mdl_locks(table_list, thd->mem_root);
 
+  /*
+    Besides using new Open_tables_state for opening system tables,
+    we also have to backup and reset/and then restore part of LEX
+    which is accessed by open_tables() in order to determine if
+    prelocking is needed and what tables should be added for it.
+    close_system_tables() doesn't require such treatment.
+  */
+  lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
   thd->reset_n_backup_open_tables_state(backup);
 
-  uint count= 0;
-  enum_open_table_action not_used;
-  bool not_used_2;
+  if (open_and_lock_tables_derived(thd, table_list, FALSE,
+                                   MYSQL_LOCK_IGNORE_FLUSH))
+  {
+    lex->restore_backup_query_tables_list(&query_tables_list_backup);
+    goto error;
+  }
+
   for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global)
   {
-    TABLE *table= open_table(thd, tables, thd->mem_root, &not_used,
-                             MYSQL_LOCK_IGNORE_FLUSH);
-    if (!table)
-      goto error;
-
-    DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_SYSTEM);
-
-    table->use_all_columns();
-    table->reginfo.lock_type= tables->lock_type;
-    tables->table= table;
-    count++;
-  }
-
-  {
-    TABLE **list= (TABLE**) thd->alloc(sizeof(TABLE*) * count);
-    TABLE **ptr= list;
-    for (TABLE_LIST *tables= table_list; tables; tables= tables->next_global)
-      *(ptr++)= tables->table;
-
-    thd->lock= mysql_lock_tables(thd, list, count,
-                                 MYSQL_LOCK_IGNORE_FLUSH, &not_used_2);
-  }
-  if (thd->lock)
-    DBUG_RETURN(FALSE);
+    DBUG_ASSERT(tables->table->s->table_category == TABLE_CATEGORY_SYSTEM);
+    tables->table->use_all_columns();
+  }
+  lex->restore_backup_query_tables_list(&query_tables_list_backup);
+
+  DBUG_RETURN(FALSE);
 
 error:
   close_system_tables(thd, backup);

=== modified file 'sql/sql_handler.cc'
--- a/sql/sql_handler.cc	2008-05-23 13:54:03 +0000
+++ b/sql/sql_handler.cc	2008-05-27 17:31:53 +0000
@@ -491,8 +491,7 @@
   */
   thd->open_tables= thd->handler_tables;
 
-  lock= mysql_lock_tables(thd, &tables->table, 1,
-                          MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, &need_reopen);
+  lock= mysql_lock_tables(thd, &tables->table, 1, 0, &need_reopen);
 
   /* restore previous context */
   thd->open_tables= backup_open_tables;

=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc	2008-05-23 13:54:03 +0000
+++ b/sql/sql_insert.cc	2008-05-27 17:31:53 +0000
@@ -1777,6 +1777,7 @@
   inline uint lock_count() { return locks_in_memory; }
 
   TABLE* get_local_table(THD* client_thd);
+  bool open_and_lock_table();
   bool handle_inserts(void);
 };
 
@@ -2238,6 +2239,41 @@
 }
 
 
+/**
+   Open and lock table for use by delayed thread and check that
+   this table is suitable for delayed inserts.
+
+   @retval FALSE - Success.
+   @retval TRUE  - Failure.
+*/
+
+bool Delayed_insert::open_and_lock_table()
+{
+  if (!(table= open_n_lock_single_table(&thd, &table_list,
+                                        TL_WRITE_DELAYED)))
+  {
+    thd.fatal_error();				// Abort waiting inserts
+    return TRUE;
+  }
+  if (!(table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED))
+  {
+    my_error(ER_ILLEGAL_HA, MYF(ME_FATALERROR), table_list.table_name);
+    return TRUE;
+  }
+  if (table->triggers)
+  {
+    /*
+      Table has triggers. This is not an error, but we do
+      not support triggers with delayed insert. Terminate the delayed
+      thread without an error and thus request lock upgrade.
+    */
+    return TRUE;
+  }
+  table->copy_blobs= 1;
+  return FALSE;
+}
+
+
 /*
  * Create a new delayed insert thread
 */
@@ -2302,28 +2338,8 @@
 
   alloc_mdl_locks(&di->table_list, thd->mem_root);
 
-  /* Open table */
-  if (!(di->table= open_n_lock_single_table(thd, &di->table_list,
-                                            TL_WRITE_DELAYED)))
-  {
-    thd->fatal_error();				// Abort waiting inserts
-    goto err;
-  }
-  if (!(di->table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED))
-  {
-    my_error(ER_ILLEGAL_HA, MYF(ME_FATALERROR), di->table_list.table_name);
-    goto err;
-  }
-  if (di->table->triggers)
-  {
-    /*
-      Table has triggers. This is not an error, but we do
-      not support triggers with delayed insert. Terminate the delayed
-      thread without an error and thus request lock upgrade.
-    */
-    goto err;
-  }
-  di->table->copy_blobs=1;
+  if (di->open_and_lock_table())
+    goto err;
 
   /* Tell client that the thread is initialized */
   pthread_cond_signal(&di->cond_client);
@@ -2398,7 +2414,7 @@
 
     if (di->tables_in_use && ! thd->lock)
     {
-      bool not_used;
+      bool need_reopen;
       /*
         Request for new delayed insert.
         Lock the table, but avoid to be blocked by a global read lock.
@@ -2411,11 +2427,29 @@
       */
       if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1,
                                           MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK,
-                                          &not_used)))
+                                          &need_reopen)))
       {
-	/* Fatal error */
-	di->dead= 1;
-	thd->killed= THD::KILL_CONNECTION;
+        if (need_reopen)
+        {
+          /*
+            We were waiting to obtain TL_WRITE_DELAYED (probably due to
+            someone having or requesting TL_WRITE_ALLOW_READ) and got
+            aborted. Try to reopen table and if it fails die.
+          */
+          close_thread_tables(thd);
+          di->table= 0;
+          if (di->open_and_lock_table())
+          {
+            di->dead= 1;
+            thd->killed= THD::KILL_CONNECTION;
+          }
+        }
+        else
+        {
+          /* Fatal error */
+          di->dead= 1;
+          thd->killed= THD::KILL_CONNECTION;
+        }
       }
       pthread_cond_broadcast(&di->cond_client);
     }
@@ -3447,6 +3481,12 @@
 
   table->reginfo.lock_type=TL_WRITE;
   hooks->prelock(&table, 1);                    // Call prelock hooks
+  /*
+    mysql_lock_tables() below should never fail with request to reopen table
+    since it won't wait for the table lock (we have exclusive metadata lock on
+    the table) and thus can't get aborted and since it ignores other threads
+    setting THD::some_tables_deleted thanks to MYSQL_LOCK_IGNORE_FLUSH.
+  */
   if (! ((*lock)= mysql_lock_tables(thd, &table, 1,
                                     MYSQL_LOCK_IGNORE_FLUSH, &not_used)) ||
         hooks->postlock(&table, 1))

=== modified file 'sql/sql_update.cc'
--- a/sql/sql_update.cc	2008-05-23 13:54:03 +0000
+++ b/sql/sql_update.cc	2008-05-27 17:31:53 +0000
@@ -220,7 +220,7 @@
       /* convert to multiupdate */
       DBUG_RETURN(2);
     }
-    if (!lock_tables(thd, table_list, table_count, &need_reopen))
+    if (!lock_tables(thd, table_list, table_count, 0, &need_reopen))
       break;
     if (!need_reopen)
       DBUG_RETURN(1);
@@ -1083,7 +1083,7 @@
   }
 
   /* now lock and fill tables */
-  if (lock_tables(thd, table_list, table_count, &need_reopen))
+  if (lock_tables(thd, table_list, table_count, 0, &need_reopen))
   {
     if (!need_reopen)
       DBUG_RETURN(TRUE);

Thread
commit into mysql-6.0 branch (dlenev:2649) WL#3726Dmitry Lenev27 May