diff --git a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc index 9b5e2fc09..4bbe9b1cb 100644 --- a/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc +++ b/query_classifier/qc_mysqlembedded/qc_mysqlembedded.cc @@ -74,6 +74,20 @@ #include #include +#if MYSQL_VERSION_MAJOR >= 10 && MYSQL_VERSION_MINOR >= 2 +#define CTE_SUPPORTED +#endif + +#if defined(CTE_SUPPORTED) +// We need to be able to access private data of With_element that has no +// public access methods. So, we use this very questionable method of +// making the private parts public. Ok, as qc_myselembedded is only +// used for verifying the output of qc_sqlite. +#define private public +#include +#undef private +#endif + /** * Defines what a particular name should be mapped to. */ @@ -159,7 +173,6 @@ static bool parse_query(GWBUF* querybuf); static bool query_is_parsed(GWBUF* buf); int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_t* n_infos); - #if MYSQL_VERSION_MAJOR >= 10 && MYSQL_VERSION_MINOR >= 3 inline void get_string_and_length(const LEX_CSTRING& ls, const char** s, size_t* length) { @@ -2725,6 +2738,22 @@ static void update_field_infos(parsing_info_t* pi, } } +#ifdef CTE_SUPPORTED +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); + } +} +#endif + static void update_field_infos(parsing_info_t* pi, LEX* lex, st_select_lex* select, @@ -2840,6 +2869,33 @@ int32_t qc_mysql_get_field_info(GWBUF* buf, const QC_FIELD_INFO** infos, uint32_ update_field_infos(pi, lex, &lex->select_lex, usage, NULL); +#ifdef CTE_SUPPORTED + if (lex->with_clauses_list) + { + With_clause* with_clause = lex->with_clauses_list; + + while (with_clause) + { + SQL_I_List& with_list = with_clause->with_list; + With_element* element = with_list.first; + + while (element) + { + update_field_infos(pi, lex, element->spec, usage, NULL); + + if (element->first_recursive) + { + update_field_infos(pi, lex, element->first_recursive, usage, NULL); + } + + element = element->next; + } + + with_clause = with_clause->next_with_clause; + } + } +#endif + List_iterator ilist(lex->value_list); while (Item* item = ilist++) { diff --git a/query_classifier/qc_sqlite/qc_sqlite.c b/query_classifier/qc_sqlite/qc_sqlite.c index 4bac109f1..8eecb6b79 100644 --- a/query_classifier/qc_sqlite/qc_sqlite.c +++ b/query_classifier/qc_sqlite/qc_sqlite.c @@ -220,6 +220,8 @@ static void update_field_infos_from_select(QC_SQLITE_INFO* info, const Select* pSelect, uint32_t usage, const ExprList* pExclude); +static void update_field_infos_from_with(QC_SQLITE_INFO* info, + const With* pWith); static void update_function_info(QC_SQLITE_INFO* info, const char* name, uint32_t usage); @@ -1070,6 +1072,20 @@ static void update_field_infos_from_expr(QC_SQLITE_INFO* info, } } +static void update_field_infos_from_with(QC_SQLITE_INFO* info, + const With* pWith) +{ + for (int i = 0; i < pWith->nCte; ++i) + { + const struct Cte* pCte = &pWith->a[i]; + + if (pCte->pSelect) + { + update_field_infos_from_select(info, pCte->pSelect, QC_USED_IN_SELECT, NULL); + } + } +} + static const char* get_token_symbol(int token) { switch (token) @@ -1466,6 +1482,11 @@ static void update_field_infos_from_select(QC_SQLITE_INFO* info, update_field_infos(info, 0, pSelect->pHaving, 0, QC_TOKEN_MIDDLE, pSelect->pEList); #endif } + + if (pSelect->pWith) + { + update_field_infos_from_with(info, pSelect->pWith); + } } static void update_database_names(QC_SQLITE_INFO* info, const char* zDatabase)