summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergei Petrunia <sergey@mariadb.com>2022-04-04 12:32:22 +0300
committerSergei Petrunia <sergey@mariadb.com>2022-04-29 10:48:26 +0300
commit3f68c2169e16814e8e268d0f9a9f29aee522453e (patch)
treec510410cc65f3943c4ed69fd7e93ee9804cc9cb5
parent02c3babdec0c931c6983863bff129114f750bd22 (diff)
downloadmariadb-git-3f68c2169e16814e8e268d0f9a9f29aee522453e.tar.gz
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.
-rw-r--r--mysql-test/main/show_analyze.result44
-rw-r--r--mysql-test/main/show_analyze.test16
-rw-r--r--mysql-test/main/show_analyze_json.result2
-rw-r--r--mysql-test/main/show_analyze_json.test2
-rw-r--r--mysql-test/main/show_explain.result2
-rw-r--r--mysql-test/main/show_explain.test2
-rw-r--r--mysql-test/main/show_explain_json.result2
-rw-r--r--mysql-test/main/show_explain_json.test2
-rw-r--r--mysql-test/suite/perfschema/r/nesting.result74
-rw-r--r--sql/item.cc20
-rw-r--r--sql/my_apc.cc1
-rw-r--r--sql/mysqld.h4
-rw-r--r--sql/sql_base.cc12
-rw-r--r--sql/sql_base.h1
-rw-r--r--sql/sql_class.cc3
-rw-r--r--sql/sql_explain.cc163
-rw-r--r--sql/sql_explain.h106
-rw-r--r--sql/sql_lex.cc3
-rw-r--r--sql/sql_parse.cc10
-rw-r--r--sql/sql_prepare.cc2
-rw-r--r--sql/sql_select.cc1
-rw-r--r--sql/sql_show.cc10
22 files changed, 319 insertions, 163 deletions
diff --git a/mysql-test/main/show_analyze.result b/mysql-test/main/show_analyze.result
index 8f50bb4d970..56dac487117 100644
--- a/mysql-test/main/show_analyze.result
+++ b/mysql-test/main/show_analyze.result
@@ -379,7 +379,7 @@ drop table t0,t1;
#
connection con1;
set @show_explain_probe_query= 'SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency';
-set debug_dbug='+d,log_slow_statement_end';
+set debug_dbug='+d,explain_notify_tables_are_closed';
SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency;
connection default;
SHOW ANALYZE FOR $thr2;
@@ -391,3 +391,45 @@ count(*) - count(*)
connection default;
disconnect con1;
set debug_sync='RESET';
+#
+# MDEV-28201: Server crashes upon SHOW ANALYZE/EXPLAIN FORMAT=JSON
+#
+CREATE TABLE t1 ( a varchar(1));
+INSERT INTO t1 VALUES ('a'),('b');
+ANALYZE format=json
+SELECT 1 FROM t1 GROUP BY convert_tz('1969-12-31 22:00:00',a,'+10:00');
+ANALYZE
+{
+ "query_block": {
+ "select_id": 1,
+ "r_loops": 1,
+ "r_total_time_ms": "REPLACED",
+ "filesort": {
+ "sort_key": "convert_tz('1969-12-31 22:00:00',t1.a,'+10:00')",
+ "r_loops": 1,
+ "r_total_time_ms": "REPLACED",
+ "r_used_priority_queue": false,
+ "r_output_rows": 1,
+ "r_buffer_size": "REPLACED",
+ "r_sort_mode": "sort_key,rowid",
+ "temporary_table": {
+ "nested_loop": [
+ {
+ "table": {
+ "table_name": "t1",
+ "access_type": "ALL",
+ "r_loops": 1,
+ "rows": 2,
+ "r_rows": 2,
+ "r_table_time_ms": "REPLACED",
+ "r_other_time_ms": "REPLACED",
+ "filtered": 100,
+ "r_filtered": 100
+ }
+ }
+ ]
+ }
+ }
+ }
+}
+DROP TABLE t1;
diff --git a/mysql-test/main/show_analyze.test b/mysql-test/main/show_analyze.test
index e4e16bb6159..bd0d3388f74 100644
--- a/mysql-test/main/show_analyze.test
+++ b/mysql-test/main/show_analyze.test
@@ -335,7 +335,7 @@ drop table t0,t1;
let $wait_condition= select State='show_explain_trap' from information_schema.processlist where id=$thr2;
connection con1;
set @show_explain_probe_query= 'SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency';
-set debug_dbug='+d,log_slow_statement_end';
+set debug_dbug='+d,explain_notify_tables_are_closed';
# Statement guarantees to produce 0 on every run
send SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency;
@@ -350,4 +350,16 @@ reap;
--echo # End
connection default;
disconnect con1;
-set debug_sync='RESET'; \ No newline at end of file
+set debug_sync='RESET';
+
+
+--echo #
+--echo # MDEV-28201: Server crashes upon SHOW ANALYZE/EXPLAIN FORMAT=JSON
+--echo #
+CREATE TABLE t1 ( a varchar(1));
+INSERT INTO t1 VALUES ('a'),('b');
+--source include/analyze-format.inc
+ANALYZE format=json
+SELECT 1 FROM t1 GROUP BY convert_tz('1969-12-31 22:00:00',a,'+10:00');
+DROP TABLE t1;
+
diff --git a/mysql-test/main/show_analyze_json.result b/mysql-test/main/show_analyze_json.result
index 5b34d8f0952..dc8ae2aa66b 100644
--- a/mysql-test/main/show_analyze_json.result
+++ b/mysql-test/main/show_analyze_json.result
@@ -1228,7 +1228,7 @@ drop table t0,t1;
#
connection con1;
set @show_explain_probe_query= 'SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency';
-set debug_dbug='+d,log_slow_statement_end';
+set debug_dbug='+d,explain_notify_tables_are_closed';
SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency;
connection default;
SHOW ANALYZE FORMAT=JSON FOR $thr2;
diff --git a/mysql-test/main/show_analyze_json.test b/mysql-test/main/show_analyze_json.test
index 18eea8c7a9a..1644c32f261 100644
--- a/mysql-test/main/show_analyze_json.test
+++ b/mysql-test/main/show_analyze_json.test
@@ -370,7 +370,7 @@ drop table t0,t1;
let $wait_condition= select State='show_explain_trap' from information_schema.processlist where id=$thr2;
connection con1;
set @show_explain_probe_query= 'SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency';
-set debug_dbug='+d,log_slow_statement_end';
+set debug_dbug='+d,explain_notify_tables_are_closed';
# Statement guarantees to produce 0 on every run
send SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency;
diff --git a/mysql-test/main/show_explain.result b/mysql-test/main/show_explain.result
index 35a528aff4e..e747126f8b7 100644
--- a/mysql-test/main/show_explain.result
+++ b/mysql-test/main/show_explain.result
@@ -1427,7 +1427,7 @@ drop table t0,t1,t2;
#
connection con1;
set @show_explain_probe_query= 'SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency';
-set debug_dbug='+d,log_slow_statement_end';
+set debug_dbug='+d,explain_notify_tables_are_closed';
SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency;
connection default;
SHOW EXPLAIN FOR $thr2;
diff --git a/mysql-test/main/show_explain.test b/mysql-test/main/show_explain.test
index 47073628ca7..b71dcc0bcfd 100644
--- a/mysql-test/main/show_explain.test
+++ b/mysql-test/main/show_explain.test
@@ -1312,7 +1312,7 @@ drop table t0,t1,t2;
let $wait_condition= select State='show_explain_trap' from information_schema.processlist where id=$thr2;
connection con1;
set @show_explain_probe_query= 'SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency';
-set debug_dbug='+d,log_slow_statement_end';
+set debug_dbug='+d,explain_notify_tables_are_closed';
# Statement guarantees to produce 0 on every run
send SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency;
diff --git a/mysql-test/main/show_explain_json.result b/mysql-test/main/show_explain_json.result
index 4713d8194f0..a5c441af5b8 100644
--- a/mysql-test/main/show_explain_json.result
+++ b/mysql-test/main/show_explain_json.result
@@ -1294,7 +1294,7 @@ Note 1051 Unknown table 'test.t2'
#
connection con1;
set @show_explain_probe_query= 'SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency';
-set debug_dbug='+d,log_slow_statement_end';
+set debug_dbug='+d,explain_notify_tables_are_closed';
SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency;
connection default;
SHOW EXPLAIN FORMAT=JSON FOR $thr2;
diff --git a/mysql-test/main/show_explain_json.test b/mysql-test/main/show_explain_json.test
index 3799ff7ec04..6dbcacd96f6 100644
--- a/mysql-test/main/show_explain_json.test
+++ b/mysql-test/main/show_explain_json.test
@@ -478,7 +478,7 @@ drop table if exists t0,t1,t2;
let $wait_condition= select State='show_explain_trap' from information_schema.processlist where id=$thr2;
connection con1;
set @show_explain_probe_query= 'SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency';
-set debug_dbug='+d,log_slow_statement_end';
+set debug_dbug='+d,explain_notify_tables_are_closed';
# Statement guarantees to produce 0 on every run
send SELECT count(*) - count(*) FROM sys.waits_by_user_by_latency;
diff --git a/mysql-test/suite/perfschema/r/nesting.result b/mysql-test/suite/perfschema/r/nesting.result
index 5fe515e2662..9e18e5ac272 100644
--- a/mysql-test/suite/perfschema/r/nesting.result
+++ b/mysql-test/suite/perfschema/r/nesting.result
@@ -128,11 +128,11 @@ relative_event_id relative_end_event_id event_name comment nesting_event_type re
11 11 stage/sql/Executing (stage) STATEMENT 0
12 12 stage/sql/End of update loop (stage) STATEMENT 0
13 13 stage/sql/Query end (stage) STATEMENT 0
-14 14 stage/sql/Commit (stage) STATEMENT 0
-15 15 stage/sql/closing tables (stage) STATEMENT 0
-16 16 stage/sql/Starting cleanup (stage) STATEMENT 0
-17 18 stage/sql/Freeing items (stage) STATEMENT 0
-18 18 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 17
+14 15 stage/sql/Commit (stage) STATEMENT 0
+15 15 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 14
+16 16 stage/sql/closing tables (stage) STATEMENT 0
+17 17 stage/sql/Starting cleanup (stage) STATEMENT 0
+18 18 stage/sql/Freeing items (stage) STATEMENT 0
19 19 wait/io/socket/sql/client_connection send STATEMENT 0
20 21 stage/sql/Reset for next command (stage) STATEMENT 0
21 21 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 20
@@ -152,11 +152,11 @@ relative_event_id relative_end_event_id event_name comment nesting_event_type re
35 35 stage/sql/Executing (stage) STATEMENT 24
36 36 stage/sql/End of update loop (stage) STATEMENT 24
37 37 stage/sql/Query end (stage) STATEMENT 24
-38 38 stage/sql/Commit (stage) STATEMENT 24
-39 39 stage/sql/closing tables (stage) STATEMENT 24
-40 40 stage/sql/Starting cleanup (stage) STATEMENT 24
-41 42 stage/sql/Freeing items (stage) STATEMENT 24
-42 42 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 41
+38 39 stage/sql/Commit (stage) STATEMENT 24
+39 39 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 38
+40 40 stage/sql/closing tables (stage) STATEMENT 24
+41 41 stage/sql/Starting cleanup (stage) STATEMENT 24
+42 42 stage/sql/Freeing items (stage) STATEMENT 24
43 43 wait/io/socket/sql/client_connection send STATEMENT 24
44 45 stage/sql/Reset for next command (stage) STATEMENT 24
45 45 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 44
@@ -176,11 +176,11 @@ relative_event_id relative_end_event_id event_name comment nesting_event_type re
59 59 stage/sql/Executing (stage) STATEMENT 48
60 60 stage/sql/End of update loop (stage) STATEMENT 48
61 61 stage/sql/Query end (stage) STATEMENT 48
-62 62 stage/sql/Commit (stage) STATEMENT 48
-63 63 stage/sql/closing tables (stage) STATEMENT 48
-64 64 stage/sql/Starting cleanup (stage) STATEMENT 48
-65 66 stage/sql/Freeing items (stage) STATEMENT 48
-66 66 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 65
+62 63 stage/sql/Commit (stage) STATEMENT 48
+63 63 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 62
+64 64 stage/sql/closing tables (stage) STATEMENT 48
+65 65 stage/sql/Starting cleanup (stage) STATEMENT 48
+66 66 stage/sql/Freeing items (stage) STATEMENT 48
67 67 wait/io/socket/sql/client_connection send STATEMENT 48
68 69 stage/sql/Reset for next command (stage) STATEMENT 48
69 69 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 68
@@ -203,12 +203,12 @@ select "With a third part to make things complete" as payload NULL NULL
84 84 stage/sql/Executing (stage) STATEMENT 72
85 85 stage/sql/End of update loop (stage) STATEMENT 72
86 86 stage/sql/Query end (stage) STATEMENT 72
-87 87 stage/sql/Commit (stage) STATEMENT 72
-88 88 stage/sql/closing tables (stage) STATEMENT 72
-89 89 stage/sql/Starting cleanup (stage) STATEMENT 72
-90 92 stage/sql/Freeing items (stage) STATEMENT 72
-91 91 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 90
-92 92 wait/io/socket/sql/client_connection send STAGE 90
+87 88 stage/sql/Commit (stage) STATEMENT 72
+88 88 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 87
+89 89 stage/sql/closing tables (stage) STATEMENT 72
+90 90 stage/sql/Starting cleanup (stage) STATEMENT 72
+91 92 stage/sql/Freeing items (stage) STATEMENT 72
+92 92 wait/io/socket/sql/client_connection send STAGE 91
93 110 statement/sql/select select "And this is the second part of a multi query" as payload;
select "With a third part to make things complete" as payload NULL NULL
94 96 stage/sql/starting (stage) STATEMENT 93
@@ -222,12 +222,12 @@ select "With a third part to make things complete" as payload NULL NULL
102 102 stage/sql/Executing (stage) STATEMENT 93
103 103 stage/sql/End of update loop (stage) STATEMENT 93
104 104 stage/sql/Query end (stage) STATEMENT 93
-105 105 stage/sql/Commit (stage) STATEMENT 93
-106 106 stage/sql/closing tables (stage) STATEMENT 93
-107 107 stage/sql/Starting cleanup (stage) STATEMENT 93
-108 110 stage/sql/Freeing items (stage) STATEMENT 93
-109 109 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 108
-110 110 wait/io/socket/sql/client_connection send STAGE 108
+105 106 stage/sql/Commit (stage) STATEMENT 93
+106 106 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 105
+107 107 stage/sql/closing tables (stage) STATEMENT 93
+108 108 stage/sql/Starting cleanup (stage) STATEMENT 93
+109 110 stage/sql/Freeing items (stage) STATEMENT 93
+110 110 wait/io/socket/sql/client_connection send STAGE 109
111 129 statement/sql/select select "With a third part to make things complete" as payload NULL NULL
112 113 stage/sql/starting (stage) STATEMENT 111
113 113 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 112
@@ -239,11 +239,11 @@ select "With a third part to make things complete" as payload NULL NULL
119 119 stage/sql/Executing (stage) STATEMENT 111
120 120 stage/sql/End of update loop (stage) STATEMENT 111
121 121 stage/sql/Query end (stage) STATEMENT 111
-122 122 stage/sql/Commit (stage) STATEMENT 111
-123 123 stage/sql/closing tables (stage) STATEMENT 111
-124 124 stage/sql/Starting cleanup (stage) STATEMENT 111
-125 126 stage/sql/Freeing items (stage) STATEMENT 111
-126 126 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 125
+122 123 stage/sql/Commit (stage) STATEMENT 111
+123 123 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 122
+124 124 stage/sql/closing tables (stage) STATEMENT 111
+125 125 stage/sql/Starting cleanup (stage) STATEMENT 111
+126 126 stage/sql/Freeing items (stage) STATEMENT 111
127 127 wait/io/socket/sql/client_connection send STATEMENT 111
128 129 stage/sql/Reset for next command (stage) STATEMENT 111
129 129 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 128
@@ -263,11 +263,11 @@ select "With a third part to make things complete" as payload NULL NULL
143 143 stage/sql/Executing (stage) STATEMENT 132
144 144 stage/sql/End of update loop (stage) STATEMENT 132
145 145 stage/sql/Query end (stage) STATEMENT 132
-146 146 stage/sql/Commit (stage) STATEMENT 132
-147 147 stage/sql/closing tables (stage) STATEMENT 132
-148 148 stage/sql/Starting cleanup (stage) STATEMENT 132
-149 150 stage/sql/Freeing items (stage) STATEMENT 132
-150 150 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 149
+146 147 stage/sql/Commit (stage) STATEMENT 132
+147 147 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 146
+148 148 stage/sql/closing tables (stage) STATEMENT 132
+149 149 stage/sql/Starting cleanup (stage) STATEMENT 132
+150 150 stage/sql/Freeing items (stage) STATEMENT 132
151 151 wait/io/socket/sql/client_connection send STATEMENT 132
152 153 stage/sql/Reset for next command (stage) STATEMENT 132
153 153 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 152
diff --git a/sql/item.cc b/sql/item.cc
index 339dc0f3d37..3b8345f4416 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -3141,10 +3141,11 @@ void Item_field::set_field(Field *field_par)
base_flags|= item_base_t::FIXED;
if (field->table->s->tmp_table == SYSTEM_TMP_TABLE)
- {
any_privileges= 0;
- refers_to_temp_table= true;
- }
+
+ if (field->table->s->tmp_table == SYSTEM_TMP_TABLE ||
+ field->table->s->tmp_table == INTERNAL_TMP_TABLE)
+ set_refers_to_temp_table(true);
}
@@ -7832,7 +7833,15 @@ Item_direct_view_ref::grouping_field_transformer_for_where(THD *thd,
void Item_field::print(String *str, enum_query_type query_type)
{
- if (!refers_to_temp_table && field && field->table->const_table &&
+ /*
+ 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.
+ */
+ 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)))
{
print_value(str);
@@ -7840,7 +7849,8 @@ void Item_field::print(String *str, enum_query_type query_type)
}
/*
Item_ident doesn't have references to the underlying Field/TABLE objects,
- so it's safe to use the following even for a temporary table:
+ so it's safe to make the following call even when the table is not
+ available already:
*/
Item_ident::print(str, query_type);
}
diff --git a/sql/my_apc.cc b/sql/my_apc.cc
index cef00c0a738..9777deb399a 100644
--- a/sql/my_apc.cc
+++ b/sql/my_apc.cc
@@ -187,6 +187,7 @@ bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
else
{
#ifndef DBUG_OFF
+ /* We didn't make the call, because the target is disabled */
n_calls_processed++;
#endif
mysql_mutex_unlock(LOCK_thd_kill_ptr);
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 9c09e993470..8d3d9f867da 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -903,6 +903,10 @@ enum enum_query_type
// it evaluates to. Should be used for error messages, so that they
// don't reveal values.
QT_NO_DATA_EXPANSION= (1 << 9),
+
+ // The temporary tables used by the query might be freed by the time
+ // this print() call is made.
+ QT_DONT_ACCESS_TMP_TABLES= (1 << 12)
};
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 1555e19b346..cb674c62ea2 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -759,6 +759,18 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
}
+int close_thread_tables_for_query(THD *thd)
+{
+ if (thd->lex && thd->lex->explain)
+ thd->lex->explain->notify_tables_are_closed();
+
+ DBUG_EXECUTE_IF("explain_notify_tables_are_closed",
+ if (dbug_user_var_equals_str(thd, "show_explain_probe_query",
+ thd->query()))
+ dbug_serve_apcs(thd, 1);
+ );
+ return close_thread_tables(thd);
+}
/*
Close all tables used by the current substatement, or all tables
used by this thread if we are on the upper level.
diff --git a/sql/sql_base.h b/sql/sql_base.h
index 5b449fdddac..c593d85b5e7 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -161,6 +161,7 @@ TABLE_LIST *find_table_in_list(TABLE_LIST *table,
const LEX_CSTRING *db_name,
const LEX_CSTRING *table_name);
int close_thread_tables(THD *thd);
+int close_thread_tables_for_query(THD *thd);
void switch_to_nullable_trigger_fields(List<Item> &items, TABLE *);
void switch_defaults_to_nullable_trigger_fields(TABLE *table);
bool fill_record_n_invoke_before_triggers(THD *thd, TABLE *table,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 07d5510648e..27875e3837b 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -2242,9 +2242,6 @@ void THD::cleanup_after_query()
thd_progress_end(this);
- if (lex && lex->explain)
- lex->explain->notify_item_objects_about_to_be_freed();
-
/*
Reset rand_used so that detection of calls to rand() will save random
seeds if needed by the slave.
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
index d8f46be870e..7c3918bfd20 100644
--- a/sql/sql_explain.cc
+++ b/sql/sql_explain.cc
@@ -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
}
diff --git a/sql/sql_explain.h b/sql/sql_explain.h
index f00d6bcf029..2847fe4a5e1 100644
--- a/sql/sql_explain.h
+++ b/sql/sql_explain.h
@@ -27,9 +27,8 @@ Query optimization produces two data structures:
produce output of SHOW EXPLAIN, EXPLAIN [FORMAT=JSON], or
ANALYZE [FORMAT=JSON], without accessing the execution data structures.
-(the only exception is that Explain data structures keep Item* pointers,
-and we require that one might call item->print(QT_EXPLAIN) when printing
-FORMAT=JSON output)
+The exception is that Explain data structures have Item* pointers. See
+ExplainDataStructureLifetime below for details.
=== ANALYZE data ===
EXPLAIN data structures have embedded ANALYZE data structures. These are
@@ -135,12 +134,13 @@ 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)= 0;
+ bool is_analyze, bool no_tmp_tbl)= 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);
+ Json_writer *writer, bool is_analyze,
+ bool no_tmp_tbl);
bool print_explain_json_cache(Json_writer *writer, bool is_analyze);
virtual ~Explain_node(){}
};
@@ -174,10 +174,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 is_analyze, bool no_tmp_tbl);
void print_explain_json_interns(Explain_query *query, Json_writer *writer,
- bool is_analyze);
+ bool is_analyze, bool no_tmp_tbl);
/* A flat array of Explain structs for tables. */
Explain_table_access** join_tabs;
@@ -261,7 +261,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 is_analyze, bool no_tmp_tbl);
Table_access_tracker *get_using_temporary_read_tracker()
{
@@ -304,7 +304,8 @@ public:
Explain_aggr_filesort(MEM_ROOT *mem_root, bool is_analyze,
Filesort *filesort);
- void print_json_members(Json_writer *writer, bool is_analyze);
+ void print_json_members(Json_writer *writer, bool is_analyze,
+ bool no_tmp_tbl);
};
class Explain_aggr_tmp_table : public Explain_aggr_node
@@ -325,7 +326,8 @@ 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);
+ void print_json_members(Json_writer *writer, bool is_analyze,
+ bool no_tmp_tbl);
friend class Window_funcs_computation;
};
@@ -378,7 +380,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 is_analyze, bool no_tmp_tbl);
const char *fake_select_type;
bool using_filesort;
@@ -417,36 +419,54 @@ class Explain_insert;
/*
Explain structure for a query (i.e. a statement).
- This should be able to survive when the query plan was deleted. Currently,
- we do not intend for it survive until after query's MEM_ROOT is freed. It
- does surivive freeing of query's items.
-
- For reference, the process of post-query cleanup is as follows:
+ This should be able to survive when the query plan was deleted. Currently,
+ we do not intend for it survive until after query's MEM_ROOT is freed.
+
+ == ExplainDataStructureLifetime ==
>dispatch_command
| >mysql_parse
- | | ...
- | | lex_end()
- | | ...
- | | >THD::cleanup_after_query
- | | | ...
- | | | free_items()
- | | | ...
- | | <THD::cleanup_after_query
+ | | ...
+ | |
+ | | explain->query_plan_ready(); // (1)
+ | |
+ | | some_join->cleanup(); // (2)
+ | |
+ | | explain->notify_tables_are_closed(); // (3)
+ | | close_thread_tables(); // (4)
+ | | ...
+ | | free_items(); // (5)
+ | | ...
| |
| <mysql_parse
|
- | log_slow_statement()
- |
+ | log_slow_statement() // (6)
+ |
| free_root()
- |
+ |
>dispatch_command
-
- That is, the order of actions is:
- - free query's Items
- - write to slow query log
- - free query's MEM_ROOT
-
+
+ (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 (?)
+
+ (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.
+
+ (5) - Item objects are freed. After this, it's certainly not possible to
+ print them into FORMAT=JSON output.
+
+ (6) - We may decide to log tabular EXPLAIN output to the slow query log.
+
*/
class Explain_query : public Sql_alloc
@@ -479,14 +499,14 @@ 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 */
bool have_query_plan() { return insert_plan || upd_del_plan|| get_node(1) != NULL; }
void query_plan_ready();
-
- void notify_item_objects_about_to_be_freed();
+ void notify_tables_are_closed();
MEM_ROOT *mem_root;
@@ -501,7 +521,7 @@ private:
Dynamic_array<Explain_union*> unions;
Dynamic_array<Explain_select*> selects;
- THD *thd; // for APC start/stop
+ THD *stmt_thd; // for APC start/stop
bool apc_enabled;
/*
Debugging aid: count how many times add_node() was called. Ideally, it
@@ -510,6 +530,9 @@ private:
is unacceptable.
*/
longlong operations;
+#ifndef DBUG_OFF
+ bool can_print_json= false;
+#endif
};
@@ -853,14 +876,15 @@ 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 is_analyze, bool no_tmp_tbl);
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);
+ void tag_to_json(Json_writer *writer, enum explain_extra_tag tag,
+ bool no_tmp_tbl);
};
@@ -943,7 +967,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 is_analyze, bool no_tmp_tbl);
};
@@ -969,7 +993,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 is_analyze, bool no_tmp_tbl);
};
@@ -996,7 +1020,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 is_analyze, bool no_tmp_tbl);
};
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 3da7b656c02..48d86981932 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -5815,7 +5815,7 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor)
}
/*
- This is used by SHOW EXPLAIN. It assuses query plan has been already
+ This is used by SHOW EXPLAIN|ANALYZE. It assumes query plan has been already
collected into QPF structures and we only need to print it out.
*/
@@ -5835,6 +5835,7 @@ 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 2c34fb0a586..308f2b5451d 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -2575,11 +2575,6 @@ void log_slow_statement(THD *thd)
end:
delete_explain_query(thd->lex);
- DBUG_EXECUTE_IF("log_slow_statement_end",
- if (dbug_user_var_equals_str(thd, "show_explain_probe_query",
- thd->query()))
- dbug_serve_apcs(thd, 1);
- );
DBUG_VOID_RETURN;
}
@@ -6074,7 +6069,7 @@ finish:
}
/* Free tables. Set stage 'closing tables' */
- close_thread_tables(thd);
+ close_thread_tables_for_query(thd);
#ifndef DBUG_OFF
@@ -6223,7 +6218,8 @@ 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);
+ lex->explain->print_explain_json(result, lex->analyze_stmt,
+ false /* is_show_cmd */);
}
else
{
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 83c064c63c6..b10a81d5e56 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -4444,7 +4444,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
/* No need to commit statement transaction, it's not started. */
DBUG_ASSERT(thd->transaction->stmt.is_empty());
- close_thread_tables(thd);
+ close_thread_tables_for_query(thd);
thd->mdl_context.rollback_to_savepoint(mdl_savepoint);
/*
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 0b4798e86c0..766cb28dfd1 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -26922,6 +26922,7 @@ int print_explain_message_line(select_result_sink *result,
ha_rows *rows,
const char *message)
{
+ /* Note: for SHOW EXPLAIN, this is caller thread's THD */
THD *thd= result->thd;
MEM_ROOT *mem_root= thd->mem_root;
Item *item_null= new (mem_root) Item_null(thd);
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 41e26c85077..2134bb04f6e 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2998,14 +2998,18 @@ void Show_explain_request::call_in_target_thread()
target_thd->query_charset());
DBUG_ASSERT(current_thd == target_thd);
- set_current_thd(request_thd);
+
+ /*
+ When producing JSON output, one should not change current_thd.
+ (If one does that, they will hit an assert when printing constant item
+ fields).
+ */
if (target_thd->lex->print_explain(explain_buf, 0 /* explain flags*/,
is_analyze, is_json_format,
&printed_anything))
{
failed_to_produce= TRUE;
}
- set_current_thd(target_thd);
if (!printed_anything)
failed_to_produce= TRUE;
@@ -3024,6 +3028,8 @@ int select_result_explain_buffer::send_data(List<Item> &items)
Switch to the receiveing thread, so that we correctly count memory used
by it. This is needed as it's the receiving thread that will free the
memory.
+ (TODO: Now that we don't change current_thd in
+ Show_explain_request::call_in_target_thread, is this necessary anymore?)
*/
set_current_thd(thd);
fill_record(thd, dst_table, dst_table->field, items, TRUE, FALSE);