From be8709eb7bdf2a68a1c04fd8ab368113f5f39b63 Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Wed, 13 Feb 2019 11:22:16 +0530 Subject: MDEV-6111 Optimizer Trace This task involves the implementation for the optimizer trace. This feature produces a trace for any SELECT/UPDATE/DELETE/, which contains information about decisions taken by the optimizer during the optimization phase (choice of table access method, various costs, transformations, etc). This feature would help to tell why some decisions were taken by the optimizer and why some were rejected. Trace is session-local, controlled by the @@optimizer_trace variable. To enable optimizer trace we need to write: set @@optimizer_trace variable= 'enabled=on'; To display the trace one can run: SELECT trace FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE; This task also involves: MDEV-18489: Limit the memory used by the optimizer trace introduces a switch optimizer_trace_max_mem_size which limits the memory used by the optimizer trace. This was implemented by Sergei Petrunia. --- sql/sql_derived.cc | 49 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) (limited to 'sql/sql_derived.cc') diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 878aa715b84..28d28ca2425 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -32,6 +32,7 @@ #include "sql_acl.h" // SELECT_ACL #include "sql_class.h" #include "sql_cte.h" +#include "my_json_writer.h" typedef bool (*dt_processor)(THD *thd, LEX *lex, TABLE_LIST *derived); @@ -198,6 +199,7 @@ mysql_handle_single_derived(LEX *lex, TABLE_LIST *derived, uint phases) if ((res= (*processors[phase])(lex->thd, lex, derived))) break; } + lex->thd->derived_tables_processing= FALSE; DBUG_RETURN(res); } @@ -368,6 +370,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) DBUG_PRINT("enter", ("Alias: '%s' Unit: %p", (derived->alias.str ? derived->alias.str : ""), derived->get_unit())); + const char *cause= NULL; if (derived->merged) { @@ -379,6 +382,7 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) if (dt_select->uncacheable & UNCACHEABLE_RAND) { /* There is random function => fall back to materialization. */ + cause= "Random function in the select"; derived->change_refs_to_fields(); derived->set_materialized_derived(); DBUG_RETURN(FALSE); @@ -401,15 +405,11 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) and small subqueries, and the bigger one can't be merged it wouldn't block the smaller one. */ - if (parent_lex->get_free_table_map(&map, &tablenr)) - { - /* There is no enough table bits, fall back to materialization. */ - goto unconditional_materialization; - } - - if (dt_select->leaf_tables.elements + tablenr > MAX_TABLES) + if (parent_lex->get_free_table_map(&map, &tablenr) || + dt_select->leaf_tables.elements + tablenr > MAX_TABLES) { /* There is no enough table bits, fall back to materialization. */ + cause= "Not enough table bits to merge subquery"; goto unconditional_materialization; } @@ -486,6 +486,24 @@ exit_merge: DBUG_RETURN(res); unconditional_materialization: + + if (unlikely(thd->trace_started())) + { + /* + Add to the optimizer trace the change in choice for merged + derived tables/views to materialised ones. + */ + Json_writer_object trace_wrapper(thd); + Json_writer_object trace_derived(thd, derived->is_derived() ? + "derived" : "view"); + trace_derived.add("table", derived->alias.str ? derived->alias.str : "") + .add_select_number(derived->get_unit()-> + first_select()->select_number) + .add("initial_choice", "merged") + .add("final_choice", "materialized") + .add("cause", cause); + } + derived->change_refs_to_fields(); derived->set_materialized_derived(); if (!derived->table || !derived->table->is_created()) @@ -654,7 +672,6 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) DBUG_ENTER("mysql_derived_prepare"); DBUG_PRINT("enter", ("unit: %p table_list: %p alias: '%s'", unit, derived, derived->alias.str)); - if (!unit) DBUG_RETURN(FALSE); @@ -747,6 +764,22 @@ 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. + */ + Json_writer_object trace_wrapper(thd); + Json_writer_object trace_derived(thd, derived->is_derived() ? + "derived" : "view"); + trace_derived.add("table", derived->alias.str ? derived->alias.str : "") + .add_select_number(derived->get_unit()->first_select()->select_number); + if (derived->is_materialized_derived()) + trace_derived.add("materialized", true); + if (derived->is_merged_derived()) + trace_derived.add("merged", true); + } /* 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 -- cgit v1.2.1