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:
@ -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;
|
||||
|
@ -14,14 +14,14 @@ explain format=json select * from t0 where 1>2;
|
||||
|
||||
explain format=json select * from t0 where a<3;
|
||||
|
||||
#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;
|
||||
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;
|
||||
|
||||
drop table t0;
|
||||
|
@ -16,6 +16,8 @@ void Json_writer::append_indent()
|
||||
|
||||
void Json_writer::start_object()
|
||||
{
|
||||
fmt_helper.on_start_object();
|
||||
|
||||
if (!element_started)
|
||||
start_element();
|
||||
|
||||
@ -28,6 +30,9 @@ void Json_writer::start_object()
|
||||
|
||||
void Json_writer::start_array()
|
||||
{
|
||||
if (fmt_helper.on_start_array())
|
||||
return;
|
||||
|
||||
if (!element_started)
|
||||
start_element();
|
||||
|
||||
@ -50,6 +55,8 @@ void Json_writer::end_object()
|
||||
|
||||
void Json_writer::end_array()
|
||||
{
|
||||
if (fmt_helper.on_end_array())
|
||||
return;
|
||||
indent_level-=INDENT_SIZE;
|
||||
if (!first_child)
|
||||
append_indent();
|
||||
@ -59,6 +66,9 @@ void Json_writer::end_array()
|
||||
|
||||
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
|
||||
DBUG_ASSERT(!element_started);
|
||||
start_element();
|
||||
@ -69,6 +79,18 @@ Json_writer& Json_writer::add_member(const char *name)
|
||||
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()
|
||||
{
|
||||
@ -84,30 +106,44 @@ void Json_writer::start_element()
|
||||
|
||||
void Json_writer::add_ll(longlong val)
|
||||
{
|
||||
if (!element_started)
|
||||
start_element();
|
||||
|
||||
char buf[64];
|
||||
my_snprintf(buf, sizeof(buf), "%ld", val);
|
||||
output.append(buf);
|
||||
element_started= false;
|
||||
add_unquoted_str(buf);
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
start_element();
|
||||
|
||||
char buf[64];
|
||||
my_snprintf(buf, sizeof(buf), "%lg", val);
|
||||
output.append(buf);
|
||||
output.append(str);
|
||||
element_started= false;
|
||||
}
|
||||
|
||||
|
||||
void Json_writer::add_str(const char *str)
|
||||
{
|
||||
if (fmt_helper.on_add_str(str))
|
||||
return;
|
||||
|
||||
if (!element_started)
|
||||
start_element();
|
||||
|
||||
@ -117,13 +153,158 @@ void Json_writer::add_str(const char *str)
|
||||
element_started= false;
|
||||
}
|
||||
|
||||
void Json_writer::add_bool(bool val)
|
||||
{
|
||||
add_str(val? "true" : "false");
|
||||
}
|
||||
|
||||
void Json_writer::add_str(const String &str)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,64 @@
|
||||
/* 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
|
||||
@ -13,12 +72,16 @@ public:
|
||||
Json_writer& add_member(const char *name);
|
||||
|
||||
/* Add atomic values */
|
||||
void add_ll(longlong val);
|
||||
void add_str(const char* val);
|
||||
void add_str(const String &str);
|
||||
|
||||
void add_ll(longlong val);
|
||||
void add_double(double val);
|
||||
void add_bool(bool val);
|
||||
|
||||
private:
|
||||
void add_unquoted_str(const char* val);
|
||||
public:
|
||||
/* Start a child object */
|
||||
void start_object();
|
||||
void start_array();
|
||||
@ -29,18 +92,24 @@ public:
|
||||
Json_writer() :
|
||||
indent_level(0), document_start(true), element_started(false),
|
||||
first_child(true)
|
||||
{}
|
||||
{
|
||||
fmt_helper.init(this);
|
||||
}
|
||||
private:
|
||||
// TODO: a stack of (name, bool is_object_or_array) elements.
|
||||
int indent_level;
|
||||
enum { INDENT_SIZE = 2 };
|
||||
|
||||
friend class Single_line_formatting_helper;
|
||||
bool document_start;
|
||||
bool element_started;
|
||||
bool first_child;
|
||||
|
||||
Single_line_formatting_helper fmt_helper;
|
||||
|
||||
void append_indent();
|
||||
void start_element();
|
||||
void start_sub_element();
|
||||
|
||||
//const char *new_member_name;
|
||||
public:
|
||||
|
Reference in New Issue
Block a user