diff options
-rw-r--r-- | mysql-test/r/explain_json.result | 42 | ||||
-rw-r--r-- | mysql-test/t/explain_json.test | 6 | ||||
-rw-r--r-- | sql/sql_explain.cc | 26 | ||||
-rw-r--r-- | sql/sql_explain.h | 19 | ||||
-rw-r--r-- | sql/sql_lex.cc | 7 | ||||
-rw-r--r-- | sql/sql_select.cc | 9 | ||||
-rw-r--r-- | sql/sql_select.h | 15 |
7 files changed, 117 insertions, 7 deletions
diff --git a/mysql-test/r/explain_json.result b/mysql-test/r/explain_json.result index 416f432f21d..2211c0a3ab7 100644 --- a/mysql-test/r/explain_json.result +++ b/mysql-test/r/explain_json.result @@ -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; diff --git a/mysql-test/t/explain_json.test b/mysql-test/t/explain_json.test index 8631d85a323..6e3e8746e70 100644 --- a/mysql-test/t/explain_json.test +++ b/mysql-test/t/explain_json.test @@ -93,5 +93,11 @@ explain format=json select * from (select a, count(*) as cnt from t1 group by a) as tbl1, t1 as 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; diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index 37843e8f7d0..4dce48e5944 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -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, Json_writer *writer, 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)); /* Derived tables are printed inside Explain_table_access objects */ - if (node->is_derived_table) + + if (!is_connection_printable_in_json(node->connection_type)) continue; if (!started) @@ -1189,6 +1203,16 @@ void Explain_table_access::print_explain_json(Explain_query *query, node->print_explain_json(query, writer, is_analyze); 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(); } diff --git a/sql/sql_explain.h b/sql/sql_explain.h index 5b2bf9fdb27..a6a0aff7716 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -77,12 +77,14 @@ const int FAKE_SELECT_LEX_ID= (int)UINT_MAX; class Explain_query; class Json_writer; + /* A node can be either a SELECT, or a UNION. */ class Explain_node : public Sql_alloc { public: + /* A type specifying what kind of node this is */ enum explain_node_type { EXPLAIN_UNION, @@ -91,16 +93,24 @@ public: EXPLAIN_DELETE, 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 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 @@ -502,6 +512,7 @@ class Explain_table_access : public Sql_alloc public: Explain_table_access() : derived_select_number(0), + non_merged_sjm_number(0), where_cond(NULL), cache_cond(NULL), pushed_index_cond(NULL) @@ -525,6 +536,8 @@ public: find the query plan for the derived table */ int derived_select_number; + /* TODO: join with the previous member. */ + int non_merged_sjm_number; enum join_type type; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index de22788cd7a..4e6c0efdb48 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -4268,8 +4268,13 @@ int st_select_lex_unit::save_union_explain(Explain_query *output) { SELECT_LEX *first= first_select(); Explain_union *eu= new (output->mem_root) Explain_union; + 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()) eu->add_select(sl->select_number); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 459c770a020..f9cdd08cf68 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -23660,8 +23660,13 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab subselect that used to produce it. */ 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 @@ -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->message= message; 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 */ 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_type= join->select_lex->type; 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); diff --git a/sql/sql_select.h b/sql/sql_select.h index 9463005b2ba..42b2e6b31c2 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -527,6 +527,21 @@ typedef struct st_join_table { bool preread_init(); 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, table_map sjm_lookup_tables) |