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:
parent
9cd2989589
commit
8aaacb5509
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
32
sql/item.cc
32
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);
|
||||
|
31
sql/item.h
31
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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
};
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user