summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/my_json_writer.h8
-rw-r--r--sql/opt_subselect.cc13
-rw-r--r--sql/opt_trace.cc60
-rw-r--r--sql/opt_trace.h38
-rw-r--r--sql/opt_trace_context.h6
-rw-r--r--sql/sql_derived.cc30
-rw-r--r--sql/sql_parse.cc4
-rw-r--r--sql/sql_prepare.cc11
-rw-r--r--sql/sql_select.cc17
9 files changed, 145 insertions, 42 deletions
diff --git a/sql/my_json_writer.h b/sql/my_json_writer.h
index d82313f996f..20f479dc6d0 100644
--- a/sql/my_json_writer.h
+++ b/sql/my_json_writer.h
@@ -224,6 +224,14 @@ public:
size_t get_truncated_bytes() { return output.get_truncated_bytes(); }
+ /*
+ Note: this may not return exact value due to pretty-printer doing
+ buffering
+ */
+ size_t get_written_size() {
+ return output.length() + output.get_truncated_bytes();
+ }
+
Json_writer() :
indent_level(0), document_start(true), element_started(false),
first_child(true)
diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc
index d91557c5be2..953f2af0be8 100644
--- a/sql/opt_subselect.cc
+++ b/sql/opt_subselect.cc
@@ -549,6 +549,12 @@ bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs,
and, depending on the rewrite, either do it, or record it to be done at a
later phase.
+ NOTE
+ * This function called at Prepare phase. It should NOT do any rewrites.
+ It only collects information that's used for doing the rewrites at the
+ optimization phase.
+ * Optimizer trace is NOT yet enabled when this function is called.
+
RETURN
0 - OK
Other - Some sort of query error
@@ -703,8 +709,6 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
{
DBUG_PRINT("info", ("Subquery is semi-join conversion candidate"));
- (void)subquery_types_allow_materialization(thd, in_subs);
-
in_subs->is_flattenable_semijoin= TRUE;
/* Register the subquery for further processing in flatten_subqueries() */
@@ -717,10 +721,6 @@ int check_and_do_in_subquery_rewrites(JOIN *join)
if (arena)
thd->restore_active_arena(arena, &backup);
in_subs->is_registered_semijoin= TRUE;
- OPT_TRACE_TRANSFORM(thd, trace_wrapper, trace_transform,
- select_lex->select_number,
- "IN (SELECT)", "semijoin");
- trace_transform.add("chosen", true);
}
}
else
@@ -1262,6 +1262,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
while ((in_subq= li++))
{
bool remove_item= TRUE;
+ (void)subquery_types_allow_materialization(thd, in_subq);
/* Stop processing if we've reached a subquery that's attached to the ON clause */
if (in_subq->do_not_convert_to_sj)
diff --git a/sql/opt_trace.cc b/sql/opt_trace.cc
index ba9220cac44..2227519d991 100644
--- a/sql/opt_trace.cc
+++ b/sql/opt_trace.cc
@@ -106,6 +106,27 @@ inline bool sql_command_can_be_traced(enum enum_sql_command sql_command)
sql_command == SQLCOM_UPDATE_MULTI;
}
+
+void opt_trace_print_expanded_union(THD *thd, SELECT_LEX_UNIT *unit,
+ Json_writer_object *writer)
+{
+ DBUG_ASSERT(thd->trace_started());
+
+ StringBuffer<1024> str(system_charset_info);
+ ulonglong save_option_bits= thd->variables.option_bits;
+ thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE;
+ unit->print(&str, enum_query_type(QT_TO_SYSTEM_CHARSET |
+ QT_SHOW_SELECT_NUMBER |
+ QT_ITEM_IDENT_SKIP_DB_NAMES |
+ QT_VIEW_INTERNAL));
+ thd->variables.option_bits= save_option_bits;
+ /*
+ The output is not very pretty lots of back-ticks, the output
+ is as the one in explain extended , lets try to improved it here.
+ */
+ writer->add("expanded_query", str.c_ptr_safe(), str.length());
+}
+
void opt_trace_print_expanded_query(THD *thd, SELECT_LEX *select_lex,
Json_writer_object *writer)
@@ -499,10 +520,49 @@ Opt_trace_start::Opt_trace_start(THD *thd, TABLE_LIST *tbl,
}
}
+
+/*
+ @brief
+ See "Handing Query Errors" section of comment for Opt_trace_start
+*/
+
+void Opt_trace_start::trace_heading_done()
+{
+ Json_writer *w;
+ if (traceable && (w= ctx->get_current_json()))
+ trace_heading_size= w->get_written_size();
+ else
+ trace_heading_size= 0;
+}
+
+
+/*
+ @brief
+ See "Handing Query Errors" section of comment for Opt_trace_start
+
+ @detail
+ We can't delete the trace right now, because some final writes (e.g.
+ the top-level closing '}' will still be made to it. Just set clean_me=true
+ so that it is deleted instead of saving it.
+*/
+
+void Opt_trace_start::clean_empty_trace()
+{
+ Json_writer *w;
+ if (traceable && (w= ctx->get_current_json()))
+ {
+ if (w->get_written_size() == trace_heading_size)
+ clean_me= true;
+ }
+}
+
+
Opt_trace_start::~Opt_trace_start()
{
if (traceable)
{
+ if (clean_me)
+ ctx->abort_trace();
ctx->end();
traceable= FALSE;
}
diff --git a/sql/opt_trace.h b/sql/opt_trace.h
index 101fb5f707e..c6583cdab80 100644
--- a/sql/opt_trace.h
+++ b/sql/opt_trace.h
@@ -69,10 +69,34 @@ struct Opt_trace_info
@param query query
@param length query's length
@param charset charset which was used to encode this query
+
+ @detail
+ == Lifecycle ==
+ The trace is created before the Name Resolution phase. Reasons:
+ 1. This way, we can have one place where we start the trace for all kinds of
+ queries. If we tried to start tracing right before query optimization
+ starts, we would have to construct Opt_trace_start object in many
+ places: one for SELECT, for UPDATE, for DELETE, etc.
+
+ 2. Privilege checking code may notify the trace that the user doesn't have
+ enough permissions to perform tracing. The trace must exist to receive
+ the notication. See missing_privilege() and the opt_trace_disable_if_...
+ functions below for details.
+
+ == Handling Query Errors ==
+ The trace is kept when query error occurs, except for the case when
+ nothing [meaningful] was traced. The second part is necessary for mtr to
+ produce the same output with and without --ps-protocol. If there is an
+ error on prepare phase, then:
+ - In --ps-protocol: PREPARE command produces no trace. The EXECUTE
+ command is not run. The trace is not generated at all.
+ - Regular SQL query: should also NOT produce any trace to match the above.
+ This is handled by trace_heading_done() and clean_empty_trace().
*/
-class Opt_trace_start {
+class Opt_trace_start
+{
public:
Opt_trace_start(THD *thd_arg, TABLE_LIST *tbl,
enum enum_sql_command sql_command,
@@ -82,8 +106,17 @@ class Opt_trace_start {
const CHARSET_INFO *query_charset);
~Opt_trace_start();
+ void trace_heading_done();
+ void clean_empty_trace();
private:
Opt_trace_context *const ctx;
+
+ /* Number of bytes written to the trace after the heading was written/ */
+ size_t trace_heading_size;
+
+ /* If true, trace should be removed (See Handling Query Errors above) */
+ bool clean_me= false;
+
/*
True: the query will be traced
False: otherwise
@@ -102,6 +135,9 @@ class Opt_trace_start {
void opt_trace_print_expanded_query(THD *thd, SELECT_LEX *select_lex,
Json_writer_object *trace_object);
+void opt_trace_print_expanded_union(THD *thd, SELECT_LEX_UNIT *unit,
+ Json_writer_object *writer);
+
void add_table_scan_values_to_trace(THD *thd, JOIN_TAB *tab);
void trace_plan_prefix(JOIN *join, uint idx, table_map join_tables);
void print_final_join_order(JOIN *join);
diff --git a/sql/opt_trace_context.h b/sql/opt_trace_context.h
index f578a0c67ec..ae77c94fdc2 100644
--- a/sql/opt_trace_context.h
+++ b/sql/opt_trace_context.h
@@ -114,6 +114,12 @@ public:
bool is_enabled();
+ void abort_trace()
+ {
+ delete current_trace;
+ current_trace= NULL;
+ }
+
void missing_privilege();
static const char *flag_names[];
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 579ea34b8e4..cf878ad29c0 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -420,6 +420,24 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
goto exit_merge;
}
+ if (unlikely(thd->trace_started()))
+ {
+ /*
+ Add to optimizer trace whether a derived table/view
+ is merged into the parent select or not.
+ */
+ OPT_TRACE_VIEWS_TRANSFORM(thd, trace_wrapper, trace_derived,
+ derived->is_derived() ? "derived" : "view",
+ derived->alias.str ? derived->alias.str : "<NULL>",
+ derived->get_unit()->first_select()->select_number,
+ derived->is_merged_derived() ? "merged" : "materialized");
+ if (derived->is_merged_derived())
+ {
+ opt_trace_print_expanded_union(thd, derived->get_unit(),
+ &trace_derived);
+ }
+ }
+
/*
exclude select lex so it doesn't show up in explain.
do this only for derived table as for views this is already done.
@@ -822,18 +840,6 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
}
}
- if (unlikely(thd->trace_started()))
- {
- /*
- Add to optimizer trace whether a derived table/view
- is merged into the parent select or not.
- */
- OPT_TRACE_VIEWS_TRANSFORM(thd, trace_wrapper, trace_derived,
- derived->is_derived() ? "derived" : "view",
- derived->alias.str ? derived->alias.str : "<NULL>",
- derived->get_unit()->first_select()->select_number,
- derived->is_merged_derived() ? "merged" : "materialized");
- }
/*
Above cascade call of prepare is important for PS protocol, but after it
is called we can check if we really need prepare for this derived
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index b9d3eec5a60..b19ac276267 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -3648,6 +3648,7 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt)
Json_writer_object trace_command(thd);
Json_writer_array trace_command_steps(thd, "steps");
+ ots.trace_heading_done();
/* store old value of binlog format */
enum_binlog_format orig_binlog_format,orig_current_stmt_binlog_format;
@@ -6154,6 +6155,9 @@ finish:
thd->wsrep_PA_safe= true;
#endif /* WITH_WSREP */
+ if (res || thd->is_error())
+ ots.clean_empty_trace();
+
DBUG_RETURN(res || thd->is_error());
}
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index cc6f572ea64..b6bc9eff50e 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -2435,17 +2435,6 @@ static bool check_prepared_statement(Prepared_statement *stmt)
lex->first_select_lex()->context.resolve_in_table_list_only(select_lex->
get_table_list());
- /*
- For the optimizer trace, this is the symmetric, for statement preparation,
- of what is done at statement execution (in mysql_execute_command()).
- */
- Opt_trace_start ots(thd, tables, lex->sql_command, &lex->var_list,
- thd->query(), thd->query_length(),
- thd->variables.character_set_client);
-
- Json_writer_object trace_command(thd);
- Json_writer_array trace_command_steps(thd, "steps");
-
/* Reset warning count for each query that uses tables */
if (tables)
thd->get_stmt_da()->opt_clear_warning_info(thd->query_id);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index a357d4f8c8a..45d8f54e264 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1287,11 +1287,6 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
join_list= &select_lex->top_join_list;
union_part= unit_arg->is_unit_op();
- Json_writer_object trace_wrapper(thd);
- Json_writer_object trace_prepare(thd, "join_preparation");
- trace_prepare.add_select_number(select_lex->select_number);
- Json_writer_array trace_steps(thd, "steps");
-
// simple check that we got usable conds
dbug_print_item(conds);
@@ -1675,12 +1670,6 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
}
}
- if (thd->trace_started())
- {
- Json_writer_object trace_wrapper(thd);
- opt_trace_print_expanded_query(thd, select_lex, &trace_wrapper);
- }
-
if (!procedure && result && result->prepare(fields_list, unit_arg))
goto err; /* purecov: inspected */
@@ -1985,7 +1974,11 @@ JOIN::optimize_inner()
Json_writer_object trace_wrapper(thd);
Json_writer_object trace_prepare(thd, "join_optimization");
- trace_prepare.add_select_number(select_lex->select_number);
+ if (thd->trace_started())
+ {
+ trace_prepare.add_select_number(select_lex->select_number);
+ opt_trace_print_expanded_query(thd, select_lex, &trace_prepare);
+ }
Json_writer_array trace_steps(thd, "steps");
/*