Commit c6aee27b authored by Oleksandr Byelkin's avatar Oleksandr Byelkin Committed by Sergei Petrunia

MDEV-7811: EXPLAIN/ANALYZE FORMAT=JSON should show subquery cache

parent 498a264d
...@@ -39,6 +39,240 @@ Handler_read_prev 0 ...@@ -39,6 +39,240 @@ Handler_read_prev 0
Handler_read_rnd 0 Handler_read_rnd 0
Handler_read_rnd_deleted 0 Handler_read_rnd_deleted 0
Handler_read_rnd_next 31 Handler_read_rnd_next 31
analyze format=json
select a, (select d from t2 where b=c) from t1;
ANALYZE
{
"query_block": {
"select_id": 1,
"r_loops": 1,
"r_total_time_ms": "REPLACED",
"table": {
"table_name": "t1",
"access_type": "ALL",
"r_loops": 1,
"rows": 10,
"r_rows": 10,
"r_total_time_ms": "REPLACED",
"filtered": 100,
"r_filtered": 100
},
"subqueries": [
{
"query_block": {
"select_id": 2,
"r_loops": 4,
"r_total_time_ms": "REPLACED",
"expression_cache": {
"state": "ENABLED",
"r_hit": 6,
"r_miss": 4,
"r_loops": 10,
"r_hit_ratio": 60
},
"table": {
"table_name": "t2",
"access_type": "ALL",
"r_loops": 4,
"rows": 4,
"r_rows": 4,
"r_total_time_ms": "REPLACED",
"filtered": 100,
"r_filtered": 18.75,
"attached_condition": "(t1.b = t2.c)"
}
}
}
]
}
}
analyze format=json
select a, (select d from t2 where b=c), (select d from t2 where b=c union select 1 order by 1 limit 1) from t1;
ANALYZE
{
"query_block": {
"select_id": 1,
"r_loops": 1,
"r_total_time_ms": "REPLACED",
"table": {
"table_name": "t1",
"access_type": "ALL",
"r_loops": 1,
"rows": 10,
"r_rows": 10,
"r_total_time_ms": "REPLACED",
"filtered": 100,
"r_filtered": 100
},
"subqueries": [
{
"query_block": {
"union_result": {
"table_name": "<union3,4>",
"access_type": "ALL",
"r_loops": 4,
"r_rows": 1,
"expression_cache": {
"state": "ENABLED",
"r_hit": 6,
"r_miss": 4,
"r_loops": 10,
"r_hit_ratio": 60
},
"query_specifications": [
{
"query_block": {
"select_id": 3,
"r_loops": 4,
"r_total_time_ms": "REPLACED",
"table": {
"table_name": "t2",
"access_type": "ALL",
"r_loops": 4,
"rows": 4,
"r_rows": 4,
"r_total_time_ms": "REPLACED",
"filtered": 100,
"r_filtered": 18.75,
"attached_condition": "(t1.b = t2.c)"
}
}
},
{
"query_block": {
"select_id": 4,
"table": {
"message": "No tables used"
}
}
}
]
}
}
},
{
"query_block": {
"select_id": 2,
"r_loops": 4,
"r_total_time_ms": "REPLACED",
"expression_cache": {
"state": "ENABLED",
"r_hit": 6,
"r_miss": 4,
"r_loops": 10,
"r_hit_ratio": 60
},
"table": {
"table_name": "t2",
"access_type": "ALL",
"r_loops": 4,
"rows": 4,
"r_rows": 4,
"r_total_time_ms": "REPLACED",
"filtered": 100,
"r_filtered": 18.75,
"attached_condition": "(t1.b = t2.c)"
}
}
}
]
}
}
explain format=json
select a, (select d from t2 where b=c) from t1;
EXPLAIN
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "t1",
"access_type": "ALL",
"rows": 10,
"filtered": 100
},
"subqueries": [
{
"query_block": {
"select_id": 2,
"expression_cache": {
"state": "UNINITIALYZED"
},
"table": {
"table_name": "t2",
"access_type": "ALL",
"rows": 4,
"filtered": 100,
"attached_condition": "(t1.b = t2.c)"
}
}
}
]
}
}
explain format=json
select a, (select d from t2 where b=c), (select d from t2 where b=c union select 1 order by 1 limit 1) from t1;
EXPLAIN
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "t1",
"access_type": "ALL",
"rows": 10,
"filtered": 100
},
"subqueries": [
{
"query_block": {
"union_result": {
"table_name": "<union3,4>",
"access_type": "ALL",
"expression_cache": {
"state": "UNINITIALYZED"
},
"query_specifications": [
{
"query_block": {
"select_id": 3,
"table": {
"table_name": "t2",
"access_type": "ALL",
"rows": 4,
"filtered": 100,
"attached_condition": "(t1.b = t2.c)"
}
}
},
{
"query_block": {
"select_id": 4,
"table": {
"message": "No tables used"
}
}
}
]
}
}
},
{
"query_block": {
"select_id": 2,
"expression_cache": {
"state": "UNINITIALYZED"
},
"table": {
"table_name": "t2",
"access_type": "ALL",
"rows": 4,
"filtered": 100,
"attached_condition": "(t1.b = t2.c)"
}
}
}
]
}
}
set optimizer_switch='subquery_cache=off'; set optimizer_switch='subquery_cache=off';
flush status; flush status;
select a, (select d from t2 where b=c) from t1; select a, (select d from t2 where b=c) from t1;
......
...@@ -24,6 +24,16 @@ select a, (select d from t2 where b=c) from t1; ...@@ -24,6 +24,16 @@ select a, (select d from t2 where b=c) from t1;
show status like "subquery_cache%"; show status like "subquery_cache%";
show status like '%Handler_read%'; show status like '%Handler_read%';
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
analyze format=json
select a, (select d from t2 where b=c) from t1;
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
analyze format=json
select a, (select d from t2 where b=c), (select d from t2 where b=c union select 1 order by 1 limit 1) from t1;
explain format=json
select a, (select d from t2 where b=c) from t1;
explain format=json
select a, (select d from t2 where b=c), (select d from t2 where b=c union select 1 order by 1 limit 1) from t1;
set optimizer_switch='subquery_cache=off'; set optimizer_switch='subquery_cache=off';
flush status; flush status;
......
...@@ -7554,6 +7554,19 @@ bool Item_cache_wrapper::set_cache(THD *thd) ...@@ -7554,6 +7554,19 @@ bool Item_cache_wrapper::set_cache(THD *thd)
DBUG_RETURN(expr_cache == NULL); DBUG_RETURN(expr_cache == NULL);
} }
Expression_cache_stat* Item_cache_wrapper::set_stat(MEM_ROOT *mem_root)
{
if (expr_cache)
{
Expression_cache_stat* stat=
new(mem_root) Expression_cache_stat(expr_cache);
if (stat)
((Expression_cache_tmptable *)expr_cache)->set_stat(stat);
return stat;
}
return NULL;
}
/** /**
Check if the current values of the parameters are in the expression cache Check if the current values of the parameters are in the expression cache
......
...@@ -3804,6 +3804,7 @@ public: ...@@ -3804,6 +3804,7 @@ public:
class Item_cache; class Item_cache;
class Expression_cache; class Expression_cache;
class Expression_cache_stat;
/** /**
The objects of this class can store its values in an expression cache. The objects of this class can store its values in an expression cache.
...@@ -3838,6 +3839,7 @@ public: ...@@ -3838,6 +3839,7 @@ public:
enum Type real_type() const { return orig_item->type(); } enum Type real_type() const { return orig_item->type(); }
bool set_cache(THD *thd); bool set_cache(THD *thd);
Expression_cache_stat* set_stat(MEM_ROOT *mem_root);
bool fix_fields(THD *thd, Item **it); bool fix_fields(THD *thd, Item **it);
void cleanup(); void cleanup();
......
...@@ -1201,7 +1201,10 @@ Item* Item_singlerow_subselect::expr_cache_insert_transformer(uchar *thd_arg) ...@@ -1201,7 +1201,10 @@ Item* Item_singlerow_subselect::expr_cache_insert_transformer(uchar *thd_arg)
if (expr_cache_is_needed(thd) && if (expr_cache_is_needed(thd) &&
(expr_cache= set_expr_cache(thd))) (expr_cache= set_expr_cache(thd)))
{
set_expr_cache_stat(thd);
DBUG_RETURN(expr_cache); DBUG_RETURN(expr_cache);
}
DBUG_RETURN(this); DBUG_RETURN(this);
} }
...@@ -1497,7 +1500,10 @@ Item* Item_exists_subselect::expr_cache_insert_transformer(uchar *thd_arg) ...@@ -1497,7 +1500,10 @@ Item* Item_exists_subselect::expr_cache_insert_transformer(uchar *thd_arg)
if (substype() == EXISTS_SUBS && expr_cache_is_needed(thd) && if (substype() == EXISTS_SUBS && expr_cache_is_needed(thd) &&
(expr_cache= set_expr_cache(thd))) (expr_cache= set_expr_cache(thd)))
{
set_expr_cache_stat(thd);
DBUG_RETURN(expr_cache); DBUG_RETURN(expr_cache);
}
DBUG_RETURN(this); DBUG_RETURN(this);
} }
...@@ -6556,3 +6562,16 @@ void subselect_table_scan_engine::cleanup() ...@@ -6556,3 +6562,16 @@ void subselect_table_scan_engine::cleanup()
{ {
} }
void Item_subselect::set_expr_cache_stat(THD *thd)
{
if(!expr_cache)
return;
Explain_query *qw= thd->lex->explain;
DBUG_ASSERT(qw);
Explain_node *node= qw->get_node(unit->first_select()->select_number);
if (!node)
return;
DBUG_ASSERT(expr_cache->type() == Item::EXPR_CACHE_ITEM);
node->cache_stat= ((Item_cache_wrapper *)expr_cache)->set_stat(qw->mem_root);
}
...@@ -244,7 +244,10 @@ public: ...@@ -244,7 +244,10 @@ public:
bool limit_index_condition_pushdown_processor(uchar *opt_arg) bool limit_index_condition_pushdown_processor(uchar *opt_arg)
{ {
return TRUE; return TRUE;
} }
void set_expr_cache_stat(THD *thd);
friend class select_result_interceptor; friend class select_result_interceptor;
friend class Item_in_optimizer; friend class Item_in_optimizer;
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "sql_select.h" #include "sql_select.h"
#include "my_json_writer.h" #include "my_json_writer.h"
#include "opt_range.h" #include "opt_range.h"
#include "sql_expression_cache.h"
const char * STR_DELETING_ALL_ROWS= "Deleting all rows"; const char * STR_DELETING_ALL_ROWS= "Deleting all rows";
const char * STR_IMPOSSIBLE_WHERE= "Impossible WHERE"; const char * STR_IMPOSSIBLE_WHERE= "Impossible WHERE";
...@@ -543,6 +544,7 @@ void Explain_union::print_explain_json(Explain_query *query, ...@@ -543,6 +544,7 @@ void Explain_union::print_explain_json(Explain_query *query,
writer->add_null(); writer->add_null();
} }
print_explain_json_cache(writer, is_analyze);
writer->add_member("query_specifications").start_array(); writer->add_member("query_specifications").start_array();
for (int i= 0; i < (int) union_members.elements(); i++) for (int i= 0; i < (int) union_members.elements(); i++)
...@@ -640,6 +642,30 @@ void Explain_node::print_explain_json_for_children(Explain_query *query, ...@@ -640,6 +642,30 @@ void Explain_node::print_explain_json_for_children(Explain_query *query,
} }
void Explain_node::print_explain_json_cache(Json_writer *writer,
bool is_analyze)
{
if (cache_stat)
{
cache_stat->flush_stat();
writer->add_member("expression_cache").start_object();
writer->add_member("state").
add_str(Expression_cache_stat::state_str[cache_stat->state]);
if (is_analyze)
{
writer->add_member("r_hit").add_ll(cache_stat->hit);
writer->add_member("r_miss").add_ll(cache_stat->miss);
writer->add_member("r_loops").add_ll(cache_stat->hit +
cache_stat->miss);
writer->add_member("r_hit_ratio").add_ll(((double)cache_stat->hit)/
((double)(cache_stat->hit +
cache_stat->miss)) * 100.0);
}
writer->end_object();
}
}
void Explain_select::replace_table(uint idx, Explain_table_access *new_tab) void Explain_select::replace_table(uint idx, Explain_table_access *new_tab)
{ {
delete join_tabs[idx]; delete join_tabs[idx];
...@@ -757,17 +783,19 @@ void Explain_select::print_explain_json(Explain_query *query, ...@@ -757,17 +783,19 @@ void Explain_select::print_explain_json(Explain_query *query,
{ {
writer->add_member("query_block").start_object(); writer->add_member("query_block").start_object();
writer->add_member("select_id").add_ll(select_id); writer->add_member("select_id").add_ll(select_id);
if (is_analyze && time_tracker.get_loops()) if (is_analyze && time_tracker.get_loops())
{ {
writer->add_member("r_loops").add_ll(time_tracker.get_loops()); writer->add_member("r_loops").add_ll(time_tracker.get_loops());
writer->add_member("r_total_time_ms").add_double(time_tracker.get_time_ms()); writer->add_member("r_total_time_ms").add_double(time_tracker.get_time_ms());
} }
if (exec_const_cond) if (exec_const_cond)
{ {
writer->add_member("const_condition"); writer->add_member("const_condition");
write_item(writer, exec_const_cond); write_item(writer, exec_const_cond);
} }
print_explain_json_cache(writer, is_analyze);
Filesort_tracker *first_table_sort= NULL; Filesort_tracker *first_table_sort= NULL;
bool first_table_sort_used= false; bool first_table_sort_used= false;
......
...@@ -84,9 +84,10 @@ class Explain_query; ...@@ -84,9 +84,10 @@ class Explain_query;
class Explain_node : public Sql_alloc class Explain_node : public Sql_alloc
{ {
public: public:
Explain_node(MEM_ROOT *root) : Explain_node(MEM_ROOT *root) :
connection_type(EXPLAIN_NODE_OTHER), cache_stat(NULL),
children(root) connection_type(EXPLAIN_NODE_OTHER),
children(root)
{} {}
/* A type specifying what kind of node this is */ /* A type specifying what kind of node this is */
enum explain_node_type enum explain_node_type
...@@ -106,10 +107,14 @@ public: ...@@ -106,10 +107,14 @@ public:
EXPLAIN_NODE_NON_MERGED_SJ /* aka JTBM semi-join */ EXPLAIN_NODE_NON_MERGED_SJ /* aka JTBM semi-join */
}; };
virtual enum explain_node_type get_type()= 0; virtual enum explain_node_type get_type()= 0;
virtual int get_select_id()= 0; virtual int get_select_id()= 0;
/**
expression cache statistics
*/
Expression_cache_stat* cache_stat;
/* /*
How this node is connected to its parent. How this node is connected to its parent.
(NOTE: EXPLAIN_NODE_NON_MERGED_SJ is set very late currently) (NOTE: EXPLAIN_NODE_NON_MERGED_SJ is set very late currently)
...@@ -135,6 +140,7 @@ public: ...@@ -135,6 +140,7 @@ public:
uint8 explain_flags, bool is_analyze); uint8 explain_flags, bool is_analyze);
void print_explain_json_for_children(Explain_query *query, void print_explain_json_for_children(Explain_query *query,
Json_writer *writer, bool is_analyze); Json_writer *writer, bool is_analyze);
void print_explain_json_cache(Json_writer *writer, bool is_analyze);
virtual ~Explain_node(){} virtual ~Explain_node(){}
}; };
...@@ -221,7 +227,7 @@ public: ...@@ -221,7 +227,7 @@ public:
members have no info members have no info
*/ */
const char *message; const char *message;
/* Expensive constant condition */ /* Expensive constant condition */
Item *exec_const_cond; Item *exec_const_cond;
......
...@@ -43,7 +43,7 @@ ulong subquery_cache_miss, subquery_cache_hit; ...@@ -43,7 +43,7 @@ ulong subquery_cache_miss, subquery_cache_hit;
Expression_cache_tmptable::Expression_cache_tmptable(THD *thd, Expression_cache_tmptable::Expression_cache_tmptable(THD *thd,
List<Item> &dependants, List<Item> &dependants,
Item *value) Item *value)
:cache_table(NULL), table_thd(thd), items(dependants), val(value), :cache_table(NULL), table_thd(thd), stat(NULL), items(dependants), val(value),
hit(0), miss(0), inited (0) hit(0), miss(0), inited (0)
{ {
DBUG_ENTER("Expression_cache_tmptable::Expression_cache_tmptable"); DBUG_ENTER("Expression_cache_tmptable::Expression_cache_tmptable");
...@@ -61,6 +61,9 @@ void Expression_cache_tmptable::disable_cache() ...@@ -61,6 +61,9 @@ void Expression_cache_tmptable::disable_cache()
cache_table->file->ha_index_end(); cache_table->file->ha_index_end();
free_tmp_table(table_thd, cache_table); free_tmp_table(table_thd, cache_table);
cache_table= NULL; cache_table= NULL;
flush_stat();
if (stat)
stat->cache= NULL;
} }
...@@ -164,6 +167,7 @@ void Expression_cache_tmptable::init() ...@@ -164,6 +167,7 @@ void Expression_cache_tmptable::init()
goto error; goto error;
} }
flush_stat();
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
error: error:
...@@ -180,6 +184,11 @@ Expression_cache_tmptable::~Expression_cache_tmptable() ...@@ -180,6 +184,11 @@ Expression_cache_tmptable::~Expression_cache_tmptable()
if (cache_table) if (cache_table)
disable_cache(); disable_cache();
else
{
flush_stat();
stat= NULL;
}
} }
...@@ -323,3 +332,7 @@ void Expression_cache_tmptable::print(String *str, enum_query_type query_type) ...@@ -323,3 +332,7 @@ void Expression_cache_tmptable::print(String *str, enum_query_type query_type)
} }
str->append('>'); str->append('>');
} }
const char *Expression_cache_stat::state_str[3]=
{"UNINITIALYZED", "DISABLED", "ENABLED"};
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "sql_select.h" #include "sql_select.h"
/** /**
Interface for expression cache Interface for expression cache
...@@ -62,6 +63,11 @@ public: ...@@ -62,6 +63,11 @@ public:
Initialize this cache Initialize this cache
*/ */
virtual void init()= 0; virtual void init()= 0;
/**
Save this object's statistics into Expression_cache_stat object
*/
virtual void flush_stat()= 0;
}; };
struct st_table_ref; struct st_table_ref;
...@@ -69,6 +75,30 @@ struct st_join_table; ...@@ -69,6 +75,30 @@ struct st_join_table;
class Item_field; class Item_field;
class Expression_cache_stat :public Sql_alloc
{
public:
enum expr_cache_state {UNINITED, STOPPED, OK};
Expression_cache_stat(Expression_cache *c) :
cache(c), hit(0), miss(0), state(UNINITED)
{}
Expression_cache *cache;
ulong hit, miss;
enum expr_cache_state state;
static const char* state_str[3];
void set(ulong h, ulong m, enum expr_cache_state s)
{hit= h; miss= m; state= s;}
void flush_stat()
{
if (cache)
cache->flush_stat();
}
};
/** /**
Implementation of expression cache over a temporary table Implementation of expression cache over a temporary table
*/ */
...@@ -85,6 +115,20 @@ public: ...@@ -85,6 +115,20 @@ public:
bool is_inited() { return inited; }; bool is_inited() { return inited; };
void init(); void init();
void set_stat(Expression_cache_stat *st)
{
stat= st;
flush_stat();
}
virtual void flush_stat()
{
if (stat)
stat->set(hit, miss, (inited ? (cache_table ?
Expression_cache_stat::OK :
Expression_cache_stat::STOPPED) :
Expression_cache_stat::UNINITED));
}
private: private:
void disable_cache(); void disable_cache();
...@@ -94,6 +138,8 @@ private: ...@@ -94,6 +138,8 @@ private:
TABLE *cache_table; TABLE *cache_table;
/* Thread handle for the temporary table */ /* Thread handle for the temporary table */
THD *table_thd; THD *table_thd;
/* EXPALIN/ANALYZE statistics */
Expression_cache_stat *stat;
/* TABLE_REF for index lookup */ /* TABLE_REF for index lookup */
struct st_table_ref ref; struct st_table_ref ref;
/* Cached result */ /* Cached result */
...@@ -103,7 +149,7 @@ private: ...@@ -103,7 +149,7 @@ private:
/* Value Item example */ /* Value Item example */
Item *val; Item *val;
/* hit/miss counters */ /* hit/miss counters */
uint hit, miss; ulong hit, miss;
/* Set on if the object has been succesfully initialized with init() */ /* Set on if the object has been succesfully initialized with init() */
bool inited; bool inited;
}; };
......
...@@ -3505,6 +3505,8 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only) ...@@ -3505,6 +3505,8 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
bool empty_union_result= true; bool empty_union_result= true;
bool is_correlated_unit= false; bool is_correlated_unit= false;
bool first= true;
bool union_plan_saved= false;
/* /*
If the subquery is a UNION, optimize all the subqueries in the UNION. If If the subquery is a UNION, optimize all the subqueries in the UNION. If
there is no UNION, then the loop will execute once for the subquery. there is no UNION, then the loop will execute once for the subquery.
...@@ -3512,6 +3514,17 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only) ...@@ -3512,6 +3514,17 @@ bool st_select_lex::optimize_unflattened_subqueries(bool const_only)
for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select()) for (SELECT_LEX *sl= un->first_select(); sl; sl= sl->next_select())
{ {
JOIN *inner_join= sl->join; JOIN *inner_join= sl->join;
if (first)
first= false;
else
{
if (!union_plan_saved)
{
union_plan_saved= true;
if (un->save_union_explain(un->thd->lex->explain))
return true; /* Failure */
}
}
if (!inner_join) if (!inner_join)
continue; continue;
SELECT_LEX *save_select= un->thd->lex->current_select; SELECT_LEX *save_select= un->thd->lex->current_select;
...@@ -4365,10 +4378,15 @@ void LEX::restore_set_statement_var() ...@@ -4365,10 +4378,15 @@ void LEX::restore_set_statement_var()
int st_select_lex_unit::save_union_explain(Explain_query *output) int st_select_lex_unit::save_union_explain(Explain_query *output)
{ {
SELECT_LEX *first= first_select(); SELECT_LEX *first= first_select();
if (output->get_union(first->select_number))
return 0; /* Already added */
Explain_union *eu= Explain_union *eu=
new (output->mem_root) Explain_union(output->mem_root, new (output->mem_root) Explain_union(output->mem_root,
thd->lex->analyze_stmt); thd->lex->analyze_stmt);
if (derived) if (derived)
eu->connection_type= Explain_node::EXPLAIN_NODE_DERIVED; eu->connection_type= Explain_node::EXPLAIN_NODE_DERIVED;
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment