← Back to team overview

maria-developers team mailing list archive

Re: The query cache patch fixed by me and then Monty (most changes by him)

 

Hi!

On 29.12.2010 23:29, Oleksandr Byelkin wrote:
On 29.12.2010 13:00, Oleksandr Byelkin wrote:
Hi!

Here is changes which was done:

I found a bug here, new version will be tomorrow...

Here is new patch and relative patch against sources with previous patch.

New patch fixes switching ON query cache amd also remove some small bugs about compilation without query cache.
=== modified file 'client/mysqltest.cc'
--- client/mysqltest.cc	2010-11-30 21:11:03 +0000
+++ client/mysqltest.cc	2010-12-28 22:19:49 +0000
@@ -111,7 +111,7 @@
   display_metadata= FALSE, display_result_sorted= FALSE;
 static my_bool disable_query_log= 0, disable_result_log= 0;
 static my_bool disable_connect_log= 1;
-static my_bool disable_warnings= 0;
+static my_bool disable_warnings= 0, disable_column_names= 0;
 static my_bool prepare_warnings_enabled= 0;
 static my_bool disable_info= 1;
 static my_bool abort_on_error= 1;
@@ -295,6 +295,7 @@
   Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
   Q_ENABLE_INFO, Q_DISABLE_INFO,
   Q_ENABLE_METADATA, Q_DISABLE_METADATA,
+  Q_ENABLE_COLUMN_NAMES, Q_DISABLE_COLUMN_NAMES,
   Q_EXEC, Q_DELIMITER,
   Q_DISABLE_ABORT_ON_ERROR, Q_ENABLE_ABORT_ON_ERROR,
   Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
@@ -368,6 +369,8 @@
   "disable_info",
   "enable_metadata",
   "disable_metadata",
+  "enable_column_names",
+  "disable_column_names",
   "exec",
   "delimiter",
   "disable_abort_on_error",
@@ -6786,6 +6789,8 @@
                            uint num_fields)
 {
   uint col_idx;
+  if (disable_column_names)
+    return;
   for (col_idx= 0; col_idx < num_fields; col_idx++)
   {
     if (col_idx)
@@ -8212,6 +8217,8 @@
       case Q_DISABLE_INFO:       disable_info=1; break;
       case Q_ENABLE_METADATA:    display_metadata=1; break;
       case Q_DISABLE_METADATA:   display_metadata=0; break;
+      case Q_ENABLE_COLUMN_NAMES:  disable_column_names=0; break;
+      case Q_DISABLE_COLUMN_NAMES: disable_column_names=1; break;
       case Q_SOURCE: do_source(command); break;
       case Q_SLEEP: do_sleep(command, 0); break;
       case Q_REAL_SLEEP: do_sleep(command, 1); break;

=== modified file 'include/m_ctype.h'
--- include/m_ctype.h	2010-04-02 09:20:09 +0000
+++ include/m_ctype.h	2010-12-28 22:19:49 +0000
@@ -148,7 +148,8 @@
   MY_LEX_USER_VARIABLE_DELIMITER, MY_LEX_SYSTEM_VAR,
   MY_LEX_IDENT_OR_KEYWORD,
   MY_LEX_IDENT_OR_HEX, MY_LEX_IDENT_OR_BIN, MY_LEX_IDENT_OR_NCHAR,
-  MY_LEX_STRING_OR_DELIMITER
+  MY_LEX_STRING_OR_DELIMITER, MY_LEX_MINUS_OR_COMMENT, MY_LEX_PLACEHOLDER,
+  MY_LEX_COMMA
 };
 
 struct charset_info_st;

=== added file 'mysql-test/include/query_cache_with_comments.inc'
--- mysql-test/include/query_cache_with_comments.inc	1970-01-01 00:00:00 +0000
+++ mysql-test/include/query_cache_with_comments.inc	2010-12-28 22:19:49 +0000
@@ -0,0 +1,129 @@
+#
+# Main test of query cache with comments
+# This is run with differnt settings of query_cache_strip_comments
+#
+
+let $query=/* with comment first */select * from t1;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=# with comment first
+select * from t1;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=-- with comment first
+select * from t1;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=/* with comment first and "quote" */select * from t1;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=# with comment first and "quote"
+select * from t1;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=-- with comment first and "quote" 
+select * from t1;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=
+    /* with comment and whitespaces first */select * from t1;
+--source include/query_cache_with_comments_eval.inc
+
+let $query= 
+    # with comment and whitespaces first
+select * from t1;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=
+    -- with comment and whitespaces first
+select * from t1;
+--source include/query_cache_with_comments_eval.inc
+
+let $internal=* internal comment *;
+
+let $query=select * /$internal/ from t1;
+--source include/query_cache_with_comments_eval.inc
+let $query=select */$internal/ from t1;
+--source include/query_cache_with_comments_eval.inc
+let $query=select */$internal/from t1;
+--source include/query_cache_with_comments_eval.inc
+
+let $internal=* internal comment with "quote" *;
+
+let $query=select * /$internal/ from t1;
+--source include/query_cache_with_comments_eval.inc
+let $query=select */$internal/ from t1;
+--source include/query_cache_with_comments_eval.inc
+let $query=select */$internal/from t1;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=select * from t1
+;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=select * from t1 ;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=select * from t1	;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=select * from t1
+/* comment in the end */;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=select * from t1
+/* *\/ */;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=select * from t1
+/* comment in the end */
+;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=select * from t1 #comment in the end;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=select * from t1 #comment in the end
+;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=select * from t1 -- comment in the end;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=select * from t1 -- comment in the end
+;
+--source include/query_cache_with_comments_eval.inc
+
+let $query=select ' \'  ' from t1;
+--source include/query_cache_with_comments_eval.inc
+
+# Following should not be cached
+--source include/query_cache_with_comments_clear.inc
+
+let $query=select a from t1/*alias*/table1 /*!50002 ORDER BY 1*/;
+--source include/query_cache_with_comments_eval.inc
+let $query=select a from t1/*alias*/table1 /*!50003 ORDER BY 1*/;
+--source include/query_cache_with_comments_eval.inc
+
+# Some strange cases
+--source include/query_cache_with_comments_clear.inc
+
+let query= select '/* hello */' from t1;
+--source include/query_cache_with_comments_eval.inc
+let query= select "/* hello */" from t1;
+--source include/query_cache_with_comments_eval.inc
+create table t2 (`/*a*/` int, `/*b*/` int);
+select `/*b*/` from t2;
+let query= select `/*a*/` from t2;
+--source include/query_cache_with_comments_eval.inc
+drop table t2;
+
+let query= select "hello""there""" from t1 limit 1;
+--source include/query_cache_with_comments_eval.inc
+
+# Testing of -- as sign
+--source include/query_cache_with_comments_clear.inc
+let query= select 1--1 from t1;
+--source include/query_cache_with_comments_eval.inc
+let query= select 1--2 from t1;
+--source include/query_cache_with_comments_eval.inc

=== added file 'mysql-test/include/query_cache_with_comments_begin.inc'
--- mysql-test/include/query_cache_with_comments_begin.inc	1970-01-01 00:00:00 +0000
+++ mysql-test/include/query_cache_with_comments_begin.inc	2010-12-28 22:19:49 +0000
@@ -0,0 +1,12 @@
+-- source include/have_query_cache.inc
+
+set GLOBAL query_cache_size=1355776;
+
+--disable_warnings
+drop table if exists t1,t2;
+--enable_warnings
+
+create table t1 (a int not null);
+insert into t1 values (1),(2),(3);
+
+--source include/query_cache_with_comments_clear.inc

=== added file 'mysql-test/include/query_cache_with_comments_clear.inc'
--- mysql-test/include/query_cache_with_comments_clear.inc	1970-01-01 00:00:00 +0000
+++ mysql-test/include/query_cache_with_comments_clear.inc	2010-12-28 22:19:49 +0000
@@ -0,0 +1,7 @@
+# Reset query cache variables.
+--echo "Flushing query cache"
+--disable_query_log
+flush query cache;
+reset query cache;
+flush status;
+--enable_query_log

=== added file 'mysql-test/include/query_cache_with_comments_end.inc'
--- mysql-test/include/query_cache_with_comments_end.inc	1970-01-01 00:00:00 +0000
+++ mysql-test/include/query_cache_with_comments_end.inc	2010-12-28 22:19:49 +0000
@@ -0,0 +1,3 @@
+DROP TABLE t1;
+SET GLOBAL query_cache_size=default;
+set global query_cache_strip_comments=OFF;

=== added file 'mysql-test/include/query_cache_with_comments_eval.inc'
--- mysql-test/include/query_cache_with_comments_eval.inc	1970-01-01 00:00:00 +0000
+++ mysql-test/include/query_cache_with_comments_eval.inc	2010-12-28 22:19:49 +0000
@@ -0,0 +1,6 @@
+echo -----------------------------------------------------;
+echo $query;
+echo -----------------------------------------------------;
+eval $query;
+eval $query;
+--source include/query_cache_with_comments_show.inc

=== added file 'mysql-test/include/query_cache_with_comments_show.inc'
--- mysql-test/include/query_cache_with_comments_show.inc	1970-01-01 00:00:00 +0000
+++ mysql-test/include/query_cache_with_comments_show.inc	2010-12-28 22:19:49 +0000
@@ -0,0 +1,7 @@
+--disable_query_log
+--disable_column_names
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_inserts";
+show status like "Qcache_hits";
+--enable_column_names
+--enable_query_log

=== modified file 'mysql-test/r/grant_cache_no_prot.result'
--- mysql-test/r/grant_cache_no_prot.result	2009-05-20 13:27:44 +0000
+++ mysql-test/r/grant_cache_no_prot.result	2010-12-28 22:19:49 +0000
@@ -174,7 +174,7 @@
 Qcache_hits	7
 show status like "Qcache_not_cached";
 Variable_name	Value
-Qcache_not_cached	7
+Qcache_not_cached	4
 ----- establish connection user4 (user=mysqltest_1) -----
 select "user4";
 user4
@@ -205,7 +205,7 @@
 Qcache_hits	8
 show status like "Qcache_not_cached";
 Variable_name	Value
-Qcache_not_cached	8
+Qcache_not_cached	5
 ----- close connections -----
 ----- switch to connection default -----
 set names binary;

=== modified file 'mysql-test/r/grant_cache_ps_prot.result'
--- mysql-test/r/grant_cache_ps_prot.result	2009-05-15 10:15:56 +0000
+++ mysql-test/r/grant_cache_ps_prot.result	2010-12-28 22:19:49 +0000
@@ -174,7 +174,7 @@
 Qcache_hits	7
 show status like "Qcache_not_cached";
 Variable_name	Value
-Qcache_not_cached	4
+Qcache_not_cached	3
 ----- establish connection user4 (user=mysqltest_1) -----
 select "user4";
 user4
@@ -205,7 +205,7 @@
 Qcache_hits	8
 show status like "Qcache_not_cached";
 Variable_name	Value
-Qcache_not_cached	5
+Qcache_not_cached	4
 ----- close connections -----
 ----- switch to connection default -----
 set names binary;

=== modified file 'mysql-test/r/query_cache.result'
--- mysql-test/r/query_cache.result	2010-01-11 13:15:28 +0000
+++ mysql-test/r/query_cache.result	2011-01-04 18:38:08 +0000
@@ -1639,6 +1639,7 @@
 set GLOBAL query_cache_limit=default;
 set GLOBAL query_cache_min_res_unit=default;
 set GLOBAL query_cache_size=default;
+set local query_cache_type=default;
 FLUSH STATUS;
 SET GLOBAL query_cache_size=10*1024*1024;
 SET @save_concurrent_insert= @@concurrent_insert;
@@ -1823,3 +1824,33 @@
 DROP TABLE t1;
 SET GLOBAL query_cache_size= default;
 End of 5.1 tests
+New query cache switching OFF mechanism test
+set global query_cache_size=1024*1024*20;
+set global query_cache_type=on;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+@@query_cache_size	@@global.query_cache_type	@@local.query_cache_type
+20971520	ON	ON
+set global query_cache_size=0;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+@@query_cache_size	@@global.query_cache_type	@@local.query_cache_type
+0	ON	ON
+set global query_cache_size=1024*1024*20;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+@@query_cache_size	@@global.query_cache_type	@@local.query_cache_type
+20971520	ON	ON
+set global query_cache_type=off;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+@@query_cache_size	@@global.query_cache_type	@@local.query_cache_type
+20971520	OFF	OFF
+set global query_cache_type=on;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+@@query_cache_size	@@global.query_cache_type	@@local.query_cache_type
+20971520	ON	OFF
+set local query_cache_type= on;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+@@query_cache_size	@@global.query_cache_type	@@local.query_cache_type
+20971520	ON	ON
+restore defaults
+SET GLOBAL query_cache_type= default;
+SET GLOBAL query_cache_size= default;
+SET LOCAL query_cache_type= default;

=== added file 'mysql-test/r/query_cache_with_comments.result'
--- mysql-test/r/query_cache_with_comments.result	1970-01-01 00:00:00 +0000
+++ mysql-test/r/query_cache_with_comments.result	2010-12-28 22:19:49 +0000
@@ -0,0 +1,589 @@
+set global query_cache_strip_comments=ON;
+set GLOBAL query_cache_size=1355776;
+drop table if exists t1,t2;
+create table t1 (a int not null);
+insert into t1 values (1),(2),(3);
+"Flushing query cache"
+-----------------------------------------------------
+/* with comment first */select * from t1
+-----------------------------------------------------
+/* with comment first */select * from t1;
+a
+1
+2
+3
+/* with comment first */select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	1
+-----------------------------------------------------
+# with comment first
+select * from t1
+-----------------------------------------------------
+# with comment first
+select * from t1;
+a
+1
+2
+3
+# with comment first
+select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	3
+-----------------------------------------------------
+-- with comment first
+select * from t1
+-----------------------------------------------------
+-- with comment first
+select * from t1;
+a
+1
+2
+3
+-- with comment first
+select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	5
+-----------------------------------------------------
+/* with comment first and "quote" */select * from t1
+-----------------------------------------------------
+/* with comment first and "quote" */select * from t1;
+a
+1
+2
+3
+/* with comment first and "quote" */select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	7
+-----------------------------------------------------
+# with comment first and "quote"
+select * from t1
+-----------------------------------------------------
+# with comment first and "quote"
+select * from t1;
+a
+1
+2
+3
+# with comment first and "quote"
+select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	9
+-----------------------------------------------------
+-- with comment first and "quote" 
+select * from t1
+-----------------------------------------------------
+-- with comment first and "quote" 
+select * from t1;
+a
+1
+2
+3
+-- with comment first and "quote" 
+select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	11
+-----------------------------------------------------
+/* with comment and whitespaces first */select * from t1
+-----------------------------------------------------
+/* with comment and whitespaces first */select * from t1;
+a
+1
+2
+3
+/* with comment and whitespaces first */select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	13
+-----------------------------------------------------
+# with comment and whitespaces first
+select * from t1
+-----------------------------------------------------
+# with comment and whitespaces first
+select * from t1;
+a
+1
+2
+3
+# with comment and whitespaces first
+select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	15
+-----------------------------------------------------
+-- with comment and whitespaces first
+select * from t1
+-----------------------------------------------------
+-- with comment and whitespaces first
+select * from t1;
+a
+1
+2
+3
+-- with comment and whitespaces first
+select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	17
+-----------------------------------------------------
+select * /* internal comment */ from t1
+-----------------------------------------------------
+select * /* internal comment */ from t1;
+a
+1
+2
+3
+select * /* internal comment */ from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	19
+-----------------------------------------------------
+select */* internal comment */ from t1
+-----------------------------------------------------
+select */* internal comment */ from t1;
+a
+1
+2
+3
+select */* internal comment */ from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	21
+-----------------------------------------------------
+select */* internal comment */from t1
+-----------------------------------------------------
+select */* internal comment */from t1;
+a
+1
+2
+3
+select */* internal comment */from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	23
+-----------------------------------------------------
+select * /* internal comment with "quote" */ from t1
+-----------------------------------------------------
+select * /* internal comment with "quote" */ from t1;
+a
+1
+2
+3
+select * /* internal comment with "quote" */ from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	25
+-----------------------------------------------------
+select */* internal comment with "quote" */ from t1
+-----------------------------------------------------
+select */* internal comment with "quote" */ from t1;
+a
+1
+2
+3
+select */* internal comment with "quote" */ from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	27
+-----------------------------------------------------
+select */* internal comment with "quote" */from t1
+-----------------------------------------------------
+select */* internal comment with "quote" */from t1;
+a
+1
+2
+3
+select */* internal comment with "quote" */from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	29
+-----------------------------------------------------
+select * from t1
+
+-----------------------------------------------------
+select * from t1
+;
+a
+1
+2
+3
+select * from t1
+;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	31
+-----------------------------------------------------
+select * from t1 
+-----------------------------------------------------
+select * from t1 ;
+a
+1
+2
+3
+select * from t1 ;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	33
+-----------------------------------------------------
+select * from t1	
+-----------------------------------------------------
+select * from t1	;
+a
+1
+2
+3
+select * from t1	;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	35
+-----------------------------------------------------
+select * from t1
+/* comment in the end */
+-----------------------------------------------------
+select * from t1
+/* comment in the end */;
+a
+1
+2
+3
+select * from t1
+/* comment in the end */;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	37
+-----------------------------------------------------
+select * from t1
+/* *\/ */
+-----------------------------------------------------
+select * from t1
+/* *\/ */;
+a
+1
+2
+3
+select * from t1
+/* *\/ */;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	39
+-----------------------------------------------------
+select * from t1
+/* comment in the end */
+
+-----------------------------------------------------
+select * from t1
+/* comment in the end */
+;
+a
+1
+2
+3
+select * from t1
+/* comment in the end */
+;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	41
+-----------------------------------------------------
+select * from t1 #comment in the end
+-----------------------------------------------------
+select * from t1 #comment in the end;
+a
+1
+2
+3
+select * from t1 #comment in the end;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	43
+-----------------------------------------------------
+select * from t1 #comment in the end
+
+-----------------------------------------------------
+select * from t1 #comment in the end
+;
+a
+1
+2
+3
+select * from t1 #comment in the end
+;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	45
+-----------------------------------------------------
+select * from t1 -- comment in the end
+-----------------------------------------------------
+select * from t1 -- comment in the end;
+a
+1
+2
+3
+select * from t1 -- comment in the end;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	47
+-----------------------------------------------------
+select * from t1 -- comment in the end
+
+-----------------------------------------------------
+select * from t1 -- comment in the end
+;
+a
+1
+2
+3
+select * from t1 -- comment in the end
+;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	49
+-----------------------------------------------------
+select ' \'  ' from t1
+-----------------------------------------------------
+select ' \'  ' from t1;
+'  
+ '  
+ '  
+ '  
+select ' \'  ' from t1;
+'  
+ '  
+ '  
+ '  
+Qcache_queries_in_cache	2
+Qcache_inserts	2
+Qcache_hits	50
+"Flushing query cache"
+-----------------------------------------------------
+select a from t1/*alias*/table1 /*!50002 ORDER BY 1*/
+-----------------------------------------------------
+select a from t1/*alias*/table1 /*!50002 ORDER BY 1*/;
+a
+1
+2
+3
+select a from t1/*alias*/table1 /*!50002 ORDER BY 1*/;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	1
+-----------------------------------------------------
+select a from t1/*alias*/table1 /*!50003 ORDER BY 1*/
+-----------------------------------------------------
+select a from t1/*alias*/table1 /*!50003 ORDER BY 1*/;
+a
+1
+2
+3
+select a from t1/*alias*/table1 /*!50003 ORDER BY 1*/;
+a
+1
+2
+3
+Qcache_queries_in_cache	2
+Qcache_inserts	2
+Qcache_hits	2
+"Flushing query cache"
+-----------------------------------------------------
+select '/* hello */' from t1
+-----------------------------------------------------
+select '/* hello */' from t1;
+/* hello */
+/* hello */
+/* hello */
+/* hello */
+select '/* hello */' from t1;
+/* hello */
+/* hello */
+/* hello */
+/* hello */
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	1
+-----------------------------------------------------
+select "/* hello */" from t1
+-----------------------------------------------------
+select "/* hello */" from t1;
+/* hello */
+/* hello */
+/* hello */
+/* hello */
+select "/* hello */" from t1;
+/* hello */
+/* hello */
+/* hello */
+/* hello */
+Qcache_queries_in_cache	2
+Qcache_inserts	2
+Qcache_hits	2
+create table t2 (`/*a*/` int, `/*b*/` int);
+select `/*b*/` from t2;
+/*b*/
+-----------------------------------------------------
+select `/*a*/` from t2
+-----------------------------------------------------
+select `/*a*/` from t2;
+/*a*/
+select `/*a*/` from t2;
+/*a*/
+Qcache_queries_in_cache	4
+Qcache_inserts	4
+Qcache_hits	3
+drop table t2;
+-----------------------------------------------------
+select "hello""there""" from t1 limit 1
+-----------------------------------------------------
+select "hello""there""" from t1 limit 1;
+hello"there"
+hello"there"
+select "hello""there""" from t1 limit 1;
+hello"there"
+hello"there"
+Qcache_queries_in_cache	3
+Qcache_inserts	5
+Qcache_hits	4
+"Flushing query cache"
+-----------------------------------------------------
+select 1--1 from t1
+-----------------------------------------------------
+select 1--1 from t1;
+1--1
+2
+2
+2
+select 1--1 from t1;
+1--1
+2
+2
+2
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	1
+-----------------------------------------------------
+select 1--2 from t1
+-----------------------------------------------------
+select 1--2 from t1;
+1--2
+3
+3
+3
+select 1--2 from t1;
+1--2
+3
+3
+3
+Qcache_queries_in_cache	2
+Qcache_inserts	2
+Qcache_hits	2
+DROP TABLE t1;
+SET GLOBAL query_cache_size=default;
+set global query_cache_strip_comments=OFF;

=== added file 'mysql-test/r/query_cache_with_comments_crash.result'
--- mysql-test/r/query_cache_with_comments_crash.result	1970-01-01 00:00:00 +0000
+++ mysql-test/r/query_cache_with_comments_crash.result	2010-12-28 22:19:49 +0000
@@ -0,0 +1,21 @@
+set GLOBAL query_cache_size=1355776;
+drop table if exists t1;
+create table t1 (a int not null);
+insert into t1 values (1),(2),(3);
+flush query cache;
+flush query cache;
+reset query cache;
+flush status;
+( select * from t1 );
+a
+1
+2
+3
+/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, @@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), @OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, @@SQL_QUOTE_SHOW_CREATE := 1 */;
+/* only comment */;
+# only comment
+;
+-- only comment
+;
+DROP TABLE t1;
+SET GLOBAL query_cache_size= default;

=== added file 'mysql-test/r/query_cache_with_comments_disable.result'
--- mysql-test/r/query_cache_with_comments_disable.result	1970-01-01 00:00:00 +0000
+++ mysql-test/r/query_cache_with_comments_disable.result	2010-12-28 22:19:49 +0000
@@ -0,0 +1,588 @@
+set GLOBAL query_cache_size=1355776;
+drop table if exists t1,t2;
+create table t1 (a int not null);
+insert into t1 values (1),(2),(3);
+"Flushing query cache"
+-----------------------------------------------------
+/* with comment first */select * from t1
+-----------------------------------------------------
+/* with comment first */select * from t1;
+a
+1
+2
+3
+/* with comment first */select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	1
+-----------------------------------------------------
+# with comment first
+select * from t1
+-----------------------------------------------------
+# with comment first
+select * from t1;
+a
+1
+2
+3
+# with comment first
+select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	2
+Qcache_inserts	2
+Qcache_hits	2
+-----------------------------------------------------
+-- with comment first
+select * from t1
+-----------------------------------------------------
+-- with comment first
+select * from t1;
+a
+1
+2
+3
+-- with comment first
+select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	3
+Qcache_inserts	3
+Qcache_hits	3
+-----------------------------------------------------
+/* with comment first and "quote" */select * from t1
+-----------------------------------------------------
+/* with comment first and "quote" */select * from t1;
+a
+1
+2
+3
+/* with comment first and "quote" */select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	4
+Qcache_inserts	4
+Qcache_hits	4
+-----------------------------------------------------
+# with comment first and "quote"
+select * from t1
+-----------------------------------------------------
+# with comment first and "quote"
+select * from t1;
+a
+1
+2
+3
+# with comment first and "quote"
+select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	5
+Qcache_inserts	5
+Qcache_hits	5
+-----------------------------------------------------
+-- with comment first and "quote" 
+select * from t1
+-----------------------------------------------------
+-- with comment first and "quote" 
+select * from t1;
+a
+1
+2
+3
+-- with comment first and "quote" 
+select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	6
+Qcache_inserts	6
+Qcache_hits	6
+-----------------------------------------------------
+/* with comment and whitespaces first */select * from t1
+-----------------------------------------------------
+/* with comment and whitespaces first */select * from t1;
+a
+1
+2
+3
+/* with comment and whitespaces first */select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	7
+Qcache_inserts	7
+Qcache_hits	7
+-----------------------------------------------------
+# with comment and whitespaces first
+select * from t1
+-----------------------------------------------------
+# with comment and whitespaces first
+select * from t1;
+a
+1
+2
+3
+# with comment and whitespaces first
+select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	8
+Qcache_inserts	8
+Qcache_hits	8
+-----------------------------------------------------
+-- with comment and whitespaces first
+select * from t1
+-----------------------------------------------------
+-- with comment and whitespaces first
+select * from t1;
+a
+1
+2
+3
+-- with comment and whitespaces first
+select * from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	9
+Qcache_inserts	9
+Qcache_hits	9
+-----------------------------------------------------
+select * /* internal comment */ from t1
+-----------------------------------------------------
+select * /* internal comment */ from t1;
+a
+1
+2
+3
+select * /* internal comment */ from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	10
+Qcache_inserts	10
+Qcache_hits	10
+-----------------------------------------------------
+select */* internal comment */ from t1
+-----------------------------------------------------
+select */* internal comment */ from t1;
+a
+1
+2
+3
+select */* internal comment */ from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	11
+Qcache_inserts	11
+Qcache_hits	11
+-----------------------------------------------------
+select */* internal comment */from t1
+-----------------------------------------------------
+select */* internal comment */from t1;
+a
+1
+2
+3
+select */* internal comment */from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	12
+Qcache_inserts	12
+Qcache_hits	12
+-----------------------------------------------------
+select * /* internal comment with "quote" */ from t1
+-----------------------------------------------------
+select * /* internal comment with "quote" */ from t1;
+a
+1
+2
+3
+select * /* internal comment with "quote" */ from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	13
+Qcache_inserts	13
+Qcache_hits	13
+-----------------------------------------------------
+select */* internal comment with "quote" */ from t1
+-----------------------------------------------------
+select */* internal comment with "quote" */ from t1;
+a
+1
+2
+3
+select */* internal comment with "quote" */ from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	14
+Qcache_inserts	14
+Qcache_hits	14
+-----------------------------------------------------
+select */* internal comment with "quote" */from t1
+-----------------------------------------------------
+select */* internal comment with "quote" */from t1;
+a
+1
+2
+3
+select */* internal comment with "quote" */from t1;
+a
+1
+2
+3
+Qcache_queries_in_cache	15
+Qcache_inserts	15
+Qcache_hits	15
+-----------------------------------------------------
+select * from t1
+
+-----------------------------------------------------
+select * from t1
+;
+a
+1
+2
+3
+select * from t1
+;
+a
+1
+2
+3
+Qcache_queries_in_cache	16
+Qcache_inserts	16
+Qcache_hits	16
+-----------------------------------------------------
+select * from t1 
+-----------------------------------------------------
+select * from t1 ;
+a
+1
+2
+3
+select * from t1 ;
+a
+1
+2
+3
+Qcache_queries_in_cache	16
+Qcache_inserts	16
+Qcache_hits	18
+-----------------------------------------------------
+select * from t1	
+-----------------------------------------------------
+select * from t1	;
+a
+1
+2
+3
+select * from t1	;
+a
+1
+2
+3
+Qcache_queries_in_cache	16
+Qcache_inserts	16
+Qcache_hits	20
+-----------------------------------------------------
+select * from t1
+/* comment in the end */
+-----------------------------------------------------
+select * from t1
+/* comment in the end */;
+a
+1
+2
+3
+select * from t1
+/* comment in the end */;
+a
+1
+2
+3
+Qcache_queries_in_cache	17
+Qcache_inserts	17
+Qcache_hits	21
+-----------------------------------------------------
+select * from t1
+/* *\/ */
+-----------------------------------------------------
+select * from t1
+/* *\/ */;
+a
+1
+2
+3
+select * from t1
+/* *\/ */;
+a
+1
+2
+3
+Qcache_queries_in_cache	18
+Qcache_inserts	18
+Qcache_hits	22
+-----------------------------------------------------
+select * from t1
+/* comment in the end */
+
+-----------------------------------------------------
+select * from t1
+/* comment in the end */
+;
+a
+1
+2
+3
+select * from t1
+/* comment in the end */
+;
+a
+1
+2
+3
+Qcache_queries_in_cache	18
+Qcache_inserts	18
+Qcache_hits	24
+-----------------------------------------------------
+select * from t1 #comment in the end
+-----------------------------------------------------
+select * from t1 #comment in the end;
+a
+1
+2
+3
+select * from t1 #comment in the end;
+a
+1
+2
+3
+Qcache_queries_in_cache	19
+Qcache_inserts	19
+Qcache_hits	25
+-----------------------------------------------------
+select * from t1 #comment in the end
+
+-----------------------------------------------------
+select * from t1 #comment in the end
+;
+a
+1
+2
+3
+select * from t1 #comment in the end
+;
+a
+1
+2
+3
+Qcache_queries_in_cache	19
+Qcache_inserts	19
+Qcache_hits	27
+-----------------------------------------------------
+select * from t1 -- comment in the end
+-----------------------------------------------------
+select * from t1 -- comment in the end;
+a
+1
+2
+3
+select * from t1 -- comment in the end;
+a
+1
+2
+3
+Qcache_queries_in_cache	20
+Qcache_inserts	20
+Qcache_hits	28
+-----------------------------------------------------
+select * from t1 -- comment in the end
+
+-----------------------------------------------------
+select * from t1 -- comment in the end
+;
+a
+1
+2
+3
+select * from t1 -- comment in the end
+;
+a
+1
+2
+3
+Qcache_queries_in_cache	20
+Qcache_inserts	20
+Qcache_hits	30
+-----------------------------------------------------
+select ' \'  ' from t1
+-----------------------------------------------------
+select ' \'  ' from t1;
+'  
+ '  
+ '  
+ '  
+select ' \'  ' from t1;
+'  
+ '  
+ '  
+ '  
+Qcache_queries_in_cache	21
+Qcache_inserts	21
+Qcache_hits	31
+"Flushing query cache"
+-----------------------------------------------------
+select a from t1/*alias*/table1 /*!50002 ORDER BY 1*/
+-----------------------------------------------------
+select a from t1/*alias*/table1 /*!50002 ORDER BY 1*/;
+a
+1
+2
+3
+select a from t1/*alias*/table1 /*!50002 ORDER BY 1*/;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	1
+-----------------------------------------------------
+select a from t1/*alias*/table1 /*!50003 ORDER BY 1*/
+-----------------------------------------------------
+select a from t1/*alias*/table1 /*!50003 ORDER BY 1*/;
+a
+1
+2
+3
+select a from t1/*alias*/table1 /*!50003 ORDER BY 1*/;
+a
+1
+2
+3
+Qcache_queries_in_cache	2
+Qcache_inserts	2
+Qcache_hits	2
+"Flushing query cache"
+-----------------------------------------------------
+select '/* hello */' from t1
+-----------------------------------------------------
+select '/* hello */' from t1;
+/* hello */
+/* hello */
+/* hello */
+/* hello */
+select '/* hello */' from t1;
+/* hello */
+/* hello */
+/* hello */
+/* hello */
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	1
+-----------------------------------------------------
+select "/* hello */" from t1
+-----------------------------------------------------
+select "/* hello */" from t1;
+/* hello */
+/* hello */
+/* hello */
+/* hello */
+select "/* hello */" from t1;
+/* hello */
+/* hello */
+/* hello */
+/* hello */
+Qcache_queries_in_cache	2
+Qcache_inserts	2
+Qcache_hits	2
+create table t2 (`/*a*/` int, `/*b*/` int);
+select `/*b*/` from t2;
+/*b*/
+-----------------------------------------------------
+select `/*a*/` from t2
+-----------------------------------------------------
+select `/*a*/` from t2;
+/*a*/
+select `/*a*/` from t2;
+/*a*/
+Qcache_queries_in_cache	4
+Qcache_inserts	4
+Qcache_hits	3
+drop table t2;
+-----------------------------------------------------
+select "hello""there""" from t1 limit 1
+-----------------------------------------------------
+select "hello""there""" from t1 limit 1;
+hello"there"
+hello"there"
+select "hello""there""" from t1 limit 1;
+hello"there"
+hello"there"
+Qcache_queries_in_cache	3
+Qcache_inserts	5
+Qcache_hits	4
+"Flushing query cache"
+-----------------------------------------------------
+select 1--1 from t1
+-----------------------------------------------------
+select 1--1 from t1;
+1--1
+2
+2
+2
+select 1--1 from t1;
+1--1
+2
+2
+2
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	1
+-----------------------------------------------------
+select 1--2 from t1
+-----------------------------------------------------
+select 1--2 from t1;
+1--2
+3
+3
+3
+select 1--2 from t1;
+1--2
+3
+3
+3
+Qcache_queries_in_cache	2
+Qcache_inserts	2
+Qcache_hits	2
+DROP TABLE t1;
+SET GLOBAL query_cache_size=default;
+set global query_cache_strip_comments=OFF;

