Minor cleanup in the optimizer trace code.

More test coverage added for the optimizer trace.
This commit is contained in:
Varun Gupta 2019-02-18 17:11:20 +05:30
parent 7d2138d4a4
commit 9cb55143ac
17 changed files with 3037 additions and 469 deletions

File diff suppressed because it is too large Load Diff

View File

@ -323,6 +323,7 @@ set optimizer_trace='enabled=off';
--echo # MDEV-18528: Optimizer trace support for multi-table UPDATE and DELETE
--echo #
set optimizer_trace=1;
create table ten(a int);
insert into ten values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t0 (a int, b int);
@ -333,3 +334,43 @@ insert into t1 select * from t0;
explain delete t0,t1 from t0, t1 where t0.a=t1.a and t1.a<3;
select * from information_schema.optimizer_trace;
drop table ten,t0,t1;
set optimizer_trace='enabled=off';
--echo #
--echo # Merged to Materialized for derived tables
--echo #
set optimizer_trace=1;
create table t1 (a int);
insert into t1 values (1),(2),(3);
explain select * from (select rand() from t1)q;
select * from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
drop table t1;
set optimizer_trace='enabled=off';
--echo #
--echo # Semi-join nest
--echo #
set optimizer_trace=1;
create table t1 (a int);
insert into t1 values (1),(2),(3);
create table t2(a int);
insert into t2 values (1),(2),(3),(1),(2),(3),(1),(2),(3);
set @save_optimizer_switch= @@optimizer_switch;
explain select * from t1 where a in (select t_inner_1.a from t1 t_inner_1, t1 t_inner_2);
select * from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
--echo # with Firstmatch, mostly for tracing fix_semijoin_strategies_for_picked_join_order
set optimizer_switch='materialization=off';
explain select * from t1 t_outer_1,t2 t_outer_2 where t_outer_1.a in (select t_inner_1.a from t2 t_inner_2, t1 t_inner_1) and
t_outer_2.a in (select t_inner_3.a from t2 t_inner_3, t1 t_inner_4);
select * from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
set optimizer_switch='materialization=on';
explain select * from t1 t_outer_1,t2 t_outer_2 where t_outer_1.a in (select t_inner_1.a from t2 t_inner_2, t1 t_inner_1) and
t_outer_2.a in (select t_inner_3.a from t2 t_inner_3, t1 t_inner_4);
select * from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
set @@optimizer_switch= @save_optimizer_switch;
drop table t1,t2;
set optimizer_trace='enabled=off';

View File

