From ea6a22bfa816ebc373316eee36c18952e812e7f6 Mon Sep 17 00:00:00 2001
From: Konstantin Osipov <kostja@sun.com>
Date: Tue, 13 Oct 2009 22:18:48 +0400
Subject: [PATCH] Introduce thd->query_cache_tls (thread local storage for
 query cache). We need more than one pointer in a thread to represent the
 query cache and net->query_cache_query can not be used any more (due to ABI
 compatibility issues and to different life time of NET and THD). This is a
 backport of the following patch from 6.0:
 ---------------------------------------------------------- revno: 2476.1157.2
 committer: kostja@bodhi.(none) timestamp: Sat 2007-06-16 13:29:24 +0400

---
 include/mysql.h.pp  |   2 +-
 include/mysql_com.h |   5 +-
 sql/mysql_priv.h    |   2 +
 sql/net_serv.cc     |  12 ++--
 sql/sql_cache.cc    | 162 +++++++++++++++++++++++++-------------------
 sql/sql_cache.h     |  27 ++++----
 sql/sql_class.cc    |   5 +-
 sql/sql_class.h     |  24 +++++++
 sql/sql_parse.cc    |   2 +-
 sql/sql_select.cc   |   2 +-
 10 files changed, 141 insertions(+), 102 deletions(-)

