summaryrefslogtreecommitdiff
path: root/sql/sql_explain.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_explain.cc')
-rw-r--r--sql/sql_explain.cc417
1 files changed, 331 insertions, 86 deletions
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
index 47581898e42..357e8f07664 100644
--- a/sql/sql_explain.cc
+++ b/sql/sql_explain.cc
@@ -20,7 +20,7 @@
#include "sql_priv.h"
#include "sql_select.h"
-
+#include "my_json_writer.h"
Explain_query::Explain_query(THD *thd_arg) :
upd_del_plan(NULL), insert_plan(NULL), thd(thd_arg), apc_enabled(false)
@@ -139,8 +139,13 @@ int Explain_query::send_explain(THD *thd)
thd->send_explain_fields(result))
return 1;
- int res;
- if ((res= print_explain(result, lex->describe, lex->analyze_stmt)))
+ int res= 0;
+ if (thd->lex->explain_json)
+ print_explain_json(result, thd->lex->analyze_stmt);
+ else
+ res= print_explain(result, lex->describe, thd->lex->analyze_stmt);
+
+ if (res)
result->abort_result_set();
else
result->send_eof();
@@ -177,6 +182,40 @@ int Explain_query::print_explain(select_result_sink *output,
}
+void Explain_query::print_explain_json(select_result_sink *output, bool is_analyze)
+{
+ Json_writer writer;
+ writer.start_object();
+
+ if (upd_del_plan)
+ {
+ //upd_del_plan->print_explain(this, output, explain_flags, is_analyze);
+ DBUG_ASSERT(0);
+ }
+ else if (insert_plan)
+ {
+ //insert_plan->print_explain(this, output, explain_flags, is_analyze);
+ DBUG_ASSERT(0);
+ }
+ else
+ {
+ /* Start printing from node with id=1 */
+ Explain_node *node= get_node(1);
+ if (!node)
+ return; /* No query plan */
+ node->print_explain_json(this, &writer, is_analyze);
+ }
+
+ writer.end_object();
+
+ const CHARSET_INFO *cs= system_charset_info;
+ List<Item> item_list;
+ String *buf= &writer.output;
+ item_list.push_back(new Item_string(buf->ptr(), buf->length(), cs));
+ output->send_data(item_list);
+}
+
+
bool print_explain_query(LEX *lex, THD *thd, String *str)
{
return lex->explain->print_explain_str(thd, str, false);
@@ -214,12 +253,59 @@ static void push_string(List<Item> *item_list, String *str)
system_charset_info));
}
+static void push_string_list(List<Item> *item_list, List<char> &lines,
+ String *buf)
+{
+ List_iterator_fast<char> it(lines);
+ char *line;
+ bool first= true;
+ while ((line= it++))
+ {
+ if (first)
+ first= false;
+ else
+ buf->append(',');
+
+ buf->append(line);
+ }
+ push_string(item_list, buf);
+}
+
+
+uint Explain_union::make_union_table_name(char *buf)
+{
+ uint childno= 0;
+ uint len= 6, lastop= 0;
+ memcpy(buf, STRING_WITH_LEN("<union"));
+
+ for (; childno < union_members.elements() && len + lastop + 5 < NAME_LEN;
+ childno++)
+ {
+ len+= lastop;
+ lastop= my_snprintf(buf + len, NAME_LEN - len,
+ "%u,", union_members.at(childno));
+ }
+
+ if (childno < union_members.elements() || len + lastop >= NAME_LEN)
+ {
+ memcpy(buf + len, STRING_WITH_LEN("...>") + 1);
+ len+= 4;
+ }
+ else
+ {
+ len+= lastop;
+ buf[len - 1]= '>'; // change ',' to '>'
+ }
+ return len;
+}
+
int Explain_union::print_explain(Explain_query *query,
select_result_sink *output,
uint8 explain_flags,
bool is_analyze)
{
+ const CHARSET_INFO *cs= system_charset_info;
char table_name_buffer[SAFE_NAME_LEN];
/* print all UNION children, in order */
@@ -240,32 +326,8 @@ int Explain_union::print_explain(Explain_query *query,
push_str(&item_list, fake_select_type);
/* `table` column: something like "<union1,2>" */
- {
- uint childno= 0;
- uint len= 6, lastop= 0;
- memcpy(table_name_buffer, STRING_WITH_LEN("<union"));
-
- for (; childno < union_members.elements() && len + lastop + 5 < NAME_LEN;
- childno++)
- {
- len+= lastop;
- lastop= my_snprintf(table_name_buffer + len, NAME_LEN - len,
- "%u,", union_members.at(childno));
- }
-
- if (childno < union_members.elements() || len + lastop >= NAME_LEN)
- {
- memcpy(table_name_buffer + len, STRING_WITH_LEN("...>") + 1);
- len+= 4;
- }
- else
- {
- len+= lastop;
- table_name_buffer[len - 1]= '>'; // change ',' to '>'
- }
- const CHARSET_INFO *cs= system_charset_info;
- item_list.push_back(new Item_string(table_name_buffer, len, cs));
- }
+ uint len= make_union_table_name(table_name_buffer);
+ item_list.push_back(new Item_string(table_name_buffer, len, cs));
/* `partitions` column */
if (explain_flags & DESCRIBE_PARTITIONS)
@@ -307,7 +369,6 @@ int Explain_union::print_explain(Explain_query *query,
{
extra_buf.append(STRING_WITH_LEN("Using filesort"));
}
- const CHARSET_INFO *cs= system_charset_info;
item_list.push_back(new Item_string(extra_buf.ptr(), extra_buf.length(), cs));
//output->unit.offset_limit_cnt= 0;
@@ -322,6 +383,36 @@ int Explain_union::print_explain(Explain_query *query,
}
+void Explain_union::print_explain_json(Explain_query *query,
+ Json_writer *writer, bool is_analyze)
+{
+ char table_name_buffer[SAFE_NAME_LEN];
+
+ writer->add_member("query_block").start_object();
+ writer->add_member("union_result").start_object();
+ // using_temporary_table
+ make_union_table_name(table_name_buffer);
+ writer->add_member("table_name").add_str(table_name_buffer);
+ writer->add_member("access_type").add_str("ALL"); // not very useful
+ writer->add_member("query_specifications").start_array();
+
+ for (int i= 0; i < (int) union_members.elements(); i++)
+ {
+ writer->start_object();
+ writer->add_member("dependent").add_str("TODO");
+ writer->add_member("cacheable").add_str("TODO");
+ Explain_select *sel= query->get_select(union_members.at(i));
+ sel->print_explain_json(query, writer, is_analyze);
+ writer->end_object();
+ }
+ writer->end_array();
+
+ //TODO: print_explain_for_children
+
+ writer->end_object();
+}
+
+
/*
Print EXPLAINs for all children nodes (i.e. for subqueries)
*/
@@ -410,21 +501,112 @@ int Explain_select::print_explain(Explain_query *query,
}
+void Explain_select::print_explain_json(Explain_query *query,
+ Json_writer *writer, bool is_analyze)
+{
+ writer->add_member("query_block").start_object();
+ writer->add_member("select_id").add_ll(1);
+ if (message)
+ {
+ writer->add_member("table").start_object();
+ writer->add_member("message").add_str(message);
+ writer->end_object();
+ }
+ else
+ {
+ for (uint i=0; i< n_join_tabs; i++)
+ {
+ // psergey-todo: Need to honor SJM nests...
+ join_tabs[i]->print_explain_json(writer, is_analyze);
+ }
+ }
+ writer->end_object();
+}
+
+
void Explain_table_access::push_extra(enum explain_extra_tag extra_tag)
{
extra_tags.append(extra_tag);
}
+void Explain_table_access::fill_key_str(String *key_str)
+{
+ const CHARSET_INFO *cs= system_charset_info;
+ bool is_hj= (type == JT_HASH || type == JT_HASH_NEXT ||
+ type == JT_HASH_RANGE || type == JT_HASH_INDEX_MERGE);
+ const char *hash_key_prefix= "#hash#";
+
+ if (key.get_key_name())
+ {
+ if (is_hj)
+ key_str->append(hash_key_prefix, strlen(hash_key_prefix), cs);
+
+ key_str->append(key.get_key_name());
+
+ if (is_hj && type != JT_HASH)
+ key_str->append(':');
+ }
+
+ if (quick_info)
+ {
+ StringBuffer<64> buf2;
+ quick_info->print_key(&buf2);
+ key_str->append(buf2);
+ }
+ if (type == JT_HASH_NEXT)
+ key_str->append(hash_next_key.get_key_name());
+}
+
+
+void Explain_table_access::fill_key_len_str(String *key_len_str)
+{
+ bool is_hj= (type == JT_HASH || type == JT_HASH_NEXT ||
+ type == JT_HASH_RANGE || type == JT_HASH_INDEX_MERGE);
+ if (key.get_key_len() != (uint)-1)
+ {
+ char buf[64];
+ size_t length;
+ length= longlong10_to_str(key.get_key_len(), buf, 10) - buf;
+ key_len_str->append(buf, length);
+ if (is_hj && type != JT_HASH)
+ key_len_str->append(':');
+ }
+
+ if (quick_info)
+ {
+ StringBuffer<64> buf2;
+ quick_info->print_key_len(&buf2);
+ key_len_str->append(buf2);
+ }
+
+ if (type == JT_HASH_NEXT)
+ {
+ char buf[64];
+ size_t length;
+ length= longlong10_to_str(hash_next_key.get_key_len(), buf, 10) - buf;
+ key_len_str->append(buf, length);
+ }
+}
+
+
+double Explain_table_access::get_r_filtered()
+{
+ double r_filtered;
+ if (r_rows > 0)
+ r_filtered= 100.0 * (double)r_rows_after_table_cond / r_rows;
+ else
+ r_filtered= 100.0;
+ return r_filtered;
+}
+
+
int Explain_table_access::print_explain(select_result_sink *output, uint8 explain_flags,
bool is_analyze,
uint select_id, const char *select_type,
bool using_temporary, bool using_filesort)
{
const CHARSET_INFO *cs= system_charset_info;
- const char *hash_key_prefix= "#hash#";
- bool is_hj= (type == JT_HASH || type == JT_HASH_NEXT ||
- type == JT_HASH_RANGE || type == JT_HASH_INDEX_MERGE);
List<Item> item_list;
Item *item_null= new Item_null();
@@ -459,32 +641,15 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
push_str(&item_list, join_type_str[type]);
/* `possible_keys` column */
- if (possible_keys_str.length() > 0)
- push_string(&item_list, &possible_keys_str);
- else
+ StringBuffer<64> possible_keys_buf;
+ if (possible_keys.is_empty())
item_list.push_back(item_null);
+ else
+ push_string_list(&item_list, possible_keys, &possible_keys_buf);
/* `key` */
StringBuffer<64> key_str;
- if (key.get_key_name())
- {
- if (is_hj)
- key_str.append(hash_key_prefix, strlen(hash_key_prefix), cs);
-
- key_str.append(key.get_key_name());
-
- if (is_hj && type != JT_HASH)
- key_str.append(':');
- }
-
- if (quick_info)
- {
- StringBuffer<64> buf2;
- quick_info->print_key(&buf2);
- key_str.append(buf2);
- }
- if (type == JT_HASH_NEXT)
- key_str.append(hash_next_key.get_key_name());
+ fill_key_str(&key_str);
if (key_str.length() > 0)
push_string(&item_list, &key_str);
@@ -493,31 +658,7 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
/* `key_len` */
StringBuffer<64> key_len_str;
-
- if (key.get_key_len() != (uint)-1)
- {
- char buf[64];
- size_t length;
- length= longlong10_to_str(key.get_key_len(), buf, 10) - buf;
- key_len_str.append(buf, length);
- if (is_hj && type != JT_HASH)
- key_len_str.append(':');
- }
-
- if (quick_info)
- {
- StringBuffer<64> buf2;
- quick_info->print_key_len(&buf2);
- key_len_str.append(buf2);
- }
-
- if (type == JT_HASH_NEXT)
- {
- char buf[64];
- size_t length;
- length= longlong10_to_str(hash_next_key.get_key_len(), buf, 10) - buf;
- key_len_str.append(buf, length);
- }
+ fill_key_len_str(&key_len_str);
if (key_len_str.length() > 0)
push_string(&item_list, &key_len_str);
@@ -561,12 +702,7 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
/* `r_filtered` */
if (is_analyze)
{
- double r_filtered;
- if (r_rows > 0)
- r_filtered= 100.0 * (double)r_rows_after_table_cond / r_rows;
- else
- r_filtered= 100.0;
- item_list.push_back(new Item_float(r_filtered, 2));
+ item_list.push_back(new Item_float(get_r_filtered(), 2));
}
/* `Extra` */
@@ -608,6 +744,115 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
}
+static void write_item(Json_writer *writer, Item *item)
+{
+ char item_buf[256];
+ String str(item_buf, sizeof(item_buf), &my_charset_bin);
+ str.length(0);
+ item->print(&str ,QT_ORDINARY);
+ writer->add_str(str.c_ptr_safe());
+}
+
+
+void Explain_table_access::tag_to_json(Json_writer *writer, enum explain_extra_tag tag)
+{
+ switch (tag)
+ {
+ case ET_OPEN_FULL_TABLE:
+ writer->add_member("open_full_table").add_bool(true);
+ break;
+ case ET_SCANNED_0_DATABASES:
+ writer->add_member("scanned_databases").add_ll(0);
+ break;
+ case ET_SCANNED_1_DATABASE:
+ writer->add_member("scanned_databases").add_ll(1);
+ break;
+ case ET_SCANNED_ALL_DATABASES:
+ writer->add_member("scanned_databases").add_str("all");
+ break;
+ case ET_SKIP_OPEN_TABLE:
+ writer->add_member("skip_open_table").add_bool(true);
+ break;
+ case ET_OPEN_FRM_ONLY:
+ writer->add_member("open_frm_only").add_bool(true);
+ break;
+ case ET_USING_INDEX_CONDITION:
+ writer->add_member("index_condition");
+ write_item(writer, pushed_index_cond);
+ break;
+ case ET_USING_WHERE:
+ writer->add_member("attached_condition");
+ write_item(writer, where_cond);
+ break;
+ case ET_USING_INDEX:
+ writer->add_member("using_index").add_bool(true);
+ break;
+ case ET_USING:
+ // index merge: case ET_USING
+ break;
+ default:
+ DBUG_ASSERT(0);
+ }
+}
+
+
+void Explain_table_access::print_explain_json(Json_writer *writer,
+ bool is_analyze)
+{
+ writer->add_member("table").start_object();
+
+ writer->add_member("table_name").add_str(table_name);
+ // partitions
+ writer->add_member("access_type").add_str(join_type_str[type]);
+ if (!possible_keys.is_empty())
+ {
+ List_iterator_fast<char> it(possible_keys);
+ const char *name;
+ writer->add_member("possible_keys").start_array();
+ while ((name= it++))
+ 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);
+
+ /* `used_key_parts` */
+ writer->add_member("used_key_parts").add_str("TODO");
+
+ 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);
+
+ if (rows_set)
+ writer->add_member("rows").add_ll(rows);
+
+ /* `r_rows` */
+ if (is_analyze)
+ {
+ ha_rows avg_rows= r_scans ? round((double) r_rows / r_scans): 0;
+ writer->add_member("r_rows").add_ll(avg_rows);
+ }
+
+ if (filtered_set)
+ writer->add_member("filtered").add_double(filtered);
+
+ /* `r_filtered` */
+ if (is_analyze)
+ writer->add_member("r_filtered").add_double(get_r_filtered());
+
+ for (int i=0; i < (int)extra_tags.elements(); i++)
+ {
+ tag_to_json(writer, extra_tags.at(i));
+ }
+
+ writer->end_object();
+}
+
+
/*
Elements in this array match members of enum Extra_tag, defined in
sql_explain.h