diff --git a/mysql-test/r/query_cache.result b/mysql-test/r/query_cache.result index 1a728354c7b..f813d2cc7e0 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -1669,4 +1669,28 @@ SELECT 1 FROM t1 GROUP BY 1 1 DROP TABLE t1; +flush status; +set query_cache_type=DEMAND; +set global query_cache_size= 1024*1024*512; +drop table if exists t1; +create table t1 (a varchar(100)); +insert into t1 values ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'); +Activate debug hook and attempt to retrieve the statement from the cache. +set session debug='+d,wait_in_query_cache_insert'; +select SQL_CACHE * from t1;; +On a second connection; clear the query cache. +show status like 'Qcache_queries_in_cache'; +Variable_name Value +Qcache_queries_in_cache 1 +set global query_cache_size= 0;; +Signal the debug hook to release the lock. +select id from information_schema.processlist where state='wait_in_query_cache_insert' into @thread_id; +kill query @thread_id; +Show query cache status. +show status like 'Qcache_queries_in_cache'; +Variable_name Value +Qcache_queries_in_cache 1 +set global query_cache_size= 0; +use test; +drop table t1; End of 5.1 tests diff --git a/mysql-test/t/query_cache.test b/mysql-test/t/query_cache.test index 9e250372d51..14cd9385ca6 100644 --- a/mysql-test/t/query_cache.test +++ b/mysql-test/t/query_cache.test @@ -1316,6 +1316,47 @@ SELECT 1 FROM t1 GROUP BY (SELECT LAST_INSERT_ID() FROM t1 ORDER BY MIN(a) ASC LIMIT 1); DROP TABLE t1; +# +# Bug #30887 Server crashes on SET GLOBAL query_cache_size=0 +# +flush status; +set query_cache_type=DEMAND; +set global query_cache_size= 1024*1024*512; +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (a varchar(100)); +insert into t1 values ('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'); +connect (bug30887con1, localhost, root, ,test); +connect (bug30887con2, localhost, root, ,test); + +connection bug30887con1; +--echo Activate debug hook and attempt to retrieve the statement from the cache. +set session debug='+d,wait_in_query_cache_insert'; +--send select SQL_CACHE * from t1; + +connection default; +let $wait_condition= select count(*)= 1 from information_schema.processlist where state= 'wait_in_query_cache_insert'; +--source include/wait_condition.inc + +connection bug30887con2; +--echo On a second connection; clear the query cache. +show status like 'Qcache_queries_in_cache'; +--send set global query_cache_size= 0; + +connection default; +--echo Signal the debug hook to release the lock. +select id from information_schema.processlist where state='wait_in_query_cache_insert' into @thread_id; +kill query @thread_id; + +--echo Show query cache status. +show status like 'Qcache_queries_in_cache'; + +disconnect bug30887con1; +disconnect bug30887con2; +set global query_cache_size= 0; +use test; +drop table t1; --echo End of 5.1 tests diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index f1803a329c5..47301e1d205 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -371,6 +371,32 @@ TODO list: __LINE__,(ulong)(B)));B->query()->unlock_reading();} #define DUMP(C) DBUG_EXECUTE("qcache", {\ (C)->cache_dump(); (C)->queries_dump();(C)->tables_dump();}) + + +/** + Causes the thread to wait in a spin lock for a query kill signal. + This function is used by the test frame work to identify race conditions. + + The signal is caught and ignored and the thread is not killed. +*/ + +static void debug_wait_for_kill(const char *info) +{ + DBUG_ENTER("debug_wait_for_kill"); + const char *prev_info; + THD *thd; + thd= current_thd; + prev_info= thd->proc_info; + thd->proc_info= info; + sql_print_information(info); + while(!thd->killed) + my_sleep(1000); + thd->killed= THD::NOT_KILLED; + sql_print_information("Exit debug_wait_for_kill"); + thd->proc_info= prev_info; + DBUG_VOID_RETURN; +} + #else #define MUTEX_LOCK(M) pthread_mutex_lock(M) #define MUTEX_UNLOCK(M) pthread_mutex_unlock(M) @@ -647,13 +673,16 @@ void query_cache_insert(NET *net, const char *packet, ulong length) if (net->query_cache_query == 0) DBUG_VOID_RETURN; + DBUG_EXECUTE_IF("wait_in_query_cache_insert", + debug_wait_for_kill("wait_in_query_cache_insert"); ); + STRUCT_LOCK(&query_cache.structure_guard_mutex); bool interrupt; query_cache.wait_while_table_flush_is_in_progress(&interrupt); if (interrupt) { STRUCT_UNLOCK(&query_cache.structure_guard_mutex); - return; + DBUG_VOID_RETURN; } Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query; @@ -667,11 +696,11 @@ void query_cache_insert(NET *net, const char *packet, ulong length) DBUG_VOID_RETURN; } + BLOCK_LOCK_WR(query_block); Query_cache_query *header= query_block->query(); Query_cache_block *result= header->result(); DUMP(&query_cache); - BLOCK_LOCK_WR(query_block); DBUG_PRINT("qcache", ("insert packet %lu bytes long",length)); /* @@ -687,6 +716,7 @@ void query_cache_insert(NET *net, const char *packet, ulong length) DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block)); // The following call will remove the lock on query_block query_cache.free_query(query_block); + query_cache.refused++; // append_result_data no success => we need unlock STRUCT_UNLOCK(&query_cache.structure_guard_mutex); DBUG_VOID_RETURN; @@ -883,6 +913,31 @@ ulong Query_cache::resize(ulong query_cache_size_arg) m_cache_status= Query_cache::FLUSH_IN_PROGRESS; STRUCT_UNLOCK(&structure_guard_mutex); + /* + Wait for all readers and writers to exit. When the list of all queries + is iterated over with a block level lock, we are done. + */ + Query_cache_block *block= queries_blocks; + if (block) + { + do + { + BLOCK_LOCK_WR(block); + Query_cache_query *query= block->query(); + if (query && query->writer()) + { + /* + 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(0); + refused++; + } + BLOCK_UNLOCK_WR(block); + block= block->next; + } while (block != queries_blocks); + } free_cache(); query_cache_size= query_cache_size_arg;