Skip to content

Commit 855356c

Browse files
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)
1 parent 9b2a65e commit 855356c

File tree

7 files changed

+218
-25
lines changed

7 files changed

+218
-25
lines changed

mysql-test/suite/federated/federatedx_create_handlers.result

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,111 @@ t3_myisam2
11401140
t3_myisam3
11411141
SELECT * FROM federated.t1 UNION SELECT * FROM t3 ORDER BY 2;
11421142
ERROR 42S22: Unknown column '2' in 'order clause'
1143+
#
1144+
# MDEV-32382 FederatedX error on pushdown of statement having CTE
1145+
#
1146+
# Single SELECT with CTE
1147+
WITH cte AS (SELECT * FROM federated.t1)
1148+
SELECT * FROM cte;
1149+
a
1150+
bcd
1151+
abc
1152+
cde
1153+
explain extended WITH cte AS (SELECT * FROM federated.t1)
1154+
SELECT * FROM cte;
1155+
id select_type table type possible_keys key key_len ref rows filtered Extra
1156+
1 PUSHED SELECT NULL NULL NULL NULL NULL NULL NULL NULL NULL
1157+
Warnings:
1158+
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`
1159+
# Pushdown of a UNION having CTE's
1160+
WITH cte AS (SELECT * FROM federated.t1),
1161+
cte2 AS (SELECT * FROM federated.t2)
1162+
SELECT * FROM cte
1163+
UNION
1164+
SELECT * FROM cte2;
1165+
a
1166+
abc
1167+
bcd
1168+
cde
1169+
def
1170+
efg
1171+
explain extended WITH cte AS (SELECT * FROM federated.t1),
1172+
cte2 AS (SELECT * FROM federated.t2)
1173+
SELECT * FROM cte
1174+
UNION
1175+
SELECT * FROM cte2;
1176+
id select_type table type possible_keys key key_len ref rows filtered Extra
1177+
NULL PUSHED UNION NULL NULL NULL NULL NULL NULL NULL NULL NULL
1178+
Warnings:
1179+
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`
1180+
# Partial pushdown is not allowed for unions with CTE's, however a CTE
1181+
# may be pushed down a derived table
1182+
WITH cte AS (SELECT * FROM federated.t1)
1183+
SELECT * FROM cte
1184+
UNION ALL
1185+
SELECT * FROM t3;
1186+
a
1187+
abc
1188+
bcd
1189+
cde
1190+
t3_myisam1
1191+
t3_myisam2
1192+
t3_myisam3
1193+
explain extended WITH cte AS (SELECT * FROM federated.t1)
1194+
SELECT * FROM cte
1195+
UNION ALL
1196+
SELECT * FROM t3;
1197+
id select_type table type possible_keys key key_len ref rows filtered Extra
1198+
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 3 100.00
1199+
2 PUSHED DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL
1200+
3 UNION t3 ALL NULL NULL NULL NULL 3 100.00
1201+
Warnings:
1202+
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`
1203+
WITH cte AS (SELECT * FROM federated.t1 UNION SELECT * FROM t3)
1204+
SELECT * FROM cte;
1205+
a
1206+
abc
1207+
bcd
1208+
cde
1209+
t3_myisam1
1210+
t3_myisam2
1211+
t3_myisam3
1212+
explain extended WITH cte AS (SELECT * FROM federated.t1 UNION SELECT * FROM t3)
1213+
SELECT * FROM cte;
1214+
id select_type table type possible_keys key key_len ref rows filtered Extra
1215+
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 6 100.00
1216+
2 DERIVED t1 ALL NULL NULL NULL NULL 3 100.00
1217+
3 UNION t3 ALL NULL NULL NULL NULL 3 100.00
1218+
NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL NULL
1219+
Warnings:
1220+
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`
1221+
# Two CTE's where one CTE refers to another
1222+
WITH cte AS (SELECT * FROM federated.t1),
1223+
cte2 AS (SELECT * FROM t3
1224+
WHERE t3.a NOT IN (SELECT * FROM cte))
1225+
SELECT * FROM cte JOIN cte2;
1226+
a a
1227+
abc t3_myisam1
1228+
abc t3_myisam2
1229+
abc t3_myisam3
1230+
bcd t3_myisam1
1231+
bcd t3_myisam2
1232+
bcd t3_myisam3
1233+
cde t3_myisam1
1234+
cde t3_myisam2
1235+
cde t3_myisam3
1236+
explain extended WITH cte AS (SELECT * FROM federated.t1),
1237+
cte2 AS (SELECT * FROM t3
1238+
WHERE t3.a NOT IN (SELECT * FROM cte))
1239+
SELECT * FROM cte JOIN cte2;
1240+
id select_type table type possible_keys key key_len ref rows filtered Extra
1241+
1 PRIMARY <derived5> ALL NULL NULL NULL NULL 3 100.00
1242+
1 PRIMARY t3 ALL NULL NULL NULL NULL 3 100.00 Using where; Using join buffer (flat, BNL join)
1243+
5 PUSHED DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL
1244+
4 MATERIALIZED <derived2> ALL NULL NULL NULL NULL 3 100.00
1245+
2 PUSHED DERIVED NULL NULL NULL NULL NULL NULL NULL NULL NULL
1246+
Warnings:
1247+
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`))))
11431248
connection master;
11441249
DROP TABLES federated.t1, federated.t2, t3, t4, t5, t6, federated.t11,
11451250
federated.t12, federated.t13, federated.t14;

mysql-test/suite/federated/federatedx_create_handlers.test

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,55 @@ SELECT * FROM federated.t1 UNION SELECT * FROM t3 ORDER BY a;
740740
SELECT * FROM federated.t1 UNION SELECT * FROM t3 ORDER BY 2;
741741

742742

743+
--echo #
744+
--echo # MDEV-32382 FederatedX error on pushdown of statement having CTE
745+
--echo #
746+
747+
--echo # Single SELECT with CTE
748+
let $query= WITH cte AS (SELECT * FROM federated.t1)
749+
SELECT * FROM cte;
750+
eval $query;
751+
eval explain extended $query;
752+
753+
--echo # Pushdown of a UNION having CTE's
754+
let $query= WITH cte AS (SELECT * FROM federated.t1),
755+
cte2 AS (SELECT * FROM federated.t2)
756+
SELECT * FROM cte
757+
UNION
758+
SELECT * FROM cte2;
759+
--sorted_result
760+
eval $query;
761+
eval explain extended $query;
762+
763+
# CREATE TABLE t3 (a int);
764+
# INSERT INTO t3 VALUES (101),(102),(103);
765+
766+
--echo # Partial pushdown is not allowed for unions with CTE's, however a CTE
767+
--echo # may be pushed down a derived table
768+
let $query= WITH cte AS (SELECT * FROM federated.t1)
769+
SELECT * FROM cte
770+
UNION ALL
771+
SELECT * FROM t3;
772+
--sorted_result
773+
eval $query;
774+
eval explain extended $query;
775+
776+
let $query= WITH cte AS (SELECT * FROM federated.t1 UNION SELECT * FROM t3)
777+
SELECT * FROM cte;
778+
--sorted_result
779+
eval $query;
780+
eval explain extended $query;
781+
782+
--echo # Two CTE's where one CTE refers to another
783+
let $query= WITH cte AS (SELECT * FROM federated.t1),
784+
cte2 AS (SELECT * FROM t3
785+
WHERE t3.a NOT IN (SELECT * FROM cte))
786+
SELECT * FROM cte JOIN cte2;
787+
--sorted_result
788+
eval $query;
789+
eval explain extended $query;
790+
791+
743792
# Cleanup
744793
connection master;
745794
DROP TABLES federated.t1, federated.t2, t3, t4, t5, t6, federated.t11,

sql/select_handler.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,18 @@ void select_handler::print_error(int error, myf errflag)
200200
{
201201
my_error(ER_GET_ERRNO, MYF(0), error, hton_name(ht)->str);
202202
}
203+
204+
select_pushdown_type select_handler::get_pushdown_type()
205+
{
206+
/*
207+
In the case of single SELECT select_lex is initialized and lex_unit==NULL,
208+
in the case of whole UNIT select_lex == NULL and lex_unit is initialized,
209+
in the case of partial pushdown both select_lex and lex_unit
210+
are initialized
211+
*/
212+
if(!lex_unit)
213+
return select_pushdown_type::SINGLE_SELECT;
214+
215+
return select_lex ? select_pushdown_type::PART_OF_UNIT :
216+
select_pushdown_type::WHOLE_UNIT;
217+
}

sql/select_handler.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
#include "mariadb.h"
2121
#include "sql_priv.h"
2222

23+
enum class select_pushdown_type {
24+
SINGLE_SELECT,
25+
PART_OF_UNIT,
26+
WHOLE_UNIT
27+
};
28+
2329
/**
2430
@class select_handler
2531
@@ -50,7 +56,7 @@ class select_handler
5056
virtual bool prepare();
5157

5258
/*
53-
Select_handler processes one of
59+
Select_handler processes these cases:
5460
- single SELECT
5561
- whole unit (multiple SELECTs combined with UNION/EXCEPT/INTERSECT)
5662
- single SELECT that is part of a unit (partial pushdown)
@@ -60,7 +66,7 @@ class select_handler
6066
in the case of partial pushdown both select_lex and lex_unit
6167
are initialized
6268
*/
63-
SELECT_LEX *select_lex; // Single select to be executed
69+
SELECT_LEX *select_lex; // Single select/part of a unit to be executed
6470
SELECT_LEX_UNIT *lex_unit; // Unit to be executed
6571

6672
/*
@@ -99,6 +105,8 @@ class select_handler
99105

100106
TABLE *create_tmp_table(THD *thd);
101107

108+
select_pushdown_type get_pushdown_type();
109+
102110
THD *thd;
103111
handlerton *ht;
104112

sql/sql_select.cc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5054,8 +5054,15 @@ select_handler *find_select_handler_inner(THD *thd,
50545054
SELECT_LEX *select_lex,
50555055
SELECT_LEX_UNIT *select_lex_unit)
50565056
{
5057-
if (select_lex->master_unit()->outer_select())
5057+
if (select_lex->master_unit()->outer_select() ||
5058+
(select_lex_unit && select_lex->master_unit()->with_clause))
5059+
{
5060+
/*
5061+
Pushdown is not supported neither for non-top-level SELECTs nor for parts
5062+
of SELECT_LEX_UNITs that have CTEs (SELECT_LEX_UNIT::with_clause)
5063+
*/
50585064
return 0;
5065+
}
50595066

50605067
TABLE_LIST *tbl= nullptr;
50615068
// For SQLCOM_INSERT_SELECT the server takes TABLE_LIST

storage/federatedx/federatedx_pushdown.cc

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -276,18 +276,6 @@ federatedx_handler_base::federatedx_handler_base(THD *thd_arg, TABLE *tbl_arg)
276276
query_table(tbl_arg)
277277
{}
278278

279-
ha_federatedx_select_handler::ha_federatedx_select_handler(
280-
THD *thd, SELECT_LEX *select_lex, TABLE *tbl)
281-
: select_handler(thd, federatedx_hton, select_lex),
282-
federatedx_handler_base(thd, tbl)
283-
{
284-
query.length(0);
285-
select_lex->print(thd, &query,
286-
enum_query_type(QT_VIEW_INTERNAL |
287-
QT_ITEM_ORIGINAL_FUNC_NULLIF |
288-
QT_PARSABLE));
289-
}
290-
291279
ha_federatedx_select_handler::~ha_federatedx_select_handler() = default;
292280

293281
ha_federatedx_select_handler::ha_federatedx_select_handler(
@@ -296,10 +284,7 @@ ha_federatedx_select_handler::ha_federatedx_select_handler(
296284
federatedx_handler_base(thd, tbl)
297285
{
298286
query.length(0);
299-
lex_unit->print(&query,
300-
enum_query_type(QT_VIEW_INTERNAL | QT_SELECT_ONLY |
301-
QT_ITEM_ORIGINAL_FUNC_NULLIF |
302-
QT_PARSABLE));
287+
lex_unit->print(&query, PRINT_QUERY_TYPE);
303288
}
304289

305290
ha_federatedx_select_handler::ha_federatedx_select_handler(
@@ -308,10 +293,31 @@ ha_federatedx_select_handler::ha_federatedx_select_handler(
308293
federatedx_handler_base(thd, tbl)
309294
{
310295
query.length(0);
311-
select_lex->print(thd, &query,
312-
enum_query_type(QT_VIEW_INTERNAL | QT_SELECT_ONLY |
313-
QT_ITEM_ORIGINAL_FUNC_NULLIF |
314-
QT_PARSABLE));
296+
if (get_pushdown_type() == select_pushdown_type::SINGLE_SELECT)
297+
{
298+
/*
299+
Must use SELECT_LEX_UNIT::print() instead of SELECT_LEX::print() here
300+
to print possible CTEs which are stored at SELECT_LEX_UNIT::with_clause
301+
*/
302+
select_lex->master_unit()->print(&query, PRINT_QUERY_TYPE);
303+
}
304+
else if (get_pushdown_type() == select_pushdown_type::PART_OF_UNIT)
305+
{
306+
/*
307+
CTEs are not supported for partial select pushdown so use
308+
SELECT_LEX::print() here
309+
*/
310+
select_lex->print(thd, &query, PRINT_QUERY_TYPE);
311+
}
312+
else
313+
{
314+
/*
315+
Other select_pushdown_types are not allowed in this constructor.
316+
The case of select_pushdown_type::WHOLE_UNIT is handled at another
317+
overload of the constuctor
318+
*/
319+
DBUG_ASSERT(0);
320+
}
315321
}
316322

317323
int federatedx_handler_base::init_scan_()

storage/federatedx/federatedx_pushdown.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ class ha_federatedx_derived_handler: public derived_handler, public federatedx_h
6262
class ha_federatedx_select_handler: public select_handler, public federatedx_handler_base
6363
{
6464
public:
65-
ha_federatedx_select_handler(THD *thd_arg, SELECT_LEX *sel_lex,
66-
TABLE *tbl);
6765
ha_federatedx_select_handler(THD *thd_arg, SELECT_LEX_UNIT *sel_unit,
6866
TABLE *tbl);
6967
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
7270
int init_scan() { return federatedx_handler_base::init_scan_(); }
7371
int next_row() { return federatedx_handler_base::next_row_(table); }
7472
int end_scan();
73+
74+
private:
75+
static constexpr auto PRINT_QUERY_TYPE=
76+
enum_query_type(QT_VIEW_INTERNAL | QT_SELECT_ONLY |
77+
QT_ITEM_ORIGINAL_FUNC_NULLIF | QT_PARSABLE);
7578
};

0 commit comments

Comments
 (0)