diff --git a/include/mysql.h.pp b/include/mysql.h.pp
index fcbcea61873..3d1304bf460 100644
--- a/include/mysql.h.pp
+++ b/include/mysql.h.pp
@@ -32,7 +32,7 @@ typedef struct st_net {
   my_bool unused2;
   my_bool compress;
   my_bool unused3;
-  unsigned char *query_cache_query;
+  unsigned char *unused;
   unsigned int last_errno;
   unsigned char error;
   my_bool unused4;
diff --git a/include/mysql_com.h b/include/mysql_com.h
index 672c359a6f7..5e1b6b8504a 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -264,10 +264,9 @@ typedef struct st_net {
   */
 #endif
   /*
-    'query_cache_query' should be accessed only via query cache
-    functions and methods to maintain proper locking.
+    Unused, please remove with the next incompatible ABI change.
   */
-  unsigned char *query_cache_query;
+  unsigned char *unused;
   unsigned int last_errno;
   unsigned char error; 
   my_bool unused4; /* Please remove with the next incompatible ABI change. */
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 8af0dc524b3..353f948b122 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -905,6 +905,8 @@ struct Query_cache_query_flags
 };
 #define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags)
 #include "sql_cache.h"
+#define query_cache_abort(A) query_cache.abort(A)
+#define query_cache_end_of_result(A) query_cache.end_of_result(A)
 #define query_cache_store_query(A, B) query_cache.store_query(A, B)
 #define query_cache_destroy() query_cache.destroy()
 #define query_cache_result_size_limit(A) query_cache.result_size_limit(A)
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
index 7a830e0961a..4945ee8d94f 100644
--- a/sql/net_serv.cc
+++ b/sql/net_serv.cc
@@ -95,8 +95,8 @@ extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received;
 #ifndef MYSQL_INSTANCE_MANAGER
 #ifdef HAVE_QUERY_CACHE
 #define USE_QUERY_CACHE
-extern void query_cache_init_query(NET *net);
-extern void query_cache_insert(NET *net, const char *packet, ulong length);
+extern void query_cache_insert(const char *packet, ulong length,
+                               unsigned pkt_nr);
 #endif // HAVE_QUERY_CACHE
 #define update_statistics(A) A
 #endif /* MYSQL_INSTANCE_MANGER */
@@ -132,11 +132,7 @@ my_bool my_net_init(NET *net, Vio* vio)
   net->compress=0; net->reading_or_writing=0;
   net->where_b = net->remain_in_buf=0;
   net->last_errno=0;
-#ifdef USE_QUERY_CACHE
-  query_cache_init_query(net);
-#else
-  net->query_cache_query= 0;
-#endif
+  net->unused= 0;
 
   if (vio != 0)					/* If real connection */
   {
@@ -586,7 +582,7 @@ net_real_write(NET *net,const uchar *packet, size_t len)
   DBUG_ENTER("net_real_write");
 
 #if defined(MYSQL_SERVER) && defined(USE_QUERY_CACHE)
-  query_cache_insert(net, (char*) packet, len);
+  query_cache_insert((char*) packet, len, net->pkt_nr);
 #endif
 
   if (net->error == 2)
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 9f427f39265..5c03e156c19 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -789,19 +789,20 @@ uchar *query_cache_query_get_key(const uchar *record, size_t *length,
   Note on double-check locking (DCL) usage.
 
   Below, in query_cache_insert(), query_cache_abort() and
-  query_cache_end_of_result() we use what is called double-check
-  locking (DCL) for NET::query_cache_query.  I.e. we test it first
-  without a lock, and, if positive, test again under the lock.
+  Query_cache::end_of_result() we use what is called double-check
+  locking (DCL) for Query_cache_tls::first_query_block.
+  I.e. we test it first without a lock, and, if positive, test again
+  under the lock.
 
-  This means that if we see 'NET::query_cache_query == 0' without a
+  This means that if we see 'first_query_block == 0' without a
   lock we will skip the operation.  But this is safe here: when we
   started to cache a query, we called Query_cache::store_query(), and
-  NET::query_cache_query was set to non-zero in this thread (and the
+  'first_query_block' was set to non-zero in this thread (and the
   thread always sees results of its memory operations, mutex or not).
-  If later we see 'NET::query_cache_query == 0' without locking a
+  If later we see 'first_query_block == 0' without locking a
   mutex, that may only mean that some other thread have reset it by
   invalidating the query.  Skipping the operation in this case is the
-  right thing to do, as NET::query_cache_query won't get non-zero for
+  right thing to do, as first_query_block won't get non-zero for
   this query again.
 
   See also comments in Query_cache::store_query() and
@@ -810,56 +811,71 @@ uchar *query_cache_query_get_key(const uchar *record, size_t *length,
   NOTE, however, that double-check locking is not applicable in
   'invalidate' functions, as we may erroneously skip invalidation,
   because the thread doing invalidation may never see non-zero
-  NET::query_cache_query.
+  'first_query_block'.
 */
 
 
-void query_cache_init_query(NET *net)
+/**
+  libmysql convenience wrapper to insert data into query cache.
+*/
+void query_cache_insert(const char *packet, ulong length,
+                        unsigned pkt_nr)
 {
+  THD *thd= current_thd;
+
   /*
-    It is safe to initialize 'NET::query_cache_query' without a lock
-    here, because before it will be accessed from different threads it
-    will be set in this thread under a lock, and access from the same
-    thread is always safe.
+    Current_thd can be NULL when a new connection is immediately ended
+    due to "Too many connections". thd->store_globals() has not been
+    called at this time and hence my_pthread_setspecific_ptr(THR_THD,
+    this) has not been called for this thread.
   */
-  net->query_cache_query= 0;
+
+  if (!thd)
+    return;
+
+  query_cache.insert(&thd->query_cache_tls,
+                     packet, length,
+                     pkt_nr);
 }
 
 
-/*
+/**
   Insert the packet into the query cache.
 */
 
-void query_cache_insert(NET *net, const char *packet, ulong length)
+void
+Query_cache::insert(Query_cache_tls *query_cache_tls,
+                    const char *packet, ulong length,
+                    unsigned pkt_nr)
 {
-  DBUG_ENTER("query_cache_insert");
+  DBUG_ENTER("Query_cache::insert");
 
   /* See the comment on double-check locking usage above. */
-  if (net->query_cache_query == 0)
+  if (query_cache_tls->first_query_block == NULL)
     DBUG_VOID_RETURN;
 
   DBUG_EXECUTE_IF("wait_in_query_cache_insert",
                   debug_wait_for_kill("wait_in_query_cache_insert"); );
 
-  if (query_cache.try_lock())
+
+  if (try_lock())
     DBUG_VOID_RETURN;
 
-  Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query;
-  if (!query_block)
+  Query_cache_block *query_block = query_cache_tls->first_query_block;
+  if (query_block == NULL)
   {
     /*
       We lost the writer and the currently processed query has been
       invalidated; there is nothing left to do.
     */
-    query_cache.unlock();
+    unlock();
     DBUG_VOID_RETURN;
   }
-
   BLOCK_LOCK_WR(query_block);
   Query_cache_query *header= query_block->query();
   Query_cache_block *result= header->result();
 
-  DUMP(&query_cache);
+  DUMP(this);
   DBUG_PRINT("qcache", ("insert packet %lu bytes long",length));
 
   /*
@@ -867,8 +883,8 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
     still need structure_guard_mutex to free the query, and therefore unlock
     it later in this function.
   */
-  if (!query_cache.append_result_data(&result, length, (uchar*) packet,
-                                      query_block))
+  if (!append_result_data(&result, length, (uchar*) packet,
+                          query_block))
   {
     DBUG_PRINT("warning", ("Can't append data"));
     header->result(result);
@@ -877,60 +893,63 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
     query_cache.free_query(query_block);
     query_cache.refused++;
     // append_result_data no success => we need unlock
-    query_cache.unlock();
+    unlock();
     DBUG_VOID_RETURN;
   }
 
   header->result(result);
-  header->last_pkt_nr= net->pkt_nr;
+  header->last_pkt_nr= pkt_nr;
   BLOCK_UNLOCK_WR(query_block);
-  DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0););
+  DBUG_EXECUTE("check_querycache",check_integrity(0););
 
   DBUG_VOID_RETURN;
 }
 
 
-void query_cache_abort(NET *net)
+void
+Query_cache::abort(Query_cache_tls *query_cache_tls)
 {
   DBUG_ENTER("query_cache_abort");
   THD *thd= current_thd;
 
   /* See the comment on double-check locking usage above. */
-  if (net->query_cache_query == 0)
+  if (query_cache_tls->first_query_block == NULL)
     DBUG_VOID_RETURN;
 
-  if (query_cache.try_lock())
+  if (try_lock())
     DBUG_VOID_RETURN;
 
   /*
     While we were waiting another thread might have changed the status
     of the writer. Make sure the writer still exists before continue.
   */
-  Query_cache_block *query_block= ((Query_cache_block*)
-                                   net->query_cache_query);
+  Query_cache_block *query_block= query_cache_tls->first_query_block;
   if (query_block)
   {
     thd_proc_info(thd, "storing result in query cache");
-    DUMP(&query_cache);
+    DUMP(this);
     BLOCK_LOCK_WR(query_block);
     // The following call will remove the lock on query_block
-    query_cache.free_query(query_block);
-    net->query_cache_query= 0;
-    DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
+    free_query(query_block);
+    query_cache_tls->first_query_block= NULL;
+    DBUG_EXECUTE("check_querycache", check_integrity(1););
   }
 
-  query_cache.unlock();
+  unlock();
+
   DBUG_VOID_RETURN;
 }
 
 
-void query_cache_end_of_result(THD *thd)
+void Query_cache::end_of_result(THD *thd)
 {
   Query_cache_block *query_block;
-  DBUG_ENTER("query_cache_end_of_result");
+  Query_cache_tls *query_cache_tls= &thd->query_cache_tls;
+  ulonglong limit_found_rows= thd->limit_found_rows;
+  DBUG_ENTER("Query_cache::end_of_result");
 
   /* See the comment on double-check locking usage above. */
-  if (thd->net.query_cache_query == 0)
+  if (query_cache_tls->first_query_block == NULL)
     DBUG_VOID_RETURN;
 
   /* Ensure that only complete results are cached. */
@@ -938,19 +957,19 @@ void query_cache_end_of_result(THD *thd)
 
   if (thd->killed)
   {
-    query_cache_abort(&thd->net);
+    query_cache_abort(&thd->query_cache_tls);
     DBUG_VOID_RETURN;
   }
 
 #ifdef EMBEDDED_LIBRARY
-  query_cache_insert(&thd->net, (char*)thd, 
-                     emb_count_querycache_size(thd));
+  insert(query_cache_tls, (char*)thd,
+                     emb_count_querycache_size(thd), 0);
 #endif
 
-  if (query_cache.try_lock())
+  if (try_lock())
     DBUG_VOID_RETURN;
 
-  query_block= ((Query_cache_block*) thd->net.query_cache_query);
+  query_block= query_cache_tls->first_query_block;
   if (query_block)
   {
     /*
@@ -959,7 +978,7 @@ void query_cache_end_of_result(THD *thd)
       block, the writer should be dropped.
     */
     thd_proc_info(thd, "storing result in query cache");
-    DUMP(&query_cache);
+    DUMP(this);
     BLOCK_LOCK_WR(query_block);
     Query_cache_query *header= query_block->query();
     Query_cache_block *last_result_block;
@@ -976,8 +995,8 @@ void query_cache_end_of_result(THD *thd)
         and removed from QC.
       */
       DBUG_ASSERT(0);
-      query_cache.free_query(query_block);
-      query_cache.unlock();
+      free_query(query_block);
+      unlock();
       DBUG_VOID_RETURN;
     }
     last_result_block= header->result()->prev;
@@ -986,17 +1005,17 @@ void query_cache_end_of_result(THD *thd)
     if (last_result_block->length >= query_cache.min_allocation_unit + len)
       query_cache.split_block(last_result_block,len);
 
-    header->found_rows(current_thd->limit_found_rows);
+    header->found_rows(limit_found_rows);
     header->result()->type= Query_cache_block::RESULT;
 
     /* Drop the writer. */
     header->writer(0);
-    thd->net.query_cache_query= 0;
+    query_cache_tls->first_query_block= NULL;
     BLOCK_UNLOCK_WR(query_block);
-    DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1););
-
+    DBUG_EXECUTE("check_querycache", check_integrity(1););
   }
-  query_cache.unlock();
+
+  unlock();
   DBUG_VOID_RETURN;
 }
 