@ -24,7 +24,7 @@ explain select * from t1 where a=1 or b=1 {
"select_id": 1,
"steps": [
{
"expanded_query": "select `t1`.`a` AS `a`,`t1`.`b` AS `b`,`t1`.`c` AS `c`,`t1`.`filler` AS `filler` from `t1` where `t1`.`a` = 1 or `t1`.`b` = 1"
"expanded_query": "select t1.a AS a,t1.b AS b,t1.c AS c,t1.filler AS filler from t1 where t1.a = 1 or t1.b = 1"
}
]
}
@ -194,15 +194,11 @@ explain select * from t1 where a=1 or b=1 {
},
{
"selectivity_for_indexes": [],
"selectivity_for_columns": []
"selectivity_for_columns": [],
"cond_selectivity": 0.002
}
]
},
{
"execution_plan_for_potential_materialization": {
"steps": []
}
},
{
"considered_execution_plans": [
{

View File

@ -32,7 +32,7 @@ explain select * from t1 where pk1 != 0 and key1 = 1 {
"select_id": 1,
"steps": [
{
"expanded_query": "select `t1`.`pk1` AS `pk1`,`t1`.`pk2` AS `pk2`,`t1`.`key1` AS `key1`,`t1`.`key2` AS `key2` from `t1` where `t1`.`pk1` <> 0 and `t1`.`key1` = 1"
"expanded_query": "select t1.pk1 AS pk1,t1.pk2 AS pk2,t1.key1 AS key1,t1.key2 AS key2 from t1 where t1.pk1 <> 0 and t1.key1 = 1"
}
]
}
@ -183,15 +183,11 @@ explain select * from t1 where pk1 != 0 and key1 = 1 {
"selectivity_from_index": 0.001
}
],
"selectivity_for_columns": []
"selectivity_for_columns": [],
"cond_selectivity": 0.001
}
]
},
{
"execution_plan_for_potential_materialization": {
"steps": []
}
},
{
"considered_execution_plans": [
{

View File

@ -55,7 +55,7 @@ select * from db1.t1 {
"select_id": 1,
"steps": [
{
"expanded_query": "select `db1`.`t1`.`a` AS `a` from `t1`"
"expanded_query": "select db1.t1.a AS a from t1"
}
]
}
@ -85,11 +85,6 @@ select * from db1.t1 {
}
]
},
{
"execution_plan_for_potential_materialization": {
"steps": []
}
},
{
"considered_execution_plans": [
{
@ -157,7 +152,7 @@ select * from db1.v1 {
"view": {
"table": "v1",
"select_id": 2,
"merged": true
"algorithm": "merged"
}
},
{
@ -165,13 +160,13 @@ select * from db1.v1 {
"select_id": 2,
"steps": [
{
"expanded_query": "/* select#2 */ select `db1`.`t1`.`a` AS `a` from `t1`"
"expanded_query": "/* select#2 */ select db1.t1.a AS a from t1"
}
]
}
},
{
"expanded_query": "/* select#1 */ select `db1`.`t1`.`a` AS `a` from `v1`"
"expanded_query": "/* select#1 */ select db1.t1.a AS a from v1"
}
]
}
@ -201,11 +196,6 @@ select * from db1.v1 {
}
]
},
{
"execution_plan_for_potential_materialization": {
"steps": []
}
},
{
"considered_execution_plans": [
{

View File

@ -226,8 +226,8 @@ Json_writer_object::Json_writer_object(THD *thd) :
my_writer->start_object();
}
Json_writer_object::Json_writer_object(THD* thd, const char *str)
: Json_writer_struct(thd)
Json_writer_object::Json_writer_object(THD* thd, const char *str) :
Json_writer_struct(thd)
{
if (my_writer)
my_writer->add_member(str).start_object();
@ -247,8 +247,8 @@ Json_writer_array::Json_writer_array(THD *thd) :
my_writer->start_array();
}
Json_writer_array::Json_writer_array(THD *thd, const char *str)
:Json_writer_struct(thd)
Json_writer_array::Json_writer_array(THD *thd, const char *str) :
Json_writer_struct(thd)
{
if (my_writer)
my_writer->add_member(str).start_array();
@ -263,6 +263,16 @@ Json_writer_array::~Json_writer_array()
}
}
Json_writer_temp_disable::Json_writer_temp_disable(THD *thd_arg)
{
thd= thd_arg;
thd->opt_trace.disable_tracing_if_required();
}
Json_writer_temp_disable::~Json_writer_temp_disable()
{
thd->opt_trace.enable_tracing_if_required();
}
bool Single_line_formatting_helper::on_add_member(const char *name)
{
DBUG_ASSERT(state== INACTIVE || state == DISABLED);

View File

@ -215,12 +215,11 @@ public:
*/
void set_size_limit(size_t mem_size) { output.set_size_limit(mem_size); }
// psergey: return how many bytes would be required to store everything
size_t get_truncated_bytes() { return output.get_truncated_bytes(); }
Json_writer() :
indent_level(0), document_start(true), element_started(false),
first_child(true), allowed_mem_size(0)
first_child(true)
{
fmt_helper.init(this);
}
@ -235,12 +234,6 @@ private:
bool element_started;
bool first_child;
/*
True when we are using the optimizer trace
FALSE otherwise
*/
size_t allowed_mem_size;
Single_line_formatting_helper fmt_helper;
void append_indent();
@ -566,6 +559,17 @@ public:
~Json_writer_array();
};
/*
RAII-based class to disable writing into the JSON document
*/
class Json_writer_temp_disable
{
public:
Json_writer_temp_disable(THD *thd_arg);
~Json_writer_temp_disable();
THD *thd;
};
/*
RAII-based helper class to detect incorrect use of Json_writer.

View File

@ -2466,9 +2466,9 @@ void TRP_GROUP_MIN_MAX::trace_basic_info(const PARAM *param,
trace_object->add("type", "index_group").add("index", index_info->name);
if (min_max_arg_part)
trace_object->add("group_attribute", min_max_arg_part->field->field_name);
trace_object->add("min_max_arg", min_max_arg_part->field->field_name);
else
trace_object->add_null("group_attribute");
trace_object->add_null("min_max_arg");
trace_object->add("min_aggregate", have_min)
.add("max_aggregate", have_max)
@ -3438,7 +3438,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
{
rows= 0;
table->reginfo.impossible_range= 1;
selectivity_for_column.add("selectivity_from_histograms", rows);
selectivity_for_column.add("selectivity_from_histogram", rows);
selectivity_for_column.add("cause", "impossible range");
goto free_alloc;
}
@ -3448,7 +3448,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
if (rows != DBL_MAX)
{
key->field->cond_selectivity= rows/table_records;
selectivity_for_column.add("selectivity_from_histograms",
selectivity_for_column.add("selectivity_from_histogram",
key->field->cond_selectivity);
}
}
@ -3472,6 +3472,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
free_root(&alloc, MYF(0));
}
selectivity_for_columns.end();
if (quick && (quick->get_type() == QUICK_SELECT_I::QS_TYPE_ROR_UNION ||
quick->get_type() == QUICK_SELECT_I::QS_TYPE_INDEX_MERGE))
@ -3546,7 +3547,7 @@ bool calculate_cond_selectivity_for_table(THD *thd, TABLE *table, Item **cond)
table->cond_selectivity_sampling_explain= &dt->list;
}
}
trace_wrapper.add("cond_selectivity", table->cond_selectivity);
DBUG_RETURN(FALSE);
}
@ -15829,7 +15830,8 @@ static void print_key_value(String *out, const KEY_PART_INFO *key_part,
{
Field *field= key_part->field;
if (field->flags & BLOB_FLAG) {
if (field->flags & BLOB_FLAG)
{
// Byte 0 of a nullable key is the null-byte. If set, key is NULL.
if (field->real_maybe_null() && *key)
out->append(STRING_WITH_LEN("NULL"));
@ -15849,7 +15851,8 @@ static void print_key_value(String *out, const KEY_PART_INFO *key_part,
Otherwise, print the key value starting immediately after the
null-byte
*/
if (*key) {
if (*key)
{
out->append(STRING_WITH_LEN("NULL"));
return;
}
@ -15862,9 +15865,11 @@ static void print_key_value(String *out, const KEY_PART_INFO *key_part,
optimizer trace expects. If the column is binary, the hex
representation is printed to the trace instead.
*/
if (field->flags & BINARY_FLAG) {
if (field->flags & BINARY_FLAG)
{
out->append("0x");
for (uint i = 0; i < store_length; i++) {
for (uint i = 0; i < store_length; i++)
{
out->append(_dig_vec_lower[*(key + i) >> 4]);
out->append(_dig_vec_lower[*(key + i) & 0x0F]);
}

View File

@ -456,6 +456,7 @@ void best_access_path(JOIN *join, JOIN_TAB *s,
table_map remaining_tables, uint idx,
bool disable_jbuf, double record_count,
POSITION *pos, POSITION *loose_scan_pos);
void trace_plan_prefix(JOIN *join, uint idx, table_map remaining_tables);
static Item *create_subq_in_equalities(THD *thd, SJ_MATERIALIZATION_INFO *sjm,
Item_in_subselect *subq_pred);
@ -697,9 +698,10 @@ 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, oto0, oto1, select_lex->select_number,
OPT_TRACE_TRANSFORM(thd, trace_wrapper, trace_transform,
select_lex->select_number,
"IN (SELECT)", "semijoin");
oto1.add("chosen", true);
trace_transform.add("chosen", true);
}
}
else
@ -840,7 +842,7 @@ bool subquery_types_allow_materialization(THD* thd, Item_in_subselect *in_subs)
in_subs->types_allow_materialization= FALSE; // Assign default values
in_subs->sjm_scan_allowed= FALSE;
OPT_TRACE_TRANSFORM(thd, oto0, oto1,
OPT_TRACE_TRANSFORM(thd, trace_wrapper, trace_transform,
in_subs->get_select_lex()->select_number,
"IN (SELECT)", "materialization");
@ -856,8 +858,8 @@ bool subquery_types_allow_materialization(THD* thd, Item_in_subselect *in_subs)
if (!inner->type_handler()->subquery_type_allows_materialization(inner,
outer))
{
oto1.add("possible", false);
oto1.add("cause", "types mismatch");
trace_transform.add("possible", false);
trace_transform.add("cause", "types mismatch");
DBUG_RETURN(FALSE);
}
}
@ -879,12 +881,12 @@ bool subquery_types_allow_materialization(THD* thd, Item_in_subselect *in_subs)
{
in_subs->types_allow_materialization= TRUE;
in_subs->sjm_scan_allowed= all_are_fields;
oto1.add("sjm_scan_allowed", all_are_fields)
trace_transform.add("sjm_scan_allowed", all_are_fields)
.add("possible", true);
DBUG_PRINT("info",("subquery_types_allow_materialization: ok, allowed"));
DBUG_RETURN(TRUE);
}
oto1.add("possible", false).add("cause", cause);
trace_transform.add("possible", false).add("cause", cause);
DBUG_RETURN(FALSE);
}
@ -1236,29 +1238,30 @@ bool convert_join_subqueries_to_semijoins(JOIN *join)
/* Stop processing if we've reached a subquery that's attached to the ON clause */
if (in_subq->do_not_convert_to_sj)
{
OPT_TRACE_TRANSFORM(thd, oto0, oto1,
OPT_TRACE_TRANSFORM(thd, trace_wrapper, trace_transform,
in_subq->get_select_lex()->select_number,
"IN (SELECT)", "semijoin");
oto1.add("converted_to_semi_join", false)
trace_transform.add("converted_to_semi_join", false)
.add("cause", "subquery attached to the ON clause");
break;
}
if (in_subq->is_flattenable_semijoin)
{
OPT_TRACE_TRANSFORM(thd, oto0, oto1,
OPT_TRACE_TRANSFORM(thd, trace_wrapper, trace_transform,
in_subq->get_select_lex()->select_number,
"IN (SELECT)", "semijoin");
if (join->table_count +
in_subq->unit->first_select()->join->table_count >= MAX_TABLES)
{
oto1.add("converted_to_semi_join", false);
oto1.add("cause", "table in parent join now exceeds MAX_TABLES");
trace_transform.add("converted_to_semi_join", false);
trace_transform.add("cause",
"table in parent join now exceeds MAX_TABLES");
break;
}
if (convert_subq_to_sj(join, in_subq))
goto restore_arena_and_fail;
oto1.add("converted_to_semi_join", true);
trace_transform.add("converted_to_semi_join", true);
}
else
{
@ -2380,6 +2383,8 @@ bool optimize_semijoin_nests(JOIN *join, table_map all_table_map)
THD *thd= join->thd;
List_iterator<TABLE_LIST> sj_list_it(join->select_lex->sj_nests);
TABLE_LIST *sj_nest;
if (!join->select_lex->sj_nests.elements)
DBUG_RETURN(FALSE);
Json_writer_object wrapper(thd);
Json_writer_object trace_semijoin_nest(thd,
"execution_plan_for_potential_materialization");
@ -2939,6 +2944,7 @@ bool Sj_materialization_picker::check_qep(JOIN *join,
{
bool sjm_scan;
SJ_MATERIALIZATION_INFO *mat_info;
THD *thd= join->thd;
if ((mat_info= at_sjmat_pos(join, remaining_tables,
new_join_tab, idx, &sjm_scan)))
{
@ -3040,6 +3046,7 @@ bool Sj_materialization_picker::check_qep(JOIN *join,
POSITION curpos, dummy;
/* Need to re-run best-access-path as we prefix_rec_count has changed */
bool disable_jbuf= (join->thd->variables.join_cache_level == 0);
Json_writer_temp_disable trace_semijoin_mat_scan(thd);
for (i= first_tab + mat_info->tables; i <= idx; i++)
{
best_access_path(join, join->positions[i].table, rem_tables, i,
@ -3590,6 +3597,12 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
table_map handled_tabs= 0;
join->sjm_lookup_tables= 0;
join->sjm_scan_tables= 0;
THD *thd= join->thd;
if (!join->select_lex->sj_nests.elements)
return;
Json_writer_object trace_wrapper(thd);
Json_writer_array trace_semijoin_strategies(thd,
"fix_semijoin_strategies_for_picked_join_order");
for (tablenr= table_count - 1 ; tablenr != join->const_tables - 1; tablenr--)
{
POSITION *pos= join->best_positions + tablenr;
@ -3614,9 +3627,19 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
first= tablenr - sjm->tables + 1;
join->best_positions[first].n_sj_tables= sjm->tables;
join->best_positions[first].sj_strategy= SJ_OPT_MATERIALIZE;
Json_writer_object semijoin_strategy(thd);
semijoin_strategy.add("semi_join_strategy","sj_materialize");
Json_writer_array semijoin_plan(thd, "join_order");
for (uint i= first; i < first+ sjm->tables; i++)
{
if (unlikely(thd->trace_started()))
{
Json_writer_object trace_one_table(thd);
trace_one_table.add_table_name(join->best_positions[i].table);
}
join->sjm_lookup_tables |= join->best_positions[i].table->table->map;
}
}
else if (pos->sj_strategy == SJ_OPT_MATERIALIZE_SCAN)
{
POSITION *first_inner= join->best_positions + pos->sjmat_picker.sjm_scan_last_inner;
@ -3653,8 +3676,16 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
POSITION dummy;
join->cur_sj_inner_tables= 0;
Json_writer_object semijoin_strategy(thd);
semijoin_strategy.add("semi_join_strategy","sj_materialize_scan");
Json_writer_array semijoin_plan(thd, "join_order");
for (i= first + sjm->tables; i <= tablenr; i++)
{
if (unlikely(thd->trace_started()))
{
Json_writer_object trace_one_table(thd);
trace_one_table.add_table_name(join->best_positions[i].table);
}
best_access_path(join, join->best_positions[i].table, rem_tables, i,
FALSE, prefix_rec_count,
join->best_positions + i, &dummy);
@ -3683,8 +3714,16 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
join buffering
*/
join->cur_sj_inner_tables= 0;
Json_writer_object semijoin_strategy(thd);
semijoin_strategy.add("semi_join_strategy","firstmatch");
Json_writer_array semijoin_plan(thd, "join_order");
for (idx= first; idx <= tablenr; idx++)
{
if (unlikely(thd->trace_started()))
{
Json_writer_object trace_one_table(thd);
trace_one_table.add_table_name(join->best_positions[idx].table);
}
if (join->best_positions[idx].use_join_buffer)
{
best_access_path(join, join->best_positions[idx].table,
@ -3713,8 +3752,16 @@ void fix_semijoin_strategies_for_picked_join_order(JOIN *join)
join buffering
*/
join->cur_sj_inner_tables= 0;
Json_writer_object semijoin_strategy(thd);
semijoin_strategy.add("semi_join_strategy","sj_materialize");
Json_writer_array semijoin_plan(thd, "join_order");
for (idx= first; idx <= tablenr; idx++)
{
if (unlikely(thd->trace_started()))
{
Json_writer_object trace_one_table(thd);
trace_one_table.add_table_name(join->best_positions[idx].table);
}
if (join->best_positions[idx].use_join_buffer || (idx == first))
{
best_access_path(join, join->best_positions[idx].table,

View File

@ -525,7 +525,7 @@ eliminate_tables_for_list(JOIN *join,
table_map tables_in_list,
Item *on_expr,
table_map tables_used_elsewhere,
Json_writer_array* eliminate_tables);
Json_writer_array* trace_eliminate_tables);
static
bool check_func_dependency(JOIN *join,
table_map dep_tables,
@ -545,7 +545,7 @@ Dep_module_expr *merge_eq_mods(Dep_module_expr *start,
Dep_module_expr *new_fields,
Dep_module_expr *end, uint and_level);
static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl,
Json_writer_array* eliminate_tables);
Json_writer_array* trace_eliminate_tables);
static
void add_module_expr(Dep_analysis_context *dac, Dep_module_expr **eq_mod,
uint and_level, Dep_value_field *field_val, Item *right,
@ -671,12 +671,12 @@ void eliminate_tables(JOIN *join)
}
table_map all_tables= join->all_tables_map();
Json_writer_array eliminated_tables(thd,"eliminated_tables");
Json_writer_array trace_eliminated_tables(thd,"eliminated_tables");
if (all_tables & ~used_tables)
{
/* There are some tables that we probably could eliminate. Try it. */
eliminate_tables_for_list(join, join->join_list, all_tables, NULL,
used_tables, &eliminated_tables);
used_tables, &trace_eliminated_tables);
}
DBUG_VOID_RETURN;
}
@ -720,7 +720,7 @@ static bool
eliminate_tables_for_list(JOIN *join, List<TABLE_LIST> *join_list,
table_map list_tables, Item *on_expr,
table_map tables_used_elsewhere,
Json_writer_array *eliminate_tables)
Json_writer_array *trace_eliminate_tables)
{
TABLE_LIST *tbl;
List_iterator<TABLE_LIST> it(*join_list);
@ -742,9 +742,10 @@ eliminate_tables_for_list(JOIN *join, List<TABLE_LIST> *join_list,
&tbl->nested_join->join_list,
tbl->nested_join->used_tables,
tbl->on_expr,
outside_used_tables, eliminate_tables))
outside_used_tables,
trace_eliminate_tables))
{
mark_as_eliminated(join, tbl, eliminate_tables);
mark_as_eliminated(join, tbl, trace_eliminate_tables);
}
else
all_eliminated= FALSE;
@ -756,7 +757,7 @@ eliminate_tables_for_list(JOIN *join, List<TABLE_LIST> *join_list,
check_func_dependency(join, tbl->table->map, NULL, tbl,
tbl->on_expr))
{
mark_as_eliminated(join, tbl, eliminate_tables);
mark_as_eliminated(join, tbl, trace_eliminate_tables);
}
else
all_eliminated= FALSE;
@ -1797,7 +1798,7 @@ Dep_module* Dep_value_field::get_next_unbound_module(Dep_analysis_context *dac,
*/
static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl,
Json_writer_array* eliminate_tables)
Json_writer_array* trace_eliminate_tables)
{
TABLE *table;
/*
@ -1810,7 +1811,7 @@ static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl,
TABLE_LIST *child;
List_iterator<TABLE_LIST> it(tbl->nested_join->join_list);
while ((child= it++))
mark_as_eliminated(join, child, eliminate_tables);
mark_as_eliminated(join, child, trace_eliminate_tables);
}
else if ((table= tbl->table))
{
@ -1821,7 +1822,7 @@ static void mark_as_eliminated(JOIN *join, TABLE_LIST *tbl,
tab->type= JT_CONST;
tab->table->const_table= 1;
join->eliminated_tables |= table->map;
eliminate_tables->add(table->alias.c_ptr_safe());
trace_eliminate_tables->add(table->alias.c_ptr_safe());
join->const_table_map|= table->map;
set_position(join, join->const_tables++, tab, (KEYUSE*)0);
}

View File

@ -66,7 +66,8 @@ bool sets_var_optimizer_trace(enum enum_sql_command sql_command,
}
ST_FIELD_INFO optimizer_trace_info[] = {
ST_FIELD_INFO optimizer_trace_info[]=
{
/* name, length, type, value, maybe_null, old_name, open_method */
{"QUERY", 65535, MYSQL_TYPE_STRING, 0, false, NULL, SKIP_OPEN_TABLE},
{"TRACE", 65535, MYSQL_TYPE_STRING, 0, false, NULL, SKIP_OPEN_TABLE},
@ -74,7 +75,8 @@ ST_FIELD_INFO optimizer_trace_info[] = {
SKIP_OPEN_TABLE},
{"INSUFFICIENT_PRIVILEGES", 1, MYSQL_TYPE_TINY, 0, false, NULL,
SKIP_OPEN_TABLE},
{NULL, 0, MYSQL_TYPE_STRING, 0, true, NULL, 0}};
{NULL, 0, MYSQL_TYPE_STRING, 0, true, NULL, 0}
};
/*
TODO: one-line needs to be implemented seperately
@ -105,15 +107,15 @@ void opt_trace_print_expanded_query(THD *thd, SELECT_LEX *select_lex,
{
if (!thd->trace_started())
return;
char buff[1024];
String str(buff, sizeof(buff), system_charset_info);
str.length(0);
StringBuffer<1024> str(system_charset_info);
ulonglong save_option_bits= thd->variables.option_bits;
thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE;
select_lex->print(thd, &str,
enum_query_type(QT_TO_SYSTEM_CHARSET |
QT_SHOW_SELECT_NUMBER |
QT_ITEM_IDENT_SKIP_DB_NAMES |
QT_VIEW_INTERNAL
));
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.
@ -187,7 +189,6 @@ void opt_trace_disable_if_no_security_context_access(THD *thd)
thd->main_security_ctx.priv_host,
thd->security_context()->priv_host)))
trace->missing_privilege();
return;
}
void opt_trace_disable_if_no_stored_proc_func_access(THD *thd, sp_head *sp)
@ -207,7 +208,6 @@ void opt_trace_disable_if_no_stored_proc_func_access(THD *thd, sp_head *sp)
thd->set_security_context(backup_thd_sctx);
if (rc)
trace->missing_privilege();
return;
}
/**
@ -347,9 +347,6 @@ class Opt_trace_stmt {
~Opt_trace_stmt()
{
delete current_json;
missing_priv= false;
ctx= NULL;
I_S_disabled= 0;
}
void set_query(const char *query_ptr, size_t length, const CHARSET_INFO *charset);
void open_struct(const char *key, char opening_bracket);
@ -372,6 +369,12 @@ private:
String query; // store the query sent by the user
Json_writer *current_json; // stores the trace
bool missing_priv; ///< whether user lacks privilege to see this trace
/*
0 <=> this trace should be in information_schema.
!=0 tracing is disabled, this currently happens when we want to trace a
sub-statement. For now traces are only collect for the top statement
not for the sub-statments.
*/
uint I_S_disabled;
};
@ -440,28 +443,11 @@ bool Opt_trace_context::is_enabled()
Opt_trace_context::Opt_trace_context()
{
current_trace= NULL;
inited= FALSE;
traces= NULL;
max_mem_size= 0;
}
Opt_trace_context::~Opt_trace_context()
{
inited= FALSE;
/*
would be nice to move this to a function
*/
if (traces)
{
while (traces->elements())
{
Opt_trace_stmt *prev= traces->at(0);
delete prev;
traces->del(0);
}
delete traces;
traces= NULL;
}
max_mem_size= 0;
delete_traces();
}
void Opt_trace_context::set_query(const char *query, size_t length, const CHARSET_INFO *charset)
@ -487,26 +473,21 @@ void Opt_trace_context::start(THD *thd, TABLE_LIST *tbl,
DBUG_ASSERT(!current_trace);
current_trace= new Opt_trace_stmt(this);
max_mem_size= max_mem_size_arg;
if (!inited)
{
traces= new Dynamic_array<Opt_trace_stmt*>();
inited= TRUE;
}
set_allowed_mem_size(remaining_mem_size());
}
void Opt_trace_context::end()
{
if (current_trace)
traces->push(current_trace);
traces.push(current_trace);
if (!traces->elements())
if (!traces.elements())
return;
if (traces->elements() > 1)
if (traces.elements() > 1)
{
Opt_trace_stmt *prev= traces->at(0);
Opt_trace_stmt *prev= traces.at(0);
delete prev;
traces->del(0);
traces.del(0);
}
current_trace= NULL;
}
@ -659,9 +640,7 @@ void Json_writer::add_str(Item *item)
if (item)
{
THD *thd= current_thd;
char buff[256];
String str(buff, sizeof(buff), system_charset_info);
str.length(0);
StringBuffer<256> str(system_charset_info);
ulonglong save_option_bits= thd->variables.option_bits;
thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE;
@ -675,19 +654,16 @@ void Json_writer::add_str(Item *item)
add_null();
}
void Opt_trace_context::flush_optimizer_trace()
void Opt_trace_context::delete_traces()
{
inited= false;
if (traces)
if (traces.elements())
{
while (traces->elements())
while (traces.elements())
{
Opt_trace_stmt *prev= traces->at(0);
Opt_trace_stmt *prev= traces.at(0);
delete prev;
traces->del(0);
traces.del(0);
}
delete traces;
traces= NULL;
}
}
@ -703,7 +679,7 @@ int fill_optimizer_trace_info(THD *thd, TABLE_LIST *tables, Item *)
*/
Opt_trace_context* ctx= &thd->opt_trace;
if (thd->opt_trace.empty())
if (!thd->opt_trace.empty())
{
Opt_trace_stmt *stmt= ctx->get_top_trace();
stmt->fill_info(&info);

View File

@ -193,9 +193,16 @@ void opt_trace_disable_if_no_stored_proc_func_access(THD *thd, sp_head *sp);
*/
int fill_optimizer_trace_info(THD *thd, TABLE_LIST *tables, Item *);
#define OPT_TRACE_TRANSFORM(writer, object_level0, object_level1, \
#define OPT_TRACE_TRANSFORM(thd, object_level0, object_level1, \
select_number, from, to) \
Json_writer_object object_level0(writer); \
Json_writer_object object_level1(writer, "transformation"); \
Json_writer_object object_level0(thd); \
Json_writer_object object_level1(thd, "transformation"); \
object_level1.add_select_number(select_number).add("from", from).add("to", to);
#define OPT_TRACE_VIEWS_TRANSFORM(thd, object_level0, object_level1, \
derived, name, select_number, algorithm) \
Json_writer_object trace_wrapper(thd); \
Json_writer_object trace_derived(thd, derived); \
trace_derived.add("table", name).add_select_number(select_number) \
.add("algorithm", algorithm);
#endif

View File

@ -19,14 +19,14 @@ public:
ulong max_mem_size_arg);
void end();
void set_query(const char *query, size_t length, const CHARSET_INFO *charset);
void flush_optimizer_trace();
void delete_traces();
void set_allowed_mem_size(size_t mem_size);
size_t remaining_mem_size();
private:
Opt_trace_stmt* top_trace()
{
return *(traces->front());
return *(traces.front());
}
public:
@ -39,7 +39,7 @@ public:
Opt_trace_stmt* get_top_trace()
{
if (!traces || !traces->elements())
if (!traces.elements())
return NULL;
return top_trace();
}
@ -52,7 +52,7 @@ public:
bool empty()
{
return traces && (static_cast<uint>(traces->elements()) != 0);
return static_cast<uint>(traces.elements()) == 0;
}
bool is_started()
@ -79,13 +79,8 @@ private:
/*
List of traces (currently it stores only 1 trace)
*/
Dynamic_array<Opt_trace_stmt*> *traces;
Dynamic_array<Opt_trace_stmt*> traces;
Opt_trace_stmt *current_trace;
/*
TRUE: if we allocate memory for list of traces
FALSE: otherwise
*/
bool inited;
size_t max_mem_size;
};

View File

@ -1411,7 +1411,7 @@ void THD::change_user(void)
sp_cache_clear(&sp_func_cache);
sp_cache_clear(&sp_package_spec_cache);
sp_cache_clear(&sp_package_body_cache);
opt_trace.flush_optimizer_trace();
opt_trace.delete_traces();
}
/**

View File

@ -34,6 +34,7 @@
#include "sql_class.h"
#include "sql_cte.h"
#include "my_json_writer.h"
#include "opt_trace.h"
typedef bool (*dt_processor)(THD *thd, LEX *lex, TABLE_LIST *derived);
@ -384,6 +385,15 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived)
{
/* There is random function => fall back to materialization. */
cause= "Random function in the select";
if (unlikely(thd->trace_started()))
{
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,
"materialized");
trace_derived.add("cause", cause);
}
derived->change_refs_to_fields();
derived->set_materialized_derived();
DBUG_RETURN(FALSE);
@ -497,19 +507,12 @@ 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 : "<NULL>")
.add_select_number(derived->get_unit()->
first_select()->select_number)
.add("initial_choice", "merged")
.add("final_choice", "materialized")
.add("cause", cause);
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,
"materialized");
trace_derived.add("cause", cause);
}
derived->change_refs_to_fields();
@ -778,15 +781,11 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
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 : "<NULL>")
.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);
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

