-# name : query_cache_enhance.patch
+# name : query_cache_with_comments.patch
# introduced : 11 or before
# maintainer : Oleg
#
+2010-11 - Ported to 5.5
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
-@@ -904,6 +904,7 @@
+@@ -915,6 +915,7 @@
#endif
#ifdef HAVE_QUERY_CACHE
ulong query_cache_min_res_unit= QUERY_CACHE_MIN_RESULT_DATA_SIZE;
extern ulonglong log_output_options;
extern ulong log_backup_output_options;
extern my_bool opt_log_queries_not_using_indexes;
+--- /dev/null
++++ b/sql/query_strip_comments.h
+@@ -0,0 +1,37 @@
++#ifndef _SQL_QUERY_STRIPC_COMMENTS_H_
++#define _SQL_QUERY_STRIPC_COMMENTS_H_
++#ifdef HAVE_QUERY_CACHE
++
++// implemented in sql_cache.cc
++class QueryStripComments
++{
++private:
++ QueryStripComments(const QueryStripComments&);
++ QueryStripComments& operator=(const QueryStripComments&);
++public:
++ QueryStripComments();
++ ~QueryStripComments();
++ void set(const char* a_query, uint a_query_length, uint a_additional_length);
++
++ char* query() { return buffer; }
++ uint query_length() { return length; }
++private:
++ void cleanup();
++private:
++ char* buffer;
++ uint length /*query length, not buffer length*/;
++ uint buffer_length;
++};
++class QueryStripComments_Backup
++{
++public:
++ QueryStripComments_Backup(THD* a_thd,QueryStripComments* qsc);
++ ~QueryStripComments_Backup();
++private:
++ THD* thd;
++ char* query;
++ uint length;
++};
++
++#endif // HAVE_QUERY_CACHE
++#endif // _SQL_QUERY_STRIPC_COMMENTS_H_
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
-@@ -344,6 +344,496 @@
+@@ -344,6 +344,198 @@
#include "probes_mysql.h"
#include "transaction.h"
++#include "query_strip_comments.h"
+
-+namespace query_comments_parser
-+{
-+
-+
-+enum Kind
-+{
-+ /* 'Empty' symbol - epsilon in classic parsers */
-+ Empty,
-+ /*
-+ Special symbols:
-+ * exclamation comment: slash-star-exclamation comment-body star-slash
-+ * single-line and multi-line comments
-+ */
-+ Special,
-+ /* Whitespaces: ' ' \t \r \n */
-+ WhiteSpace,
-+ /*
-+ 1) C-style comment (slash-star comment-body star-slash)
-+ 2) signle-line comment:
-+ * sharp comment (sharp comment-body new-line)
-+ * minus-minus comment (minus-minus comment-body new-line)
-+ */
-+ Comment,
-+ /* Not a special symbols (this symbols can't be before SELECT ). */
-+ Another,
-+ /* Error: not-closed quotes, not-closed C-style comment, end-of-query */
-+ Error
-+};
++/*
++ Number of bytes to be allocated in a query cache buffer in addition to the
++ query string length.
+
++ The query buffer layout is:
+
-+/**
-+ Analyze kind of prefix of input string.
++ buffer :==
++ <statement> The input statement(s)
++ '\0' Terminating null char
++ <db_length> Length of following current database name (size_t)
++ <db_name> Name of current database
++ <flags> Flags struct
++*/
++#define QUERY_BUFFER_ADDITIONAL_LENGTH(db_length) \
++ (1 + sizeof(size_t) + db_length + QUERY_CACHE_FLAGS_SIZE)
+
-+ @param where pointer to pointer to begin of string. After analyzing input
-+ string function skip analyzed prefix and return pointer to the next part
-+ of string in the @param where.
++QueryStripComments::QueryStripComments()
++{
++ buffer = 0;
++ length = 0;
++ buffer_length = 0;
++}
++QueryStripComments::~QueryStripComments()
++{
++ cleanup();
++}
+
-+ @return kind of analyzed prefix.
-+*/
-+static Kind analyze(const char **where, const char *const end)
++inline bool query_strip_comments_is_white_space(char c)
++{
++ return ((' ' == c) || ('\t' == c) || ('\r' == c) || ('\n' ==c ));
++}
++void QueryStripComments::set(const char* query, uint query_length, uint additional_length)
+{
-+ DBUG_ASSERT(where != NULL);
-+ DBUG_ASSERT(*where != NULL);
-+ const char*&to= *where;
-+ /* if empty */
-+ if (*to == '\0')
++ uint new_buffer_length = query_length + additional_length;
++ if(new_buffer_length > buffer_length)
+ {
-+ return Empty;
++ cleanup();
++ buffer = (char*)my_malloc(new_buffer_length,MYF(0));
+ }
-+
-+ /* current symbol */
-+ char current= *to;
-+
-+ switch (current)
++ uint query_position = 0;
++ uint position = 0;
++ // Skip whitespaces from begin
++ while((query_position < query_length) && query_strip_comments_is_white_space(query[query_position]))
++ {
++ ++query_position;
++ }
++ long int last_space = -1;
++ while(query_position < query_length)
+ {
-+ case '\'':
-+ case '"':
-+ /* skip quote */
-+ to++;
-+ /* search pair */
-+ while (true)
++ char current = query[query_position];
++ bool insert_space = false; // insert space to buffer, (IMPORTANT) don't update query_position
++ switch(current)
+ {
-+ /* check for pair of quote */
-+ if (*to == current)
++ case '\'':
++ case '"':
+ {
-+ /* skip second quote */
-+ to++;
-+ /* check for same symbol after second quote */
-+ if (to < end && *to == current)
++ buffer[position++] = query[query_position++]; // copy current symbol
++ while(query_position < query_length)
+ {
-+ /* same symbol, skip it */
-+ to++;
-+ /* check for end-of-line */
-+ if (to == end)
++ if(current == query[query_position]) // found pair quote
+ {
-+ /* not found - not closed quote */
-+ return Error;
++ break;
+ }
-+ else
++ buffer[position++] = query[query_position++]; // copy current symbol
++ }
++ break;
++ }
++ case '/':
++ {
++ if(((query_position + 2) < query_length) && ('*' == query[query_position+1]) && ('!' != query[query_position+2]))
++ {
++ query_position += 2; // skip "/*"
++ do
++ {
++ if('*' == query[query_position] && '/' == query[query_position+1]) // check for "*/"
++ {
++ query_position += 2; // skip "*/"
++ insert_space = true;
++ break;
++ }
++ else
++ {
++ ++query_position;
++ }
++ }
++ while(query_position < query_length);
++ if(!insert_space)
+ {
-+ /* continue search of pair */
+ continue;
+ }
+ }
++ break;
++ }
++ case '-':
++ {
++ if(query[query_position+1] == '-')
++ {
++ ++query_position; // skip "-", and go to search of "\n"
++ }
+ else
+ {
-+ return Another;
++ break;
+ }
+ }
-+ /* check for escaped symbols */
-+ if (*to == '\\')
-+ {
-+ /* backslash, skip it */
-+ to++;
-+ }
-+ /* check for end-of-line */
-+ if (to == end)
-+ {
-+ /* not found - not closed quote */
-+ return Error;
-+ }
-+ /* skip current symbol */
-+ to++;
-+ }
-+ case '-':
-+ /* Skip minus */
-+ to++;
-+ /* Check for second minus */
-+ if (*to != '-')
-+ {
-+ /* Just minus */
-+ return Another;
-+ }
-+ else
-+ {
-+ /*
-+ Prefix is minus-minus, next case-branch is processing
-+ single line comments.
-+ */
-+ }
-+ case '#':
-+ /*
-+ This is single-line comment, it started by "#" or "--".
-+ Skip first symbol.
-+ */
-+ to++;
-+ /* search new-line */
-+ to= strchr(to, '\n');
-+ if (NULL == to)
-+ {
-+ /* not found, end of the comment is the end of the query */
-+ to= end;
-+ }
-+ else
-+ {
-+ /* skip end-of-line */
-+ to++;
-+ }
-+ return Comment;
-+ case '/':
-+ /* skip slash */
-+ to++;
-+ /* check for star */
-+ if (*to == '*')
-+ {
-+ /* skip star */
-+ to++;
-+ /* check for exclamation */
-+ bool exclamation= (*to == '!');
-+ /* search star-slash */
-+ to= strstr(to, "*/");
-+ if (NULL == to)
++ case '#':
+ {
-+ /* not found - not closed comment */
-+ return Error;
++ do
++ {
++ ++query_position; // skip current symbol (# or -)
++ if('\n' == query[query_position]) // check for '\n'
++ {
++ ++query_position; // skip '\n'
++ insert_space = true;
++ break;
++ }
++ }
++ while(query_position < query_length);
++ if(insert_space)
++ {
++ break;
++ }
++ else
++ {
++ continue;
++ }
+ }
-+ else
++ default:
++ if(query_strip_comments_is_white_space(current))
+ {
-+ /* found */
-+ DBUG_ASSERT(to + 1 < end);
-+ DBUG_ASSERT(0 == strncmp(to, "*/", 2));
-+ /* skip star-slash */
-+ to++;
-+ to++;
-+ return (exclamation ? Special : Comment);
++ insert_space = true;
++ ++query_position;
+ }
++ break; // make gcc happy
+ }
-+ else
-+ {
-+ /* just slash */
-+ return Another;
-+ }
-+ case ' ':
-+ case '\t':
-+ case '\r':
-+ case '\n':
-+ {
-+ /* skip space */
-+ to++;
-+ return WhiteSpace;
-+ }
-+ case '\\':
++ if(insert_space)
+ {
-+ /* skip backslash */
-+ to++;
-+ if (to == end)
++ if((uint) (last_space + 1) != position)
+ {
-+ /*
-+ query complete by backslash
-+ probable error?
-+ */
-+ return Another;
++ last_space = position;
++ buffer[position++] = ' ';
+ }
-+ else
-+ {
-+ /* skip after backslash symbol */
-+ to++;
-+ return Another;
-+ }
-+ }
-+ case '(':
-+ case ')':
-+ {
-+ /* skip parenthese */
-+ to++;
-+ return Special;
+ }
-+ default:
++ else if (query_position < query_length)
+ {
-+ /* skip symbol */
-+ to++;
-+ return Another;
++ buffer[position++] = query[query_position++];
+ }
-+ };
-+}
-+
-+
-+static bool remove_comments_from_query(const char *const query,
-+ const size_t query_length,
-+ char *const result,
-+ size_t *result_length)
-+{
-+ /* pointer to begin of parsed block */
-+ const char *from= query;
-+ const char *to= query;
-+ /* pointer to end of the query */
-+ const char *const end= query + query_length;
-+ /* pointer to last space */
-+ const char *space= NULL;
-+ /* current position in result buffer */
-+ char *current= result;
-+ while (true)
++ }
++ while((0 < position) && query_strip_comments_is_white_space(buffer[position - 1]))
+ {
-+ from= to;
-+ switch (analyze(&to, end))
-+ {
-+ case Empty:
-+ {
-+ /*
-+ parse completed
-+ check for whitespace in the end
-+ */
-+ if (current == space)
-+ {
-+ /* drop whitespace in the end of query */
-+ --current;
-+ }
-+ /* result is null-terminated string */
-+ *current= 0;
-+ /* set result length */
-+ *result_length= current - result;
-+ /* all right */
-+ return true;
-+ }
-+ case Comment:
-+ /* should just insert space instead of comment */
-+ case WhiteSpace:
-+ if (space == current || from == query)
-+ {
-+ /* previous symbol was space */
-+ }
-+ else
-+ {
-+ /* insert space to result buffer */
-+ *current= ' ';
-+ /* switch after inserted space */
-+ current++;
-+ }
-+ /* remember last-after-space position */
-+ space= current;
-+ /* parse again */
-+ continue;
-+ case Special:
-+ case Another:
-+ {
-+ /* calculate parsed block size */
-+ size_t block_size= to - from;
-+ /* copy parsed block to result */
-+ memcpy(current, from, block_size);
-+ /* switch result after copied block */
-+ current+= block_size;
-+ /* switch after parsed block */
-+ from= to;
-+ /* parse again */
-+ continue;
-+ }
-+ case Error:
-+ default:
-+ {
-+ /* bad source query */
-+ return false;
-+ }
-+ }
++ --position;
+ }
++ buffer[position] = 0;
++ length = position;
+}
-+
-+
-+static size_t skip_not_another(const char *const query, size_t query_length)
++void QueryStripComments::cleanup()
+{
-+ const char *from= query;
-+ const char *to= query;
-+ const char *const end= query + query_length;
-+ while (true)
++ if(buffer)
+ {
-+ switch (analyze(&to, end))
-+ {
-+ case Error:
-+ return 0;
-+ case Empty:
-+ case Another:
-+ return (from - query);
-+ default:
-+ from= to;
-+ continue;
-+ };
++ my_free(buffer);
+ }
++ buffer = 0;
++ length = 0;
++ buffer_length = 0;
+}
-+
-+
-+static size_t skip_default(const char *const query, size_t /* query_length */)
-+{
-+ size_t query_position= 0;
-+ /*
-+ Skip '(' characters in queries like following:
-+ (select a from t1) union (select a from t1);
-+ */
-+ while (query[query_position]=='(')
-+ query_position++;
-+ return query_position;
-+}
-+
-+
-+} /* namespace query_comments_parser */
-+
-+class Query_Switcher
++QueryStripComments_Backup::QueryStripComments_Backup(THD* a_thd,QueryStripComments* qsc)
+{
-+private:
-+ Query_Switcher(const Query_Switcher&);
-+ Query_Switcher& operator=(const Query_Switcher&);
-+
-+
-+public:
-+ Query_Switcher(THD *thd) :
-+ target_query(&(thd_query_string(thd)->str)),
-+ target_length(&(thd_query_string(thd)->length)),
-+ backup_query(thd->query()),
-+ backup_length(thd->query_length())
-+ {
-+ }
-+
-+ Query_Switcher(char **query,
-+ size_t *length) :
-+ target_query(query),
-+ target_length(length),
-+ backup_query(*query),
-+ backup_length(*length)
-+ {
-+ }
-+public:
-+ void replace(Query_Without_Comments *query_without_comments)
++ if(opt_query_cache_strip_comments)
+ {
-+ *target_query= query_without_comments->query();
-+ *target_length= query_without_comments->length();
++ thd = a_thd;
++ query = thd->query();
++ length = thd->query_length();
++ qsc->set(query, length, QUERY_BUFFER_ADDITIONAL_LENGTH(thd->db_length));
++ *(size_t *) (qsc->query() + qsc->query_length() + 1)= thd->db_length;
++ thd->set_query(qsc->query(),qsc->query_length());
+ }
-+ void restore()
++ else
+ {
-+ *target_query= backup_query;
-+ *target_length= backup_length;
++ thd = 0;
++ query = 0;
++ length = 0;
+ }
-+private:
-+ char* *target_query;
-+ size_t *target_length;
-+public:
-+ char *const backup_query;
-+ size_t const backup_length;
-+};
-+
-+class Comments_Processor
++}
++QueryStripComments_Backup::~QueryStripComments_Backup()
+{
-+private:
-+ Comments_Processor(const Comments_Processor&);
-+ Comments_Processor& operator=(const Comments_Processor&);
-+
-+
-+public:
-+ Comments_Processor(THD *thd) :
-+ query_switcher (thd),
-+ db_length (thd->db_length),
-+ query_without_comments(&(thd->query_without_comments)),
-+ enabled (opt_query_cache_strip_comments),
-+ restore (false)
-+ {
-+ }
-+
-+
-+ Comments_Processor(Query_Without_Comments *current_query_without_comments,
-+ char **query,
-+ size_t *length,
-+ const size_t current_db_length) :
-+ query_switcher (query, length),
-+ db_length (current_db_length),
-+ query_without_comments(current_query_without_comments),
-+ enabled (opt_query_cache_strip_comments),
-+ restore (false)
-+ {
-+ }
-+
-+
-+ ~Comments_Processor()
++ if(thd)
+ {
-+ restore_comments();
++ thd->set_query(query,length);
+ }
-+
-+
-+ size_t prefix_length()
-+ {
-+ using query_comments_parser::skip_not_another;
-+ using query_comments_parser::skip_default;
-+ if (enabled)
-+ {
-+ return skip_not_another(query_switcher.backup_query,
-+ query_switcher.backup_length);
-+ }
-+ else
-+ {
-+ return skip_default(query_switcher.backup_query,
-+ query_switcher.backup_length);
-+ }
-+ }
-+
-+
-+ bool remove_comments()
-+ {
-+ if (!enabled || restore)
-+ {
-+ return true;
-+ }
-+ /* Allocate memory for query rewrite */
-+ if (!query_without_comments->allocate(query_switcher.backup_length,
-+ db_length))
-+ {
-+ return false;
-+ }
-+ /* Remove comment from query */
-+ size_t result_length;
-+ using query_comments_parser::remove_comments_from_query;
-+ if (!(restore= remove_comments_from_query(query_switcher.backup_query,
-+ query_switcher.backup_length,
-+ query_without_comments->query(),
-+ &result_length)))
-+ {
-+ return false;
-+ }
-+ query_without_comments->set_length(result_length);
-+ size_t db_length_from_query=
-+ *((size_t*)(query_switcher.backup_query +
-+ query_switcher.backup_length + 1));
-+ *((size_t*)(query_without_comments->query() +
-+ result_length + 1))= db_length_from_query;
-+ /* Replace original query by striped */
-+ query_switcher.replace(query_without_comments);
-+ return restore;
-+ }
-+
-+
-+ void restore_comments()
-+ {
-+ if (enabled && restore)
-+ {
-+ /* Replace striped query by original */
-+ query_switcher.restore();
-+
-+ /* Clean query_without_comments */
-+ query_without_comments->set_length(0);
-+
-+ /* Mark as restored */
-+ restore= false;
-+ }
-+ }
-+private:
-+ Query_Switcher query_switcher;
-+private:
-+ const size_t db_length;
-+private:
-+ Query_Without_Comments *query_without_comments;
-+ bool enabled;
-+ bool restore;
-+};
++}
+
#ifdef EMBEDDED_LIBRARY
#include "emb_qcache.h"
#endif
-@@ -454,7 +944,12 @@
+@@ -454,7 +646,12 @@
Query_cache_wait_state wait_state(thd, __func__, __FILE__, __LINE__);
DBUG_ENTER("Query_cache::try_lock");
-+ const char *old_proc_info= thd->proc_info;
++ const char* old_proc_info= thd->proc_info;
+ thd_proc_info(thd,"Waiting on query cache mutex");
+ DEBUG_SYNC(thd, "before_query_cache_mutex");
mysql_mutex_lock(&structure_guard_mutex);
while (1)
{
if (m_cache_lock_status == Query_cache::UNLOCKED)
-@@ -1274,6 +1769,8 @@
+@@ -1274,6 +1471,8 @@
unlock();
DBUG_VOID_RETURN;
}
-+ Comments_Processor comments_processor(thd);
-+ comments_processor.remove_comments();
++ QueryStripComments *query_strip_comments = &(thd->query_strip_comments);
++ QueryStripComments_Backup backup(thd,query_strip_comments);
/* Key is query + database + flag */
if (thd->db_length)
-@@ -1440,7 +1937,7 @@
- */
-
- int
--Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
-+Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length_uint)
- {
- ulonglong engine_data;
- Query_cache_query *query;
-@@ -1452,6 +1949,11 @@
+@@ -1451,6 +1650,9 @@
+ Query_cache_block_table *block_table, *block_table_end;
ulong tot_length;
Query_cache_query_flags flags;
++ QueryStripComments *query_strip_comments = &(thd->query_strip_comments);
++ char *sql_backup = sql;
++ uint query_length_backup = query_length;
DBUG_ENTER("Query_cache::send_result_to_client");
-+ size_t query_length= query_length_uint;
-+ Comments_Processor comments_processor(&(thd->query_without_comments),
-+ &sql,
-+ &query_length,
-+ thd->db_length);
/*
- Testing 'query_cache_size' without a lock here is safe: the thing
-@@ -1471,13 +1973,7 @@
- }
+@@ -1472,21 +1674,103 @@
{
-- uint i= 0;
+ uint i= 0;
- /*
- Skip '(' characters in queries like following:
- (select a from t1) union (select a from t1);
- */
- while (sql[i]=='(')
- i++;
-+ size_t i= comments_processor.prefix_length();
++ if(opt_query_cache_strip_comments)
++ {
++ /* Skip all comments and non-letter symbols */
++ uint& query_position = i;
++ char* query = sql;
++ while(query_position < query_length)
++ {
++ bool check = false;
++ char current = query[query_position];
++ switch(current)
++ {
++ case '/':
++ if(((query_position + 2) < query_length) && ('*' == query[query_position+1]) && ('!' != query[query_position+2]))
++ {
++ query_position += 2; // skip "/*"
++ do
++ {
++ if('*' == query[query_position] && '/' == query[query_position+1]) // check for "*/" (without space)
++ {
++ query_position += 2; // skip "*/" (without space)
++ break;
++ }
++ else
++ {
++ ++query_position;
++ }
++ }
++ while(query_position < query_length);
++ continue; // analyze current symbol
++ }
++ break;
++ case '-':
++ if(query[query_position+1] == '-')
++ {
++ ++query_position; // skip "-"
++ }
++ else
++ {
++ break;
++ }
++ case '#':
++ do
++ {
++ ++query_position; // skip current symbol
++ if('\n' == query[query_position]) // check for '\n'
++ {
++ ++query_position; // skip '\n'
++ break;
++ }
++ }
++ while(query_position < query_length);
++ continue; // analyze current symbol
++ case '\r':
++ case '\n':
++ case '\t':
++ case ' ':
++ case '(':
++ case ')':
++ break;
++ default:
++ check = true;
++ break; // make gcc happy
++ } // switch(current)
++ if(check)
++ {
++ if(query_position + 2 < query_length)
++ {
++ // cacheable
++ break;
++ }
++ else
++ {
++ DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached"));
++ goto err;
++ }
++ } // if(check)
++ ++query_position;
++ } // while(query_position < query_length)
++ }
++ else // if(opt_query_cache_strip_comments)
++ {
++ /*
++ 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).
++ } // if(opt_query_cache_strip_comments)
++ /*
++ Test if the query is a SELECT
++ (pre-space is removed in dispatch_command).
- /*
- Test if the query is a SELECT
-@@ -1487,10 +1983,11 @@
- 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] != '/')
-+ if (!((i + 2 < query_length) &&
-+ ((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;
-@@ -1543,6 +2040,7 @@
+- First '/' looks like comment before command it is not
+- frequently appeared in real life, consequently we can
+- check all such queries, too.
+- */
++ 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') &&
+@@ -1543,6 +1827,14 @@
goto err_unlock;
Query_cache_block *query_block;
-+ comments_processor.remove_comments();
++ if(opt_query_cache_strip_comments)
++ {
++ query_strip_comments->set(sql, query_length,
++ QUERY_BUFFER_ADDITIONAL_LENGTH(thd->db_length));
++ sql = query_strip_comments->query();
++ query_length = query_strip_comments->query_length();
++ *(size_t *) (sql + query_length + 1)= thd->db_length;
++ }
tot_length= query_length + 1 + sizeof(size_t) +
thd->db_length + QUERY_CACHE_FLAGS_SIZE;
-@@ -1611,6 +2109,7 @@
+@@ -1611,6 +1903,8 @@
(uchar*) &flags, QUERY_CACHE_FLAGS_SIZE);
query_block = (Query_cache_block *) my_hash_search(&queries, (uchar*) sql,
tot_length);
-+ comments_processor.restore_comments();
++ sql = sql_backup;
++ query_length = query_length_backup;
/* Quick abort on unlocked data */
if (query_block == 0 ||
query_block->query()->result() == 0 ||
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
-@@ -1485,6 +1485,74 @@
+@@ -40,6 +40,9 @@
+ #include "thr_lock.h" /* thr_lock_type, THR_LOCK_DATA,
+ THR_LOCK_INFO */
- extern "C" void my_message_sql(uint error, const char *str, myf MyFlags);
++#ifdef HAVE_QUERY_CACHE
++#include "query_strip_comments.h"
++#endif // HAVE_QUERY_CACHE
-+
+ class Reprepare_observer;
+ class Relay_log_info;
+@@ -766,6 +769,9 @@
+ statement lifetime. FIXME: must be const
+ */
+ ulong id;
+#ifdef HAVE_QUERY_CACHE
-+
-+
-+/*
-+ @class Query_Without_Comments
-+ This class provides way for safety (re)allocation
-+ a memory for a query without comments.
-+*/
-+class Query_Without_Comments
-+{
-+private:
-+ /*
-+ Denied copy and assigment for object of this class.
-+ */
-+ Query_Without_Comments(const Query_Without_Comments&);
-+ Query_Without_Comments& operator=(const Query_Without_Comments&);
-+
-+
-+public:
-+ /*
-+ Constructor is filling fields by zero (no allocation).
-+ */
-+ Query_Without_Comments();
-+
-+
-+ /*
-+ Destructor clean allocated memory
-+ */
-+ ~Query_Without_Comments();
-+public:
-+
-+
-+/*
-+ (Re)allocate memory for query. Query length after that is 0.
-+ */
-+ bool allocate(size_t query_length, size_t db_length);
-+
-+
-+ /*
-+ Set result query length, when query
-+ without comments is copied to buffer.
-+ */
-+ void set_length(size_t query_length);
-+
-+
-+public:
-+ /*
-+ Result query.
-+ */
-+ char* query();
-+
-+
-+ /*
-+ Result query length
-+ */
-+ size_t length();
-+
-+
-+private:
-+ char* buffer;
-+ size_t q_length;
-+ size_t b_length;
-+};
-+
-+
-+#endif /* HAVE_QUERY_CACHE */
-+
- /**
- @class THD
- For each client connection we create a separate thread with THD serving as
-@@ -1542,6 +1610,7 @@
- struct st_mysql_stmt *current_stmt;
- #endif
- #ifdef HAVE_QUERY_CACHE
-+ Query_Without_Comments query_without_comments;
- Query_cache_tls query_cache_tls;
- #endif
- NET net; // client connection descriptor
++ QueryStripComments query_strip_comments; // see sql_cache.cc
++#endif //HAVE_QUERY_CACHE
+
+ /*
+ MARK_COLUMNS_NONE: Means mark_used_colums is not set and no indicator to
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
-@@ -1815,6 +1815,11 @@
+@@ -1895,6 +1895,11 @@
NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0),
ON_UPDATE(fix_query_cache_size));
"Don't cache results that are bigger than this",
--- /dev/null
+++ b/mysql-test/include/percona_query_cache_with_comments.inc
-@@ -0,0 +1,117 @@
+@@ -0,0 +1,95 @@
+--source include/percona_query_cache_with_comments_clear.inc
+let $query=/* with comment first */select * from t1;
+eval $query;
+;
+--source include/percona_query_cache_with_comments_eval.inc
+
-+let $query=select */* a comment \*/from t1;
-+--source include/percona_query_cache_with_comments_eval.inc
-+
-+let $query=select *# a comment \\
-+from t1;
-+--source include/percona_query_cache_with_comments_eval.inc
-+
-+let $query=select *-- a comment \\
-+from t1;
-+--source include/percona_query_cache_with_comments_eval.inc
-+
-+let $query=select "\\\\"" /* not a comment */" from t1;
-+--source include/percona_query_cache_with_comments_eval.inc
-+
-+let $query=select "\\\\"" /*! not a comment */" from t1;
-+--source include/percona_query_cache_with_comments_eval.inc
-+
-+# following two queries related to bug #856404.
-+# There are different queries, but opt_query_cache_strip_comments thinks that they are equal.
+let $query=select ' \' ' from t1;
+--source include/percona_query_cache_with_comments_eval.inc
-+
-+let $query=select ' \' /* comment inside quotes with internal backslash quote */' from t1;
-+--source include/percona_query_cache_with_comments_eval.inc
--- /dev/null
+++ b/mysql-test/include/percona_query_cache_with_comments_begin.inc
@@ -0,0 +1,12 @@
+
--- /dev/null
+++ b/mysql-test/r/percona_query_cache_with_comments.result
-@@ -0,0 +1,1058 @@
+@@ -0,0 +1,866 @@
+set global query_cache_strip_comments=ON;
+set GLOBAL query_cache_size=1355776;
+drop table if exists t1;
+2
+3
+select * from t1
-+/* comment in the end */
-+;
-+a
-+1
-+2
-+3
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 1
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 1
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 42
-+-----------------------------------------------------
-+select * from t1 #comment in the end
-+-----------------------------------------------------
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 1
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 1
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 42
-+select * from t1 #comment in the end;
-+a
-+1
-+2
-+3
-+select * from t1 #comment in the end;
-+a
-+1
-+2
-+3
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 1
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 1
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 44
-+-----------------------------------------------------
-+select * from t1 #comment in the end
-+
-+-----------------------------------------------------
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 1
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 1
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 44
-+select * from t1 #comment in the end
-+;
-+a
-+1
-+2
-+3
-+select * from t1 #comment in the end
-+;
-+a
-+1
-+2
-+3
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 1
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 1
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 46
-+-----------------------------------------------------
-+select * from t1 -- comment in the end
-+-----------------------------------------------------
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 1
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 1
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 46
-+select * from t1 -- comment in the end;
-+a
-+1
-+2
-+3
-+select * from t1 -- comment in the end;
-+a
-+1
-+2
-+3
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 1
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 1
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 48
-+-----------------------------------------------------
-+select * from t1 -- comment in the end
-+
-+-----------------------------------------------------
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 1
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 1
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 48
-+select * from t1 -- comment in the end
-+;
-+a
-+1
-+2
-+3
-+select * from t1 -- comment in the end
++/* comment in the end */
+;
+a
+1
+Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
-+Qcache_hits 50
++Qcache_hits 42
+-----------------------------------------------------
-+select */* a comment \*/from t1
++select * from t1 #comment in the end
+-----------------------------------------------------
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
-+Qcache_hits 50
-+select */* a comment \*/from t1;
++Qcache_hits 42
++select * from t1 #comment in the end;
+a
+1
+2
+3
-+select */* a comment \*/from t1;
++select * from t1 #comment in the end;
+a
+1
+2
+Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
-+Qcache_hits 52
++Qcache_hits 44
+-----------------------------------------------------
-+select *# a comment \
-+from t1
++select * from t1 #comment in the end
++
+-----------------------------------------------------
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
-+Qcache_hits 52
-+select *# a comment \
-+from t1;
++Qcache_hits 44
++select * from t1 #comment in the end
++;
+a
+1
+2
+3
-+select *# a comment \
-+from t1;
++select * from t1 #comment in the end
++;
+a
+1
+2
+Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
-+Qcache_hits 54
++Qcache_hits 46
+-----------------------------------------------------
-+select *-- a comment \
-+from t1
++select * from t1 -- comment in the end
+-----------------------------------------------------
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
-+Qcache_hits 54
-+select *-- a comment \
-+from t1;
++Qcache_hits 46
++select * from t1 -- comment in the end;
+a
+1
+2
+3
-+select *-- a comment \
-+from t1;
++select * from t1 -- comment in the end;
+a
+1
+2
+Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
-+Qcache_hits 56
++Qcache_hits 48
+-----------------------------------------------------
-+select "\\"" /* not a comment */" from t1
++select * from t1 -- comment in the end
++
+-----------------------------------------------------
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
-+Qcache_hits 56
-+select "\\"" /* not a comment */" from t1;
-+\" /* not a comment */
-+\" /* not a comment */
-+\" /* not a comment */
-+\" /* not a comment */
-+select "\\"" /* not a comment */" from t1;
-+\" /* not a comment */
-+\" /* not a comment */
-+\" /* not a comment */
-+\" /* not a comment */
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 2
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 2
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 57
-+-----------------------------------------------------
-+select "\\"" /*! not a comment */" from t1
-+-----------------------------------------------------
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 2
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 2
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 57
-+select "\\"" /*! not a comment */" from t1;
-+\" /*! not a comment */
-+\" /*! not a comment */
-+\" /*! not a comment */
-+\" /*! not a comment */
-+select "\\"" /*! not a comment */" from t1;
-+\" /*! not a comment */
-+\" /*! not a comment */
-+\" /*! not a comment */
-+\" /*! not a comment */
++Qcache_hits 48
++select * from t1 -- comment in the end
++;
++a
++1
++2
++3
++select * from t1 -- comment in the end
++;
++a
++1
++2
++3
+show status like "Qcache_queries_in_cache";
+Variable_name Value
-+Qcache_queries_in_cache 3
++Qcache_queries_in_cache 1
+show status like "Qcache_inserts";
+Variable_name Value
-+Qcache_inserts 3
++Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
-+Qcache_hits 58
++Qcache_hits 50
+-----------------------------------------------------
+select ' \' ' from t1
+-----------------------------------------------------
+show status like "Qcache_queries_in_cache";
+Variable_name Value
-+Qcache_queries_in_cache 3
++Qcache_queries_in_cache 1
+show status like "Qcache_inserts";
+Variable_name Value
-+Qcache_inserts 3
++Qcache_inserts 1
+show status like "Qcache_hits";
+Variable_name Value
-+Qcache_hits 58
++Qcache_hits 50
+select ' \' ' from t1;
+'
+ '
+ '
+show status like "Qcache_queries_in_cache";
+Variable_name Value
-+Qcache_queries_in_cache 4
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 4
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 59
-+-----------------------------------------------------
-+select ' \' /* comment inside quotes with internal backslash quote */' from t1
-+-----------------------------------------------------
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 4
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 4
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 59
-+select ' \' /* comment inside quotes with internal backslash quote */' from t1;
-+' /* comment inside quotes with internal backslash quote */
-+ ' /* comment inside quotes with internal backslash quote */
-+ ' /* comment inside quotes with internal backslash quote */
-+ ' /* comment inside quotes with internal backslash quote */
-+select ' \' /* comment inside quotes with internal backslash quote */' from t1;
-+' /* comment inside quotes with internal backslash quote */
-+ ' /* comment inside quotes with internal backslash quote */
-+ ' /* comment inside quotes with internal backslash quote */
-+ ' /* comment inside quotes with internal backslash quote */
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 5
++Qcache_queries_in_cache 2
+show status like "Qcache_inserts";
+Variable_name Value
-+Qcache_inserts 5
++Qcache_inserts 2
+show status like "Qcache_hits";
+Variable_name Value
-+Qcache_hits 60
++Qcache_hits 51
+DROP TABLE t1;
+SET GLOBAL query_cache_size=default;
+set global query_cache_strip_comments=OFF;
+SET GLOBAL query_cache_size= default;
--- /dev/null
+++ b/mysql-test/r/percona_query_cache_with_comments_disable.result
-@@ -0,0 +1,1057 @@
+@@ -0,0 +1,865 @@
+set GLOBAL query_cache_size=1355776;
+drop table if exists t1;
+create table t1 (a int not null);
+Variable_name Value
+Qcache_hits 25
+-----------------------------------------------------
-+select */* a comment \*/from t1
++select ' \' ' from t1
+-----------------------------------------------------
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 25
-+select */* a comment \*/from t1;
-+a
-+1
-+2
-+3
-+select */* a comment \*/from t1;
-+a
-+1
-+2
-+3
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 21
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 21
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 26
-+-----------------------------------------------------
-+select *# a comment \
-+from t1
-+-----------------------------------------------------
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 21
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 21
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 26
-+select *# a comment \
-+from t1;
-+a
-+1
-+2
-+3
-+select *# a comment \
-+from t1;
-+a
-+1
-+2
-+3
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 22
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 22
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 27
-+-----------------------------------------------------
-+select *-- a comment \
-+from t1
-+-----------------------------------------------------
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 22
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 22
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 27
-+select *-- a comment \
-+from t1;
-+a
-+1
-+2
-+3
-+select *-- a comment \
-+from t1;
-+a
-+1
-+2
-+3
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 23
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 23
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 28
-+-----------------------------------------------------
-+select "\\"" /* not a comment */" from t1
-+-----------------------------------------------------
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 23
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 23
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 28
-+select "\\"" /* not a comment */" from t1;
-+\" /* not a comment */
-+\" /* not a comment */
-+\" /* not a comment */
-+\" /* not a comment */
-+select "\\"" /* not a comment */" from t1;
-+\" /* not a comment */
-+\" /* not a comment */
-+\" /* not a comment */
-+\" /* not a comment */
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 24
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 24
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 29
-+-----------------------------------------------------
-+select "\\"" /*! not a comment */" from t1
-+-----------------------------------------------------
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 24
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 24
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 29
-+select "\\"" /*! not a comment */" from t1;
-+\" /*! not a comment */
-+\" /*! not a comment */
-+\" /*! not a comment */
-+\" /*! not a comment */
-+select "\\"" /*! not a comment */" from t1;
-+\" /*! not a comment */
-+\" /*! not a comment */
-+\" /*! not a comment */
-+\" /*! not a comment */
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 25
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 25
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 30
-+-----------------------------------------------------
-+select ' \' ' from t1
-+-----------------------------------------------------
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 25
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 25
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 30
+select ' \' ' from t1;
+'
+ '
+ '
+show status like "Qcache_queries_in_cache";
+Variable_name Value
-+Qcache_queries_in_cache 26
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 26
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 31
-+-----------------------------------------------------
-+select ' \' /* comment inside quotes with internal backslash quote */' from t1
-+-----------------------------------------------------
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 26
-+show status like "Qcache_inserts";
-+Variable_name Value
-+Qcache_inserts 26
-+show status like "Qcache_hits";
-+Variable_name Value
-+Qcache_hits 31
-+select ' \' /* comment inside quotes with internal backslash quote */' from t1;
-+' /* comment inside quotes with internal backslash quote */
-+ ' /* comment inside quotes with internal backslash quote */
-+ ' /* comment inside quotes with internal backslash quote */
-+ ' /* comment inside quotes with internal backslash quote */
-+select ' \' /* comment inside quotes with internal backslash quote */' from t1;
-+' /* comment inside quotes with internal backslash quote */
-+ ' /* comment inside quotes with internal backslash quote */
-+ ' /* comment inside quotes with internal backslash quote */
-+ ' /* comment inside quotes with internal backslash quote */
-+show status like "Qcache_queries_in_cache";
-+Variable_name Value
-+Qcache_queries_in_cache 27
++Qcache_queries_in_cache 21
+show status like "Qcache_inserts";
+Variable_name Value
-+Qcache_inserts 27
++Qcache_inserts 21
+show status like "Qcache_hits";
+Variable_name Value
-+Qcache_hits 32
++Qcache_hits 26
+DROP TABLE t1;
+SET GLOBAL query_cache_size=default;
+set global query_cache_strip_comments=OFF;
+SET GLOBAL query_cache_size=0;
--- a/mysql-test/r/mysqld--help-notwin.result
+++ b/mysql-test/r/mysqld--help-notwin.result
-@@ -493,6 +493,10 @@
+@@ -500,6 +500,10 @@
The minimum size for blocks allocated by the query cache
--query-cache-size=#
The memory allocated to store results from old queries
--query-cache-type=name
OFF = Don't cache or retrieve results. ON = Cache all
results except SELECT SQL_NO_CACHE ... queries. DEMAND =
-@@ -931,6 +935,7 @@
+@@ -942,6 +946,7 @@
query-cache-limit 1048576
query-cache-min-res-unit 4096
query-cache-size 0
+try_lock_mutex_query
+SET GLOBAL query_cache_size=0;
--- /dev/null
-+++ b/mysql-test/r/percona_query_cache_with_comments_crash_2.result
++++ b/mysql-test/r/percona_bug856404.result
@@ -0,0 +1,8 @@
+DROP TABLE IF EXISTS table17_int;
+DROP TABLE IF EXISTS table30_int;
+DROP TABLE table17_int;
+DROP TABLE table30_int;
--- /dev/null
-+++ b/mysql-test/t/percona_query_cache_with_comments_crash_2-master.opt
++++ b/mysql-test/t/percona_bug856404-master.opt
@@ -0,0 +1 @@
+--query-cache-size=10M --query-cache-strip-comments
--- /dev/null
-+++ b/mysql-test/t/percona_query_cache_with_comments_crash_2.test
-@@ -0,0 +1,9 @@
++++ b/mysql-test/t/percona_bug856404.test
+@@ -0,0 +1,15 @@
++########################################################################
++# Bug #856404: Crash when query_cache_strip_comments enabled
++########################################################################
++
+--disable_warnings
+DROP TABLE IF EXISTS table17_int;
+DROP TABLE IF EXISTS table30_int;
+--enable_warnings
++
+CREATE TABLE `table17_int` (pk integer auto_increment primary key, `col_char_10_not_null_key` char(10), `col_enum_not_null_key` int);
+CREATE TABLE `table30_int` (pk integer auto_increment primary key, `col_enum_not_null_key` int);
+SELECT X . `pk` FROM `table17_int` AS X LEFT JOIN `table30_int` AS Y USING ( `col_enum_not_null_key` ) WHERE X . `col_char_10_not_null_key` != ' you need to translate Views labels into other languages, consider installing the <a href=\" !path\">Internationalization</a> package\'s Views translation module.' LIMIT 7 /*Generated by THREAD_ID 1*/;
++
+DROP TABLE table17_int;
+DROP TABLE table30_int;
---- a/sql/sql_class.cc
-+++ b/sql/sql_class.cc
-@@ -807,6 +807,99 @@
- sql_errno == ER_TRG_NO_DEFINER);
- }
-
-+#ifdef HAVE_QUERY_CACHE
-+
-+
-+Query_Without_Comments::Query_Without_Comments() :
-+ buffer(0),
-+ q_length(0),
-+ b_length(0)
-+{
-+}
-+
-+
-+Query_Without_Comments::~Query_Without_Comments()
-+{
-+ if(buffer)
-+ {
-+ my_free(buffer);
-+ }
-+}
-+
-+
-+bool Query_Without_Comments::allocate(size_t query_length, size_t db_length)
-+{
-+ DBUG_ENTER("Query_Without_Comments::allocate");
-+ DBUG_PRINT("info", ("old buffer: %p "
-+ "old query: '%-.4096s' "
-+ "old buffer length: %u "
-+ "old query length: %u",
-+ buffer,
-+ buffer,
-+ (uint) b_length,
-+ (uint) q_length));
-+ /* save maximum query length for check in the set_length */
-+ q_length= query_length;
-+ /* according to sql_parse.cc memory allocation */
-+ size_t new_b_length= (query_length + 1) + sizeof(size_t) + db_length +
-+ QUERY_CACHE_FLAGS_SIZE;
-+ if (b_length < new_b_length)
-+ {
-+ b_length= new_b_length;
-+ if (buffer)
-+ {
-+ buffer= (char*) my_realloc(buffer, b_length, MYF(0));
-+ }
-+ else
-+ {
-+ buffer= (char *) my_malloc(b_length, MYF(0));
-+ }
-+ }
-+ buffer[0]= 0;
-+ DBUG_PRINT("info", ("buffer: %p "
-+ "buffer length: %u "
-+ "query maximum length: %u",
-+ buffer,
-+ (uint) b_length,
-+ (uint) q_length));
-+ DBUG_RETURN(buffer);
-+}
-+
-+
-+void Query_Without_Comments::set_length(size_t query_length)
-+{
-+ DBUG_ENTER("Query_Without_Comments::set_length");
-+ DBUG_ASSERT(query_length <= q_length);
-+ buffer[query_length]= 0;
-+ DBUG_PRINT("info", ("buffer: %p "
-+ "query: '%-.4096s' "
-+ "buffer length: %u "
-+ "query maximum length: %u "
-+ "query length: %u",
-+ buffer,
-+ buffer,
-+ (uint) b_length,
-+ (uint) q_length,
-+ (uint) query_length));
-+ q_length= query_length;
-+ DBUG_VOID_RETURN;
-+}
-+
-+
-+char* Query_Without_Comments::query()
-+{
-+ return buffer;
-+}
-+
-+
-+size_t Query_Without_Comments::length()
-+{
-+ return q_length;
-+}
-+
-+
-+#endif // HAVE_QUERY_CACHE
-+
-
- THD::THD()
- :Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION,