1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

MDEV-11858 json_merge() concatenates instead of merging.

Fix json_merge implementation.
This commit is contained in:
Alexey Botchkov
2017-02-10 01:05:27 +04:00
parent 3ae038b732
commit 25aaecb240
5 changed files with 236 additions and 71 deletions

View File

@ -555,3 +555,15 @@ json_set('[]', '$[0][0][0]', 100)
SELECT JSON_search( '{"": "a"}', "one", 'a'); SELECT JSON_search( '{"": "a"}', "one", 'a');
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]}}

View File

@ -819,7 +819,7 @@ select json_merge( '[1, 2]', '[3, 4' );
json_merge( '[1, 2]', '[3, 4' ) json_merge( '[1, 2]', '[3, 4' )
NULL NULL
Warnings: 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 error ER_INVALID_JSON_TEXT_IN_PARAM
select json_merge( '[1, 2', '[3, 4]' ); select json_merge( '[1, 2', '[3, 4]' );
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] [{"a": 2}, 1, 2]
select json_merge( '{"a": 1, "b": 2 }', '{"b": 3, "d": 4 }' ); select json_merge( '{"a": 1, "b": 2 }', '{"b": 3, "d": 4 }' );
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 }' ); select json_merge( '{"a": 1, "b": 2 }', '{"b": [3, 4], "d": 4 }' );
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 }' ); select json_merge( '{"a": 1, "b": [2, 3] }', '{"b": 4, "d": 4 }' );
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 }' ); 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 }' ) 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 }' ); 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 }' ) 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 }' ); 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 }' ) 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 }' ); 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 }' ) 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] }' ); 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] }' ) 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 } }' ); 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 } }' ) 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' ); select json_merge( '1', '2', '3' );
json_merge( '1', '2', '3' ) json_merge( '1', '2', '3' )
[1, 2, 3] [1, 2, 3]
@ -898,7 +898,7 @@ json_merge
'{ "d": false, "b": { "g": 3, "d": 5 }, "f": [ 1, 2 ] }', '{ "d": false, "b": { "g": 3, "d": 5 }, "f": [ 1, 2 ] }',
'{ "m": true, "b": { "h": 8, "d": 4 }, "e": [ 3, 4 ] }' '{ "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 SELECT JSON_MERGE
( (
'{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }', '{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
@ -920,7 +920,7 @@ JSON_MERGE
'{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }', '{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
'{ "b": [ false, 34 ] }' '{ "b": [ false, 34 ] }'
) )
{"a": "foo", "b": [true, {"c": 123}], "b": [false, 34]} {"a": "foo", "b": [true, {"c": 123}, false, 34]}
SELECT JSON_MERGE SELECT JSON_MERGE
( (
'{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }', '{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
@ -931,7 +931,7 @@ JSON_MERGE
'{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }', '{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
'{ "b": "bar" }' '{ "b": "bar" }'
) )
{"a": "foo", "b": [true, {"c": 123}], "b": "bar"} {"a": "foo", "b": [true, {"c": 123}, "bar"]}
SELECT JSON_MERGE SELECT JSON_MERGE
( (
'{ "a" : { "b" : 1 } }', '{ "a" : { "b" : 1 } }',
@ -942,7 +942,7 @@ JSON_MERGE
'{ "a" : { "b" : 1 } }', '{ "a" : { "b" : 1 } }',
'{ "a" : { "c" : 1 } }' '{ "a" : { "c" : 1 } }'
) )
{"a": {"b": 1}, "a": {"c": 1}} {"a": {"b": 1, "c": 1}}
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Test of JSON_TYPE function. # Test of JSON_TYPE function.
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------

View File

@ -225,3 +225,12 @@ SELECT json_set('[]', '$[0][0][0]', 100);
# MDEV-11857 json_search() shows "Out of memory" with empty key. # MDEV-11857 json_search() shows "Out of memory" with empty key.
# #
SELECT JSON_search( '{"": "a"}', "one", 'a'); 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}}') ;

View File

@ -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) String *Item_func_json_merge::val_str(String *str)
{ {
DBUG_ASSERT(fixed == 1); 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++) 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); js2= args[n_arg]->val_str(&tmp_js2);
if (args[n_arg]->null_value) if (args[n_arg]->null_value)
goto null_return; 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(), json_scan_start(&je2, js2->charset(),(const uchar *) js2->ptr(),
(const uchar *) js2->ptr() + js2->length()); (const uchar *) js2->ptr() + js2->length());
if (json_read_value(&je1) || json_read_value(&je2)) if (do_merge(str, &je1, &je2))
goto error_return; 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. */ /* Swap str and js1. */
if (str == &tmp_js1) if (str == &tmp_js1)

View File

@ -1197,13 +1197,6 @@ int json_skip_to_level(json_engine_t *j, int level)
int json_skip_key(json_engine_t *j) 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)) if (json_read_value(j))
return 1; return 1;