diff --git a/include/maxscale/query_classifier.h b/include/maxscale/query_classifier.h index b7e3f4639..db1960994 100644 --- a/include/maxscale/query_classifier.h +++ b/include/maxscale/query_classifier.h @@ -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. * diff --git a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc index 29ec2af53..e13a469ea 100644 --- a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc +++ b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc @@ -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* 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* excludep) +static void add_field_info(parsing_info_t* pi, Item_field* item, uint32_t usage, List* 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* 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* excludep) +static void add_field_info(parsing_info_t* pi, Item* item, uint32_t usage, List* 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* excludep); + static void update_field_infos(parsing_info_t* pi, collect_source_t source, Item* item, + uint32_t usage, List* 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), excludep); + add_field_info(pi, static_cast(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); - 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); + 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* excludep) +{ + List_iterator 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 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 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 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 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 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; diff --git a/query_classifier/qc_sqlite/qc_sqlite.c b/query_classifier/qc_sqlite/qc_sqlite.c index fc579103c..9ff54994e 100644 --- a/query_classifier/qc_sqlite/qc_sqlite.c +++ b/query_classifier/qc_sqlite/qc_sqlite.c @@ -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; diff --git a/query_classifier/qc_sqlite/sqlite-src-3110100/src/parse.y b/query_classifier/qc_sqlite/sqlite-src-3110100/src/parse.y index b015bc5c7..ab6b50ea7 100644 --- a/query_classifier/qc_sqlite/sqlite-src-3110100/src/parse.y +++ b/query_classifier/qc_sqlite/sqlite-src-3110100/src/parse.y @@ -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; } diff --git a/query_classifier/test/compare.cc b/query_classifier/test/compare.cc index 9c267846d..102b9e42b 100644 --- a/query_classifier/test/compare.cc +++ b/query_classifier/test/compare.cc @@ -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) diff --git a/server/core/query_classifier.c b/server/core/query_classifier.c index 3b9abef57..0a12f374a 100644 --- a/server/core/query_classifier.c +++ b/server/core/query_classifier.c @@ -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;