MXS-1337 Move more functions to QcSqliteInfo
This commit is contained in:
@ -175,22 +175,7 @@ static bool parse_query(GWBUF* query, uint32_t collect);
|
||||
static void parse_query_string(const char* query, int len, bool suppress_logging);
|
||||
static bool query_is_parsed(GWBUF* query, uint32_t collect);
|
||||
static bool should_exclude(const char* zName, const ExprList* pExclude);
|
||||
static void update_field_infos_from_expr(QcSqliteInfo* info,
|
||||
QcAliases* pAliases,
|
||||
const Expr* pExpr,
|
||||
uint32_t usage,
|
||||
const ExprList* pExclude);
|
||||
static void update_field_infos(QcSqliteInfo* info,
|
||||
QcAliases* pAliases,
|
||||
int prev_token,
|
||||
const Expr* pExpr,
|
||||
uint32_t usage,
|
||||
qc_token_position_t pos,
|
||||
const ExprList* pExclude);
|
||||
static void update_function_info(QcSqliteInfo* info,
|
||||
const char* name,
|
||||
uint32_t usage);
|
||||
static void update_names_from_srclist(QcSqliteInfo* info, QcAliases* pAliases, const SrcList* pSrc);
|
||||
static const char* get_token_symbol(int token);
|
||||
|
||||
// Defined in parse.y
|
||||
extern "C"
|
||||
@ -765,6 +750,314 @@ public:
|
||||
return truth;
|
||||
}
|
||||
|
||||
void update_field_infos(QcAliases* pAliases,
|
||||
int prev_token,
|
||||
const Expr* pExpr,
|
||||
uint32_t usage,
|
||||
qc_token_position_t pos,
|
||||
const ExprList* pExclude)
|
||||
{
|
||||
const Expr* pLeft = pExpr->pLeft;
|
||||
const Expr* pRight = pExpr->pRight;
|
||||
const char* zToken = pExpr->u.zToken;
|
||||
|
||||
bool ignore_exprlist = false;
|
||||
|
||||
switch (pExpr->op)
|
||||
{
|
||||
case TK_ASTERISK: // select *
|
||||
update_field_infos_from_expr(pAliases, pExpr, usage, pExclude);
|
||||
break;
|
||||
|
||||
case TK_DOT: // select a.b ... select a.b.c
|
||||
update_field_infos_from_expr(pAliases, pExpr, usage, pExclude);
|
||||
break;
|
||||
|
||||
case TK_ID: // select a
|
||||
update_field_infos_from_expr(pAliases, pExpr, usage, pExclude);
|
||||
break;
|
||||
|
||||
case TK_VARIABLE:
|
||||
{
|
||||
if (zToken[0] == '@')
|
||||
{
|
||||
if (zToken[1] == '@')
|
||||
{
|
||||
if ((prev_token == TK_EQ) && (pos == QC_TOKEN_LEFT))
|
||||
{
|
||||
m_type_mask |= QUERY_TYPE_GSYSVAR_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((strcasecmp(&zToken[2], "identity") == 0) ||
|
||||
(strcasecmp(&zToken[2], "last_insert_id") == 0))
|
||||
{
|
||||
m_type_mask |= QUERY_TYPE_MASTER_READ;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_type_mask |= QUERY_TYPE_SYSVAR_READ;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((prev_token == TK_EQ) && (pos == QC_TOKEN_LEFT))
|
||||
{
|
||||
m_type_mask |= QUERY_TYPE_USERVAR_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_type_mask |= QUERY_TYPE_USERVAR_READ;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (zToken[0] != '?')
|
||||
{
|
||||
MXS_WARNING("%s reported as VARIABLE.", zToken);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MXS_DEBUG("Token %d not handled explicitly.", pExpr->op);
|
||||
// Fallthrough intended.
|
||||
case TK_BETWEEN:
|
||||
case TK_CASE:
|
||||
case TK_EXISTS:
|
||||
case TK_FUNCTION:
|
||||
case TK_IN:
|
||||
case TK_SELECT:
|
||||
switch (pExpr->op)
|
||||
{
|
||||
case TK_EQ:
|
||||
// We don't report "=" if it's not used in a specific context (SELECT, WHERE)
|
||||
// and if it is used in SET. We also exclude it it in a context where a
|
||||
// variable is set.
|
||||
if (((usage != 0) && (usage != QC_USED_IN_SET)) &&
|
||||
(!pExpr->pLeft || (pExpr->pLeft->op != TK_VARIABLE)))
|
||||
{
|
||||
update_function_info(get_token_symbol(pExpr->op), usage);
|
||||
}
|
||||
break;
|
||||
|
||||
case TK_GE:
|
||||
case TK_GT:
|
||||
case TK_LE:
|
||||
case TK_LT:
|
||||
case TK_NE:
|
||||
|
||||
case TK_BETWEEN:
|
||||
case TK_BITAND:
|
||||
case TK_BITOR:
|
||||
case TK_CASE:
|
||||
case TK_IN:
|
||||
case TK_ISNULL:
|
||||
case TK_MINUS:
|
||||
case TK_NOTNULL:
|
||||
case TK_PLUS:
|
||||
case TK_SLASH:
|
||||
case TK_STAR:
|
||||
update_function_info(get_token_symbol(pExpr->op), usage);
|
||||
break;
|
||||
|
||||
case TK_REM:
|
||||
if (m_sql_mode == QC_SQL_MODE_ORACLE)
|
||||
{
|
||||
if ((pLeft && (pLeft->op == TK_ID)) &&
|
||||
(pRight && (pRight->op == TK_ID)) &&
|
||||
(strcasecmp(pLeft->u.zToken, "sql") == 0) &&
|
||||
(strcasecmp(pRight->u.zToken, "rowcount") == 0))
|
||||
{
|
||||
char sqlrowcount[13]; // strlen("sql") + strlen("%") + strlen("rowcount") + 1
|
||||
sprintf(sqlrowcount, "%s%%%s", pLeft->u.zToken, pRight->u.zToken);
|
||||
|
||||
update_function_info(sqlrowcount, usage);
|
||||
|
||||
pLeft = NULL;
|
||||
pRight = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
update_function_info(get_token_symbol(pExpr->op), usage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
update_function_info(get_token_symbol(pExpr->op), usage);
|
||||
}
|
||||
break;
|
||||
|
||||
case TK_UMINUS:
|
||||
switch (this_unit.parse_as)
|
||||
{
|
||||
case QC_PARSE_AS_DEFAULT:
|
||||
update_function_info(get_token_symbol(pExpr->op), usage);
|
||||
break;
|
||||
|
||||
case QC_PARSE_AS_103:
|
||||
// In MariaDB 10.3 a unary minus is not considered a function.
|
||||
break;
|
||||
|
||||
default:
|
||||
ss_dassert(!true);
|
||||
}
|
||||
break;
|
||||
|
||||
case TK_FUNCTION:
|
||||
if (zToken)
|
||||
{
|
||||
if (strcasecmp(zToken, "last_insert_id") == 0)
|
||||
{
|
||||
m_type_mask |= (QUERY_TYPE_READ | QUERY_TYPE_MASTER_READ);
|
||||
}
|
||||
else if (is_sequence_related_function(zToken))
|
||||
{
|
||||
m_type_mask |= QUERY_TYPE_WRITE;
|
||||
ignore_exprlist = true;
|
||||
}
|
||||
else if (!is_builtin_readonly_function(zToken,
|
||||
this_thread.version_major,
|
||||
this_thread.version_minor,
|
||||
this_thread.version_patch,
|
||||
m_sql_mode == QC_SQL_MODE_ORACLE))
|
||||
{
|
||||
m_type_mask |= QUERY_TYPE_WRITE;
|
||||
}
|
||||
|
||||
// We exclude "row", because we cannot detect all rows the same
|
||||
// way qc_mysqlembedded does.
|
||||
if (!ignore_exprlist && (strcasecmp(zToken, "row") != 0))
|
||||
{
|
||||
update_function_info(zToken, usage);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (pLeft)
|
||||
{
|
||||
update_field_infos(pAliases, pExpr->op, pExpr->pLeft, usage, QC_TOKEN_LEFT, pExclude);
|
||||
}
|
||||
|
||||
if (pRight)
|
||||
{
|
||||
if (usage & QC_USED_IN_SET)
|
||||
{
|
||||
usage &= ~QC_USED_IN_SET;
|
||||
}
|
||||
|
||||
update_field_infos(pAliases, pExpr->op, pExpr->pRight, usage, QC_TOKEN_RIGHT, pExclude);
|
||||
}
|
||||
|
||||
if (pExpr->x.pList)
|
||||
{
|
||||
switch (pExpr->op)
|
||||
{
|
||||
case TK_BETWEEN:
|
||||
case TK_CASE:
|
||||
case TK_FUNCTION:
|
||||
if (!ignore_exprlist)
|
||||
{
|
||||
update_field_infos_from_exprlist(pAliases, pExpr->x.pList, usage, pExclude);
|
||||
}
|
||||
break;
|
||||
|
||||
case TK_EXISTS:
|
||||
case TK_IN:
|
||||
case TK_SELECT:
|
||||
if (pExpr->flags & EP_xIsSelect)
|
||||
{
|
||||
uint32_t sub_usage = usage;
|
||||
|
||||
sub_usage &= ~QC_USED_IN_SELECT;
|
||||
sub_usage |= QC_USED_IN_SUBSELECT;
|
||||
update_field_infos_from_subselect(pAliases, pExpr->x.pSelect, sub_usage, pExclude);
|
||||
}
|
||||
else
|
||||
{
|
||||
update_field_infos_from_exprlist(pAliases, pExpr->x.pList, usage, pExclude);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void update_field_infos_from_expr(QcAliases* pAliases,
|
||||
const Expr* pExpr,
|
||||
uint32_t usage,
|
||||
const ExprList* pExclude)
|
||||
{
|
||||
QC_FIELD_INFO item = {};
|
||||
|
||||
if (pExpr->op == TK_ASTERISK)
|
||||
{
|
||||
item.column = (char*)"*";
|
||||
}
|
||||
else if (pExpr->op == TK_ID)
|
||||
{
|
||||
// select a from...
|
||||
item.column = pExpr->u.zToken;
|
||||
}
|
||||
else if (pExpr->op == TK_DOT)
|
||||
{
|
||||
if (pExpr->pLeft->op == TK_ID &&
|
||||
(pExpr->pRight->op == TK_ID || pExpr->pRight->op == TK_ASTERISK))
|
||||
{
|
||||
// select a.b from...
|
||||
item.table = pExpr->pLeft->u.zToken;
|
||||
if (pExpr->pRight->op == TK_ID)
|
||||
{
|
||||
item.column = pExpr->pRight->u.zToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.column = (char*)"*";
|
||||
}
|
||||
}
|
||||
else if (pExpr->pLeft->op == TK_ID &&
|
||||
pExpr->pRight->op == TK_DOT &&
|
||||
pExpr->pRight->pLeft->op == TK_ID &&
|
||||
(pExpr->pRight->pRight->op == TK_ID || pExpr->pRight->pRight->op == TK_ASTERISK))
|
||||
{
|
||||
// select a.b.c from...
|
||||
item.database = pExpr->pLeft->u.zToken;
|
||||
item.table = pExpr->pRight->pLeft->u.zToken;
|
||||
if (pExpr->pRight->pRight->op == TK_ID)
|
||||
{
|
||||
item.column = pExpr->pRight->pRight->u.zToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.column = (char*)"*";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.column)
|
||||
{
|
||||
bool should_update = true;
|
||||
|
||||
if ((pExpr->flags & EP_DblQuoted) == 0)
|
||||
{
|
||||
if ((strcasecmp(item.column, "true") == 0) || (strcasecmp(item.column, "false") == 0))
|
||||
{
|
||||
should_update = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (should_update)
|
||||
{
|
||||
update_field_info(pAliases, item.database, item.table, item.column, usage, pExclude);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_field_infos_from_exprlist(QcAliases* pAliases,
|
||||
const ExprList* pEList,
|
||||
uint32_t usage,
|
||||
@ -774,7 +1067,7 @@ public:
|
||||
{
|
||||
ExprList::ExprList_item* pItem = &pEList->a[i];
|
||||
|
||||
update_field_infos(this, pAliases, 0, pItem->pExpr, usage, QC_TOKEN_MIDDLE, pExclude);
|
||||
update_field_infos(pAliases, 0, pItem->pExpr, usage, QC_TOKEN_MIDDLE, pExclude);
|
||||
}
|
||||
}
|
||||
|
||||
@ -838,7 +1131,7 @@ public:
|
||||
if (pSelect->pWhere)
|
||||
{
|
||||
m_has_clause = true;
|
||||
update_field_infos(this, &aliases,
|
||||
update_field_infos(&aliases,
|
||||
0, pSelect->pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, pSelect->pEList);
|
||||
}
|
||||
|
||||
@ -854,7 +1147,7 @@ public:
|
||||
#if defined(COLLECT_HAVING_AS_WELL)
|
||||
// A HAVING clause can only refer to fields that already have been
|
||||
// mentioned. Consequently, they need not be collected.
|
||||
update_field_infos(this, aliases, 0, pSelect->pHaving, 0, QC_TOKEN_MIDDLE, pSelect->pEList);
|
||||
update_field_infos(aliases, 0, pSelect->pHaving, 0, QC_TOKEN_MIDDLE, pSelect->pEList);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -909,6 +1202,71 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void update_function_info(const char* name, uint32_t usage)
|
||||
{
|
||||
ss_dassert(name);
|
||||
|
||||
if (!(m_collect & QC_COLLECT_FUNCTIONS) || (m_collected & QC_COLLECT_FUNCTIONS))
|
||||
{
|
||||
// If function information should not be collected, or if function information
|
||||
// has already been collected, we just return.
|
||||
return;
|
||||
}
|
||||
|
||||
name = map_function_name(m_pFunction_name_mappings, name);
|
||||
|
||||
QC_FUNCTION_INFO item = { (char*)name, usage };
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < m_function_infos_len; ++i)
|
||||
{
|
||||
QC_FUNCTION_INFO* function_info = m_pFunction_infos + i;
|
||||
|
||||
if (strcasecmp(item.name, function_info->name) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QC_FUNCTION_INFO* function_infos = NULL;
|
||||
|
||||
if (i == m_function_infos_len) // If true, the function was not present already.
|
||||
{
|
||||
if (m_function_infos_len < m_function_infos_capacity)
|
||||
{
|
||||
function_infos = m_pFunction_infos;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t capacity = m_function_infos_capacity ? 2 * m_function_infos_capacity : 8;
|
||||
function_infos = (QC_FUNCTION_INFO*)MXS_REALLOC(m_pFunction_infos,
|
||||
capacity * sizeof(QC_FUNCTION_INFO));
|
||||
|
||||
if (function_infos)
|
||||
{
|
||||
m_pFunction_infos = function_infos;
|
||||
m_function_infos_capacity = capacity;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pFunction_infos[i].usage |= usage;
|
||||
}
|
||||
|
||||
// If function_infos is NULL, then the function was found and has already been noted.
|
||||
if (function_infos)
|
||||
{
|
||||
ss_dassert(item.name);
|
||||
item.name = MXS_STRDUP(item.name);
|
||||
|
||||
if (item.name)
|
||||
{
|
||||
function_infos[m_function_infos_len++] = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// sqlite3 callbacks
|
||||
//
|
||||
@ -1136,7 +1494,7 @@ public:
|
||||
|
||||
if (pWhere)
|
||||
{
|
||||
update_field_infos(this, &aliases, 0, pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, 0);
|
||||
update_field_infos(&aliases, 0, pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1359,14 +1717,14 @@ public:
|
||||
{
|
||||
ExprList::ExprList_item* pItem = &pChanges->a[i];
|
||||
|
||||
update_field_infos(this, &aliases,
|
||||
update_field_infos(&aliases,
|
||||
0, pItem->pExpr, QC_USED_IN_SET, QC_TOKEN_MIDDLE, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (pWhere)
|
||||
{
|
||||
update_field_infos(this, &aliases, 0, pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, pChanges);
|
||||
update_field_infos(&aliases, 0, pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, pChanges);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3104,149 +3462,12 @@ static bool should_exclude(const char* zName, const ExprList* pExclude)
|
||||
return i != pExclude->nExpr;
|
||||
}
|
||||
|
||||
static void update_function_info(QcSqliteInfo* info,
|
||||
const char* name,
|
||||
uint32_t usage)
|
||||
{
|
||||
ss_dassert(name);
|
||||
|
||||
if (!(info->m_collect & QC_COLLECT_FUNCTIONS) || (info->m_collected & QC_COLLECT_FUNCTIONS))
|
||||
{
|
||||
// If function information should not be collected, or if function information
|
||||
// has already been collected, we just return.
|
||||
return;
|
||||
}
|
||||
|
||||
name = map_function_name(info->m_pFunction_name_mappings, name);
|
||||
|
||||
QC_FUNCTION_INFO item = { (char*)name, usage };
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < info->m_function_infos_len; ++i)
|
||||
{
|
||||
QC_FUNCTION_INFO* function_info = info->m_pFunction_infos + i;
|
||||
|
||||
if (strcasecmp(item.name, function_info->name) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QC_FUNCTION_INFO* function_infos = NULL;
|
||||
|
||||
if (i == info->m_function_infos_len) // If true, the function was not present already.
|
||||
{
|
||||
if (info->m_function_infos_len < info->m_function_infos_capacity)
|
||||
{
|
||||
function_infos = info->m_pFunction_infos;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t capacity = info->m_function_infos_capacity ? 2 * info->m_function_infos_capacity : 8;
|
||||
function_infos = (QC_FUNCTION_INFO*)MXS_REALLOC(info->m_pFunction_infos,
|
||||
capacity * sizeof(QC_FUNCTION_INFO));
|
||||
|
||||
if (function_infos)
|
||||
{
|
||||
info->m_pFunction_infos = function_infos;
|
||||
info->m_function_infos_capacity = capacity;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info->m_pFunction_infos[i].usage |= usage;
|
||||
}
|
||||
|
||||
// If function_infos is NULL, then the function was found and has already been noted.
|
||||
if (function_infos)
|
||||
{
|
||||
ss_dassert(item.name);
|
||||
item.name = MXS_STRDUP(item.name);
|
||||
|
||||
if (item.name)
|
||||
{
|
||||
function_infos[info->m_function_infos_len++] = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern void maxscale_update_function_info(const char* name, uint32_t usage)
|
||||
{
|
||||
QcSqliteInfo* info = this_thread.pInfo;
|
||||
QcSqliteInfo* pInfo = this_thread.pInfo;
|
||||
ss_dassert(pInfo);
|
||||
|
||||
update_function_info(info, name, usage);
|
||||
}
|
||||
|
||||
static void update_field_infos_from_expr(QcSqliteInfo* info,
|
||||
QcAliases* pAliases,
|
||||
const Expr* pExpr,
|
||||
uint32_t usage,
|
||||
const ExprList* pExclude)
|
||||
{
|
||||
QC_FIELD_INFO item = {};
|
||||
|
||||
if (pExpr->op == TK_ASTERISK)
|
||||
{
|
||||
item.column = (char*)"*";
|
||||
}
|
||||
else if (pExpr->op == TK_ID)
|
||||
{
|
||||
// select a from...
|
||||
item.column = pExpr->u.zToken;
|
||||
}
|
||||
else if (pExpr->op == TK_DOT)
|
||||
{
|
||||
if (pExpr->pLeft->op == TK_ID &&
|
||||
(pExpr->pRight->op == TK_ID || pExpr->pRight->op == TK_ASTERISK))
|
||||
{
|
||||
// select a.b from...
|
||||
item.table = pExpr->pLeft->u.zToken;
|
||||
if (pExpr->pRight->op == TK_ID)
|
||||
{
|
||||
item.column = pExpr->pRight->u.zToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.column = (char*)"*";
|
||||
}
|
||||
}
|
||||
else if (pExpr->pLeft->op == TK_ID &&
|
||||
pExpr->pRight->op == TK_DOT &&
|
||||
pExpr->pRight->pLeft->op == TK_ID &&
|
||||
(pExpr->pRight->pRight->op == TK_ID || pExpr->pRight->pRight->op == TK_ASTERISK))
|
||||
{
|
||||
// select a.b.c from...
|
||||
item.database = pExpr->pLeft->u.zToken;
|
||||
item.table = pExpr->pRight->pLeft->u.zToken;
|
||||
if (pExpr->pRight->pRight->op == TK_ID)
|
||||
{
|
||||
item.column = pExpr->pRight->pRight->u.zToken;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.column = (char*)"*";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.column)
|
||||
{
|
||||
bool should_update = true;
|
||||
|
||||
if ((pExpr->flags & EP_DblQuoted) == 0)
|
||||
{
|
||||
if ((strcasecmp(item.column, "true") == 0) || (strcasecmp(item.column, "false") == 0))
|
||||
{
|
||||
should_update = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (should_update)
|
||||
{
|
||||
info->update_field_info(pAliases, item.database, item.table, item.column, usage, pExclude);
|
||||
}
|
||||
}
|
||||
pInfo->update_function_info(name, usage);
|
||||
}
|
||||
|
||||
static const char* get_token_symbol(int token)
|
||||
@ -3317,245 +3538,6 @@ static const char* get_token_symbol(int token)
|
||||
}
|
||||
}
|
||||
|
||||
static void update_field_infos(QcSqliteInfo* info,
|
||||
QcAliases* pAliases,
|
||||
int prev_token,
|
||||
const Expr* pExpr,
|
||||
uint32_t usage,
|
||||
qc_token_position_t pos,
|
||||
const ExprList* pExclude)
|
||||
{
|
||||
const Expr* pLeft = pExpr->pLeft;
|
||||
const Expr* pRight = pExpr->pRight;
|
||||
const char* zToken = pExpr->u.zToken;
|
||||
|
||||
bool ignore_exprlist = false;
|
||||
|
||||
switch (pExpr->op)
|
||||
{
|
||||
case TK_ASTERISK: // select *
|
||||
update_field_infos_from_expr(info, pAliases, pExpr, usage, pExclude);
|
||||
break;
|
||||
|
||||
case TK_DOT: // select a.b ... select a.b.c
|
||||
update_field_infos_from_expr(info, pAliases, pExpr, usage, pExclude);
|
||||
break;
|
||||
|
||||
case TK_ID: // select a
|
||||
update_field_infos_from_expr(info, pAliases, pExpr, usage, pExclude);
|
||||
break;
|
||||
|
||||
case TK_VARIABLE:
|
||||
{
|
||||
if (zToken[0] == '@')
|
||||
{
|
||||
if (zToken[1] == '@')
|
||||
{
|
||||
if ((prev_token == TK_EQ) && (pos == QC_TOKEN_LEFT))
|
||||
{
|
||||
info->m_type_mask |= QUERY_TYPE_GSYSVAR_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((strcasecmp(&zToken[2], "identity") == 0) ||
|
||||
(strcasecmp(&zToken[2], "last_insert_id") == 0))
|
||||
{
|
||||
info->m_type_mask |= QUERY_TYPE_MASTER_READ;
|
||||
}
|
||||
else
|
||||
{
|
||||
info->m_type_mask |= QUERY_TYPE_SYSVAR_READ;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((prev_token == TK_EQ) && (pos == QC_TOKEN_LEFT))
|
||||
{
|
||||
info->m_type_mask |= QUERY_TYPE_USERVAR_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
info->m_type_mask |= QUERY_TYPE_USERVAR_READ;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (zToken[0] != '?')
|
||||
{
|
||||
MXS_WARNING("%s reported as VARIABLE.", zToken);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MXS_DEBUG("Token %d not handled explicitly.", pExpr->op);
|
||||
// Fallthrough intended.
|
||||
case TK_BETWEEN:
|
||||
case TK_CASE:
|
||||
case TK_EXISTS:
|
||||
case TK_FUNCTION:
|
||||
case TK_IN:
|
||||
case TK_SELECT:
|
||||
switch (pExpr->op)
|
||||
{
|
||||
case TK_EQ:
|
||||
// We don't report "=" if it's not used in a specific context (SELECT, WHERE)
|
||||
// and if it is used in SET. We also exclude it it in a context where a
|
||||
// variable is set.
|
||||
if (((usage != 0) && (usage != QC_USED_IN_SET)) &&
|
||||
(!pExpr->pLeft || (pExpr->pLeft->op != TK_VARIABLE)))
|
||||
{
|
||||
update_function_info(info, get_token_symbol(pExpr->op), usage);
|
||||
}
|
||||
break;
|
||||
|
||||
case TK_GE:
|
||||
case TK_GT:
|
||||
case TK_LE:
|
||||
case TK_LT:
|
||||
case TK_NE:
|
||||
|
||||
case TK_BETWEEN:
|
||||
case TK_BITAND:
|
||||
case TK_BITOR:
|
||||
case TK_CASE:
|
||||
case TK_IN:
|
||||
case TK_ISNULL:
|
||||
case TK_MINUS:
|
||||
case TK_NOTNULL:
|
||||
case TK_PLUS:
|
||||
case TK_SLASH:
|
||||
case TK_STAR:
|
||||
update_function_info(info, get_token_symbol(pExpr->op), usage);
|
||||
break;
|
||||
|
||||
case TK_REM:
|
||||
if (info->m_sql_mode == QC_SQL_MODE_ORACLE)
|
||||
{
|
||||
if ((pLeft && (pLeft->op == TK_ID)) &&
|
||||
(pRight && (pRight->op == TK_ID)) &&
|
||||
(strcasecmp(pLeft->u.zToken, "sql") == 0) &&
|
||||
(strcasecmp(pRight->u.zToken, "rowcount") == 0))
|
||||
{
|
||||
char sqlrowcount[13]; // strlen("sql") + strlen("%") + strlen("rowcount") + 1
|
||||
sprintf(sqlrowcount, "%s%%%s", pLeft->u.zToken, pRight->u.zToken);
|
||||
|
||||
update_function_info(info, sqlrowcount, usage);
|
||||
|
||||
pLeft = NULL;
|
||||
pRight = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
update_function_info(info, get_token_symbol(pExpr->op), usage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
update_function_info(info, get_token_symbol(pExpr->op), usage);
|
||||
}
|
||||
break;
|
||||
|
||||
case TK_UMINUS:
|
||||
switch (this_unit.parse_as)
|
||||
{
|
||||
case QC_PARSE_AS_DEFAULT:
|
||||
update_function_info(info, get_token_symbol(pExpr->op), usage);
|
||||
break;
|
||||
|
||||
case QC_PARSE_AS_103:
|
||||
// In MariaDB 10.3 a unary minus is not considered a function.
|
||||
break;
|
||||
|
||||
default:
|
||||
ss_dassert(!true);
|
||||
}
|
||||
break;
|
||||
|
||||
case TK_FUNCTION:
|
||||
if (zToken)
|
||||
{
|
||||
if (strcasecmp(zToken, "last_insert_id") == 0)
|
||||
{
|
||||
info->m_type_mask |= (QUERY_TYPE_READ | QUERY_TYPE_MASTER_READ);
|
||||
}
|
||||
else if (info->is_sequence_related_function(zToken))
|
||||
{
|
||||
info->m_type_mask |= QUERY_TYPE_WRITE;
|
||||
ignore_exprlist = true;
|
||||
}
|
||||
else if (!is_builtin_readonly_function(zToken,
|
||||
this_thread.version_major,
|
||||
this_thread.version_minor,
|
||||
this_thread.version_patch,
|
||||
info->m_sql_mode == QC_SQL_MODE_ORACLE))
|
||||
{
|
||||
info->m_type_mask |= QUERY_TYPE_WRITE;
|
||||
}
|
||||
|
||||
// We exclude "row", because we cannot detect all rows the same
|
||||
// way qc_mysqlembedded does.
|
||||
if (!ignore_exprlist && (strcasecmp(zToken, "row") != 0))
|
||||
{
|
||||
update_function_info(info, zToken, usage);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (pLeft)
|
||||
{
|
||||
update_field_infos(info, pAliases, pExpr->op, pExpr->pLeft, usage, QC_TOKEN_LEFT, pExclude);
|
||||
}
|
||||
|
||||
if (pRight)
|
||||
{
|
||||
if (usage & QC_USED_IN_SET)
|
||||
{
|
||||
usage &= ~QC_USED_IN_SET;
|
||||
}
|
||||
|
||||
update_field_infos(info, pAliases, pExpr->op, pExpr->pRight, usage, QC_TOKEN_RIGHT, pExclude);
|
||||
}
|
||||
|
||||
if (pExpr->x.pList)
|
||||
{
|
||||
switch (pExpr->op)
|
||||
{
|
||||
case TK_BETWEEN:
|
||||
case TK_CASE:
|
||||
case TK_FUNCTION:
|
||||
if (!ignore_exprlist)
|
||||
{
|
||||
info->update_field_infos_from_exprlist(pAliases, pExpr->x.pList, usage, pExclude);
|
||||
}
|
||||
break;
|
||||
|
||||
case TK_EXISTS:
|
||||
case TK_IN:
|
||||
case TK_SELECT:
|
||||
if (pExpr->flags & EP_xIsSelect)
|
||||
{
|
||||
uint32_t sub_usage = usage;
|
||||
|
||||
sub_usage &= ~QC_USED_IN_SELECT;
|
||||
sub_usage |= QC_USED_IN_SUBSELECT;
|
||||
info->update_field_infos_from_subselect(pAliases, pExpr->x.pSelect, sub_usage, pExclude);
|
||||
}
|
||||
else
|
||||
{
|
||||
info->update_field_infos_from_exprlist(pAliases, pExpr->x.pList, usage, pExclude);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* SQLITE
|
||||
|
Reference in New Issue
Block a user