Skip to content

Commit a8c200c

Browse files
committed
MDEV-22042 Server crash in Item_field::print on ANALYZE FORMAT=JSON
When processing a query with a recursive CTE a temporary table is used for each recursive reference of the CTE. As any temporary table it uses its own mem-root for table definition structures. Due to specifics of the current implementation of ANALYZE stmt command this mem-root can be freed only at the very of query processing. Such deallocation of mem-root memory happens in close_thread_tables(). The function looks through the list of the tmp tables rec_tables attached to the THD of the query and frees corresponding mem-roots. If the query uses a stored function then such list is created for each query of the function. When a new rec_list has to be created the old one has to be saved and then restored at the proper moment. The bug occurred because only one rec_list for the query containing CTE was created. As a result close_thread_tables() freed tmp mem-roots used for rec_tables prematurely destroying some data needed for the output produced by the ANALYZE command.
1 parent fff7897 commit a8c200c

File tree

3 files changed

+205
-0
lines changed

3 files changed

+205
-0
lines changed

mysql-test/r/cte_recursive.result

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3648,3 +3648,165 @@ select * from t1 as t;
36483648
id select_type table type possible_keys key key_len ref rows Extra
36493649
1 PRIMARY t ALL NULL NULL NULL NULL 4
36503650
drop table t1,t2;
3651+
#
3652+
# MDEV-22042: ANALYZE of query using stored function and recursive CTE
3653+
#
3654+
create table t1 (a1 varchar(20),a2 varchar(20)) engine=myisam;
3655+
insert into t1 values (1,1),(2,2),(3,3);
3656+
create table t2 (
3657+
a2 varchar(20) primary key, b1 varchar(20), key (b1)
3658+
) engine=myisam;
3659+
insert into t2 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7);
3660+
insert into t2 values (11,11),(12,12),(13,13),(14,14),(15,15),(16,16),(17,17);
3661+
create function f1(id varchar(20)) returns varchar(50)
3662+
begin
3663+
declare res varchar (50);
3664+
select a2 into res from t2 where a2=id and b1=1 limit 1;
3665+
return res;
3666+
end$$
3667+
select fv
3668+
from (select t1.a1, f1(t1.a2) fv from t1) dt
3669+
where (dt.a1) in (with recursive cte as (select a2 from t2 where a2='2'
3670+
union select tt2.a2 from t2 tt2 join cte on tt2.b1=cte.a2)
3671+
select a2 from cte);
3672+
fv
3673+
NULL
3674+
explain select fv
3675+
from (select t1.a1, f1(t1.a2) fv from t1) dt
3676+
where (dt.a1) in (with recursive cte as (select a2 from t2 where a2='2'
3677+
union select tt2.a2 from t2 tt2 join cte on tt2.b1=cte.a2)
3678+
select a2 from cte);
3679+
id select_type table type possible_keys key key_len ref rows Extra
3680+
1 PRIMARY <subquery3> ALL distinct_key NULL NULL NULL 2
3681+
1 PRIMARY t1 ALL NULL NULL NULL NULL 3 Using where; Using join buffer (flat, BNL join)
3682+
3 MATERIALIZED <derived4> ALL NULL NULL NULL NULL 2
3683+
4 DERIVED t2 const PRIMARY PRIMARY 22 const 1 Using index
3684+
5 RECURSIVE UNION <derived4> ALL NULL NULL NULL NULL 2
3685+
5 RECURSIVE UNION tt2 ref b1 b1 23 cte.a2 2
3686+
NULL UNION RESULT <union4,5> ALL NULL NULL NULL NULL NULL
3687+
analyze format=json select fv
3688+
from (select t1.a1, f1(t1.a2) fv from t1) dt
3689+
where (dt.a1) in (with recursive cte as (select a2 from t2 where a2='2'
3690+
union select tt2.a2 from t2 tt2 join cte on tt2.b1=cte.a2)
3691+
select a2 from cte);
3692+
ANALYZE
3693+
{
3694+
"query_block": {
3695+
"select_id": 1,
3696+
"r_loops": 1,
3697+
"r_total_time_ms": "REPLACED",
3698+
"table": {
3699+
"table_name": "<subquery3>",
3700+
"access_type": "ALL",
3701+
"possible_keys": ["distinct_key"],
3702+
"r_loops": 1,
3703+
"rows": 2,
3704+
"r_rows": 1,
3705+
"r_total_time_ms": "REPLACED",
3706+
"filtered": 100,
3707+
"r_filtered": 100,
3708+
"materialized": {
3709+
"unique": 1,
3710+
"query_block": {
3711+
"select_id": 3,
3712+
"table": {
3713+
"table_name": "<derived4>",
3714+
"access_type": "ALL",
3715+
"r_loops": 1,
3716+
"rows": 2,
3717+
"r_rows": 1,
3718+
"r_total_time_ms": "REPLACED",
3719+
"filtered": 100,
3720+
"r_filtered": 100,
3721+
"materialized": {
3722+
"query_block": {
3723+
"recursive_union": {
3724+
"table_name": "<union4,5>",
3725+
"access_type": "ALL",
3726+
"r_loops": 0,
3727+
"r_rows": null,
3728+
"query_specifications": [
3729+
{
3730+
"query_block": {
3731+
"select_id": 4,
3732+
"r_loops": 1,
3733+
"r_total_time_ms": "REPLACED",
3734+
"table": {
3735+
"table_name": "t2",
3736+
"access_type": "const",
3737+
"possible_keys": ["PRIMARY"],
3738+
"key": "PRIMARY",
3739+
"key_length": "22",
3740+
"used_key_parts": ["a2"],
3741+
"ref": ["const"],
3742+
"r_loops": 0,
3743+
"rows": 1,
3744+
"r_rows": null,
3745+
"filtered": 100,
3746+
"r_filtered": null,
3747+
"using_index": true
3748+
}
3749+
}
3750+
},
3751+
{
3752+
"query_block": {
3753+
"select_id": 5,
3754+
"r_loops": 1,
3755+
"r_total_time_ms": "REPLACED",
3756+
"table": {
3757+
"table_name": "<derived4>",
3758+
"access_type": "ALL",
3759+
"r_loops": 1,
3760+
"rows": 2,
3761+
"r_rows": 1,
3762+
"r_total_time_ms": "REPLACED",
3763+
"filtered": 100,
3764+
"r_filtered": 100
3765+
},
3766+
"table": {
3767+
"table_name": "tt2",
3768+
"access_type": "ref",
3769+
"possible_keys": ["b1"],
3770+
"key": "b1",
3771+
"key_length": "23",
3772+
"used_key_parts": ["b1"],
3773+
"ref": ["cte.a2"],
3774+
"r_loops": 1,
3775+
"rows": 2,
3776+
"r_rows": 1,
3777+
"r_total_time_ms": "REPLACED",
3778+
"filtered": 100,
3779+
"r_filtered": 100
3780+
}
3781+
}
3782+
}
3783+
]
3784+
}
3785+
}
3786+
}
3787+
}
3788+
}
3789+
}
3790+
},
3791+
"block-nl-join": {
3792+
"table": {
3793+
"table_name": "t1",
3794+
"access_type": "ALL",
3795+
"r_loops": 1,
3796+
"rows": 3,
3797+
"r_rows": 3,
3798+
"r_total_time_ms": "REPLACED",
3799+
"filtered": 100,
3800+
"r_filtered": 100
3801+
},
3802+
"buffer_type": "flat",
3803+
"buffer_size": "256Kb",
3804+
"join_type": "BNL",
3805+
"attached_condition": "t1.a1 = cte.a2",
3806+
"r_filtered": 33.333
3807+
}
3808+
}
3809+
}
3810+
drop function f1;
3811+
drop table t1,t2;
3812+
End of 10.2 tests

mysql-test/t/cte_recursive.test

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2536,3 +2536,42 @@ with recursive cte as
25362536
select * from t1 as t;
25372537

25382538
drop table t1,t2;
2539+
2540+
--echo #
2541+
--echo # MDEV-22042: ANALYZE of query using stored function and recursive CTE
2542+
--echo #
2543+
2544+
create table t1 (a1 varchar(20),a2 varchar(20)) engine=myisam;
2545+
insert into t1 values (1,1),(2,2),(3,3);
2546+
2547+
create table t2 (
2548+
a2 varchar(20) primary key, b1 varchar(20), key (b1)
2549+
) engine=myisam;
2550+
insert into t2 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7);
2551+
insert into t2 values (11,11),(12,12),(13,13),(14,14),(15,15),(16,16),(17,17);
2552+
2553+
delimiter $$;
2554+
create function f1(id varchar(20)) returns varchar(50)
2555+
begin
2556+
declare res varchar (50);
2557+
select a2 into res from t2 where a2=id and b1=1 limit 1;
2558+
return res;
2559+
end$$
2560+
delimiter ;$$
2561+
2562+
let q=
2563+
select fv
2564+
from (select t1.a1, f1(t1.a2) fv from t1) dt
2565+
where (dt.a1) in (with recursive cte as (select a2 from t2 where a2='2'
2566+
union select tt2.a2 from t2 tt2 join cte on tt2.b1=cte.a2)
2567+
select a2 from cte);
2568+
2569+
eval $q;
2570+
eval explain $q;
2571+
--source include/analyze-format.inc
2572+
eval analyze format=json $q;
2573+
2574+
drop function f1;
2575+
drop table t1,t2;
2576+
2577+
--echo End of 10.2 tests

sql/sp_head.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
11261126
backup_arena;
11271127
query_id_t old_query_id;
11281128
TABLE *old_derived_tables;
1129+
TABLE *old_rec_tables;
11291130
LEX *old_lex;
11301131
Item_change_list old_change_list;
11311132
String old_packet;
@@ -1201,6 +1202,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
12011202
old_query_id= thd->query_id;
12021203
old_derived_tables= thd->derived_tables;
12031204
thd->derived_tables= 0;
1205+
old_rec_tables= thd->rec_tables;
1206+
thd->rec_tables= 0;
12041207
save_sql_mode= thd->variables.sql_mode;
12051208
thd->variables.sql_mode= m_sql_mode;
12061209
save_abort_on_warning= thd->abort_on_warning;
@@ -1468,6 +1471,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
14681471
thd->set_query_id(old_query_id);
14691472
DBUG_ASSERT(!thd->derived_tables);
14701473
thd->derived_tables= old_derived_tables;
1474+
thd->rec_tables= old_rec_tables;
14711475
thd->variables.sql_mode= save_sql_mode;
14721476
thd->abort_on_warning= save_abort_on_warning;
14731477
thd->m_reprepare_observer= save_reprepare_observer;

0 commit comments

Comments
 (0)