Commit a2f245e4 authored by Sergei Petrunia's avatar Sergei Petrunia

MDEV-10372: EXPLAIN fixes for recursive CTEs, including FORMAT=JSON

- Tabular EXPLAIN now prints "RECURSIVE UNION".
- There is a basic implementation of EXPLAIN FORMAT=JSON.
- it produces "recursive_union" JSON struct
- No other details or ANALYZE support, yet.
parent e1c92a6c
This diff is collapsed.
......@@ -977,5 +977,106 @@ as
select * from ancestors;
--echo #
--echo # EXPLAIN FORMAT=JSON on a query where one recursive CTE uses another:
--echo #
explain
with recursive
prev_gen
as
(
select folks.*
from folks, prev_gen
where folks.id=prev_gen.father or folks.id=prev_gen.mother
union
select *
from folks
where name='Me'
),
ancestors
as
(
select *
from folks
where name='Me'
union
select *
from ancestors
union
select *
from prev_gen
)
select ancestors.name, ancestors.dob from ancestors;
explain FORMAT=JSON
with recursive
prev_gen
as
(
select folks.*
from folks, prev_gen
where folks.id=prev_gen.father or folks.id=prev_gen.mother
union
select *
from folks
where name='Me'
),
ancestors
as
(
select *
from folks
where name='Me2'
union
select *
from ancestors where id < 234
union
select *
from prev_gen where id < 345
)
select ancestors.name, ancestors.dob from ancestors;
--echo #
explain format=json
with recursive
ancestor_couples(h_id, h_name, h_dob, h_father, h_mother,
w_id, w_name, w_dob, w_father, w_mother)
as
(
select h.*, w.*
from folks h, folks w, coupled_ancestors a
where a.father = h.id AND a.mother = w.id
union
select h.*, w.*
from folks v, folks h, folks w
where v.name = 'Me' and
(v.father = h.id AND v.mother= w.id)
),
coupled_ancestors (id, name, dob, father, mother)
as
(
select h_id, h_name, h_dob, h_father, h_mother
from ancestor_couples
union all
select w_id, w_name, w_dob, w_father, w_mother
from ancestor_couples
)
select h_name, h_dob, w_name, w_dob
from ancestor_couples;
drop table folks;
--echo #
--echo # MDEV-10372: [bb-10.2-mdev9864 tree] EXPLAIN with recursive CTE enters endless recursion
--echo #
create table t1(a int);
insert into t1 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
explain format=json
with recursive T as (select a from t1 union select a+10 from T where a < 1000)
select * from T;
drop table t1;
......@@ -547,7 +547,12 @@ void Explain_union::print_explain_json(Explain_query *query,
bool started_object= print_explain_json_cache(writer, is_analyze);
writer->add_member("query_block").start_object();
writer->add_member("union_result").start_object();
if (is_recursive_cte)
writer->add_member("recursive_union").start_object();
else
writer->add_member("union_result").start_object();
// using_temporary_table
make_union_table_name(table_name_buffer);
writer->add_member("table_name").add_str(table_name_buffer);
......
......@@ -327,6 +327,7 @@ class Explain_union : public Explain_node
public:
Explain_union(MEM_ROOT *root, bool is_analyze) :
Explain_node(root),
is_recursive_cte(false),
fake_select_lex_explain(root, is_analyze)
{}
......@@ -362,6 +363,7 @@ class Explain_union : public Explain_node
const char *fake_select_type;
bool using_filesort;
bool using_tmp;
bool is_recursive_cte;
/*
Explain data structure for "fake_select_lex" (i.e. for the degenerate
......
......@@ -4368,7 +4368,26 @@ void st_select_lex::set_explain_type(bool on_the_fly)
type= is_uncacheable ? "UNCACHEABLE UNION": "UNION";
if (this == master_unit()->fake_select_lex)
type= "UNION RESULT";
/*
join below may be =NULL when this functions is called at an early
stage. It will be later called again and we will set the correct
value.
*/
if (join)
{
bool uses_cte= false;
for (JOIN_TAB *tab= first_explain_order_tab(join); tab;
tab= next_explain_order_tab(join, tab))
{
if (tab->table->pos_in_table_list->with)
{
uses_cte= true;
break;
}
}
if (uses_cte)
type= "RECURSIVE UNION";
}
}
}
}
......@@ -4683,7 +4702,9 @@ int st_select_lex_unit::save_union_explain(Explain_query *output)
new (output->mem_root) Explain_union(output->mem_root,
thd->lex->analyze_stmt);
if (with_element && with_element->is_recursive)
eu->is_recursive_cte= true;
if (derived)
eu->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
/*
......
......@@ -24186,7 +24186,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta,
In case this is a derived table, here we remember the number of
subselect that used to produce it.
*/
eta->derived_select_number= table->derived_select_number;
if (!(table_list && table_list->is_with_table_recursive_reference()))
eta->derived_select_number= table->derived_select_number;
/* The same for non-merged semi-joins */
eta->non_merged_sjm_number = get_non_merged_semijoin_select();
......
......@@ -2278,4 +2278,8 @@ class Pushdown_query: public Sql_alloc
bool test_if_order_compatible(SQL_I_List<ORDER> &a, SQL_I_List<ORDER> &b);
int test_if_group_changed(List<Cached_item> &list);
int create_sort_index(THD *thd, JOIN *join, JOIN_TAB *tab, Filesort *fsort);
JOIN_TAB *first_explain_order_tab(JOIN* join);
JOIN_TAB *next_explain_order_tab(JOIN* join, JOIN_TAB* tab);
#endif /* SQL_SELECT_INCLUDED */
......@@ -1166,7 +1166,7 @@ bool st_select_lex_unit::exec()
}
// One step of recursive execution
bool st_select_lex_unit::exec_recursive(bool is_driving_recursive)
{
st_select_lex *lex_select_save= thd->lex->current_select;
......
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