diff options
author | Oleksandr Byelkin <sanja@mariadb.com> | 2015-04-02 18:19:33 +0200 |
---|---|---|
committer | Oleksandr Byelkin <sanja@mariadb.com> | 2015-04-03 15:37:27 +0200 |
commit | a220905083c382bec096dd1435b6620d348bbfeb (patch) | |
tree | 15cb6f7d3d7bd05d47568662cb74641d073675f6 | |
parent | 7f613ebdb6865e2d1304d20977764b523f6a5080 (diff) | |
download | mariadb-git-a220905083c382bec096dd1435b6620d348bbfeb.tar.gz |
MDEV-7833:ANALYZE FORMAT=JSON and Range checked for each record
-rw-r--r-- | mysql-test/r/analyze_format_json.result | 64 | ||||
-rw-r--r-- | mysql-test/t/analyze_format_json.test | 30 | ||||
-rw-r--r-- | sql/sql_explain.cc | 106 | ||||
-rw-r--r-- | sql/sql_explain.h | 20 | ||||
-rw-r--r-- | sql/sql_select.cc | 20 | ||||
-rw-r--r-- | sql/sql_select.h | 7 |
6 files changed, 234 insertions, 13 deletions
diff --git a/mysql-test/r/analyze_format_json.result b/mysql-test/r/analyze_format_json.result index 8b6cc437014..4d3f6752b7c 100644 --- a/mysql-test/r/analyze_format_json.result +++ b/mysql-test/r/analyze_format_json.result @@ -341,3 +341,67 @@ ANALYZE } } 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; diff --git a/mysql-test/t/analyze_format_json.test b/mysql-test/t/analyze_format_json.test index 473c9b3d008..8d838d0eee2 100644 --- a/mysql-test/t/analyze_format_json.test +++ b/mysql-test/t/analyze_format_json.test @@ -104,3 +104,33 @@ analyze format=json delete from t1 where pk < 10 and b > 4; 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; diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index 6c353185cb1..c1f1b0218b5 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -22,6 +22,7 @@ #include "sql_priv.h" #include "sql_select.h" #include "my_json_writer.h" +#include "opt_range.h" const char * STR_DELETING_ALL_ROWS= "Deleting all rows"; 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); char *cp; if (!(cp = (char*)alloc_root(mem_root, len+1))) - return 1; + return NULL; memcpy(cp, str, len+1); 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) { - writer->add_member("range-checked-for-each-record").start_object(); - add_json_keyset(writer, "keys", &possible_keys); + range_checked_fer->print_json(writer, is_analyze); } 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); } + +/** + 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(); + } +} diff --git a/sql/sql_explain.h b/sql/sql_explain.h index 65027bdc907..97a04573361 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -54,7 +54,7 @@ it into the slow query log. class String_list: public List<char> { 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; @@ -622,11 +622,29 @@ private: 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 { public: String_list key_set; 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); }; /* diff --git a/sql/sql_select.cc b/sql/sql_select.cc index efa6cdaae20..c087732bab1 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -11398,6 +11398,7 @@ void JOIN_TAB::cleanup() table->reginfo.join_tab= 0; } end_read_record(&read_record); + explain_plan= NULL; DBUG_VOID_RETURN; } @@ -18762,9 +18763,13 @@ test_if_quick_select(JOIN_TAB *tab) delete tab->select->quick; tab->select->quick=0; - return tab->select->test_quick_select(tab->join->thd, tab->keys, - (table_map) 0, HA_POS_ERROR, 0, - FALSE, /*remove where parts*/FALSE); + int res= tab->select->test_quick_select(tab->join->thd, tab->keys, + (table_map) 0, HA_POS_ERROR, 0, + 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; } + /* TODO: this function is only applicable for the first non-const optimization join tab. @@ -23427,6 +23433,8 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab quick_type= -1; QUICK_SELECT_I *quick= NULL; + + explain_plan= eta; eta->key.clear(); 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->range_checked_fer= new (thd->mem_root) Explain_range_checked_fer; - eta->range_checked_fer->keys_map= tab->keys; - append_possible_keys(thd->mem_root, eta->range_checked_fer->key_set, - table, tab->keys); + if (eta->range_checked_fer) + eta->range_checked_fer-> + append_possible_keys_stat(thd->mem_root, table, tab->keys); } else if (tab->select->cond || (tab->cache_select && tab->cache_select->cond)) diff --git a/sql/sql_select.h b/sql/sql_select.h index 94ca52471e0..f70f30eba83 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -362,7 +362,12 @@ typedef struct st_join_table { SJ_TMP_TABLE *check_weed_out_table; /* for EXPLAIN only: */ 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 match and return to the specified join tab. May point to |