Skip to content

Commit a244be7

Browse files
committed
MDEV-23406 Signal 8 in maria_create after recursive cte query
This bug could cause a crash when executing queries that used mutually recursive CTEs with system variable big_tables set to 1. It happened due to several bugs in the code that handled recursive table references referred mutually recursive CTEs. For each recursive table reference a temporary table is created that contains all rows generated for the corresponding recursive CTE table on the previous step of recursion. This temporary table should be created in the same way as the temporary table created for a regular materialized derived table using the method select_union::create_result_table(). In this case when the temporary table is created it uses the select_union::TMP_TABLE_PARAM structure as the parameter for the table construction. However the code created the temporary table using just the function create_tmp_table() and passed pointers to certain fields of the TMP_TABLE_PARAM structure used for accumulation of rows of the recursive CTE table as parameters for update. This was a mistake because now different temporary tables cannot share some TMP_TABLE_PARAM fields in a general case. Besides, depending on how mutually recursive CTE tables were defined and which of them were referred in the executed query the select_union object allocated for a recursive table reference could be allocated again after the the temporary table had been created. In this case the TMP_TABLE_PARAM object associated with the temporary table created for the recursive table reference contained unassigned fields needed for execution when Aria engine is employed as the engine for temporary tables. This patch ensures that - select_union object is created only once for any recursive table reference - any temporary table created for recursive CTEs uses its own TMP_TABLE_PARAM structure The patch also fixes a problem caused by incomplete cleanup of join tables associated with recursive table references. Approved by Oleksandr Byelkin <sanja@mariadb.com>
1 parent 719da2c commit a244be7

File tree

7 files changed

+370
-39
lines changed

7 files changed

+370
-39
lines changed

