Commit 37c444e1 authored by Sergei Petrunia's avatar Sergei Petrunia

EXPLAIN FORMAT=JSON: further development

Writing JSON:
- Fix a bug in Single_line_formatting_helper
- Add Json_writer_nesting_guard - safety class

EXPLAIN JSON support
- Add basic subquery support
- Add tests for UNION/UNION ALL.
parent 3d5f97fd
...@@ -175,4 +175,138 @@ EXPLAIN ...@@ -175,4 +175,138 @@ EXPLAIN
} }
} }
drop table t1,t2; drop table t1,t2;
#
# Try a UNION
#
explain format=json select * from t0 A union select * from t0 B;
EXPLAIN
{
"query_block": {
"union_result": {
"table_name": "<union1,2>",
"access_type": "ALL",
"query_specifications": [
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "A",
"access_type": "ALL",
"rows": 10,
"filtered": 100
}
}
},
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "B",
"access_type": "ALL",
"rows": 10,
"filtered": 100
}
}
}
]
}
}
}
explain format=json select * from t0 A union all select * from t0 B;
EXPLAIN
{
"query_block": {
"union_result": {
"table_name": "<union1,2>",
"access_type": "ALL",
"query_specifications": [
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "A",
"access_type": "ALL",
"rows": 10,
"filtered": 100
}
}
},
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "B",
"access_type": "ALL",
"rows": 10,
"filtered": 100
}
}
}
]
}
}
}
#
# Subqueries
#
create table t1 (a int, b int);
insert into t1 select a,a from t0;
explain format=json select a, a > (select max(b) from t1 where t1.b=t0.a) from t0;
EXPLAIN
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "t0",
"access_type": "ALL",
"rows": 10,
"filtered": 100
},
"subqueries": [
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "t1",
"access_type": "ALL",
"rows": 10,
"filtered": 100,
"attached_condition": "(t1.b = t0.a)"
}
}
}
]
}
}
explain format=json
select * from t0 where
a > (select max(b) from t1 where t1.b=t0.a) or a < 3 ;
EXPLAIN
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "t0",
"access_type": "ALL",
"rows": 10,
"filtered": 100,
"attached_condition": "((t0.a > (subquery#2)) or (t0.a < 3))"
},
"subqueries": [
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "t1",
"access_type": "ALL",
"rows": 10,
"filtered": 100,
"attached_condition": "(t1.b = t0.a)"
}
}
}
]
}
}
drop table t1;
drop table t0; drop table t0;
...@@ -39,4 +39,24 @@ explain format=json select * from t2 where a1=1 or (b1=2 and b2=3); ...@@ -39,4 +39,24 @@ explain format=json select * from t2 where a1=1 or (b1=2 and b2=3);
explain format=json select * from t0,t2 where t2.b1=t0.a and t2.b2=4; explain format=json select * from t0,t2 where t2.b1=t0.a and t2.b2=4;
drop table t1,t2; drop table t1,t2;
--echo #
--echo # Try a UNION
--echo #
explain format=json select * from t0 A union select * from t0 B;
explain format=json select * from t0 A union all select * from t0 B;
--echo #
--echo # Subqueries
--echo #
create table t1 (a int, b int);
insert into t1 select a,a from t0;
explain format=json select a, a > (select max(b) from t1 where t1.b=t0.a) from t0;
explain format=json
select * from t0 where
a > (select max(b) from t1 where t1.b=t0.a) or a < 3 ;
drop table t1;
drop table t0; drop table t0;
...@@ -7536,6 +7536,13 @@ void Item_cache_wrapper::init_on_demand() ...@@ -7536,6 +7536,13 @@ void Item_cache_wrapper::init_on_demand()
void Item_cache_wrapper::print(String *str, enum_query_type query_type) void Item_cache_wrapper::print(String *str, enum_query_type query_type)
{ {
if (query_type == QT_EXPLAIN)
{
/* Don't print the cache in EXPLAIN EXTENDED */
orig_item->print(str, query_type);
return;
}
str->append(func_name()); str->append(func_name());
if (expr_cache) if (expr_cache)
{ {
......
...@@ -894,6 +894,21 @@ void Item_subselect::update_used_tables() ...@@ -894,6 +894,21 @@ void Item_subselect::update_used_tables()
void Item_subselect::print(String *str, enum_query_type query_type) void Item_subselect::print(String *str, enum_query_type query_type)
{ {
if (query_type == QT_EXPLAIN)
{
str->append("(subquery#");
if (engine)
{
char buf[64];
ll2str(engine->get_identifier(), buf, 10, 0);
str->append(buf);
}
else
str->append("NULL"); // TODO: what exactly does this mean?
str->append(")");
return;
}
if (engine) if (engine)
{ {
str->append('('); str->append('(');
...@@ -3716,6 +3731,10 @@ int subselect_union_engine::exec() ...@@ -3716,6 +3731,10 @@ int subselect_union_engine::exec()
return res; return res;
} }
int subselect_union_engine::get_identifier()
{
return unit->first_select()->select_number;
}
/* /*
Search for at least one row satisfying select condition Search for at least one row satisfying select condition
......
...@@ -871,6 +871,7 @@ public: ...@@ -871,6 +871,7 @@ public:
bool is_executed() const; bool is_executed() const;
bool no_rows(); bool no_rows();
virtual enum_engine_type engine_type() { return UNION_ENGINE; } virtual enum_engine_type engine_type() { return UNION_ENGINE; }
int get_identifier();
}; };
......
...@@ -79,7 +79,12 @@ Json_writer& Json_writer::add_member(const char *name) ...@@ -79,7 +79,12 @@ Json_writer& Json_writer::add_member(const char *name)
return *this; return *this;
} }
/* Used by formatting helper to print something that is formatted by the helper. */
/*
Used by formatting helper to print something that is formatted by the helper.
We should only separate it from the previous element.
*/
void Json_writer::start_sub_element() void Json_writer::start_sub_element()
{ {
//element_started= true; //element_started= true;
...@@ -184,6 +189,7 @@ bool Single_line_formatting_helper::on_add_member(const char *name) ...@@ -184,6 +189,7 @@ bool Single_line_formatting_helper::on_add_member(const char *name)
return false; // not handled return false; // not handled
} }
bool Single_line_formatting_helper::on_start_array() bool Single_line_formatting_helper::on_start_array()
{ {
if (state == ADD_MEMBER) if (state == ADD_MEMBER)
...@@ -200,6 +206,7 @@ bool Single_line_formatting_helper::on_start_array() ...@@ -200,6 +206,7 @@ bool Single_line_formatting_helper::on_start_array()
} }
} }
bool Single_line_formatting_helper::on_end_array() bool Single_line_formatting_helper::on_end_array()
{ {
if (state == IN_ARRAY) if (state == IN_ARRAY)
...@@ -211,12 +218,14 @@ bool Single_line_formatting_helper::on_end_array() ...@@ -211,12 +218,14 @@ bool Single_line_formatting_helper::on_end_array()
return false; // not handled return false; // not handled
} }
void Single_line_formatting_helper::on_start_object() void Single_line_formatting_helper::on_start_object()
{ {
// Nested objects will not be printed on one line // Nested objects will not be printed on one line
disable_and_flush(); disable_and_flush();
} }
bool Single_line_formatting_helper::on_add_str(const char *str) bool Single_line_formatting_helper::on_add_str(const char *str)
{ {
if (state == IN_ARRAY) if (state == IN_ARRAY)
...@@ -244,9 +253,13 @@ bool Single_line_formatting_helper::on_add_str(const char *str) ...@@ -244,9 +253,13 @@ bool Single_line_formatting_helper::on_add_str(const char *str)
return false; // not handled return false; // not handled
} }
/*
Append everything accumulated to the output on one line
*/
void Single_line_formatting_helper::flush_on_one_line() void Single_line_formatting_helper::flush_on_one_line()
{ {
// append everything to output on one line
owner->start_sub_element(); owner->start_sub_element();
char *ptr= buffer; char *ptr= buffer;
int nr= 0; int nr= 0;
...@@ -281,6 +294,7 @@ void Single_line_formatting_helper::flush_on_one_line() ...@@ -281,6 +294,7 @@ void Single_line_formatting_helper::flush_on_one_line()
void Single_line_formatting_helper::disable_and_flush() void Single_line_formatting_helper::disable_and_flush()
{ {
bool start_array= (state == IN_ARRAY);
state= DISABLED; state= DISABLED;
// deactivate ourselves and flush all accumulated calls. // deactivate ourselves and flush all accumulated calls.
char *ptr= buffer; char *ptr= buffer;
...@@ -291,11 +305,13 @@ void Single_line_formatting_helper::disable_and_flush() ...@@ -291,11 +305,13 @@ void Single_line_formatting_helper::disable_and_flush()
if (nr == 0) if (nr == 0)
{ {
owner->add_member(str); owner->add_member(str);
if (start_array)
owner->start_array();
} }
else else
{ {
if (nr == 1) //if (nr == 1)
owner->start_array(); // owner->start_array();
owner->add_str(str); owner->add_str(str);
} }
......
...@@ -34,9 +34,30 @@ class Single_line_formatting_helper ...@@ -34,9 +34,30 @@ class Single_line_formatting_helper
DISABLED DISABLED
}; };
/*
This works like a finite automaton.
state=DISABLED means the helper is disabled - all on_XXX functions will
return false (which means "not handled") and do nothing.
+->-+
| v
INACTIVE ---> ADD_MEMBER ---> IN_ARRAY--->-+
^ |
+------------------<--------------------+
For other states:
INACTIVE - initial state, we have nothing.
ADD_MEMBER - add_member() was called, the buffer has "member_name\0".
IN_ARRAY - start_array() was called.
*/
enum enum_state state; enum enum_state state;
enum { MAX_LINE_LEN= 80 }; enum { MAX_LINE_LEN= 80 };
char buffer[80]; char buffer[80];
/* The data in the buffer is located between buffer[0] and buf_ptr */
char *buf_ptr; char *buf_ptr;
uint line_len; uint line_len;
...@@ -101,6 +122,7 @@ private: ...@@ -101,6 +122,7 @@ private:
enum { INDENT_SIZE = 2 }; enum { INDENT_SIZE = 2 };
friend class Single_line_formatting_helper; friend class Single_line_formatting_helper;
friend class Json_writer_nesting_guard;
bool document_start; bool document_start;
bool element_started; bool element_started;
bool first_child; bool first_child;
...@@ -116,3 +138,40 @@ public: ...@@ -116,3 +138,40 @@ public:
String output; String output;
}; };
/*
RAII-based helper class to detect incorrect use of Json_writer.
The idea is that a function typically must leave Json_writer at the same
identation level as it was when it was invoked. Leaving it at a different
level typically means we forgot to close an object or an array
So, here is a way to guard
void foo(Json_writer *writer)
{
Json_writer_nesting_guard(writer);
.. do something with writer
// at the end of the function, ~Json_writer_nesting_guard() is called
// and it makes sure that the nesting is the same as when the function was
// entered.
}
*/
class Json_writer_nesting_guard
{
Json_writer* writer;
int indent_level;
public:
Json_writer_nesting_guard(Json_writer *writer_arg) :
writer(writer_arg),
indent_level(writer->indent_level)
{}
~Json_writer_nesting_guard()
{
DBUG_ASSERT(indent_level == writer->indent_level);
}
};
...@@ -303,7 +303,7 @@ int Explain_union::print_explain(Explain_query *query, ...@@ -303,7 +303,7 @@ int Explain_union::print_explain(Explain_query *query,
uint8 explain_flags, uint8 explain_flags,
bool is_analyze) bool is_analyze)
{ {
const CHARSET_INFO *cs= system_charset_info; //const CHARSET_INFO *cs= system_charset_info;
char table_name_buffer[SAFE_NAME_LEN]; char table_name_buffer[SAFE_NAME_LEN];
/* print all UNION children, in order */ /* print all UNION children, in order */
...@@ -391,6 +391,7 @@ int Explain_union::print_explain(Explain_query *query, ...@@ -391,6 +391,7 @@ int Explain_union::print_explain(Explain_query *query,
void Explain_union::print_explain_json(Explain_query *query, void Explain_union::print_explain_json(Explain_query *query,
Json_writer *writer, bool is_analyze) Json_writer *writer, bool is_analyze)
{ {
Json_writer_nesting_guard guard(writer);
char table_name_buffer[SAFE_NAME_LEN]; char table_name_buffer[SAFE_NAME_LEN];
writer->add_member("query_block").start_object(); writer->add_member("query_block").start_object();
...@@ -404,17 +405,18 @@ void Explain_union::print_explain_json(Explain_query *query, ...@@ -404,17 +405,18 @@ void Explain_union::print_explain_json(Explain_query *query,
for (int i= 0; i < (int) union_members.elements(); i++) for (int i= 0; i < (int) union_members.elements(); i++)
{ {
writer->start_object(); writer->start_object();
writer->add_member("dependent").add_str("TODO"); //writer->add_member("dependent").add_str("TODO");
writer->add_member("cacheable").add_str("TODO"); //writer->add_member("cacheable").add_str("TODO");
Explain_select *sel= query->get_select(union_members.at(i)); Explain_select *sel= query->get_select(union_members.at(i));
sel->print_explain_json(query, writer, is_analyze); sel->print_explain_json(query, writer, is_analyze);
writer->end_object(); writer->end_object();
} }
writer->end_array(); writer->end_array();
//TODO: print_explain_for_children print_explain_json_for_children(query, writer, is_analyze);
writer->end_object(); writer->end_object(); // union_result
writer->end_object(); // query_block
} }
...@@ -437,6 +439,25 @@ int Explain_node::print_explain_for_children(Explain_query *query, ...@@ -437,6 +439,25 @@ int Explain_node::print_explain_for_children(Explain_query *query,
} }
void Explain_node::print_explain_json_for_children(Explain_query *query,
Json_writer *writer,
bool is_analyze)
{
if (!children.elements())
return;
writer->add_member("subqueries").start_array();
for (int i= 0; i < (int) children.elements(); i++)
{
writer->start_object();
Explain_node *node= query->get_node(children.at(i));
node->print_explain_json(query, writer, is_analyze);
writer->end_object();
}
writer->end_array();
}
void Explain_select::replace_table(uint idx, Explain_table_access *new_tab) void Explain_select::replace_table(uint idx, Explain_table_access *new_tab)
{ {
delete join_tabs[idx]; delete join_tabs[idx];
...@@ -514,6 +535,8 @@ int Explain_select::print_explain(Explain_query *query, ...@@ -514,6 +535,8 @@ int Explain_select::print_explain(Explain_query *query,
void Explain_select::print_explain_json(Explain_query *query, void Explain_select::print_explain_json(Explain_query *query,
Json_writer *writer, bool is_analyze) Json_writer *writer, bool is_analyze)
{ {
Json_writer_nesting_guard guard(writer);
writer->add_member("query_block").start_object(); writer->add_member("query_block").start_object();
writer->add_member("select_id").add_ll(1); writer->add_member("select_id").add_ll(1);
if (message) if (message)
...@@ -530,6 +553,8 @@ void Explain_select::print_explain_json(Explain_query *query, ...@@ -530,6 +553,8 @@ void Explain_select::print_explain_json(Explain_query *query,
join_tabs[i]->print_explain_json(writer, is_analyze); join_tabs[i]->print_explain_json(writer, is_analyze);
} }
} }
print_explain_json_for_children(query, writer, is_analyze);
writer->end_object(); writer->end_object();
} }
...@@ -658,7 +683,7 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai ...@@ -658,7 +683,7 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
uint select_id, const char *select_type, uint select_id, const char *select_type,
bool using_temporary, bool using_filesort) bool using_temporary, bool using_filesort)
{ {
const CHARSET_INFO *cs= system_charset_info; //const CHARSET_INFO *cs= system_charset_info;
List<Item> item_list; List<Item> item_list;
Item *item_null= new Item_null(); Item *item_null= new Item_null();
...@@ -899,6 +924,7 @@ void Explain_table_access::tag_to_json(Json_writer *writer, enum explain_extra_t ...@@ -899,6 +924,7 @@ void Explain_table_access::tag_to_json(Json_writer *writer, enum explain_extra_t
void Explain_table_access::print_explain_json(Json_writer *writer, void Explain_table_access::print_explain_json(Json_writer *writer,
bool is_analyze) bool is_analyze)
{ {
Json_writer_nesting_guard guard(writer);
writer->add_member("table").start_object(); writer->add_member("table").start_object();
writer->add_member("table_name").add_str(table_name); writer->add_member("table_name").add_str(table_name);
......
...@@ -112,6 +112,8 @@ public: ...@@ -112,6 +112,8 @@ public:
int print_explain_for_children(Explain_query *query, select_result_sink *output, int print_explain_for_children(Explain_query *query, select_result_sink *output,
uint8 explain_flags, bool is_analyze); uint8 explain_flags, bool is_analyze);
void print_explain_json_for_children(Explain_query *query,
Json_writer *writer, bool is_analyze);
virtual ~Explain_node(){} virtual ~Explain_node(){}
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment