qc: Provide information about field usage

Together with the field names, now qc_get_field_info also returns
field usage information, that is, in what context a field is used.
This allows, for instance, the cache to take action if a a particular
field is selected (SELECT a FROM ...), but not if it is used in a
GROUP BY clause (...GROUP BY a).

This caused a significant modifications of qc_mysqlembedded that
earlier did not walk the parse-tree, but instead looped over of a
list of st_select_lex instances that, the name notwithstanding,
also contain information about other things but SELECTs. The former
approach lost all contextual information, so it was not possible
to know where a particular field was used.

Now the parse tree is walked, which means that the contextual
information is known, and thus the field usage can be updated.
This commit is contained in:
Johan Wikman 2016-11-08 18:05:00 +02:00
parent d7c476314e
commit 15e9652c46
6 changed files with 581 additions and 231 deletions

View File

@ -86,17 +86,37 @@ typedef enum qc_parse_result
QC_QUERY_PARSED = 3 /*< The query was fully parsed; completely classified. */
} qc_parse_result_t;
/**
* qc_field_usage_t defines where a particular field appears.
*
* QC_USED_IN_SELECT : The field appears on the left side of FROM in a top-level SELECT statement.
* QC_USED_IN_SUBSELECT: The field appears on the left side of FROM in a sub-select SELECT statement.
* QC_USED_IN_WHERE : The field appears in a WHERE clause.
* QC_USED_IN_SET : The field appears in the SET clause of an UPDATE statement.
* QC_USED_IN_GROUP_BY : The field appears in a GROUP BY clause.
*
* Note that multiple bits may be set at the same time. For instance, for a statement like
* "SELECT fld FROM tbl WHERE fld = 1 GROUP BY fld", the bits QC_USED_IN_SELECT, QC_USED_IN_WHERE
* and QC_USED_IN_GROUP_BY will be set.
*/
typedef enum qc_field_usage
{
QC_USED_IN_SELECT = 0x01, /*< SELECT fld FROM... */
QC_USED_IN_SUBSELECT = 0x02, /*< SELECT 1 FROM ... SELECT fld ... */
QC_USED_IN_WHERE = 0x04, /*< SELECT ... FROM ... WHERE fld = ... */
QC_USED_IN_SET = 0x08, /*< UPDATE ... SET fld = ... */
QC_USED_IN_GROUP_BY = 0x10, /*< ... GROUP BY fld */
} qc_field_usage_t;
/**
* QC_FIELD_INFO contains information about a field used in a statement.
*/
typedef struct qc_field_info
{
char* database; /** Present if the field is of the form "a.b.c", NULL otherwise. */
char* table; /** Present if the field is of the form "a.b", NULL otherwise. */
char* column; /** Always present. */
// TODO: Possibly add bits telling where the field is used; e.g. in the select
// TODO: part or the where part, or both.
char* database; /** Present if the field is of the form "a.b.c", NULL otherwise. */
char* table; /** Present if the field is of the form "a.b", NULL otherwise. */
char* column; /** Always present. */
uint32_t usage; /** Bitfield denoting where the column appears. */
} QC_FIELD_INFO;
/**
@ -224,6 +244,25 @@ void qc_thread_end(void);
*/
qc_parse_result_t qc_parse(GWBUF* stmt);
/**
* Convert a qc_field_usage_t enum to corresponding string.
*
* @param usage The value to be converted
*
* @return The corresponding string. Must @b not be freed.
*/
const char* qc_field_usage_to_string(qc_field_usage_t usage);
/**
* Convert a mask of qc_field_usage_t enum values to corresponding string.
*
* @param usage_mask Mask of qc_field_usage_t values.
*
* @return The corresponding string, or NULL if memory allocation fails.
* @b Must be freed by the caller.
*/
char* qc_field_usage_mask_to_string(uint32_t usage_mask);
/**
* Returns information about affected fields.
*

View File

@ -1767,20 +1767,12 @@ static void add_field_info(parsing_info_t* info,
const char* database,
const char* table,
const char* column,
uint32_t usage,
List<Item>* excludep)
{
ss_dassert(column);
// If only a column is specified, but not a table or database and we
// have a list of expressions that should be excluded, we check if the column
// value is present in that list. This is in order to exclude the second "d" in
// a statement like "select a as d from x where d = 2".
if (column && !table && !database && excludep && should_exclude(column, excludep))
{
return;
}
QC_FIELD_INFO item = { (char*)database, (char*)table, (char*)column };
QC_FIELD_INFO item = { (char*)database, (char*)table, (char*)column, usage };
size_t i;
for (i = 0; i < info->field_infos_len; ++i)
@ -1814,22 +1806,33 @@ static void add_field_info(parsing_info_t* info,
if (i == info->field_infos_len) // If true, the field was not present already.
{
if (info->field_infos_len < info->field_infos_capacity)
// If only a column is specified, but not a table or database and we
// have a list of expressions that should be excluded, we check if the column
// value is present in that list. This is in order to exclude the second "d" in
// a statement like "select a as d from x where d = 2".
if (!(column && !table && !database && excludep && should_exclude(column, excludep)))
{
field_infos = info->field_infos;
}
else
{
size_t capacity = info->field_infos_capacity ? 2 * info->field_infos_capacity : 8;
field_infos = (QC_FIELD_INFO*)realloc(info->field_infos, capacity * sizeof(QC_FIELD_INFO));
if (field_infos)
if (info->field_infos_len < info->field_infos_capacity)
{
info->field_infos = field_infos;
info->field_infos_capacity = capacity;
field_infos = info->field_infos;
}
else
{
size_t capacity = info->field_infos_capacity ? 2 * info->field_infos_capacity : 8;
field_infos = (QC_FIELD_INFO*)realloc(info->field_infos, capacity * sizeof(QC_FIELD_INFO));
if (field_infos)
{
info->field_infos = field_infos;
info->field_infos_capacity = capacity;
}
}
}
}
else
{
info->field_infos[i].usage |= usage;
}
// If field_infos is NULL, then the field was found and has already been noted.
if (field_infos)
@ -1848,7 +1851,7 @@ static void add_field_info(parsing_info_t* info,
}
}
static void add_field_info(parsing_info_t* pi, Item_field* item, List<Item>* excludep)
static void add_field_info(parsing_info_t* pi, Item_field* item, uint32_t usage, List<Item>* excludep)
{
const char* database = item->db_name;
const char* table = item->table_name;
@ -1934,16 +1937,16 @@ static void add_field_info(parsing_info_t* pi, Item_field* item, List<Item>* exc
break;
}
add_field_info(pi, database, table, column, excludep);
add_field_info(pi, database, table, column, usage, excludep);
}
static void add_field_info(parsing_info_t* pi, Item* item, List<Item>* excludep)
static void add_field_info(parsing_info_t* pi, Item* item, uint32_t usage, List<Item>* excludep)
{
const char* database = NULL;
const char* table = NULL;
const char* column = item->name;
add_field_info(pi, database, table, column, excludep);
add_field_info(pi, database, table, column, usage, excludep);
}
typedef enum collect_source
@ -1954,9 +1957,16 @@ typedef enum collect_source
COLLECT_GROUP_BY,
} collect_source_t;
static void update_field_infos(parsing_info_t* pi,
LEX* lex,
st_select_lex* select,
uint32_t usage,
List<Item>* excludep);
static void update_field_infos(parsing_info_t* pi,
collect_source_t source,
Item* item,
uint32_t usage,
List<Item>* excludep)
{
switch (item->type())
@ -1968,13 +1978,13 @@ static void update_field_infos(parsing_info_t* pi,
while (Item *i = ilist++)
{
update_field_infos(pi, source, i, excludep);
update_field_infos(pi, source, i, usage, excludep);
}
}
break;
case Item::FIELD_ITEM:
add_field_info(pi, static_cast<Item_field*>(item), excludep);
add_field_info(pi, static_cast<Item_field*>(item), usage, excludep);
break;
case Item::REF_ITEM:
@ -1983,7 +1993,7 @@ static void update_field_infos(parsing_info_t* pi,
{
Item_ref* ref_item = static_cast<Item_ref*>(item);
add_field_info(pi, item, excludep);
add_field_info(pi, item, usage, excludep);
size_t n_items = ref_item->cols();
@ -1993,7 +2003,7 @@ static void update_field_infos(parsing_info_t* pi,
if (reffed_item != ref_item)
{
update_field_infos(pi, source, ref_item->element_index(i), excludep);
update_field_infos(pi, source, ref_item->element_index(i), usage, excludep);
}
}
}
@ -2007,7 +2017,7 @@ static void update_field_infos(parsing_info_t* pi,
for (size_t i = 0; i < n_items; ++i)
{
update_field_infos(pi, source, row_item->element_index(i), excludep);
update_field_infos(pi, source, row_item->element_index(i), usage, excludep);
}
}
break;
@ -2021,7 +2031,7 @@ static void update_field_infos(parsing_info_t* pi,
for (size_t i = 0; i < n_items; ++i)
{
update_field_infos(pi, source, items[i], excludep);
update_field_infos(pi, source, items[i], usage, excludep);
}
}
break;
@ -2048,7 +2058,21 @@ static void update_field_infos(parsing_info_t* pi,
if (in_subselect_item->left_expr_orig)
{
update_field_infos(pi, source,
in_subselect_item->left_expr_orig, excludep);
in_subselect_item->left_expr_orig, usage, excludep);
}
st_select_lex* ssl = in_subselect_item->get_select_lex();
if (ssl)
{
uint32_t sub_usage = usage;
sub_usage &= ~QC_USED_IN_SELECT;
sub_usage |= QC_USED_IN_SUBSELECT;
update_field_infos(pi,
get_lex(pi),
ssl,
sub_usage,
excludep);
}
#else
#pragma message "Figure out what to do with versions < 5.5.48."
@ -2058,10 +2082,21 @@ static void update_field_infos(parsing_info_t* pi,
break;
case Item_subselect::EXISTS_SUBS:
case Item_subselect::SINGLEROW_SUBS:
// TODO: Handle these explicitly as well.
break;
case Item_subselect::SINGLEROW_SUBS:
{
Item_singlerow_subselect* ss_item = static_cast<Item_singlerow_subselect*>(item);
st_select_lex *ssl = ss_item->get_select_lex();
usage &= ~QC_USED_IN_SELECT;
usage |= QC_USED_IN_SUBSELECT;
update_field_infos(pi, get_lex(pi), ssl, usage, excludep);
}
break;
case Item_subselect::UNKNOWN_SUBS:
default:
MXS_ERROR("Unknown subselect type: %d", subselect_item->substype());
@ -2075,6 +2110,69 @@ static void update_field_infos(parsing_info_t* pi,
}
}
static void update_field_infos(parsing_info_t* pi,
LEX* lex,
st_select_lex* select,
uint32_t usage,
List<Item>* excludep)
{
List_iterator<Item> ilist(select->item_list);
while (Item *item = ilist++)
{
update_field_infos(pi, COLLECT_SELECT, item, usage, NULL);
}
if (select->group_list.first)
{
ORDER* order = select->group_list.first;
while (order)
{
Item* item = *order->item;
update_field_infos(pi, COLLECT_GROUP_BY, item, QC_USED_IN_GROUP_BY,
&select->item_list);
order = order->next;
}
}
if (select->where)
{
update_field_infos(pi, COLLECT_WHERE,
select->where,
QC_USED_IN_WHERE,
&select->item_list);
}
#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.
if (select->having)
{
update_field_infos(pi, COLLECT_HAVING,
select->having,
0,
&select->item_list);
}
#endif
TABLE_LIST* table_list = select->get_table_list();
if (table_list)
{
st_select_lex *sl = table_list->get_single_select();
if (sl)
{
// This is for "SELECT 1 FROM (SELECT ...)"
usage &= ~QC_USED_IN_SELECT;
usage |= QC_USED_IN_SUBSELECT;
update_field_infos(pi, get_lex(pi), sl, usage, excludep);
}
}
}
void qc_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, size_t* n_infos)
{
if (!buf)
@ -2088,10 +2186,10 @@ void qc_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, size_t* n_infos)
}
parsing_info_t* pi = get_pinfo(buf);
ss_dassert(pi);
if (!pi->field_infos)
{
ss_dassert(pi);
LEX* lex = get_lex(buf);
ss_dassert(lex);
@ -2100,56 +2198,27 @@ void qc_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, size_t* n_infos)
return;
}
lex->current_select = lex->all_selects_list;
uint32_t usage = 0;
while (lex->current_select)
switch (lex->sql_command)
{
List_iterator<Item> ilist(lex->current_select->item_list);
case SQLCOM_UPDATE:
case SQLCOM_UPDATE_MULTI:
usage |= QC_USED_IN_SET;
break;
while (Item *item = ilist++)
{
update_field_infos(pi, COLLECT_SELECT, item, NULL);
}
if (lex->current_select->group_list.first)
{
ORDER* order = lex->current_select->group_list.first;
while (order)
{
Item* item = *order->item;
update_field_infos(pi, COLLECT_GROUP_BY, item, &lex->current_select->item_list);
order = order->next;
}
}
if (lex->current_select->where)
{
update_field_infos(pi, COLLECT_WHERE,
lex->current_select->where,
&lex->current_select->item_list);
}
#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.
if (lex->current_select->having)
{
update_field_infos(pi, COLLECT_HAVING,
lex->current_select->having,
&lex->current_select->item_list);
}
#endif
lex->current_select = lex->current_select->next_select_in_list();
default:
usage |= QC_USED_IN_SELECT;
}
lex->current_select = &lex->select_lex;
update_field_infos(pi, lex, &lex->select_lex, usage, NULL);
List_iterator<Item> ilist(lex->value_list);
while (Item* item = ilist++)
{
update_field_infos(pi, COLLECT_SELECT, item, NULL);
update_field_infos(pi, COLLECT_SELECT, item, 0, NULL);
}
if ((lex->sql_command == SQLCOM_INSERT) ||
@ -2159,7 +2228,7 @@ void qc_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, size_t* n_infos)
List_iterator<Item> ilist(lex->field_list);
while (Item *item = ilist++)
{
update_field_infos(pi, COLLECT_SELECT, item, NULL);
update_field_infos(pi, COLLECT_SELECT, item, 0, NULL);
}
if (lex->insert_list)
@ -2167,10 +2236,44 @@ void qc_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, size_t* n_infos)
List_iterator<Item> ilist(*lex->insert_list);
while (Item *item = ilist++)
{
update_field_infos(pi, COLLECT_SELECT, item, NULL);
update_field_infos(pi, COLLECT_SELECT, item, 0, NULL);
}
}
}
if (lex->sql_command == SQLCOM_SET_OPTION)
{
#if defined(WAY_TO_DOWNCAST_SET_VAR_BASE_EXISTS)
// The list of set_var_base contains the value of variables.
// However, the actual type is a derived type of set_var_base
// and there is no information using which we could do the
// downcast...
List_iterator<set_var_base> ilist(lex->var_list);
while (set_var_base* var = ilist++)
{
// Is set_var_base a set_var, set_var_user, set_var_password
// set_var_role
...
}
#endif
// ...so, we will simply assume that any nested selects are
// from statements like "set @a:=(SELECT a from t1)".
usage &= ~QC_USED_IN_SELECT;
usage |= QC_USED_IN_SUBSELECT;
st_select_lex* select = lex->all_selects_list;
while (select)
{
if (select->nest_level != 0) // Not the top-level select.
{
update_field_infos(pi, lex, select, usage, NULL);
}
select = select->next_select_in_list();
}
}
}
*infos = pi->field_infos;

View File

@ -140,17 +140,34 @@ static bool parse_query(GWBUF* query);
static void parse_query_string(const char* query, size_t len);
static bool query_is_parsed(GWBUF* query);
static bool should_exclude(const char* zName, const ExprList* pExclude);
static void update_fields_infos(QC_SQLITE_INFO* info,
int prev_token,
const Expr* pExpr,
qc_token_position_t pos,
const ExprList* pExclude);
static void update_fields_infos_from_exprlist(QC_SQLITE_INFO* info,
const ExprList* pEList, const ExprList* pExclude);
static void update_fields_infos_from_idlist(QC_SQLITE_INFO* info,
const IdList* pIds, const ExprList* pExclude);
static void update_fields_infos_from_select(QC_SQLITE_INFO* info,
const Select* pSelect, const ExprList* pExclude);
static void update_field_info(QC_SQLITE_INFO* info,
const char* database,
const char* table,
const char* column,
uint32_t usage,
const ExprList* pExclude);
static void update_field_infos_from_expr(QC_SQLITE_INFO* info,
const struct Expr* pExpr,
uint32_t usage,
const ExprList* pExclude);
static void update_field_infos(QC_SQLITE_INFO* info,
int prev_token,
const Expr* pExpr,
uint32_t usage,
qc_token_position_t pos,
const ExprList* pExclude);
static void update_field_infos_from_exprlist(QC_SQLITE_INFO* info,
const ExprList* pEList,
uint32_t usage,
const ExprList* pExclude);
static void update_field_infos_from_idlist(QC_SQLITE_INFO* info,
const IdList* pIds,
uint32_t usage,
const ExprList* pExclude);
static void update_field_infos_from_select(QC_SQLITE_INFO* info,
const Select* pSelect,
uint32_t usage,
const ExprList* pExclude);
static void update_database_names(QC_SQLITE_INFO* info, const char* name);
static void update_names(QC_SQLITE_INFO* info, const char* zDatabase, const char* zTable);
static void update_names_from_srclist(QC_SQLITE_INFO* info, const SrcList* pSrc);
@ -185,7 +202,7 @@ extern void exposed_sqlite3StartTable(Parse *pParse, /* Parser context */
int isView, /* True if this is a VIEW */
int isVirtual, /* True if this is a VIRTUAL table */
int noErr); /* Do nothing if table already exists */
extern void maxscaleCollectInfoFromSelect(Parse*, Select*);
extern void maxscaleCollectInfoFromSelect(Parse*, Select*, int);
/**
* Used for freeing a QC_SQLITE_INFO object added to a GWBUF.
@ -688,24 +705,16 @@ static bool should_exclude(const char* zName, const ExprList* pExclude)
return i != pExclude->nExpr;
}
static void update_field_infos(QC_SQLITE_INFO* info,
const char* database,
const char* table,
const char* column,
const ExprList* pExclude)
static void update_field_info(QC_SQLITE_INFO* info,
const char* database,
const char* table,
const char* column,
uint32_t usage,
const ExprList* pExclude)
{
ss_dassert(column);
// If only a column is specified, but not a table or database and we
// have a list of expressions that should be excluded, we check if the column
// value is present in that list. This is in order to exclude the second "d" in
// a statement like "select a as d from x where d = 2".
if (column && !table && !database && pExclude && should_exclude(column, pExclude))
{
return;
}
QC_FIELD_INFO item = { (char*)database, (char*)table, (char*)column };
QC_FIELD_INFO item = { (char*)database, (char*)table, (char*)column, usage };
int i;
for (i = 0; i < info->field_infos_len; ++i)
@ -739,22 +748,33 @@ static void update_field_infos(QC_SQLITE_INFO* info,
if (i == info->field_infos_len) // If true, the field was not present already.
{
if (info->field_infos_len < info->field_infos_capacity)
// If only a column is specified, but not a table or database and we
// have a list of expressions that should be excluded, we check if the column
// value is present in that list. This is in order to exclude the second "d" in
// a statement like "select a as d from x where d = 2".
if (!(column && !table && !database && pExclude && should_exclude(column, pExclude)))
{
field_infos = info->field_infos;
}
else
{
size_t capacity = info->field_infos_capacity ? 2 * info->field_infos_capacity : 8;
field_infos = MXS_REALLOC(info->field_infos, capacity * sizeof(QC_FIELD_INFO));
if (field_infos)
if (info->field_infos_len < info->field_infos_capacity)
{
info->field_infos = field_infos;
info->field_infos_capacity = capacity;
field_infos = info->field_infos;
}
else
{
size_t capacity = info->field_infos_capacity ? 2 * info->field_infos_capacity : 8;
field_infos = MXS_REALLOC(info->field_infos, capacity * sizeof(QC_FIELD_INFO));
if (field_infos)
{
info->field_infos = field_infos;
info->field_infos_capacity = capacity;
}
}
}
}
else
{
info->field_infos[i].usage |= usage;
}
// If field_infos is NULL, then the field was found and has already been noted.
if (field_infos)
@ -775,6 +795,7 @@ static void update_field_infos(QC_SQLITE_INFO* info,
static void update_field_infos_from_expr(QC_SQLITE_INFO* info,
const struct Expr* pExpr,
uint32_t usage,
const ExprList* pExclude)
{
QC_FIELD_INFO item = {};
@ -837,32 +858,33 @@ static void update_field_infos_from_expr(QC_SQLITE_INFO* info,
if (should_update)
{
update_field_infos(info, item.database, item.table, item.column, pExclude);
update_field_info(info, item.database, item.table, item.column, usage, pExclude);
}
}
}
static void update_fields_infos(QC_SQLITE_INFO* info,
int prev_token,
const Expr* pExpr,
qc_token_position_t pos,
const ExprList* pExclude)
static void update_field_infos(QC_SQLITE_INFO* info,
int prev_token,
const Expr* pExpr,
uint32_t usage,
qc_token_position_t pos,
const ExprList* pExclude)
{
const char* zToken = pExpr->u.zToken;
switch (pExpr->op)
{
case TK_ASTERISK: // select *
update_field_infos_from_expr(info, pExpr, pExclude);
update_field_infos_from_expr(info, pExpr, usage, pExclude);
break;
case TK_DOT: // select a.b ... select a.b.c
update_field_infos_from_expr(info, pExpr, pExclude);
update_field_infos_from_expr(info, pExpr, usage, pExclude);
break;
case TK_ID: // select a
update_field_infos_from_expr(info, pExpr, pExclude);
update_field_infos_from_expr(info, pExpr, usage, pExclude);
break;
case TK_VARIABLE:
@ -923,12 +945,17 @@ static void update_fields_infos(QC_SQLITE_INFO* info,
if (pExpr->pLeft)
{
update_fields_infos(info, pExpr->op, pExpr->pLeft, QC_TOKEN_LEFT, pExclude);
update_field_infos(info, pExpr->op, pExpr->pLeft, usage, QC_TOKEN_LEFT, pExclude);
}
if (pExpr->pRight)
{
update_fields_infos(info, pExpr->op, pExpr->pRight, QC_TOKEN_RIGHT, pExclude);
if (usage & QC_USED_IN_SET)
{
usage &= ~QC_USED_IN_SET;
}
update_field_infos(info, pExpr->op, pExpr->pRight, usage, QC_TOKEN_RIGHT, pExclude);
}
if (pExpr->x.pList)
@ -938,7 +965,7 @@ static void update_fields_infos(QC_SQLITE_INFO* info,
case TK_BETWEEN:
case TK_CASE:
case TK_FUNCTION:
update_fields_infos_from_exprlist(info, pExpr->x.pList, pExclude);
update_field_infos_from_exprlist(info, pExpr->x.pList, usage, pExclude);
break;
case TK_EXISTS:
@ -946,11 +973,15 @@ static void update_fields_infos(QC_SQLITE_INFO* info,
case TK_SELECT:
if (pExpr->flags & EP_xIsSelect)
{
update_fields_infos_from_select(info, pExpr->x.pSelect, pExclude);
uint32_t sub_usage = usage;
sub_usage &= ~QC_USED_IN_SELECT;
sub_usage |= QC_USED_IN_SUBSELECT;
update_field_infos_from_select(info, pExpr->x.pSelect, sub_usage, pExclude);
}
else
{
update_fields_infos_from_exprlist(info, pExpr->x.pList, pExclude);
update_field_infos_from_exprlist(info, pExpr->x.pList, usage, pExclude);
}
break;
}
@ -959,33 +990,36 @@ static void update_fields_infos(QC_SQLITE_INFO* info,
}
}
static void update_fields_infos_from_exprlist(QC_SQLITE_INFO* info,
const ExprList* pEList,
const ExprList* pExclude)
static void update_field_infos_from_exprlist(QC_SQLITE_INFO* info,
const ExprList* pEList,
uint32_t usage,
const ExprList* pExclude)
{
for (int i = 0; i < pEList->nExpr; ++i)
{
struct ExprList_item* pItem = &pEList->a[i];
update_fields_infos(info, 0, pItem->pExpr, QC_TOKEN_MIDDLE, pExclude);
update_field_infos(info, 0, pItem->pExpr, usage, QC_TOKEN_MIDDLE, pExclude);
}
}
static void update_fields_infos_from_idlist(QC_SQLITE_INFO* info,
const IdList* pIds,
const ExprList* pExclude)
static void update_field_infos_from_idlist(QC_SQLITE_INFO* info,
const IdList* pIds,
uint32_t usage,
const ExprList* pExclude)
{
for (int i = 0; i < pIds->nId; ++i)
{
struct IdList_item* pItem = &pIds->a[i];
update_field_infos(info, NULL, NULL, pItem->zName, pExclude);
update_field_info(info, NULL, NULL, pItem->zName, usage, pExclude);
}
}
static void update_fields_infos_from_select(QC_SQLITE_INFO* info,
const Select* pSelect,
const ExprList* pExclude)
static void update_field_infos_from_select(QC_SQLITE_INFO* info,
const Select* pSelect,
uint32_t usage,
const ExprList* pExclude)
{
if (pSelect->pSrc)
{
@ -1001,7 +1035,12 @@ static void update_fields_infos_from_select(QC_SQLITE_INFO* info,
if (pSrc->a[i].pSelect)
{
update_fields_infos_from_select(info, pSrc->a[i].pSelect, pExclude);
uint32_t sub_usage = usage;
sub_usage &= ~QC_USED_IN_SELECT;
sub_usage |= QC_USED_IN_SUBSELECT;
update_field_infos_from_select(info, pSrc->a[i].pSelect, sub_usage, pExclude);
}
#ifdef QC_COLLECT_NAMES_FROM_USING
@ -1011,7 +1050,7 @@ static void update_fields_infos_from_select(QC_SQLITE_INFO* info,
// does not reveal its value, right?
if (pSrc->a[i].pUsing)
{
update_fields_infos_from_idlist(info, pSrc->a[i].pUsing, pSelect->pEList);
update_field_infos_from_idlist(info, pSrc->a[i].pUsing, 0, pSelect->pEList);
}
#endif
}
@ -1019,18 +1058,18 @@ static void update_fields_infos_from_select(QC_SQLITE_INFO* info,
if (pSelect->pEList)
{
update_fields_infos_from_exprlist(info, pSelect->pEList, NULL);
update_field_infos_from_exprlist(info, pSelect->pEList, usage, NULL);
}
if (pSelect->pWhere)
if (pSelect->pWhere)
{
info->has_clause = true;
update_fields_infos(info, 0, pSelect->pWhere, QC_TOKEN_MIDDLE, pSelect->pEList);
update_field_infos(info, 0, pSelect->pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, pSelect->pEList);
}
if (pSelect->pGroupBy)
{
update_fields_infos_from_exprlist(info, pSelect->pGroupBy, pSelect->pEList);
update_field_infos_from_exprlist(info, pSelect->pGroupBy, QC_USED_IN_GROUP_BY, pSelect->pEList);
}
if (pSelect->pHaving)
@ -1039,7 +1078,7 @@ static void update_fields_infos_from_select(QC_SQLITE_INFO* info,
#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_fields_infos(info, 0, pSelect->pHaving, QC_TOKEN_MIDDLE, pSelect->pEList);
update_field_infos(info, 0, pSelect->pHaving, 0, QC_TOKEN_MIDDLE, pSelect->pEList);
#endif
}
}
@ -1285,7 +1324,7 @@ void mxs_sqlite3CreateView(Parse *pParse, /* The parsing context */
if (pSelect)
{
update_fields_infos_from_select(info, pSelect, NULL);
update_field_infos_from_select(info, pSelect, QC_USED_IN_SELECT, NULL);
info->is_real_query = false;
}
@ -1355,7 +1394,7 @@ void mxs_sqlite3DeleteFrom(Parse* pParse, SrcList* pTabList, Expr* pWhere, SrcLi
if (pWhere)
{
update_fields_infos(info, 0, pWhere, QC_TOKEN_MIDDLE, 0);
update_field_infos(info, 0, pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, 0);
}
exposed_sqlite3ExprDelete(pParse->db, pWhere);
@ -1419,7 +1458,7 @@ void mxs_sqlite3EndTable(Parse *pParse, /* Parse context */
{
if (pSelect)
{
update_fields_infos_from_select(info, pSelect, NULL);
update_field_infos_from_select(info, pSelect, QC_USED_IN_SELECT, NULL);
info->is_real_query = false;
}
else if (pOldTable)
@ -1465,17 +1504,28 @@ void mxs_sqlite3Insert(Parse* pParse,
if (pColumns)
{
update_fields_infos_from_idlist(info, pColumns, NULL);
update_field_infos_from_idlist(info, pColumns, 0, NULL);
}
if (pSelect)
{
update_fields_infos_from_select(info, pSelect, NULL);
uint32_t usage;
if (pSelect->selFlags & SF_Values) // Synthesized from VALUES clause
{
usage = 0;
}
else
{
usage = QC_USED_IN_SELECT;
}
update_field_infos_from_select(info, pSelect, usage, NULL);
}
if (pSet)
{
update_fields_infos_from_exprlist(info, pSet, NULL);
update_field_infos_from_exprlist(info, pSet, 0, NULL);
}
exposed_sqlite3SrcListDelete(pParse->db, pTabList);
@ -1511,7 +1561,7 @@ int mxs_sqlite3Select(Parse* pParse, Select* p, SelectDest* pDest)
info->status = QC_QUERY_PARSED;
info->operation = QUERY_OP_SELECT;
maxscaleCollectInfoFromSelect(pParse, p);
maxscaleCollectInfoFromSelect(pParse, p, 0);
// NOTE: By convention, the select is deleted in parse.y.
}
else
@ -1599,13 +1649,13 @@ void mxs_sqlite3Update(Parse* pParse, SrcList* pTabList, ExprList* pChanges, Exp
{
struct ExprList_item* pItem = &pChanges->a[i];
update_fields_infos(info, 0, pItem->pExpr, QC_TOKEN_MIDDLE, NULL);
update_field_infos(info, 0, pItem->pExpr, QC_USED_IN_SET, QC_TOKEN_MIDDLE, NULL);
}
}
if (pWhere)
{
update_fields_infos(info, 0, pWhere, QC_TOKEN_MIDDLE, pChanges);
update_field_infos(info, 0, pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, pChanges);
}
exposed_sqlite3SrcListDelete(pParse->db, pTabList);
@ -1613,7 +1663,7 @@ void mxs_sqlite3Update(Parse* pParse, SrcList* pTabList, ExprList* pChanges, Exp
exposed_sqlite3ExprDelete(pParse->db, pWhere);
}
void maxscaleCollectInfoFromSelect(Parse* pParse, Select* pSelect)
void maxscaleCollectInfoFromSelect(Parse* pParse, Select* pSelect, int sub_select)
{
QC_SQLITE_INFO* info = this_thread.info;
ss_dassert(info);
@ -1631,7 +1681,9 @@ void maxscaleCollectInfoFromSelect(Parse* pParse, Select* pSelect)
info->types = QUERY_TYPE_READ;
}
update_fields_infos_from_select(info, pSelect, NULL);
uint32_t usage = sub_select ? QC_USED_IN_SUBSELECT : QC_USED_IN_SELECT;
update_field_infos_from_select(info, pSelect, usage, NULL);
}
void maxscaleAlterTable(Parse *pParse, /* Parser context. */
@ -1783,12 +1835,13 @@ void maxscaleExplain(Parse* pParse, SrcList* pName)
info->status = QC_QUERY_PARSED;
info->types = QUERY_TYPE_READ;
update_names(info, "information_schema", "COLUMNS");
update_field_infos(info, "information_schema", "COLUMNS", "COLUMN_DEFAULT", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "COLUMN_KEY", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "COLUMN_NAME", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "COLUMN_TYPE", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "EXTRA", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "IS_NULLABLE", NULL);
uint32_t u = QC_USED_IN_SELECT;
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_DEFAULT", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_KEY", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_NAME", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_TYPE", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "EXTRA", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "IS_NULLABLE", u, NULL);
exposed_sqlite3SrcListDelete(pParse->db, pName);
}
@ -2308,7 +2361,8 @@ void maxscaleSet(Parse* pParse, int scope, mxs_set_t kind, ExprList* pList)
if (pValue->op == TK_SELECT)
{
update_fields_infos_from_select(info, pValue->x.pSelect, NULL);
update_field_infos_from_select(info, pValue->x.pSelect,
QC_USED_IN_SUBSELECT, NULL);
info->is_real_query = false; // TODO: This is what qc_mysqlembedded claims.
}
}
@ -2356,6 +2410,8 @@ extern void maxscaleShow(Parse* pParse, MxsShow* pShow)
zName = name;
}
uint32_t u = QC_USED_IN_SELECT;
switch (pShow->what)
{
case MXS_SHOW_COLUMNS:
@ -2364,24 +2420,24 @@ extern void maxscaleShow(Parse* pParse, MxsShow* pShow)
update_names(info, "information_schema", "COLUMNS");
if (pShow->data == MXS_SHOW_COLUMNS_FULL)
{
update_field_infos(info, "information_schema", "COLUMNS", "COLLATION_NAME", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "COLUMN_COMMENT", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "COLUMN_DEFAULT", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "COLUMN_KEY", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "COLUMN_NAME", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "COLUMN_TYPE", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "EXTRA", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "IS_NULLABLE", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "PRIVILEGES", NULL);
update_field_info(info, "information_schema", "COLUMNS", "COLLATION_NAME", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_COMMENT", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_DEFAULT", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_KEY", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_NAME", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_TYPE", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "EXTRA", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "IS_NULLABLE", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "PRIVILEGES", u, NULL);
}
else
{
update_field_infos(info, "information_schema", "COLUMNS", "COLUMN_DEFAULT", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "COLUMN_KEY", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "COLUMN_NAME", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "COLUMN_TYPE", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "EXTRA", NULL);
update_field_infos(info, "information_schema", "COLUMNS", "IS_NULLABLE", NULL);
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_DEFAULT", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_KEY", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_NAME", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_TYPE", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "EXTRA", u, NULL);
update_field_info(info, "information_schema", "COLUMNS", "IS_NULLABLE", u, NULL);
}
}
break;
@ -2404,7 +2460,7 @@ extern void maxscaleShow(Parse* pParse, MxsShow* pShow)
{
info->types = QUERY_TYPE_SHOW_DATABASES;
update_names(info, "information_schema", "SCHEMATA");
update_field_infos(info, "information_schema", "SCHEMATA", "SCHEMA_NAME", NULL);
update_field_info(info, "information_schema", "SCHEMATA", "SCHEMA_NAME", u, NULL);
}
break;
@ -2414,19 +2470,19 @@ extern void maxscaleShow(Parse* pParse, MxsShow* pShow)
{
info->types = QUERY_TYPE_WRITE;
update_names(info, "information_schema", "STATISTICS");
update_field_infos(info, "information_schema", "STATISTICS", "CARDINALITY", NULL);
update_field_infos(info, "information_schema", "STATISTICS", "COLLATION", NULL);
update_field_infos(info, "information_schema", "STATISTICS", "COLUMN_NAME", NULL);
update_field_infos(info, "information_schema", "STATISTICS", "COMMENT", NULL);
update_field_infos(info, "information_schema", "STATISTICS", "INDEX_COMMENT", NULL);
update_field_infos(info, "information_schema", "STATISTICS", "INDEX_NAME", NULL);
update_field_infos(info, "information_schema", "STATISTICS", "INDEX_TYPE", NULL);
update_field_infos(info, "information_schema", "STATISTICS", "NON_UNIQUE", NULL);
update_field_infos(info, "information_schema", "STATISTICS", "NULLABLE", NULL);
update_field_infos(info, "information_schema", "STATISTICS", "PACKED", NULL);
update_field_infos(info, "information_schema", "STATISTICS", "SEQ_IN_INDEX", NULL);
update_field_infos(info, "information_schema", "STATISTICS", "SUB_PART", NULL);
update_field_infos(info, "information_schema", "STATISTICS", "TABLE_NAME", NULL);
update_field_info(info, "information_schema", "STATISTICS", "CARDINALITY", u, NULL);
update_field_info(info, "information_schema", "STATISTICS", "COLLATION", u, NULL);
update_field_info(info, "information_schema", "STATISTICS", "COLUMN_NAME", u, NULL);
update_field_info(info, "information_schema", "STATISTICS", "COMMENT", u, NULL);
update_field_info(info, "information_schema", "STATISTICS", "INDEX_COMMENT", u, NULL);
update_field_info(info, "information_schema", "STATISTICS", "INDEX_NAME", u, NULL);
update_field_info(info, "information_schema", "STATISTICS", "INDEX_TYPE", u, NULL);
update_field_info(info, "information_schema", "STATISTICS", "NON_UNIQUE", u, NULL);
update_field_info(info, "information_schema", "STATISTICS", "NULLABLE", u, NULL);
update_field_info(info, "information_schema", "STATISTICS", "PACKED", u, NULL);
update_field_info(info, "information_schema", "STATISTICS", "SEQ_IN_INDEX", u, NULL);
update_field_info(info, "information_schema", "STATISTICS", "SUB_PART", u, NULL);
update_field_info(info, "information_schema", "STATISTICS", "TABLE_NAME", u, NULL);
}
break;
@ -2434,24 +2490,24 @@ extern void maxscaleShow(Parse* pParse, MxsShow* pShow)
{
info->types = QUERY_TYPE_WRITE;
update_names(info, "information_schema", "TABLES");
update_field_infos(info, "information_schema", "TABLES", "AUTO_INCREMENT", NULL);
update_field_infos(info, "information_schema", "TABLES", "AVG_ROW_LENGTH", NULL);
update_field_infos(info, "information_schema", "TABLES", "CHECKSUM", NULL);
update_field_infos(info, "information_schema", "TABLES", "CHECK_TIME", NULL);
update_field_infos(info, "information_schema", "TABLES", "CREATE_OPTIONS", NULL);
update_field_infos(info, "information_schema", "TABLES", "CREATE_TIME", NULL);
update_field_infos(info, "information_schema", "TABLES", "DATA_FREE", NULL);
update_field_infos(info, "information_schema", "TABLES", "DATA_LENGTH", NULL);
update_field_infos(info, "information_schema", "TABLES", "ENGINE", NULL);
update_field_infos(info, "information_schema", "TABLES", "INDEX_LENGTH", NULL);
update_field_infos(info, "information_schema", "TABLES", "MAX_DATA_LENGTH", NULL);
update_field_infos(info, "information_schema", "TABLES", "ROW_FORMAT", NULL);
update_field_infos(info, "information_schema", "TABLES", "TABLE_COLLATION", NULL);
update_field_infos(info, "information_schema", "TABLES", "TABLE_COMMENT", NULL);
update_field_infos(info, "information_schema", "TABLES", "TABLE_NAME", NULL);
update_field_infos(info, "information_schema", "TABLES", "TABLE_ROWS", NULL);
update_field_infos(info, "information_schema", "TABLES", "UPDATE_TIME", NULL);
update_field_infos(info, "information_schema", "TABLES", "VERSION", NULL);
update_field_info(info, "information_schema", "TABLES", "AUTO_INCREMENT", u, NULL);
update_field_info(info, "information_schema", "TABLES", "AVG_ROW_LENGTH", u, NULL);
update_field_info(info, "information_schema", "TABLES", "CHECKSUM", u, NULL);
update_field_info(info, "information_schema", "TABLES", "CHECK_TIME", u, NULL);
update_field_info(info, "information_schema", "TABLES", "CREATE_OPTIONS", u, NULL);
update_field_info(info, "information_schema", "TABLES", "CREATE_TIME", u, NULL);
update_field_info(info, "information_schema", "TABLES", "DATA_FREE", u, NULL);
update_field_info(info, "information_schema", "TABLES", "DATA_LENGTH", u, NULL);
update_field_info(info, "information_schema", "TABLES", "ENGINE", u, NULL);
update_field_info(info, "information_schema", "TABLES", "INDEX_LENGTH", u, NULL);
update_field_info(info, "information_schema", "TABLES", "MAX_DATA_LENGTH", u, NULL);
update_field_info(info, "information_schema", "TABLES", "ROW_FORMAT", u, NULL);
update_field_info(info, "information_schema", "TABLES", "TABLE_COLLATION", u, NULL);
update_field_info(info, "information_schema", "TABLES", "TABLE_COMMENT", u, NULL);
update_field_info(info, "information_schema", "TABLES", "TABLE_NAME", u, NULL);
update_field_info(info, "information_schema", "TABLES", "TABLE_ROWS", u, NULL);
update_field_info(info, "information_schema", "TABLES", "UPDATE_TIME", u, NULL);
update_field_info(info, "information_schema", "TABLES", "VERSION", u, NULL);
}
break;
@ -2465,8 +2521,8 @@ extern void maxscaleShow(Parse* pParse, MxsShow* pShow)
// TODO: qc_mysqlembedded does not set the type bit.
info->types = QUERY_TYPE_UNKNOWN;
update_names(info, "information_schema", "SESSION_STATUS");
update_field_infos(info, "information_schema", "SESSION_STATUS", "VARIABLE_NAME", NULL);
update_field_infos(info, "information_schema", "SESSION_STATUS", "VARIABLE_VALUE", NULL);
update_field_info(info, "information_schema", "SESSION_STATUS", "VARIABLE_NAME", u, NULL);
update_field_info(info, "information_schema", "SESSION_STATUS", "VARIABLE_VALUE", u, NULL);
break;
case MXS_SHOW_STATUS_MASTER:
@ -2491,7 +2547,7 @@ extern void maxscaleShow(Parse* pParse, MxsShow* pShow)
{
info->types = QUERY_TYPE_SHOW_TABLES;
update_names(info, "information_schema", "TABLE_NAMES");
update_field_infos(info, "information_schema", "TABLE_NAMES", "TABLE_NAME", NULL);
update_field_info(info, "information_schema", "TABLE_NAMES", "TABLE_NAME", u, NULL);
}
break;
@ -2506,8 +2562,8 @@ extern void maxscaleShow(Parse* pParse, MxsShow* pShow)
info->types = QUERY_TYPE_SYSVAR_READ;
}
update_names(info, "information_schema", "SESSION_VARIABLES");
update_field_infos(info, "information_schema", "SESSION_STATUS", "VARIABLE_NAME", NULL);
update_field_infos(info, "information_schema", "SESSION_STATUS", "VARIABLE_VALUE", NULL);
update_field_info(info, "information_schema", "SESSION_STATUS", "VARIABLE_NAME", u, NULL);
update_field_info(info, "information_schema", "SESSION_STATUS", "VARIABLE_VALUE", u, NULL);
}
break;

