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:
@@ -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
|
||||
|
Reference in New Issue
Block a user