Commit e9dbbf11 authored by Igor Babaev's avatar Igor Babaev

MDEV-22748 MariaDB crash on WITH RECURSIVE large query

This bug is the same as the bug MDEV-17024. The crashes caused by these
bugs were due to premature cleanups of the unit specifying recursive CTEs
that happened in some cases when there were several outer references the
same recursive CTE.
The problem of premature cleanups for recursive CTEs could be already
resolved by the correction in TABLE_LIST::set_as_with_table() introduced
in this patch. ALL other changes introduced by the patches for MDEV-17024
and MDEV-22748 guarantee that this clean-ups are performed as soon as
possible: when the select containing the last outer reference to a
recursive CTE is being cleaned up the specification of the recursive CTE
should be cleaned up as well.
parent be0c46eb
This diff is collapsed.
......@@ -2390,6 +2390,30 @@ select * from cte1, cte2 where cte1.c1 = 3;
eval $q3;
let $q4=
with recursive
rcte(a) as
(select 1 union select cast(a+1 as unsigned) from rcte where a < 10),
cte1 as
(select count(*) as c1 from rcte,t1 where a between 3 and 5 and id=a-3),
cte2 as
(select count(*) as c2 from rcte,t1 where a between 7 and 8 and id=a-7)
select * from cte2, cte1;
eval $q4;
eval explain extended $q4;
eval prepare stmt from "$q4";
execute stmt;
execute stmt;
drop procedure p;
drop table t2;
create table t2 (c1 int, c2 int);
eval create procedure p() insert into t2 $q4;
call p();
select * from t2;
drop procedure p;
drop table t1,t2;
......@@ -2574,4 +2598,48 @@ eval analyze format=json $q;
drop function f1;
drop table t1,t2;
--echo End of 10.2 tests
--echo #
--echo # MDEV-22748: two materialized CTEs using the same recursive CTE
--echo # (see also test case for MDEV-17024)
--echo #
CREATE TABLE t1 (YEAR int(4), d1 date , d2 date) ;
INSERT INTO t1 VALUES (2018,'2018-01-01','2018-09-20');
CREATE TABLE t2 (id int, tm date);
INSERT INTO t2 VALUES (1,'2018-08-30'),(2,'2018-08-30'),(3,'2018-08-30');
CREATE TABLE t3 (id int, tm date);
INSERT INTO t3 VALUES (1,'2018-08-30'),(2,'2018-08-30');
let $q=
WITH RECURSIVE
cte AS
(SELECT YEAR(t1.d1) AS YEAR, t1.d1 AS st, t1.d1 + INTERVAL 1 MONTH AS fn
FROM t1
UNION ALL
SELECT YEAR(cte.st + INTERVAL 1 MONTH),
cte.st + INTERVAL 1 MONTH, t1.d2 + INTERVAL 1 DAY
FROM cte JOIN t1
WHERE cte.st + INTERVAL 1 MONTH < t1.d2 ),
cte2 AS (SELECT YEAR, COUNT(*)
FROM cte JOIN t2 ON t2.tm BETWEEN cte.st AND cte.fn),
cte3 AS (SELECT YEAR, COUNT(*)
FROM cte JOIN t3 ON t3.tm BETWEEN cte.st AND cte.fn)
SELECT t1.* FROM t1 JOIN cte2 USING (YEAR) JOIN cte3 USING (YEAR);
eval $q;
eval EXPLAIN EXTENDED $q;
eval PREPARE stmt FROM "$q";
EXECUTE stmt;
EXECUTE stmt;
CREATE TABLE t4 (YEAR int(4), d1 date , d2 date);
eval CREATE PROCEDURE p() INSERT INTO t4 $q;
CALL p();
SELECT * FROM t4;
DROP PROCEDURE p;
DROP TABLE t1,t2,t3,t4;
--echo #
--echo # End of 10.2 tests
--echo #
......@@ -1099,6 +1099,7 @@ bool TABLE_LIST::set_as_with_table(THD *thd, With_element *with_elem)
{
derived= with_elem->spec;
if (derived != select_lex->master_unit() &&
!with_elem->is_recursive &&
!is_with_table_recursive_reference())
{
derived->move_as_slave(select_lex);
......
......@@ -1054,7 +1054,6 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
DBUG_ASSERT(derived->table && derived->table->is_created());
select_union *derived_result= derived->derived_result;
SELECT_LEX *save_current_select= lex->current_select;
bool derived_recursive_is_filled= false;
if (derived_is_recursive)
{
......@@ -1067,7 +1066,6 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
{
/* In this case all iteration are performed */
res= derived->fill_recursive(thd);
derived_recursive_is_filled= true;
}
}
else if (unit->is_union())
......@@ -1123,9 +1121,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
}
}
if (res || (!lex->describe &&
(!derived_is_recursive ||
derived_recursive_is_filled)))
if (res || (!lex->describe && !derived_is_recursive))
unit->cleanup();
lex->current_select= save_current_select;
......
......@@ -1540,6 +1540,19 @@ bool st_select_lex::cleanup()
delete join;
join= 0;
}
for (TABLE_LIST *tbl= get_table_list(); tbl; tbl= tbl->next_local)
{
if (tbl->is_recursive_with_table() &&
!tbl->is_with_table_recursive_reference())
{
/*
If query is killed before open_and_process_table() for tbl
is called then 'with' is already set, but 'derived' is not.
*/
st_select_lex_unit *unit= tbl->with->spec;
error|= (bool) error | (uint) unit->cleanup();
}
}
for (SELECT_LEX_UNIT *lex_unit= first_inner_unit(); lex_unit ;
lex_unit= lex_unit->next_unit())
{
......
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