Commit a2209050 authored by Oleksandr Byelkin's avatar Oleksandr Byelkin

MDEV-7833:ANALYZE FORMAT=JSON and Range checked for each record

parent 7f613ebd
...@@ -341,3 +341,67 @@ ANALYZE ...@@ -341,3 +341,67 @@ ANALYZE
} }
} }
drop table t1, t3, t2; drop table t1, t3, t2;
#
# MDEV-7833:ANALYZE FORMAT=JSON and Range checked for each record
#
create table t3(a int);
insert into t3 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t4(a int);
insert into t4 select A.a + B.a* 10 + C.a * 100 from t3 A, t3 B, t3 C;
create table t1 (lb1 int, rb1 int, lb2 int, rb2 int, c1 int, c2 int);
insert into t1 values (1,2,10,20,15,15);
insert into t1 values (3,5,10,20,15,15);
insert into t1 values (10,20,10,20,15,15);
insert into t1 values (10,20,1,2,15,15);
insert into t1 values (10,20,10,20,1,3);
create table t2 (key1 int, key2 int, key3 int, key4 int, col1 int,
key(key1), key(key2), key(key3), key(key4));
insert into t2 select a,a,a,a,a from t3;
insert into t2 select 15,15,15,15,15 from t4;
analyze format=json
select * from t1, t2 where (t2.key1 between t1.lb1 and t1.rb1) and
(t2.key2 between t1.lb2 and t1.rb2) and
(t2.key3=t1.c1 OR t2.key4=t1.c2);
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": 5,
"r_rows": 5,
"r_total_time_ms": "REPLACED",
"filtered": 100,
"r_filtered": 100
},
"range-checked-for-each-record": {
"keys": ["key1", "key2", "key3", "key4"],
"r_keys": {
"full_scan": 1,
"index_merge": 1,
"range": {
"key1": 2,
"key2": 1,
"key3": 0,
"key4": 0
}
},
"table": {
"table_name": "t2",
"access_type": "ALL",
"possible_keys": ["key1", "key2", "key3", "key4"],
"r_loops": 5,
"rows": 1010,
"r_rows": 203.8,
"r_total_time_ms": "REPLACED",
"filtered": 100,
"r_filtered": 98.135
}
}
}
}
drop table t1,t2,t3,t4;
...@@ -104,3 +104,33 @@ analyze format=json ...@@ -104,3 +104,33 @@ analyze format=json
delete from t1 where pk < 10 and b > 4; delete from t1 where pk < 10 and b > 4;
drop table t1, t3, t2; drop table t1, t3, t2;
--echo #
--echo # MDEV-7833:ANALYZE FORMAT=JSON and Range checked for each record
--echo #
create table t3(a int);
insert into t3 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t4(a int);
insert into t4 select A.a + B.a* 10 + C.a * 100 from t3 A, t3 B, t3 C;
create table t1 (lb1 int, rb1 int, lb2 int, rb2 int, c1 int, c2 int);
insert into t1 values (1,2,10,20,15,15);
insert into t1 values (3,5,10,20,15,15);
insert into t1 values (10,20,10,20,15,15);
insert into t1 values (10,20,1,2,15,15);
insert into t1 values (10,20,10,20,1,3);
create table t2 (key1 int, key2 int, key3 int, key4 int, col1 int,
key(key1), key(key2), key(key3), key(key4));
insert into t2 select a,a,a,a,a from t3;
insert into t2 select 15,15,15,15,15 from t4;
--replace_regex /"r_total_time_ms": [0-9]*[.]?[0-9]*/"r_total_time_ms": "REPLACED"/
analyze format=json
select * from t1, t2 where (t2.key1 between t1.lb1 and t1.rb1) and
(t2.key2 between t1.lb2 and t1.rb2) and
(t2.key3=t1.c1 OR t2.key4=t1.c2);
drop table t1,t2,t3,t4;
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "sql_priv.h" #include "sql_priv.h"
#include "sql_select.h" #include "sql_select.h"
#include "my_json_writer.h" #include "my_json_writer.h"
#include "opt_range.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";
...@@ -1076,15 +1077,26 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai ...@@ -1076,15 +1077,26 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
} }
bool String_list::append_str(MEM_ROOT *mem_root, const char *str) /**
Adds copy of the string to the list
@param mem_root where to allocate string
@param str string to copy and add
@return
NULL - out of memory error
poiner on allocated copy of the string
*/
const char *String_list::append_str(MEM_ROOT *mem_root, const char *str)
{ {
size_t len= strlen(str); size_t len= strlen(str);
char *cp; char *cp;
if (!(cp = (char*)alloc_root(mem_root, len+1))) if (!(cp = (char*)alloc_root(mem_root, len+1)))
return 1; return NULL;
memcpy(cp, str, len+1); memcpy(cp, str, len+1);
push_back(cp); push_back(cp);
return 0; return cp;
} }
...@@ -1207,8 +1219,7 @@ void Explain_table_access::print_explain_json(Explain_query *query, ...@@ -1207,8 +1219,7 @@ void Explain_table_access::print_explain_json(Explain_query *query,
if (range_checked_fer) if (range_checked_fer)
{ {
writer->add_member("range-checked-for-each-record").start_object(); range_checked_fer->print_json(writer, is_analyze);
add_json_keyset(writer, "keys", &possible_keys);
} }
if (full_scan_on_null_key) if (full_scan_on_null_key)
...@@ -1986,3 +1997,88 @@ void create_explain_query_if_not_exists(LEX *lex, MEM_ROOT *mem_root) ...@@ -1986,3 +1997,88 @@ void create_explain_query_if_not_exists(LEX *lex, MEM_ROOT *mem_root)
create_explain_query(lex, mem_root); create_explain_query(lex, mem_root);
} }
/**
Build arrays for collectiong keys statistics, sdd possible key names
to the list and name array
@param alloc MEM_ROOT to put data in
@param list list of possible key names to fill
@param table table of the keys
@patam possible_keys possible keys map
@retval 0 - OK
@retval 1 - Error
*/
int Explain_range_checked_fer::append_possible_keys_stat(MEM_ROOT *alloc,
TABLE *table,
key_map possible_keys)
{
uint j;
multi_alloc_root(alloc, &keys_stat, sizeof(ha_rows) * table->s->keys,
&keys_stat_names, sizeof(char *) * table->s->keys, NULL);
if ((!keys_stat) || (!keys_stat_names))
{
keys_stat= NULL;
keys_stat_names= NULL;
return 1;
}
keys_map= possible_keys;
keys= table->s->keys;
bzero(keys_stat, sizeof(ha_rows) * table->s->keys);
for (j= 0; j < table->s->keys; j++)
{
if (possible_keys.is_set(j))
keys_stat_names[j]= key_set.append_str(alloc, table->key_info[j].name);
else
keys_stat_names[j]= NULL;
}
return 0;
}
void Explain_range_checked_fer::collect_data(QUICK_SELECT_I *quick)
{
if (quick)
{
if (quick->index == MAX_KEY)
index_merge++;
else
{
DBUG_ASSERT(quick->index < keys);
DBUG_ASSERT(keys_stat);
DBUG_ASSERT(keys_stat_names);
DBUG_ASSERT(keys_stat_names[ quick->index]);
keys_stat[quick->index]++;
}
}
else
full_scan++;
}
void Explain_range_checked_fer::print_json(Json_writer *writer,
bool is_analyze)
{
writer->add_member("range-checked-for-each-record").start_object();
add_json_keyset(writer, "keys", &key_set);
if (is_analyze)
{
writer->add_member("r_keys").start_object();
writer->add_member("full_scan").add_ll(full_scan);
writer->add_member("index_merge").add_ll(index_merge);
if (keys_stat)
{
writer->add_member("range").start_object();
for (uint i= 0; i < keys; i++)
{
if (keys_stat_names[i])
{
writer->add_member(keys_stat_names[i]).add_ll(keys_stat[i]);
}
}
writer->end_object();
}
writer->end_object();
}
}
...@@ -54,7 +54,7 @@ it into the slow query log. ...@@ -54,7 +54,7 @@ it into the slow query log.
class String_list: public List<char> class String_list: public List<char>
{ {
public: public:
bool append_str(MEM_ROOT *mem_root, const char *str); const char *append_str(MEM_ROOT *mem_root, const char *str);
}; };
class Json_writer; class Json_writer;
...@@ -622,11 +622,29 @@ class Explain_quick_select : public Sql_alloc ...@@ -622,11 +622,29 @@ class Explain_quick_select : public Sql_alloc
It's a set of keys, tabular explain prints hex bitmap, json prints key names. It's a set of keys, tabular explain prints hex bitmap, json prints key names.
*/ */
typedef const char* NAME;
class Explain_range_checked_fer : public Sql_alloc class Explain_range_checked_fer : public Sql_alloc
{ {
public: public:
String_list key_set; String_list key_set;
key_map keys_map; key_map keys_map;
private:
ha_rows full_scan, index_merge;
ha_rows *keys_stat;
NAME *keys_stat_names;
uint keys;
public:
Explain_range_checked_fer()
:Sql_alloc(), full_scan(0), index_merge(0),
keys_stat(0), keys_stat_names(0), keys(0)
{}
int append_possible_keys_stat(MEM_ROOT *alloc,
TABLE *table, key_map possible_keys);
void collect_data(QUICK_SELECT_I *quick);
void print_json(Json_writer *writer, bool is_analyze);
}; };
/* /*
......
...@@ -11398,6 +11398,7 @@ void JOIN_TAB::cleanup() ...@@ -11398,6 +11398,7 @@ void JOIN_TAB::cleanup()
table->reginfo.join_tab= 0; table->reginfo.join_tab= 0;
} }
end_read_record(&read_record); end_read_record(&read_record);
explain_plan= NULL;
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -18762,9 +18763,13 @@ test_if_quick_select(JOIN_TAB *tab) ...@@ -18762,9 +18763,13 @@ test_if_quick_select(JOIN_TAB *tab)
delete tab->select->quick; delete tab->select->quick;
tab->select->quick=0; tab->select->quick=0;
return tab->select->test_quick_select(tab->join->thd, tab->keys, int res= tab->select->test_quick_select(tab->join->thd, tab->keys,
(table_map) 0, HA_POS_ERROR, 0, (table_map) 0, HA_POS_ERROR, 0,
FALSE, /*remove where parts*/FALSE); FALSE, /*remove where parts*/FALSE);
if (tab->explain_plan && tab->explain_plan->range_checked_fer)
tab->explain_plan->range_checked_fer->collect_data(tab->select->quick);
return res;
} }
...@@ -23387,6 +23392,7 @@ int append_possible_keys(MEM_ROOT *alloc, String_list &list, TABLE *table, ...@@ -23387,6 +23392,7 @@ int append_possible_keys(MEM_ROOT *alloc, String_list &list, TABLE *table,
return 0; return 0;
} }
/* /*
TODO: this function is only applicable for the first non-const optimization TODO: this function is only applicable for the first non-const optimization
join tab. join tab.
...@@ -23427,6 +23433,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab ...@@ -23427,6 +23433,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab
quick_type= -1; quick_type= -1;
QUICK_SELECT_I *quick= NULL; QUICK_SELECT_I *quick= NULL;
explain_plan= eta;
eta->key.clear(); eta->key.clear();
eta->quick_info= NULL; eta->quick_info= NULL;
...@@ -23690,9 +23698,9 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab ...@@ -23690,9 +23698,9 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab
{ {
eta->push_extra(ET_RANGE_CHECKED_FOR_EACH_RECORD); eta->push_extra(ET_RANGE_CHECKED_FOR_EACH_RECORD);
eta->range_checked_fer= new (thd->mem_root) Explain_range_checked_fer; eta->range_checked_fer= new (thd->mem_root) Explain_range_checked_fer;
eta->range_checked_fer->keys_map= tab->keys; if (eta->range_checked_fer)
append_possible_keys(thd->mem_root, eta->range_checked_fer->key_set, eta->range_checked_fer->
table, tab->keys); append_possible_keys_stat(thd->mem_root, table, tab->keys);
} }
else if (tab->select->cond || else if (tab->select->cond ||
(tab->cache_select && tab->cache_select->cond)) (tab->cache_select && tab->cache_select->cond))
......
...@@ -362,7 +362,12 @@ typedef struct st_join_table { ...@@ -362,7 +362,12 @@ typedef struct st_join_table {
SJ_TMP_TABLE *check_weed_out_table; SJ_TMP_TABLE *check_weed_out_table;
/* for EXPLAIN only: */ /* for EXPLAIN only: */
SJ_TMP_TABLE *first_weedout_table; SJ_TMP_TABLE *first_weedout_table;
/**
reference to saved plan and execution statistics
*/
Explain_table_access *explain_plan;
/* /*
If set, means we should stop join enumeration after we've got the first If set, means we should stop join enumeration after we've got the first
match and return to the specified join tab. May point to match and return to the specified join tab. May point to
......
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