mysql-test/r/cte_recursive.result

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4235,5 +4235,224 @@ drop database db1;
42354235
create database test;
42364236
use test;
42374237
#
4238+
# MDEV-23406: query with mutually recursive CTEs when big_tables=1
4239+
#
4240+
set @save_big_tables=@@big_tables;
4241+
set big_tables=1;
4242+
create table folks(id int, name char(32), dob date, father int, mother int);
4243+
insert into folks values
4244+
(100, 'Me', '2000-01-01', 20, 30),
4245+
(20, 'Dad', '1970-02-02', 10, 9),
4246+
(30, 'Mom', '1975-03-03', 8, 7),
4247+
(10, 'Grandpa Bill', '1940-04-05', null, null),
4248+
(9, 'Grandma Ann', '1941-10-15', null, null),
4249+
(25, 'Uncle Jim', '1968-11-18', 8, 7),
4250+
(98, 'Sister Amy', '2001-06-20', 20, 30),
4251+
(7, 'Grandma Sally', '1943-08-23', null, 6),
4252+
(8, 'Grandpa Ben', '1940-10-21', null, null),
4253+
(6, 'Grandgrandma Martha', '1923-05-17', null, null),
4254+
(67, 'Cousin Eddie', '1992-02-28', 25, 27),
4255+
(27, 'Auntie Melinda', '1971-03-29', null, null);
4256+
with recursive
4257+
ancestor_couples(h_id, h_name, h_dob, h_father, h_mother,
4258+
w_id, w_name, w_dob, w_father, w_mother)
4259+
as
4260+
(
4261+
select h.*, w.*
4262+
from folks h, folks w, coupled_ancestors a
4263+
where a.father = h.id AND a.mother = w.id
4264+
union
4265+
select h.*, w.*
4266+
from folks v, folks h, folks w
4267+
where v.name = 'Me' and
4268+
(v.father = h.id AND v.mother= w.id)
4269+
),
4270+
coupled_ancestors (id, name, dob, father, mother)
4271+
as
4272+
(
4273+
select h_id, h_name, h_dob, h_father, h_mother
4274+
from ancestor_couples
4275+
union
4276+
select w_id, w_name, w_dob, w_father, w_mother
4277+
from ancestor_couples
4278+
)
4279+
select h_name, h_dob, w_name, w_dob
4280+
from ancestor_couples;
4281+
h_name h_dob w_name w_dob
4282+
Dad 1970-02-02 Mom 1975-03-03
4283+
Grandpa Bill 1940-04-05 Grandma Ann 1941-10-15
4284+
Grandpa Ben 1940-10-21 Grandma Sally 1943-08-23
4285+
explain with recursive
4286+
ancestor_couples(h_id, h_name, h_dob, h_father, h_mother,
4287+
w_id, w_name, w_dob, w_father, w_mother)
4288+
as
4289+
(
4290+
select h.*, w.*
4291+
from folks h, folks w, coupled_ancestors a
4292+
where a.father = h.id AND a.mother = w.id
4293+
union
4294+
select h.*, w.*
4295+
from folks v, folks h, folks w
4296+
where v.name = 'Me' and
4297+
(v.father = h.id AND v.mother= w.id)
4298+
),
4299+
coupled_ancestors (id, name, dob, father, mother)
4300+
as
4301+
(
4302+
select h_id, h_name, h_dob, h_father, h_mother
4303+
from ancestor_couples
4304+
union
4305+
select w_id, w_name, w_dob, w_father, w_mother
4306+
from ancestor_couples
4307+
)
4308+
select h_name, h_dob, w_name, w_dob
4309+
from ancestor_couples;
4310+
id select_type table type possible_keys key key_len ref rows Extra
4311+
1 PRIMARY <derived3> ALL NULL NULL NULL NULL 1728
4312+
4 DERIVED <derived3> ALL NULL NULL NULL NULL 1728
4313+
5 RECURSIVE UNION <derived3> ALL NULL NULL NULL NULL 1728
4314+
NULL UNION RESULT <union4,5> ALL NULL NULL NULL NULL NULL
4315+
3 DERIVED v ALL NULL NULL NULL NULL 12 Using where
4316+
3 DERIVED h ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join)
4317+
3 DERIVED w ALL NULL NULL NULL NULL 12 Using where; Using join buffer (incremental, BNL join)
4318+
2 RECURSIVE UNION <derived4> ALL NULL NULL NULL NULL 2
4319+
2 RECURSIVE UNION h ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join)
4320+
2 RECURSIVE UNION w ALL NULL NULL NULL NULL 12 Using where; Using join buffer (incremental, BNL join)
4321+
NULL UNION RESULT <union3,2> ALL NULL NULL NULL NULL NULL
4322+
prepare stmt from "with recursive
4323+
ancestor_couples(h_id, h_name, h_dob, h_father, h_mother,
4324+
w_id, w_name, w_dob, w_father, w_mother)
4325+
as
4326+
(
4327+
select h.*, w.*
4328+
from folks h, folks w, coupled_ancestors a
4329+
where a.father = h.id AND a.mother = w.id
4330+
union
4331+
select h.*, w.*
4332+
from folks v, folks h, folks w
4333+
where v.name = 'Me' and
4334+
(v.father = h.id AND v.mother= w.id)
4335+
),
4336+
coupled_ancestors (id, name, dob, father, mother)
4337+
as
4338+
(
4339+
select h_id, h_name, h_dob, h_father, h_mother
4340+
from ancestor_couples
4341+
union
4342+
select w_id, w_name, w_dob, w_father, w_mother
4343+
from ancestor_couples
4344+
)
4345+
select h_name, h_dob, w_name, w_dob
4346+
from ancestor_couples";
4347+
execute stmt;
4348+
h_name h_dob w_name w_dob
4349+
Dad 1970-02-02 Mom 1975-03-03
4350+
Grandpa Bill 1940-04-05 Grandma Ann 1941-10-15
4351+
Grandpa Ben 1940-10-21 Grandma Sally 1943-08-23
4352+
execute stmt;
4353+
h_name h_dob w_name w_dob
4354+
Dad 1970-02-02 Mom 1975-03-03
4355+
Grandpa Bill 1940-04-05 Grandma Ann 1941-10-15
4356+
Grandpa Ben 1940-10-21 Grandma Sally 1943-08-23
4357+
deallocate prepare stmt;
4358+
with recursive
4359+
ancestor_couples(h_id, h_name, h_dob, h_father, h_mother,
4360+
w_id, w_name, w_dob, w_father, w_mother)
4361+
as
4362+
(
4363+
select h.*, w.*
4364+
from folks h, folks w, coupled_ancestors a
4365+
where a.father = h.id AND a.mother = w.id
4366+
),
4367+
coupled_ancestors (id, name, dob, father, mother)
4368+
as
4369+
(
4370+
select *
4371+
from folks
4372+
where name = 'Me'
4373+
union all
4374+
select h_id, h_name, h_dob, h_father, h_mother
4375+
from ancestor_couples
4376+
union all
4377+
select w_id, w_name, w_dob, w_father, w_mother
4378+
from ancestor_couples
4379+
)
4380+
select h_name, h_dob, w_name, w_dob
4381+
from ancestor_couples;
4382+
h_name h_dob w_name w_dob
4383+
Dad 1970-02-02 Mom 1975-03-03
4384+
Grandpa Bill 1940-04-05 Grandma Ann 1941-10-15
4385+
Grandpa Ben 1940-10-21 Grandma Sally 1943-08-23
4386+
explain with recursive
4387+
ancestor_couples(h_id, h_name, h_dob, h_father, h_mother,
4388+
w_id, w_name, w_dob, w_father, w_mother)
4389+
as
4390+
(
4391+
select h.*, w.*
4392+
from folks h, folks w, coupled_ancestors a
4393+
where a.father = h.id AND a.mother = w.id
4394+
),
4395+
coupled_ancestors (id, name, dob, father, mother)
4396+
as
4397+
(
4398+
select *
4399+
from folks
4400+
where name = 'Me'
4401+
union all
4402+
select h_id, h_name, h_dob, h_father, h_mother
4403+
from ancestor_couples
4404+
union all
4405+
select w_id, w_name, w_dob, w_father, w_mother
4406+
from ancestor_couples
4407+
)
4408+
select h_name, h_dob, w_name, w_dob
4409+
from ancestor_couples;
4410+
id select_type table type possible_keys key key_len ref rows Extra
4411+
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 2
4412+
3 DERIVED folks ALL NULL NULL NULL NULL 12 Using where
4413+
4 RECURSIVE UNION <derived2> ALL NULL NULL NULL NULL 2
4414+
5 RECURSIVE UNION <derived2> ALL NULL NULL NULL NULL 2
4415+
NULL UNION RESULT <union3,4,5> ALL NULL NULL NULL NULL NULL
4416+
2 DERIVED h ALL NULL NULL NULL NULL 12
4417+
2 DERIVED <derived3> ALL NULL NULL NULL NULL 12 Using where; Using join buffer (flat, BNL join)
4418+
2 DERIVED w ALL NULL NULL NULL NULL 12 Using where; Using join buffer (incremental, BNL join)
4419+
prepare stmt from "with recursive
4420+
ancestor_couples(h_id, h_name, h_dob, h_father, h_mother,
4421+
w_id, w_name, w_dob, w_father, w_mother)
4422+
as
4423+
(
4424+
select h.*, w.*
4425+
from folks h, folks w, coupled_ancestors a
4426+
where a.father = h.id AND a.mother = w.id
4427+
),
4428+
coupled_ancestors (id, name, dob, father, mother)
4429+
as
4430+
(
4431+
select *
4432+
from folks
4433+
where name = 'Me'
4434+
union all
4435+
select h_id, h_name, h_dob, h_father, h_mother
4436+
from ancestor_couples
4437+
union all
4438+
select w_id, w_name, w_dob, w_father, w_mother
4439+
from ancestor_couples
4440+
)
4441+
select h_name, h_dob, w_name, w_dob
4442+
from ancestor_couples";
4443+
execute stmt;
4444+
h_name h_dob w_name w_dob
4445+
Dad 1970-02-02 Mom 1975-03-03
4446+
Grandpa Bill 1940-04-05 Grandma Ann 1941-10-15
4447+
Grandpa Ben 1940-10-21 Grandma Sally 1943-08-23
4448+
execute stmt;
4449+
h_name h_dob w_name w_dob
4450+
Dad 1970-02-02 Mom 1975-03-03
4451+
Grandpa Bill 1940-04-05 Grandma Ann 1941-10-15
4452+
Grandpa Ben 1940-10-21 Grandma Sally 1943-08-23
4453+
deallocate prepare stmt;
4454+
drop table folks;
4455+
set big_tables=@save_big_tables;
4456+
#
42384457
# End of 10.2 tests
42394458
#

