← Back to team overview

maria-discuss team mailing list archive

MySQL error code - forward compatible

 

Dear MariaDB,

Both Percona-Server/XtraDB and MariaDB experience problems concerning
forward compatible error codes.
1) Both servers have a lot of features backported from mysql 5.5. Some
features add new error (new error codes from MySQL 5.5).
2) Some Percona-Server/XtraDB and MariaDB features add new error code.
In the first case we should save error code. For example, Percona has
patch query_cache_totally_disable.patch
(http://www.percona.com/docs/wiki/patches:query_cache_totally_disable)
This patch adds error ER_QUERY_CACHE_DISABLED - error code number 1651
in mysql 5.5
(http://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html#error_er_query_cache_disabled)
We must be consistent with error code numbers, because they may be
used in scripts.
After migrating PerconaServer/XtraDB and MariaDB to mysql 5.5, users
might experience troubles because of this.
In the second case we should add error code numbers, unique for our errors, as
we cannot use any numbers from mysql 5.5.
For example:
http://www.percona.com/docs/wiki/patches:slave_type_conversions_error_on_truncate
MySQL has a tool "comp_err" that generates
1) errmsg.sys files
2) header file include/mysqld_error.h
3) header file include/mysqld_ername.h
from the file errmsg.txt
To keep error numbers consistent, we should add some fictive errors to
errmsg.txt, because comp_err assigns error code numbers sequentially,
without gaps.
I propose patch to comp_err (see attachment error_pad.patch).
This patch allows usage of a new syntax, with prefix "PADD_", for example:

  PADD_QUERY_CACHE_DISABLED 1651
    eng "ER_QUERY_CACHE_DISABLED padding to 1651 error"
  ER_QUERY_CACHE_DISABLED
    eng "Query cache is disabled; restart the server with query_cache_type=1 to enable it"

comp_err with my patch padds empty intervals (from last error code
number to 1651) by error message "ER_QUERY_CACHE_DISABLED padding to
1651 error", i.e.
and ER_QUERY_CACHE_DISABLED now has error code 1651 (as desired).
I propose to use this patch for Percona's and Maria's errors, for example:

  PADD_PERCONA_NEW_ERROR_CODE 4000
    end "Padd empty space to error code number 4000 (Percona error codes)"
...some percona error codes...
  PADD_MARIA_DB_NEW_ERROR_CODE 5000
    eng "Padd empty space to error code number 5000 (MariaDB error codes)"
...some maria error codes...

Patch only adds prefix "PADD_" and padds error in sys files.
All other mysql code (load*.sys files, my_error, etc) works as old one.
Please, let me know what you think of it.

Best regards, Oleg.
diff -Nur a/extra/comp_err.c b/extra/comp_err.c
--- a/extra/comp_err.c	2010-07-09 16:34:53.000000000 +0400
+++ b/extra/comp_err.c	2010-08-05 00:42:25.448460121 +0400
@@ -35,6 +35,7 @@
 #define DEFAULT_CHARSET_DIR "../sql/share/charsets"
 #define ER_PREFIX "ER_"
 #define WARN_PREFIX "WARN_"
+#define PADD_PREFIX "PADD_"
 static char *OUTFILE= (char*) "errmsg.sys";
 static char *HEADERFILE= (char*) "mysqld_error.h";
 static char *NAMEFILE= (char*) "mysqld_ername.h";
@@ -89,6 +90,7 @@
   const char *sql_code1;		/* sql state */
   const char *sql_code2;		/* ODBC state */
   struct errors *next_error;            /* Pointer to next error */
+  my_bool is_padding;                   /* If true - padd this er_name while er_code != d_code*/
   DYNAMIC_ARRAY msg;                    /* All language texts for this error */
 };
 
@@ -127,6 +129,7 @@
 
 
 static struct languages *parse_charset_string(char *str);
+static struct errors *parse_padd_string(char *ptr, int er_count);
 static struct errors *parse_error_string(char *ptr, int er_count);
 static struct message *parse_message_string(struct message *new_message,
 					    char *str);
