1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

MDEV-6109: EXPLAIN JSON

Add pretty-printing of possible_keys column.
This commit is contained in:
Sergei Petrunia
2014-08-12 15:02:09 +04:00
parent 33d53c4c24
commit 84485dbe7c
4 changed files with 306 additions and 25 deletions

View File

@ -38,4 +38,35 @@ EXPLAIN
} }
} }
} }
create table t1 (a int, b int, filler char(32), key(a));
insert into t1
select
A.a + B.a* 10 + C.a * 100,
A.a + B.a* 10 + C.a * 100,
'filler'
from t0 A, t0 B, t0 C;
explain format=json select * from t0,t1 where t1.a=t0.a;
EXPLAIN
{
"query_block": {
"select_id": 1,
"table": {
"table_name": "t0",
"access_type": "ALL",
"rows": 10,
"filtered": 100,
"attached_condition": "(t0.a is not null)"
},
"table": {
"table_name": "t1",
"access_type": "ref",
"possible_keys": ["a"],
"key": "a",
"used_key_parts": "TODO",
"key_length": "5",
"rows": 1,
"filtered": 100
}
}
}
drop table t0; drop table t0;

View File

@ -14,14 +14,14 @@ explain format=json select * from t0 where 1>2;
explain format=json select * from t0 where a<3; explain format=json select * from t0 where a<3;
#create table t1 (a int, b int, filler char(32), key(a)); create table t1 (a int, b int, filler char(32), key(a));
#insert into t1 insert into t1
#select select
# A.a + B.a* 10 + C.a * 100, A.a + B.a* 10 + C.a * 100,
# A.a + B.a* 10 + C.a * 100, A.a + B.a* 10 + C.a * 100,
# 'filler' 'filler'
#from t0 A, t0 B, t0 C; from t0 A, t0 B, t0 C;
#
#explain format=json select * from t0,t1 where t1.a=t0.a; explain format=json select * from t0,t1 where t1.a=t0.a;
drop table t0; drop table t0;

View File

