diff --git a/mysql-test/r/func_json.result b/mysql-test/r/func_json.result index e72da47e4ae..bdee7cb9f7f 100644 --- a/mysql-test/r/func_json.result +++ b/mysql-test/r/func_json.result @@ -555,3 +555,15 @@ json_set('[]', '$[0][0][0]', 100) SELECT JSON_search( '{"": "a"}', "one", 'a'); JSON_search( '{"": "a"}', "one", 'a') "$." +select json_merge('{"a":"b"}', '{"a":"c"}') ; +json_merge('{"a":"b"}', '{"a":"c"}') +{"a": ["b", "c"]} +select json_merge('{"a":{"x":"b"}}', '{"a":"c"}') ; +json_merge('{"a":{"x":"b"}}', '{"a":"c"}') +{"a": [{"x": "b"}, "c"]} +select json_merge('{"a":{"u":12, "x":"b"}}', '{"a":{"x":"c"}}') ; +json_merge('{"a":{"u":12, "x":"b"}}', '{"a":{"x":"c"}}') +{"a": {"u": 12, "x": ["b", "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]}} diff --git a/mysql-test/suite/json/r/json_no_table.result b/mysql-test/suite/json/r/json_no_table.result index 3201d0fc432..9ea5e94c96e 100644 --- a/mysql-test/suite/json/r/json_no_table.result +++ b/mysql-test/suite/json/r/json_no_table.result @@ -819,7 +819,7 @@ select json_merge( '[1, 2]', '[3, 4' ); json_merge( '[1, 2]', '[3, 4' ) NULL Warnings: -Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_merge' +Warning 4037 Unexpected end of JSON text in argument 2 to function 'json_merge' error ER_INVALID_JSON_TEXT_IN_PARAM select json_merge( '[1, 2', '[3, 4]' ); json_merge( '[1, 2', '[3, 4]' ) @@ -855,31 +855,31 @@ json_merge( '{ "a": 2 }', '[1, 2]' ) [{"a": 2}, 1, 2] select json_merge( '{"a": 1, "b": 2 }', '{"b": 3, "d": 4 }' ); json_merge( '{"a": 1, "b": 2 }', '{"b": 3, "d": 4 }' ) -{"a": 1, "b": 2, "b": 3, "d": 4} +{"a": 1, "b": [2, 3], "d": 4} select json_merge( '{"a": 1, "b": 2 }', '{"b": [3, 4], "d": 4 }' ); json_merge( '{"a": 1, "b": 2 }', '{"b": [3, 4], "d": 4 }' ) -{"a": 1, "b": 2, "b": [3, 4], "d": 4} +{"a": 1, "b": [2, 3, 4], "d": 4} select json_merge( '{"a": 1, "b": [2, 3] }', '{"b": 4, "d": 4 }' ); json_merge( '{"a": 1, "b": [2, 3] }', '{"b": 4, "d": 4 }' ) -{"a": 1, "b": [2, 3], "b": 4, "d": 4} +{"a": 1, "b": [2, 3, 4], "d": 4} select json_merge( '{"a": 1, "b": 2 }', '{"b": {"e": 7, "f": 8}, "d": 4 }' ); json_merge( '{"a": 1, "b": 2 }', '{"b": {"e": 7, "f": 8}, "d": 4 }' ) -{"a": 1, "b": 2, "b": {"e": 7, "f": 8}, "d": 4} +{"a": 1, "b": [2, {"e": 7, "f": 8}], "d": 4} select json_merge( '{"b": {"e": 7, "f": 8}, "d": 4 }', '{"a": 1, "b": 2 }' ); json_merge( '{"b": {"e": 7, "f": 8}, "d": 4 }', '{"a": 1, "b": 2 }' ) -{"b": {"e": 7, "f": 8}, "d": 4, "a": 1, "b": 2} +{"b": [{"e": 7, "f": 8}, 2], "d": 4, "a": 1} select json_merge( '{"a": 1, "b": [2, 9] }', '{"b": [10, 11], "d": 4 }' ); json_merge( '{"a": 1, "b": [2, 9] }', '{"b": [10, 11], "d": 4 }' ) -{"a": 1, "b": [2, 9], "b": [10, 11], "d": 4} +{"a": 1, "b": [2, 9, 10, 11], "d": 4} select json_merge( '{"a": 1, "b": [2, 9] }', '{"b": {"e": 7, "f": 8}, "d": 4 }' ); json_merge( '{"a": 1, "b": [2, 9] }', '{"b": {"e": 7, "f": 8}, "d": 4 }' ) -{"a": 1, "b": [2, 9], "b": {"e": 7, "f": 8}, "d": 4} +{"a": 1, "b": [2, 9, {"e": 7, "f": 8}], "d": 4} select json_merge( '{"b": {"e": 7, "f": 8}, "d": 4 }', '{"a": 1, "b": [2, 9] }' ); json_merge( '{"b": {"e": 7, "f": 8}, "d": 4 }', '{"a": 1, "b": [2, 9] }' ) -{"b": {"e": 7, "f": 8}, "d": 4, "a": 1, "b": [2, 9]} +{"b": [{"e": 7, "f": 8}, 2, 9], "d": 4, "a": 1} select json_merge( '{"b": {"e": 7, "f": 8}, "d": 4 }', '{ "a": 1, "b": {"e": 20, "g": 21 } }' ); json_merge( '{"b": {"e": 7, "f": 8}, "d": 4 }', '{ "a": 1, "b": {"e": 20, "g": 21 } }' ) -{"b": {"e": 7, "f": 8}, "d": 4, "a": 1, "b": {"e": 20, "g": 21}} +{"b": {"e": [7, 20], "f": 8, "g": 21}, "d": 4, "a": 1} select json_merge( '1', '2', '3' ); json_merge( '1', '2', '3' ) [1, 2, 3] @@ -898,7 +898,7 @@ json_merge '{ "d": false, "b": { "g": 3, "d": 5 }, "f": [ 1, 2 ] }', '{ "m": true, "b": { "h": 8, "d": 4 }, "e": [ 3, 4 ] }' ) -{"a": true, "b": {"c": 3, "d": 4}, "e": [1, 2], "d": false, "b": {"g": 3, "d": 5}, "f": [1, 2], "m": true, "b": {"h": 8, "d": 4}, "e": [3, 4]} +{"a": true, "b": {"c": 3, "d": [4, 5, 4], "g": 3, "h": 8}, "e": [1, 2, 3, 4], "d": false, "f": [1, 2], "m": true} SELECT JSON_MERGE ( '{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }', @@ -920,7 +920,7 @@ JSON_MERGE '{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }', '{ "b": [ false, 34 ] }' ) -{"a": "foo", "b": [true, {"c": 123}], "b": [false, 34]} +{"a": "foo", "b": [true, {"c": 123}, false, 34]} SELECT JSON_MERGE ( '{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }', @@ -931,7 +931,7 @@ JSON_MERGE '{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }', '{ "b": "bar" }' ) -{"a": "foo", "b": [true, {"c": 123}], "b": "bar"} +{"a": "foo", "b": [true, {"c": 123}, "bar"]} SELECT JSON_MERGE ( '{ "a" : { "b" : 1 } }', @@ -942,7 +942,7 @@ JSON_MERGE '{ "a" : { "b" : 1 } }', '{ "a" : { "c" : 1 } }' ) -{"a": {"b": 1}, "a": {"c": 1}} +{"a": {"b": 1, "c": 1}} # ---------------------------------------------------------------------- # Test of JSON_TYPE function. # ---------------------------------------------------------------------- diff --git a/mysql-test/t/func_json.test b/mysql-test/t/func_json.test index 9b4673d19a1..aedc65159c5 100644 --- a/mysql-test/t/func_json.test +++ b/mysql-test/t/func_json.test @@ -225,3 +225,12 @@ SELECT json_set('[]', '$[0][0][0]', 100); # MDEV-11857 json_search() shows "Out of memory" with empty key. # SELECT JSON_search( '{"": "a"}', "one", 'a'); + +# +# MDEV-11858 json_merge() concatenates instead of merging. +# + +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}}') ; diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 3cf7b55d6e3..ae885dc7702 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -1721,6 +1721,203 @@ err_return: } +static int do_merge(String *str, json_engine_t *je1, json_engine_t *je2) +{ + if (json_read_value(je1) || json_read_value(je2)) + return 1; + + if (je1->value_type == JSON_VALUE_OBJECT && + je2->value_type == JSON_VALUE_OBJECT) + { + json_engine_t sav_je1= *je1; + json_engine_t sav_je2= *je2; + + int first_key= 1; + json_string_t key_name; + + json_string_set_cs(&key_name, je1->s.cs); + + if (str->append("{", 1)) + return 3; + while (json_scan_next(je1) == 0 && + je1->state != JST_OBJ_END) + { + const uchar *key_start, *key_end; + /* Loop through the Json_1 keys and compare with the Json_2 keys. */ + DBUG_ASSERT(je1->state == JST_KEY); + key_start= je1->s.c_str; + do + { + key_end= je1->s.c_str; + } while (json_read_keyname_chr(je1) == 0); + + if (je1->s.error) + return 1; + + if (first_key) + first_key= 0; + else + { + if (str->append(", ", 2)) + return 3; + *je2= sav_je2; + } + + if (str->append("\"", 1) || + append_simple(str, key_start, key_end - key_start) || + str->append("\":", 2)) + return 3; + + while (json_scan_next(je2) == 0 && + je2->state != JST_OBJ_END) + { + int ires; + DBUG_ASSERT(je2->state == JST_KEY); + json_string_set_str(&key_name, key_start, key_end); + if (!json_key_matches(je2, &key_name)) + { + if (je2->s.error || json_skip_key(je2)) + return 2; + continue; + } + + /* Json_2 has same key as Json_1. Merge them. */ + if ((ires= do_merge(str, je1, je2))) + return ires; + goto merged_j1; + } + if (je2->s.error) + return 2; + + key_start= je1->s.c_str; + /* Just append the Json_1 key value. */ + if (json_skip_key(je1)) + return 1; + if (append_simple(str, key_start, je1->s.c_str - key_start)) + return 3; + +merged_j1: + continue; + } + + *je2= sav_je2; + /* + Now loop through the Json_2 keys. + Skip if there is same key in Json_1 + */ + while (json_scan_next(je2) == 0 && + je2->state != JST_OBJ_END) + { + const uchar *key_start, *key_end; + DBUG_ASSERT(je2->state == JST_KEY); + key_start= je2->s.c_str; + do + { + key_end= je2->s.c_str; + } while (json_read_keyname_chr(je2) == 0); + + if (je2->s.error) + return 1; + + *je1= sav_je1; + while (json_scan_next(je1) == 0 && + je1->state != JST_OBJ_END) + { + DBUG_ASSERT(je1->state == JST_KEY); + json_string_set_str(&key_name, key_start, key_end); + if (!json_key_matches(je1, &key_name)) + { + if (je1->s.error || json_skip_key(je1)) + return 2; + continue; + } + if (json_skip_key(je2) || + json_skip_level(je1)) + return 1; + goto continue_j2; + } + + if (je1->s.error) + return 2; + + if (first_key) + first_key= 0; + else if (str->append(", ", 2)) + return 3; + + if (json_skip_key(je2)) + return 1; + + if (str->append("\"", 1) || + append_simple(str, key_start, je2->s.c_str - key_start)) + return 3; + +continue_j2: + continue; + } + + if (str->append("}", 1)) + return 3; + } + else + { + const uchar *end1, *beg1, *end2, *beg2; + + beg1= je1->value_begin; + + /* Merge as a single array. */ + if (je1->value_type == JSON_VALUE_ARRAY) + { + if (json_skip_level(je1)) + return 1; + end1= je1->s.c_str - je1->sav_c_len; + } + else + { + if (str->append("[", 1)) + return 3; + if (je1->value_type == JSON_VALUE_OBJECT) + { + if (json_skip_level(je1)) + return 1; + end1= je1->s.c_str; + } + else + end1= je1->value_end; + } + + if (str->append((const char*) beg1, end1 - beg1), + str->append(", ", 2)) + return 3; + + if (json_value_scalar(je2)) + { + beg2= je2->value_begin; + end2= je2->value_end; + } + else + { + if (je2->value_type == JSON_VALUE_OBJECT) + beg2= je2->value_begin; + else + beg2= je2->s.c_str; + if (json_skip_level(je2)) + return 2; + end2= je2->s.c_str; + } + + if (str->append((const char*) beg2, end2 - beg2)) + return 3; + + if (je2->value_type != JSON_VALUE_ARRAY && + str->append("]", 1)) + return 3; + } + + return 0; +} + + String *Item_func_json_merge::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -1733,6 +1930,9 @@ String *Item_func_json_merge::val_str(String *str) for (n_arg=1; n_arg < arg_count; n_arg++) { + str->set_charset(js1->charset()); + str->length(0); + js2= args[n_arg]->val_str(&tmp_js2); if (args[n_arg]->null_value) goto null_return; @@ -1743,58 +1943,9 @@ String *Item_func_json_merge::val_str(String *str) json_scan_start(&je2, js2->charset(),(const uchar *) js2->ptr(), (const uchar *) js2->ptr() + js2->length()); - if (json_read_value(&je1) || json_read_value(&je2)) + if (do_merge(str, &je1, &je2)) goto error_return; - str->length(0); - if (je1.value_type == JSON_VALUE_OBJECT && - je2.value_type == JSON_VALUE_OBJECT) - { - /* Wrap as a single objects. */ - if (json_skip_level(&je1)) - goto error_return; - if (str->append(js1->ptr(), - ((const char *)je1.s.c_str - js1->ptr()) - je1.sav_c_len) || - str->append(", ", 2) || - str->append((const char *)je2.s.c_str, - js2->length() - ((const char *)je2.s.c_str - js2->ptr()))) - goto error_return; - } - else - { - const char *end1, *beg2; - - /* Merge as a single array. */ - if (je1.value_type == JSON_VALUE_ARRAY) - { - if (json_skip_level(&je1)) - goto error_return; - end1= (const char *) (je1.s.c_str - je1.sav_c_len); - } - else - { - if (str->append("[", 1)) - goto error_return; - end1= js1->end(); - } - - if (str->append(js1->ptr(), end1 - js1->ptr()), - str->append(", ", 2)) - goto error_return; - - if (je2.value_type == JSON_VALUE_ARRAY) - beg2= (const char *) je2.s.c_str; - else - beg2= js2->ptr(); - - if (str->append(beg2, js2->end() - beg2)) - goto error_return; - - if (je2.value_type != JSON_VALUE_ARRAY && - str->append("]", 1)) - goto error_return; - } - { /* Swap str and js1. */ if (str == &tmp_js1) diff --git a/strings/json_lib.c b/strings/json_lib.c index 213be62aa22..2a5d71a2133 100644 --- a/strings/json_lib.c +++ b/strings/json_lib.c @@ -1197,13 +1197,6 @@ int json_skip_to_level(json_engine_t *j, int level) int json_skip_key(json_engine_t *j) { - if (j->state == JST_KEY) - { - while (json_read_keyname_chr(j) == 0); - if (j->s.error) - return 1; - } - if (json_read_value(j)) return 1;