=== added file 'mysql-test/r/query_cache_with_comments_ps.result'
--- mysql-test/r/query_cache_with_comments_ps.result	1970-01-01 00:00:00 +0000
+++ mysql-test/r/query_cache_with_comments_ps.result	2010-12-28 22:19:49 +0000
@@ -0,0 +1,255 @@
+set GLOBAL query_cache_size=1355776;
+drop table if exists t1;
+"Flushing query cache"
+create table t1 (a int not null);
+insert into t1 values (1),(2),(3);
+set global query_cache_strip_comments=ON;
+Qcache_queries_in_cache	0
+Qcache_inserts	0
+Qcache_hits	0
+prepare stmt from '/* with comment */ select * from t1';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	0
+execute stmt;
+a
+1
+2
+3
+execute stmt;
+a
+1
+2
+3
+execute stmt;
+a
+1
+2
+3
+execute stmt;
+a
+1
+2
+3
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	5
+prepare stmt from 'select * from t1';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	6
+prepare stmt from 'select * /*internal comment*/from t1';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	7
+prepare stmt from 'select * /*internal comment*/ from t1';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	8
+prepare stmt from 'select * from t1 /* at the end */';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	9
+prepare stmt from 'select * from t1 /* with "quote" */';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	10
+prepare stmt from 'select * from t1 /* with \'quote\' */';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	11
+prepare stmt from 'select * from t1 # 123
+';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	12
+prepare stmt from 'select * from t1 # 123 with "quote"
+';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	13
+prepare stmt from 'select * from t1 # 123 with \'quote\'
+';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	14
+prepare stmt from 'select * from t1
+# 123
+';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	15
+prepare stmt from '#456
+select * from t1
+# 123
+';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	16
+prepare stmt from 'select * from t1 -- 123
+';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	17
+prepare stmt from 'select * from t1
+-- 123
+';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	18
+prepare stmt from '-- comment in first
+select * from t1
+# 123
+';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	1
+Qcache_inserts	1
+Qcache_hits	19
+prepare stmt from '(#456(
+select * from t1
+# 123(
+)';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	2
+Qcache_inserts	2
+Qcache_hits	19
+prepare stmt from '/*test*/(-- comment in first(
+select * from t1
+-- 123 asdasd
+/* test */)';
+execute stmt;
+a
+1
+2
+3
+Qcache_queries_in_cache	2
+Qcache_inserts	2
+Qcache_hits	20
+prepare stmt from 'select "test",a from t1';
+execute stmt;
+test	a
+test	1
+test	2
+test	3
+execute stmt;
+test	a
+test	1
+test	2
+test	3
+Qcache_queries_in_cache	3
+Qcache_inserts	3
+Qcache_hits	21
+prepare stmt from 'select "test /* internal \'comment\' */",a from t1';
+execute stmt;
+test /* internal 'comment' */	a
+test /* internal 'comment' */	1
+test /* internal 'comment' */	2
+test /* internal 'comment' */	3
+Qcache_queries_in_cache	4
+Qcache_inserts	4
+Qcache_hits	21
+prepare stmt from 'select "test #internal comment" ,a from t1';
+execute stmt;
+test #internal comment	a
+test #internal comment	1
+test #internal comment	2
+test #internal comment	3
+Qcache_queries_in_cache	5
+Qcache_inserts	5
+Qcache_hits	21
+prepare stmt from 'select "test #internal comment" #external comment
+,a from t1';
+execute stmt;
+test #internal comment	a
+test #internal comment	1
+test #internal comment	2
+test #internal comment	3
+Qcache_queries_in_cache	5
+Qcache_inserts	5
+Qcache_hits	22
+DROP TABLE t1;
+SET GLOBAL query_cache_size= default;
+set global query_cache_strip_comments=OFF;

=== added file 'mysql-test/r/status_wait_query_cache_mutex.result'
--- mysql-test/r/status_wait_query_cache_mutex.result	1970-01-01 00:00:00 +0000
+++ mysql-test/r/status_wait_query_cache_mutex.result	2010-12-28 22:19:49 +0000
@@ -0,0 +1,24 @@
+set GLOBAL query_cache_size=1355776;
+"Flushing query cache"
+DROP TABLE IF EXISTS t;
+CREATE TABLE t(id INT, number INT);
+INSERT INTO t VALUES (0,1);
+INSERT INTO t VALUES (1,2);
+INSERT INTO t VALUES (2,3);
+SELECT number from t where id > 0;
+number
+2
+3
+SET SESSION debug="+d,status_wait_query_cache_mutex_sleep";
+SELECT number from t where id > 0;
+SET SESSION debug="+d,status_wait_query_cache_mutex_sleep";
+SELECT number from t where id > 0;
+SET SESSION debug="+d,status_wait_query_cache_mutex_sleep";
+SHOW PROCESSLIST;
+Id	User	Host	db	Command	Time	State	Info
+Id	root	localhost	test	Sleep	Time		NULL
+Id	root	localhost	test	Query	Time	Waiting on query cache mutex	SELECT number from t where id > 0
+Id	root	localhost	test	Query	Time	Waiting on query cache mutex	SELECT number from t where id > 0
+Id	root	localhost	test	Query	Time	NULL	SHOW PROCESSLIST
+DROP TABLE t;
+set GLOBAL query_cache_size=0;

=== modified file 'mysql-test/t/query_cache.test'
--- mysql-test/t/query_cache.test	2010-01-11 13:15:28 +0000
+++ mysql-test/t/query_cache.test	2011-01-04 18:36:51 +0000
@@ -1254,6 +1254,7 @@
 set GLOBAL query_cache_limit=default;
 set GLOBAL query_cache_min_res_unit=default;
 set GLOBAL query_cache_size=default;
+set local query_cache_type=default;
 
 #
 # Bug#33756 - query cache with concurrent_insert=0 appears broken
@@ -1514,3 +1515,23 @@
 
 --echo End of 5.1 tests
 
+--echo New query cache switching OFF mechanism test
+set global query_cache_size=1024*1024*20;
+set global query_cache_type=on;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+set global query_cache_size=0;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+set global query_cache_size=1024*1024*20;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+set global query_cache_type=off;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+set global query_cache_type=on;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+set local query_cache_type= on;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+
+
+--echo restore defaults
+SET GLOBAL query_cache_type= default;
+SET GLOBAL query_cache_size= default;
+SET LOCAL query_cache_type= default;

=== added file 'mysql-test/t/query_cache_with_comments.test'
--- mysql-test/t/query_cache_with_comments.test	1970-01-01 00:00:00 +0000
+++ mysql-test/t/query_cache_with_comments.test	2010-12-28 22:19:49 +0000
@@ -0,0 +1,5 @@
+--disable_ps_protocol
+set global query_cache_strip_comments=ON;
+-- source include/query_cache_with_comments_begin.inc
+-- source include/query_cache_with_comments.inc
+-- source include/query_cache_with_comments_end.inc

=== added file 'mysql-test/t/query_cache_with_comments_crash.test'
--- mysql-test/t/query_cache_with_comments_crash.test	1970-01-01 00:00:00 +0000
+++ mysql-test/t/query_cache_with_comments_crash.test	2010-12-28 22:19:49 +0000
@@ -0,0 +1,22 @@
+-- source include/have_query_cache.inc
+set GLOBAL query_cache_size=1355776;
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+create table t1 (a int not null);
+insert into t1 values (1),(2),(3);
+flush query cache; # This crashed in some versions
+flush query cache; # This crashed in some versions
+reset query cache;
+flush status;
+( select * from t1 );
+/*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, @@SQL_MODE := REPLACE(REPLACE(@@SQL_MODE, 'ANSI_QUOTES', ''), ',,', ','), @OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, @@SQL_QUOTE_SHOW_CREATE := 1 */;
+/* only comment */;
+let query=# only comment
+;
+eval $query;
+let query=-- only comment
+;
+eval $query;
+DROP TABLE t1;
+SET GLOBAL query_cache_size= default;

=== added file 'mysql-test/t/query_cache_with_comments_disable.test'
--- mysql-test/t/query_cache_with_comments_disable.test	1970-01-01 00:00:00 +0000
+++ mysql-test/t/query_cache_with_comments_disable.test	2010-12-28 22:19:49 +0000
@@ -0,0 +1,3 @@
+-- source include/query_cache_with_comments_begin.inc
+-- source include/query_cache_with_comments.inc
+-- source include/query_cache_with_comments_end.inc

=== added file 'mysql-test/t/query_cache_with_comments_ps.test'
--- mysql-test/t/query_cache_with_comments_ps.test	1970-01-01 00:00:00 +0000
+++ mysql-test/t/query_cache_with_comments_ps.test	2010-12-28 22:19:49 +0000
@@ -0,0 +1,139 @@
+-- source include/have_query_cache.inc
+
+set GLOBAL query_cache_size=1355776;
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+--source include/query_cache_with_comments_clear.inc
+
+#
+# First simple test
+#
+
+create table t1 (a int not null);
+insert into t1 values (1),(2),(3);
+
+set global query_cache_strip_comments=ON;
+
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from '/* with comment */ select * from t1';
+execute stmt;
+
+--source include/query_cache_with_comments_show.inc
+
+execute stmt;
+execute stmt;
+execute stmt;
+execute stmt;
+execute stmt;
+
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select * from t1';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select * /*internal comment*/from t1';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select * /*internal comment*/ from t1';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select * from t1 /* at the end */';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select * from t1 /* with "quote" */';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select * from t1 /* with \'quote\' */';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select * from t1 # 123
+';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select * from t1 # 123 with "quote"
+';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select * from t1 # 123 with \'quote\'
+';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select * from t1
+# 123
+';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from '#456
+select * from t1
+# 123
+';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select * from t1 -- 123
+';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select * from t1
+-- 123
+';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from '-- comment in first
+select * from t1
+# 123
+';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from '(#456(
+select * from t1
+# 123(
+)';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from '/*test*/(-- comment in first(
+select * from t1
+-- 123 asdasd
+/* test */)';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select "test",a from t1';
+execute stmt;
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select "test /* internal \'comment\' */",a from t1';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select "test #internal comment" ,a from t1';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+prepare stmt from 'select "test #internal comment" #external comment
+,a from t1';
+execute stmt;
+--source include/query_cache_with_comments_show.inc
+
+DROP TABLE t1;
+SET GLOBAL query_cache_size= default;
+set global query_cache_strip_comments=OFF;

=== added file 'mysql-test/t/status_wait_query_cache_mutex.test'
--- mysql-test/t/status_wait_query_cache_mutex.test	1970-01-01 00:00:00 +0000
+++ mysql-test/t/status_wait_query_cache_mutex.test	2010-12-28 22:19:49 +0000
@@ -0,0 +1,37 @@
+--source include/have_query_cache.inc
+--source include/have_debug.inc
+set GLOBAL query_cache_size=1355776;
+--source include/query_cache_with_comments_clear.inc
+
+-- disable_warnings
+DROP TABLE IF EXISTS t;
+-- enable_warnings
+CREATE TABLE t(id INT, number INT);
+INSERT INTO t VALUES (0,1);
+INSERT INTO t VALUES (1,2);
+INSERT INTO t VALUES (2,3);
+SELECT number from t where id > 0;
+--connect (conn0,localhost,root,,)
+--connect (conn1,localhost,root,,)
+--connect (conn2,localhost,root,,)
+
+--connection conn0
+--error 0, ER_UNKNOWN_SYSTEM_VARIABLE
+SET SESSION debug="+d,status_wait_query_cache_mutex_sleep";
+SEND SELECT number from t where id > 0;
+SLEEP 1.0;
+
+--connection conn1
+--error 0, ER_UNKNOWN_SYSTEM_VARIABLE
+SET SESSION debug="+d,status_wait_query_cache_mutex_sleep";
+SEND SELECT number from t where id > 0;
+SLEEP 1.0;
+
+--connection conn2
+--error 0, ER_UNKNOWN_SYSTEM_VARIABLE
+SET SESSION debug="+d,status_wait_query_cache_mutex_sleep";
+--replace_column 1 Id 6 Time
+SHOW PROCESSLIST;
+
+DROP TABLE t;
+set GLOBAL query_cache_size=0;

=== modified file 'mysys/charset.c'
--- mysys/charset.c	2010-11-30 21:11:03 +0000
+++ mysys/charset.c	2010-12-28 22:19:49 +0000
@@ -97,6 +97,9 @@
   state_map[(uchar)'@']= (uchar) MY_LEX_USER_END;
   state_map[(uchar) '`']= (uchar) MY_LEX_USER_VARIABLE_DELIMITER;
   state_map[(uchar)'"']= (uchar) MY_LEX_STRING_OR_DELIMITER;
+  state_map[(uchar)'-']= (uchar) MY_LEX_MINUS_OR_COMMENT;
+  state_map[(uchar)',']= (uchar) MY_LEX_COMMA;
+  state_map[(uchar)'?']= (uchar) MY_LEX_PLACEHOLDER;
 
   /*
     Create a second map to make it faster to find identifiers

=== modified file 'sql-common/client_plugin.c'
--- sql-common/client_plugin.c	2010-03-29 15:13:53 +0000
+++ sql-common/client_plugin.c	2010-12-28 22:19:49 +0000
@@ -50,7 +50,9 @@
 static my_bool initialized= 0;
 static MEM_ROOT mem_root;
 
+#ifdef HAVE_DLOPEN
 static const char *plugin_declarations_sym= "_mysql_client_plugin_declaration_";
+#endif
 static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]=
 {
   0, /* these two are taken by Connector/C */

=== modified file 'sql/handler.cc'
--- sql/handler.cc	2010-11-30 21:11:03 +0000
+++ sql/handler.cc	2010-12-28 22:19:49 +0000
@@ -1254,7 +1254,7 @@
     {
 #ifdef HAVE_QUERY_CACHE
       if (thd->transaction.changed_tables)
-        query_cache.invalidate(thd->transaction.changed_tables);
+        query_cache.invalidate(thd, thd->transaction.changed_tables);
 #endif
       thd->variables.tx_isolation=thd->session_tx_isolation;
     }

=== modified file 'sql/log_event.cc'
--- sql/log_event.cc	2010-12-01 20:24:49 +0000
+++ sql/log_event.cc	2010-12-28 22:19:49 +0000
@@ -7521,7 +7521,7 @@
       const_cast<Relay_log_info*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table);
     }
 #ifdef HAVE_QUERY_CACHE
-    query_cache.invalidate_locked_for_write(rli->tables_to_lock);
+    query_cache.invalidate_locked_for_write(thd, rli->tables_to_lock);
 #endif
   }
 

=== modified file 'sql/log_event_old.cc'
--- sql/log_event_old.cc	2010-09-11 18:43:48 +0000
+++ sql/log_event_old.cc	2010-12-28 22:19:49 +0000
@@ -137,7 +137,7 @@
       const_cast<Relay_log_info*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table);
     }
 #ifdef HAVE_QUERY_CACHE
-    query_cache.invalidate_locked_for_write(rli->tables_to_lock);
+    query_cache.invalidate_locked_for_write(thd, rli->tables_to_lock);
 #endif
   }
 
@@ -1636,7 +1636,7 @@
       const_cast<Relay_log_info*>(rli)->m_table_map.set_table(ptr->table_id, ptr->table);
     }
 #ifdef HAVE_QUERY_CACHE
-    query_cache.invalidate_locked_for_write(rli->tables_to_lock);
+    query_cache.invalidate_locked_for_write(thd, rli->tables_to_lock);
 #endif
   }
 

=== modified file 'sql/mysql_priv.h'
--- sql/mysql_priv.h	2010-11-30 21:11:03 +0000
+++ sql/mysql_priv.h	2011-01-04 15:37:59 +0000
@@ -885,7 +885,7 @@
 #define query_cache_resize(A) query_cache.resize(A)
 #define query_cache_set_min_res_unit(A) query_cache.set_min_res_unit(A)
 #define query_cache_invalidate3(A, B, C) query_cache.invalidate(A, B, C)
-#define query_cache_invalidate1(A) query_cache.invalidate(A)
+#define query_cache_invalidate1(A,B) query_cache.invalidate(A,B)
 #define query_cache_send_result_to_client(A, B, C) \
   query_cache.send_result_to_client(A, B, C)
 #define query_cache_invalidate_by_MyISAM_filename_ref \
@@ -897,19 +897,19 @@
   (((L)->sql_command == SQLCOM_SELECT) && (L)->safe_to_cache_query)
 #else
 #define QUERY_CACHE_FLAGS_SIZE 0
-#define query_cache_store_query(A, B)
-#define query_cache_destroy()
-#define query_cache_result_size_limit(A)
-#define query_cache_init()
-#define query_cache_resize(A)
-#define query_cache_set_min_res_unit(A)
-#define query_cache_invalidate3(A, B, C)
-#define query_cache_invalidate1(A)
+#define query_cache_store_query(A, B) {}
+#define query_cache_destroy() {}
+#define query_cache_result_size_limit(A) {}
+#define query_cache_init() {}
+#define query_cache_resize(A) {}
+#define query_cache_set_min_res_unit(A) {}
+#define query_cache_invalidate3(A, B, C) {}
+#define query_cache_invalidate1(A, B) {}
 #define query_cache_send_result_to_client(A, B, C) 0
 #define query_cache_invalidate_by_MyISAM_filename_ref NULL
 
-#define query_cache_abort(A)
-#define query_cache_end_of_result(A)
+#define query_cache_abort(A) {}
+#define query_cache_end_of_result(A) {}
 #define query_cache_maybe_disabled(T) 1
 #define query_cache_is_cacheable_query(L) 0
 #endif /*HAVE_QUERY_CACHE*/
@@ -2022,6 +2022,7 @@
 extern my_bool opt_secure_auth, debug_assert_if_crashed_table;
 extern char* opt_secure_file_priv;
 extern my_bool opt_log_slow_admin_statements, opt_log_slow_slave_statements;
+extern my_bool opt_query_cache_strip_comments;
 extern my_bool sp_automatic_privileges, opt_noacl;
 extern my_bool opt_old_style_user_limits, trust_function_creators;
 extern uint opt_crash_binlog_innodb;

=== modified file 'sql/mysqld.cc'
--- sql/mysqld.cc	2010-11-30 21:11:03 +0000
+++ sql/mysqld.cc	2010-12-28 22:19:49 +0000
@@ -547,6 +547,7 @@
 char* opt_secure_file_priv= 0;
 my_bool opt_log_slow_admin_statements= 0;
 my_bool opt_log_slow_slave_statements= 0;
+my_bool opt_query_cache_strip_comments = 0;
 my_bool lower_case_file_system= 0;
 my_bool opt_large_pages= 0;
 my_bool opt_myisam_use_mmap= 0;
@@ -6032,7 +6033,8 @@
   OPT_SLOW_QUERY_LOG_FILE,
   OPT_IGNORE_BUILTIN_INNODB,
   OPT_BINLOG_DIRECT_NON_TRANS_UPDATE,
-  OPT_DEFAULT_CHARACTER_SET_OLD
+  OPT_DEFAULT_CHARACTER_SET_OLD,
+  OPT_QUERY_CACHE_STRIP_COMMENTS
 };
 
 
@@ -7397,6 +7399,13 @@
    &query_cache_size, &query_cache_size, 0, GET_ULONG,
    REQUIRED_ARG, 0, 0, (longlong) ULONG_MAX, 0, 1024, 0},
 #ifdef HAVE_QUERY_CACHE
+  {"query_cache_strip_comments", OPT_QUERY_CACHE_STRIP_COMMENTS,
+   "Enable and disable optimisation \"strip comment for query cache\" - "
+   "optimisation strip all comments from query while search query result "
+   "in query cache",
+   (uchar**) &opt_query_cache_strip_comments,
+   (uchar**) &opt_query_cache_strip_comments,
+   0, GET_BOOL, REQUIRED_ARG, 0, 0, 1, 0, 1, 0},
   {"query_cache_type", OPT_QUERY_CACHE_TYPE,
    "0 = OFF = Don't cache or retrieve results. 1 = ON = Cache all results "
    "except SELECT SQL_NO_CACHE ... queries. 2 = DEMAND = Cache only SELECT "

=== modified file 'sql/set_var.cc'
--- sql/set_var.cc	2010-12-04 14:32:42 +0000
+++ sql/set_var.cc	2011-01-04 18:17:12 +0000
@@ -127,9 +127,11 @@
 static void fix_net_write_timeout(THD *thd, enum_var_type type);
 static void fix_net_retry_count(THD *thd, enum_var_type type);
 static void fix_max_join_size(THD *thd, enum_var_type type);
+#ifdef HAVE_QUERY_CACHE
 static void fix_query_cache_size(THD *thd, enum_var_type type);
-#ifdef HAVE_QUERY_CACHE
 static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type);
+static int check_query_cache_type(THD *thd, set_var *var);
+static void fix_query_cache_type(THD *thd, enum_var_type type);
 #endif
 static void fix_myisam_max_sort_file_size(THD *thd, enum_var_type type);
 static void fix_max_binlog_size(THD *thd, enum_var_type type);
@@ -530,9 +532,6 @@
                                               &SV::div_precincrement);
 static sys_var_long_ptr	sys_rpl_recovery_rank(&vars, "rpl_recovery_rank",
 					      &rpl_recovery_rank);
-static sys_var_long_ptr	sys_query_cache_size(&vars, "query_cache_size",
-					     &query_cache_size,
-					     fix_query_cache_size);
 
 static sys_var_thd_ulong	sys_range_alloc_block_size(&vars, "range_alloc_block_size",
 						   &SV::range_alloc_block_size);
@@ -597,14 +596,20 @@
                                               &thread_handling_typelib);
 
 #ifdef HAVE_QUERY_CACHE
+static sys_var_long_ptr	sys_query_cache_size(&vars, "query_cache_size",
+                                             &query_cache_size,
+                                             fix_query_cache_size);
 static sys_var_long_ptr	sys_query_cache_limit(&vars, "query_cache_limit",
-					      &query_cache.query_cache_limit);
-static sys_var_long_ptr        sys_query_cache_min_res_unit(&vars, "query_cache_min_res_unit",
-						     &query_cache_min_res_unit,
-						     fix_query_cache_min_res_unit);
+                                              &query_cache.query_cache_limit);
+static sys_var_long_ptr
+  sys_query_cache_min_res_unit(&vars, "query_cache_min_res_unit",
+                               &query_cache_min_res_unit,
+                               fix_query_cache_min_res_unit);
 static sys_var_thd_enum	sys_query_cache_type(&vars, "query_cache_type",
 					     &SV::query_cache_type,
-					     &query_cache_type_typelib);
+					     &query_cache_type_typelib,
+                                             fix_query_cache_type,
+                                             check_query_cache_type);
 static sys_var_thd_bool
 sys_query_cache_wlock_invalidate(&vars, "query_cache_wlock_invalidate",
 				 &SV::query_cache_wlock_invalidate);
@@ -898,6 +903,8 @@
 #ifndef EMBEDDED_LIBRARY
 static sys_var_const_str_ptr    sys_repl_report_host(&vars, "report_host", &report_host);
 static sys_var_const_str_ptr    sys_repl_report_user(&vars, "report_user", &report_user);
+static sys_var_bool_ptr       sys_query_cache_strip_comments(&vars, "query_cache_strip_comments",
+                                                       &opt_query_cache_strip_comments);
 static sys_var_const_str_ptr    sys_repl_report_password(&vars, "report_password", &report_password);
 
 static uchar *slave_get_report_port(THD *thd)
@@ -1187,10 +1194,9 @@
 {}
 #endif /* HAVE_REPLICATION */
 
-
+#ifdef HAVE_QUERY_CACHE
 static void fix_query_cache_size(THD *thd, enum_var_type type)
 {
-#ifdef HAVE_QUERY_CACHE
   ulong new_cache_size= query_cache.resize(query_cache_size);
 
   /*
@@ -1204,11 +1210,55 @@
 			query_cache_size, new_cache_size);
   
   query_cache_size= new_cache_size;
-#endif
-}
-
-
-#ifdef HAVE_QUERY_CACHE
+}
+
+
+/**
+  Trigger before query_cache_type variable is updated.
+  @param thd Thread handler
+  @param var Pointer to the new variable status
+
+  @return Status code
+   @retval TRUE  Failure
+   @retval FALSE Success
+*/
+
+static int check_query_cache_type(THD *thd, set_var *var)
+{
+  /*
+    Don't allow changes of the query_cache_type if the query cache
+    is disabled.
+  */
+  if (query_cache.is_disable_in_progress())
+  {
+    my_error(ER_QUERY_CACHE_IS_DISABLED, MYF(0));
+    return TRUE;
+  }
+  if (var->type != OPT_GLOBAL &&
+      global_system_variables.query_cache_type == 0 &&
+      var->value->val_int() != 0)
+  {
+    my_error(ER_QUERY_CACHE_IS_GLOBALY_DISABLED, MYF(0));
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+
+static void fix_query_cache_type(THD *thd, enum_var_type type)
+{
+  if (type == OPT_GLOBAL)
+  {
+    if (global_system_variables.query_cache_type != 0 &&
+        query_cache.is_disabled())
+      fix_query_cache_size(thd, type);
+    else if (global_system_variables.query_cache_type == 0)
+      query_cache.disable_query_cache();
+  }
+}
+
+
 static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type)
 {
   query_cache_min_res_unit= 
@@ -3644,6 +3694,16 @@
   Functions to handle SET mysql_internal_variable=const_expr
 *****************************************************************************/
 
+/**
+  Verify that the supplied value is correct.
+
+  @param thd Thread handler
+
+  @return status code
+    @retval -1 Failure
+    @retval 0 Success
+*/
+
 int set_var::check(THD *thd)
 {
   if (var->is_readonly())

=== modified file 'sql/set_var.h'
--- sql/set_var.h	2010-09-11 18:43:48 +0000
+++ sql/set_var.h	2010-12-28 22:19:49 +0000
@@ -514,10 +514,15 @@
   { chain_sys_var(chain); }
   bool check(THD *thd, set_var *var)
   {
-    int ret= 0;
-    if (check_func)
-      ret= (*check_func)(thd, var);
-    return ret ? ret : check_enum(thd, var, enum_names);
+    /*
+      check_enum fails if the character representation supplied was wrong
+      or that the integer value was wrong or missing.
+    */
+    if (check_enum(thd, var, enum_names))
+      return TRUE;
+    if ((check_func && (*check_func)(thd, var)))
+      return TRUE;
+    return FALSE;
   }
   bool update(THD *thd, set_var *var);
   void set_default(THD *thd, enum_var_type type);

=== modified file 'sql/share/errmsg.txt'
--- sql/share/errmsg.txt	2010-09-12 12:29:54 +0000
+++ sql/share/errmsg.txt	2010-12-30 16:47:21 +0000
@@ -6247,3 +6247,7 @@
         eng "Unknown option '%-.64s'"
 ER_BAD_OPTION_VALUE
         eng "Incorrect value '%-.64s' for option '%-.64s'"
+ER_QUERY_CACHE_IS_DISABLED
+  eng "Query cache is disabled (resize or similar command in progress); repeat this command later"
+ER_QUERY_CACHE_IS_GLOBALY_DISABLED
+  eng "Query cache is globally disabled and you can't enable it only for this session"

=== modified file 'sql/sql_cache.cc'
--- sql/sql_cache.cc	2010-11-30 21:11:03 +0000
+++ sql/sql_cache.cc	2010-12-30 17:33:22 +0000
@@ -286,6 +286,7 @@
          if (and only if) this query has a registered result set writer
          (thd->net.query_cache_query).
  4. Query_cache::invalidate
+    Query_cache::invalidate_locked_for_write
        - Called from various places to invalidate query cache based on data-
          base, table and myisam file name. During an on going invalidation
          the query cache is temporarily disabled.
@@ -334,6 +335,8 @@
 #include "../storage/myisammrg/ha_myisammrg.h"
 #include "../storage/myisammrg/myrg_def.h"
 
+const uchar *query_state_map;
+
 #ifdef EMBEDDED_LIBRARY
 #include "emb_qcache.h"
 #endif
@@ -422,6 +425,136 @@
 };
 
 
+/*
+  Check if character is a white space.
+*/
+
+inline bool is_white_space(char c)
+{
+  return (query_state_map[(uint) ((uchar) c)] == MY_LEX_SKIP);
+}
+
+
+/**
+  Generate a query_string without query comments or duplicated space
+
+  @param new_query	    New query without 'fluff' is stored here
+  @param query		    Original query
+  @param query_length	    Length of original query
+  @param additional_length  Extra space for query cache we need to allocate
+  			    in new_query buffer.
+
+  Note:
+    If there is no space to allocate new_query, we will put original query
+    into new_query.
+*/
+
+static void make_base_query(String *new_query,
+                            const char *query, size_t query_length,
+                            size_t additional_length)
+{
+  char *buffer;
+  const char *query_end, *last_space;
+
+  /* The following is guaranteed by the query_cache interface */
+  DBUG_ASSERT(query[query_length] == 0);
+  DBUG_ASSERT(!is_white_space(query[0]));
+
+  if (new_query->realloc(query_length + additional_length))
+  {
+    /*
+      We could not allocate the query.  Use original query for
+      the query cache;  Better than nothing....
+    */
+    new_query->set(query, query_length, system_charset_info);
+    return;
+  }
+
+  buffer= (char*) new_query->ptr();             // Store base query here
+  query_end= query + query_length;
+  last_space= 0;                                // No space found yet
+
+  while (query < query_end)
+  {
+    char current = *(query++);
+    switch (current) {
+    case '\'':
+    case '`':
+    case '"':
+      *(buffer++)= current;                     // copy first quote
+      while (query < query_end)
+      {
+        *(buffer++)= *query;
+        if (*(query++) == current)              // found pair quote
+          break;
+      }
+      continue;                                 // Continue with next symbol
+    case '/':                                   // Start of comment ?
+      /*
+        Comment of format /#!number #/, must be skipped.
+        These may include '"' and other comments, but it should
+        be safe to parse the content as a normal string.
+      */
+      if (query[0] != '*' || query[1] == '!')
+        break;
+
+      query++;                               // skip "/"
+      while (++query < query_end)
+      {
+        if (query[0] == '*' && query[1] == '/')
+        {
+          query+= 2;
+          goto insert_space;
+        }
+      }
+      continue;                                 // Will end outer loop
+    case '-':
+      if (*query != '-' || !is_white_space(query[1])) // Not a comment
+        break;
+      query++;                 // skip second "-", and go to search of "\n"
+      /* fall through */
+    case '#':
+      while (query < query_end)
+      {
+        if (*(query++) == '\n')
+          goto insert_space;
+      }
+      continue;                                 // Will end outer loop
+    default:
+      if (is_white_space(current))
+        goto insert_space;
+      break;
+    }
+    *(buffer++)= current;
+    continue;
+
+insert_space:
+    if (buffer != last_space)
+    {
+      *(buffer++)= ' ';
+      last_space= buffer;
+    }
+  }
+  if (buffer == last_space)
+    buffer--;                                   // Remove the last space
+  *buffer= 0;
+  new_query->length((size_t) (buffer - new_query->ptr()));
+}
+
+
+/**
+  Check and change local variable if global one is switched
+
+  @param thd             thread handle
+*/
+
+void inline fix_local_query_cache_mode(THD *thd)
+{
+  if (global_system_variables.query_cache_type == 0)
+    thd->variables.query_cache_type= 0;
+}
+
+
 /**
   Serialize access to the query cache.
   If the lock cannot be granted the thread hangs in a conditional wait which
@@ -431,30 +564,41 @@
   effect by another thread. This enables a quick path in execution to skip waits
   when the outcome is known.
 
-  @param use_timeout TRUE if the lock can abort because of a timeout.
+  @param mode TIMEOUT the lock can abort because of a timeout
+              TRY the lock can abort because it is locked now
+              WAIT wait for lock (default)
 
-  @note use_timeout is optional and default value is FALSE.
+  @note mode is optional and default value is WAIT.
 
   @return
    @retval FALSE An exclusive lock was taken
    @retval TRUE The locking attempt failed
 */
 
-bool Query_cache::try_lock(bool use_timeout)
+bool Query_cache::try_lock(THD *thd, Cache_try_lock_mode mode)
 {
   bool interrupt= FALSE;
+  const char* old_proc_info;
   DBUG_ENTER("Query_cache::try_lock");
+  if (!thd)
+    thd = current_thd;
+
+  old_proc_info= thd->proc_info;
+  thd_proc_info(thd,"Waiting on query cache mutex");
 
   pthread_mutex_lock(&structure_guard_mutex);
+  DBUG_EXECUTE_IF("status_wait_query_cache_mutex_sleep", {
+      sleep(5);
+    });
+  m_requests_in_progress++;
+  fix_local_query_cache_mode(thd);
   while (1)
   {
     if (m_cache_lock_status == Query_cache::UNLOCKED)
     {
       m_cache_lock_status= Query_cache::LOCKED;
 #ifndef DBUG_OFF
-      THD *thd= current_thd;
-      if (thd)
-        m_cache_lock_thread_id= thd->thread_id;
+      m_cache_lock_thread_id= thd->thread_id;
 #endif
       break;
     }
@@ -474,7 +618,11 @@
         To prevent send_result_to_client() and query_cache_insert() from
         blocking execution for too long a timeout is put on the lock.
       */
-      if (use_timeout)
+      if (mode == WAIT)
+      {
+        pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
+      }
+      else if (mode == TIMEOUT)
       {
         struct timespec waittime;
         set_timespec_nsec(waittime,(ulong)(50000000L));  /* Wait for 50 msec */
@@ -488,11 +636,16 @@
       }
       else
       {
-        pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
+        DBUG_ASSERT(mode == TRY);
+        interrupt= TRUE;
+        break;
       }
     }
   }
+  if (interrupt)
+    m_requests_in_progress--;
   pthread_mutex_unlock(&structure_guard_mutex);
+  thd->proc_info = old_proc_info;
 
   DBUG_RETURN(interrupt);
 }
@@ -514,10 +667,12 @@
   DBUG_ENTER("Query_cache::lock_and_suspend");
 
   pthread_mutex_lock(&structure_guard_mutex);
+  m_requests_in_progress++;
   while (m_cache_lock_status != Query_cache::UNLOCKED)
     pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
   m_cache_lock_status= Query_cache::LOCKED_NO_WAIT;
 #ifndef DBUG_OFF
+  /* Here thd may not be set during shutdown */
   THD *thd= current_thd;
   if (thd)
     m_cache_lock_thread_id= thd->thread_id;
@@ -537,18 +692,18 @@
   It is used by all methods which invalidates one or more tables.
  */
 
-void Query_cache::lock(void)
+void Query_cache::lock(THD *thd)
 {
   DBUG_ENTER("Query_cache::lock");
 
   pthread_mutex_lock(&structure_guard_mutex);
+  m_requests_in_progress++;
+  fix_local_query_cache_mode(thd);
   while (m_cache_lock_status != Query_cache::UNLOCKED)
     pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
   m_cache_lock_status= Query_cache::LOCKED;
 #ifndef DBUG_OFF
-  THD *thd= current_thd;
-  if (thd)
-    m_cache_lock_thread_id= thd->thread_id;
+  m_cache_lock_thread_id= thd->thread_id;
 #endif
   pthread_mutex_unlock(&structure_guard_mutex);
 
@@ -565,6 +720,7 @@
   DBUG_ENTER("Query_cache::unlock");
   pthread_mutex_lock(&structure_guard_mutex);
 #ifndef DBUG_OFF
+  /* Thd may not be set in resize() at mysqld start */
   THD *thd= current_thd;
   if (thd)
     DBUG_ASSERT(m_cache_lock_thread_id == thd->thread_id);
@@ -574,6 +730,14 @@
   m_cache_lock_status= Query_cache::UNLOCKED;
   DBUG_PRINT("Query_cache",("Sending signal"));
   pthread_cond_signal(&COND_cache_status_changed);
+  DBUG_ASSERT(m_requests_in_progress > 0);
+  m_requests_in_progress--;
+  if (m_requests_in_progress == 0 && m_cache_status == DISABLE_REQUEST)
+  {
+    /* No clients => just free query cache */
+    free_cache();
+    m_cache_status= DISABLED;
+  }
   pthread_mutex_unlock(&structure_guard_mutex);
   DBUG_VOID_RETURN;
 }
@@ -590,25 +754,24 @@
    @retval FALSE No directive found.
 */
  
-static bool has_no_cache_directive(char *sql)
+static bool has_no_cache_directive(const char *sql)
 {
-  int i=0;
-  while (sql[i] == ' ')
-    ++i;
+  while (is_white_space(*sql))
+    sql++;
     
-  if (my_toupper(system_charset_info, sql[i])    == 'S' &&
-      my_toupper(system_charset_info, sql[i+1])  == 'Q' &&
-      my_toupper(system_charset_info, sql[i+2])  == 'L' &&
-      my_toupper(system_charset_info, sql[i+3])  == '_' &&
-      my_toupper(system_charset_info, sql[i+4])  == 'N' &&
-      my_toupper(system_charset_info, sql[i+5])  == 'O' &&
-      my_toupper(system_charset_info, sql[i+6])  == '_' &&
-      my_toupper(system_charset_info, sql[i+7])  == 'C' &&
-      my_toupper(system_charset_info, sql[i+8])  == 'A' &&
-      my_toupper(system_charset_info, sql[i+9])  == 'C' &&
-      my_toupper(system_charset_info, sql[i+10]) == 'H' &&
-      my_toupper(system_charset_info, sql[i+11]) == 'E' &&
-      my_toupper(system_charset_info, sql[i+12]) == ' ')
+  if (my_toupper(system_charset_info, sql[0])  == 'S' &&
+      my_toupper(system_charset_info, sql[1])  == 'Q' &&
+      my_toupper(system_charset_info, sql[2])  == 'L' &&
+      my_toupper(system_charset_info, sql[3])  == '_' &&
+      my_toupper(system_charset_info, sql[4])  == 'N' &&
+      my_toupper(system_charset_info, sql[5])  == 'O' &&
+      my_toupper(system_charset_info, sql[6])  == '_' &&
+      my_toupper(system_charset_info, sql[7])  == 'C' &&
+      my_toupper(system_charset_info, sql[8])  == 'A' &&
+      my_toupper(system_charset_info, sql[9])  == 'C' &&
+      my_toupper(system_charset_info, sql[10]) == 'H' &&
+      my_toupper(system_charset_info, sql[11]) == 'E' &&
+      my_isspace(system_charset_info, sql[12]))
     return TRUE;
   
   return FALSE;       
@@ -866,14 +1029,18 @@
 {
   DBUG_ENTER("query_cache_insert");
 
-  /* See the comment on double-check locking usage above. */
   if (net->query_cache_query == 0)
     DBUG_VOID_RETURN;
 
+  DBUG_ASSERT(current_thd);
+
   DBUG_EXECUTE_IF("wait_in_query_cache_insert",
                   debug_wait_for_kill("wait_in_query_cache_insert"); );
 
-  if (query_cache.try_lock())
+  if(query_cache.is_disabled())
+    DBUG_VOID_RETURN;
+
+  if (query_cache.try_lock(NULL, Query_cache::WAIT))
     DBUG_VOID_RETURN;
 
   Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query;
@@ -931,7 +1098,10 @@
   if (net->query_cache_query == 0)
     DBUG_VOID_RETURN;
 
-  if (query_cache.try_lock())
+  if(query_cache.is_disabled())
+    DBUG_VOID_RETURN;
+
+  if (query_cache.try_lock(NULL, Query_cache::WAIT))
   {
     net->query_cache_query = 0;
     DBUG_VOID_RETURN;
@@ -984,7 +1154,7 @@
                      emb_count_querycache_size(thd));
 #endif
 
-  if (query_cache.try_lock())
+  if (query_cache.try_lock(thd, Query_cache::WAIT))
   {
     thd->net.query_cache_query= 0;
     DBUG_VOID_RETURN;
@@ -1073,6 +1243,7 @@
    query_cache_limit(query_cache_limit_arg),
    queries_in_cache(0), hits(0), inserts(0), refused(0),
    total_blocks(0), lowmem_prunes(0),
+   m_cache_status(OK),
    min_allocation_unit(ALIGN_SIZE(min_allocation_unit_arg)),
    min_result_data_size(ALIGN_SIZE(min_result_data_size_arg)),
    def_query_hash_size(ALIGN_SIZE(def_query_hash_size_arg)),
@@ -1096,6 +1267,12 @@
 			query_cache_size_arg));
   DBUG_ASSERT(initialized);
 
+  if (global_system_variables.query_cache_type == 0)
+  {
+    my_error(ER_QUERY_CACHE_IS_DISABLED, MYF(0));
+    DBUG_RETURN(0);
+  }
+
   lock_and_suspend();
 
   /*
@@ -1128,8 +1305,17 @@
   query_cache_size= query_cache_size_arg;
   new_query_cache_size= init_cache();
 
+  /*
+    m_cache_status is internal query cache switch so switching it on/off
+    will not be reflected on global_system_variables.query_cache_type
+  */
   if (new_query_cache_size)
+  {
     DBUG_EXECUTE("check_querycache",check_integrity(1););
+    m_cache_status= OK;                         // size > 0 => enable cache
+  }
+  else
+    m_cache_status= DISABLED;                   // size 0 means the cache disabled
 
   unlock();
   DBUG_RETURN(new_query_cache_size);
@@ -1148,6 +1334,9 @@
 {
   TABLE_COUNTER_TYPE local_tables;
   ulong tot_length;
+  const char *query;
+  size_t query_length;
+  uint8 tables_type;
   DBUG_ENTER("Query_cache::store_query");
   /*
     Testing 'query_cache_size' without a lock here is safe: the thing
@@ -1157,12 +1346,23 @@
 
     See also a note on double-check locking usage above.
   */
-  if (thd->locked_tables || query_cache_size == 0)
-    DBUG_VOID_RETURN;
-  uint8 tables_type= 0;
-
-  if ((local_tables= is_cacheable(thd, thd->query_length(),
-				  thd->query(), thd->lex, tables_used,
+  if (!thd->query_cache_is_applicable || query_cache_size == 0)
+  {
+    DBUG_PRINT("qcache", ("Query cache not ready"));
+    DBUG_VOID_RETURN;
+  }
+  if (thd->lex->sql_command != SQLCOM_SELECT)
+  {
+    DBUG_PRINT("qcache", ("Ignoring not SELECT command"));
+    DBUG_VOID_RETURN;
+  }
+
+  /* The following assert fails if we haven't called send_result_to_client */
+  DBUG_ASSERT(thd->base_query.is_alloced() ||
+              thd->base_query.ptr() == thd->query());
+
+  tables_type= 0;
+  if ((local_tables= is_cacheable(thd, thd->lex, tables_used,
 				  &tables_type)))
   {
     NET *net= &thd->net;
@@ -1239,7 +1439,7 @@
       The 'TRUE' parameter indicate that the lock is allowed to timeout
 
     */
-    if (try_lock(TRUE))
+    if (try_lock(thd, Query_cache::WAIT))
       DBUG_VOID_RETURN;
     if (query_cache_size == 0)
     {
@@ -1255,11 +1455,13 @@
       DBUG_VOID_RETURN;
     }
 
+    query=        thd->base_query.ptr();
+    query_length= thd->base_query.length();
+
     /* Key is query + database + flag */
     if (thd->db_length)
     {
-      memcpy(thd->query() + thd->query_length() + 1, thd->db, 
-        thd->db_length);
+      memcpy((char*) (query + query_length + 1), thd->db, thd->db_length);
       DBUG_PRINT("qcache", ("database: %s  length: %u",
 			    thd->db, (unsigned) thd->db_length)); 
     }
@@ -1267,24 +1469,24 @@
     {
       DBUG_PRINT("qcache", ("No active database"));
     }
-    tot_length= thd->query_length() + thd->db_length + 1 +
+    tot_length= query_length + thd->db_length + 1 +
       QUERY_CACHE_FLAGS_SIZE;
     /*
       We should only copy structure (don't use it location directly)
       because of alignment issue
     */
-    memcpy((void*) (thd->query() + (tot_length - QUERY_CACHE_FLAGS_SIZE)),
+    memcpy((void*) (query + (tot_length - QUERY_CACHE_FLAGS_SIZE)),
 	   &flags, QUERY_CACHE_FLAGS_SIZE);
 
     /* Check if another thread is processing the same query? */
     Query_cache_block *competitor = (Query_cache_block *)
-      hash_search(&queries, (uchar*) thd->query(), tot_length);
+      hash_search(&queries, (uchar*) query, tot_length);
     DBUG_PRINT("qcache", ("competitor 0x%lx", (ulong) competitor));
     if (competitor == 0)
     {
       /* Query is not in cache and no one is working with it; Store it */
       Query_cache_block *query_block;
-      query_block= write_block_data(tot_length, (uchar*) thd->query(),
+      query_block= write_block_data(tot_length, (uchar*) query,
 				    ALIGN_SIZE(sizeof(Query_cache_query)),
 				    Query_cache_block::QUERY, local_tables);
       if (query_block != 0)
@@ -1341,7 +1543,7 @@
       DBUG_PRINT("qcache", ("Another thread process same query"));
     }
   }
-  else if (thd->lex->sql_command == SQLCOM_SELECT)
+  else
     statistic_increment(refused, &structure_guard_mutex);
 
 end:
@@ -1416,7 +1618,7 @@
 */
 
 int
-Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
+Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length)
 {
   ulonglong engine_data;
   Query_cache_query *query;
@@ -1427,6 +1629,7 @@
   Query_cache_block_table *block_table, *block_table_end;
   ulong tot_length;
   Query_cache_query_flags flags;
+  const char *sql, *sql_end;
   DBUG_ENTER("Query_cache::send_result_to_client");
 
   /*
@@ -1436,51 +1639,96 @@
 
     See also a note on double-check locking usage above.
   */
-  if (thd->locked_tables || thd->variables.query_cache_type == 0 ||
-      query_cache_size == 0)
-    goto err;
-
-  if (!thd->lex->safe_to_cache_query)
-  {
-    DBUG_PRINT("qcache", ("SELECT is non-cacheable"));
-    goto err;
-  }
-
-  {
-    uint i= 0;
-    /*
-      Skip '(' characters in queries like following:
-      (select a from t1) union (select a from t1);
-    */
-    while (sql[i]=='(')
-      i++;
-
-    /*
-      Test if the query is a SELECT
-      (pre-space is removed in dispatch_command).
-
-      First '/' looks like comment before command it is not
-      frequently appeared in real life, consequently we can
-      check all such queries, too.
-    */
-    if ((my_toupper(system_charset_info, sql[i])     != 'S' ||
-         my_toupper(system_charset_info, sql[i + 1]) != 'E' ||
-         my_toupper(system_charset_info, sql[i + 2]) != 'L') &&
-        sql[i] != '/')
-    {
-      DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached"));
-      goto err;
-    }
-    
-    if (query_length > 20 && has_no_cache_directive(&sql[i+6]))
-    {
-      /*
-        We do not increase 'refused' statistics here since it will be done
-        later when the query is parsed.
-      */
-      DBUG_PRINT("qcache", ("The statement has a SQL_NO_CACHE directive"));
-      goto err;
-    }
+  if (is_disabled() || thd->locked_tables ||
+      thd->variables.query_cache_type == 0)
+    goto err;
+  DBUG_ASSERT(query_cache_size != 0);           // otherwise cache would be disabled
+
+  thd->query_cache_is_applicable= 1;
+  sql= org_sql; sql_end= sql + query_length;
+
+  /*
+    Skip all comments at start of query. The following tests is false for
+    all normal queries.
+  */
+  if (!my_isalpha(system_charset_info, *sql))
+  {
+    while (sql < sql_end)
+    {
+      char current= *sql;
+      switch (current) {
+      case '/':
+        if (sql[1] != '*')
+          break;
+        sql+= 2;                              // Skip '/*'
+        if (*sql == '!')
+        {
+          /*
+            Found / *!number comment; Skip number to see if sql
+            starts with 'select'
+          */
+          sql++;
+          while (my_isdigit(system_charset_info, *sql))
+            sql++;
+        }
+        else
+        {
+          while (sql++ < sql_end)
+          {
+            if (sql[-1] == '*' && *sql == '/')
+            {
+              sql++;
+              break;
+            }
+          }
+        }
+        continue;
+      case '-':
+        if (sql[1] != '-' || !is_white_space(sql[2])) // Not a comment
+          break;
+        sql++;                               // Skip first '-'
+        /* Fall through */
+      case '#':
+        while (++sql < sql_end)
+        {
+          if (*sql == '\n')
+          {
+            sql++;                            // Skip '\n'
+            break;
+          }
+        }
+        /* Continue with analyzing current symbol */
+        continue;
+      case '\r':
+      case '\n':
+      case '\t':
+      case ' ':
+      case '(':    // To handle (select a from t1) union (select a from t1);
+        sql++;
+        continue;
+      default:
+        break;
+      }
+      /* We only come here when we found the first word of the sql */
+      break;
+    }
+  }
+  if ((my_toupper(system_charset_info, sql[0]) != 'S' ||
+       my_toupper(system_charset_info, sql[1]) != 'E' ||
+       my_toupper(system_charset_info, sql[2]) != 'L'))
+  {
+    DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached"));
+    goto err;
+  }
+
+  if ((sql_end - sql) > 20 && has_no_cache_directive(sql+6))
+  {
+    /*
+      We do not increase 'refused' statistics here since it will be done
+      later when the query is parsed.
+    */
+    DBUG_PRINT("qcache", ("The statement has a SQL_NO_CACHE directive"));
+    goto err;
   }
 
   /*
@@ -1488,9 +1736,9 @@
     disabled or if a full cache flush is in progress, the attempt to
     get the lock is aborted.
 
-    The 'TRUE' parameter indicate that the lock is allowed to timeout
+    The WAIT parameter indicate that the lock is allowed to timeout.
   */
-  if (try_lock(TRUE))
+  if (try_lock(thd, Query_cache::WAIT))
     goto err;
 
   if (query_cache_size == 0)
@@ -1503,11 +1751,23 @@
   DBUG_ASSERT(thd->net.query_cache_query == 0);
 
   Query_cache_block *query_block;
+  if (opt_query_cache_strip_comments)
+  {
+    make_base_query(&thd->base_query, sql, (size_t) (sql_end - sql),
+                    thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE);
+    sql=          thd->base_query.ptr();
+    query_length= thd->base_query.length();
+  }
+  else
+  {
+    sql= org_sql;
+    thd->base_query.set(sql, query_length, system_charset_info);
+  }
 
   tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
   if (thd->db_length)
   {
-    memcpy(sql+query_length+1, thd->db, thd->db_length);
+    memcpy((char*) (sql+query_length+1), thd->db, thd->db_length);
     DBUG_PRINT("qcache", ("database: '%s'  length: %u",
 			  thd->db, (unsigned)thd->db_length));
   }
@@ -1638,7 +1898,7 @@
           temporary tables => assign following variable to make check
           faster.
         */
-        thd->lex->safe_to_cache_query=0;
+        thd->query_cache_is_applicable= 0;      // Query can't be cached
         BLOCK_UNLOCK_RD(query_block);
         DBUG_RETURN(-1);
       }
@@ -1654,7 +1914,7 @@
 		 ("probably no SELECT access to %s.%s =>  return to normal processing",
 		  table_list.db, table_list.alias));
       unlock();
-      thd->lex->safe_to_cache_query=0;		// Don't try to cache this
+      thd->query_cache_is_applicable= 0;        // Query can't be cached
       BLOCK_UNLOCK_RD(query_block);
       DBUG_RETURN(-1);				// Privilege error
     }
@@ -1663,7 +1923,7 @@
       DBUG_PRINT("qcache", ("Need to check column privileges for %s.%s",
 			    table_list.db, table_list.alias));
       BLOCK_UNLOCK_RD(query_block);
-      thd->lex->safe_to_cache_query= 0;		// Don't try to cache this
+      thd->query_cache_is_applicable= 0;        // Query can't be cached
       goto err_unlock;				// Parse query
     }
 #endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
@@ -1687,7 +1947,7 @@
                                   table->key_length());
       }
       else
-        thd->lex->safe_to_cache_query= 0;       // Don't try to cache this
+        thd->query_cache_is_applicable= 0;      // Query can't be cached
       goto err_unlock;				// Parse query
     }
     else
@@ -1738,12 +1998,15 @@
 
 err_unlock:
   unlock();
-err:
   /*
     query_plan_flags doesn't have to be changed here as it contains
     QPLAN_QC_NO by default
   */
   DBUG_RETURN(0);				// Query was not cached
+
+err:
+  thd->query_cache_is_applicable= 0;            // Query can't be cached
+  DBUG_RETURN(0);				// Query was not cached
 }
 
 
@@ -1755,6 +2018,8 @@
 			     my_bool using_transactions)
 {
   DBUG_ENTER("Query_cache::invalidate (table list)");
+  if (is_disabled())
+    DBUG_VOID_RETURN;
 
   using_transactions= using_transactions &&
     (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
@@ -1782,10 +2047,12 @@
   DBUG_VOID_RETURN;
 }
 
-void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
+void Query_cache::invalidate(THD *thd, CHANGED_TABLE_LIST *tables_used)
 {
   DBUG_ENTER("Query_cache::invalidate (changed table list)");
-  THD *thd= current_thd;
+  if (is_disabled())
+    DBUG_VOID_RETURN;
+
   for (; tables_used; tables_used= tables_used->next)
   {
     thd_proc_info(thd, "invalidating query cache entries (table list)");
@@ -1808,10 +2075,13 @@
   NOTE
     can be used only for opened tables
 */
-void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used)
+void Query_cache::invalidate_locked_for_write(THD *thd,
+                                              TABLE_LIST *tables_used)
 {
-  THD *thd= current_thd;
   DBUG_ENTER("Query_cache::invalidate_locked_for_write");
+  if (is_disabled())
+    DBUG_VOID_RETURN;
+
   for (; tables_used; tables_used= tables_used->next_local)
   {
     thd_proc_info(thd, "invalidating query cache entries (table)");
@@ -1832,7 +2102,9 @@
 			     my_bool using_transactions)
 {
   DBUG_ENTER("Query_cache::invalidate (table)");
-  
+  if (is_disabled())
+    DBUG_VOID_RETURN;
+
   using_transactions= using_transactions &&
     (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
   if (using_transactions && 
@@ -1849,6 +2121,8 @@
 			     my_bool using_transactions)
 {
   DBUG_ENTER("Query_cache::invalidate (key)");
+  if (is_disabled())
+   DBUG_VOID_RETURN;
 
   using_transactions= using_transactions &&
     (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN));
@@ -1865,18 +2139,19 @@
    Remove all cached queries that uses the given database.
 */
 
-void Query_cache::invalidate(char *db)
+void Query_cache::invalidate(THD *thd, char *db)
 {
-  bool restart= FALSE;
+  bool restart;
   DBUG_ENTER("Query_cache::invalidate (db)");
+  if (is_disabled())
+    DBUG_VOID_RETURN;
 
+  restart= FALSE;
   /*
     Lock the query cache and queue all invalidation attempts to avoid
     the risk of a race between invalidation, cache inserts and flushes.
   */
-  lock();
-
-  THD *thd= current_thd;
+  lock(thd);
 
   if (query_cache_size > 0)
   {
@@ -1954,6 +2229,9 @@
 void Query_cache::flush()
 {
   DBUG_ENTER("Query_cache::flush");
+  if (is_disabled())
+    DBUG_VOID_RETURN;
+
   DBUG_EXECUTE_IF("wait_in_query_cache_flush1",
                   debug_wait_for_kill("wait_in_query_cache_flush1"););
 
@@ -1985,11 +2263,14 @@
 {
   DBUG_ENTER("Query_cache::pack");
 
+  if (is_disabled())
+    DBUG_VOID_RETURN;
+
   /*
     If the entire qc is being invalidated we can bail out early
     instead of waiting for the lock.
   */
-  if (try_lock())
+  if (try_lock(NULL, Query_cache::WAIT))
     DBUG_VOID_RETURN;
 
   if (query_cache_size == 0)
@@ -2026,11 +2307,25 @@
     pthread_cond_destroy(&COND_cache_status_changed);
     pthread_mutex_destroy(&structure_guard_mutex);
     initialized = 0;
+    DBUG_ASSERT(m_requests_in_progress == 0);
   }
   DBUG_VOID_RETURN;
 }
 
 
+void Query_cache::disable_query_cache(void)
+{
+  m_cache_status= DISABLE_REQUEST;
+  /*
+    If there is no requests in progress try to free buffer.
+    try_lock(TRY) will exit immediately if there is lock.
+    unlock() should free block.
+  */
+  if (m_requests_in_progress == 0 && !try_lock(NULL, TRY))
+    unlock();
+}
+
+
 /*****************************************************************************
   init/destroy
 *****************************************************************************/
@@ -2041,7 +2336,19 @@
   pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST);
   pthread_cond_init(&COND_cache_status_changed, NULL);
   m_cache_lock_status= Query_cache::UNLOCKED;
+  m_cache_status= Query_cache::OK;
+  m_requests_in_progress= 0;
   initialized = 1;
+  query_state_map= default_charset_info->state_map;
+  /*
+    If we explicitly turn off query cache from the command line query cache will
+    be disabled for the reminder of the server life time. This is because we
+    want to avoid locking the QC specific mutex if query cache isn't going to
+    be used.
+  */
+  if (global_system_variables.query_cache_type == 0)
+    query_cache.disable_query_cache();
+
   DBUG_VOID_RETURN;
 }
 
@@ -2735,7 +3042,7 @@
     Lock the query cache and queue all invalidation attempts to avoid
     the risk of a race between invalidation, cache inserts and flushes.
   */
-  lock();
+  lock(thd);
 
   DBUG_EXECUTE_IF("wait_in_query_cache_invalidate2",
                   debug_wait_for_kill("wait_in_query_cache_invalidate2"); );
@@ -3516,7 +3823,7 @@
     {
       DBUG_PRINT("qcache", ("Don't cache statement as it refers to "
                             "tables with column privileges."));
-      thd->lex->safe_to_cache_query= 0;
+      thd->query_cache_is_applicable= 0;        // Query can't be cached
       DBUG_RETURN(0);
     }
 #endif
@@ -3581,13 +3888,13 @@
 */
 
 TABLE_COUNTER_TYPE
-Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex,
+Query_cache::is_cacheable(THD *thd, LEX *lex,
                           TABLE_LIST *tables_used, uint8 *tables_type)
 {
   TABLE_COUNTER_TYPE table_count;
   DBUG_ENTER("Query_cache::is_cacheable");
 
-  if (query_cache_is_cacheable_query(lex) &&
+  if (thd->lex->safe_to_cache_query &&
       (thd->variables.query_cache_type == 1 ||
        (thd->variables.query_cache_type == 2 && (lex->select_lex.options &
 						 OPTION_TO_QUERY_CACHE))))
@@ -3652,7 +3959,7 @@
     {
       DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
 			    tables_used->db, tables_used->alias));
-      thd->lex->safe_to_cache_query= 0;          // Don't try to cache this
+      thd->query_cache_is_applicable= 0;        // Query can't be cached
       DBUG_RETURN(1);
     }
   }
@@ -4745,3 +5052,4 @@
 #endif /* DBUG_OFF */
 
 #endif /*HAVE_QUERY_CACHE*/
+

=== modified file 'sql/sql_cache.h'
--- sql/sql_cache.h	2010-09-23 22:00:32 +0000
+++ sql/sql_cache.h	2010-12-30 17:18:05 +0000
@@ -276,8 +276,11 @@
   my_thread_id m_cache_lock_thread_id;
 #endif
   pthread_cond_t COND_cache_status_changed;
+  uint m_requests_in_progress;
   enum Cache_lock_status { UNLOCKED, LOCKED_NO_WAIT, LOCKED };
   Cache_lock_status m_cache_lock_status;
+  enum Cache_staus {OK, DISABLE_REQUEST, DISABLED};
+  Cache_staus m_cache_status;
 
   void free_query_internal(Query_cache_block *point);
   void invalidate_table_internal(THD *thd, uchar *key, uint32 key_length);
@@ -292,7 +295,7 @@
       2. query block (for operation inside query (query block/results))
 
     Thread doing cache flush releases the mutex once it sets
-    m_cache_status flag, so other threads may bypass the cache as
+    m_cache_lock_status flag, so other threads may bypass the cache as
     if it is disabled, not waiting for reset to finish.  The exception
     is other threads that were going to do cache flush---they'll wait
     till the end of a flush operation.
@@ -407,7 +410,7 @@
     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,
                                   LEX *lex, TABLE_LIST *tables_used,
                                   uint8 *tables_type);
   TABLE_COUNTER_TYPE process_and_count_tables(THD *thd,
@@ -423,6 +426,10 @@
 	      uint def_query_hash_size = QUERY_CACHE_DEF_QUERY_HASH_SIZE,
 	      uint def_table_hash_size = QUERY_CACHE_DEF_TABLE_HASH_SIZE);
 
+  bool is_disabled(void) { return m_cache_status != OK; }
+  bool is_disable_in_progress(void)
+  { return m_cache_status == DISABLE_REQUEST; }
+
   /* initialize cache (mutex) */
   void init();
   /* resize query cache (return real query size, 0 if disabled) */
@@ -442,16 +449,16 @@
   int send_result_to_client(THD *thd, char *query, uint query_length);
 
   /* Remove all queries that uses any of the listed following tables */
-  void invalidate(THD* thd, TABLE_LIST *tables_used,
+  void invalidate(THD *thd, TABLE_LIST *tables_used,
 		  my_bool using_transactions);
-  void invalidate(CHANGED_TABLE_LIST *tables_used);
-  void invalidate_locked_for_write(TABLE_LIST *tables_used);
-  void invalidate(THD* thd, TABLE *table, my_bool using_transactions);
+  void invalidate(THD *thd, CHANGED_TABLE_LIST *tables_used);
+  void invalidate_locked_for_write(THD *thd, TABLE_LIST *tables_used);
+  void invalidate(THD *thd, TABLE *table, my_bool using_transactions);
   void invalidate(THD *thd, const char *key, uint32  key_length,
 		  my_bool using_transactions);
 
   /* Remove all queries that uses any of the tables in following database */
-  void invalidate(char *db);
+  void invalidate(THD *thd, char *db);
 
   /* Remove all queries that uses any of the listed following table */
   void invalidate_by_MyISAM_filename(const char *filename);
@@ -485,10 +492,13 @@
 			const char *name);
   my_bool in_blocks(Query_cache_block * point);
 
-  bool try_lock(bool use_timeout= FALSE);
-  void lock(void);
+  enum Cache_try_lock_mode {WAIT, TIMEOUT, TRY};
+  bool try_lock(THD *thd, Cache_try_lock_mode mode= WAIT);
+  void lock(THD *thd);
   void lock_and_suspend(void);
   void unlock(void);
+
+  void disable_query_cache(void);
 };
 
 extern Query_cache query_cache;

=== modified file 'sql/sql_class.h'
--- sql/sql_class.h	2010-11-30 21:11:03 +0000
+++ sql/sql_class.h	2010-12-29 10:45:11 +0000
@@ -675,8 +675,12 @@
     ENGINE INNODB STATUS.
   */
   LEX_STRING query_string;
+  /*
+    If opt_query_cache_strip_comments is set, this contains query without
+    comments. If not set, it contains pointer to query_string.
+  */
+  String base_query;
   Server_side_cursor *cursor;
-
   inline char *query() { return query_string.str; }
   inline uint32 query_length() { return query_string.length; }
   void set_query_inner(char *query_arg, uint32 query_length_arg);
@@ -697,7 +701,8 @@
   char *db;
   size_t db_length;
 
-public:
+  /* This is set to 1 of last call to send_result_to_client() was ok */
+  my_bool query_cache_is_applicable;
 
   /* This constructor is called for backup statements */
   Statement() {}

=== modified file 'sql/sql_db.cc'
--- sql/sql_db.cc	2010-11-24 22:57:34 +0000
+++ sql/sql_db.cc	2010-12-28 22:19:49 +0000
@@ -975,7 +975,7 @@
     {
       ha_drop_database(path);
       tmp_disable_binlog(thd);
-      query_cache_invalidate1(db);
+      query_cache_invalidate1(thd, db);
       (void) sp_drop_db_routines(thd, db); /* @todo Do not ignore errors */
 #ifdef HAVE_EVENT_SCHEDULER
       Events::drop_schema_events(thd, db);

=== modified file 'sql/sql_lex.cc'
--- sql/sql_lex.cc	2010-11-30 21:11:03 +0000
+++ sql/sql_lex.cc	2010-12-28 22:19:49 +0000
@@ -818,45 +818,48 @@
 	yylval->lex_str.length=2;
 	return NULL_SYM;
       }
+      /* Fall through */
     case MY_LEX_CHAR:			// Unknown or single char token
     case MY_LEX_SKIP:			// This should not happen
-      if (c == '-' && lip->yyPeek() == '-' &&
+      if (c != ')')
+	lip->next_state= MY_LEX_START;	// Allow signed numbers
+      return((int) c);
+
+    case MY_LEX_MINUS_OR_COMMENT:
+      if (lip->yyPeek() == '-' &&
           (my_isspace(cs,lip->yyPeekn(1)) ||
            my_iscntrl(cs,lip->yyPeekn(1))))
       {
         state=MY_LEX_COMMENT;
         break;
       }
-
-      if (c != ')')
-	lip->next_state= MY_LEX_START;	// Allow signed numbers
-
-      if (c == ',')
-      {
-        /*
-          Warning:
-          This is a work around, to make the "remember_name" rule in
-          sql/sql_yacc.yy work properly.
-          The problem is that, when parsing "select expr1, expr2",
-          the code generated by bison executes the *pre* action
-          remember_name (see select_item) *before* actually parsing the
-          first token of expr2.
-        */
-        lip->restart_token();
-      }
-      else
-      {
-        /*
-          Check for a placeholder: it should not precede a possible identifier
-          because of binlogging: when a placeholder is replaced with
-          its value in a query for the binlog, the query must stay
-          grammatically correct.
-        */
-        if (c == '?' && lip->stmt_prepare_mode &&
-            !ident_map[(uchar) lip->yyPeek()])
+      lip->next_state= MY_LEX_START;	// Allow signed numbers
+      return((int) c);
+
+    case MY_LEX_PLACEHOLDER:
+      /*
+        Check for a placeholder: it should not precede a possible identifier
+        because of binlogging: when a placeholder is replaced with
+        its value in a query for the binlog, the query must stay
+        grammatically correct.
+      */
+      lip->next_state= MY_LEX_START;	// Allow signed numbers
+      if (lip->stmt_prepare_mode && !ident_map[(uchar) lip->yyPeek()])
         return(PARAM_MARKER);
-      }
+      return((int) c);
 
+    case MY_LEX_COMMA:
+      lip->next_state= MY_LEX_START;	// Allow signed numbers
+      /*
+        Warning:
+        This is a work around, to make the "remember_name" rule in
+        sql/sql_yacc.yy work properly.
+        The problem is that, when parsing "select expr1, expr2",
+        the code generated by bison executes the *pre* action
+        remember_name (see select_item) *before* actually parsing the
+        first token of expr2.
+      */
+      lip->restart_token();
       return((int) c);
 
     case MY_LEX_IDENT_OR_NCHAR:

=== modified file 'sql/sql_parse.cc'
--- sql/sql_parse.cc	2010-11-30 21:11:03 +0000
+++ sql/sql_parse.cc	2010-12-28 22:19:49 +0000
@@ -1050,7 +1050,6 @@
   }
 
   thread_running++;
-  /* TODO: set thd->lex->sql_command to SQLCOM_END here */
   VOID(pthread_mutex_unlock(&LOCK_thread_count));
 
   /**
@@ -1242,15 +1241,15 @@
 #endif
 
       thd->set_query(beginning_of_next_stmt, length);
-      VOID(pthread_mutex_lock(&LOCK_thread_count));
       /*
         Count each statement from the client.
       */
       statistic_increment(thd->status_var.questions, &LOCK_status);
+      thd->set_time(); /* Reset the query start time for next query. */
+      VOID(pthread_mutex_lock(&LOCK_thread_count));
       thd->query_id= next_query_id();
-      thd->set_time(); /* Reset the query start time. */
+      VOID(pthread_mutex_unlock(&LOCK_thread_count));
       /* TODO: set thd->lex->sql_command to SQLCOM_END here */
-      VOID(pthread_mutex_unlock(&LOCK_thread_count));
       mysql_parse(thd, beginning_of_next_stmt, length, &end_of_stmt);
     }
 
@@ -3581,7 +3580,7 @@
     {
 #ifdef HAVE_QUERY_CACHE
       if (thd->variables.query_cache_wlock_invalidate)
-	query_cache.invalidate_locked_for_write(first_table);
+	query_cache.invalidate_locked_for_write(thd, first_table);
 #endif /*HAVE_QUERY_CACHE*/
       thd->locked_tables=thd->lock;
       thd->lock=0;

=== modified file 'mysql-test/r/query_cache.result'
--- mysql-test/r/query_cache.result	2010-01-11 13:15:28 +0000
+++ mysql-test/r/query_cache.result	2011-01-04 18:38:08 +0000
@@ -1639,6 +1639,7 @@
 set GLOBAL query_cache_limit=default;
 set GLOBAL query_cache_min_res_unit=default;
 set GLOBAL query_cache_size=default;
+set local query_cache_type=default;
 FLUSH STATUS;
 SET GLOBAL query_cache_size=10*1024*1024;
 SET @save_concurrent_insert= @@concurrent_insert;
@@ -1823,3 +1824,33 @@
 DROP TABLE t1;
 SET GLOBAL query_cache_size= default;
 End of 5.1 tests
+New query cache switching OFF mechanism test
+set global query_cache_size=1024*1024*20;
+set global query_cache_type=on;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+@@query_cache_size	@@global.query_cache_type	@@local.query_cache_type
+20971520	ON	ON
+set global query_cache_size=0;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+@@query_cache_size	@@global.query_cache_type	@@local.query_cache_type
+0	ON	ON
+set global query_cache_size=1024*1024*20;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+@@query_cache_size	@@global.query_cache_type	@@local.query_cache_type
+20971520	ON	ON
+set global query_cache_type=off;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+@@query_cache_size	@@global.query_cache_type	@@local.query_cache_type
+20971520	OFF	OFF
+set global query_cache_type=on;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+@@query_cache_size	@@global.query_cache_type	@@local.query_cache_type
+20971520	ON	OFF
+set local query_cache_type= on;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+@@query_cache_size	@@global.query_cache_type	@@local.query_cache_type
+20971520	ON	ON
+restore defaults
+SET GLOBAL query_cache_type= default;
+SET GLOBAL query_cache_size= default;
+SET LOCAL query_cache_type= default;

=== modified file 'mysql-test/t/query_cache.test'
--- mysql-test/t/query_cache.test	2010-01-11 13:15:28 +0000
+++ mysql-test/t/query_cache.test	2011-01-04 18:36:51 +0000
@@ -1254,6 +1254,7 @@
 set GLOBAL query_cache_limit=default;
 set GLOBAL query_cache_min_res_unit=default;
 set GLOBAL query_cache_size=default;
+set local query_cache_type=default;
 
 #
 # Bug#33756 - query cache with concurrent_insert=0 appears broken
@@ -1514,3 +1515,23 @@
 
 --echo End of 5.1 tests
 
+--echo New query cache switching OFF mechanism test
+set global query_cache_size=1024*1024*20;
+set global query_cache_type=on;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+set global query_cache_size=0;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+set global query_cache_size=1024*1024*20;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+set global query_cache_type=off;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+set global query_cache_type=on;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+set local query_cache_type= on;
+select @@query_cache_size, @@global.query_cache_type, @@local.query_cache_type;
+
+
+--echo restore defaults
+SET GLOBAL query_cache_type= default;
+SET GLOBAL query_cache_size= default;
+SET LOCAL query_cache_type= default;

=== modified file 'sql/mysql_priv.h'
--- sql/mysql_priv.h	2011-01-04 18:44:37 +0000
+++ sql/mysql_priv.h	2011-01-04 15:37:59 +0000
@@ -897,19 +897,19 @@
   (((L)->sql_command == SQLCOM_SELECT) && (L)->safe_to_cache_query)
 #else
 #define QUERY_CACHE_FLAGS_SIZE 0
-#define query_cache_store_query(A, B)
-#define query_cache_destroy()
-#define query_cache_result_size_limit(A)
-#define query_cache_init()
-#define query_cache_resize(A)
-#define query_cache_set_min_res_unit(A)
-#define query_cache_invalidate3(A, B, C)
-#define query_cache_invalidate1(A)
+#define query_cache_store_query(A, B) {}
+#define query_cache_destroy() {}
+#define query_cache_result_size_limit(A) {}
+#define query_cache_init() {}
+#define query_cache_resize(A) {}
+#define query_cache_set_min_res_unit(A) {}
+#define query_cache_invalidate3(A, B, C) {}
+#define query_cache_invalidate1(A, B) {}
 #define query_cache_send_result_to_client(A, B, C) 0
 #define query_cache_invalidate_by_MyISAM_filename_ref NULL
 
-#define query_cache_abort(A)
-#define query_cache_end_of_result(A)
+#define query_cache_abort(A) {}
+#define query_cache_end_of_result(A) {}
 #define query_cache_maybe_disabled(T) 1
 #define query_cache_is_cacheable_query(L) 0
 #endif /*HAVE_QUERY_CACHE*/

=== modified file 'sql/set_var.cc'
--- sql/set_var.cc	2011-01-04 18:44:37 +0000
+++ sql/set_var.cc	2011-01-04 18:17:12 +0000
@@ -130,6 +130,8 @@
 #ifdef HAVE_QUERY_CACHE
 static void fix_query_cache_size(THD *thd, enum_var_type type);
 static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type);
+static int check_query_cache_type(THD *thd, set_var *var);
+static void fix_query_cache_type(THD *thd, enum_var_type type);
 #endif
 static void fix_myisam_max_sort_file_size(THD *thd, enum_var_type type);
 static void fix_max_binlog_size(THD *thd, enum_var_type type);
@@ -603,10 +605,10 @@
   sys_query_cache_min_res_unit(&vars, "query_cache_min_res_unit",
                                &query_cache_min_res_unit,
                                fix_query_cache_min_res_unit);
-static int check_query_cache_type(THD *thd, set_var *var);
 static sys_var_thd_enum	sys_query_cache_type(&vars, "query_cache_type",
 					     &SV::query_cache_type,
-					     &query_cache_type_typelib, NULL,
+					     &query_cache_type_typelib,
+                                             fix_query_cache_type,
                                              check_query_cache_type);
 static sys_var_thd_bool
 sys_query_cache_wlock_invalidate(&vars, "query_cache_wlock_invalidate",
@@ -1217,8 +1219,8 @@
   @param var Pointer to the new variable status
 
   @return Status code
-   @retval 1 Failure
-   @retval 0 Success
+   @retval TRUE  Failure
+   @retval FALSE Success
 */
 
 static int check_query_cache_type(THD *thd, set_var *var)
@@ -1229,11 +1231,31 @@
   */
   if (query_cache.is_disable_in_progress())
   {
-    my_error(ER_QUERY_CACHE_IS_DISABLED,MYF(0));
-    return 1;
-  }
-
-  return 0;
+    my_error(ER_QUERY_CACHE_IS_DISABLED, MYF(0));
+    return TRUE;
+  }
+  if (var->type != OPT_GLOBAL &&
+      global_system_variables.query_cache_type == 0 &&
+      var->value->val_int() != 0)
+  {
+    my_error(ER_QUERY_CACHE_IS_GLOBALY_DISABLED, MYF(0));
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+
+static void fix_query_cache_type(THD *thd, enum_var_type type)
+{
+  if (type == OPT_GLOBAL)
+  {
+    if (global_system_variables.query_cache_type != 0 &&
+        query_cache.is_disabled())
+      fix_query_cache_size(thd, type);
+    else if (global_system_variables.query_cache_type == 0)
+      query_cache.disable_query_cache();
+  }
 }
 
 

=== modified file 'sql/share/errmsg.txt'
--- sql/share/errmsg.txt	2011-01-04 18:44:37 +0000
+++ sql/share/errmsg.txt	2010-12-30 16:47:21 +0000
@@ -6249,4 +6249,5 @@
         eng "Incorrect value '%-.64s' for option '%-.64s'"
 ER_QUERY_CACHE_IS_DISABLED
   eng "Query cache is disabled (resize or similar command in progress); repeat this command later"
-
+ER_QUERY_CACHE_IS_GLOBALY_DISABLED
+  eng "Query cache is globally disabled and you can't enable it only for this session"

=== modified file 'sql/sql_cache.cc'
--- sql/sql_cache.cc	2011-01-04 18:44:37 +0000
+++ sql/sql_cache.cc	2010-12-30 17:33:22 +0000
@@ -543,6 +543,19 @@
 
 
 /**
+  Check and change local variable if global one is switched
+
+  @param thd             thread handle
+*/
+
+void inline fix_local_query_cache_mode(THD *thd)
+{
+  if (global_system_variables.query_cache_type == 0)
+    thd->variables.query_cache_type= 0;
+}
+
+
+/**
   Serialize access to the query cache.
   If the lock cannot be granted the thread hangs in a conditional wait which
   is signalled on each unlock.
@@ -562,12 +575,13 @@
    @retval TRUE The locking attempt failed
 */
 
-bool Query_cache::try_lock(Cache_try_lock_mode mode)
+bool Query_cache::try_lock(THD *thd, Cache_try_lock_mode mode)
 {
   bool interrupt= FALSE;
-  THD *thd = current_thd;
   const char* old_proc_info;
   DBUG_ENTER("Query_cache::try_lock");
+  if (!thd)
+    thd = current_thd;
 
   old_proc_info= thd->proc_info;
   thd_proc_info(thd,"Waiting on query cache mutex");
@@ -576,6 +590,8 @@
   DBUG_EXECUTE_IF("status_wait_query_cache_mutex_sleep", {
       sleep(5);
     });
+  m_requests_in_progress++;
+  fix_local_query_cache_mode(thd);
   while (1)
   {
     if (m_cache_lock_status == Query_cache::UNLOCKED)
@@ -626,8 +642,8 @@
       }
     }
   }
-  if (!interrupt)
-    m_requests_in_progress++;
+  if (interrupt)
+    m_requests_in_progress--;
   pthread_mutex_unlock(&structure_guard_mutex);
   thd->proc_info = old_proc_info;
 
@@ -651,6 +667,7 @@
   DBUG_ENTER("Query_cache::lock_and_suspend");
 
   pthread_mutex_lock(&structure_guard_mutex);
+  m_requests_in_progress++;
   while (m_cache_lock_status != Query_cache::UNLOCKED)
     pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
   m_cache_lock_status= Query_cache::LOCKED_NO_WAIT;
@@ -662,7 +679,6 @@
 #endif
   /* Wake up everybody, a whole cache flush is starting! */
   pthread_cond_broadcast(&COND_cache_status_changed);
-  m_requests_in_progress++;
   pthread_mutex_unlock(&structure_guard_mutex);
 
   DBUG_VOID_RETURN;
@@ -676,19 +692,19 @@
   It is used by all methods which invalidates one or more tables.
  */
 
-void Query_cache::lock(void)
+void Query_cache::lock(THD *thd)
 {
   DBUG_ENTER("Query_cache::lock");
 
   pthread_mutex_lock(&structure_guard_mutex);
+  m_requests_in_progress++;
+  fix_local_query_cache_mode(thd);
   while (m_cache_lock_status != Query_cache::UNLOCKED)
     pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
   m_cache_lock_status= Query_cache::LOCKED;
 #ifndef DBUG_OFF
-  THD *thd= current_thd;
   m_cache_lock_thread_id= thd->thread_id;
 #endif
-  m_requests_in_progress++;
   pthread_mutex_unlock(&structure_guard_mutex);
 
   DBUG_VOID_RETURN;
@@ -1024,7 +1040,7 @@
   if(query_cache.is_disabled())
     DBUG_VOID_RETURN;
 
-  if (query_cache.try_lock())
+  if (query_cache.try_lock(NULL, Query_cache::WAIT))
     DBUG_VOID_RETURN;
 
   Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query;
@@ -1085,7 +1101,7 @@
   if(query_cache.is_disabled())
     DBUG_VOID_RETURN;
 
-  if (query_cache.try_lock())
+  if (query_cache.try_lock(NULL, Query_cache::WAIT))
   {
     net->query_cache_query = 0;
     DBUG_VOID_RETURN;
@@ -1138,7 +1154,7 @@
                      emb_count_querycache_size(thd));
 #endif
 
-  if (query_cache.try_lock())
+  if (query_cache.try_lock(thd, Query_cache::WAIT))
   {
     thd->net.query_cache_query= 0;
     DBUG_VOID_RETURN;
@@ -1251,6 +1267,12 @@
 			query_cache_size_arg));
   DBUG_ASSERT(initialized);
 
+  if (global_system_variables.query_cache_type == 0)
+  {
+    my_error(ER_QUERY_CACHE_IS_DISABLED, MYF(0));
+    DBUG_RETURN(0);
+  }
+
   lock_and_suspend();
 
   /*
@@ -1283,8 +1305,17 @@
   query_cache_size= query_cache_size_arg;
   new_query_cache_size= init_cache();
 
+  /*
+    m_cache_status is internal query cache switch so switching it on/off
+    will not be reflected on global_system_variables.query_cache_type
+  */
   if (new_query_cache_size)
+  {
     DBUG_EXECUTE("check_querycache",check_integrity(1););
+    m_cache_status= OK;                         // size > 0 => enable cache
+  }
+  else
+    m_cache_status= DISABLED;                   // size 0 means the cache disabled
 
   unlock();
   DBUG_RETURN(new_query_cache_size);
@@ -1315,7 +1346,7 @@
 
     See also a note on double-check locking usage above.
   */
-  if (!thd->query_cache_is_valid || query_cache_size == 0)
+  if (!thd->query_cache_is_applicable || query_cache_size == 0)
   {
     DBUG_PRINT("qcache", ("Query cache not ready"));
     DBUG_VOID_RETURN;
@@ -1408,7 +1439,7 @@
       The 'TRUE' parameter indicate that the lock is allowed to timeout
 
     */
-    if (try_lock(Query_cache::WAIT))
+    if (try_lock(thd, Query_cache::WAIT))
       DBUG_VOID_RETURN;
     if (query_cache_size == 0)
     {
@@ -1609,10 +1640,11 @@
     See also a note on double-check locking usage above.
   */
   if (is_disabled() || thd->locked_tables ||
-      thd->variables.query_cache_type == 0 || query_cache_size == 0)
+      thd->variables.query_cache_type == 0)
     goto err;
+  DBUG_ASSERT(query_cache_size != 0);           // otherwise cache would be disabled
 
-  thd->query_cache_is_valid= 1;
+  thd->query_cache_is_applicable= 1;
   sql= org_sql; sql_end= sql + query_length;
 
   /*
@@ -1706,7 +1738,7 @@
 
     The WAIT parameter indicate that the lock is allowed to timeout.
   */
-  if (try_lock(Query_cache::WAIT))
+  if (try_lock(thd, Query_cache::WAIT))
     goto err;
 
   if (query_cache_size == 0)
@@ -1866,7 +1898,7 @@
           temporary tables => assign following variable to make check
           faster.
         */
-        thd->query_cache_is_valid= 0;         // Query can't be cached
+        thd->query_cache_is_applicable= 0;      // Query can't be cached
         BLOCK_UNLOCK_RD(query_block);
         DBUG_RETURN(-1);
       }
@@ -1882,7 +1914,7 @@
 		 ("probably no SELECT access to %s.%s =>  return to normal processing",
 		  table_list.db, table_list.alias));
       unlock();
-      thd->query_cache_is_valid= 0;                 // Query can't be cached
+      thd->query_cache_is_applicable= 0;        // Query can't be cached
       BLOCK_UNLOCK_RD(query_block);
       DBUG_RETURN(-1);				// Privilege error
     }
