From 2078392cc9bb49720ca3949731078af113ae4f43 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Wed, 30 Mar 2016 18:39:10 +0300 Subject: [PATCH] Make EXPLAIN FORMAT=JSON be able to show the key that's used for sorting. This will be useful for window functions development. --- mysql-test/r/analyze_format_json.result | 5 ++ mysql-test/r/analyze_stmt_orderby.result | 9 +++ mysql-test/r/explain_json.result | 5 ++ mysql-test/r/win.result | 2 + sql/sql_explain.cc | 79 +++++++++++++++++------- sql/sql_explain.h | 24 +++++-- sql/sql_select.cc | 12 ++-- 7 files changed, 100 insertions(+), 36 deletions(-) diff --git a/mysql-test/r/analyze_format_json.result b/mysql-test/r/analyze_format_json.result index a66a3895008..f58b651464e 100644 --- a/mysql-test/r/analyze_format_json.result +++ b/mysql-test/r/analyze_format_json.result @@ -490,6 +490,7 @@ ANALYZE "volatile parameter": "REPLACED", "having_condition": "(TOP > t2.a)", "filesort": { + "sort_key": "t2.a", "r_loops": 1, "volatile parameter": "REPLACED", "r_used_priority_queue": false, @@ -519,6 +520,7 @@ ANALYZE "r_loops": 1, "volatile parameter": "REPLACED", "filesort": { + "sort_key": "t2.a", "r_loops": 1, "volatile parameter": "REPLACED", "r_used_priority_queue": false, @@ -559,6 +561,7 @@ ANALYZE "r_loops": 1, "volatile parameter": "REPLACED", "filesort": { + "sort_key": "t2.a", "r_loops": 1, "volatile parameter": "REPLACED", "r_used_priority_queue": false, @@ -680,6 +683,7 @@ ANALYZE "r_loops": 1, "volatile parameter": "REPLACED", "filesort": { + "sort_key": "group_concat(t3.f3 separator ',')", "r_loops": 1, "volatile parameter": "REPLACED", "r_used_priority_queue": false, @@ -687,6 +691,7 @@ ANALYZE "volatile parameter": "REPLACED", "temporary_table": { "filesort": { + "sort_key": "(subquery#2)", "r_loops": 1, "volatile parameter": "REPLACED", "r_used_priority_queue": false, diff --git a/mysql-test/r/analyze_stmt_orderby.result b/mysql-test/r/analyze_stmt_orderby.result index 244da2f596e..37f0005148e 100644 --- a/mysql-test/r/analyze_stmt_orderby.result +++ b/mysql-test/r/analyze_stmt_orderby.result @@ -172,6 +172,7 @@ EXPLAIN "query_block": { "select_id": 1, "filesort": { + "sort_key": "t2.b", "temporary_table": { "table": { "table_name": "t0", @@ -204,6 +205,7 @@ ANALYZE "r_loops": 1, "r_total_time_ms": "REPLACED", "filesort": { + "sort_key": "t2.b", "r_loops": 1, "r_total_time_ms": "REPLACED", "r_limit": 4, @@ -256,6 +258,7 @@ EXPLAIN "select_id": 1, "read_sorted_file": { "filesort": { + "sort_key": "t0.a", "table": { "table_name": "t0", "access_type": "ALL", @@ -289,6 +292,7 @@ ANALYZE "read_sorted_file": { "r_rows": 10, "filesort": { + "sort_key": "t0.a", "r_loops": 1, "r_total_time_ms": "REPLACED", "r_used_priority_queue": false, @@ -345,6 +349,7 @@ ANALYZE "r_loops": 1, "r_total_time_ms": "REPLACED", "filesort": { + "sort_key": "t2.c", "r_loops": 1, "r_total_time_ms": "REPLACED", "r_used_priority_queue": false, @@ -454,6 +459,7 @@ ANALYZE "r_loops": 1, "r_total_time_ms": "REPLACED", "filesort": { + "sort_key": "count(distinct t5.b)", "r_loops": 1, "r_total_time_ms": "REPLACED", "r_limit": 1, @@ -461,6 +467,7 @@ ANALYZE "r_output_rows": 2, "temporary_table": { "filesort": { + "sort_key": "t5.a", "r_loops": 1, "r_total_time_ms": "REPLACED", "r_used_priority_queue": false, @@ -510,8 +517,10 @@ EXPLAIN "query_block": { "select_id": 1, "filesort": { + "sort_key": "count(distinct t5.b)", "temporary_table": { "filesort": { + "sort_key": "t5.a", "temporary_table": { "table": { "table_name": "t6", diff --git a/mysql-test/r/explain_json.result b/mysql-test/r/explain_json.result index 89df0209073..af5d1b800aa 100644 --- a/mysql-test/r/explain_json.result +++ b/mysql-test/r/explain_json.result @@ -486,6 +486,7 @@ EXPLAIN "query_block": { "select_id": 2, "filesort": { + "sort_key": "t1.a", "temporary_table": { "table": { "table_name": "t1", @@ -529,6 +530,7 @@ EXPLAIN "query_block": { "select_id": 2, "filesort": { + "sort_key": "t1.a", "temporary_table": { "table": { "table_name": "t1", @@ -1129,6 +1131,7 @@ EXPLAIN "select_id": 1, "having_condition": "(TOP > t2.a)", "filesort": { + "sort_key": "t2.a", "temporary_table": { "table": { "table_name": "t2", @@ -1147,6 +1150,7 @@ EXPLAIN "query_block": { "select_id": 1, "filesort": { + "sort_key": "t2.a", "temporary_table": { "table": { "table_name": "t2", @@ -1176,6 +1180,7 @@ EXPLAIN "query_block": { "select_id": 1, "filesort": { + "sort_key": "t2.a", "temporary_table": { "table": { "table_name": "t2", diff --git a/mysql-test/r/win.result b/mysql-test/r/win.result index 11d1fe9279f..e86f8a40794 100644 --- a/mysql-test/r/win.result +++ b/mysql-test/r/win.result @@ -1436,6 +1436,7 @@ EXPLAIN "query_block": { "select_id": 1, "filesort": { + "sort_key": "t1.a", "window_functions_computation": { "temporary_table": { "table": { @@ -1488,6 +1489,7 @@ EXPLAIN "select_id": 1, "having_condition": "(MX in (3,5,7))", "filesort": { + "sort_key": "t1.b", "window_functions_computation": { "temporary_table": { "table": { diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index e70ad7d5239..605b741bdd2 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -30,6 +30,7 @@ const char * STR_IMPOSSIBLE_WHERE= "Impossible WHERE"; const char * STR_NO_ROWS_AFTER_PRUNING= "No matching rows after partition pruning"; static void write_item(Json_writer *writer, Item *item); +static void append_item_to_str(String *out, Item *item); Explain_query::Explain_query(THD *thd_arg, MEM_ROOT *root) : mem_root(root), upd_del_plan(NULL), insert_plan(NULL), @@ -877,8 +878,7 @@ void Explain_select::print_explain_json(Explain_query *query, case AGGR_OP_FILESORT: { writer->add_member("filesort").start_object(); - if (is_analyze) - ((Explain_aggr_filesort*)node)->tracker->print_json_members(writer); + ((Explain_aggr_filesort*)node)->print_json_members(writer, is_analyze); break; } case AGGR_OP_REMOVE_DUPLICATES: @@ -905,6 +905,41 @@ void Explain_select::print_explain_json(Explain_query *query, writer->end_object(); } +void Explain_aggr_filesort::init(THD *thd, Filesort *filesort) +{ + for (ORDER *ord= filesort->order; ord; ord= ord->next) + { + sort_items.push_back(ord->item[0], thd->mem_root); + } + filesort->tracker= &tracker; +} + +void Explain_aggr_filesort::print_json_members(Json_writer *writer, + bool is_analyze) +{ + char item_buf[256]; + String str(item_buf, sizeof(item_buf), &my_charset_bin); + str.length(0); + + List_iterator_fast it(sort_items); + Item *item; + bool first= true; + while ((item= it++)) + { + if (first) + first= false; + else + { + str.append(", "); + } + append_item_to_str(&str, item); + } + + writer->add_member("sort_key").add_str(str.c_ptr_safe()); + + if (is_analyze) + tracker.print_json_members(writer); +} void Explain_basic_join::print_explain_json(Explain_query *query, Json_writer *writer, @@ -1222,7 +1257,7 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai extra_buf.append(STRING_WITH_LEN("Using temporary")); } - if (using_filesort || this->using_filesort) + if (using_filesort || this->pre_join_sort) { if (first) first= false; @@ -1282,6 +1317,15 @@ static void write_item(Json_writer *writer, Item *item) writer->add_str(str.c_ptr_safe()); } +static void append_item_to_str(String *out, Item *item) +{ + THD *thd= current_thd; + ulonglong save_option_bits= thd->variables.option_bits; + thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE; + + item->print(out, QT_EXPLAIN); + thd->variables.option_bits= save_option_bits; +} void Explain_table_access::tag_to_json(Json_writer *writer, enum explain_extra_tag tag) { @@ -1409,15 +1453,6 @@ void add_json_keyset(Json_writer *writer, const char *elem_name, print_json_array(writer, elem_name, *keyset); } -/* - @param fs_tracker Normally NULL. When not NULL, it means that the join tab - used filesort to pre-sort the data. Then, sorted data - was read and the rest of the join was executed. - - @note - EXPLAIN command will check whether fs_tracker is present, but it can't use - any value from fs_tracker (these are only valid for ANALYZE). -*/ void Explain_table_access::print_explain_json(Explain_query *query, Json_writer *writer, @@ -1425,7 +1460,7 @@ void Explain_table_access::print_explain_json(Explain_query *query, { Json_writer_nesting_guard guard(writer); - if (using_filesort) + if (pre_join_sort) { /* filesort was invoked on this join tab before doing the join with the rest */ writer->add_member("read_sorted_file").start_object(); @@ -1452,9 +1487,7 @@ void Explain_table_access::print_explain_json(Explain_query *query, } } writer->add_member("filesort").start_object(); - - if (is_analyze) - fs_tracker->print_json_members(writer); + pre_join_sort->print_json_members(writer, is_analyze); } if (bka_type.is_using_jbuf()) @@ -1532,11 +1565,11 @@ void Explain_table_access::print_explain_json(Explain_query *query, if (is_analyze) { writer->add_member("r_rows"); - if (fs_tracker) + if (pre_join_sort) { /* Get r_rows value from filesort */ - if (fs_tracker->get_r_loops()) - writer->add_double(fs_tracker->get_avg_examined_rows()); + if (pre_join_sort->tracker.get_r_loops()) + writer->add_double(pre_join_sort->tracker.get_avg_examined_rows()); else writer->add_null(); } @@ -1563,11 +1596,11 @@ void Explain_table_access::print_explain_json(Explain_query *query, if (is_analyze) { writer->add_member("r_filtered"); - if (fs_tracker) + if (pre_join_sort) { /* Get r_filtered value from filesort */ - if (fs_tracker->get_r_loops()) - writer->add_double(fs_tracker->get_r_filtered()); + if (pre_join_sort->tracker.get_r_loops()) + writer->add_double(pre_join_sort->tracker.get_r_filtered()); else writer->add_null(); } @@ -1645,7 +1678,7 @@ void Explain_table_access::print_explain_json(Explain_query *query, writer->end_object(); } - if (using_filesort) + if (pre_join_sort) { writer->end_object(); // filesort writer->end_object(); // read_sorted_file diff --git a/sql/sql_explain.h b/sql/sql_explain.h index 8c632d7f36a..46f232ab783 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -281,9 +281,18 @@ public: class Explain_aggr_filesort : public Explain_aggr_node { + List sort_items; public: enum_explain_aggr_node_type get_type() { return AGGR_OP_FILESORT; } - Filesort_tracker *tracker; + Filesort_tracker tracker; + + Explain_aggr_filesort(bool is_analyze) : tracker(is_analyze) + { + child= NULL; + } + + void init(THD* thd, Filesort *filesort); + void print_json_members(Json_writer *writer, bool is_analyze); }; class Explain_aggr_tmp_table : public Explain_aggr_node @@ -663,8 +672,7 @@ public: cache_cond(NULL), pushed_index_cond(NULL), sjm_nest(NULL), - using_filesort(false), - fs_tracker(NULL) + pre_join_sort(NULL) {} ~Explain_table_access() { delete sjm_nest; } @@ -757,9 +765,13 @@ public: Item *pushed_index_cond; Explain_basic_join *sjm_nest; - - bool using_filesort; - Filesort_tracker *fs_tracker; + + /* + This describes a possible filesort() call that is done before doing the + join operation. + */ + Explain_aggr_filesort *pre_join_sort; + /* ANALYZE members */ /* Tracker for reading the table */ diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d7d0b6eaf7a..ecf4fd34490 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -23746,7 +23746,6 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, explain_plan= eta; eta->key.clear(); eta->quick_info= NULL; - eta->using_filesort= false; SQL_SELECT *tab_select; /* @@ -23758,9 +23757,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, if (filesort) { - eta->using_filesort= true; // This fixes EXPLAIN - eta->fs_tracker= filesort->tracker= - new Filesort_tracker(thd->lex->analyze_stmt); + eta->pre_join_sort= new Explain_aggr_filesort(thd->lex->analyze_stmt); + eta->pre_join_sort->init(thd, filesort); } tracker= &eta->tracker; @@ -24201,9 +24199,9 @@ void save_agg_explain_data(JOIN *join, Explain_select *xpl_sel) if (join_tab->filesort) { - Explain_aggr_filesort *eaf = new Explain_aggr_filesort; - eaf->tracker= new Filesort_tracker(join->thd->lex->analyze_stmt); - join_tab->filesort->tracker= eaf->tracker; + bool is_analyze= join->thd->lex->analyze_stmt; + Explain_aggr_filesort *eaf = new Explain_aggr_filesort(is_analyze); + eaf->init(join->thd, join_tab->filesort); prev_node= node; node= eaf;