MXS-1150: Do not cache non-cachable functions

The resultset of SELECTs that use functions whose result will
always vary or whose result depend upon the user executing the
query should not be cached. The list of functions is the same
as that specified for the query cache of MariaDB:
https://mariadb.com/kb/en/mariadb/query-cache/
This commit is contained in:
Johan Wikman
2017-03-02 12:36:38 +02:00
parent 923ed9329d
commit da5edadcda
2 changed files with 122 additions and 4 deletions

View File

@ -3,12 +3,19 @@
This filter was introduced in MariaDB MaxScale 2.1.
## Overview
_Note that the cache is still experimental and that non-backward compatible
changes may be made._
The cache filter is a simple cache that is capable of caching the result of
SELECTs, so that subsequent identical SELECTs are served directly by MaxScale,
without the queries being routed to any server.
_Note that the cache is still experimental and that non-backward compatible
changes may be made._
SELECTs using the following functions will not be cached: `BENCHMARK`,
`CONNECTION_ID`, `CONVERT_TZ`, `CURDATE`, `CURRENT_DATE`, `CURRENT_TIMESTAMP`,
`CURTIME`, `DATABASE`, `ENCRYPT`, `FOUND_ROWS`, `GET_LOCK`, `IS_FREE_LOCK`,
`IS_USED_LOCK`, `LAST_INSERT_ID`, `LOAD_FILE`, `LOCALTIME`, `LOCALTIMESTAMP`,
`MASTER_POS_WAIT`, `NOW`, `RAND`, `RELEASE_LOCK`, `SESSION_USER`, `SLEEP`,
`SYSDATE`, `SYSTEM_USER`, `UNIX_TIMESTAMP`, `USER`, `UUID`, `UUID_SHORT`.
Note that installing the cache causes all statements to be parsed. The
implication of that is that unless statements _already_ need to be parsed,
@ -28,7 +35,7 @@ Resultsets of prepared statements are **not** cached.
### Transactions
The cache will be used and populated in the following circumstances:
* There is _no_ on-going transaction, that is, _autocommit_ is used,
* There is _no_ explicit transaction active, that is, _autocommit_ is used,
* there is an _explicitly_ read-only transaction (that is,`START TRANSACTION
READ ONLY`) active, or
* there is a transaction active and _no_ statement that modify the database
@ -44,7 +51,6 @@ If user or system variables are used in the _SELECT_ statement, the result
will not be cached.
### Security
The cache is **not** aware of grants.
The implication is that unless the cache has been explicitly configured

View File

@ -35,6 +35,108 @@ inline bool cache_max_resultset_size_exceeded(const CACHE_CONFIG& config, uint64
}
namespace
{
const char* NON_CACHEABLE_FUNCTIONS[] =
{
"benchmark",
"connection_id",
"convert_tz",
"curdate",
"current_date",
"current_timestamp",
"curtime",
"database",
"encrypt",
"found_rows",
"get_lock",
"is_free_lock",
"is_used_lock",
"last_insert_id",
"load_file",
"localtime",
"localtimestamp",
"master_pos_wait",
"now",
"rand",
"release_lock",
"session_user",
"sleep",
"sysdate",
"system_user",
"unix_timestamp",
"user",
"uuid",
"uuid_short",
};
const char* NON_CACHEABLE_VARIABLES[] =
{
"current_date",
"current_timestamp",
"localtime",
"localtimestamp",
};
const size_t N_NON_CACHEABLE_FUNCTIONS = sizeof(NON_CACHEABLE_FUNCTIONS)/sizeof(NON_CACHEABLE_FUNCTIONS[0]);
const size_t N_NON_CACHEABLE_VARIABLES = sizeof(NON_CACHEABLE_VARIABLES)/sizeof(NON_CACHEABLE_VARIABLES[0]);
int compare_name(const void* pLeft, const void* pRight)
{
return strcasecmp((const char*)pLeft, *(const char**)pRight);
}
inline bool uses_name(const char* zName, const char** pzNames, size_t nNames)
{
return bsearch(zName, pzNames, nNames, sizeof(const char*), compare_name) != NULL;
}
bool uses_non_cacheable_function(GWBUF* pPacket)
{
bool rv = false;
const QC_FUNCTION_INFO* pInfo;
size_t nInfos;
qc_get_function_info(pPacket, &pInfo, &nInfos);
const QC_FUNCTION_INFO* pEnd = pInfo + nInfos;
while (!rv && (pInfo != pEnd))
{
rv = uses_name(pInfo->name, NON_CACHEABLE_FUNCTIONS, N_NON_CACHEABLE_FUNCTIONS);
++pInfo;
}
return rv;
}
bool uses_non_cacheable_variable(GWBUF* pPacket)
{
bool rv = false;
const QC_FIELD_INFO* pInfo;
size_t nInfos;
qc_get_field_info(pPacket, &pInfo, &nInfos);
const QC_FIELD_INFO* pEnd = pInfo + nInfos;
while (!rv && (pInfo != pEnd))
{
rv = uses_name(pInfo->column, NON_CACHEABLE_VARIABLES, N_NON_CACHEABLE_VARIABLES);
++pInfo;
}
return rv;
}
}
CacheFilterSession::CacheFilterSession(MXS_SESSION* pSession, Cache* pCache, char* zDefaultDb)
: maxscale::FilterSession(pSession)
, m_state(CACHE_EXPECTING_NOTHING)
@ -719,6 +821,16 @@ bool CacheFilterSession::should_consult_cache(GWBUF* pPacket)
consult_cache = false;
zReason = "system variables are read";
}
else if (uses_non_cacheable_function(pPacket))
{
consult_cache = false;
zReason = "uses non-cacheable function";
}
else if (uses_non_cacheable_variable(pPacket))
{
consult_cache = false;
zReason = "uses non-cacheable variable";
}
}
else
{