diff options
-rw-r--r-- | mysql-test/r/explain_json.result | 31 | ||||
-rw-r--r-- | mysql-test/t/explain_json.test | 18 | ||||
-rw-r--r-- | sql/my_json_writer.cc | 205 | ||||
-rw-r--r-- | sql/my_json_writer.h | 77 |
4 files changed, 306 insertions, 25 deletions
diff --git a/mysql-test/r/explain_json.result b/mysql-test/r/explain_json.result index de902a7c31a..6f38892ef05 100644 --- a/mysql-test/r/explain_json.result +++ b/mysql-test/r/explain_json.result @@ -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; diff --git a/mysql-test/t/explain_json.test b/mysql-test/t/explain_json.test index e9306101f81..aedff8d78cb 100644 --- a/mysql-test/t/explain_json.test +++ b/mysql-test/t/explain_json.test @@ -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; diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc index d07b13a3d65..201b2f955ff 100644 --- a/sql/my_json_writer.cc +++ b/sql/my_json_writer.cc @@ -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; +} + diff --git a/sql/my_json_writer.h b/sql/my_json_writer.h index 101df7c3215..d92f98f20d8 100644 --- a/sql/my_json_writer.h +++ b/sql/my_json_writer.h @@ -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: |