@@ -1891,7 +1923,7 @@
       DBUG_PRINT("qcache", ("Need to check column privileges for %s.%s",
 			    table_list.db, table_list.alias));
       BLOCK_UNLOCK_RD(query_block);
-      thd->query_cache_is_valid= 0;           // Query can't be cached
+      thd->query_cache_is_applicable= 0;        // Query can't be cached
       goto err_unlock;				// Parse query
     }
 #endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
@@ -1915,7 +1947,7 @@
                                   table->key_length());
       }
       else
-        thd->query_cache_is_valid= 0;                 // Query can't be cached
+        thd->query_cache_is_applicable= 0;      // Query can't be cached
       goto err_unlock;				// Parse query
     }
     else
@@ -1973,7 +2005,7 @@
   DBUG_RETURN(0);				// Query was not cached
 
 err:
-  thd->query_cache_is_valid= 0;                 // Query can't be cached
+  thd->query_cache_is_applicable= 0;            // Query can't be cached
   DBUG_RETURN(0);				// Query was not cached
 }
 
@@ -2119,7 +2151,7 @@
     Lock the query cache and queue all invalidation attempts to avoid
     the risk of a race between invalidation, cache inserts and flushes.
   */
-  lock();
+  lock(thd);
 
   if (query_cache_size > 0)
   {
@@ -2238,7 +2270,7 @@
     If the entire qc is being invalidated we can bail out early
     instead of waiting for the lock.
   */
-  if (try_lock())
+  if (try_lock(NULL, Query_cache::WAIT))
     DBUG_VOID_RETURN;
 
   if (query_cache_size == 0)
@@ -2281,6 +2313,19 @@
 }
 
 
