mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
EXPLAIN FORMAT=JSON: Add support for non-merged semi-joins
This commit is contained in:
@ -464,4 +464,46 @@ EXPLAIN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#
|
||||||
|
# Non-merged semi-join (aka JTBM)
|
||||||
|
#
|
||||||
|
explain format=json
|
||||||
|
select * from t1 where a in (select max(a) from t1 group by b);
|
||||||
|
EXPLAIN
|
||||||
|
{
|
||||||
|
"query_block": {
|
||||||
|
"select_id": 1,
|
||||||
|
"table": {
|
||||||
|
"table_name": "t1",
|
||||||
|
"access_type": "ALL",
|
||||||
|
"rows": 10,
|
||||||
|
"filtered": 100,
|
||||||
|
"attached_condition": "(t1.a is not null)"
|
||||||
|
},
|
||||||
|
"table": {
|
||||||
|
"table_name": "<subquery2>",
|
||||||
|
"access_type": "eq_ref",
|
||||||
|
"possible_keys": ["distinct_key"],
|
||||||
|
"key": "distinct_key",
|
||||||
|
"key_length": "4",
|
||||||
|
"used_key_parts": ["max(a)"],
|
||||||
|
"ref": ["test.t1.a"],
|
||||||
|
"rows": 1,
|
||||||
|
"filtered": 100,
|
||||||
|
"materialized": {
|
||||||
|
"unique": 1,
|
||||||
|
"query_block": {
|
||||||
|
"select_id": 2,
|
||||||
|
"table": {
|
||||||
|
"table_name": "t1",
|
||||||
|
"access_type": "ALL",
|
||||||
|
"rows": 10,
|
||||||
|
"filtered": 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop table t1;
|
||||||
drop table t0;
|
drop table t0;
|
||||||
|
@ -93,5 +93,11 @@ explain format=json
|
|||||||
select * from (select a, count(*) as cnt from t1 group by a) as tbl1, t1 as
|
select * from (select a, count(*) as cnt from t1 group by a) as tbl1, t1 as
|
||||||
tbl2 where cnt=tbl2.a;
|
tbl2 where cnt=tbl2.a;
|
||||||
|
|
||||||
|
--echo #
|
||||||
|
--echo # Non-merged semi-join (aka JTBM)
|
||||||
|
--echo #
|
||||||
|
explain format=json
|
||||||
|
select * from t1 where a in (select max(a) from t1 group by b);
|
||||||
|
drop table t1;
|
||||||
drop table t0;
|
drop table t0;
|
||||||
|
|
||||||
|
@ -554,6 +554,19 @@ int Explain_node::print_explain_for_children(Explain_query *query,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
This tells whether a child subquery should be printed in JSON output.
|
||||||
|
|
||||||
|
Derived tables and Non-merged semi-joins should not be printed, because they
|
||||||
|
are printed inline in Explain_table_access.
|
||||||
|
*/
|
||||||
|
bool is_connection_printable_in_json(enum Explain_node::explain_connection_type type)
|
||||||
|
{
|
||||||
|
return (type != Explain_node::EXPLAIN_NODE_DERIVED &&
|
||||||
|
type != Explain_node::EXPLAIN_NODE_NON_MERGED_SJ);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Explain_node::print_explain_json_for_children(Explain_query *query,
|
void Explain_node::print_explain_json_for_children(Explain_query *query,
|
||||||
Json_writer *writer,
|
Json_writer *writer,
|
||||||
bool is_analyze)
|
bool is_analyze)
|
||||||
@ -565,7 +578,8 @@ void Explain_node::print_explain_json_for_children(Explain_query *query,
|
|||||||
{
|
{
|
||||||
Explain_node *node= query->get_node(children.at(i));
|
Explain_node *node= query->get_node(children.at(i));
|
||||||
/* Derived tables are printed inside Explain_table_access objects */
|
/* Derived tables are printed inside Explain_table_access objects */
|
||||||
if (node->is_derived_table)
|
|
||||||
|
if (!is_connection_printable_in_json(node->connection_type))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!started)
|
if (!started)
|
||||||
@ -1189,6 +1203,16 @@ void Explain_table_access::print_explain_json(Explain_query *query,
|
|||||||
node->print_explain_json(query, writer, is_analyze);
|
node->print_explain_json(query, writer, is_analyze);
|
||||||
writer->end_object();
|
writer->end_object();
|
||||||
}
|
}
|
||||||
|
if (non_merged_sjm_number)
|
||||||
|
{
|
||||||
|
/* This is a non-merged semi-join table. Print its contents here */
|
||||||
|
writer->add_member("materialized").start_object();
|
||||||
|
writer->add_member("unique").add_ll(1);
|
||||||
|
Explain_node *node= query->get_node(non_merged_sjm_number);
|
||||||
|
node->connection_type= Explain_node::EXPLAIN_NODE_NON_MERGED_SJ;
|
||||||
|
node->print_explain_json(query, writer, is_analyze);
|
||||||
|
writer->end_object();
|
||||||
|
}
|
||||||
|
|
||||||
writer->end_object();
|
writer->end_object();
|
||||||
}
|
}
|
||||||
|
@ -77,12 +77,14 @@ const int FAKE_SELECT_LEX_ID= (int)UINT_MAX;
|
|||||||
class Explain_query;
|
class Explain_query;
|
||||||
|
|
||||||
class Json_writer;
|
class Json_writer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A node can be either a SELECT, or a UNION.
|
A node can be either a SELECT, or a UNION.
|
||||||
*/
|
*/
|
||||||
class Explain_node : public Sql_alloc
|
class Explain_node : public Sql_alloc
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/* A type specifying what kind of node this is */
|
||||||
enum explain_node_type
|
enum explain_node_type
|
||||||
{
|
{
|
||||||
EXPLAIN_UNION,
|
EXPLAIN_UNION,
|
||||||
@ -91,16 +93,24 @@ public:
|
|||||||
EXPLAIN_DELETE,
|
EXPLAIN_DELETE,
|
||||||
EXPLAIN_INSERT
|
EXPLAIN_INSERT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* How this node is connected */
|
||||||
|
enum explain_connection_type {
|
||||||
|
EXPLAIN_NODE_OTHER,
|
||||||
|
EXPLAIN_NODE_DERIVED, /* Materialized derived table */
|
||||||
|
EXPLAIN_NODE_NON_MERGED_SJ /* aka JTBM semi-join */
|
||||||
|
};
|
||||||
|
|
||||||
Explain_node() : is_derived_table(false) {}
|
Explain_node() : connection_type(EXPLAIN_NODE_OTHER) {}
|
||||||
|
|
||||||
virtual enum explain_node_type get_type()= 0;
|
virtual enum explain_node_type get_type()= 0;
|
||||||
virtual int get_select_id()= 0;
|
virtual int get_select_id()= 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TRUE means this is a derived table. FALSE means otherwise.
|
How this node is connected to its parent.
|
||||||
|
(NOTE: EXPLAIN_NODE_NON_MERGED_SJ is set very late currently)
|
||||||
*/
|
*/
|
||||||
bool is_derived_table;
|
enum explain_connection_type connection_type;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A node may have children nodes. When a node's explain structure is
|
A node may have children nodes. When a node's explain structure is
|
||||||
@ -502,6 +512,7 @@ class Explain_table_access : public Sql_alloc
|
|||||||
public:
|
public:
|
||||||
Explain_table_access() :
|
Explain_table_access() :
|
||||||
derived_select_number(0),
|
derived_select_number(0),
|
||||||
|
non_merged_sjm_number(0),
|
||||||
where_cond(NULL),
|
where_cond(NULL),
|
||||||
cache_cond(NULL),
|
cache_cond(NULL),
|
||||||
pushed_index_cond(NULL)
|
pushed_index_cond(NULL)
|
||||||
@ -525,6 +536,8 @@ public:
|
|||||||
find the query plan for the derived table
|
find the query plan for the derived table
|
||||||
*/
|
*/
|
||||||
int derived_select_number;
|
int derived_select_number;
|
||||||
|
/* TODO: join with the previous member. */
|
||||||
|
int non_merged_sjm_number;
|
||||||
|
|
||||||
enum join_type type;
|
enum join_type type;
|
||||||
|
|
||||||
|
@ -4268,8 +4268,13 @@ int st_select_lex_unit::save_union_explain(Explain_query *output)
|
|||||||
{
|
{
|
||||||
SELECT_LEX *first= first_select();
|
SELECT_LEX *first= first_select();
|
||||||
Explain_union *eu= new (output->mem_root) Explain_union;
|
Explain_union *eu= new (output->mem_root) Explain_union;
|
||||||
|
|
||||||
if (derived)
|
if (derived)
|
||||||
eu->is_derived_table= true;
|
eu->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
|
||||||
|
/*
|
||||||
|
Note: Non-merged semi-joins cannot be made out of UNIONs currently, so we
|
||||||
|
dont ever set EXPLAIN_NODE_NON_MERGED_SJ.
|
||||||
|
*/
|
||||||
|
|
||||||
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
|
for (SELECT_LEX *sl= first; sl; sl= sl->next_select())
|
||||||
eu->add_select(sl->select_number);
|
eu->add_select(sl->select_number);
|
||||||
|
@ -23660,8 +23660,13 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab
|
|||||||
subselect that used to produce it.
|
subselect that used to produce it.
|
||||||
*/
|
*/
|
||||||
eta->derived_select_number= table->derived_select_number;
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Save Query Plan Footprint
|
Save Query Plan Footprint
|
||||||
|
|
||||||
@ -23693,7 +23698,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
|
|||||||
xpl_sel->select_type= join->select_lex->type;
|
xpl_sel->select_type= join->select_lex->type;
|
||||||
xpl_sel->message= message;
|
xpl_sel->message= message;
|
||||||
if (select_lex->master_unit()->derived)
|
if (select_lex->master_unit()->derived)
|
||||||
xpl_sel->is_derived_table= true;
|
xpl_sel->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
|
||||||
/* Setting xpl_sel->message means that all other members are invalid */
|
/* Setting xpl_sel->message means that all other members are invalid */
|
||||||
output->add_node(xpl_sel);
|
output->add_node(xpl_sel);
|
||||||
}
|
}
|
||||||
@ -23712,7 +23717,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
|
|||||||
xpl_sel->select_id= join->select_lex->select_number;
|
xpl_sel->select_id= join->select_lex->select_number;
|
||||||
xpl_sel->select_type= join->select_lex->type;
|
xpl_sel->select_type= join->select_lex->type;
|
||||||
if (select_lex->master_unit()->derived)
|
if (select_lex->master_unit()->derived)
|
||||||
xpl_sel->is_derived_table= true;
|
xpl_sel->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
|
||||||
|
|
||||||
JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS);
|
JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS);
|
||||||
|
|
||||||
|
@ -527,6 +527,21 @@ typedef struct st_join_table {
|
|||||||
bool preread_init();
|
bool preread_init();
|
||||||
|
|
||||||
bool is_sjm_nest() { return MY_TEST(bush_children); }
|
bool is_sjm_nest() { return MY_TEST(bush_children); }
|
||||||
|
|
||||||
|
/*
|
||||||
|
If this join_tab reads a non-merged semi-join (also called jtbm), return
|
||||||
|
the select's number. Otherwise, return 0.
|
||||||
|
*/
|
||||||
|
int get_non_merged_semijoin_select() const
|
||||||
|
{
|
||||||
|
Item_in_subselect *subq;
|
||||||
|
if (table->pos_in_table_list &&
|
||||||
|
(subq= table->pos_in_table_list->jtbm_subselect))
|
||||||
|
{
|
||||||
|
return subq->unit->first_select()->select_number;
|
||||||
|
}
|
||||||
|
return 0; /* Not a merged semi-join */
|
||||||
|
}
|
||||||
|
|
||||||
bool access_from_tables_is_allowed(table_map used_tables,
|
bool access_from_tables_is_allowed(table_map used_tables,
|
||||||
table_map sjm_lookup_tables)
|
table_map sjm_lookup_tables)
|
||||||
|
Reference in New Issue
Block a user