diff --git a/mysql-test/suite/federated/federatedx_create_handlers.result b/mysql-test/suite/federated/federatedx_create_handlers.result index eaf9bda6f383e..231862f76ad26 100644 --- a/mysql-test/suite/federated/federatedx_create_handlers.result +++ b/mysql-test/suite/federated/federatedx_create_handlers.result @@ -1140,6 +1140,111 @@ t3_myisam2 t3_myisam3 SELECT * FROM federated.t1 UNION SELECT * FROM t3 ORDER BY 2; ERROR 42S22: Unknown column '2' in 'order clause' +# +# MDEV-32382 FederatedX error on pushdown of statement having CTE +# +# Single SELECT with CTE +WITH cte AS (SELECT * FROM federated.t1) +SELECT * FROM cte; +a +bcd +abc +cde +explain extended WITH cte AS (SELECT * FROM federated.t1) +SELECT * FROM cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PUSHED SELECT NULL NULL NULL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 with cte as (/* select#2 */ select `federated`.`t1`.`a` AS `a` from `federated`.`t1`)/* select#1 */ select `cte`.`a` AS `a` from `cte` +# Pushdown of a UNION having CTE's +WITH cte AS (SELECT * FROM federated.t1), +cte2 AS (SELECT * FROM federated.t2) +SELECT * FROM cte +UNION +SELECT * FROM cte2; +a +abc +bcd +cde +def +efg +explain extended WITH cte AS (SELECT * FROM federated.t1), +cte2 AS (SELECT * FROM federated.t2) +SELECT * FROM cte +UNION +SELECT * FROM cte2; +id select_type table type possible_keys key key_len ref rows filtered Extra +NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 with cte as (/* select#2 */ select `federated`.`t1`.`a` AS `a` from `federated`.`t1`), cte2 as (/* select#3 */ select `federated`.`t2`.`a` AS `a` from `federated`.`t2`)/* select#1 */ select `cte`.`a` AS `a` from `cte` union /* select#4 */ select `cte2`.`a` AS `a` from `cte2` +# Partial pushdown is not allowed for unions with CTE's, however a CTE +# may be pushed down a derived table +WITH cte AS (SELECT * FROM federated.t1) +SELECT * FROM cte +UNION ALL +SELECT * FROM t3; +a +abc +bcd +cde +t3_myisam1 +t3_myisam2 +t3_myisam3 +explain extended WITH cte AS (SELECT * FROM federated.t1) +SELECT * FROM cte +UNION ALL +SELECT * FROM t3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 3 100.00 +2 PUSHED DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL +3 UNION t3 ALL NULL NULL NULL NULL 3 100.00 +Warnings: +Note 1003 with cte as (/* select#2 */ select `federated`.`t1`.`a` AS `a` from `federated`.`t1`)/* select#1 */ select `cte`.`a` AS `a` from `cte` union all /* select#3 */ select `federated`.`t3`.`a` AS `a` from `federated`.`t3` +WITH cte AS (SELECT * FROM federated.t1 UNION SELECT * FROM t3) +SELECT * FROM cte; +a +abc +bcd +cde +t3_myisam1 +t3_myisam2 +t3_myisam3 +explain extended WITH cte AS (SELECT * FROM federated.t1 UNION SELECT * FROM t3) +SELECT * FROM cte; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 6 100.00 +2 DERIVED t1 ALL NULL NULL NULL NULL 3 100.00 +3 UNION t3 ALL NULL NULL NULL NULL 3 100.00 +NULL UNION RESULT ALL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 with cte as (/* select#2 */ select `federated`.`t1`.`a` AS `a` from `federated`.`t1` union /* select#3 */ select `federated`.`t3`.`a` AS `a` from `federated`.`t3`)/* select#1 */ select `cte`.`a` AS `a` from `cte` +# Two CTE's where one CTE refers to another +WITH cte AS (SELECT * FROM federated.t1), +cte2 AS (SELECT * FROM t3 +WHERE t3.a NOT IN (SELECT * FROM cte)) +SELECT * FROM cte JOIN cte2; +a a +abc t3_myisam1 +abc t3_myisam2 +abc t3_myisam3 +bcd t3_myisam1 +bcd t3_myisam2 +bcd t3_myisam3 +cde t3_myisam1 +cde t3_myisam2 +cde t3_myisam3 +explain extended WITH cte AS (SELECT * FROM federated.t1), +cte2 AS (SELECT * FROM t3 +WHERE t3.a NOT IN (SELECT * FROM cte)) +SELECT * FROM cte JOIN cte2; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY ALL NULL NULL NULL NULL 3 100.00 +1 PRIMARY t3 ALL NULL NULL NULL NULL 3 100.00 Using where; Using join buffer (flat, BNL join) +5 PUSHED DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL +4 MATERIALIZED ALL NULL NULL NULL NULL 3 100.00 +2 PUSHED DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL +Warnings: +Note 1003 with cte as (/* select#2 */ select `federated`.`t1`.`a` AS `a` from `federated`.`t1`), cte2 as (/* select#3 */ select `federated`.`t3`.`a` AS `a` from `federated`.`t3` where !<`federated`.`t3`.`a`>((`federated`.`t3`.`a`,`federated`.`t3`.`a` in ( (/* select#4 */ select `cte`.`a` from `cte` ), (`federated`.`t3`.`a` in on distinct_key where `federated`.`t3`.`a` = ``.`a`)))))/* select#1 */ select `cte`.`a` AS `a`,`federated`.`t3`.`a` AS `a` from `cte` join `federated`.`t3` where !<`federated`.`t3`.`a`>((`federated`.`t3`.`a`,`federated`.`t3`.`a` in ( (/* select#4 */ select `cte`.`a` from `cte` ), (`federated`.`t3`.`a` in on distinct_key where `federated`.`t3`.`a` = ``.`a`)))) connection master; DROP TABLES federated.t1, federated.t2, t3, t4, t5, t6, federated.t11, federated.t12, federated.t13, federated.t14; diff --git a/mysql-test/suite/federated/federatedx_create_handlers.test b/mysql-test/suite/federated/federatedx_create_handlers.test index f0cf366202387..e65972635e097 100644 --- a/mysql-test/suite/federated/federatedx_create_handlers.test +++ b/mysql-test/suite/federated/federatedx_create_handlers.test @@ -740,6 +740,55 @@ SELECT * FROM federated.t1 UNION SELECT * FROM t3 ORDER BY a; SELECT * FROM federated.t1 UNION SELECT * FROM t3 ORDER BY 2; +--echo # +--echo # MDEV-32382 FederatedX error on pushdown of statement having CTE +--echo # + +--echo # Single SELECT with CTE +let $query= WITH cte AS (SELECT * FROM federated.t1) + SELECT * FROM cte; +eval $query; +eval explain extended $query; + +--echo # Pushdown of a UNION having CTE's +let $query= WITH cte AS (SELECT * FROM federated.t1), + cte2 AS (SELECT * FROM federated.t2) + SELECT * FROM cte + UNION + SELECT * FROM cte2; +--sorted_result +eval $query; +eval explain extended $query; + +# CREATE TABLE t3 (a int); +# INSERT INTO t3 VALUES (101),(102),(103); + +--echo # Partial pushdown is not allowed for unions with CTE's, however a CTE +--echo # may be pushed down a derived table +let $query= WITH cte AS (SELECT * FROM federated.t1) + SELECT * FROM cte + UNION ALL + SELECT * FROM t3; +--sorted_result +eval $query; +eval explain extended $query; + +let $query= WITH cte AS (SELECT * FROM federated.t1 UNION SELECT * FROM t3) + SELECT * FROM cte; +--sorted_result +eval $query; +eval explain extended $query; + +--echo # Two CTE's where one CTE refers to another +let $query= WITH cte AS (SELECT * FROM federated.t1), + cte2 AS (SELECT * FROM t3 + WHERE t3.a NOT IN (SELECT * FROM cte)) + SELECT * FROM cte JOIN cte2; +--sorted_result +eval $query; +eval explain extended $query; + + # Cleanup connection master; DROP TABLES federated.t1, federated.t2, t3, t4, t5, t6, federated.t11, diff --git a/sql/select_handler.cc b/sql/select_handler.cc index e80e2548d5bb7..ab2a6850a6e70 100644 --- a/sql/select_handler.cc +++ b/sql/select_handler.cc @@ -200,3 +200,18 @@ void select_handler::print_error(int error, myf errflag) { my_error(ER_GET_ERRNO, MYF(0), error, hton_name(ht)->str); } + +select_pushdown_type select_handler::get_pushdown_type() +{ + /* + In the case of single SELECT select_lex is initialized and lex_unit==NULL, + in the case of whole UNIT select_lex == NULL and lex_unit is initialized, + in the case of partial pushdown both select_lex and lex_unit + are initialized + */ + if(!lex_unit) + return select_pushdown_type::SINGLE_SELECT; + + return select_lex ? select_pushdown_type::PART_OF_UNIT : + select_pushdown_type::WHOLE_UNIT; +} diff --git a/sql/select_handler.h b/sql/select_handler.h index ea59eab425006..207af43f56ca0 100644 --- a/sql/select_handler.h +++ b/sql/select_handler.h @@ -20,6 +20,12 @@ #include "mariadb.h" #include "sql_priv.h" +enum class select_pushdown_type { + SINGLE_SELECT, + PART_OF_UNIT, + WHOLE_UNIT +}; + /** @class select_handler @@ -50,7 +56,7 @@ class select_handler virtual bool prepare(); /* - Select_handler processes one of + Select_handler processes these cases: - single SELECT - whole unit (multiple SELECTs combined with UNION/EXCEPT/INTERSECT) - single SELECT that is part of a unit (partial pushdown) @@ -60,7 +66,7 @@ class select_handler in the case of partial pushdown both select_lex and lex_unit are initialized */ - SELECT_LEX *select_lex; // Single select to be executed + SELECT_LEX *select_lex; // Single select/part of a unit to be executed SELECT_LEX_UNIT *lex_unit; // Unit to be executed /* @@ -99,6 +105,8 @@ class select_handler TABLE *create_tmp_table(THD *thd); + select_pushdown_type get_pushdown_type(); + THD *thd; handlerton *ht; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 05b788b6e1d79..7414a8948a29a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5054,8 +5054,15 @@ select_handler *find_select_handler_inner(THD *thd, SELECT_LEX *select_lex, SELECT_LEX_UNIT *select_lex_unit) { - if (select_lex->master_unit()->outer_select()) + if (select_lex->master_unit()->outer_select() || + (select_lex_unit && select_lex->master_unit()->with_clause)) + { + /* + Pushdown is not supported neither for non-top-level SELECTs nor for parts + of SELECT_LEX_UNITs that have CTEs (SELECT_LEX_UNIT::with_clause) + */ return 0; + } TABLE_LIST *tbl= nullptr; // For SQLCOM_INSERT_SELECT the server takes TABLE_LIST diff --git a/storage/federatedx/federatedx_pushdown.cc b/storage/federatedx/federatedx_pushdown.cc index 7615e61ae093e..c4d77a73984df 100644 --- a/storage/federatedx/federatedx_pushdown.cc +++ b/storage/federatedx/federatedx_pushdown.cc @@ -276,18 +276,6 @@ federatedx_handler_base::federatedx_handler_base(THD *thd_arg, TABLE *tbl_arg) query_table(tbl_arg) {} -ha_federatedx_select_handler::ha_federatedx_select_handler( - THD *thd, SELECT_LEX *select_lex, TABLE *tbl) - : select_handler(thd, federatedx_hton, select_lex), - federatedx_handler_base(thd, tbl) -{ - query.length(0); - select_lex->print(thd, &query, - enum_query_type(QT_VIEW_INTERNAL | - QT_ITEM_ORIGINAL_FUNC_NULLIF | - QT_PARSABLE)); -} - ha_federatedx_select_handler::~ha_federatedx_select_handler() = default; ha_federatedx_select_handler::ha_federatedx_select_handler( @@ -296,10 +284,7 @@ ha_federatedx_select_handler::ha_federatedx_select_handler( federatedx_handler_base(thd, tbl) { query.length(0); - lex_unit->print(&query, - enum_query_type(QT_VIEW_INTERNAL | QT_SELECT_ONLY | - QT_ITEM_ORIGINAL_FUNC_NULLIF | - QT_PARSABLE)); + lex_unit->print(&query, PRINT_QUERY_TYPE); } ha_federatedx_select_handler::ha_federatedx_select_handler( @@ -308,10 +293,31 @@ ha_federatedx_select_handler::ha_federatedx_select_handler( federatedx_handler_base(thd, tbl) { query.length(0); - select_lex->print(thd, &query, - enum_query_type(QT_VIEW_INTERNAL | QT_SELECT_ONLY | - QT_ITEM_ORIGINAL_FUNC_NULLIF | - QT_PARSABLE)); + if (get_pushdown_type() == select_pushdown_type::SINGLE_SELECT) + { + /* + Must use SELECT_LEX_UNIT::print() instead of SELECT_LEX::print() here + to print possible CTEs which are stored at SELECT_LEX_UNIT::with_clause + */ + select_lex->master_unit()->print(&query, PRINT_QUERY_TYPE); + } + else if (get_pushdown_type() == select_pushdown_type::PART_OF_UNIT) + { + /* + CTEs are not supported for partial select pushdown so use + SELECT_LEX::print() here + */ + select_lex->print(thd, &query, PRINT_QUERY_TYPE); + } + else + { + /* + Other select_pushdown_types are not allowed in this constructor. + The case of select_pushdown_type::WHOLE_UNIT is handled at another + overload of the constuctor + */ + DBUG_ASSERT(0); + } } int federatedx_handler_base::init_scan_() diff --git a/storage/federatedx/federatedx_pushdown.h b/storage/federatedx/federatedx_pushdown.h index c2ce80b070880..1d5bdab137893 100644 --- a/storage/federatedx/federatedx_pushdown.h +++ b/storage/federatedx/federatedx_pushdown.h @@ -62,8 +62,6 @@ class ha_federatedx_derived_handler: public derived_handler, public federatedx_h class ha_federatedx_select_handler: public select_handler, public federatedx_handler_base { public: - ha_federatedx_select_handler(THD *thd_arg, SELECT_LEX *sel_lex, - TABLE *tbl); ha_federatedx_select_handler(THD *thd_arg, SELECT_LEX_UNIT *sel_unit, TABLE *tbl); ha_federatedx_select_handler(THD *thd_arg, SELECT_LEX *sel_lex, @@ -72,4 +70,9 @@ class ha_federatedx_select_handler: public select_handler, public federatedx_han int init_scan() { return federatedx_handler_base::init_scan_(); } int next_row() { return federatedx_handler_base::next_row_(table); } int end_scan(); + +private: + static constexpr auto PRINT_QUERY_TYPE= + enum_query_type(QT_VIEW_INTERNAL | QT_SELECT_ONLY | + QT_ITEM_ORIGINAL_FUNC_NULLIF | QT_PARSABLE); };