From f76d5fefb818760f41488c1793fca27d97c9c2a0 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Tue, 14 Feb 2017 17:51:03 +0400 Subject: [PATCH] MDEV-11439 No data type JSON, but CAST(something AS JSON) pretends to work. json_detailed() fixed --- mysql-test/r/func_json.result | 22 +++++++++++ mysql-test/t/func_json.test | 4 ++ sql/item_create.cc | 25 ++++++++++--- sql/item_jsonfunc.cc | 69 ++++++++++++++++++++++++++++++++--- sql/item_jsonfunc.h | 5 ++- 5 files changed, 112 insertions(+), 13 deletions(-) diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result index e8d11fce56f..4c1c82ed331 100644 --- a/mysql-test/r/func_json.result +++ b/mysql-test/r/func_json.result @@ -567,3 +567,25 @@ json_merge('{"a":{"u":12, "x":"b"}}', '{"a":{"x":"c"}}') select json_merge('{"a":{"u":12, "x":"b", "r":1}}', '{"a":{"x":"c", "r":2}}') ; json_merge('{"a":{"u":12, "x":"b", "r":1}}', '{"a":{"x":"c", "r":2}}') {"a": {"u": 12, "x": ["b", "c"], "r": [1, 2]}} +select json_compact('{"a":1, "b":[1,2,3], "c":{"aa":"v1", "bb": "v2"}}'); +json_compact('{"a":1, "b":[1,2,3], "c":{"aa":"v1", "bb": "v2"}}') +{"a":1,"b":[1,2,3],"c":{"aa":"v1","bb":"v2"}} +select json_loose('{"a":1, "b":[1,2,3], "c":{"aa":"v1", "bb": "v2"}}'); +json_loose('{"a":1, "b":[1,2,3], "c":{"aa":"v1", "bb": "v2"}}') +{"a": 1, "b": [1, 2, 3], "c": {"aa": "v1", "bb": "v2"}} +select json_detailed('{"a":1, "b":[1,2,3], "c":{"aa":"v1", "bb": "v2"}}'); +json_detailed('{"a":1, "b":[1,2,3], "c":{"aa":"v1", "bb": "v2"}}') +{ + "a": 1, + "b": + [ + 1, + 2, + 3 + ], + "c": + { + "aa": "v1", + "bb": "v2" + } +} diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test index aedc65159c5..be56eb46d09 100644 --- a/mysql-test/t/func_json.test +++ b/mysql-test/t/func_json.test @@ -234,3 +234,7 @@ select json_merge('{"a":"b"}', '{"a":"c"}') ; select json_merge('{"a":{"x":"b"}}', '{"a":"c"}') ; select json_merge('{"a":{"u":12, "x":"b"}}', '{"a":{"x":"c"}}') ; select json_merge('{"a":{"u":12, "x":"b", "r":1}}', '{"a":{"x":"c", "r":2}}') ; + +select json_compact('{"a":1, "b":[1,2,3], "c":{"aa":"v1", "bb": "v2"}}'); +select json_loose('{"a":1, "b":[1,2,3], "c":{"aa":"v1", "bb": "v2"}}'); +select json_detailed('{"a":1, "b":[1,2,3], "c":{"aa":"v1", "bb": "v2"}}'); diff --git a/sql/item_create.cc b/sql/item_create.cc index d2be36e105f..dee1db1ee2e 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -1788,10 +1788,10 @@ protected: }; -class Create_func_json_detailed : public Create_func_arg2 +class Create_func_json_detailed: public Create_native_func { public: - virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); + virtual Item *create_native(THD *thd, LEX_STRING name, List *item_list); static Create_func_json_detailed s_singleton; @@ -1801,7 +1801,6 @@ protected: }; - class Create_func_json_type : public Create_func_arg1 { public: @@ -5046,9 +5045,25 @@ Create_func_json_exists::create_2_arg(THD *thd, Item *arg1, Item *arg2) Create_func_json_detailed Create_func_json_detailed::s_singleton; Item* -Create_func_json_detailed::create_2_arg(THD *thd, Item *arg1, Item *arg2) +Create_func_json_detailed::create_native(THD *thd, LEX_STRING name, + List *item_list) { - return new (thd->mem_root) Item_func_json_format(thd, arg1, arg2); + Item *func= NULL; + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 1 || arg_count > 2 /* json_doc, [path]...*/) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + } + else + { + func= new (thd->mem_root) Item_func_json_format(thd, *item_list); + } + + return func; } diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index ae885dc7702..d955ddb53df 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -113,8 +113,24 @@ static int st_append_escaped(String *s, const String *a) } +static const int TAB_SIZE_LIMIT= 8; +static const char tab_arr[TAB_SIZE_LIMIT+1]= " "; + +static int append_tab(String *js, int depth, int tab_size) +{ + if (js->append("\n", 1)) + return 1; + for (int i=0; iappend(tab_arr, tab_size)) + return 1; + } + return 0; +} + + static int json_nice(json_engine_t *je, String *nice_js, - Item_func_json_format::formats mode) + Item_func_json_format::formats mode, int tab_size=4) { int depth= 0; const char *comma, *colon; @@ -122,19 +138,24 @@ static int json_nice(json_engine_t *je, String *nice_js, int first_value= 1; DBUG_ASSERT(je->s.cs == nice_js->charset()); + DBUG_ASSERT(mode != Item_func_json_format::DETAILED || + (tab_size >= 0 && tab_size <= TAB_SIZE_LIMIT)); + comma= ", "; + colon= "\": "; if (mode == Item_func_json_format::LOOSE) { - comma= ", "; comma_len= 2; - colon= "\": "; + colon_len= 3; + } + else if (mode == Item_func_json_format::DETAILED) + { + comma_len= 1; colon_len= 3; } else { - comma= ","; comma_len= 1; - colon= "\":"; colon_len= 2; } @@ -158,6 +179,10 @@ static int json_nice(json_engine_t *je, String *nice_js, if (!first_value) nice_js->append(comma, comma_len); + if (mode == Item_func_json_format::DETAILED && + append_tab(nice_js, depth, tab_size)) + goto error; + nice_js->append("\"", 1); append_simple(nice_js, key_start, key_end - key_start); nice_js->append(colon, colon_len); @@ -170,6 +195,11 @@ static int json_nice(json_engine_t *je, String *nice_js, if (!first_value) nice_js->append(comma, comma_len); + if (mode == Item_func_json_format::DETAILED && + depth > 0 && + append_tab(nice_js, depth, tab_size)) + goto error; + handle_value: if (json_read_value(je)) goto error; @@ -183,6 +213,10 @@ handle_value: } else { + if (mode == Item_func_json_format::DETAILED && + depth > 0 && + append_tab(nice_js, depth, tab_size)) + goto error; nice_js->append((je->value_type == JSON_VALUE_OBJECT) ? "{" : "[", 1); first_value= 1; depth++; @@ -193,6 +227,9 @@ handle_value: case JST_OBJ_END: case JST_ARRAY_END: depth--; + if (mode == Item_func_json_format::DETAILED && + append_tab(nice_js, depth, tab_size)) + goto error; nice_js->append((je->state == JST_OBJ_END) ? "}": "]", 1); first_value= 0; break; @@ -2941,15 +2978,35 @@ String *Item_func_json_format::val_str(String *str) { String *js= args[0]->val_str(&tmp_js); json_engine_t je; + int tab_size; + if ((null_value= args[0]->null_value)) return 0; + if (fmt == DETAILED) + { + tab_size= 4; + if (arg_count > 1) + { + tab_size= args[1]->val_int(); + if (args[1]->null_value) + { + null_value= 1; + return 0; + } + } + if (tab_size < 0) + tab_size= 0; + else if (tab_size > TAB_SIZE_LIMIT) + tab_size= TAB_SIZE_LIMIT; + } + json_scan_start(&je, js->charset(), (const uchar *) js->ptr(), (const uchar *) js->ptr()+js->length()); str->length(0); str->set_charset(js->charset()); - if (json_nice(&je, str, fmt)) + if (json_nice(&je, str, fmt, tab_size)) { null_value= 1; report_json_error(js, &je, 0); diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index 941123041d2..0eedfa18be1 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -443,8 +443,9 @@ protected: public: Item_func_json_format(THD *thd, Item *js, formats format): Item_str_func(thd, js), fmt(format) {} - Item_func_json_format(THD *thd, Item *js, Item *tabsize): - Item_str_func(thd, js, tabsize), fmt(DETAILED) {} + Item_func_json_format(THD *thd, List &list): + Item_str_func(thd, list), fmt(DETAILED) {} + const char *func_name() const; void fix_length_and_dec(); String *val_str(String *str);