Skip to content

Commit

Permalink
MDEV-32382 FederatedX error on pushdown of statements having CTE
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
mariadb-OlegSmirnov committed Nov 2, 2023
1 parent 9b2a65e commit 855356c
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 25 deletions.
105 changes: 105 additions & 0 deletions mysql-test/suite/federated/federatedx_create_handlers.result
Expand Up @@ -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 <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;
DROP TABLES federated.t1, federated.t2, t3, t4, t5, t6, federated.t11,
federated.t12, federated.t13, federated.t14;
Expand Down
49 changes: 49 additions & 0 deletions mysql-test/suite/federated/federatedx_create_handlers.test
Expand Up @@ -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,
Expand Down
15 changes: 15 additions & 0 deletions sql/select_handler.cc
Expand Up @@ -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;
}
12 changes: 10 additions & 2 deletions sql/select_handler.h
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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

/*
Expand Down Expand Up @@ -99,6 +105,8 @@ class select_handler

TABLE *create_tmp_table(THD *thd);

select_pushdown_type get_pushdown_type();

THD *thd;
handlerton *ht;

Expand Down
9 changes: 8 additions & 1 deletion sql/sql_select.cc
Expand Up @@ -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
Expand Down
46 changes: 26 additions & 20 deletions storage/federatedx/federatedx_pushdown.cc
Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -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_()
Expand Down
7 changes: 5 additions & 2 deletions storage/federatedx/federatedx_pushdown.h
Expand Up @@ -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,
Expand All @@ -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);
};

0 comments on commit 855356c

Please sign in to comment.