+void Query_cache::disable_query_cache(void)
+{
+  m_cache_status= DISABLE_REQUEST;
+  /*
+    If there is no requests in progress try to free buffer.
+    try_lock(TRY) will exit immediately if there is lock.
+    unlock() should free block.
+  */
+  if (m_requests_in_progress == 0 && !try_lock(NULL, TRY))
+    unlock();
+}
+
+
 /*****************************************************************************
   init/destroy
 *****************************************************************************/
@@ -2997,7 +3042,7 @@
     Lock the query cache and queue all invalidation attempts to avoid
     the risk of a race between invalidation, cache inserts and flushes.
   */
-  lock();
+  lock(thd);
 
   DBUG_EXECUTE_IF("wait_in_query_cache_invalidate2",
                   debug_wait_for_kill("wait_in_query_cache_invalidate2"); );
@@ -3778,7 +3823,7 @@
     {
       DBUG_PRINT("qcache", ("Don't cache statement as it refers to "
                             "tables with column privileges."));
-      thd->query_cache_is_valid= 0;                 // Query can't be cached
+      thd->query_cache_is_applicable= 0;        // Query can't be cached
       DBUG_RETURN(0);
     }
 #endif
@@ -3914,7 +3959,7 @@
     {
       DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
 			    tables_used->db, tables_used->alias));
