mirror of
https://github.com/MariaDB/server.git
synced 2025-04-18 21:44:20 +03:00
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)
This commit is contained in:
parent
9b2a65e41a
commit
855356ca6d
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 @@ protected:
|
||||
|
||||
TABLE *create_tmp_table(THD *thd);
|
||||
|
||||
select_pushdown_type get_pushdown_type();
|
||||
|
||||
THD *thd;
|
||||
handlerton *ht;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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_()
|
||||
|
@ -62,8 +62,6 @@ public:
|
||||
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 @@ public:
|
||||
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);
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user