mysql-test/t/cte_recursive.test

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2725,6 +2725,100 @@ drop database db1;
27252725
create database test;
27262726
use test;
27272727

2728+
--echo #
2729+
--echo # MDEV-23406: query with mutually recursive CTEs when big_tables=1
2730+
--echo #
2731+
2732+
set @save_big_tables=@@big_tables;
2733+
set big_tables=1;
2734+
2735+
create table folks(id int, name char(32), dob date, father int, mother int);
2736+
2737+
insert into folks values
2738+
(100, 'Me', '2000-01-01', 20, 30),
2739+
(20, 'Dad', '1970-02-02', 10, 9),
2740+
(30, 'Mom', '1975-03-03', 8, 7),
2741+
(10, 'Grandpa Bill', '1940-04-05', null, null),
2742+
(9, 'Grandma Ann', '1941-10-15', null, null),
2743+
(25, 'Uncle Jim', '1968-11-18', 8, 7),
2744+
(98, 'Sister Amy', '2001-06-20', 20, 30),
2745+
(7, 'Grandma Sally', '1943-08-23', null, 6),
2746+
(8, 'Grandpa Ben', '1940-10-21', null, null),
2747+
(6, 'Grandgrandma Martha', '1923-05-17', null, null),
2748+
(67, 'Cousin Eddie', '1992-02-28', 25, 27),
2749+
(27, 'Auntie Melinda', '1971-03-29', null, null);
2750+
2751+
let q=
2752+
with recursive
2753+
ancestor_couples(h_id, h_name, h_dob, h_father, h_mother,
2754+
w_id, w_name, w_dob, w_father, w_mother)
2755+
as
2756+
(
2757+
select h.*, w.*
2758+
from folks h, folks w, coupled_ancestors a
2759+
where a.father = h.id AND a.mother = w.id
2760+
union
2761+
select h.*, w.*
2762+
from folks v, folks h, folks w
2763+
where v.name = 'Me' and
2764+
(v.father = h.id AND v.mother= w.id)
2765+
),
2766+
coupled_ancestors (id, name, dob, father, mother)
2767+
as
2768+
(
2769+
select h_id, h_name, h_dob, h_father, h_mother
2770+
from ancestor_couples
2771+
union
2772+
select w_id, w_name, w_dob, w_father, w_mother
2773+
from ancestor_couples
2774+
)
2775+
select h_name, h_dob, w_name, w_dob
2776+
from ancestor_couples;
2777+
2778+
eval $q;
2779+
eval explain $q;
2780+
eval prepare stmt from "$q";
2781+
execute stmt;
2782+
execute stmt;
2783+
deallocate prepare stmt;
2784+
2785+
let $q=
2786+
with recursive
2787+
ancestor_couples(h_id, h_name, h_dob, h_father, h_mother,
2788+
w_id, w_name, w_dob, w_father, w_mother)
2789+
as
2790+
(
2791+
select h.*, w.*
2792+
from folks h, folks w, coupled_ancestors a
2793+
where a.father = h.id AND a.mother = w.id
2794+
),
2795+
coupled_ancestors (id, name, dob, father, mother)
2796+
as
2797+
(
2798+
select *
2799+
from folks
2800+
where name = 'Me'
2801+
union all
2802+
select h_id, h_name, h_dob, h_father, h_mother
2803+
from ancestor_couples
2804+
union all
2805+
select w_id, w_name, w_dob, w_father, w_mother
2806+
from ancestor_couples
2807+
)
2808+
select h_name, h_dob, w_name, w_dob
2809+
from ancestor_couples;
2810+
2811+
eval $q;
2812+
eval explain $q;
2813+
eval prepare stmt from "$q";
2814+
execute stmt;
2815+
execute stmt;
2816+
deallocate prepare stmt;
2817+
2818+
drop table folks;
2819+
2820+
set big_tables=@save_big_tables;
2821+
27282822
--echo #
27292823
--echo # End of 10.2 tests
27302824
--echo #

