diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/my_json_writer.cc | 102 | ||||
-rw-r--r-- | sql/my_json_writer.h | 52 |
2 files changed, 143 insertions, 11 deletions
diff --git a/sql/my_json_writer.cc b/sql/my_json_writer.cc index 8e35b25b822..c95e4c6d657 100644 --- a/sql/my_json_writer.cc +++ b/sql/my_json_writer.cc @@ -39,7 +39,7 @@ inline void Json_writer::on_start_object() #if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST) if(!fmt_helper.is_making_writer_calls()) { - VALIDITY_ASSERT(got_name == named_item_expected()); + validate_named_item_expectation(true); named_items_expectation.push_back(true); } #endif @@ -68,7 +68,7 @@ void Json_writer::start_array() #if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST) if(!fmt_helper.is_making_writer_calls()) { - VALIDITY_ASSERT(got_name == named_item_expected()); + validate_named_item_expectation(true); named_items_expectation.push_back(false); got_name= false; } @@ -91,9 +91,20 @@ void Json_writer::start_array() void Json_writer::end_object() { #if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST) - VALIDITY_ASSERT(named_item_expected()); + if (!named_item_expected()) + { + print_json_tail_to_stderr( + "end_object failed since an unnamed item is expected"); + VALIDITY_ASSERT(false); + } named_items_expectation.pop_back(); - VALIDITY_ASSERT(!got_name); + + if (got_name) + { + print_json_tail_to_stderr( + "end_object failed since got_name==true"); + VALIDITY_ASSERT(false); + } got_name= false; #endif indent_level-=INDENT_SIZE; @@ -107,7 +118,12 @@ void Json_writer::end_object() void Json_writer::end_array() { #if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST) - VALIDITY_ASSERT(!named_item_expected()); + if (named_item_expected()) + { + print_json_tail_to_stderr( + "end_array failed since a named item is expected"); + VALIDITY_ASSERT(false); + } named_items_expectation.pop_back(); got_name= false; #endif @@ -130,8 +146,18 @@ Json_writer& Json_writer::add_member(const char *name, size_t len) { if (!fmt_helper.on_add_member(name, len)) { +#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST) // assert that we are in an object - DBUG_ASSERT(!element_started); + if (element_started) + { + char err_descr[200]; + snprintf(err_descr, sizeof(err_descr), + "failed to add_member(\"%s\", %zu) since element_started=false", + name, len); + print_json_tail_to_stderr(err_descr); + VALIDITY_ASSERT(false); + } +#endif start_element(); output.append('"'); @@ -242,8 +268,12 @@ void Json_writer::add_unquoted_str(const char* str) void Json_writer::add_unquoted_str(const char* str, size_t len) { - VALIDITY_ASSERT(fmt_helper.is_making_writer_calls() || - got_name == named_item_expected()); +#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST) + if (!fmt_helper.is_making_writer_calls()) + { + validate_named_item_expectation(); + } +#endif if (on_add_str(str, len)) return; @@ -275,8 +305,12 @@ void Json_writer::add_str(const char *str) void Json_writer::add_str(const char* str, size_t num_bytes) { - VALIDITY_ASSERT(fmt_helper.is_making_writer_calls() || - got_name == named_item_expected()); +#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST) + if (!fmt_helper.is_making_writer_calls()) + { + validate_named_item_expectation(); + } +#endif if (on_add_str(str, num_bytes)) return; @@ -294,6 +328,53 @@ void Json_writer::add_str(const String &str) add_str(str.ptr(), str.length()); } +#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST) +void Json_writer::print_json_tail_to_stderr(const char *err_descr) +{ + size_t pos_to_print_from= + output.get_string()->length() < MAX_JSON_LEN_FOR_STDERR ? + 0 : output.get_string()->length() - MAX_JSON_LEN_FOR_STDERR; + auto str_to_print= output.get_string()->c_ptr() + pos_to_print_from; + fprintf(stderr, "JSON writer error: %s.\nJSON tail: %s\n", + err_descr, str_to_print); +} + +void Json_writer::validate_named_item_expectation(bool fix_wth_implicit_member) +{ + if (got_name != named_item_expected()) + { + if (!got_name && fix_wth_implicit_member) + { + /* + Context is expecting a named member but has an unnamed one. + Add a member with automatically assigned name to preserve + JSON document correctness + */ + add_implicit_member(); + return; + } + const char *expected= named_item_expected() ? + "named" : "unnamed"; + const char *actual= named_item_expected() ? + "unnamed" : "named"; + char err_descr[100]; + snprintf(err_descr, sizeof(err_descr), + "expected a %s member but a %s one has been added", + expected, actual); + print_json_tail_to_stderr(err_descr); + VALIDITY_ASSERT(false); + } +} + +void Json_writer::add_implicit_member() +{ + char name[50]; + sprintf(name, "implicitly_added_member_#%u", implicit_member_num); + add_member(name); + implicit_member_num++; +} +#endif + Json_writer_temp_disable::Json_writer_temp_disable(THD *thd_arg) { thd= thd_arg; @@ -308,6 +389,7 @@ bool Single_line_formatting_helper::on_add_member(const char *name, size_t len) { DBUG_ASSERT(state== INACTIVE || state == DISABLED); + if (state != DISABLED) { // remove everything from the array diff --git a/sql/my_json_writer.h b/sql/my_json_writer.h index 4b69ba70675..d6b8ade091f 100644 --- a/sql/my_json_writer.h +++ b/sql/my_json_writer.h @@ -189,7 +189,7 @@ public: str.append(c); } - const String *get_string() { return &str; } + String *get_string() { return &str; } size_t length() { return str.length(); } private: String str; @@ -296,6 +296,56 @@ private: void start_element(); void start_sub_element(); +#if !defined(NDEBUG) || defined(JSON_WRITER_UNIT_TEST) + /* + Prints the tail of JSON composed so far to stderr. + Output is limited by MAX_JSON_LEN_FOR_STDERR characters. + Format is as follows: + "JSON writer error: <description of the error>. + JSON tail: <...JSON...> + */ + void print_json_tail_to_stderr(const char *err_descr); + + enum { MAX_JSON_LEN_FOR_STDERR = 1000 }; + + /* + If the context of the JSON document is expecting a unnamed member + but a named one is added this will trigger an assertion failure. + The same works vice versa unless the "fix_wth_implicit_member" + flag is set. When fix_wth_implicit_member == true and the context is + expecting a named member but has got an unnamed one, + the function will implicitly add a JSON member and automatically assign + its name. In this case correctness of JSON document is preserved + and no assertion will trigger. + Example: + transformation": { + "select_id": 2, + { + "join_optimization": { + "select_id": 3, + "steps": [] + } + + will be converted to + transformation": { + "select_id": 2, + "implicitly_added_member_#1": { + "join_optimization": { + "select_id": 3, + "steps": [] + } + */ + void validate_named_item_expectation(bool fix_wth_implicit_member= false); + + void add_implicit_member(); + + /* + This incremental counter is used to automatically assign names + for implicitly added members + */ + unsigned int implicit_member_num= 1; +#endif + public: String_with_limit output; }; |