From 2ac3b39e68bf6bf1b56e6eafd290c0a78368d0be Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 28 Nov 2014 22:23:29 +0300 Subject: [PATCH] EXPLAIN FORMAT=JSON: support derived tables --- mysql-test/r/explain_json.result | 71 ++++++++++++++++++++++++++++++++ mysql-test/t/explain_json.test | 13 ++++++ sql/sql_explain.cc | 37 +++++++++++++---- sql/sql_explain.h | 17 +++++++- sql/sql_lex.cc | 2 + sql/sql_select.cc | 10 +++++ 6 files changed, 141 insertions(+), 9 deletions(-) diff --git a/mysql-test/r/explain_json.result b/mysql-test/r/explain_json.result index d97abbaa2e3..416f432f21d 100644 --- a/mysql-test/r/explain_json.result +++ b/mysql-test/r/explain_json.result @@ -393,4 +393,75 @@ EXPLAIN } } } +# +# A derived table +# +create table t1 (a int, b int); +insert into t1 select a,a from t0; +explain format=json +select * from (select a, count(*) as cnt from t1 group by a) as tbl +where cnt>0; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "", + "access_type": "ALL", + "rows": 10, + "filtered": 100, + "attached_condition": "(tbl.cnt > 0)", + "materialized": { + "query_block": { + "select_id": 2, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 10, + "filtered": 100 + } + } + } + } + } +} +explain format=json +select * from (select a, count(*) as cnt from t1 group by a) as tbl1, t1 as +tbl2 where cnt=tbl2.a; +EXPLAIN +{ + "query_block": { + "select_id": 1, + "table": { + "table_name": "tbl2", + "access_type": "ALL", + "rows": 10, + "filtered": 100, + "attached_condition": "(tbl2.a is not null)" + }, + "table": { + "table_name": "", + "access_type": "ref", + "possible_keys": ["key0"], + "key": "key0", + "key_length": "8", + "used_key_parts": ["cnt"], + "ref": ["test.tbl2.a"], + "rows": 2, + "filtered": 100, + "attached_condition": "(tbl1.cnt = tbl2.a)", + "materialized": { + "query_block": { + "select_id": 2, + "table": { + "table_name": "t1", + "access_type": "ALL", + "rows": 10, + "filtered": 100 + } + } + } + } + } +} drop table t0; diff --git a/mysql-test/t/explain_json.test b/mysql-test/t/explain_json.test index 3917ce40b1e..8631d85a323 100644 --- a/mysql-test/t/explain_json.test +++ b/mysql-test/t/explain_json.test @@ -80,5 +80,18 @@ explain format=json delete from t0 where a < 3; explain format=json update t0 set a=3 where a in (2,3,4); +--echo # +--echo # A derived table +--echo # +create table t1 (a int, b int); +insert into t1 select a,a from t0; +explain format=json +select * from (select a, count(*) as cnt from t1 group by a) as tbl +where cnt>0; + +explain format=json +select * from (select a, count(*) as cnt from t1 group by a) as tbl1, t1 as +tbl2 where cnt=tbl2.a; + drop table t0; diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index f8a45b55195..37843e8f7d0 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -558,18 +558,29 @@ void Explain_node::print_explain_json_for_children(Explain_query *query, Json_writer *writer, bool is_analyze) { - if (!children.elements()) - return; - - writer->add_member("subqueries").start_array(); + Json_writer_nesting_guard guard(writer); + + bool started= false; for (int i= 0; i < (int) children.elements(); i++) { - writer->start_object(); Explain_node *node= query->get_node(children.at(i)); + /* Derived tables are printed inside Explain_table_access objects */ + if (node->is_derived_table) + continue; + + if (!started) + { + writer->add_member("subqueries").start_array(); + started= true; + } + + writer->start_object(); node->print_explain_json(query, writer, is_analyze); writer->end_object(); } - writer->end_array(); + + if (started) + writer->end_array(); } @@ -665,7 +676,7 @@ void Explain_select::print_explain_json(Explain_query *query, for (uint i=0; i< n_join_tabs; i++) { // psergey-todo: Need to honor SJM nests... - join_tabs[i]->print_explain_json(writer, is_analyze); + join_tabs[i]->print_explain_json(query, writer, is_analyze); } } @@ -1054,7 +1065,8 @@ void Explain_table_access::tag_to_json(Json_writer *writer, enum explain_extra_t } -void Explain_table_access::print_explain_json(Json_writer *writer, +void Explain_table_access::print_explain_json(Explain_query *query, + Json_writer *writer, bool is_analyze) { Json_writer_nesting_guard guard(writer); @@ -1169,6 +1181,15 @@ void Explain_table_access::print_explain_json(Json_writer *writer, } } + if (derived_select_number) + { + /* This is a derived table. Print its contents here */ + writer->add_member("materialized").start_object(); + Explain_node *node= query->get_node(derived_select_number); + 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 5fd1d5ec59a..5b2bf9fdb27 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -92,9 +92,16 @@ public: EXPLAIN_INSERT }; + Explain_node() : is_derived_table(false) {} + virtual enum explain_node_type get_type()= 0; virtual int get_select_id()= 0; + /* + TRUE means this is a derived table. FALSE means otherwise. + */ + bool is_derived_table; + /* A node may have children nodes. When a node's explain structure is created, children nodes may not yet have QPFs. This is why we store ids. @@ -494,6 +501,7 @@ class Explain_table_access : public Sql_alloc { public: Explain_table_access() : + derived_select_number(0), where_cond(NULL), cache_cond(NULL), pushed_index_cond(NULL) @@ -512,6 +520,12 @@ public: /* id and 'select_type' are cared-of by the parent Explain_select */ StringBuffer<32> table_name; + /* + Non-zero number means this is a derived table. The number can be used to + find the query plan for the derived table + */ + int derived_select_number; + enum join_type type; StringBuffer<32> used_partitions; @@ -581,7 +595,8 @@ public: bool is_analyze, uint select_id, const char *select_type, bool using_temporary, bool using_filesort); - void print_explain_json(Json_writer *writer, bool is_analyze); + void print_explain_json(Explain_query *query, Json_writer *writer, + bool is_analyze); /* ANALYZE members*/ Table_access_tracker tracker; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7441f46a266..de22788cd7a 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -4268,6 +4268,8 @@ 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; 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 f66525d9f18..459c770a020 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -23654,6 +23654,12 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab tab->cache->save_explain_data(&eta->bka_type); } } + + /* + 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; } /* @@ -23686,6 +23692,8 @@ 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; xpl_sel->message= message; + if (select_lex->master_unit()->derived) + xpl_sel->is_derived_table= true; /* Setting xpl_sel->message means that all other members are invalid */ output->add_node(xpl_sel); } @@ -23703,6 +23711,8 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table, join->select_lex->set_explain_type(true); 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; JOIN_TAB* const first_top_tab= first_breadth_first_tab(join, WALK_OPTIMIZATION_TABS);