@@ -1074,7 +1093,7 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
            Drop the writer; this will cancel any attempts to store 
            the processed statement associated with this writer.
          */
-        query->writer()->query_cache_query= 0;
+        query->writer()->first_query_block= NULL;
         query->writer(0);
         refused++;
       }
@@ -1268,8 +1287,8 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
 	double_linked_list_simple_include(query_block, &queries_blocks);
 	inserts++;
 	queries_in_cache++;
-	net->query_cache_query= (uchar*) query_block;
-	header->writer(net);
+	thd->query_cache_tls.first_query_block= query_block;
+	header->writer(&thd->query_cache_tls);
 	header->tables_type(tables_type);
 
         unlock();
@@ -1325,7 +1344,10 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
 {
   ulonglong engine_data;
   Query_cache_query *query;
-  Query_cache_block *first_result_block, *result_block;
+#ifndef EMBEDDED_LIBRARY
+  Query_cache_block *first_result_block;
+#endif
+  Query_cache_block *result_block;
   Query_cache_block_table *block_table, *block_table_end;
   ulong tot_length;
   Query_cache_query_flags flags;
@@ -1396,12 +1418,6 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
   if (query_cache_size == 0)
     goto err_unlock;
 
-  /*
-    Check that we haven't forgot to reset the query cache variables;
-    make sure there are no attached query cache writer to this thread.
-   */
-  DBUG_ASSERT(thd->net.query_cache_query == 0);
-
   Query_cache_block *query_block;
 
   tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
@@ -1483,7 +1499,10 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
   BLOCK_LOCK_RD(query_block);
 
   query = query_block->query();
-  result_block= first_result_block= query->result();
+  result_block= query->result();
+#ifndef EMBEDDED_LIBRARY
+  first_result_block= result_block;
+#endif
 
   if (result_block == 0 || result_block->type != Query_cache_block::RESULT)
   {
@@ -2261,7 +2280,7 @@ void Query_cache::free_query_internal(Query_cache_block *query_block)
   if (query->writer() != 0)
   {
     /* Tell MySQL that this query should not be cached anymore */
-    query->writer()->query_cache_query= 0;
+    query->writer()->first_query_block= NULL;
     query->writer(0);
   }
   double_linked_list_exclude(query_block, &queries_blocks);
@@ -3474,7 +3493,8 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
 */
 
 TABLE_COUNTER_TYPE
-Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex,
+Query_cache::is_cacheable(THD *thd, size_t query_len, const char *query,
+                          LEX *lex,
                           TABLE_LIST *tables_used, uint8 *tables_type)
 {
   TABLE_COUNTER_TYPE table_count;
@@ -3773,10 +3793,10 @@ my_bool Query_cache::move_by_type(uchar **border,
       If someone is writing to this block, inform the writer that the block
       has been moved.
     */
-    NET *net = new_block->query()->writer();
-    if (net != 0)
+    Query_cache_tls *query_cache_tls= new_block->query()->writer();
+    if (query_cache_tls != NULL)
     {
-      net->query_cache_query= (uchar*) new_block;
+      query_cache_tls->first_query_block= new_block;
     }
     /* Fix hash to point at moved block */
     hash_replace(&queries, &record_idx, (uchar*) new_block);
diff --git a/sql/sql_cache.h b/sql/sql_cache.h
index 777ddd39280..a1132adfa5a 100644
--- a/sql/sql_cache.h
+++ b/sql/sql_cache.h
@@ -64,6 +64,8 @@ struct Query_cache_table;
 struct Query_cache_query;
 struct Query_cache_result;
 class Query_cache;
+struct Query_cache_tls;
+struct st_lex;
 
 /**
   This class represents a node in the linked chain of queries
@@ -137,7 +139,7 @@ struct Query_cache_query
   ulonglong limit_found_rows;
   rw_lock_t lock;
   Query_cache_block *res;
-  NET *wri;
+  Query_cache_tls *wri;
   ulong len;
   uint8 tbls_type;
   unsigned int last_pkt_nr;
@@ -149,8 +151,8 @@ struct Query_cache_query
   inline void found_rows(ulonglong rows)   { limit_found_rows= rows; }
   inline Query_cache_block *result()	   { return res; }
   inline void result(Query_cache_block *p) { res= p; }
-  inline NET *writer()			   { return wri; }
-  inline void writer(NET *p)		   { wri= p; }
+  inline Query_cache_tls *writer()	   { return wri; }
+  inline void writer(Query_cache_tls *p)   { wri= p; }
   inline uint8 tables_type()               { return tbls_type; }
   inline void tables_type(uint8 type)      { tbls_type= type; }
   inline ulong length()			   { return len; }
@@ -407,7 +409,8 @@ class Query_cache
     If query is cacheable return number tables in query
     (query without tables not cached)
   */
-  TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query,
+  TABLE_COUNTER_TYPE is_cacheable(THD *thd, size_t query_len,
+                                  const char *query,
                                   LEX *lex, TABLE_LIST *tables_used,
                                   uint8 *tables_type);
   TABLE_COUNTER_TYPE process_and_count_tables(THD *thd,
@@ -462,10 +465,13 @@ class Query_cache
 
   void destroy();
 
-  friend void query_cache_init_query(NET *net);
-  friend void query_cache_insert(NET *net, const char *packet, ulong length);
-  friend void query_cache_end_of_result(THD *thd);
-  friend void query_cache_abort(NET *net);
+  void insert(Query_cache_tls *query_cache_tls,
+              const char *packet,
+              ulong length,
+              unsigned pkt_nr);
+
+  void end_of_result(THD *thd);
+  void abort(Query_cache_tls *query_cache_tls);
 
   /*
     The following functions are only used when debugging
@@ -493,9 +499,4 @@ class Query_cache
 
 extern Query_cache query_cache;
 extern TYPELIB query_cache_type_typelib;
-void query_cache_init_query(NET *net);
-void query_cache_insert(NET *net, const char *packet, ulong length);
-void query_cache_end_of_result(THD *thd);
-void query_cache_abort(NET *net);
-
 #endif
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 77f78d80c12..722cdd22794 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -501,9 +501,6 @@ THD::THD()
   net.vio=0;
 #endif
   client_capabilities= 0;                       // minimalistic client
-#ifdef HAVE_QUERY_CACHE
-  query_cache_init_query(&net);                 // If error on boot
-#endif
   ull=0;
   system_thread= NON_SYSTEM_THREAD;
   cleanup_done= abort_on_warning= no_warnings_for_error= 0;
@@ -796,7 +793,7 @@ THD::raise_condition_no_handler(uint sql_errno,
   MYSQL_ERROR *cond= NULL;
   DBUG_ENTER("THD::raise_condition_no_handler");
 
-  query_cache_abort(& net);
+  query_cache_abort(&query_cache_tls);
 
   /* FIXME: broken special case */
   if (no_warnings_for_error && (level == MYSQL_ERROR::WARN_LEVEL_ERROR))
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 4dea3acc5e7..76602e00074 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -315,6 +315,27 @@ typedef enum enum_diag_condition_item_name
 */
 extern const LEX_STRING Diag_condition_item_names[];
 
+/**
+  Query_cache_tls -- query cache thread local data.
+*/
+
+class Query_cache_block;
+
+struct Query_cache_tls
+{
+  /*
+    'first_query_block' should be accessed only via query cache
+    functions and methods to maintain proper locking.
+  */
+  Query_cache_block *first_query_block;
+  void set_first_query_block(Query_cache_block *first_query_block_arg)
+  {
+    first_query_block= first_query_block_arg;
+  }
+
+  Query_cache_tls() :first_query_block(NULL) {}
+};
+
 #include "sql_lex.h"				/* Must be here */
 
 class Delayed_insert;
@@ -1239,6 +1260,9 @@ class THD :public Statement,
     fields then.
   */
   struct st_mysql_stmt *current_stmt;
+#endif
+#ifdef HAVE_QUERY_CACHE
+  Query_cache_tls query_cache_tls;
 #endif
   NET	  net;				// client connection descriptor
   Protocol *protocol;			// Current protocol
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index ff91975f741..43660d525e3 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -6026,7 +6026,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length,
       DBUG_PRINT("info",("Command aborted. Fatal_error: %d",
 			 thd->is_fatal_error));
 
-      query_cache_abort(&thd->net);
+      query_cache_abort(&thd->query_cache_tls);
     }
     if (thd->lex->sphead)
     {
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 3e2d85e4951..67221b0aafb 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -9187,7 +9187,7 @@ remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value)
            thd->substitute_null_with_insert_id))
       {
 #ifdef HAVE_QUERY_CACHE
-	query_cache_abort(&thd->net);
+	query_cache_abort(&thd->query_cache_tls);
 #endif
 	COND *new_cond;
 	if ((new_cond= new Item_func_eq(args[0],
-- 
2.30.9