-      thd->query_cache_is_valid= 0;           // Query can't be cached
+      thd->query_cache_is_applicable= 0;        // Query can't be cached
       DBUG_RETURN(1);
     }
   }

=== modified file 'sql/sql_cache.h'
--- sql/sql_cache.h	2011-01-04 18:44:37 +0000
+++ sql/sql_cache.h	2010-12-30 17:18:05 +0000
@@ -284,17 +284,6 @@
 
   void free_query_internal(Query_cache_block *point);
   void invalidate_table_internal(THD *thd, uchar *key, uint32 key_length);
-  void disable_query_cache(void)
-  {
-    m_cache_status= DISABLE_REQUEST;
-    /*
-      If there is no requests in progress try to free buffer.
-      try_lock(TRY) will exit immediately if there is lock.
-      unlock() should free block.
-    */
-    if (m_requests_in_progress == 0 && !try_lock(TRY))
-      unlock();
-  }
 
 protected:
   /*
@@ -504,10 +493,12 @@
   my_bool in_blocks(Query_cache_block * point);
 
   enum Cache_try_lock_mode {WAIT, TIMEOUT, TRY};
-  bool try_lock(Cache_try_lock_mode mode= WAIT);
-  void lock(void);
+  bool try_lock(THD *thd, Cache_try_lock_mode mode= WAIT);
+  void lock(THD *thd);
   void lock_and_suspend(void);
   void unlock(void);
+
+  void disable_query_cache(void);
 };
 
 extern Query_cache query_cache;

=== modified file 'sql/sql_class.h'
--- sql/sql_class.h	2011-01-04 18:44:37 +0000
+++ sql/sql_class.h	2010-12-29 10:45:11 +0000
@@ -702,7 +702,7 @@
   size_t db_length;
 
   /* This is set to 1 of last call to send_result_to_client() was ok */
-  my_bool query_cache_is_valid;
+  my_bool query_cache_is_applicable;
 
   /* This constructor is called for backup statements */
   Statement() {}


Follow ups

References