1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-07 00:04:31 +03:00

MDEV-28201: Server crashes upon SHOW ANALYZE/EXPLAIN FORMAT=JSON

- Describe the lifetime of EXPLAIN data structures in
  sql_explain.h:ExplainDataStructureLifetime.

- Make Item_field::set_field() call set_refers_to_temp_table()
  when it refers to a temp. table.
- Introduce QT_DONT_ACCESS_TMP_TABLES flag for Item::print.
  It directs Item_field::print to not try access its the
  temp table.
- Introduce Explain_query::notify_tables_are_closed()
  and call it right before the query closes its tables.
- Make Explain data stuctures' print_explain_json() methods
  accept "no_tmp_tbl" parameter which means pass
  QT_DONT_ACCESS_TMP_TABLES when printing items.
- Make Show_explain_request::call_in_target_thread() not call
  set_current_thd(). This wasn't needed as the code inside
  lex->print_explain() uses output->thd anyway. output->thd
  refers to the SHOW command's THD object.
This commit is contained in:
Sergei Petrunia
2022-04-04 12:32:22 +03:00
parent 02c3babdec
commit 3f68c2169e
22 changed files with 319 additions and 163 deletions

View File

@@ -39,12 +39,12 @@ 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);
static void append_item_to_str(String *out, Item *item);
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);
Explain_query::Explain_query(THD *thd_arg, MEM_ROOT *root) :
mem_root(root), upd_del_plan(nullptr), insert_plan(nullptr),
unions(root), selects(root), thd(thd_arg), apc_enabled(false),
unions(root), selects(root), stmt_thd(thd_arg), apc_enabled(false),
operations(0)
{
}
@@ -65,7 +65,7 @@ static void print_json_array(Json_writer *writer,
Explain_query::~Explain_query()
{
if (apc_enabled)
thd->apc_target.disable();
stmt_thd->apc_target.disable();
delete upd_del_plan;
delete insert_plan;
@@ -155,20 +155,32 @@ void Explain_query::add_upd_del_plan(Explain_update *upd_del_plan_arg)
void Explain_query::query_plan_ready()
{
if (!apc_enabled)
thd->apc_target.enable();
stmt_thd->apc_target.enable();
apc_enabled= true;
#ifndef DBUG_OFF
can_print_json= true;
#endif
}
void Explain_query::notify_item_objects_about_to_be_freed()
void Explain_query::notify_tables_are_closed()
{
/*
Disable processing of SHOW EXPLAIN|ANALYZE. The query is about to close
the tables it is using, which will make it impossible to print Item*
values. See sql_explain.h:ExplainDataStructureLifetime for details.
*/
if (apc_enabled)
{
thd->apc_target.disable();
stmt_thd->apc_target.disable();
apc_enabled= false;
#ifndef DBUG_OFF
can_print_json= false;
#endif
}
}
/*
Send EXPLAIN output to the client.
*/
@@ -184,7 +196,7 @@ int Explain_query::send_explain(THD *thd)
int res= 0;
if (thd->lex->explain_json)
print_explain_json(result, thd->lex->analyze_stmt);
print_explain_json(result, thd->lex->analyze_stmt, false /*is_show_cmd*/);
else
res= print_explain(result, lex->describe, thd->lex->analyze_stmt);
@@ -225,27 +237,48 @@ 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;
#ifndef DBUG_OFF
DBUG_ASSERT(can_print_json);
#endif
writer.start_object();
if (is_analyze && query_time_in_progress_ms > 0)
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);
upd_del_plan->print_explain_json(this, &writer, is_analyze, is_show_cmd);
else if (insert_plan)
insert_plan->print_explain_json(this, &writer, is_analyze);
insert_plan->print_explain_json(this, &writer, is_analyze, is_show_cmd);
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);
node->print_explain_json(this, &writer, is_analyze, is_show_cmd);
}
writer.end_object();
@@ -253,6 +286,7 @@ int Explain_query::print_explain_json(select_result_sink *output,
CHARSET_INFO *cs= system_charset_info;
List<Item> item_list;
const String *buf= writer.output.get_string();
THD *thd= output->thd;
item_list.push_back(new (thd->mem_root)
Item_string(thd, buf->ptr(), buf->length(), cs),
thd->mem_root);
@@ -607,7 +641,8 @@ int Explain_union::print_explain(Explain_query *query,
void Explain_union::print_explain_json(Explain_query *query,
Json_writer *writer, bool is_analyze)
Json_writer *writer, bool is_analyze,
bool no_tmp_tbl)
{
Json_writer_nesting_guard guard(writer);
char table_name_buffer[SAFE_NAME_LEN];
@@ -652,12 +687,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);
sel->print_explain_json(query, writer, is_analyze, no_tmp_tbl);
writer->end_object();
}
writer->end_array();
print_explain_json_for_children(query, writer, is_analyze);
print_explain_json_for_children(query, writer, is_analyze, no_tmp_tbl);
writer->end_object(); // union_result
writer->end_object(); // query_block
@@ -715,7 +750,8 @@ 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 is_analyze,
bool no_tmp_tbl)
{
Json_writer_nesting_guard guard(writer);
@@ -735,7 +771,7 @@ void Explain_node::print_explain_json_for_children(Explain_query *query,
}
writer->start_object();
node->print_explain_json(query, writer, is_analyze);
node->print_explain_json(query, writer, is_analyze, no_tmp_tbl);
writer->end_object();
}
@@ -915,7 +951,8 @@ void Explain_select::add_linkage(Json_writer *writer)
}
void Explain_select::print_explain_json(Explain_query *query,
Json_writer *writer, bool is_analyze)
Json_writer *writer, bool is_analyze,
bool no_tmp_tbl)
{
Json_writer_nesting_guard guard(writer);
@@ -937,7 +974,7 @@ void Explain_select::print_explain_json(Explain_query *query,
message);
writer->end_object();
print_explain_json_for_children(query, writer, is_analyze);
print_explain_json_for_children(query, writer, is_analyze, no_tmp_tbl);
writer->end_object();
}
else
@@ -959,17 +996,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);
write_item(writer, exec_const_cond, no_tmp_tbl);
}
if (outer_ref_cond)
{
writer->add_member("outer_ref_condition");
write_item(writer, outer_ref_cond);
write_item(writer, outer_ref_cond, no_tmp_tbl);
}
if (pseudo_bits_cond)
{
writer->add_member("pseudo_bits_condition");
write_item(writer, pseudo_bits_cond);
write_item(writer, pseudo_bits_cond, no_tmp_tbl);
}
/* we do not print HAVING which always evaluates to TRUE */
@@ -977,7 +1014,7 @@ void Explain_select::print_explain_json(Explain_query *query,
{
writer->add_member("having_condition");
if (likely(having))
write_item(writer, having);
write_item(writer, having, no_tmp_tbl);
else
{
/* Normally we should not go this branch, left just for safety */
@@ -1000,7 +1037,8 @@ void Explain_select::print_explain_json(Explain_query *query,
case AGGR_OP_FILESORT:
{
writer->add_member("filesort").start_object();
((Explain_aggr_filesort*)node)->print_json_members(writer, is_analyze);
auto aggr_node= (Explain_aggr_filesort*)node;
aggr_node->print_json_members(writer, is_analyze, no_tmp_tbl);
break;
}
case AGGR_OP_REMOVE_DUPLICATES:
@@ -1010,7 +1048,8 @@ void Explain_select::print_explain_json(Explain_query *query,
{
//TODO: make print_json_members virtual?
writer->add_member("window_functions_computation").start_object();
((Explain_aggr_window_funcs*)node)->print_json_members(writer, is_analyze);
auto aggr_node= (Explain_aggr_window_funcs*)node;
aggr_node->print_json_members(writer, is_analyze, no_tmp_tbl);
break;
}
default:
@@ -1019,7 +1058,8 @@ void Explain_select::print_explain_json(Explain_query *query,
started_objects++;
}
Explain_basic_join::print_explain_json_interns(query, writer, is_analyze);
Explain_basic_join::print_explain_json_interns(query, writer, is_analyze,
no_tmp_tbl);
for (;started_objects; started_objects--)
writer->end_object();
@@ -1048,7 +1088,8 @@ Explain_aggr_filesort::Explain_aggr_filesort(MEM_ROOT *mem_root,
void Explain_aggr_filesort::print_json_members(Json_writer *writer,
bool is_analyze)
bool is_analyze,
bool no_tmp_tbl)
{
char item_buf[256];
String str(item_buf, sizeof(item_buf), &my_charset_bin);
@@ -1068,7 +1109,7 @@ void Explain_aggr_filesort::print_json_members(Json_writer *writer,
{
str.append(STRING_WITH_LEN(", "));
}
append_item_to_str(&str, item);
append_item_to_str(&str, item, no_tmp_tbl);
if (*direction == ORDER::ORDER_DESC)
str.append(STRING_WITH_LEN(" desc"));
}
@@ -1081,7 +1122,8 @@ 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 is_analyze,
bool no_tmp_tbl)
{
Explain_aggr_filesort *srt;
List_iterator<Explain_aggr_filesort> it(sorts);
@@ -1090,19 +1132,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);
srt->print_json_members(writer, is_analyze, no_tmp_tbl);
}
}
void Explain_basic_join::print_explain_json(Explain_query *query,
Json_writer *writer,
bool is_analyze)
bool is_analyze, bool no_tmp_tbl)
{
writer->add_member("query_block").start_object();
writer->add_member("select_id").add_ll(select_id);
print_explain_json_interns(query, writer, is_analyze);
print_explain_json_interns(query, writer, is_analyze, no_tmp_tbl);
writer->end_object();
}
@@ -1111,7 +1153,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 is_analyze, bool no_tmp_tbl)
{
{
Json_writer_array loop(writer, "nested_loop");
@@ -1124,7 +1166,7 @@ print_explain_json_interns(Explain_query *query,
writer->start_array();
}
join_tabs[i]->print_explain_json(query, writer, is_analyze);
join_tabs[i]->print_explain_json(query, writer, is_analyze, no_tmp_tbl);
if (join_tabs[i]->end_dups_weedout)
{
@@ -1133,7 +1175,7 @@ print_explain_json_interns(Explain_query *query,
}
}
} // "nested_loop"
print_explain_json_for_children(query, writer, is_analyze);
print_explain_json_for_children(query, writer, is_analyze, no_tmp_tbl);
}
@@ -1292,7 +1334,7 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
uint select_id, const char *select_type,
bool using_temporary, bool using_filesort)
{
THD *thd= output->thd;
THD *thd= output->thd; // note: for SHOW EXPLAIN, this is target thd.
MEM_ROOT *mem_root= thd->mem_root;
List<Item> item_list;
@@ -1533,7 +1575,7 @@ const char *String_list::append_str(MEM_ROOT *mem_root, const char *str)
}
static void write_item(Json_writer *writer, Item *item)
static void write_item(Json_writer *writer, Item *item, bool no_tmp_tbl)
{
THD *thd= current_thd;
char item_buf[256];
@@ -1543,23 +1585,27 @@ static void write_item(Json_writer *writer, Item *item)
ulonglong save_option_bits= thd->variables.option_bits;
thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE;
item->print(&str, QT_EXPLAIN);
auto qtype= QT_EXPLAIN | (no_tmp_tbl? QT_DONT_ACCESS_TMP_TABLES : 0);
item->print(&str, (enum_query_type)qtype);
thd->variables.option_bits= save_option_bits;
writer->add_str(str.c_ptr_safe());
}
static void append_item_to_str(String *out, Item *item)
static void append_item_to_str(String *out, Item *item, bool no_tmp_tbl)
{
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);
auto qtype= QT_EXPLAIN | (no_tmp_tbl? QT_DONT_ACCESS_TMP_TABLES : 0);
item->print(out, (enum_query_type)qtype);
thd->variables.option_bits= save_option_bits;
}
void Explain_table_access::tag_to_json(Json_writer *writer, enum explain_extra_tag tag)
void Explain_table_access::tag_to_json(Json_writer *writer,
enum explain_extra_tag tag,
bool no_tmp_tbl)
{
switch (tag)
{
@@ -1583,11 +1629,11 @@ void Explain_table_access::tag_to_json(Json_writer *writer, enum explain_extra_t
break;
case ET_USING_INDEX_CONDITION:
writer->add_member("index_condition");
write_item(writer, pushed_index_cond);
write_item(writer, pushed_index_cond, no_tmp_tbl);
break;
case ET_USING_INDEX_CONDITION_BKA:
writer->add_member("index_condition_bka");
write_item(writer, pushed_index_cond);
write_item(writer, pushed_index_cond, no_tmp_tbl);
break;
case ET_USING_WHERE:
{
@@ -1601,7 +1647,7 @@ void Explain_table_access::tag_to_json(Json_writer *writer, enum explain_extra_t
if (item)
{
writer->add_member("attached_condition");
write_item(writer, item);
write_item(writer, item, no_tmp_tbl);
}
}
break;
@@ -1714,7 +1760,7 @@ void Explain_rowid_filter::print_explain_json(Explain_query *query,
void Explain_table_access::print_explain_json(Explain_query *query,
Json_writer *writer,
bool is_analyze)
bool is_analyze, bool no_tmp_tbl)
{
Json_writer_object jsobj(writer);
@@ -1745,7 +1791,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);
pre_join_sort->print_json_members(writer, is_analyze, no_tmp_tbl);
}
if (bka_type.is_using_jbuf())
@@ -1882,7 +1928,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));
tag_to_json(writer, extra_tags.at(i), no_tmp_tbl);
}
if (full_scan_on_null_key)
@@ -1903,7 +1949,7 @@ void Explain_table_access::print_explain_json(Explain_query *query,
if (where_cond)
{
writer->add_member("attached_condition");
write_item(writer, where_cond);
write_item(writer, where_cond, no_tmp_tbl);
}
if (is_analyze)
@@ -1927,7 +1973,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);
node->print_explain_json(query, writer, is_analyze, no_tmp_tbl);
writer->end_object();
}
if (non_merged_sjm_number)
@@ -1937,7 +1983,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);
node->print_explain_json(query, writer, is_analyze, no_tmp_tbl);
writer->end_object();
}
if (sjm_nest)
@@ -1945,7 +1991,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);
sjm_nest->print_explain_json(query, writer, is_analyze, no_tmp_tbl);
writer->end_object();
}
@@ -2251,7 +2297,8 @@ int Explain_delete::print_explain(Explain_query *query,
void Explain_delete::print_explain_json(Explain_query *query,
Json_writer *writer,
bool is_analyze)
bool is_analyze,
bool no_tmp_tbl)
{
Json_writer_nesting_guard guard(writer);
@@ -2266,7 +2313,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);
Explain_update::print_explain_json(query, writer, is_analyze, no_tmp_tbl);
}
@@ -2369,7 +2416,8 @@ int Explain_update::print_explain(Explain_query *query,
void Explain_update::print_explain_json(Explain_query *query,
Json_writer *writer,
bool is_analyze)
bool is_analyze,
bool no_tmp_tbl)
{
Json_writer_nesting_guard guard(writer);
@@ -2534,7 +2582,7 @@ void Explain_update::print_explain_json(Explain_query *query,
if (where_cond)
{
writer->add_member("attached_condition");
write_item(writer, where_cond);
write_item(writer, where_cond, no_tmp_tbl);
}
/*** The part of plan that is before the buffering/sorting ends here ***/
@@ -2546,7 +2594,7 @@ void Explain_update::print_explain_json(Explain_query *query,
writer->end_object(); // table
print_explain_json_for_children(query, writer, is_analyze);
print_explain_json_for_children(query, writer, is_analyze, no_tmp_tbl);
writer->end_object(); // query_block
}
@@ -2576,7 +2624,8 @@ int Explain_insert::print_explain(Explain_query *query,
}
void Explain_insert::print_explain_json(Explain_query *query,
Json_writer *writer, bool is_analyze)
Json_writer *writer, bool is_analyze,
bool no_tmp_tbl)
{
Json_writer_nesting_guard guard(writer);
@@ -2585,7 +2634,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);
print_explain_json_for_children(query, writer, is_analyze, no_tmp_tbl);
writer->end_object(); // query_block
}