From 5cfd3270ec79238b27765af3062ae7d97f6f06d0 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Tue, 27 May 2014 21:04:45 +0400 Subject: MDEV-6109: EXPLAIN JSON - First code, "EXPLAIN FORMAT=JSON stmt" and "ANALYZE FORMAT=JSON stmt" work for basic queries. Complex constructs (e.g subqueries, etc) not yet supported. - No test infrastructure yet --- sql/sql_explain.cc | 417 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 331 insertions(+), 86 deletions(-) (limited to 'sql/sql_explain.cc') 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_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_list, String *str) system_charset_info)); } +static void push_string_list(List *item_list, List &lines, + String *buf) +{ + List_iterator_fast 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("= 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 "" */ - { - uint childno= 0; - uint len= 6, lastop= 0; - memcpy(table_name_buffer, STRING_WITH_LEN("= 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_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 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 -- cgit v1.2.1 From 33d53c4c24881d4906cacc791c2049faa96a0ee6 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Sat, 9 Aug 2014 06:37:56 +0400 Subject: MDEV-6109: EXPLAIN JSON - Add first testcases - Don't overquote when printing conditions - Other small output fixes --- sql/sql_explain.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'sql/sql_explain.cc') diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index 496e113162a..846868197ac 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -774,10 +774,17 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai static void write_item(Json_writer *writer, Item *item) { + THD *thd= current_thd; char item_buf[256]; String str(item_buf, sizeof(item_buf), &my_charset_bin); str.length(0); - item->print(&str ,QT_ORDINARY); + + ulonglong save_option_bits= thd->variables.option_bits; + thd->variables.option_bits &= ~OPTION_QUOTE_SHOW_CREATE; + + item->print(&str, QT_EXPLAIN); + + thd->variables.option_bits= save_option_bits; writer->add_str(str.c_ptr_safe()); } @@ -848,7 +855,8 @@ void Explain_table_access::print_explain_json(Json_writer *writer, writer->add_member("key").add_str(key_str); /* `used_key_parts` */ - writer->add_member("used_key_parts").add_str("TODO"); + if (key_str.length()) + writer->add_member("used_key_parts").add_str("TODO"); StringBuffer<64> key_len_str; fill_key_len_str(&key_len_str); -- cgit v1.2.1 From a9d43d70f5d83ac652fd970f5b2b8dfdb77c1136 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Tue, 12 Aug 2014 18:14:56 +0400 Subject: EXPLAIN FORMAT=JSON: produce the 'ref' column. --- sql/sql_explain.cc | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) (limited to 'sql/sql_explain.cc') diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index 846868197ac..f7fc3b106ce 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -253,7 +253,7 @@ static void push_string(List *item_list, String *str) system_charset_info)); } -static void push_string_list(List *item_list, List &lines, +static void push_string_list(List *item_list, String_list &lines, String *buf) { List_iterator_fast it(lines); @@ -677,10 +677,11 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai item_list.push_back(item_null); /* `ref` */ - if (ref_set) - push_string(&item_list, &ref); - else + StringBuffer<64> ref_list_buf; + if (ref_list.is_empty()) item_list.push_back(item_null); + else + push_string_list(&item_list, ref_list, &ref_list_buf); /* `rows` */ if (rows_set) @@ -772,6 +773,18 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai } +bool 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))) + return 1; + memcpy(cp, str, len+1); + push_back(cp); + return 0; +} + + static void write_item(Json_writer *writer, Item *item) { THD *thd= current_thd; @@ -857,12 +870,26 @@ void Explain_table_access::print_explain_json(Json_writer *writer, /* `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); + + /* `ref` */ + // TODO: need to print this as an array. + if (!ref_list.is_empty()) + { + List_iterator_fast it(ref_list); + const char *str; + writer->add_member("ref").start_array(); + while ((str= it++)) + writer->add_str(str); + writer->end_array(); + } + /* `rows` */ if (rows_set) writer->add_member("rows").add_ll(rows); @@ -873,6 +900,7 @@ void Explain_table_access::print_explain_json(Json_writer *writer, writer->add_member("r_rows").add_ll(avg_rows); } + /* `filtered` */ if (filtered_set) writer->add_member("filtered").add_double(filtered); -- cgit v1.2.1 From 041e03e251e783d51ca86e53112e3b87bd2da146 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Thu, 14 Aug 2014 01:12:05 +0400 Subject: EXPLAIN FORMAT=JSON: produce used_key_parts, JSON-ish output for index_merge. --- sql/sql_explain.cc | 122 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 110 insertions(+), 12 deletions(-) (limited to 'sql/sql_explain.cc') 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 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 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 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 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) { -- cgit v1.2.1 From 3c5ce8a0a32a74cd8e0ddc81bcfacf7c85f0d90a Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 21 Nov 2014 21:44:06 +0300 Subject: Make testsuite to pass - Drop all tables in explain_json.test - Tabular form should print ref='' when type='fulltext' (another peculiarity of the traditional EXPLAIN format) - String_list::append_str should allocate memory for \0, too - Some temporary code for EXPLAIN JSON and join buffering. --- sql/sql_explain.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'sql/sql_explain.cc') diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index a468d16bbe4..e8bc907a9fb 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -721,7 +721,15 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai /* `ref` */ StringBuffer<64> ref_list_buf; if (ref_list.is_empty()) - item_list.push_back(item_null); + { + if (type == JT_FT) + { + /* Traditionally, EXPLAIN lines with type=fulltext have ref='' */ + push_str(&item_list, ""); + } + else + item_list.push_back(item_null); + } else push_string_list(&item_list, ref_list, &ref_list_buf); @@ -819,7 +827,7 @@ bool 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))) + if (!(cp = (char*)alloc_root(mem_root, len+1))) return 1; memcpy(cp, str, len+1); push_back(cp); @@ -880,6 +888,9 @@ void Explain_table_access::tag_to_json(Json_writer *writer, enum explain_extra_t case ET_USING: // index merge: case ET_USING break; + case ET_USING_JOIN_BUFFER: + // TODO TODO + break; default: DBUG_ASSERT(0); } -- cgit v1.2.1