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 <monty@mariadb.org>
This commit is contained in:
Sergei Petrunia 2023-08-14 11:09:51 +03:00
parent 9cd2989589
commit 8aaacb5509
15 changed files with 199 additions and 157 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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)
};

View File

@ -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)
{

View File

@ -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<Explain_aggr_filesort> 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
}

View File

@ -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);
};

View File

@ -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

View File

@ -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
{

View File

@ -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<Item> 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

View File

@ -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;