View File

@ -94,7 +94,7 @@ extern int mxs_sqlite3Select(Parse*, Select*, SelectDest*);
extern void mxs_sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int);
extern void mxs_sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
extern void maxscaleCollectInfoFromSelect(Parse*, Select*);
extern void maxscaleCollectInfoFromSelect(Parse*, Select*, int);
extern void maxscaleAlterTable(Parse*, mxs_alter_t command, SrcList*, Token*);
extern void maxscaleCall(Parse*, SrcList* pName);
@ -1444,7 +1444,7 @@ table_factor(A) ::= nm(X) DOT nm(Y) as_opt id(Z). {
}
table_factor(A) ::= LP oneselect(S) RP as_opt id. {
maxscaleCollectInfoFromSelect(pParse, S);
maxscaleCollectInfoFromSelect(pParse, S, 1);
sqlite3SelectDelete(pParse->db, S);
A = 0;
}

View File

@ -862,6 +862,7 @@ public:
: m_database(info.database ? info.database : "")
, m_table(info.table ? info.table : "")
, m_column(info.column ? info.column : "")
, m_usage(info.usage)
{}
bool eq(const QcFieldInfo& rhs) const
@ -869,7 +870,8 @@ public:
return
m_database == rhs.m_database &&
m_table == rhs.m_table &&
m_column == rhs.m_column;
m_column == rhs.m_column &&
m_usage == rhs.m_usage;
}
bool lt(const QcFieldInfo& rhs) const
@ -900,6 +902,14 @@ public:
{
rv = true;
}
else if (m_column > rhs.m_column)
{
rv = false;
}
else
{
rv = (m_usage < rhs.m_usage);
}
}
}
@ -921,12 +931,19 @@ public:
}
out << m_column;
out << "(";
char* s = qc_field_usage_mask_to_string(m_usage);
out << s;
free(s);
out << ")";
}
private:
std::string m_database;
std::string m_table;
std::string m_column;
uint32_t m_usage;
};
ostream& operator << (ostream& out, const QcFieldInfo& x)