@@ -229,6 +232,11 @@
 
   for (tmp_error= error_head; tmp_error; tmp_error= tmp_error->next_error)
   {
+    if (tmp_error->is_padding)
+    {
+      er_last= tmp_error->d_code;
+      continue;
+    }
     /*
        generating mysqld_error.h
        fprintf() will automatically add \r on windows
@@ -318,12 +324,29 @@
 		"language\n", tmp_error->er_name, tmp_lang->lang_short_name);
 	goto err;
       }
-      if (copy_rows(to, tmp->text, row_nr, start_pos))
+      if (tmp_error->is_padding)
       {
-	fprintf(stderr, "Failed to copy rows to %s\n", outfile);
-	goto err;
+        int padd_to= tmp_error->d_code;
+        char* padd_message= tmp->text;
+        while ((row_nr+er_offset) < padd_to)
+        {
+          if (copy_rows(to, padd_message,row_nr,start_pos))
+          {
+            fprintf(stderr, "Failed to copy rows to %s\n", outfile);
+            goto err;
+          }
+          row_nr++;
+        }
+      }
+      else
+      {
+        if (copy_rows(to, tmp->text, row_nr, start_pos))
+        {
+          fprintf(stderr, "Failed to copy rows to %s\n", outfile);
+          goto err;
+        }
+        row_nr++;
       }
-      row_nr++;
     }
 
     /* continue with header of the errmsg.sys file */
@@ -474,14 +497,26 @@
 	DBUG_RETURN(0);
       continue;
     }
-    if (is_prefix(str, ER_PREFIX) || is_prefix(str, WARN_PREFIX))
+    if (is_prefix(str, ER_PREFIX) || is_prefix(str, WARN_PREFIX) || is_prefix(str, PADD_PREFIX))
     {
-      if (!(current_error= parse_error_string(str, rcount)))
+      if (is_prefix(str, PADD_PREFIX))
       {
-	fprintf(stderr, "Failed to parse the error name string\n");
-	DBUG_RETURN(0);
+        if (!(current_error= parse_padd_string(str, rcount)))
+        {
+          fprintf(stderr, "Failed to parse the error padd string\n");
+          DBUG_RETURN(0);
+        }
+        rcount= current_error->d_code - er_offset;  /* Count number of unique errors */
+      }
+      else
+      {
+        if (!(current_error= parse_error_string(str, rcount)))
+        {
+          fprintf(stderr, "Failed to parse the error name string\n");
+          DBUG_RETURN(0);
+        }
+        rcount++;                         /* Count number of unique errors */
       }
-      rcount++;                         /* Count number of unique errors */
 
       /* add error to the list */
       *tail_error= current_error;
@@ -822,80 +857,124 @@
   DBUG_RETURN(new_message);
 }
 
+static struct errors* create_new_error(my_bool is_padding, char *er_name, int d_code, const char *sql_code1, const char *sql_code2)
+{
+  struct errors *new_error;
+  DBUG_ENTER("create_new_error");
+  /* create a new element */
+  new_error= (struct errors *) my_malloc(sizeof(*new_error), MYF(MY_WME));
+  if (my_init_dynamic_array(&new_error->msg, sizeof(struct message), 0, 0))
+    DBUG_RETURN(0);				/* OOM: Fatal error */
+  new_error->is_padding= is_padding;
+  DBUG_PRINT("info", ("is_padding: %s", (is_padding ? "true" : "false")));
+  new_error->er_name= er_name;
+  DBUG_PRINT("info", ("er_name: %s", er_name));
+  new_error->d_code= d_code;
+  DBUG_PRINT("info", ("d_code: %d", d_code));
+  new_error->sql_code1= sql_code1;
+  DBUG_PRINT("info", ("sql_code1: %s", sql_code1));
+  new_error->sql_code2= sql_code2;
+  DBUG_PRINT("info", ("sql_code2: %s", sql_code2));
+  DBUG_RETURN(new_error);
+}
 
 /*
-  Parsing the string with error name and codes; returns the pointer to
+  Parsing the string with padd syntax (name + error to pad); returns the pointer to
   the errors struct
 */
 