sql/sql_class.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5152,10 +5152,15 @@ class select_union_recursive :public select_union
51525152
public:
51535153
/* The temporary table with the new records generated by one iterative step */
51545154
TABLE *incr_table;
5155+
/* The TMP_TABLE_PARAM structure used to create incr_table */
5156+
TMP_TABLE_PARAM incr_table_param;
51555157
/* One of tables from the list rec_tables (determined dynamically) */
51565158
TABLE *first_rec_table_to_update;
5157-
/* The temporary tables used for recursive table references */
5158-
List<TABLE> rec_tables;
5159+
/*
5160+
The list of all recursive table references to the CTE for whose
5161+
specification this select_union_recursive was created
5162+
*/
5163+
List<TABLE_LIST> rec_table_refs;
51595164
/*
51605165
The count of how many times cleanup() was called with cleaned==false
51615166
for the unit specifying the recursive CTE for which this object was created
@@ -5165,7 +5170,8 @@ class select_union_recursive :public select_union
51655170

51665171
select_union_recursive(THD *thd_arg):
51675172
select_union(thd_arg),
5168-
incr_table(0), first_rec_table_to_update(0), cleanup_count(0) {};
5173+
incr_table(0), first_rec_table_to_update(0), cleanup_count(0)
5174+
{ incr_table_param.init(); };
51695175

51705176
int send_data(List<Item> &items);
51715177
bool create_result_table(THD *thd, List<Item> *column_types,

sql/sql_cte.cc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,10 +1429,11 @@ void With_element::print(String *str, enum_query_type query_type)
14291429

14301430
bool With_element::instantiate_tmp_tables()
14311431
{
1432-
List_iterator_fast<TABLE> li(rec_result->rec_tables);
1433-
TABLE *rec_table;
1434-
while ((rec_table= li++))
1432+
List_iterator_fast<TABLE_LIST> li(rec_result->rec_table_refs);
1433+
TABLE_LIST *rec_tbl;
1434+
while ((rec_tbl= li++))
14351435
{
1436+
TABLE *rec_table= rec_tbl->table;
14361437
if (!rec_table->is_created() &&
14371438
instantiate_tmp_table(rec_table,
14381439
rec_table->s->key_info,

sql/sql_derived.cc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
677677
if (derived->is_with_table_recursive_reference())
678678
{
679679
/* Here 'derived" is a secondary recursive table reference */
680-
unit->with_element->rec_result->rec_tables.push_back(derived->table);
680+
unit->with_element->rec_result->rec_table_refs.push_back(derived);
681681
}
682682
}
683683
DBUG_ASSERT(derived->table || res);
@@ -733,7 +733,9 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
733733

734734
derived->fill_me= FALSE;
735735

736-
if (!(derived->derived_result= new (thd->mem_root) select_union(thd)))
736+
if ((!derived->is_with_table_recursive_reference() ||
737+
!derived->derived_result) &&
738+
!(derived->derived_result= new (thd->mem_root) select_union(thd)))
737739
DBUG_RETURN(TRUE); // out of memory
738740

739741
lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED;
@@ -752,7 +754,8 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
752754
Depending on the result field translation will or will not
753755
be created.
754756
*/
755-
if (derived->init_derived(thd, FALSE))
757+
if (!derived->is_with_table_recursive_reference() &&
758+
derived->init_derived(thd, FALSE))
756759
goto exit;
757760

758761
/*

0 commit comments

Comments
 (0)