View File

@ -121,6 +121,7 @@ static bool best_extension_by_limited_search(JOIN *join,
double read_time, uint depth,
uint prune_level,
uint use_cond_selectivity);
void trace_plan_prefix(JOIN *join, uint idx, table_map remaining_tables);
static uint determine_search_depth(JOIN* join);
C_MODE_START
static int join_tab_cmp(const void *dummy, const void* ptr1, const void* ptr2);
@ -302,8 +303,6 @@ void set_postjoin_aggr_write_func(JOIN_TAB *tab);
static Item **get_sargable_cond(JOIN *join, TABLE *table);
static void trace_plan_prefix(JOIN *join, uint idx, table_map remaining_tables);
#ifndef DBUG_OFF
/*
@ -377,14 +376,10 @@ static void trace_table_dependencies(THD *thd,
}
}
Json_writer_array depends_on(thd, "depends_on_map_bits");
static_assert(sizeof(table_ref->get_map()) <= 64,
"RAND_TABLE_BIT may be in join_tabs[i].dependent, so we test "
"all 64 bits.");
for (uint j = 0; j < 64; j++)
{
if (join_tabs[i].dependent & (1ULL << j))
depends_on.add(static_cast<longlong>(j));
}
Table_map_iterator it(join_tabs[i].dependent);
uint dep_bit;
while ((dep_bit= it++) != Table_map_iterator::BITMAP_END)
depends_on.add(static_cast<longlong>(dep_bit));
}
}
@ -9045,7 +9040,7 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
}
static void trace_plan_prefix(JOIN *join, uint idx, table_map remaining_tables)
void trace_plan_prefix(JOIN *join, uint idx, table_map remaining_tables)
{
THD *const thd= join->thd;
Json_writer_array plan_prefix(thd, "plan_prefix");
@ -9261,9 +9256,6 @@ best_extension_by_limited_search(JOIN *join,
current_record_count / (double) TIME_FOR_COMPARE -
filter_cmp_gain;
/*
TODO add filtering estimates here
*/
advance_sj_state(join, remaining_tables, idx, &current_record_count,
&current_read_time, &loose_scan_pos);
@ -11070,12 +11062,6 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
tab->table->intersect_keys.is_set(tab->ref.key))))
{
/* Range uses longer key; Use this instead of ref on key */
/*
We can trace here, changing ref access to range access here
have a range that uses longer key.
Lets take @spetrunia's opinion
*/
Json_writer_object ref_to_range(thd);
ref_to_range.add("ref_to_range", true);
ref_to_range.add("cause", "range uses longer key");
@ -16413,6 +16399,8 @@ void optimize_wo_join_buffering(JOIN *join, uint first_tab, uint last_tab,
double cost, rec_count;
table_map reopt_remaining_tables= last_remaining_tables;
uint i;
THD *thd= join->thd;
Json_writer_temp_disable trace_wo_join_buffering(thd);
if (first_tab > join->const_tables)
{

View File

@ -665,11 +665,11 @@ void print_keyuse_array_for_trace(THD *thd, DYNAMIC_ARRAY *keyuse_array)
KEYUSE *keyuse= (KEYUSE*)dynamic_array_ptr(keyuse_array, i);
Json_writer_object keyuse_elem(thd);
keyuse_elem.add_table_name(keyuse->table->reginfo.join_tab);
keyuse_elem.add("field", (keyuse->keypart == FT_KEYPART) ? "<fulltext>"
: (keyuse->is_for_hash_join()
? keyuse->table->field[keyuse->keypart]
->field_name.str
: keyuse->table->key_info[keyuse->key]
keyuse_elem.add("field", (keyuse->keypart == FT_KEYPART) ? "<fulltext>":
(keyuse->is_for_hash_join() ?
keyuse->table->field[keyuse->keypart]
->field_name.str :
keyuse->table->key_info[keyuse->key]
.key_part[keyuse->keypart]
.field->field_name.str));
keyuse_elem.add("equals",keyuse->val);