View File

@ -27,6 +27,12 @@
#define QC_TRACE()
#endif
struct type_name_info
{
const char* name;
size_t name_len;
};
static const char default_qc_name[] = "qc_sqlite";
static QUERY_CLASSIFIER* classifier;
@ -213,6 +219,141 @@ char* qc_get_prepare_name(GWBUF* query)
return classifier->qc_get_prepare_name(query);
}
struct type_name_info field_usage_to_type_name_info(qc_field_usage_t usage)
{
struct type_name_info info;
switch (usage)
{
case QC_USED_IN_SELECT:
{
static const char name[] = "QC_USED_IN_SELECT";
info.name = name;
info.name_len = sizeof(name) - 1;
}
break;
case QC_USED_IN_SUBSELECT:
{
static const char name[] = "QC_USED_IN_SUBSELECT";
info.name = name;
info.name_len = sizeof(name) - 1;
}
break;
case QC_USED_IN_WHERE:
{
static const char name[] = "QC_USED_IN_WHERE";
info.name = name;
info.name_len = sizeof(name) - 1;
}
break;
case QC_USED_IN_SET:
{
static const char name[] = "QC_USED_IN_SET";
info.name = name;
info.name_len = sizeof(name) - 1;
}
break;
case QC_USED_IN_GROUP_BY:
{
static const char name[] = "QC_USED_IN_GROUP_BY";
info.name = name;
info.name_len = sizeof(name) - 1;
}
break;
default:
{
static const char name[] = "UNKNOWN_FIELD_USAGE";
info.name = name;
info.name_len = sizeof(name) - 1;
}
break;
}
return info;
}
const char* qc_field_usage_to_string(qc_field_usage_t usage)
{
return field_usage_to_type_name_info(usage).name;
}
static const qc_field_usage_t FIELD_USAGE_VALUES[] =
{
QC_USED_IN_SELECT,
QC_USED_IN_SUBSELECT,
QC_USED_IN_WHERE,
QC_USED_IN_SET,
QC_USED_IN_GROUP_BY,
};
static const int N_FIELD_USAGE_VALUES =
sizeof(FIELD_USAGE_VALUES) / sizeof(FIELD_USAGE_VALUES[0]);
static const int FIELD_USAGE_MAX_LEN = 20; // strlen("QC_USED_IN_SUBSELECT");
char* qc_field_usage_mask_to_string(uint32_t mask)
{
size_t len = 0;
// First calculate how much space will be needed.
for (int i = 0; i < N_FIELD_USAGE_VALUES; ++i)
{
if (mask & FIELD_USAGE_VALUES[i])
{
if (len != 0)
{
++len; // strlen("|");
}
len += FIELD_USAGE_MAX_LEN;
}
}
++len;
// Then make one allocation and build the string.
char* s = (char*) MXS_MALLOC(len);
if (s)
{
if (len > 1)
{
char* p = s;
for (int i = 0; i < N_FIELD_USAGE_VALUES; ++i)
{
qc_field_usage_t value = FIELD_USAGE_VALUES[i];
if (mask & value)
{
if (p != s)
{
strcpy(p, "|");
++p;
}
struct type_name_info info = field_usage_to_type_name_info(value);
strcpy(p, info.name);
p += info.name_len;
}
}
}
else
{
*s = 0;
}
}
return s;
}
const char* qc_op_to_string(qc_query_op_t op)
{
switch (op)
@ -261,12 +402,6 @@ const char* qc_op_to_string(qc_query_op_t op)
}
}
struct type_name_info
{
const char* name;
size_t name_len;
};
struct type_name_info type_to_type_name_info(qc_query_type_t type)
{
struct type_name_info info;