1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

Merge 10.1 (with ANALYZE) and 10.1-explain-json

This commit is contained in:
Sergei Petrunia
2014-08-09 01:52:54 +04:00
15 changed files with 643 additions and 104 deletions

View File

@@ -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)
@@ -311,7 +373,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;
@@ -326,6 +387,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)
*/
@@ -421,21 +512,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()
{
//psergey-todo: modify this to produce separate filtered% for both parts of
//WHERE.
double r_filtered= tracker.get_filtered_after_where();
if (bka_type.is_using_jbuf())
r_filtered *= jbuf_tracker.get_filtered_after_where();
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();
@@ -470,32 +652,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);
@@ -504,31 +669,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);
@@ -631,6 +772,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 && tracker.has_scans())
{
ha_rows avg_rows= tracker.get_avg_rows();
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