summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOleksandr Byelkin <sanja@mariadb.com>2015-04-02 18:19:33 +0200
committerOleksandr Byelkin <sanja@mariadb.com>2015-04-03 15:37:27 +0200
commita220905083c382bec096dd1435b6620d348bbfeb (patch)
tree15cb6f7d3d7bd05d47568662cb74641d073675f6
parent7f613ebdb6865e2d1304d20977764b523f6a5080 (diff)
downloadmariadb-git-a220905083c382bec096dd1435b6620d348bbfeb.tar.gz
MDEV-7833:ANALYZE FORMAT=JSON and Range checked for each record
-rw-r--r--mysql-test/r/analyze_format_json.result64
-rw-r--r--mysql-test/t/analyze_format_json.test30
-rw-r--r--sql/sql_explain.cc106
-rw-r--r--sql/sql_explain.h20
-rw-r--r--sql/sql_select.cc20
-rw-r--r--sql/sql_select.h7
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