summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorSergei Petrunia <psergey@askmonty.org>2014-08-14 01:12:05 +0400
committerSergei Petrunia <psergey@askmonty.org>2014-08-14 01:12:05 +0400
commit041e03e251e783d51ca86e53112e3b87bd2da146 (patch)
tree8df2309ea02720724bff15e5af86903e4bb95dd4 /sql
parenta9d43d70f5d83ac652fd970f5b2b8dfdb77c1136 (diff)
downloadmariadb-git-041e03e251e783d51ca86e53112e3b87bd2da146.tar.gz
EXPLAIN FORMAT=JSON: produce used_key_parts, JSON-ish output for index_merge.
Diffstat (limited to 'sql')
-rw-r--r--sql/opt_range.cc4
-rw-r--r--sql/sql_explain.cc122
-rw-r--r--sql/sql_explain.h49
-rw-r--r--sql/sql_select.cc10
4 files changed, 146 insertions, 39 deletions
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index fc2aa75e604..88d8b0551cb 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -12105,7 +12105,7 @@ Explain_quick_select* QUICK_RANGE_SELECT::get_explain(MEM_ROOT *alloc)
{
Explain_quick_select *res;
if ((res= new (alloc) Explain_quick_select(QS_TYPE_RANGE)))
- res->range.set(alloc, head->key_info[index].name, max_used_key_length);
+ res->range.set(alloc, &head->key_info[index], max_used_key_length);
return res;
}
@@ -12114,7 +12114,7 @@ Explain_quick_select* QUICK_GROUP_MIN_MAX_SELECT::get_explain(MEM_ROOT *alloc)
{
Explain_quick_select *res;
if ((res= new (alloc) Explain_quick_select(QS_TYPE_GROUP_MIN_MAX)))
- res->range.set(alloc, head->key_info[index].name, max_used_key_length);
+ res->range.set(alloc, &head->key_info[index], max_used_key_length);
return res;
}
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
index f7fc3b106ce..9571e5a2886 100644
--- a/sql/sql_explain.cc
+++ b/sql/sql_explain.cc
@@ -541,7 +541,7 @@ void Explain_table_access::push_extra(enum explain_extra_tag extra_tag)
}
-void Explain_table_access::fill_key_str(String *key_str)
+void Explain_table_access::fill_key_str(String *key_str, bool is_json)
{
const CHARSET_INFO *cs= system_charset_info;
bool is_hj= (type == JT_HASH || type == JT_HASH_NEXT ||
@@ -562,7 +562,10 @@ void Explain_table_access::fill_key_str(String *key_str)
if (quick_info)
{
StringBuffer<64> buf2;
- quick_info->print_key(&buf2);
+ if (is_json)
+ quick_info->print_extra_recursive(&buf2);
+ else
+ quick_info->print_key(&buf2);
key_str->append(buf2);
}
if (type == JT_HASH_NEXT)
@@ -570,6 +573,16 @@ void Explain_table_access::fill_key_str(String *key_str)
}
+/*
+ Fill "key_length".
+ - this is just used key length for ref/range
+ - for index_merge, it is a comma-separated list of lengths.
+ - for hash join, it is key_len:pseudo_key_len
+
+ The column looks identical in tabular and json forms. In JSON, we consider
+ the column legacy, it is superceded by used_key_parts.
+*/
+
void Explain_table_access::fill_key_len_str(String *key_len_str)
{
bool is_hj= (type == JT_HASH || type == JT_HASH_NEXT ||
@@ -601,6 +614,35 @@ void Explain_table_access::fill_key_len_str(String *key_len_str)
}
+void Explain_index_use::set(MEM_ROOT *mem_root, KEY *key, uint key_len_arg)
+{
+ set_pseudo_key(mem_root, key->name);
+ key_len= key_len_arg;
+ uint len= 0;
+ for (uint i= 0; i < key->usable_key_parts; i++)
+ {
+ key_parts_list.append_str(mem_root, key->key_part[i].field->field_name);
+ len += key->key_part[i].store_length;
+ if (len >= key_len_arg)
+ break;
+ }
+}
+
+
+void Explain_index_use::set_pseudo_key(MEM_ROOT *root, const char* key_name_arg)
+{
+ if (key_name_arg)
+ {
+ size_t name_len= strlen(key_name_arg);
+ if ((key_name= (char*)alloc_root(root, name_len+1)))
+ memcpy(key_name, key_name_arg, name_len+1);
+ }
+ else
+ key_name= NULL;
+ key_len= -1;
+}
+
+
double Explain_table_access::get_r_filtered()
{
//psergey-todo: modify this to produce separate filtered% for both parts of
@@ -660,7 +702,7 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
/* `key` */
StringBuffer<64> key_str;
- fill_key_str(&key_str);
+ fill_key_str(&key_str, false);
if (key_str.length() > 0)
push_string(&item_list, &key_str);
@@ -861,24 +903,51 @@ void Explain_table_access::print_explain_json(Json_writer *writer,
writer->add_str(name);
writer->end_array();
}
+
/* `key` */
- StringBuffer<64> key_str;
- fill_key_str(&key_str);
- if (key_str.length())
- writer->add_member("key").add_str(key_str);
+ /* For non-basic quick select, 'key' will not be present */
+ if (!quick_info || quick_info->is_basic())
+ {
+ StringBuffer<64> key_str;
+ fill_key_str(&key_str, true);
+ if (key_str.length())
+ writer->add_member("key").add_str(key_str);
+ }
- /* `used_key_parts` */
- if (key_str.length())
- writer->add_member("used_key_parts").add_str("TODO");
-
/* `key_length` */
StringBuffer<64> key_len_str;
fill_key_len_str(&key_len_str);
if (key_len_str.length())
writer->add_member("key_length").add_str(key_len_str);
+
+ /* `used_key_parts` */
+ String_list *parts_list= NULL;
+ if (quick_info && quick_info->is_basic())
+ parts_list= &quick_info->range.key_parts_list;
+ else
+ parts_list= &key.key_parts_list;
+
+ if (parts_list && !parts_list->is_empty())
+ {
+ List_iterator_fast<char> it(*parts_list);
+ const char *name;
+ writer->add_member("used_key_parts").start_array();
+ while ((name= it++))
+ writer->add_str(name);
+ writer->end_array();
+ }
+
+ if (quick_info && !quick_info->is_basic())
+ {
+ writer->add_member("index_merge").start_object();
+ quick_info->print_json(writer);
+ writer->end_object();
+ }
+
+
+ // TODO: here, if quick select is not basic, print its nested form.
/* `ref` */
- // TODO: need to print this as an array.
if (!ref_list.is_empty())
{
List_iterator_fast<char> it(ref_list);
@@ -1046,6 +1115,35 @@ void Explain_quick_select::print_extra(String *str)
print_extra_recursive(str);
}
+void Explain_quick_select::print_json(Json_writer *writer)
+{
+ if (is_basic())
+ {
+ writer->add_member("range").start_object();
+
+ writer->add_member("key").add_str(range.get_key_name());
+
+ List_iterator_fast<char> it(range.key_parts_list);
+ const char *name;
+ writer->add_member("used_key_parts").start_array();
+ while ((name= it++))
+ writer->add_str(name);
+ writer->end_array();
+
+ writer->end_object();
+ }
+ else
+ {
+ writer->add_member(get_name_by_type()).start_object();
+
+ List_iterator_fast<Explain_quick_select> it (children);
+ Explain_quick_select* child;
+ while ((child = it++))
+ child->print_json(writer);
+
+ writer->end_object();
+ }
+}
void Explain_quick_select::print_extra_recursive(String *str)
{
diff --git a/sql/sql_explain.h b/sql/sql_explain.h
index 26aa5753900..2500b3d3c05 100644
--- a/sql/sql_explain.h
+++ b/sql/sql_explain.h
@@ -14,6 +14,15 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+class String_list: public List<char>
+{
+public:
+ bool append_str(MEM_ROOT *mem_root, const char *str);
+};
+
+
+
/* Data structures for ANALYZE */
class Table_access_tracker
{
@@ -417,21 +426,16 @@ class Explain_index_use : public Sql_alloc
{
char *key_name;
uint key_len;
- /* will add #keyparts here if we implement EXPLAIN FORMAT=JSON */
public:
-
- void set(MEM_ROOT *root, const char *key_name_arg, uint key_len_arg)
+ String_list key_parts_list;
+
+ void clear()
{
- if (key_name_arg)
- {
- size_t name_len= strlen(key_name_arg);
- if ((key_name= (char*)alloc_root(root, name_len+1)))
- memcpy(key_name, key_name_arg, name_len+1);
- }
- else
- key_name= NULL;
- key_len= key_len_arg;
+ key_name= NULL;
+ key_len= (uint)-1;
}
+ void set(MEM_ROOT *root, KEY *key_name, uint key_len_arg);
+ void set_pseudo_key(MEM_ROOT *root, const char *key_name);
inline const char *get_key_name() { return key_name; }
inline uint get_key_len() { return key_len; }
@@ -448,6 +452,13 @@ public:
{}
const int quick_type;
+
+ bool is_basic()
+ {
+ return (quick_type == QUICK_SELECT_I::QS_TYPE_RANGE ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_RANGE_DESC ||
+ quick_type == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX);
+ }
/* This is used when quick_type == QUICK_SELECT_I::QS_TYPE_RANGE */
Explain_index_use range;
@@ -458,16 +469,12 @@ public:
void print_extra(String *str);
void print_key(String *str);
void print_key_len(String *str);
-private:
- void print_extra_recursive(String *str);
- const char *get_name_by_type();
-};
+ void print_json(Json_writer *writer);
-class String_list: public List<char>
-{
-public:
- bool append_str(MEM_ROOT *mem_root, const char *str);
+ void print_extra_recursive(String *str);
+private:
+ const char *get_name_by_type();
};
@@ -565,7 +572,7 @@ public:
private:
void append_tag_name(String *str, enum explain_extra_tag tag);
- void fill_key_str(String *key_str);
+ void fill_key_str(String *key_str, bool is_json);
void fill_key_len_str(String *key_len_str);
double get_r_filtered();
void tag_to_json(Json_writer *writer, enum explain_extra_tag tag);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 213a7eae647..8ef3f64016e 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -23310,7 +23310,7 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab
quick_type= -1;
QUICK_SELECT_I *quick= NULL;
- eta->key.set(thd->mem_root, NULL, (uint)-1);
+ eta->key.clear();
eta->quick_info= NULL;
tab->tracker= &eta->tracker;
@@ -23437,7 +23437,7 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab
if (key_info) /* 'index' or 'ref' access */
{
- eta->key.set(thd->mem_root, key_info->name, key_len);
+ eta->key.set(thd->mem_root, key_info, key_len);
if (tab->ref.key_parts && tab_type != JT_FT)
{
@@ -23458,8 +23458,10 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab
if (tab_type == JT_HASH_NEXT) /* full index scan + hash join */
{
eta->hash_next_key.set(thd->mem_root,
- table->key_info[tab->index].name,
+ & table->key_info[tab->index],
table->key_info[tab->index].key_length);
+ // psergey-todo: ^ is the above correct? are we necessarily joining on all
+ // columns?
}
if (!key_info)
@@ -23490,7 +23492,7 @@ void JOIN_TAB::save_explain_data(Explain_table_access *eta, table_map prefix_tab
}
if (key_name_buf.length())
- eta->key.set(thd->mem_root, key_name_buf.c_ptr_safe(), -1);
+ eta->key.set_pseudo_key(thd->mem_root, key_name_buf.c_ptr_safe());
}
}