-static struct errors *parse_error_string(char *str, int er_count)
+static struct errors *parse_padd_string(char* str, int er_count)
 {
-  struct errors *new_error;
+  char *er_name;
+  uint d_code;
   char *start;
   DBUG_ENTER("parse_error_string");
   DBUG_PRINT("enter", ("str: %s", str));
 
-  /* create a new element */
-  new_error= (struct errors *) my_malloc(sizeof(*new_error), MYF(MY_WME));
+  start= str;
+  str= skip_delimiters(str);
 
-  if (my_init_dynamic_array(&new_error->msg, sizeof(struct message), 0, 0))
+  /* getting the error name */
+
+  if (!(er_name= get_word(&str)))
     DBUG_RETURN(0);				/* OOM: Fatal error */
 
-  /* getting the error name */
+  str= skip_delimiters(str);
+
+  if (!(d_code= parse_error_offset(start)))
+  {
+    fprintf(stderr, "Failed to parse the error padd string '%s' '%s' (d_code doesn't parse)!\n",er_name,str);
+    DBUG_RETURN(0);
+  }
+  if (d_code < (er_offset + er_count))
+  {
+    fprintf(stderr, "Error to padding less current error number!\n");
+    DBUG_RETURN(0);
+  }
+  DBUG_RETURN(create_new_error(TRUE,er_name,d_code,empty_string,empty_string));
+}
+
+/*
+  Parsing the string with error name and codes; returns the pointer to
+  the errors struct
+*/
+
+static struct errors *parse_error_string(char *str, int er_count)
+{
+  char *er_name;
+  int d_code;
+  const char *sql_code1= empty_string;
+  const char *sql_code2= empty_string;
+  char *start;
+  DBUG_ENTER("parse_error_string");
+  DBUG_PRINT("enter", ("str: %s", str));
+
   start= str;
   str= skip_delimiters(str);
 
-  if (!(new_error->er_name= get_word(&str)))
+  /* getting the error name */
+
+  if (!(er_name= get_word(&str)))
     DBUG_RETURN(0);				/* OOM: Fatal error */
-  DBUG_PRINT("info", ("er_name: %s", new_error->er_name));
 
   str= skip_delimiters(str);
 
   /* getting the code1 */
-
-  new_error->d_code= er_offset + er_count;
-  DBUG_PRINT("info", ("d_code: %d", new_error->d_code));
+  d_code= er_offset + er_count;
 
   str= skip_delimiters(str);
 
   /* if we reached EOL => no more codes, but this can happen */
   if (!*str)
   {
-    new_error->sql_code1= empty_string;
-    new_error->sql_code2= empty_string;
     DBUG_PRINT("info", ("str: %s", str));
-    DBUG_RETURN(new_error);
+    goto complete_create;
   }
-
   /* getting the sql_code 1 */
-
-  if (!(new_error->sql_code1= get_word(&str)))
+  if (!(sql_code1= get_word(&str)))
     DBUG_RETURN(0);				/* OOM: Fatal error */
-  DBUG_PRINT("info", ("sql_code1: %s", new_error->sql_code1));
 
   str= skip_delimiters(str);
 
   /* if we reached EOL => no more codes, but this can happen */
   if (!*str)
   {
-    new_error->sql_code2= empty_string;
     DBUG_PRINT("info", ("str: %s", str));
-    DBUG_RETURN(new_error);
+    goto complete_create;
   }
-
   /* getting the sql_code 2 */
-  if (!(new_error->sql_code2= get_word(&str)))
+  if (!(sql_code2= get_word(&str)))
     DBUG_RETURN(0);				/* OOM: Fatal error */
-  DBUG_PRINT("info", ("sql_code2: %s", new_error->sql_code2));
 
   str= skip_delimiters(str);
+
   if (*str)
   {
     fprintf(stderr, "The error line did not end with sql/odbc code!");
     DBUG_RETURN(0);
   }
-
-  DBUG_RETURN(new_error);
+complete_create:
+  DBUG_RETURN(create_new_error(FALSE,er_name,d_code,sql_code1,sql_code2));
 }
 
 
