From 8aaacb5509a7981062d3ad0331cef212e3d79d5d Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Mon, 14 Aug 2023 11:09:51 +0300 Subject: [PATCH] MDEV-31432 tmp_table field accessed after free Before this patch, the code in Item_field::print() used this convention (described in sql_explain.h:ExplainDataStructureLifetime): - By default, the table that Item_field refers to is accessible. - ANALYZE and SHOW {EXPLAIN|ANALYZE} may print Items after some temporary tables have been dropped. They use QT_DONT_ACCESS_TMP_TABLES flag. When it is ON, Item_field::print will not access the table it refers to, if it is a temp.table The bug was that EXPLAIN statement also may compute subqueries (depending on subquery context and @@expensive_subquery_limit setting). After the computation, the subquery calls JOIN::cleanup(true) which drops some of its temporary tables. Calling Item_field::print() that refer to such table will cause an access to free'd memory. In this patch, we take into account that query optimization can compute a subquery and discard its temporary tables. Item_field::print() now assumes that any temporary table might have already been dropped. This means QT_DONT_ACCESS_TMP_TABLES flag is not needed - we imply it is always present. But we also make one exception: derived tables are not freed in JOIN::cleanup() call. They are freed later in close_thread_tables(), at the same time when regular tables are closed. Because of that, Item_field::print may assume that temp.tables representing derived tables are available. Initial patch by: Rex Jonston Reviewed by: Monty --- mysql-test/main/show_analyze.result | 39 +++++++++ mysql-test/main/show_analyze.test | 41 +++++++++ sql/item.cc | 32 ++++--- sql/item.h | 31 +++---- sql/item_func.cc | 2 +- sql/item_subselect.cc | 4 +- sql/item_sum.cc | 2 +- sql/mysqld.h | 6 +- sql/sql_base.cc | 3 + sql/sql_explain.cc | 131 +++++++++++----------------- sql/sql_explain.h | 53 +++++------ sql/sql_lex.cc | 1 - sql/sql_parse.cc | 3 +- sql/sql_select.cc | 6 +- sql/sql_window.cc | 2 +- 15 files changed, 199 insertions(+), 157 deletions(-) diff --git a/mysql-test/main/show_analyze.result b/mysql-test/main/show_analyze.result index c5fd61ecf64..f0a492344ba 100644 --- a/mysql-test/main/show_analyze.result +++ b/mysql-test/main/show_analyze.result @@ -434,3 +434,42 @@ ANALYZE } } DROP TABLE t1; +# +# MDEV-31432 tmp_table field accessed after free +# testing for the above (MDEV-28201) caused use after free error +# +create table t1 (x int) engine=myisam; +insert into t1 values(1); +set @tmp=@@optimizer_trace; +set @@optimizer_trace=1; +SELECT +1 IN +(( +SELECT +1 IN (SELECT 1 AS x0 +FROM +( +SELECT * +FROM (SELECT 1 AS x) AS x5 +GROUP BY x,x +HAVING +x IN ( +SELECT * +FROM t1 AS x1 +WHERE +x IN (SELECT 1 AS x +FROM t1 AS x3 +GROUP BY x +HAVING +x IN (SELECT 0 FROM t1 AS x4) +) +) +) AS x6 +) +FROM +t1 +)) as VAL; +VAL +0 +set optimizer_trace=@tmp; +drop table t1; diff --git a/mysql-test/main/show_analyze.test b/mysql-test/main/show_analyze.test index 9d59d5b2188..58d36d7dd16 100644 --- a/mysql-test/main/show_analyze.test +++ b/mysql-test/main/show_analyze.test @@ -364,3 +364,44 @@ ANALYZE format=json SELECT 1 FROM t1 GROUP BY convert_tz('1969-12-31 22:00:00',a,'+10:00'); DROP TABLE t1; +--echo # +--echo # MDEV-31432 tmp_table field accessed after free +--echo # testing for the above (MDEV-28201) caused use after free error +--echo # +create table t1 (x int) engine=myisam; +insert into t1 values(1); +set @tmp=@@optimizer_trace; +set @@optimizer_trace=1; +# Different warning text is produced in regular and --ps-protocol runs: +--disable_warnings +SELECT + 1 IN + (( + SELECT + 1 IN (SELECT 1 AS x0 + FROM + ( + SELECT * + FROM (SELECT 1 AS x) AS x5 + GROUP BY x,x + HAVING + x IN ( + SELECT * + FROM t1 AS x1 + WHERE + x IN (SELECT 1 AS x + FROM t1 AS x3 + GROUP BY x + HAVING + x IN (SELECT 0 FROM t1 AS x4) + ) + ) + ) AS x6 + ) + FROM + t1 + )) as VAL; +--enable_warnings +set optimizer_trace=@tmp; +drop table t1; + diff --git a/sql/item.cc b/sql/item.cc index 6b3a5ffe084..5591bcdb9c4 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3138,7 +3138,7 @@ void Item_field::set_field(Field *field_par) if (field->table->s->tmp_table == SYSTEM_TMP_TABLE || field->table->s->tmp_table == INTERNAL_TMP_TABLE) - set_refers_to_temp_table(true); + set_refers_to_temp_table(); } @@ -3615,7 +3615,7 @@ Item *Item_field::get_tmp_table_item(THD *thd) if (new_item) { new_item->field= new_item->result_field; - new_item->set_refers_to_temp_table(true); + new_item->set_refers_to_temp_table(); } return new_item; } @@ -3626,9 +3626,14 @@ longlong Item_field::val_int_endpoint(bool left_endp, bool *incl_endp) return null_value? LONGLONG_MIN : res; } -void Item_field::set_refers_to_temp_table(bool value) +void Item_field::set_refers_to_temp_table() { - refers_to_temp_table= value; + /* + Derived temp. tables have non-zero derived_select_number. + We don't need to distingish between other kinds of temp.tables currently. + */ + refers_to_temp_table= (field->table->derived_select_number != 0)? + REFERS_TO_DERIVED_TMP : REFERS_TO_OTHER_TMP; } @@ -6292,7 +6297,7 @@ void Item_field::cleanup() field= 0; item_equal= NULL; null_value= FALSE; - refers_to_temp_table= FALSE; + refers_to_temp_table= NO_TEMP_TABLE; DBUG_VOID_RETURN; } @@ -7860,14 +7865,15 @@ void Item_field::print(String *str, enum_query_type query_type) { /* If the field refers to a constant table, print the value. - (1): But don't attempt to do that if - * the field refers to a temporary (work) table, and - * temp. tables might already have been dropped. + There are two exceptions: + 1. For temporary (aka "work") tables, we can only access the derived temp. + tables. Other kinds of tables might already have been dropped. + 2. Don't print constants if QT_NO_DATA_EXPANSION or QT_VIEW_INTERNAL is + specified. */ - if (!(refers_to_temp_table && // (1) - (query_type & QT_DONT_ACCESS_TMP_TABLES)) && // (1) - field && field->table->const_table && - !(query_type & (QT_NO_DATA_EXPANSION | QT_VIEW_INTERNAL))) + if ((refers_to_temp_table != REFERS_TO_OTHER_TMP) && // (1) + !(query_type & (QT_NO_DATA_EXPANSION | QT_VIEW_INTERNAL)) && // (2) + field && field->table->const_table) { print_value(str); return; @@ -9145,7 +9151,7 @@ Item* Item_cache_wrapper::get_tmp_table_item(THD *thd) { auto item_field= new (thd->mem_root) Item_field(thd, result_field); if (item_field) - item_field->set_refers_to_temp_table(true); + item_field->set_refers_to_temp_table(); return item_field; } return copy_or_same(thd); diff --git a/sql/item.h b/sql/item.h index 6b7008c8075..cc1075dc1a9 100644 --- a/sql/item.h +++ b/sql/item.h @@ -3568,27 +3568,18 @@ public: private: /* - Setting this member to TRUE (via set_refers_to_temp_table()) - ensures print() function continues to work even if the table - has been dropped. + Indicates whether this Item_field refers to a regular or some kind of + temporary table. + This is needed for print() to work: it may be called even after the table + referred by the Item_field has been dropped. - We need this for "ANALYZE statement" feature. Query execution has - these steps: - 1. Run the query. - 2. Cleanup starts. Temporary tables are destroyed - 3. print "ANALYZE statement" output, if needed - 4. Call close_thread_table() for regular tables. - - Step #4 is done after step #3, so "ANALYZE stmt" has no problem printing - Item_field objects that refer to regular tables. - - However, Step #3 is done after Step #2. Attempt to print Item_field objects - that refer to temporary tables will cause access to freed memory. - - To resolve this, we use refers_to_temp_table member to refer to items - in temporary (work) tables. + See ExplainDataStructureLifetime in sql_explain.h for details. */ - bool refers_to_temp_table= false; + enum { + NO_TEMP_TABLE= 0, + REFERS_TO_DERIVED_TMP= 1, + REFERS_TO_OTHER_TMP=2 + } refers_to_temp_table = NO_TEMP_TABLE; public: Item_field(THD *thd, Name_resolution_context *context_arg, @@ -3804,7 +3795,7 @@ public: return field->table->pos_in_table_list->outer_join; } bool check_index_dependence(void *arg) override; - void set_refers_to_temp_table(bool value); + void set_refers_to_temp_table(); friend class Item_default_value; friend class Item_insert_value; friend class st_select_lex_unit; diff --git a/sql/item_func.cc b/sql/item_func.cc index ee0f507d3e8..cb432b4f82f 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -749,7 +749,7 @@ Item *Item_func::get_tmp_table_item(THD *thd) { auto item_field= new (thd->mem_root) Item_field(thd, result_field); if (item_field) - item_field->set_refers_to_temp_table(true); + item_field->set_refers_to_temp_table(); return item_field; } return copy_or_same(thd); diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index ff004bbb045..e020627d68b 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1030,7 +1030,7 @@ Item *Item_subselect::get_tmp_table_item(THD *thd_arg) auto item_field= new (thd->mem_root) Item_field(thd_arg, result_field); if (item_field) - item_field->set_refers_to_temp_table(true); + item_field->set_refers_to_temp_table(); return item_field; } return copy_or_same(thd_arg); @@ -5310,7 +5310,7 @@ bool subselect_hash_sj_engine::make_semi_join_conds() Item_field *right_col_item= new (thd->mem_root) Item_field(thd, context, tmp_table->field[i]); if (right_col_item) - right_col_item->set_refers_to_temp_table(true); + right_col_item->set_refers_to_temp_table(); if (!right_col_item || !(eq_cond= new (thd->mem_root) diff --git a/sql/item_sum.cc b/sql/item_sum.cc index ffac6dbb912..bbd09a59267 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -562,7 +562,7 @@ Item *Item_sum::get_tmp_table_item(THD *thd) auto item_field= new (thd->mem_root) Item_field(thd, result_field_tmp++); if (item_field) - item_field->set_refers_to_temp_table(true); + item_field->set_refers_to_temp_table(); sum_item->args[i]= item_field; } } diff --git a/sql/mysqld.h b/sql/mysqld.h index 5263e397a15..28a0cb30637 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -905,11 +905,7 @@ enum enum_query_type // don't reveal values. QT_NO_DATA_EXPANSION= (1 << 9), // Remove wrappers added for TVC when creating or showing view - QT_NO_WRAPPERS_FOR_TVC_IN_VIEW= (1 << 12), - - // The temporary tables used by the query might be freed by the time - // this print() call is made. - QT_DONT_ACCESS_TMP_TABLES= (1 << 13) + QT_NO_WRAPPERS_FOR_TVC_IN_VIEW= (1 << 12) }; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index c9c74046fd9..a4269e20614 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -871,6 +871,9 @@ int close_thread_tables(THD *thd) TODO: Probably even better approach is to simply associate list of derived tables with (sub-)statement instead of thread and destroy them at the end of its execution. + + Note: EXPLAIN/ANALYZE depends on derived tables being freed here. See + sql_explain.h:ExplainDataStructureLifetime. */ if (thd->derived_tables) { diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index 9c09259cb56..30ce9535a14 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -39,8 +39,8 @@ const char *unit_operation_text[4]= const char *pushed_derived_text= "PUSHED DERIVED"; const char *pushed_select_text= "PUSHED SELECT"; -static void write_item(Json_writer *writer, Item *item, bool no_tmp_tbl); -static void append_item_to_str(String *out, Item *item, bool no_tmp_tbl); +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(nullptr), insert_plan(nullptr), @@ -196,7 +196,7 @@ int Explain_query::send_explain(THD *thd, bool extended) int res= 0; if (thd->lex->explain_json) - print_explain_json(result, thd->lex->analyze_stmt, false /*is_show_cmd*/); + print_explain_json(result, thd->lex->analyze_stmt); else { res= print_explain(result, lex->describe, thd->lex->analyze_stmt); @@ -252,16 +252,8 @@ int Explain_query::print_explain(select_result_sink *output, } -/* - @param is_show_cmd TRUE<=> This is a SHOW EXPLAIN|ANALYZE command. - (These commands may be called at late stage in - the query processing, we need to pass no_tmp_tbl=true - to other print functions) -*/ - int Explain_query::print_explain_json(select_result_sink *output, bool is_analyze, - bool is_show_cmd, ulonglong query_time_in_progress_ms) { Json_writer writer; @@ -275,25 +267,17 @@ int Explain_query::print_explain_json(select_result_sink *output, writer.add_member("r_query_time_in_progress_ms"). add_ull(query_time_in_progress_ms); - /* - If we are printing ANALYZE FORMAT=JSON output, take into account that - query's temporary tables have already been freed. See sql_explain.h, - sql_explain.h:ExplainDataStructureLifetime for details. - */ - if (is_analyze) - is_show_cmd= true; - if (upd_del_plan) - upd_del_plan->print_explain_json(this, &writer, is_analyze, is_show_cmd); + upd_del_plan->print_explain_json(this, &writer, is_analyze); else if (insert_plan) - insert_plan->print_explain_json(this, &writer, is_analyze, is_show_cmd); + insert_plan->print_explain_json(this, &writer, is_analyze); else { /* Start printing from node with id=1 */ Explain_node *node= get_node(1); if (!node) return 1; /* No query plan */ - node->print_explain_json(this, &writer, is_analyze, is_show_cmd); + node->print_explain_json(this, &writer, is_analyze); } writer.end_object(); @@ -656,8 +640,7 @@ int Explain_union::print_explain(Explain_query *query, void Explain_union::print_explain_json(Explain_query *query, - Json_writer *writer, bool is_analyze, - bool no_tmp_tbl) + Json_writer *writer, bool is_analyze) { Json_writer_nesting_guard guard(writer); char table_name_buffer[SAFE_NAME_LEN]; @@ -702,12 +685,12 @@ void Explain_union::print_explain_json(Explain_query *query, //writer->add_member("dependent").add_str("TODO"); //writer->add_member("cacheable").add_str("TODO"); Explain_select *sel= query->get_select(union_members.at(i)); - sel->print_explain_json(query, writer, is_analyze, no_tmp_tbl); + sel->print_explain_json(query, writer, is_analyze); writer->end_object(); } writer->end_array(); - print_explain_json_for_children(query, writer, is_analyze, no_tmp_tbl); + print_explain_json_for_children(query, writer, is_analyze); writer->end_object(); // union_result writer->end_object(); // query_block @@ -769,8 +752,7 @@ bool is_connection_printable_in_json(enum Explain_node::explain_connection_type void Explain_node::print_explain_json_for_children(Explain_query *query, Json_writer *writer, - bool is_analyze, - bool no_tmp_tbl) + bool is_analyze) { Json_writer_nesting_guard guard(writer); @@ -797,7 +779,7 @@ void Explain_node::print_explain_json_for_children(Explain_query *query, } writer->start_object(); - node->print_explain_json(query, writer, is_analyze, no_tmp_tbl); + node->print_explain_json(query, writer, is_analyze); writer->end_object(); } @@ -977,8 +959,7 @@ void Explain_select::add_linkage(Json_writer *writer) } void Explain_select::print_explain_json(Explain_query *query, - Json_writer *writer, bool is_analyze, - bool no_tmp_tbl) + Json_writer *writer, bool is_analyze) { Json_writer_nesting_guard guard(writer); @@ -1000,7 +981,7 @@ void Explain_select::print_explain_json(Explain_query *query, message); writer->end_object(); - print_explain_json_for_children(query, writer, is_analyze, no_tmp_tbl); + print_explain_json_for_children(query, writer, is_analyze); writer->end_object(); } else @@ -1022,17 +1003,17 @@ void Explain_select::print_explain_json(Explain_query *query, if (exec_const_cond) { writer->add_member("const_condition"); - write_item(writer, exec_const_cond, no_tmp_tbl); + write_item(writer, exec_const_cond); } if (outer_ref_cond) { writer->add_member("outer_ref_condition"); - write_item(writer, outer_ref_cond, no_tmp_tbl); + write_item(writer, outer_ref_cond); } if (pseudo_bits_cond) { writer->add_member("pseudo_bits_condition"); - write_item(writer, pseudo_bits_cond, no_tmp_tbl); + write_item(writer, pseudo_bits_cond); } /* we do not print HAVING which always evaluates to TRUE */ @@ -1040,7 +1021,7 @@ void Explain_select::print_explain_json(Explain_query *query, { writer->add_member("having_condition"); if (likely(having)) - write_item(writer, having, no_tmp_tbl); + write_item(writer, having); else { /* Normally we should not go this branch, left just for safety */ @@ -1064,7 +1045,7 @@ void Explain_select::print_explain_json(Explain_query *query, { writer->add_member("filesort").start_object(); auto aggr_node= (Explain_aggr_filesort*)node; - aggr_node->print_json_members(writer, is_analyze, no_tmp_tbl); + aggr_node->print_json_members(writer, is_analyze); break; } case AGGR_OP_REMOVE_DUPLICATES: @@ -1075,7 +1056,7 @@ void Explain_select::print_explain_json(Explain_query *query, //TODO: make print_json_members virtual? writer->add_member("window_functions_computation").start_object(); auto aggr_node= (Explain_aggr_window_funcs*)node; - aggr_node->print_json_members(writer, is_analyze, no_tmp_tbl); + aggr_node->print_json_members(writer, is_analyze); break; } default: @@ -1084,8 +1065,7 @@ void Explain_select::print_explain_json(Explain_query *query, started_objects++; } - Explain_basic_join::print_explain_json_interns(query, writer, is_analyze, - no_tmp_tbl); + Explain_basic_join::print_explain_json_interns(query, writer, is_analyze); for (;started_objects; started_objects--) writer->end_object(); @@ -1114,8 +1094,7 @@ Explain_aggr_filesort::Explain_aggr_filesort(MEM_ROOT *mem_root, void Explain_aggr_filesort::print_json_members(Json_writer *writer, - bool is_analyze, - bool no_tmp_tbl) + bool is_analyze) { char item_buf[256]; String str(item_buf, sizeof(item_buf), &my_charset_bin); @@ -1135,7 +1114,7 @@ void Explain_aggr_filesort::print_json_members(Json_writer *writer, { str.append(STRING_WITH_LEN(", ")); } - append_item_to_str(&str, item, no_tmp_tbl); + append_item_to_str(&str, item); if (*direction == ORDER::ORDER_DESC) str.append(STRING_WITH_LEN(" desc")); } @@ -1148,8 +1127,7 @@ void Explain_aggr_filesort::print_json_members(Json_writer *writer, void Explain_aggr_window_funcs::print_json_members(Json_writer *writer, - bool is_analyze, - bool no_tmp_tbl) + bool is_analyze) { Explain_aggr_filesort *srt; List_iterator it(sorts); @@ -1158,19 +1136,19 @@ void Explain_aggr_window_funcs::print_json_members(Json_writer *writer, { Json_writer_object sort(writer); Json_writer_object filesort(writer, "filesort"); - srt->print_json_members(writer, is_analyze, no_tmp_tbl); + srt->print_json_members(writer, is_analyze); } } void Explain_basic_join::print_explain_json(Explain_query *query, Json_writer *writer, - bool is_analyze, bool no_tmp_tbl) + bool is_analyze) { writer->add_member("query_block").start_object(); writer->add_member("select_id").add_ll(select_id); - print_explain_json_interns(query, writer, is_analyze, no_tmp_tbl); + print_explain_json_interns(query, writer, is_analyze); writer->end_object(); } @@ -1179,7 +1157,7 @@ void Explain_basic_join::print_explain_json(Explain_query *query, void Explain_basic_join:: print_explain_json_interns(Explain_query *query, Json_writer *writer, - bool is_analyze, bool no_tmp_tbl) + bool is_analyze) { { Json_writer_array loop(writer, "nested_loop"); @@ -1192,7 +1170,7 @@ print_explain_json_interns(Explain_query *query, writer->start_array(); } - join_tabs[i]->print_explain_json(query, writer, is_analyze, no_tmp_tbl); + join_tabs[i]->print_explain_json(query, writer, is_analyze); if (join_tabs[i]->end_dups_weedout) { @@ -1201,7 +1179,7 @@ print_explain_json_interns(Explain_query *query, } } } // "nested_loop" - print_explain_json_for_children(query, writer, is_analyze, no_tmp_tbl); + print_explain_json_for_children(query, writer, is_analyze); } @@ -1601,7 +1579,7 @@ const char *String_list::append_str(MEM_ROOT *mem_root, const char *str) } -static void write_item(Json_writer *writer, Item *item, bool no_tmp_tbl) +static void write_item(Json_writer *writer, Item *item) { THD *thd= current_thd; char item_buf[256]; @@ -1611,27 +1589,25 @@ static void write_item(Json_writer *writer, Item *item, bool no_tmp_tbl) ulonglong save_option_bits= thd->variables.option_bits; thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE; - auto qtype= QT_EXPLAIN | (no_tmp_tbl? QT_DONT_ACCESS_TMP_TABLES : 0); - item->print(&str, (enum_query_type)qtype); + item->print(&str, QT_EXPLAIN); thd->variables.option_bits= save_option_bits; writer->add_str(str.c_ptr_safe()); } -static void append_item_to_str(String *out, Item *item, bool no_tmp_tbl) +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; - auto qtype= QT_EXPLAIN | (no_tmp_tbl? QT_DONT_ACCESS_TMP_TABLES : 0); - item->print(out, (enum_query_type)qtype); + 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, - bool no_tmp_tbl) + enum explain_extra_tag tag) { switch (tag) { @@ -1655,11 +1631,11 @@ void Explain_table_access::tag_to_json(Json_writer *writer, break; case ET_USING_INDEX_CONDITION: writer->add_member("index_condition"); - write_item(writer, pushed_index_cond, no_tmp_tbl); + write_item(writer, pushed_index_cond); break; case ET_USING_INDEX_CONDITION_BKA: writer->add_member("index_condition_bka"); - write_item(writer, pushed_index_cond, no_tmp_tbl); + write_item(writer, pushed_index_cond); break; case ET_USING_WHERE: { @@ -1673,7 +1649,7 @@ void Explain_table_access::tag_to_json(Json_writer *writer, if (item) { writer->add_member("attached_condition"); - write_item(writer, item, no_tmp_tbl); + write_item(writer, item); } } break; @@ -1807,7 +1783,7 @@ static void trace_engine_stats(handler *file, Json_writer *writer) void Explain_table_access::print_explain_json(Explain_query *query, Json_writer *writer, - bool is_analyze, bool no_tmp_tbl) + bool is_analyze) { Json_writer_object jsobj(writer); @@ -1838,7 +1814,7 @@ void Explain_table_access::print_explain_json(Explain_query *query, } } writer->add_member("filesort").start_object(); - pre_join_sort->print_json_members(writer, is_analyze, no_tmp_tbl); + pre_join_sort->print_json_members(writer, is_analyze); } if (bka_type.is_using_jbuf()) @@ -1976,7 +1952,7 @@ void Explain_table_access::print_explain_json(Explain_query *query, for (int i=0; i < (int)extra_tags.elements(); i++) { - tag_to_json(writer, extra_tags.at(i), no_tmp_tbl); + tag_to_json(writer, extra_tags.at(i)); } if (full_scan_on_null_key) @@ -1997,7 +1973,7 @@ void Explain_table_access::print_explain_json(Explain_query *query, if (where_cond) { writer->add_member("attached_condition"); - write_item(writer, where_cond, no_tmp_tbl); + write_item(writer, where_cond); } if (is_analyze) @@ -2044,7 +2020,7 @@ void Explain_table_access::print_explain_json(Explain_query *query, { writer->add_member("lateral").add_ll(1); } - node->print_explain_json(query, writer, is_analyze, no_tmp_tbl); + node->print_explain_json(query, writer, is_analyze); writer->end_object(); } if (non_merged_sjm_number) @@ -2054,7 +2030,7 @@ void Explain_table_access::print_explain_json(Explain_query *query, 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, no_tmp_tbl); + node->print_explain_json(query, writer, is_analyze); writer->end_object(); } if (sjm_nest) @@ -2062,7 +2038,7 @@ void Explain_table_access::print_explain_json(Explain_query *query, /* 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); - sjm_nest->print_explain_json(query, writer, is_analyze, no_tmp_tbl); + sjm_nest->print_explain_json(query, writer, is_analyze); writer->end_object(); } @@ -2368,8 +2344,7 @@ int Explain_delete::print_explain(Explain_query *query, void Explain_delete::print_explain_json(Explain_query *query, Json_writer *writer, - bool is_analyze, - bool no_tmp_tbl) + bool is_analyze) { Json_writer_nesting_guard guard(writer); @@ -2384,7 +2359,7 @@ void Explain_delete::print_explain_json(Explain_query *query, writer->end_object(); // query_block return; } - Explain_update::print_explain_json(query, writer, is_analyze, no_tmp_tbl); + Explain_update::print_explain_json(query, writer, is_analyze); } @@ -2487,8 +2462,7 @@ int Explain_update::print_explain(Explain_query *query, void Explain_update::print_explain_json(Explain_query *query, Json_writer *writer, - bool is_analyze, - bool no_tmp_tbl) + bool is_analyze) { Json_writer_nesting_guard guard(writer); @@ -2655,7 +2629,7 @@ void Explain_update::print_explain_json(Explain_query *query, if (where_cond) { writer->add_member("attached_condition"); - write_item(writer, where_cond, no_tmp_tbl); + write_item(writer, where_cond); } /*** The part of plan that is before the buffering/sorting ends here ***/ @@ -2667,7 +2641,7 @@ void Explain_update::print_explain_json(Explain_query *query, writer->end_object(); // table - print_explain_json_for_children(query, writer, is_analyze, no_tmp_tbl); + print_explain_json_for_children(query, writer, is_analyze); writer->end_object(); // query_block } @@ -2697,8 +2671,7 @@ int Explain_insert::print_explain(Explain_query *query, } void Explain_insert::print_explain_json(Explain_query *query, - Json_writer *writer, bool is_analyze, - bool no_tmp_tbl) + Json_writer *writer, bool is_analyze) { Json_writer_nesting_guard guard(writer); @@ -2707,7 +2680,7 @@ void Explain_insert::print_explain_json(Explain_query *query, writer->add_member("table").start_object(); writer->add_member("table_name").add_str(table_name.c_ptr()); writer->end_object(); // table - print_explain_json_for_children(query, writer, is_analyze, no_tmp_tbl); + print_explain_json_for_children(query, writer, is_analyze); writer->end_object(); // query_block } diff --git a/sql/sql_explain.h b/sql/sql_explain.h index 894309568ba..4510c1aa3ce 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -134,13 +134,12 @@ public: virtual int print_explain(Explain_query *query, select_result_sink *output, uint8 explain_flags, bool is_analyze)=0; virtual void print_explain_json(Explain_query *query, Json_writer *writer, - bool is_analyze, bool no_tmp_tbl)= 0; + bool is_analyze)= 0; int print_explain_for_children(Explain_query *query, select_result_sink *output, uint8 explain_flags, bool is_analyze); void print_explain_json_for_children(Explain_query *query, - Json_writer *writer, bool is_analyze, - bool no_tmp_tbl); + Json_writer *writer, bool is_analyze); bool print_explain_json_cache(Json_writer *writer, bool is_analyze); virtual ~Explain_node() = default; }; @@ -174,10 +173,10 @@ public: int print_explain(Explain_query *query, select_result_sink *output, uint8 explain_flags, bool is_analyze); void print_explain_json(Explain_query *query, Json_writer *writer, - bool is_analyze, bool no_tmp_tbl); + bool is_analyze); void print_explain_json_interns(Explain_query *query, Json_writer *writer, - bool is_analyze, bool no_tmp_tbl); + bool is_analyze); /* A flat array of Explain structs for tables. */ Explain_table_access** join_tabs; @@ -261,7 +260,7 @@ public: int print_explain(Explain_query *query, select_result_sink *output, uint8 explain_flags, bool is_analyze); void print_explain_json(Explain_query *query, Json_writer *writer, - bool is_analyze, bool no_tmp_tbl); + bool is_analyze); Table_access_tracker *get_using_temporary_read_tracker() { @@ -304,8 +303,7 @@ public: Explain_aggr_filesort(MEM_ROOT *mem_root, bool is_analyze, Filesort *filesort); - void print_json_members(Json_writer *writer, bool is_analyze, - bool no_tmp_tbl); + void print_json_members(Json_writer *writer, bool is_analyze); }; class Explain_aggr_tmp_table : public Explain_aggr_node @@ -326,8 +324,7 @@ class Explain_aggr_window_funcs : public Explain_aggr_node public: enum_explain_aggr_node_type get_type() { return AGGR_OP_WINDOW_FUNCS; } - void print_json_members(Json_writer *writer, bool is_analyze, - bool no_tmp_tbl); + void print_json_members(Json_writer *writer, bool is_analyze); friend class Window_funcs_computation; }; @@ -380,7 +377,7 @@ public: int print_explain(Explain_query *query, select_result_sink *output, uint8 explain_flags, bool is_analyze); void print_explain_json(Explain_query *query, Json_writer *writer, - bool is_analyze, bool no_tmp_tbl); + bool is_analyze); const char *fake_select_type; bool using_filesort; @@ -448,19 +445,19 @@ class Explain_insert; (1) - Query plan construction is finished and it is available for reading. - (2) - Temporary tables are freed. After this point, - we need to pass QT_DONT_ACCESS_TMP_TABLES to item->print(). Since - we don't track when #2 happens for each temp.table, we pass this - flag whenever we're printing the query plan for a SHOW command. - Also, we pass it when printing ANALYZE (?) + (2) - Temporary tables are freed (with exception of derived tables + which are freed at step (4)). + The tables are no longer accessible but one can still call + item->print(), even for items that refer to temp.tables (see + Item_field::print() for details) (3) - Notification about (4). - (4) - Tables used by the query are closed. One known consequence of this is - that the values of the const tables' fields are not available anymore. - We could use the same approach as in QT_DONT_ACCESS_TMP_TABLES to work - around that, but instead we disallow producing FORMAT=JSON output at - step #3. We also processing of SHOW command. The rationale is that - query is close to finish anyway. + (4) - Tables used by the query are closed. One consequence of this is that + the values of the const tables' fields are not available anymore. + We could adjust the code in Item_field::print() to handle this but + instead we make step (3) disallow production of FORMAT=JSON output. + We also disable processing of SHOW EXPLAIN|ANALYZE output because + the query is about to finish anyway. (5) - Item objects are freed. After this, it's certainly not possible to print them into FORMAT=JSON output. @@ -499,7 +496,6 @@ public: bool print_explain_str(THD *thd, String *out_str, bool is_analyze); int print_explain_json(select_result_sink *output, bool is_analyze, - bool is_show_cmd, ulonglong query_time_in_progress_ms= 0); /* If true, at least part of EXPLAIN can be printed */ @@ -908,15 +904,14 @@ public: uint select_id, const char *select_type, bool using_temporary, bool using_filesort); void print_explain_json(Explain_query *query, Json_writer *writer, - bool is_analyze, bool no_tmp_tbl); + bool is_analyze); private: void append_tag_name(String *str, enum explain_extra_tag tag); void fill_key_str(String *key_str, bool is_json) const; void fill_key_len_str(String *key_len_str, bool is_json) const; double get_r_filtered(); - void tag_to_json(Json_writer *writer, enum explain_extra_tag tag, - bool no_tmp_tbl); + void tag_to_json(Json_writer *writer, enum explain_extra_tag tag); }; @@ -1003,7 +998,7 @@ public: virtual int print_explain(Explain_query *query, select_result_sink *output, uint8 explain_flags, bool is_analyze); virtual void print_explain_json(Explain_query *query, Json_writer *writer, - bool is_analyze, bool no_tmp_tbl); + bool is_analyze); }; @@ -1029,7 +1024,7 @@ public: int print_explain(Explain_query *query, select_result_sink *output, uint8 explain_flags, bool is_analyze); void print_explain_json(Explain_query *query, Json_writer *writer, - bool is_analyze, bool no_tmp_tbl); + bool is_analyze); }; @@ -1056,7 +1051,7 @@ public: virtual int print_explain(Explain_query *query, select_result_sink *output, uint8 explain_flags, bool is_analyze); virtual void print_explain_json(Explain_query *query, Json_writer *writer, - bool is_analyze, bool no_tmp_tbl); + bool is_analyze); }; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 31f31850807..c367bc7fe15 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -5802,7 +5802,6 @@ int LEX::print_explain(select_result_sink *output, uint8 explain_flags, query_time_in_progress_ms= (now - start_time) / (HRTIME_RESOLUTION / 1000); res= explain->print_explain_json(output, is_analyze, - true /* is_show_cmd */, query_time_in_progress_ms); } else diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2cfbbc22af9..b0338250088 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6235,8 +6235,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) result->remove_offset_limit(); if (lex->explain_json) { - lex->explain->print_explain_json(result, lex->analyze_stmt, - false /* is_show_cmd */); + lex->explain->print_explain_json(result, lex->analyze_stmt); } else { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 2d4e28f095c..3a6a8859326 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -20014,7 +20014,7 @@ bool Create_tmp_table::add_fields(THD *thd, if (!(tmp_item= new (thd->mem_root) Item_field(thd, new_field))) goto err; - ((Item_field*) tmp_item)->set_refers_to_temp_table(true); + ((Item_field*) tmp_item)->set_refers_to_temp_table(); arg= sum_item->set_arg(i, thd, tmp_item); thd->mem_root= &table->mem_root; @@ -27290,7 +27290,7 @@ change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array, Item_field *new_field= new (thd->mem_root) Item_field(thd, field); if (!suv || !new_field) DBUG_RETURN(true); // Fatal error - new_field->set_refers_to_temp_table(true); + new_field->set_refers_to_temp_table(); List list; list.push_back(new_field, thd->mem_root); suv->set_arguments(thd, list); @@ -27309,7 +27309,7 @@ change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array, { item_field= (Item*) new (thd->mem_root) Item_field(thd, field); if (item_field) - ((Item_field*) item_field)->set_refers_to_temp_table(true); + ((Item_field*) item_field)->set_refers_to_temp_table(); } if (!item_field) DBUG_RETURN(true); // Fatal error diff --git a/sql/sql_window.cc b/sql/sql_window.cc index 4d6d5ddd951..9a46413d3e7 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -3122,7 +3122,7 @@ bool Window_funcs_sort::setup(THD *thd, SQL_SELECT *sel, Item_field *item= new (thd->mem_root) Item_field(thd, join_tab->table->field[0]); if (item) - item->set_refers_to_temp_table(true); + item->set_refers_to_temp_table(); order->item= (Item **)alloc_root(thd->mem_root, 2 * sizeof(Item *)); order->item[1]= NULL; order->item[0]= item;