From dd85b4b601c2e297237bfb4b476e222ff3dd0de5 Mon Sep 17 00:00:00 2001
From: unknown <mats@mysql.com>
Date: Thu, 22 Jun 2006 13:28:04 +0200
Subject: [PATCH] Patch to handle some bad situations resulting from the fix
 for BUG#19995.

sql/handler.cc:
  Generating table maps from all locks that can be available: THD::extra_lock,
  THD::lock, and THD::locked_tables.
sql/sql_class.h:
  Adding member Open_tables:state::extra_lock to hold the extra lock used by select_create.
  Removing select_insert::lock.
sql/sql_insert.cc:
  Adding member Open_tables:state::extra_lock to hold the extra lock used by select_create.
  Removing select_insert::lock.
---
 sql/handler.cc    | 54 ++++++++++++++++++++++++-----------------------
 sql/sql_class.h   | 13 ++++++++----
 sql/sql_insert.cc | 44 ++++++++------------------------------
 3 files changed, 46 insertions(+), 65 deletions(-)

diff --git a/sql/handler.cc b/sql/handler.cc
index 0895c6cf454..e4e7fd1e6d3 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -3263,37 +3263,39 @@ namespace
   int write_locked_table_maps(THD *thd)
   {
     DBUG_ENTER("write_locked_table_maps");
-    DBUG_PRINT("enter", ("thd=%p, thd->lock=%p, thd->locked_tables=%p",
-                         thd, thd->lock, thd->locked_tables));
+    DBUG_PRINT("enter", ("thd=%p, thd->lock=%p, thd->locked_tables=%p, thd->extra_lock",
+                         thd, thd->lock, thd->locked_tables, thd->extra_lock));
 
     if (thd->get_binlog_table_maps() == 0)
     {
-      /*
-        Exactly one table has to be locked, otherwise this code is not
-        guaranteed to work.
-      */
-      DBUG_ASSERT((thd->lock != NULL) + (thd->locked_tables != NULL) == 1);
-
-      MYSQL_LOCK *lock= thd->lock ? thd->lock : thd->locked_tables;
-      DBUG_ASSERT(lock->table_count > 0);
-      TABLE **const end_ptr= lock->table + lock->table_count;
-      for (TABLE **table_ptr= lock->table ; 
-           table_ptr != end_ptr ;
-           ++table_ptr)
+      MYSQL_LOCK *const locks[] = {
+        thd->extra_lock, thd->lock, thd->locked_tables
+      };
+      for (my_ptrdiff_t i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i )
       {
-        TABLE *const table= *table_ptr;
-        DBUG_PRINT("info", ("Checking table %s", table->s->table_name));
-        if (table->current_lock == F_WRLCK &&
-            check_table_binlog_row_based(thd, table))
+        MYSQL_LOCK const *const lock= locks[i];
+        if (lock == NULL)
+          continue;
+
+        TABLE **const end_ptr= lock->table + lock->table_count;
+        for (TABLE **table_ptr= lock->table ; 
+             table_ptr != end_ptr ;
+             ++table_ptr)
         {
-          int const has_trans= table->file->has_transactions();
-          int const error= thd->binlog_write_table_map(table, has_trans);
-          /*
-            If an error occurs, it is the responsibility of the caller to
-            roll back the transaction.
-          */
-          if (unlikely(error))
-            DBUG_RETURN(1);
+          TABLE *const table= *table_ptr;
+          DBUG_PRINT("info", ("Checking table %s", table->s->table_name));
+          if (table->current_lock == F_WRLCK &&
+              check_table_binlog_row_based(thd, table))
+          {
+            int const has_trans= table->file->has_transactions();
+            int const error= thd->binlog_write_table_map(table, has_trans);
+            /*
+              If an error occurs, it is the responsibility of the caller to
+              roll back the transaction.
+            */
+            if (unlikely(error))
+              DBUG_RETURN(1);
+          }
         }
       }
     }
diff --git a/sql/sql_class.h b/sql/sql_class.h
index b6283b6d174..fa4a29ff112 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -693,6 +693,14 @@ class Open_tables_state
      THD::prelocked_mode for more info.)
   */
   MYSQL_LOCK *locked_tables;