diff -Nur a/patch_info/query_cache_totally_disable.info b/patch_info/query_cache_totally_disable.info
--- a/patch_info/query_cache_totally_disable.info      1970-01-01 03:00:00.000000000 +0300
+++ b/patch_info/query_cache_totally_disable.info      2010-07-29 01:31:19.284017962 +0400
@@ -0,0 +1,6 @@
+File=query_cache_totally_disable.patch
+Name=Totally disable query cache
+Version=1.0
+Author=Percona
+License=GPL
+Comment=
diff -Nur a/mysql-test/r/query_cache_disabled.result b/mysql-test/r/query_cache_disabled.result
--- a/mysql-test/r/query_cache_disabled.result	1970-01-01 03:00:00.000000000 +0300
+++ b/mysql-test/r/query_cache_disabled.result	2010-07-31 20:44:34.764016531 +0400
@@ -0,0 +1,14 @@
+SHOW GLOBAL VARIABLES LIKE 'query_cache_type';
+Variable_name	Value
+query_cache_type	OFF
+SET GLOBAL query_cache_type=ON;
+ERROR HY000: Query cache is disabled; restart the server with query_cache_type=1 to enable it
+SET GLOBAL query_cache_type=DEMAND;
+ERROR HY000: Query cache is disabled; restart the server with query_cache_type=1 to enable it
+SET GLOBAL query_cache_type=OFF;
+ERROR HY000: Query cache is disabled; restart the server with query_cache_type=1 to enable it
+SET GLOBAL query_cache_size=1024*1024;
+SHOW GLOBAL VARIABLES LIKE 'query_cache_size';
+Variable_name	Value
+query_cache_size	1048576
+SET GLOBAL query_cache_size=0;
diff -Nur a/mysql-test/t/query_cache_disabled-master.opt b/mysql-test/t/query_cache_disabled-master.opt
--- a/mysql-test/t/query_cache_disabled-master.opt	1970-01-01 03:00:00.000000000 +0300
+++ b/mysql-test/t/query_cache_disabled-master.opt	2010-07-31 20:41:47.184017514 +0400
@@ -0,0 +1 @@
+--query_cache_type=0
diff -Nur a/mysql-test/t/query_cache_disabled.test b/mysql-test/t/query_cache_disabled.test
--- a/mysql-test/t/query_cache_disabled.test	1970-01-01 03:00:00.000000000 +0300
+++ b/mysql-test/t/query_cache_disabled.test	2010-07-31 20:41:47.184017514 +0400
@@ -0,0 +1,15 @@
+-- source include/have_query_cache.inc
+#
+# Bug#38551 query cache can still consume [very little] cpu time even when it is off.
+#
+SHOW GLOBAL VARIABLES LIKE 'query_cache_type';
+--error ER_QUERY_CACHE_DISABLED
+SET GLOBAL query_cache_type=ON;
+--error ER_QUERY_CACHE_DISABLED
+SET GLOBAL query_cache_type=DEMAND;
+--error ER_QUERY_CACHE_DISABLED
+SET GLOBAL query_cache_type=OFF;
+SET GLOBAL query_cache_size=1024*1024;
+SHOW GLOBAL VARIABLES LIKE 'query_cache_size';
+SET GLOBAL query_cache_size=0;
+
diff -Nur a/sql/set_var.cc b/sql/set_var.cc
--- a/sql/set_var.cc	2010-07-31 20:42:05.484017174 +0400
+++ b/sql/set_var.cc	2010-07-31 20:41:47.194017112 +0400
@@ -125,8 +125,10 @@
 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);
 static void fix_query_cache_min_res_unit(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);
 static void fix_max_relay_log_size(THD *thd, enum_var_type type);
@@ -569,9 +571,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);
@@ -637,14 +636,20 @@
                                             NULL);
 
 #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 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);
+					     &query_cache_type_typelib, NULL,
+                                             check_query_cache_type);
 static sys_var_thd_bool
 sys_query_cache_wlock_invalidate(&vars, "query_cache_wlock_invalidate",
 				 &SV::query_cache_wlock_invalidate);
@@ -1249,10 +1254,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);
 
   /*
@@ -1266,11 +1270,35 @@
 			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 1 Failure
+   @retval 0 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_disabled())
+  {
+    my_error(ER_QUERY_CACHE_DISABLED,MYF(0));
+    return 1;
+  }
+
+  return 0;
+}
+
+
 static void fix_query_cache_min_res_unit(THD *thd, enum_var_type type)
 {
   query_cache_min_res_unit= 
@@ -3648,6 +3676,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())
diff -Nur a/sql/set_var.h b/sql/set_var.h
--- a/sql/set_var.h	2010-07-31 20:42:00.884030418 +0400
+++ b/sql/set_var.h	2010-07-31 20:41:47.224016185 +0400
@@ -519,10 +519,16 @@
   { 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;
+    else if ((check_func && (*check_func)(thd, var)))
+      return TRUE;
+    else
+      return FALSE;
   }
   bool update(THD *thd, set_var *var);
   void set_default(THD *thd, enum_var_type type);
diff -Nur a/sql/share/errmsg.txt b/sql/share/errmsg.txt
--- a/sql/share/errmsg.txt	2010-07-09 16:35:08.000000000 +0400
+++ b/sql/share/errmsg.txt	2010-07-31 20:41:47.244015659 +0400
@@ -6213,3 +6213,8 @@
 ER_DEBUG_SYNC_HIT_LIMIT
   eng "debug sync point hit limit reached"
   ger "Debug Sync Point Hit Limit erreicht"
+PADD_QUERY_CACHE_DISABLED 1651
+  eng "ER_QUERY_CACHE_DISABLED padding to 1651 error"
+ER_QUERY_CACHE_DISABLED
+  eng "Query cache is disabled; restart the server with query_cache_type=1 to enable it"
+
diff -Nur a/sql/sql_cache.cc b/sql/sql_cache.cc
--- a/sql/sql_cache.cc	2010-07-31 20:42:05.642766729 +0400
+++ b/sql/sql_cache.cc	2010-07-31 20:41:47.254016654 +0400
@@ -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.
@@ -1044,11 +1045,14 @@
   DBUG_EXECUTE_IF("wait_in_query_cache_insert",
                   debug_wait_for_kill("wait_in_query_cache_insert"); );
 
+  if(query_cache.is_disabled())
+    DBUG_VOID_RETURN;
+
   if (query_cache.try_lock())
     DBUG_VOID_RETURN;
 
   Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query;
-  if (!query_block)
+  if (NULL == query_block)
   {
     /*
       We lost the writer and the currently processed query has been
@@ -1102,6 +1106,9 @@
   if (net->query_cache_query == 0)
     DBUG_VOID_RETURN;
 
+  if(query_cache.is_disabled())
+    DBUG_VOID_RETURN;
+
   if (query_cache.try_lock())
     DBUG_VOID_RETURN;
 
@@ -1235,6 +1242,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_query_cache_is_disabled(FALSE),
    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)),
@@ -1549,8 +1557,8 @@
 
     See also a note on double-check locking usage above.
   */
