mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
MDEV-7833:ANALYZE FORMAT=JSON and Range checked for each record
This commit is contained in:
@ -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
|
|||||||
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 @@
|
|||||||
#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
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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,
|
|||||||
|
|
||||||
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)
|
|||||||
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.
|
|||||||
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 @@ private:
|
|||||||
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()
|
|||||||
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)
|
|||||||
|
|
||||||
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,
|
|||||||
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
|
|||||||
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
|
|||||||
{
|
{
|
||||||
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 {
|
|||||||
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
|
||||||
|
Reference in New Issue
Block a user