mirror of
https://github.com/MariaDB/server.git
synced 2025-08-07 00:04:31 +03:00
MDEV-238: SHOW EXPLAIN: Server crashes in JOIN::print_explain with FROM subquery and GROUP BY
- Support SHOW EXPLAIN for selects that have "Using temporary; Using filesort".
This commit is contained in:
@@ -284,4 +284,29 @@ a
|
|||||||
7
|
7
|
||||||
8
|
8
|
||||||
9
|
9
|
||||||
|
set debug='';
|
||||||
|
#
|
||||||
|
# MDEV-238: SHOW EXPLAIN: Server crashes in JOIN::print_explain with FROM subquery and GROUP BY
|
||||||
|
#
|
||||||
|
CREATE TABLE t2 ( a INT );
|
||||||
|
INSERT INTO t2 VALUES (1),(2),(1),(4),(2);
|
||||||
|
explain SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a;
|
||||||
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
|
1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
|
||||||
|
1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using join buffer (flat, BNL join)
|
||||||
|
set debug='d,show_explain_in_find_all_keys';
|
||||||
|
SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a;
|
||||||
|
# NOTE: current code will not show "Using join buffer":
|
||||||
|
show explain for $thr2;
|
||||||
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
|
1 SIMPLE t2 ALL NULL NULL NULL NULL 5 Using temporary; Using filesort
|
||||||
|
1 SIMPLE t2 ALL NULL NULL NULL NULL 5
|
||||||
|
Warnings:
|
||||||
|
Note 1003 SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a
|
||||||
|
a
|
||||||
|
1
|
||||||
|
2
|
||||||
|
4
|
||||||
|
set debug='';
|
||||||
|
DROP TABLE t2;
|
||||||
drop table t0,t1;
|
drop table t0,t1;
|
||||||
|
@@ -300,8 +300,30 @@ connection default;
|
|||||||
evalp show explain for $thr2;
|
evalp show explain for $thr2;
|
||||||
connection con1;
|
connection con1;
|
||||||
reap;
|
reap;
|
||||||
|
set debug='';
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # MDEV-238: SHOW EXPLAIN: Server crashes in JOIN::print_explain with FROM subquery and GROUP BY
|
||||||
|
--echo #
|
||||||
|
CREATE TABLE t2 ( a INT );
|
||||||
|
INSERT INTO t2 VALUES (1),(2),(1),(4),(2);
|
||||||
|
explain SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a;
|
||||||
|
|
||||||
|
set debug='d,show_explain_in_find_all_keys';
|
||||||
|
send SELECT alias.a FROM t2, ( SELECT * FROM t2 ) AS alias GROUP BY alias.a;
|
||||||
|
|
||||||
|
connection default;
|
||||||
|
--source include/wait_condition.inc
|
||||||
|
--echo # NOTE: current code will not show "Using join buffer":
|
||||||
|
evalp show explain for $thr2;
|
||||||
|
connection con1;
|
||||||
|
reap;
|
||||||
|
set debug='';
|
||||||
|
|
||||||
|
|
||||||
## TODO: Test this: have several SHOW EXPLAIN requests be queued up for a
|
## TODO: Test this: have several SHOW EXPLAIN requests be queued up for a
|
||||||
## thread and served together.
|
## thread and served together.
|
||||||
|
|
||||||
|
DROP TABLE t2;
|
||||||
|
|
||||||
drop table t0,t1;
|
drop table t0,t1;
|
||||||
|
@@ -515,6 +515,12 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
|
|||||||
if (indexfile || flag)
|
if (indexfile || flag)
|
||||||
ref_pos= &file->ref[0];
|
ref_pos= &file->ref[0];
|
||||||
next_pos=ref_pos;
|
next_pos=ref_pos;
|
||||||
|
|
||||||
|
DBUG_EXECUTE_IF("show_explain_in_find_all_keys",
|
||||||
|
dbug_serve_apcs(thd, 1);
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
if (! indexfile && ! quick_select)
|
if (! indexfile && ! quick_select)
|
||||||
{
|
{
|
||||||
next_pos=(uchar*) 0; /* Find records in sequence */
|
next_pos=(uchar*) 0; /* Find records in sequence */
|
||||||
|
@@ -7132,28 +7132,36 @@ prev_record_reads(POSITION *positions, uint idx, table_map found_ref)
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum enum_exec_or_opt {WALK_OPTIMIZATION_TABS , WALK_EXECUTION_TABS};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Enumerate join tabs in breadth-first fashion, including const tables.
|
Enumerate join tabs in breadth-first fashion, including const tables.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
JOIN_TAB *first_breadth_first_tab(JOIN *join)
|
JOIN_TAB *first_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind)
|
||||||
{
|
{
|
||||||
return join->join_tab; /* There's always one (i.e. first) table */
|
/* There's always one (i.e. first) table */
|
||||||
|
return (tabs_kind == WALK_EXECUTION_TABS)? join->join_tab:
|
||||||
|
join->table_access_tabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab)
|
JOIN_TAB *next_breadth_first_tab(JOIN *join, enum enum_exec_or_opt tabs_kind,
|
||||||
|
JOIN_TAB *tab)
|
||||||
{
|
{
|
||||||
|
JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, tabs_kind);
|
||||||
|
const uint n_top_tabs_count= (tabs_kind == WALK_EXECUTION_TABS)?
|
||||||
|
join->top_join_tab_count:
|
||||||
|
join->top_table_access_tabs_count;
|
||||||
if (!tab->bush_root_tab)
|
if (!tab->bush_root_tab)
|
||||||
{
|
{
|
||||||
/* We're at top level. Get the next top-level tab */
|
/* We're at top level. Get the next top-level tab */
|
||||||
tab++;
|
tab++;
|
||||||
if (tab < join->join_tab + join->top_join_tab_count)
|
if (tab < first_top_tab + n_top_tabs_count)
|
||||||
return tab;
|
return tab;
|
||||||
|
|
||||||
/* No more top-level tabs. Switch to enumerating SJM nest children */
|
/* No more top-level tabs. Switch to enumerating SJM nest children */
|
||||||
tab= join->join_tab;
|
tab= first_top_tab;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -7177,7 +7185,7 @@ JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab)
|
|||||||
Ok, "tab" points to a top-level table, and we need to find the next SJM
|
Ok, "tab" points to a top-level table, and we need to find the next SJM
|
||||||
nest and enter it.
|
nest and enter it.
|
||||||
*/
|
*/
|
||||||
for (; tab < join->join_tab + join->top_join_tab_count; tab++)
|
for (; tab < first_top_tab + n_top_tabs_count; tab++)
|
||||||
{
|
{
|
||||||
if (tab->bush_children)
|
if (tab->bush_children)
|
||||||
return tab->bush_children->start;
|
return tab->bush_children->start;
|
||||||
@@ -7201,7 +7209,7 @@ JOIN_TAB *first_top_level_tab(JOIN *join, enum enum_with_const_tables with_const
|
|||||||
|
|
||||||
JOIN_TAB *next_top_level_tab(JOIN *join, JOIN_TAB *tab)
|
JOIN_TAB *next_top_level_tab(JOIN *join, JOIN_TAB *tab)
|
||||||
{
|
{
|
||||||
tab= next_breadth_first_tab(join, tab);
|
tab= next_breadth_first_tab(join, WALK_EXECUTION_TABS, tab);
|
||||||
if (tab && tab->bush_root_tab)
|
if (tab && tab->bush_root_tab)
|
||||||
tab= NULL;
|
tab= NULL;
|
||||||
return tab;
|
return tab;
|
||||||
@@ -7501,6 +7509,13 @@ get_best_combination(JOIN *join)
|
|||||||
|
|
||||||
join->top_join_tab_count= join->join_tab_ranges.head()->end -
|
join->top_join_tab_count= join->join_tab_ranges.head()->end -
|
||||||
join->join_tab_ranges.head()->start;
|
join->join_tab_ranges.head()->start;
|
||||||
|
/*
|
||||||
|
Save pointers to select join tabs for SHOW EXPLAIN
|
||||||
|
*/
|
||||||
|
join->table_access_tabs= join->join_tab;
|
||||||
|
join->top_table_access_tabs_count= join->top_join_tab_count;
|
||||||
|
|
||||||
|
|
||||||
update_depend_map(join);
|
update_depend_map(join);
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
@@ -7922,6 +7937,7 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table)
|
|||||||
!(parent->join_tab_reexec= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB))))
|
!(parent->join_tab_reexec= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB))))
|
||||||
DBUG_RETURN(TRUE); /* purecov: inspected */
|
DBUG_RETURN(TRUE); /* purecov: inspected */
|
||||||
|
|
||||||
|
// psergey-todo: here, save the pointer for original join_tabs.
|
||||||
join_tab= parent->join_tab_reexec;
|
join_tab= parent->join_tab_reexec;
|
||||||
table= &parent->table_reexec[0]; parent->table_reexec[0]= temp_table;
|
table= &parent->table_reexec[0]; parent->table_reexec[0]= temp_table;
|
||||||
table_count= top_join_tab_count= 1;
|
table_count= top_join_tab_count= 1;
|
||||||
@@ -10077,7 +10093,12 @@ void JOIN_TAB::cleanup()
|
|||||||
if (cache)
|
if (cache)
|
||||||
{
|
{
|
||||||
cache->free();
|
cache->free();
|
||||||
cache= 0;
|
cache= 0; // psergey: this is why we don't see "Using join cache" in SHOW EXPLAIN
|
||||||
|
// when it is run for "Using temporary+filesort" queries while they
|
||||||
|
// are at reading-from-tmp-table phase.
|
||||||
|
//
|
||||||
|
// TODO ask igor if this can be just moved to later phase
|
||||||
|
// (JOIN_CACHE objects themselves are not big, arent they)
|
||||||
}
|
}
|
||||||
limit= 0;
|
limit= 0;
|
||||||
if (table)
|
if (table)
|
||||||
@@ -21204,9 +21225,10 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly,
|
|||||||
|
|
||||||
bool printing_materialize_nest= FALSE;
|
bool printing_materialize_nest= FALSE;
|
||||||
uint select_id= join->select_lex->select_number;
|
uint select_id= join->select_lex->select_number;
|
||||||
|
JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS);
|
||||||
|
|
||||||
for (JOIN_TAB *tab= first_breadth_first_tab(join); tab;
|
for (JOIN_TAB *tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS); tab;
|
||||||
tab= next_breadth_first_tab(join, tab))
|
tab= next_breadth_first_tab(join, WALK_OPTIMIZATION_TABS, tab))
|
||||||
{
|
{
|
||||||
if (tab->bush_root_tab)
|
if (tab->bush_root_tab)
|
||||||
{
|
{
|
||||||
@@ -21643,7 +21665,7 @@ int JOIN::print_explain(select_result_sink *result, bool on_the_fly,
|
|||||||
extra.append(STRING_WITH_LEN("; End temporary"));
|
extra.append(STRING_WITH_LEN("; End temporary"));
|
||||||
else if (tab->do_firstmatch)
|
else if (tab->do_firstmatch)
|
||||||
{
|
{
|
||||||
if (tab->do_firstmatch == join->join_tab - 1)
|
if (tab->do_firstmatch == /*join->join_tab*/ first_top_tab - 1)
|
||||||
extra.append(STRING_WITH_LEN("; FirstMatch"));
|
extra.append(STRING_WITH_LEN("; FirstMatch"));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -896,6 +896,20 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
JOIN_TAB *join_tab, **best_ref;
|
JOIN_TAB *join_tab, **best_ref;
|
||||||
|
|
||||||
|
/*
|
||||||
|
For "Using temporary+Using filesort" queries, JOIN::join_tab can point to
|
||||||
|
either:
|
||||||
|
1. array of join tabs describing how to run the select, or
|
||||||
|
2. array of single join tab describing read from the temporary table.
|
||||||
|
|
||||||
|
SHOW EXPLAIN code needs to read/show #1. This is why two next members are
|
||||||
|
there for saving it.
|
||||||
|
*/
|
||||||
|
JOIN_TAB *table_access_tabs;
|
||||||
|
uint top_table_access_tabs_count;
|
||||||
|
|
||||||
|
|
||||||
JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs
|
JOIN_TAB **map2table; ///< mapping between table indexes and JOIN_TABs
|
||||||
JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution
|
JOIN_TAB *join_tab_save; ///< saved join_tab for subquery reexecution
|
||||||
|
|
||||||
|
@@ -850,7 +850,12 @@ struct st_table {
|
|||||||
See TABLE_LIST::process_index_hints().
|
See TABLE_LIST::process_index_hints().
|
||||||
*/
|
*/
|
||||||
bool force_index_group;
|
bool force_index_group;
|
||||||
bool distinct,const_table,no_rows, used_for_duplicate_elimination;
|
/*
|
||||||
|
TRUE<=> this table was created with create_tmp_table(... distinct=TRUE..)
|
||||||
|
call
|
||||||
|
*/
|
||||||
|
bool distinct;
|
||||||
|
bool const_table,no_rows, used_for_duplicate_elimination;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
If set, the optimizer has found that row retrieval should access index
|
If set, the optimizer has found that row retrieval should access index
|
||||||
|
Reference in New Issue
Block a user