-  if (thd->locked_tables || thd->variables.query_cache_type == 0 ||
-      query_cache_size == 0)
+  if (is_disabled() || thd->locked_tables ||
+      thd->variables.query_cache_type == 0 || query_cache_size == 0)
     goto err;
 
   if (!thd->lex->safe_to_cache_query)
@@ -1950,6 +1958,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));
@@ -1980,6 +1990,9 @@
 void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
 {
   DBUG_ENTER("Query_cache::invalidate (changed table list)");
+  if (is_disabled())
+    DBUG_VOID_RETURN;
+
   THD *thd= current_thd;
   for (; tables_used; tables_used= tables_used->next)
   {
@@ -2005,8 +2018,11 @@
 */
 void Query_cache::invalidate_locked_for_write(TABLE_LIST *tables_used)
 {
-  THD *thd= current_thd;
   DBUG_ENTER("Query_cache::invalidate_locked_for_write");
+  if (is_disabled())
+    DBUG_VOID_RETURN;
+
+  THD *thd= current_thd;
   for (; tables_used; tables_used= tables_used->next_local)
   {
     thd_proc_info(thd, "invalidating query cache entries (table)");
@@ -2027,7 +2043,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 && 
@@ -2044,6 +2062,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));
@@ -2062,9 +2082,11 @@
 
 void Query_cache::invalidate(char *db)
 {
-  bool restart= FALSE;
-  DBUG_ENTER("Query_cache::invalidate (db)");
 
+  DBUG_ENTER("Query_cache::invalidate (db)");
+  if (is_disabled())
+    DBUG_VOID_RETURN;
+  bool restart= FALSE;
   /*
     Lock the query cache and queue all invalidation attempts to avoid
     the risk of a race between invalidation, cache inserts and flushes.
@@ -2149,6 +2171,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"););
 
@@ -2180,6 +2205,9 @@
 {
   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.
@@ -2237,6 +2265,15 @@
   pthread_cond_init(&COND_cache_status_changed, NULL);
   m_cache_lock_status= Query_cache::UNLOCKED;
   initialized = 1;
+  /*
+    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;
 }
 
@@ -4940,3 +4977,4 @@
 #endif /* DBUG_OFF */
 
 #endif /*HAVE_QUERY_CACHE*/
+
diff -Nur a/sql/sql_cache.h b/sql/sql_cache.h
--- a/sql/sql_cache.h	2010-07-09 16:35:15.000000000 +0400
+++ b/sql/sql_cache.h	2010-07-31 20:41:47.264017369 +0400
@@ -279,8 +279,11 @@
   enum Cache_lock_status { UNLOCKED, LOCKED_NO_WAIT, LOCKED };
   Cache_lock_status m_cache_lock_status;
 
+  bool m_query_cache_is_disabled;
+
   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_query_cache_is_disabled= TRUE; }
 
 protected:
   /*
@@ -423,6 +426,8 @@
 	      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_query_cache_is_disabled; }
+
   /* initialize cache (mutex) */
   void init();
   /* resize query cache (return real query size, 0 if disabled) */