summaryrefslogtreecommitdiff
path: root/sql/sql_cache.cc
diff options
context:
space:
mode:
authorunknown <sanja@askmonty.org>2011-05-18 16:27:19 +0300
committerunknown <sanja@askmonty.org>2011-05-18 16:27:19 +0300
commit1177bea2f1cd0c9f887670b65c9a403a26f07d76 (patch)
tree86c61cd5b446c4e6a3fc89470b3e35e806d99924 /sql/sql_cache.cc
parent0da2df248ba5915630d04e7b7335bea409670cd6 (diff)
downloadmariadb-git-1177bea2f1cd0c9f887670b65c9a403a26f07d76.tar.gz
Rewritten patch of percona - switching query cache on and off, removing comments.
client/mysqltest.cc: Column names. mysql-test/r/grant_cache_no_prot.result: fix of text. mysql-test/r/grant_cache_ps_prot.result: Fix of test. mysql-test/r/query_cache.result: Switching on and off query cache. mysql-test/t/query_cache.test: Switching on and off query cache. mysys/charset.c: Fix of parser. sql/handler.cc: thd added to parameters. sql/log_event.cc: thd added to parameters. sql/log_event_old.cc: thd added to parameters. sql/mysql_priv.h: Fixed functions definitions. sql/mysqld.cc: Comments stripping. sql/set_var.cc: Switching on and off query cache. sql/set_var.h: Switching on and off query cache. sql/share/errmsg.txt: New errors. sql/sql_cache.cc: Switching query cache on and off, removing comments. sql/sql_cache.h: thd added to parameters. sql/sql_class.h: Comments stripping. sql/sql_db.cc: thd added to parameters. sql/sql_lex.cc: lex fixed. sql/sql_parse.cc: thd added to parameters.
Diffstat (limited to 'sql/sql_cache.cc')
-rw-r--r--sql/sql_cache.cc537
1 files changed, 425 insertions, 112 deletions
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index 8c68f4c44bc..2b48a0fc08d 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -286,6 +286,7 @@ functions:
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 @@ TODO list:
#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 @@ TYPELIB query_cache_type_typelib=
};
+/*
+ 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,31 +564,47 @@ TYPELIB query_cache_type_typelib=
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;
+ bool interrupt= TRUE;
+ const char* old_proc_info;
DBUG_ENTER("Query_cache::try_lock");
+ 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);
+ });
+ if (m_cache_status == DISABLED)
+ {
+ pthread_mutex_unlock(&structure_guard_mutex);
+ thd->proc_info= old_proc_info;
+ DBUG_RETURN(TRUE);
+ }
+ 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
+ interrupt= FALSE;
break;
}
else if (m_cache_lock_status == Query_cache::LOCKED_NO_WAIT)
@@ -464,7 +613,6 @@ bool Query_cache::try_lock(bool use_timeout)
If query cache is protected by a LOCKED_NO_WAIT lock this thread
should avoid using the query cache as it is being evicted.
*/
- interrupt= TRUE;
break;
}
else
@@ -474,25 +622,36 @@ bool Query_cache::try_lock(bool use_timeout)
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 */
int res= pthread_cond_timedwait(&COND_cache_status_changed,
&structure_guard_mutex,&waittime);
if (res == ETIMEDOUT)
- {
- interrupt= TRUE;
break;
- }
}
else
{
- pthread_cond_wait(&COND_cache_status_changed, &structure_guard_mutex);
+ /**
+ If we are here, then mode is == TRY and there was someone else using
+ the query cache. (m_cache_lock_status != Query_cache::UNLOCKED).
+ Signal that we didn't get a lock.
+ */
+ DBUG_ASSERT(m_requests_in_progress > 1);
+ DBUG_ASSERT(mode == TRY);
+ break;
}
}
}
+ if (interrupt)
+ m_requests_in_progress--;
pthread_mutex_unlock(&structure_guard_mutex);
+ thd->proc_info = old_proc_info;
DBUG_RETURN(interrupt);
}
@@ -514,10 +673,12 @@ void Query_cache::lock_and_suspend(void)
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 +698,18 @@ void Query_cache::lock_and_suspend(void)
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 +726,7 @@ void Query_cache::unlock(void)
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 +736,14 @@ void Query_cache::unlock(void)
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 +760,24 @@ void Query_cache::unlock(void)
@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 +1035,23 @@ void query_cache_insert(NET *net, const char *packet, ulong length)
{
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())
+ /* First we check if query cache is disable without doing a mutex lock */
+ if(query_cache.is_disabled())
+ DBUG_VOID_RETURN;
+
+ /*
+ Lock the cache with try_lock(). try_lock() will fail if
+ cache was disabled between the above test and lock.
+ */
+ if (query_cache.try_lock(current_thd, Query_cache::WAIT))
DBUG_VOID_RETURN;
Query_cache_block *query_block= (Query_cache_block*)net->query_cache_query;
@@ -931,7 +1109,7 @@ void query_cache_abort(NET *net)
if (net->query_cache_query == 0)
DBUG_VOID_RETURN;
- if (query_cache.try_lock())
+ if (query_cache.try_lock(current_thd, Query_cache::WAIT))
{
net->query_cache_query = 0;
DBUG_VOID_RETURN;
@@ -984,7 +1162,7 @@ void query_cache_end_of_result(THD *thd)
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 +1251,7 @@ Query_cache::Query_cache(ulong query_cache_limit_arg,
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 +1275,12 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
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 +1313,17 @@ ulong Query_cache::resize(ulong query_cache_size_arg)
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 +1342,9 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
{
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 +1354,23 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
See also a note on double-check locking usage above.
*/
- if (thd->locked_tables || query_cache_size == 0)
+ 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;
- uint8 tables_type= 0;
+ }
+
+ /* 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());
- if ((local_tables= is_cacheable(thd, thd->query_length(),
- thd->query(), thd->lex, tables_used,
+ tables_type= 0;
+ if ((local_tables= is_cacheable(thd, thd->lex, tables_used,
&tables_type)))
{
NET *net= &thd->net;
@@ -1239,7 +1447,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
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 +1463,13 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
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 +1477,24 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
{
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 +1551,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
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 +1626,7 @@ send_data_in_chunks(NET *net, const uchar *packet, ulong len)
*/
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 +1637,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
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 +1647,96 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
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)
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;
- if (!thd->lex->safe_to_cache_query)
+ /*
+ 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", ("SELECT is non-cacheable"));
+ DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached"));
goto err;
}
+ if ((sql_end - sql) > 20 && has_no_cache_directive(sql+6))
{
- 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.
+ We do not increase 'refused' statistics here since it will be done
+ later when the query is parsed.
*/
- 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;
- }
+ DBUG_PRINT("qcache", ("The statement has a SQL_NO_CACHE directive"));
+ goto err;
}
/*
@@ -1488,9 +1744,9 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
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 +1759,23 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
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 +1906,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
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 +1922,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
("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 +1931,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
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 +1955,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
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 +2006,15 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d",
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 +2026,8 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
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 +2055,12 @@ void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used,
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 +2083,13 @@ void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used)
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 +2110,9 @@ void Query_cache::invalidate(THD *thd, TABLE *table,
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 +2129,8 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
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 +2147,19 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length,
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 +2237,9 @@ void Query_cache::invalidate_by_MyISAM_filename(const char *filename)
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"););
@@ -1981,7 +2267,7 @@ void Query_cache::flush()
*/
-void Query_cache::pack(ulong join_limit, uint iteration_limit)
+void Query_cache::pack(THD *thd, ulong join_limit, uint iteration_limit)
{
DBUG_ENTER("Query_cache::pack");
@@ -1989,7 +2275,7 @@ void Query_cache::pack(ulong join_limit, uint iteration_limit)
If the entire qc is being invalidated we can bail out early
instead of waiting for the lock.
*/
- if (try_lock())
+ if (try_lock(thd, Query_cache::WAIT))
DBUG_VOID_RETURN;
if (query_cache_size == 0)
@@ -2026,11 +2312,25 @@ void Query_cache::destroy()
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(current_thd, TRY))
+ unlock();
+}
+
+
/*****************************************************************************
init/destroy
*****************************************************************************/
@@ -2041,7 +2341,19 @@ void Query_cache::init()
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;
}
@@ -2747,7 +3059,7 @@ void Query_cache::invalidate_table(THD *thd, uchar * key, uint32 key_length)
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"); );
@@ -3528,7 +3840,7 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
{
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
@@ -3593,13 +3905,13 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used,
*/
TABLE_COUNTER_TYPE
-Query_cache::is_cacheable(THD *thd, uint32 query_len, char *query, LEX *lex,
+Query_cache::is_cacheable(THD *thd, 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))))
@@ -3664,7 +3976,7 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
{
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);
}
}
@@ -4757,3 +5069,4 @@ err2:
#endif /* DBUG_OFF */
#endif /*HAVE_QUERY_CACHE*/
+