From ad4e8dad94b5adde8aa0cf76481917e3ae3b9bf6 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Wed, 23 Aug 2017 14:21:04 +0300 Subject: [PATCH] MXS-1364 Drop the usage field But for the most trivial statements did not really provide useful information. The arguments of the "function" '=' are now reported. --- Documentation/Filters/Cache.md | 13 ++ include/maxscale/query_classifier.h | 55 +---- .../qc_mysqlembedded/qc_mysqlembedded.cc | 175 +++++++-------- query_classifier/qc_sqlite/qc_sqlite.cc | 200 +++++++----------- .../qc_sqlite/sqlite-src-3110100/src/parse.y | 4 +- query_classifier/test/compare.cc | 119 +---------- query_classifier/test/cte_recursive.test | 73 ++++--- query_classifier/test/delete.test | 4 +- query_classifier/test/join.test | 10 +- .../test/qc_mysqlembedded_unsupported.test | 13 ++ .../test/qc_sqlite_unsupported.test | 4 + query_classifier/test/select.test | 4 +- server/core/query_classifier.cc | 135 ------------ server/modules/filter/cache/rules.cc | 170 +++++++-------- 14 files changed, 330 insertions(+), 649 deletions(-) diff --git a/Documentation/Filters/Cache.md b/Documentation/Filters/Cache.md index 46534ee2d..7d0e7601c 100644 --- a/Documentation/Filters/Cache.md +++ b/Documentation/Filters/Cache.md @@ -360,6 +360,19 @@ select * from tbl where b = 3 and a = 2; as well. Although they conceptually are identical, there will be two cache entries. +Note that if a column has been specified in a rule, then a statement +will match _irrespective_ of where that particular column appears. +For instance, if a rule specifies that the result of statements referring +to the the column _a_ should be cached, then the following statement will +match +``` +select a from tbl; +``` +and so will +``` +select b from tbl where a > 5; +``` + ### Qualified Names When using `=` or `!=` in the rule object in conjunction with `database`, diff --git a/include/maxscale/query_classifier.h b/include/maxscale/query_classifier.h index 2730851eb..72972f308 100644 --- a/include/maxscale/query_classifier.h +++ b/include/maxscale/query_classifier.h @@ -122,38 +122,6 @@ 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_NAME contains information about the name of a field used in a statement. - */ -typedef struct qc_field_name -{ - 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. */ -} QC_FIELD_NAME; - /** * QC_FIELD_INFO contains information about a field used in a statement. */ @@ -162,7 +130,6 @@ 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. */ - uint32_t usage; /** Bitfield denoting where the column appears. */ } QC_FIELD_INFO; /** @@ -171,8 +138,7 @@ typedef struct qc_field_info typedef struct qc_function_info { char* name; /** Name of function. */ - uint32_t usage; /** Bitfield denoting where the column appears. */ - QC_FIELD_NAME* fields; /** What fields the function accesses. */ + QC_FIELD_INFO* fields; /** What fields the function accesses. */ uint32_t n_fields; /** The number of fields in @c fields. */ } QC_FUNCTION_INFO; @@ -569,25 +535,6 @@ void qc_thread_end(uint32_t kind); */ qc_parse_result_t qc_parse(GWBUF* stmt, uint32_t collect); -/** - * 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 09b20ed19..4979da6c5 100644 --- a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc +++ b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc @@ -1792,7 +1792,7 @@ static void parsing_info_done(void* ptr) for (size_t j = 0; j < fi.n_fields; ++j) { - QC_FIELD_NAME& field = fi.fields[j]; + QC_FIELD_INFO& field = fi.fields[j]; free(field.database); free(field.table); @@ -2239,14 +2239,13 @@ 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); unalias_names(select, database, table, &database, &table); - QC_FIELD_INFO item = { (char*)database, (char*)table, (char*)column, usage }; + QC_FIELD_INFO item = { (char*)database, (char*)table, (char*)column }; size_t i; for (i = 0; i < info->field_infos_len; ++i) @@ -2303,10 +2302,6 @@ static void add_field_info(parsing_info_t* info, } } } - 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) @@ -2335,7 +2330,7 @@ static void add_function_field_usage(const char* database, while (!found && (i < fi->n_fields)) { - QC_FIELD_NAME& field = fi->fields[i]; + QC_FIELD_INFO& field = fi->fields[i]; if (strcasecmp(field.column, column) == 0) { @@ -2361,14 +2356,14 @@ static void add_function_field_usage(const char* database, if (!found) { - QC_FIELD_NAME* fields = (QC_FIELD_NAME*)realloc(fi->fields, - (fi->n_fields + 1) * sizeof(QC_FIELD_NAME)); + QC_FIELD_INFO* fields = (QC_FIELD_INFO*)realloc(fi->fields, + (fi->n_fields + 1) * sizeof(QC_FIELD_INFO)); ss_dassert(fields); if (fields) { // Ignore potential alloc failures - QC_FIELD_NAME& field = fields[fi->n_fields]; + QC_FIELD_INFO& field = fields[fi->n_fields]; field.database = database ? strdup(database) : NULL; field.table = table ? strdup(table) : NULL; field.column = strdup(column); @@ -2505,7 +2500,8 @@ static QC_FUNCTION_INFO* get_function_info(parsing_info_t* info, const char* nam function_info = &info->function_infos[info->function_infos_len++]; function_info->name = strdup(name); - function_info->usage = 0; + function_info->fields = NULL; + function_info->n_fields = 0; } return function_info; @@ -2514,7 +2510,6 @@ static QC_FUNCTION_INFO* get_function_info(parsing_info_t* info, const char* nam static QC_FUNCTION_INFO* add_function_info(parsing_info_t* info, st_select_lex* select, const char* name, - uint32_t usage, Item** items, int n_items) { @@ -2524,7 +2519,7 @@ static QC_FUNCTION_INFO* add_function_info(parsing_info_t* info, name = map_function_name(info->function_name_mappings, name); - QC_FUNCTION_INFO item = { (char*)name, usage }; + QC_FUNCTION_INFO item = { (char*)name }; size_t i; for (i = 0; i < info->function_infos_len; ++i) @@ -2558,17 +2553,11 @@ static QC_FUNCTION_INFO* add_function_info(parsing_info_t* info, function_info = &info->function_infos[info->function_infos_len++]; function_info->name = strdup(name); - function_info->usage = 0; function_info->fields = NULL; function_info->n_fields = 0; } - function_info->usage |= usage; - - if (strcmp(name, "=") != 0) - { - add_function_field_usage(select, items, n_items, function_info); - } + add_function_field_usage(select, items, n_items, function_info); return function_info; } @@ -2576,7 +2565,6 @@ static QC_FUNCTION_INFO* add_function_info(parsing_info_t* info, static void add_field_info(parsing_info_t* pi, st_select_lex* select, Item_field* item, - uint32_t usage, List* excludep) { const char* database = item->db_name; @@ -2668,13 +2656,12 @@ static void add_field_info(parsing_info_t* pi, break; } - add_field_info(pi, select, database, table, column, usage, excludep); + add_field_info(pi, select, database, table, column, excludep); } static void add_field_info(parsing_info_t* pi, st_select_lex* select, Item* item, - uint32_t usage, List* excludep) { const char* database = NULL; @@ -2686,7 +2673,7 @@ static void add_field_info(parsing_info_t* pi, strncpy(column, s, l); column[l] = 0; - add_field_info(pi, select, database, table, column, usage, excludep); + add_field_info(pi, select, database, table, column, excludep); } typedef enum collect_source @@ -2700,7 +2687,6 @@ typedef enum collect_source static void update_field_infos(parsing_info_t* pi, LEX* lex, st_select_lex* select, - uint32_t usage, List* excludep); static void remove_surrounding_back_ticks(char* s) @@ -2772,7 +2758,6 @@ static void update_field_infos(parsing_info_t* pi, st_select_lex* select, collect_source_t source, Item* item, - uint32_t usage, List* excludep) { switch (item->type()) @@ -2784,13 +2769,13 @@ static void update_field_infos(parsing_info_t* pi, while (Item *i = ilist++) { - update_field_infos(pi, select, source, i, usage, excludep); + update_field_infos(pi, select, source, i, excludep); } } break; case Item::FIELD_ITEM: - add_field_info(pi, select, static_cast(item), usage, excludep); + add_field_info(pi, select, static_cast(item), excludep); break; case Item::REF_ITEM: @@ -2799,7 +2784,7 @@ static void update_field_infos(parsing_info_t* pi, { Item_ref* ref_item = static_cast(item); - add_field_info(pi, select, item, usage, excludep); + add_field_info(pi, select, item, excludep); size_t n_items = ref_item->cols(); @@ -2809,7 +2794,7 @@ static void update_field_infos(parsing_info_t* pi, if (reffed_item != ref_item) { - update_field_infos(pi, select, source, ref_item->element_index(i), usage, excludep); + update_field_infos(pi, select, source, ref_item->element_index(i), excludep); } } } @@ -2823,7 +2808,7 @@ static void update_field_infos(parsing_info_t* pi, for (size_t i = 0; i < n_items; ++i) { - update_field_infos(pi, select, source, row_item->element_index(i), usage, excludep); + update_field_infos(pi, select, source, row_item->element_index(i), excludep); } } break; @@ -2929,12 +2914,12 @@ static void update_field_infos(parsing_info_t* pi, strcpy(func_name, "addtime"); } - add_function_info(pi, select, func_name, usage, items, n_items); + add_function_info(pi, select, func_name, items, n_items); } for (size_t i = 0; i < n_items; ++i) { - update_field_infos(pi, select, source, items[i], usage, excludep); + update_field_infos(pi, select, source, items[i], excludep); } } break; @@ -2946,7 +2931,7 @@ static void update_field_infos(parsing_info_t* pi, switch (subselect_item->substype()) { case Item_subselect::IN_SUBS: - fi = add_function_info(pi, select, "in", usage, 0, 0); + fi = add_function_info(pi, select, "in", 0, 0); case Item_subselect::ALL_SUBS: case Item_subselect::ANY_SUBS: { @@ -2962,7 +2947,7 @@ static void update_field_infos(parsing_info_t* pi, if (in_subselect_item->left_expr_orig) { update_field_infos(pi, select, source, // TODO: Might be wrong select. - in_subselect_item->left_expr_orig, usage, excludep); + in_subselect_item->left_expr_orig, excludep); if (subselect_item->substype() == Item_subselect::IN_SUBS) { @@ -2977,15 +2962,9 @@ static void update_field_infos(parsing_info_t* pi, 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); if (subselect_item->substype() == Item_subselect::IN_SUBS) @@ -3009,15 +2988,9 @@ static void update_field_infos(parsing_info_t* pi, st_select_lex* ssl = exists_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); } } @@ -3028,10 +3001,7 @@ static void update_field_infos(parsing_info_t* pi, 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); + update_field_infos(pi, get_lex(pi), ssl, excludep); } break; @@ -3052,14 +3022,13 @@ static void update_field_infos(parsing_info_t* pi, static void update_field_infos(parsing_info_t* pi, LEX* lex, st_select_lex_unit* select, - uint32_t usage, List* excludep) { st_select_lex* s = select->first_select(); if (s) { - update_field_infos(pi, lex, s, usage, excludep); + update_field_infos(pi, lex, s, excludep); } } #endif @@ -3067,14 +3036,13 @@ 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, select, COLLECT_SELECT, item, usage, NULL); + update_field_infos(pi, select, COLLECT_SELECT, item, NULL); } if (select->group_list.first) @@ -3084,8 +3052,7 @@ static void update_field_infos(parsing_info_t* pi, { Item* item = *order->item; - update_field_infos(pi, select, COLLECT_GROUP_BY, item, QC_USED_IN_GROUP_BY, - &select->item_list); + update_field_infos(pi, select, COLLECT_GROUP_BY, item, &select->item_list); order = order->next; } @@ -3093,15 +3060,8 @@ static void update_field_infos(parsing_info_t* pi, if (select->where) { - uint32_t sub_usage = QC_USED_IN_WHERE; - // TODO: The usage bits should get an overhaul. The following would make sense - // TODO: but breaks things overall. So for another time. - // TODO: sub_usage &= ~QC_USED_IN_SELECT; - // TODO: sub_usage |= QC_USED_IN_WHERE; - update_field_infos(pi, select, COLLECT_WHERE, select->where, - sub_usage, &select->item_list); } @@ -3126,9 +3086,7 @@ static void update_field_infos(parsing_info_t* pi, 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); + update_field_infos(pi, get_lex(pi), sl, excludep); } } } @@ -3168,29 +3126,35 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_ return QC_RESULT_OK; } - uint32_t usage = 0; - - switch (lex->sql_command) - { - case SQLCOM_UPDATE: - case SQLCOM_UPDATE_MULTI: - usage |= QC_USED_IN_SET; - break; - - default: - usage |= QC_USED_IN_SELECT; - } - lex->current_select = &lex->select_lex; - update_field_infos(pi, lex, &lex->select_lex, usage, NULL); + update_field_infos(pi, lex, &lex->select_lex, NULL); + + QC_FUNCTION_INFO* fi = NULL; + + if ((lex->sql_command == SQLCOM_UPDATE) || (lex->sql_command == SQLCOM_UPDATE_MULTI)) + { + List_iterator ilist(lex->current_select->item_list); + Item *item = ilist++; + + fi = get_function_info(pi, "="); + + while (item) + { + update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, NULL); + + if (item->type() == Item::FIELD_ITEM) + { + add_function_field_usage(lex->current_select, static_cast(item), fi); + } + + item = ilist++; + } + } #ifdef CTE_SUPPORTED if (lex->with_clauses_list) { - usage &= ~QC_USED_IN_SELECT; - usage |= QC_USED_IN_SUBSELECT; - With_clause* with_clause = lex->with_clauses_list; while (with_clause) @@ -3200,11 +3164,11 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_ while (element) { - update_field_infos(pi, lex, element->spec, usage, NULL); + update_field_infos(pi, lex, element->spec, NULL); if (element->is_recursive && element->first_recursive) { - update_field_infos(pi, lex, element->first_recursive, usage, NULL); + update_field_infos(pi, lex, element->first_recursive, NULL); } element = element->next; @@ -3218,7 +3182,15 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_ List_iterator ilist(lex->value_list); while (Item* item = ilist++) { - update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, 0, NULL); + update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, NULL); + + if (fi) + { + if (item->type() == Item::FIELD_ITEM) + { + add_function_field_usage(lex->current_select, static_cast(item), fi); + } + } } if ((lex->sql_command == SQLCOM_INSERT) || @@ -3227,9 +3199,24 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_ (lex->sql_command == SQLCOM_REPLACE_SELECT)) { List_iterator ilist(lex->field_list); - while (Item *item = ilist++) + Item* item = ilist++; + + if (item) { - update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, 0, NULL); + // We get here in case of "insert into t set a = 0". + QC_FUNCTION_INFO* fi = get_function_info(pi, "="); + + while (item) + { + update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, NULL); + + if (item->type() == Item::FIELD_ITEM) + { + add_function_field_usage(lex->current_select, static_cast(item), fi); + } + + item = ilist++; + } } if (lex->insert_list) @@ -3237,7 +3224,7 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_ List_iterator ilist(*lex->insert_list); while (Item *item = ilist++) { - update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, 0, NULL); + update_field_infos(pi, lex->current_select, COLLECT_SELECT, item, NULL); } } } @@ -3270,17 +3257,13 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_ // code after the closing }. } - usage &= ~QC_USED_IN_SELECT; - usage &= ~QC_USED_IN_SET; - 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); + update_field_infos(pi, lex, select, NULL); } select = select->next_select_in_list(); diff --git a/query_classifier/qc_sqlite/qc_sqlite.cc b/query_classifier/qc_sqlite/qc_sqlite.cc index 1e07b5e6e..6d6e3f5c6 100644 --- a/query_classifier/qc_sqlite/qc_sqlite.cc +++ b/query_classifier/qc_sqlite/qc_sqlite.cc @@ -256,19 +256,18 @@ public: return pInfo; } - template // QC_FIELD_INFO, QC_FIELD_NAME - static void finish(T& t) + static void finish_field_info(QC_FIELD_INFO& info) { - MXS_FREE(t.database); - MXS_FREE(t.table); - MXS_FREE(t.column); + MXS_FREE(info.database); + MXS_FREE(info.table); + MXS_FREE(info.column); } static void finish_function_info(QC_FUNCTION_INFO& info) { MXS_FREE(info.name); - std::for_each(info.fields, info.fields + info.n_fields, finish); + std::for_each(info.fields, info.fields + info.n_fields, finish_field_info); } ~QcSqliteInfo() @@ -279,7 +278,7 @@ public: std::for_each(m_database_names.begin(), m_database_names.end(), mxs_free); free(m_zPrepare_name); gwbuf_free(m_pPreparable_stmt); - std::for_each(m_field_infos.begin(), m_field_infos.end(), finish); + std::for_each(m_field_infos.begin(), m_field_infos.end(), finish_field_info); std::for_each(m_function_infos.begin(), m_function_infos.end(), finish_function_info); // Data in m_function_field_usage is freed in finish_function_info(). @@ -606,7 +605,6 @@ public: const char* zDatabase, const char* zTable, const char* zColumn, - uint32_t usage, const ExprList* pExclude) { ss_dassert(zColumn); @@ -648,7 +646,6 @@ public: item.table = zTable ? MXS_STRDUP(zTable) : NULL; ss_dassert(zColumn); item.column = MXS_STRDUP(zColumn); - item.usage = usage; // We are happy if we at least could dup the column. @@ -658,10 +655,6 @@ public: } } } - else - { - i->usage |= usage; - } } void update_names(const char* zDatabase, const char* zTable, const char* zAlias, QcAliases* pAliases) @@ -800,7 +793,6 @@ public: void update_field_infos(QcAliases* pAliases, int prev_token, const Expr* pExpr, - uint32_t usage, qc_token_position_t pos, const ExprList* pExclude) { @@ -813,15 +805,15 @@ public: switch (pExpr->op) { case TK_ASTERISK: // select * - update_field_infos_from_expr(pAliases, pExpr, usage, pExclude); + update_field_infos_from_expr(pAliases, pExpr, pExclude); break; case TK_DOT: // select a.b ... select a.b.c - update_field_infos_from_expr(pAliases, pExpr, usage, pExclude); + update_field_infos_from_expr(pAliases, pExpr, pExclude); break; case TK_ID: // select a - update_field_infos_from_expr(pAliases, pExpr, usage, pExclude); + update_field_infos_from_expr(pAliases, pExpr, pExclude); break; case TK_VARIABLE: @@ -878,16 +870,6 @@ public: 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(pAliases, get_token_symbol(pExpr->op), usage, pExclude); - } - break; - case TK_GE: case TK_GT: case TK_LE: @@ -908,12 +890,11 @@ public: { int i = update_function_info(pAliases, get_token_symbol(pExpr->op), - usage, pExclude); if (i != -1) { - vector& fields = m_function_field_usage[i]; + vector& fields = m_function_field_usage[i]; if (pExpr->pLeft) { @@ -947,19 +928,19 @@ public: char sqlrowcount[13]; // strlen("sql") + strlen("%") + strlen("rowcount") + 1 sprintf(sqlrowcount, "%s%%%s", pLeft->u.zToken, pRight->u.zToken); - update_function_info(pAliases, sqlrowcount, usage, pExclude); + update_function_info(pAliases, sqlrowcount, pExclude); pLeft = NULL; pRight = NULL; } else { - update_function_info(pAliases, get_token_symbol(pExpr->op), usage, pExclude); + update_function_info(pAliases, get_token_symbol(pExpr->op), pExclude); } } else { - update_function_info(pAliases, get_token_symbol(pExpr->op), usage, pExclude); + update_function_info(pAliases, get_token_symbol(pExpr->op), pExclude); } break; @@ -967,7 +948,7 @@ public: switch (this_unit.parse_as) { case QC_PARSE_AS_DEFAULT: - update_function_info(pAliases, get_token_symbol(pExpr->op), usage, pExclude); + update_function_info(pAliases, get_token_symbol(pExpr->op), pExclude); break; case QC_PARSE_AS_103: @@ -1004,7 +985,7 @@ public: // way qc_mysqlembedded does. if (!ignore_exprlist && (strcasecmp(zToken, "row") != 0)) { - update_function_info(pAliases, zToken, pExpr->x.pList, usage, pExclude); + update_function_info(pAliases, zToken, pExpr->x.pList, pExclude); } } break; @@ -1015,17 +996,12 @@ public: if (pLeft) { - update_field_infos(pAliases, pExpr->op, pExpr->pLeft, usage, QC_TOKEN_LEFT, pExclude); + update_field_infos(pAliases, pExpr->op, pExpr->pLeft, 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); + update_field_infos(pAliases, pExpr->op, pExpr->pRight, QC_TOKEN_RIGHT, pExclude); } if (pExpr->x.pList) @@ -1035,7 +1011,7 @@ public: case TK_FUNCTION: if (!ignore_exprlist) { - update_field_infos_from_exprlist(pAliases, pExpr->x.pList, usage, pExclude); + update_field_infos_from_exprlist(pAliases, pExpr->x.pList, pExclude); } break; @@ -1045,11 +1021,6 @@ public: case TK_IN: case TK_SELECT: { - uint32_t sub_usage = usage; - - sub_usage &= ~QC_USED_IN_SELECT; - sub_usage |= QC_USED_IN_SUBSELECT; - const char* zName = NULL; switch (pExpr->op) @@ -1063,24 +1034,23 @@ public: if (pExpr->flags & EP_xIsSelect) { - update_field_infos_from_subselect(pAliases, pExpr->x.pSelect, - sub_usage, pExclude); + update_field_infos_from_subselect(pAliases, pExpr->x.pSelect, pExclude); if (zName) { update_function_info(pAliases, zName, - pExpr->x.pSelect->pEList, sub_usage, pExclude); + pExpr->x.pSelect->pEList, pExclude); } } else { - update_field_infos_from_exprlist(pAliases, pExpr->x.pList, usage, pExclude); + update_field_infos_from_exprlist(pAliases, pExpr->x.pList, pExclude); if (zName) { update_function_info(pAliases, zName, - pExpr->x.pList, sub_usage, pExclude); + pExpr->x.pList, pExclude); } } } @@ -1170,7 +1140,6 @@ public: void update_field_infos_from_expr(QcAliases* pAliases, const Expr* pExpr, - uint32_t usage, const ExprList* pExclude) { const char* zDatabase; @@ -1179,33 +1148,31 @@ public: if (get_field_name(pExpr, &zDatabase, &zTable, &zColumn)) { - update_field_info(pAliases, zDatabase, zTable, zColumn, usage, pExclude); + update_field_info(pAliases, zDatabase, zTable, zColumn, pExclude); } } void update_field_infos_from_exprlist(QcAliases* pAliases, const ExprList* pEList, - uint32_t usage, const ExprList* pExclude) { for (int i = 0; i < pEList->nExpr; ++i) { ExprList::ExprList_item* pItem = &pEList->a[i]; - update_field_infos(pAliases, 0, pItem->pExpr, usage, QC_TOKEN_MIDDLE, pExclude); + update_field_infos(pAliases, 0, pItem->pExpr, QC_TOKEN_MIDDLE, pExclude); } } void update_field_infos_from_idlist(QcAliases* pAliases, const IdList* pIds, - uint32_t usage, const ExprList* pExclude) { for (int i = 0; i < pIds->nId; ++i) { IdList::IdList_item* pItem = &pIds->a[i]; - update_field_info(pAliases, NULL, NULL, pItem->zName, usage, pExclude); + update_field_info(pAliases, NULL, NULL, pItem->zName, pExclude); } } @@ -1217,7 +1184,6 @@ public: void update_field_infos_from_select(QcAliases& aliases, const Select* pSelect, - uint32_t usage, const ExprList* pExclude, compound_approach_t compound_approach = ANALYZE_COMPOUND_SELECTS) { @@ -1234,12 +1200,7 @@ public: if (pSrc->a[i].pSelect) { - uint32_t sub_usage = usage; - - sub_usage &= ~QC_USED_IN_SELECT; - sub_usage |= QC_USED_IN_SUBSELECT; - - update_field_infos_from_select(aliases, pSrc->a[i].pSelect, sub_usage, pExclude); + update_field_infos_from_select(aliases, pSrc->a[i].pSelect, pExclude); } #ifdef QC_COLLECT_NAMES_FROM_USING @@ -1257,20 +1218,20 @@ public: if (pSelect->pEList) { - update_field_infos_from_exprlist(&aliases, pSelect->pEList, usage, NULL); + update_field_infos_from_exprlist(&aliases, pSelect->pEList, NULL); } if (pSelect->pWhere) { m_has_clause = true; update_field_infos(&aliases, - 0, pSelect->pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, pSelect->pEList); + 0, pSelect->pWhere, QC_TOKEN_MIDDLE, pSelect->pEList); } if (pSelect->pGroupBy) { update_field_infos_from_exprlist(&aliases, - pSelect->pGroupBy, QC_USED_IN_GROUP_BY, pSelect->pEList); + pSelect->pGroupBy, pSelect->pEList); } if (pSelect->pHaving) @@ -1296,7 +1257,7 @@ public: while (pPrior) { - update_field_infos_from_subselect(&aliases, pPrior, usage, pExclude, + update_field_infos_from_subselect(&aliases, pPrior, pExclude, IGNORE_COMPOUND_SELECTS); pPrior = pPrior->pPrior; } @@ -1306,13 +1267,12 @@ public: void update_field_infos_from_subselect(QcAliases* pAliases, const Select* pSelect, - uint32_t usage, const ExprList* pExclude, compound_approach_t compound_approach = ANALYZE_COMPOUND_SELECTS) { QcAliases aliases(*pAliases); - update_field_infos_from_select(aliases, pSelect, usage, pExclude, compound_approach); + update_field_infos_from_select(aliases, pSelect, pExclude, compound_approach); } void update_field_infos_from_with(QcAliases* pAliases, const With* pWith) @@ -1323,7 +1283,7 @@ public: if (pCte->pSelect) { - update_field_infos_from_subselect(pAliases, pCte->pSelect, QC_USED_IN_SUBSELECT, NULL); + update_field_infos_from_subselect(pAliases, pCte->pSelect, NULL); } } } @@ -1349,20 +1309,20 @@ public: const char* zDatabase, const char* zTable, const char* zColumn, - vector& fields) + vector& fields) { ss_dassert(zColumn); honour_aliases(pAliases, &zDatabase, &zTable); - MatchFieldName predicate(zDatabase, zTable, zColumn); + MatchFieldName predicate(zDatabase, zTable, zColumn); - vector::iterator i = find_if(fields.begin(), fields.end(), predicate); + vector::iterator i = find_if(fields.begin(), fields.end(), predicate); if (i == fields.end()) // Not present { //TODO: Add exclusion? - QC_FIELD_NAME item; + QC_FIELD_INFO item; item.database = zDatabase ? MXS_STRDUP(zDatabase) : NULL; item.table = zTable ? MXS_STRDUP(zTable) : NULL; @@ -1378,7 +1338,7 @@ public: static void update_function_fields(const QcAliases* pAliases, const Expr* pExpr, const ExprList* pExclude, - vector& fields) + vector& fields) { const char* zDatabase; const char* zTable; @@ -1410,7 +1370,7 @@ public: static void update_function_fields(const QcAliases* pAliases, const ExprList* pEList, const ExprList* pExclude, - vector& fields) + vector& fields) { for (int i = 0; i < pEList->nExpr; ++i) { @@ -1424,7 +1384,6 @@ public: const char* name, const Expr* pExpr, const ExprList* pEList, - uint32_t usage, const ExprList* pExclude) { @@ -1440,7 +1399,7 @@ public: name = map_function_name(m_pFunction_name_mappings, name); - QC_FUNCTION_INFO item = { (char*)name, usage }; + QC_FUNCTION_INFO item = { (char*)name }; size_t i; for (i = 0; i < m_function_infos.size(); ++i) @@ -1467,14 +1426,10 @@ public: m_function_field_usage.resize(m_function_field_usage.size() + 1); } } - else - { - m_function_infos[i].usage |= usage; - } if (pExpr || pEList) { - vector& fields = m_function_field_usage[i]; + vector& fields = m_function_field_usage[i]; if (pExpr) { @@ -1500,28 +1455,25 @@ public: int update_function_info(const QcAliases* pAliases, const char* name, const Expr* pExpr, - uint32_t usage, const ExprList* pExclude) { - return update_function_info(pAliases, name, pExpr, NULL, usage, pExclude); + return update_function_info(pAliases, name, pExpr, NULL, pExclude); } int update_function_info(const QcAliases* pAliases, const char* name, const ExprList* pEList, - uint32_t usage, const ExprList* pExclude) { - return update_function_info(pAliases, name, NULL, pEList, usage, pExclude); + return update_function_info(pAliases, name, NULL, pEList, pExclude); } int update_function_info(const QcAliases* pAliases, const char* name, - uint32_t usage, const ExprList* pExclude) { - return update_function_info(pAliases, name, NULL, NULL, usage, pExclude); + return update_function_info(pAliases, name, NULL, NULL, pExclude); } // @@ -1681,7 +1633,7 @@ public: if (pSelect) { - update_field_infos_from_select(aliases, pSelect, QC_USED_IN_SELECT, NULL); + update_field_infos_from_select(aliases, pSelect, NULL); } exposed_sqlite3ExprListDelete(pParse->db, pCNames); @@ -1751,7 +1703,7 @@ public: if (pWhere) { - update_field_infos(&aliases, 0, pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, 0); + update_field_infos(&aliases, 0, pWhere, QC_TOKEN_MIDDLE, 0); } } @@ -1806,7 +1758,7 @@ public: if (pSelect) { QcAliases aliases; - update_field_infos_from_select(aliases, pSelect, QC_USED_IN_SELECT, NULL); + update_field_infos_from_select(aliases, pSelect, NULL); } else if (pOldTable) { @@ -1839,28 +1791,37 @@ public: if (pColumns) { - update_field_infos_from_idlist(&aliases, pColumns, 0, NULL); + update_field_infos_from_idlist(&aliases, pColumns, NULL); + + int i = update_function_info(&aliases, "=", NULL); + + if (i != -1) + { + vector& fields = m_function_field_usage[i]; + + for (int j = 0; j < pColumns->nId; ++j) + { + update_function_fields(&aliases, NULL, NULL, pColumns->a[j].zName, fields); + } + + if (fields.size() != 0) + { + QC_FUNCTION_INFO& info = m_function_infos[i]; + + info.fields = &fields[0]; + info.n_fields = fields.size(); + } + } } if (pSelect) { - 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(aliases, pSelect, usage, NULL); + update_field_infos_from_select(aliases, pSelect, NULL); } if (pSet) { - update_field_infos_from_exprlist(&aliases, pSet, 0, NULL); + update_field_infos_from_exprlist(&aliases, pSet, NULL); } } @@ -1975,13 +1936,13 @@ public: ExprList::ExprList_item* pItem = &pChanges->a[i]; update_field_infos(&aliases, - 0, pItem->pExpr, QC_USED_IN_SET, QC_TOKEN_MIDDLE, NULL); + 0, pItem->pExpr, QC_TOKEN_MIDDLE, NULL); } } if (pWhere) { - update_field_infos(&aliases, 0, pWhere, QC_USED_IN_WHERE, QC_TOKEN_MIDDLE, pChanges); + update_field_infos(&aliases, 0, pWhere, QC_TOKEN_MIDDLE, pChanges); } } @@ -2015,10 +1976,8 @@ public: m_type_mask = QUERY_TYPE_READ; } - uint32_t usage = sub_select ? QC_USED_IN_SUBSELECT : QC_USED_IN_SELECT; - QcAliases aliases; - update_field_infos_from_select(aliases, pSelect, usage, NULL); + update_field_infos_from_select(aliases, pSelect, NULL); } void maxscaleAlterTable(Parse *pParse, /* Parser context. */ @@ -2062,7 +2021,7 @@ public: if (pExprList) { - update_field_infos_from_exprlist(NULL, pExprList, 0, NULL); + update_field_infos_from_exprlist(NULL, pExprList, NULL); } exposed_sqlite3SrcListDelete(pParse->db, pName); @@ -2891,8 +2850,7 @@ public: if (pValue->op == TK_SELECT) { QcAliases aliases; - update_field_infos_from_select(aliases, pValue->x.pSelect, - QC_USED_IN_SUBSELECT, NULL); + update_field_infos_from_select(aliases, pValue->x.pSelect, NULL); } } break; @@ -2918,8 +2876,6 @@ public: m_status = QC_QUERY_PARSED; m_operation = QUERY_OP_SHOW; - uint32_t u = QC_USED_IN_SELECT; - switch (pShow->what) { case MXS_SHOW_COLUMNS: @@ -3248,7 +3204,7 @@ public: GWBUF* m_pPreparable_stmt; // The preparable statement. vector m_field_infos; // Vector of fields used by the statement. vector m_function_infos; // Vector of functions used by the statement. - vector > m_function_field_usage; // Vector of vector fields used by functions + vector > m_function_field_usage; // Vector of vector fields used by functions // of the statement. Data referred to from // m_function_infos size_t m_function_infos_len; // The used entries in function_infos. @@ -3307,7 +3263,7 @@ extern void maxscaleShow(Parse*, MxsShow* pShow); extern void maxscaleTruncate(Parse*, Token* pDatabase, Token* pName); extern void maxscaleUse(Parse*, Token*); -extern void maxscale_update_function_info(const char* name, const Expr* pExpr, unsigned usage); +extern void maxscale_update_function_info(const char* name, const Expr* pExpr); extern void maxscaleComment(); extern int maxscaleKeyword(int token); @@ -3682,12 +3638,12 @@ static bool should_exclude(const char* zName, const ExprList* pExclude) return i != pExclude->nExpr; } -extern void maxscale_update_function_info(const char* name, const Expr* pExpr, uint32_t usage) +extern void maxscale_update_function_info(const char* name, const Expr* pExpr) { QcSqliteInfo* pInfo = this_thread.pInfo; ss_dassert(pInfo); - pInfo->update_function_info(NULL, name, pExpr, usage, NULL); + pInfo->update_function_info(NULL, name, pExpr, NULL); } static const char* get_token_symbol(int token) 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 b7a748bdc..f1aff5ac8 100644 --- a/query_classifier/qc_sqlite/sqlite-src-3110100/src/parse.y +++ b/query_classifier/qc_sqlite/sqlite-src-3110100/src/parse.y @@ -130,7 +130,7 @@ extern void maxscaleShow(Parse*, MxsShow* pShow); extern void maxscaleTruncate(Parse*, Token* pDatabase, Token* pName); extern void maxscaleUse(Parse*, Token*); -extern void maxscale_update_function_info(const char* name, const Expr* pExpr, unsigned usage); +extern void maxscale_update_function_info(const char* name, const Expr* pExpr); // Exposed utility functions void exposed_sqlite3ExprDelete(sqlite3 *db, Expr *pExpr) @@ -1205,7 +1205,7 @@ selcollist(A) ::= sclp(P) MATCH LP id(X) RP AGAINST LP expr(Y) RP. { // Could be a subselect as well, but we just don't know it at this point. sqlite3ExprDelete(pParse->db, Y.pExpr); Expr *p = sqlite3PExpr(pParse, TK_ID, 0, 0, &X); - maxscale_update_function_info("match", p, QC_USED_IN_SELECT); + maxscale_update_function_info("match", p); A = sqlite3ExprListAppend(pParse, P, p); } %endif diff --git a/query_classifier/test/compare.cc b/query_classifier/test/compare.cc index 17acd4e1c..86f6193ce 100644 --- a/query_classifier/test/compare.cc +++ b/query_classifier/test/compare.cc @@ -855,7 +855,6 @@ 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 @@ -863,8 +862,7 @@ public: return m_database == rhs.m_database && m_table == rhs.m_table && - m_column == rhs.m_column && - m_usage == rhs.m_usage; + m_column == rhs.m_column; } bool lt(const QcFieldInfo& rhs) const @@ -891,18 +889,7 @@ public: } else { - if (m_column < rhs.m_column) - { - rv = true; - } - else if (m_column > rhs.m_column) - { - rv = false; - } - else - { - rv = (m_usage < rhs.m_usage); - } + rv = m_column < rhs.m_column; } } @@ -917,35 +904,6 @@ public: m_column == o.m_column; } - static bool at_most_usage_differs(const std::set& l, - const std::set& r) - { - bool rv = false; - - if (l.size() == r.size()) - { - rv = true; - - std::set::iterator i = l.begin(); - std::set::iterator j = r.begin(); - - while (rv && (i != l.end())) - { - if (!i->has_same_name(*j)) - { - rv = false; - } - else - { - ++i; - ++j; - } - } - } - - return rv; - } - void print(ostream& out) const { if (!m_database.empty()) @@ -961,19 +919,12 @@ 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) @@ -1040,11 +991,6 @@ bool compare_get_field_info(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1, ss << f1; success = true; } - else if (QcFieldInfo::at_most_usage_differs(f1, f2)) - { - ss << "WRN: " << f1 << " != " << f2; - success = true; - } else { ss << "ERR: " << f1 << " != " << f2; @@ -1061,7 +1007,6 @@ class QcFunctionInfo public: QcFunctionInfo(const QC_FUNCTION_INFO& info) : m_name(info.name) - , m_usage(info.usage) , m_pFields(info.fields) , m_nFields(info.n_fields) { @@ -1073,7 +1018,6 @@ public: { return m_name == rhs.m_name && - m_usage == rhs.m_usage && have_same_fields(*this, rhs); } @@ -1089,14 +1033,6 @@ public: { rv = false; } - else if (m_usage < rhs.m_usage) - { - rv = true; - } - else if (m_usage > rhs.m_usage) - { - rv = false; - } else { std::set lfs; @@ -1111,37 +1047,6 @@ public: return rv; } - static bool at_most_usage_differs(const std::set& l, - const std::set& r) - { - bool rv = false; - - if (l.size() == r.size()) - { - rv = true; - - std::set::iterator i = l.begin(); - std::set::iterator j = r.begin(); - - while (rv && (i != l.end())) - { - if (i->m_name != j->m_name) - { - rv = false; - } - else if (!have_same_fields(*i, *j)) - { - rv = false; - } - - ++i; - ++j; - } - } - - return rv; - } - void print(ostream& out) const { out << m_name; @@ -1150,7 +1055,7 @@ public: for (uint32_t i = 0; i < m_nFields; ++i) { - const QC_FIELD_NAME& name = m_pFields[i]; + const QC_FIELD_INFO& name = m_pFields[i]; if (name.database) { @@ -1173,12 +1078,6 @@ public: } out << ")"; - - out << "["; - char* s = qc_field_usage_mask_to_string(m_usage); - out << s; - free(s); - out << "]"; } private: @@ -1208,7 +1107,7 @@ private: return rv; } - static std::string get_field_name(const QC_FIELD_NAME& field) + static std::string get_field_name(const QC_FIELD_INFO& field) { string s; @@ -1226,13 +1125,14 @@ private: s += field.column; + std::transform(s.begin(), s.end(), s.begin(), tolower); + return s; } private: std::string m_name; - uint32_t m_usage; - const QC_FIELD_NAME* m_pFields; + const QC_FIELD_INFO* m_pFields; uint32_t m_nFields; }; @@ -1300,11 +1200,6 @@ bool compare_get_function_info(QUERY_CLASSIFIER* pClassifier1, GWBUF* pCopy1, ss << f1; success = true; } - else if (QcFunctionInfo::at_most_usage_differs(f1, f2)) - { - ss << "WRN: " << f1 << " != " << f2; - success = true; - } else { ss << "ERR: " << f1 << " != " << f2; diff --git a/query_classifier/test/cte_recursive.test b/query_classifier/test/cte_recursive.test index a319b9aeb..9a285e948 100644 --- a/query_classifier/test/cte_recursive.test +++ b/query_classifier/test/cte_recursive.test @@ -248,41 +248,44 @@ as select ancestors.name, ancestors.dob from ancestors; --echo # recursive definition with two attached non-recursive -with recursive -ancestors(id,name,dob) -as -( - with - father(child_id,id,name,dob) - as - ( - select folks.id, f.id, f.name, f.dob - from folks, folks f - where folks.father=f.id - - ), - mother(child_id,id,name,dob) - as - ( - select folks.id, m.id, m.name, m.dob - from folks, folks m - where folks.mother=m.id - - ) - select folks.id, folks.name, folks.dob - from folks - where name='Me' - union - select f.id, f.name, f.dob - from ancestors a, father f - where f.child_id=a.id - union - select m.id, m.name, m.dob - from ancestors a, mother m - where m.child_id=a.id - -) -select ancestors.name, ancestors.dob from ancestors; +#MXS qc_sqlite +#MXS qc_get_function_info : ERR: =(folks.name, folks.father, folks.id, folks.mother, mother.child_id, ancestors.id, father.child_id) != =(mother.child_id, ancestors.id, folks.father, folks.id, folks.mother, mother.id, father.child_id, name) +#MXS The reported function arguments to '=' are disjoint. The relevant are there though (folks.*). +#MXS with recursive +#MXS ancestors(id,name,dob) +#MXS as +#MXS ( +#MXS with +#MXS father(child_id,id,name,dob) +#MXS as +#MXS ( +#MXS select folks.id, f.id, f.name, f.dob +#MXS from folks, folks f +#MXS where folks.father=f.id +#MXS +#MXS ), +#MXS mother(child_id,id,name,dob) +#MXS as +#MXS ( +#MXS select folks.id, m.id, m.name, m.dob +#MXS from folks, folks m +#MXS where folks.mother=m.id +#MXS +#MXS ) +#MXS select folks.id, folks.name, folks.dob +#MXS from folks +#MXS where name='Me' +#MXS union +#MXS select f.id, f.name, f.dob +#MXS from ancestors a, father f +#MXS where f.child_id=a.id +#MXS union +#MXS select m.id, m.name, m.dob +#MXS from ancestors a, mother m +#MXS where m.child_id=a.id +#MXS +#MXS ) +#MXS select ancestors.name, ancestors.dob from ancestors; --echo # simple recursion with one anchor and one recursive select --echo # the anchor is the first select in the specification diff --git a/query_classifier/test/delete.test b/query_classifier/test/delete.test index 285446768..404a8b4b4 100644 --- a/query_classifier/test/delete.test +++ b/query_classifier/test/delete.test @@ -169,7 +169,9 @@ drop table t1; # create table t1(f1 int primary key); insert into t1 values (4),(3),(1),(2); -delete from t1 where (@a:= f1) order by f1 limit 1; +#MXS qc_myselembedded +#MXS qc_get_function_info : ERR: != =(f1) +#MXS delete from t1 where (@a:= f1) order by f1 limit 1; select @a; drop table t1; diff --git a/query_classifier/test/join.test b/query_classifier/test/join.test index 35958291d..7a5518b6b 100644 --- a/query_classifier/test/join.test +++ b/query_classifier/test/join.test @@ -677,8 +677,10 @@ insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); create table t2 (a int, b int, filler char(100), key(a), key(b)); create table t3 (a int, b int, filler char(100), key(a), key(b)); -insert into t2 - select @a:= A.a + 10*(B.a + 10*C.a), @a, 'filler' from t1 A, t1 B, t1 C; +#MXS qc_mysqlembedded +#MXS qc_get_function_info : ERR: *(t1.a) +(t1.a) != *(t1.a) +(t1.a) =() +#MXS insert into t2 +#MXS select @a:= A.a + 10*(B.a + 10*C.a), @a, 'filler' from t1 A, t1 B, t1 C; insert into t3 select * from t2 where a < 800; # The order of tables must be t2,t3: @@ -692,7 +694,9 @@ create table t1 (a int); insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9); create table t2 (a int, b int, primary key(a)); -insert into t2 select @v:=A.a+10*B.a, @v from t1 A, t1 B; +#MXS qc_mysqlembedded +#MXS qc_get_function_info : ERR: *(t1.a) +(t1.a) != *(t1.a) +(t1.a) =() +#MXS insert into t2 select @v:=A.a+10*B.a, @v from t1 A, t1 B; explain select * from t1; show status like '%cost%'; diff --git a/query_classifier/test/qc_mysqlembedded_unsupported.test b/query_classifier/test/qc_mysqlembedded_unsupported.test index c4b487996..091a22461 100644 --- a/query_classifier/test/qc_mysqlembedded_unsupported.test +++ b/query_classifier/test/qc_mysqlembedded_unsupported.test @@ -31,3 +31,16 @@ select * from (select a from t1 where b >= 'c') as t union select * from (select a from t1 where b >= 'c') as t where t.a >= 4; + +#MXS qc_myselembedded +#MXS qc_get_function_info : ERR: != =(f1) +delete from t1 where (@a:= f1) order by f1 limit 1; + +#MXS qc_mysqlembedded +#MXS qc_get_function_info : ERR: *(t1.a) +(t1.a) != *(t1.a) +(t1.a) =() +insert into t2 + select @a:= A.a + 10*(B.a + 10*C.a), @a, 'filler' from t1 A, t1 B, t1 C; + +#MXS qc_mysqlembedded +#MXS qc_get_function_info : ERR: *(t1.a) +(t1.a) != *(t1.a) +(t1.a) =() +insert into t2 select @v:=A.a+10*B.a, @v from t1 A, t1 B; diff --git a/query_classifier/test/qc_sqlite_unsupported.test b/query_classifier/test/qc_sqlite_unsupported.test index f1ca70e1b..206dd7709 100644 --- a/query_classifier/test/qc_sqlite_unsupported.test +++ b/query_classifier/test/qc_sqlite_unsupported.test @@ -102,3 +102,7 @@ select t2.fld3 from t2 where companynr = 58 and fld3 like "%imaginable%"; #MXS qc_sqlite #MXS qc_get_function_info : ERR: >(a, b)[QC_USED_IN_WHERE] avg(a)[QC_USED_IN_SUBSELECT|QC_USED_IN_WHERE] != >(a)[QC_USED_IN_WHERE] avg(a)[QC_USED_IN_SUBSELECT|QC_USED_IN_WHERE] CREATE VIEW v1 AS SELECT a,1 as b FROM t1 WHERE a>(SELECT AVG(a) FROM t1) AND b>(SELECT 1); + +#MXS qc_sqlite +#MXS qc_get_function_info : ERR: =(t2.fld3) != =(fld3) +select t2.fld3 from t2 where fld3 = 'honeysuckle'; diff --git a/query_classifier/test/select.test b/query_classifier/test/select.test index de5761cb0..889983673 100644 --- a/query_classifier/test/select.test +++ b/query_classifier/test/select.test @@ -1283,7 +1283,9 @@ select fld3 from t2 order by fld3 desc limit 5,5; # The table is read directly with read-next on fld3 # -select t2.fld3 from t2 where fld3 = 'honeysuckle'; +#MXS qc_sqlite +#MXS qc_get_function_info : ERR: =(t2.fld3) != =(fld3) +#MXS select t2.fld3 from t2 where fld3 = 'honeysuckle'; # MXS: qc_get_function_info : ERR: =()[QC_USED_IN_WHERE] like(t2.fld3)[QC_USED_IN_WHERE] != =()[QC_USED_IN_WHERE] like(fld3)[QC_USED_IN_WHERE] #select t2.fld3 from t2 where fld3 LIKE 'honeysuckl_'; select fld3 from t2 where fld3 LIKE 'honeysuckl_'; diff --git a/server/core/query_classifier.cc b/server/core/query_classifier.cc index 033a90852..18fc15508 100644 --- a/server/core/query_classifier.cc +++ b/server/core/query_classifier.cc @@ -352,141 +352,6 @@ GWBUF* qc_get_preparable_stmt(GWBUF* stmt) return preparable_stmt; } -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) diff --git a/server/modules/filter/cache/rules.cc b/server/modules/filter/cache/rules.cc index 35a4e3c6e..112413859 100644 --- a/server/modules/filter/cache/rules.cc +++ b/server/modules/filter/cache/rules.cc @@ -1144,55 +1144,52 @@ static bool cache_rule_matches_column_regexp(CACHE_RULE *self, { const QC_FIELD_INFO *info = (infos + i); - if (info->usage & QC_USED_IN_SELECT) + size_t database_len; + const char *database; + + if (info->database) { - size_t database_len; - const char *database; - - if (info->database) - { - database = info->database; - database_len = strlen(info->database); - } - else - { - database = default_database; - database_len = default_database_len; - } - - size_t table_len; - const char *table; - - if (info->table) - { - table = info->table; - table_len = strlen(info->table); - } - else - { - table = default_table; - table_len = default_table_len; - } - - char buffer[database_len + 1 + table_len + strlen(info->column) + 1]; - buffer[0] = 0; - - if (database) - { - strcat(buffer, database); - strcat(buffer, "."); - } - - if (table) - { - strcat(buffer, table); - strcat(buffer, "."); - } - - strcat(buffer, info->column); - - matches = cache_rule_compare(self, thread_id, buffer); + database = info->database; + database_len = strlen(info->database); } + else + { + database = default_database; + database_len = default_database_len; + } + + size_t table_len; + const char *table; + + if (info->table) + { + table = info->table; + table_len = strlen(info->table); + } + else + { + table = default_table; + table_len = default_table_len; + } + + char buffer[database_len + 1 + table_len + strlen(info->column) + 1]; + buffer[0] = 0; + + if (database) + { + strcat(buffer, database); + strcat(buffer, "."); + } + + if (table) + { + strcat(buffer, table); + strcat(buffer, "."); + } + + strcat(buffer, info->column); + + matches = cache_rule_compare(self, thread_id, buffer); ++i; } @@ -1280,84 +1277,81 @@ static bool cache_rule_matches_column_simple(CACHE_RULE *self, const char *defau { const QC_FIELD_INFO *info = (infos + i); - if (info->usage & QC_USED_IN_SELECT) + if ((strcasecmp(info->column, rule_column) == 0) || strcmp(rule_column, "*") == 0) { - if ((strcasecmp(info->column, rule_column) == 0) || strcmp(rule_column, "*") == 0) + if (rule_table) { - if (rule_table) + const char* check_table = info->table ? info->table : default_table; + + if (check_table) { - const char* check_table = info->table ? info->table : default_table; - - if (check_table) + if (strcasecmp(check_table, rule_table) == 0) { - if (strcasecmp(check_table, rule_table) == 0) + if (rule_database) { - if (rule_database) - { - const char *check_database = - info->database ? info->database : default_database; + const char *check_database = + info->database ? info->database : default_database; - if (check_database) + if (check_database) + { + if (strcasecmp(check_database, rule_database) == 0) { - if (strcasecmp(check_database, rule_database) == 0) - { - // The column, table and database matched. - matches = true; - } - else - { - // The column, table matched but the database did not. - matches = false; - } + // The column, table and database matched. + matches = true; } else { - // If the rules specify a database but we do not know the database, - // we consider the databases not to match. + // The column, table matched but the database did not. matches = false; } } else { - // If the rule specifies no database, then if the column and the table - // matches, the rule matches. - matches = true; + // If the rules specify a database but we do not know the database, + // we consider the databases not to match. + matches = false; } } else { - // The column matched, but the table did not. - matches = false; + // If the rule specifies no database, then if the column and the table + // matches, the rule matches. + matches = true; } } else { - // If the rules specify a table but we do not know the table, we - // consider the tables not to match. + // The column matched, but the table did not. matches = false; } } else { - // The column matched and there is no table rule. - matches = true; + // If the rules specify a table but we do not know the table, we + // consider the tables not to match. + matches = false; } } else { - // The column did not match. - matches = false; - } - - if (self->op == CACHE_OP_NEQ) - { - matches = !matches; + // The column matched and there is no table rule. + matches = true; } } + else + { + // The column did not match. + matches = false; + } - ++i; + if (self->op == CACHE_OP_NEQ) + { + matches = !matches; + } } + ++i; + if (tables) { for (i = 0; i < (size_t)n_tables; ++i)