+
+  /*
+    CREATE-SELECT keeps an extra lock for the table being
+    created. This field is used to keep the extra lock available for
+    lower level routines, which would otherwise miss that lock.
+   */
+  MYSQL_LOCK *extra_lock;
+
   /*
     prelocked_mode_type enum and prelocked_mode member are used for
     indicating whenever "prelocked mode" is on, and what type of
@@ -745,7 +753,7 @@ class Open_tables_state
   void reset_open_tables_state()
   {
     open_tables= temporary_tables= handler_tables= derived_tables= 0;
-    lock= locked_tables= 0;
+    extra_lock= lock= locked_tables= 0;
     prelocked_mode= NON_PRELOCKED;
     state_flags= 0U;
   }
@@ -1591,9 +1599,6 @@ class select_insert :public select_result_interceptor {
   bool send_eof();
   /* not implemented: select_insert is never re-used in prepared statements */
   void cleanup();
-
-protected:
-  MYSQL_LOCK *lock;
 };
 
 
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 4c2c0099908..954b49f9efa 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -2188,7 +2188,6 @@ select_insert::select_insert(TABLE_LIST *table_list_par, TABLE *table_par,
                              bool ignore_check_option_errors)
   :table_list(table_list_par), table(table_par), fields(fields_par),
    last_insert_id(0),
-   lock(0),
    insert_into_view(table_list_par && table_list_par->view != 0)
 {
   bzero((char*) &info,sizeof(info));
@@ -2356,6 +2355,7 @@ bool select_insert::send_data(List<Item> &values)
 {
   DBUG_ENTER("select_insert::send_data");
   bool error=0;
+
   if (unit->offset_limit_cnt)
   {						// using limit offset,count
     unit->offset_limit_cnt--;
@@ -2377,34 +2377,8 @@ bool select_insert::send_data(List<Item> &values)
     }
   }
 
-  /*
-    The thd->lock lock contain the locks for the select part of the
-    statement and the 'lock' variable contain the write lock for the
-    currently locked table that is being created or inserted
-    into. However, the row-based replication will investigate the
-    thd->lock to decide what table maps are to be written, so this one
-    has to contain the tables locked for writing.  To be able to write
-    table map for the table being created, we temporarily set
-    THD::lock to select_insert::lock while writing the record to the
-    storage engine. We cannot set this elsewhere, since the execution
-    of a stored function inside the select expression might cause the
-    lock structures to be NULL.
-   */
-  
-  {
-    MYSQL_LOCK *saved_lock= NULL;
-    if (lock)
-    {
-      saved_lock= thd->lock;
-      thd->lock= lock;
-    }
-
-    error= write_record(thd, table, &info);
+  error= write_record(thd, table, &info);
     
-    if (lock)
-      thd->lock= saved_lock;
-  }
-
   if (!error)
   {
     if (table->triggers || info.handle_duplicates == DUP_UPDATE)
@@ -2776,8 +2750,8 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
 
   unit= u;
   if (!(table= create_table_from_items(thd, create_info, create_table,
-                                       extra_fields, keys, &values, &lock,
-                                       hook_ptr)))
+                                       extra_fields, keys, &values,
+                                       &thd->extra_lock, hook_ptr)))
     DBUG_RETURN(-1);				// abort() deletes table
 
   if (table->s->fields < values.elements)
@@ -2884,13 +2858,13 @@ bool select_create::send_eof()
   {
     table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
     VOID(pthread_mutex_lock(&LOCK_open));
-    mysql_unlock_tables(thd, lock);
+    mysql_unlock_tables(thd, thd->extra_lock);
     if (!table->s->tmp_table)
     {
       if (close_thread_table(thd, &table))
         VOID(pthread_cond_broadcast(&COND_refresh));
     }
-    lock=0;
+    thd->extra_lock=0;
     table=0;
     VOID(pthread_mutex_unlock(&LOCK_open));
   }
@@ -2900,10 +2874,10 @@ bool select_create::send_eof()
 void select_create::abort()
 {
   VOID(pthread_mutex_lock(&LOCK_open));
-  if (lock)
+  if (thd->extra_lock)
   {
-    mysql_unlock_tables(thd, lock);
-    lock=0;
+    mysql_unlock_tables(thd, thd->extra_lock);
+    thd->extra_lock=0;
   }
   if (table)
   {
-- 
2.30.9