@ -16,6 +16,8 @@ void Json_writer::append_indent()
void Json_writer::start_object() void Json_writer::start_object()
{ {
fmt_helper.on_start_object();
if (!element_started) if (!element_started)
start_element(); start_element();
@ -28,6 +30,9 @@ void Json_writer::start_object()
void Json_writer::start_array() void Json_writer::start_array()
{ {
if (fmt_helper.on_start_array())
return;
if (!element_started) if (!element_started)
start_element(); start_element();
@ -50,6 +55,8 @@ void Json_writer::end_object()
void Json_writer::end_array() void Json_writer::end_array()
{ {
if (fmt_helper.on_end_array())
return;
indent_level-=INDENT_SIZE; indent_level-=INDENT_SIZE;
if (!first_child) if (!first_child)
append_indent(); append_indent();
@ -59,6 +66,9 @@ void Json_writer::end_array()
Json_writer& Json_writer::add_member(const char *name) Json_writer& Json_writer::add_member(const char *name)
{ {
if (fmt_helper.on_add_member(name))
return *this; // handled
// assert that we are in an object // assert that we are in an object
DBUG_ASSERT(!element_started); DBUG_ASSERT(!element_started);
start_element(); start_element();
@ -69,6 +79,18 @@ 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. */
void Json_writer::start_sub_element()
{
//element_started= true;
if (first_child)
first_child= false;
else
output.append(',');
append_indent();
}
void Json_writer::start_element() void Json_writer::start_element()
{ {
@ -84,30 +106,44 @@ void Json_writer::start_element()
void Json_writer::add_ll(longlong val) void Json_writer::add_ll(longlong val)
{ {
if (!element_started)
start_element();
char buf[64]; char buf[64];
my_snprintf(buf, sizeof(buf), "%ld", val); my_snprintf(buf, sizeof(buf), "%ld", val);
output.append(buf); add_unquoted_str(buf);
element_started= false;
} }
void Json_writer::add_double(double val) void Json_writer::add_double(double val)
{ {
char buf[64];
my_snprintf(buf, sizeof(buf), "%lg", val);
add_unquoted_str(buf);
}
void Json_writer::add_bool(bool val)
{
add_unquoted_str(val? "true" : "false");
}
void Json_writer::add_unquoted_str(const char* str)
{
if (fmt_helper.on_add_str(str))
return;
if (!element_started) if (!element_started)
start_element(); start_element();
char buf[64]; output.append(str);
my_snprintf(buf, sizeof(buf), "%lg", val);
output.append(buf);
element_started= false; element_started= false;
} }
void Json_writer::add_str(const char *str) void Json_writer::add_str(const char *str)
{ {
if (fmt_helper.on_add_str(str))
return;
if (!element_started) if (!element_started)
start_element(); start_element();
@ -117,13 +153,158 @@ void Json_writer::add_str(const char *str)
element_started= false; element_started= false;
} }
void Json_writer::add_bool(bool val)
{
add_str(val? "true" : "false");
}
void Json_writer::add_str(const String &str) void Json_writer::add_str(const String &str)
{ {
add_str(str.ptr()); add_str(str.ptr());
} }
bool Single_line_formatting_helper::on_add_member(const char *name)
{
DBUG_ASSERT(state== INACTIVE || state == DISABLED);
if (state != DISABLED)
{
// remove everything from the array
buf_ptr= buffer;
//append member name to the array
size_t len= strlen(name);
if (len < MAX_LINE_LEN)
{
memcpy(buf_ptr, name, len);
buf_ptr+=len;
*(buf_ptr++)= 0;
line_len= owner->indent_level + len + 1;
state= ADD_MEMBER;
return true; // handled
}
}
return false; // not handled
}
bool Single_line_formatting_helper::on_start_array()
{
if (state == ADD_MEMBER)
{
state= IN_ARRAY;
return true; // handled
}
else
{
state= INACTIVE;
// TODO: what if we have accumulated some stuff already? shouldn't we
// flush it?
return false; // not handled
}
}
bool Single_line_formatting_helper::on_end_array()
{
if (state == IN_ARRAY)
{
flush_on_one_line();
state= INACTIVE;
return true; // handled
}
return false; // not handled
}
void Single_line_formatting_helper::on_start_object()
{
// Nested objects will not be printed on one line
disable_and_flush();
}
bool Single_line_formatting_helper::on_add_str(const char *str)
{
if (state == IN_ARRAY)
{
size_t len= strlen(str);
// New length will be:
// "$string",
// quote + quote + comma + space = 4
if (line_len + len + 4 > MAX_LINE_LEN)
{
disable_and_flush();
return false; // didn't handle the last element
}
//append string to array
memcpy(buf_ptr, str, len);
buf_ptr+=len;
*(buf_ptr++)= 0;
line_len += len + 4;
return true; // handled
}
disable_and_flush();
return false; // not handled
}
void Single_line_formatting_helper::flush_on_one_line()
{
// append everything to output on one line
owner->start_sub_element();
char *ptr= buffer;
int nr= 0;
while (ptr < buf_ptr)
{
char *str= ptr;
if (nr == 0)
{
owner->output.append('"');
owner->output.append(str);
owner->output.append("\": ");
owner->output.append('[');
}
else
{
if (nr != 1)
owner->output.append(", ");
owner->output.append('"');
owner->output.append(str);
owner->output.append('"');
}
nr++;
while (*ptr!=0)
ptr++;
ptr++;
}
owner->output.append(']');
}
void Single_line_formatting_helper::disable_and_flush()
{
state= DISABLED;
// deactivate ourselves and flush all accumulated calls.
char *ptr= buffer;
int nr= 0;
while (ptr < buf_ptr)
{
char *str= ptr;
if (nr == 0)
{
owner->add_member(str);
}
else
{
if (nr == 1)
owner->start_array();
owner->add_str(str);
}
nr++;
while (*ptr!=0)
ptr++;
ptr++;
}
buf_ptr= buffer;
state= INACTIVE;
}

View File

@ -1,5 +1,64 @@
/* Todo: SkySQL copyrights */ /* Todo: SkySQL copyrights */
class Json_writer;
/*
The idea is to catch arrays that can be printed on one line:
arrayName : [ "boo", 123, 456 ]
and actually print them on one line. Arrrays that occupy too much space on
the line, or have nested members cannot be printed on one line.
We hook into JSON printing functions and try to detect the pattern. While
detecting the pattern, we will accumulate "boo", 123, 456 as strings.
Then,
- either the pattern is broken, and we print the elements out,
- or the pattern lasts till the end of the array, and we print the
array on one line.
TODO:
fix the quoting. If we start to accumulate an array and but then it grows
too large to be printed on one line, the elements will be printed as
strings (even if some of them could be initially numbers).
*/
class Single_line_formatting_helper
{
enum enum_state
{
INACTIVE,
ADD_MEMBER,
IN_ARRAY,
DISABLED
};
enum enum_state state;
enum { MAX_LINE_LEN= 80 };
char buffer[80];
char *buf_ptr;
uint line_len;
Json_writer *owner;
public:
Single_line_formatting_helper() : state(INACTIVE), buf_ptr(buffer) {}
void init(Json_writer *owner_arg) { owner= owner_arg; }
bool on_add_member(const char *name);
bool on_start_array();
bool on_end_array();
void on_start_object();
// on_end_object() is not needed.
bool on_add_str(const char *str);
void flush_on_one_line();
void disable_and_flush();
};
/* /*
A class to write well-formed JSON documents. The documents are also formatted A class to write well-formed JSON documents. The documents are also formatted
@ -13,12 +72,16 @@ public:
Json_writer& add_member(const char *name); Json_writer& add_member(const char *name);
/* Add atomic values */ /* Add atomic values */
void add_ll(longlong val);
void add_str(const char* val); void add_str(const char* val);
void add_str(const String &str); void add_str(const String &str);
void add_ll(longlong val);
void add_double(double val); void add_double(double val);
void add_bool(bool val); void add_bool(bool val);
private:
void add_unquoted_str(const char* val);
public:
/* Start a child object */ /* Start a child object */
void start_object(); void start_object();
void start_array(); void start_array();
@ -29,18 +92,24 @@ public:
Json_writer() : Json_writer() :
indent_level(0), document_start(true), element_started(false), indent_level(0), document_start(true), element_started(false),
first_child(true) first_child(true)
{} {
fmt_helper.init(this);
}
private: private:
// TODO: a stack of (name, bool is_object_or_array) elements. // TODO: a stack of (name, bool is_object_or_array) elements.
int indent_level; int indent_level;
enum { INDENT_SIZE = 2 }; enum { INDENT_SIZE = 2 };
friend class Single_line_formatting_helper;
bool document_start; bool document_start;
bool element_started; bool element_started;
bool first_child; bool first_child;
Single_line_formatting_helper fmt_helper;
void append_indent(); void append_indent();
void start_element(); void start_element();
void start_sub_element();
//const char *new_member_name; //const char *new_member_name;
public: public: