Commit 855356ca authored by Oleg Smirnov's avatar Oleg Smirnov

MDEV-32382 FederatedX error on pushdown of statements having CTE

Pushing down statements to FederatedX engine is implemented by
printing either SELECT_LEX or SELECT_LEX_UNIT into a string and
sending that string to the engine. In the case of pushing down a
single SELECT having a CTE (WITH clause) there was a problem, because
normally single SELECTs were printed using SELECT_LEX::print().
But CTEs are stored in the upper unit of the SELECT_LEX -
SELECT_LEX_UNIT, so they were not unfolded in the string produced.

The solution is to invoke SELECT_LEX_UNIT::print() when pushing down
single SELECT statements (but not those which are parts of units),
so the possible CTEs are unfolded and printed.

Reviewed by Sergei Petrunia (sergey@mariadb.com)
parent 9b2a65e4
...@@ -1140,6 +1140,111 @@ t3_myisam2 ...@@ -1140,6 +1140,111 @@ t3_myisam2
t3_myisam3 t3_myisam3
SELECT * FROM federated.t1 UNION SELECT * FROM t3 ORDER BY 2; SELECT * FROM federated.t1 UNION SELECT * FROM t3 ORDER BY 2;
ERROR 42S22: Unknown column '2' in 'order clause' 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 <derived2> 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 <derived2> 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 <union2,3> 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 <derived5> 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 <derived2> 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 !<expr_cache><`federated`.`t3`.`a`>(<in_optimizer>(`federated`.`t3`.`a`,`federated`.`t3`.`a` in ( <materialize> (/* select#4 */ select `cte`.`a` from `cte` ), <primary_index_lookup>(`federated`.`t3`.`a` in <temporary table> on distinct_key where `federated`.`t3`.`a` = `<subquery4>`.`a`)))))/* select#1 */ select `cte`.`a` AS `a`,`federated`.`t3`.`a` AS `a` from `cte` join `federated`.`t3` where !<expr_cache><`federated`.`t3`.`a`>(<in_optimizer>(`federated`.`t3`.`a`,`federated`.`t3`.`a` in ( <materialize> (/* select#4 */ select `cte`.`a` from `cte` ), <primary_index_lookup>(`federated`.`t3`.`a` in <temporary table> on distinct_key where `federated`.`t3`.`a` = `<subquery4>`.`a`))))
connection master; connection master;
DROP TABLES federated.t1, federated.t2, t3, t4, t5, t6, federated.t11, DROP TABLES federated.t1, federated.t2, t3, t4, t5, t6, federated.t11,
federated.t12, federated.t13, federated.t14; federated.t12, federated.t13, federated.t14;
......
...@@ -740,6 +740,55 @@ SELECT * FROM federated.t1 UNION SELECT * FROM t3 ORDER BY a; ...@@ -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; 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 # Cleanup
connection master; connection master;
DROP TABLES federated.t1, federated.t2, t3, t4, t5, t6, federated.t11, DROP TABLES federated.t1, federated.t2, t3, t4, t5, t6, federated.t11,
......
...@@ -200,3 +200,18 @@ void select_handler::print_error(int error, myf errflag) ...@@ -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); 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;
}
...@@ -20,6 +20,12 @@ ...@@ -20,6 +20,12 @@
#include "mariadb.h" #include "mariadb.h"
#include "sql_priv.h" #include "sql_priv.h"
enum class select_pushdown_type {
SINGLE_SELECT,
PART_OF_UNIT,
WHOLE_UNIT
};
/** /**
@class select_handler @class select_handler
...@@ -50,7 +56,7 @@ class select_handler ...@@ -50,7 +56,7 @@ class select_handler
virtual bool prepare(); virtual bool prepare();
/* /*
Select_handler processes one of Select_handler processes these cases:
- single SELECT - single SELECT
- whole unit (multiple SELECTs combined with UNION/EXCEPT/INTERSECT) - whole unit (multiple SELECTs combined with UNION/EXCEPT/INTERSECT)
- single SELECT that is part of a unit (partial pushdown) - single SELECT that is part of a unit (partial pushdown)
...@@ -60,7 +66,7 @@ class select_handler ...@@ -60,7 +66,7 @@ class select_handler
in the case of partial pushdown both select_lex and lex_unit in the case of partial pushdown both select_lex and lex_unit
are initialized 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 SELECT_LEX_UNIT *lex_unit; // Unit to be executed
/* /*
...@@ -99,6 +105,8 @@ class select_handler ...@@ -99,6 +105,8 @@ class select_handler
TABLE *create_tmp_table(THD *thd); TABLE *create_tmp_table(THD *thd);
select_pushdown_type get_pushdown_type();
THD *thd; THD *thd;
handlerton *ht; handlerton *ht;
......
...@@ -5054,8 +5054,15 @@ select_handler *find_select_handler_inner(THD *thd, ...@@ -5054,8 +5054,15 @@ select_handler *find_select_handler_inner(THD *thd,
SELECT_LEX *select_lex, SELECT_LEX *select_lex,
SELECT_LEX_UNIT *select_lex_unit) 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; return 0;
}
TABLE_LIST *tbl= nullptr; TABLE_LIST *tbl= nullptr;
// For SQLCOM_INSERT_SELECT the server takes TABLE_LIST // For SQLCOM_INSERT_SELECT the server takes TABLE_LIST
...@@ -276,18 +276,6 @@ federatedx_handler_base::federatedx_handler_base(THD *thd_arg, TABLE *tbl_arg) ...@@ -276,18 +276,6 @@ federatedx_handler_base::federatedx_handler_base(THD *thd_arg, TABLE *tbl_arg)
query_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() = default;
ha_federatedx_select_handler::ha_federatedx_select_handler( ha_federatedx_select_handler::ha_federatedx_select_handler(
...@@ -296,10 +284,7 @@ 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) federatedx_handler_base(thd, tbl)
{ {
query.length(0); query.length(0);
lex_unit->print(&query, lex_unit->print(&query, PRINT_QUERY_TYPE);
enum_query_type(QT_VIEW_INTERNAL | QT_SELECT_ONLY |
QT_ITEM_ORIGINAL_FUNC_NULLIF |
QT_PARSABLE));
} }
ha_federatedx_select_handler::ha_federatedx_select_handler( ha_federatedx_select_handler::ha_federatedx_select_handler(
...@@ -308,10 +293,31 @@ 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) federatedx_handler_base(thd, tbl)
{ {
query.length(0); query.length(0);
select_lex->print(thd, &query, if (get_pushdown_type() == select_pushdown_type::SINGLE_SELECT)
enum_query_type(QT_VIEW_INTERNAL | QT_SELECT_ONLY | {
QT_ITEM_ORIGINAL_FUNC_NULLIF | /*
QT_PARSABLE)); 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_() int federatedx_handler_base::init_scan_()
......
...@@ -62,8 +62,6 @@ class ha_federatedx_derived_handler: public derived_handler, public federatedx_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 class ha_federatedx_select_handler: public select_handler, public federatedx_handler_base
{ {
public: 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, ha_federatedx_select_handler(THD *thd_arg, SELECT_LEX_UNIT *sel_unit,
TABLE *tbl); TABLE *tbl);
ha_federatedx_select_handler(THD *thd_arg, SELECT_LEX *sel_lex, 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 ...@@ -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 init_scan() { return federatedx_handler_base::init_scan_(); }
int next_row() { return federatedx_handler_base::next_row_(table); } int next_row() { return federatedx_handler_base::next_row_(table); }
int end_scan(); 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);
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment