summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOleksandr Byelkin <sanja@mariadb.com>2022-08-15 13:19:15 +0200
committerOleksandr Byelkin <sanja@mariadb.com>2022-08-15 13:19:15 +0200
commit8cb75b9863a7043ebf2545158b3d2e634bca1831 (patch)
tree78a135a6fb23ce5a6ff960139eca653961e99689
parent10ed52767d25f3f9b9f5eee4983a3dee612ed2c6 (diff)
parent11d6de7032ab7fb221315ee5680504f34d1ba3ed (diff)
downloadmariadb-git-8cb75b9863a7043ebf2545158b3d2e634bca1831.tar.gz
Merge branch '10.9' into bb-10.9-release
-rw-r--r--mysql-test/main/func_json.result11
-rw-r--r--mysql-test/main/func_json.test9
-rw-r--r--sql/item_jsonfunc.cc155
3 files changed, 125 insertions, 50 deletions
diff --git a/mysql-test/main/func_json.result b/mysql-test/main/func_json.result
index e17c140e5ca..f5a1cf44b81 100644
--- a/mysql-test/main/func_json.result
+++ b/mysql-test/main/func_json.result
@@ -2295,5 +2295,16 @@ SELECT * FROM JSON_TABLE('{"foo":["bar","qux"]}','$**.*[0]' COLUMNS(col1 CHAR(8)
col1
bar
#
+# MDEV-29212: json_overlaps() does not check nested key-value pair correctly
+#
+SET @json1 = '{"kk":{"k1":"v1","k2":"v2"}}';
+SET @json2 = '{"kk":{"k1":"v1","k2":"v2","k3":"v3"}}';
+SELECT JSON_OVERLAPS(@json2, @json1);
+JSON_OVERLAPS(@json2, @json1)
+0
+SELECT JSON_OVERLAPS(@json1, @json2);
+JSON_OVERLAPS(@json1, @json2)
+0
+#
# End of 10.9 Test
#
diff --git a/mysql-test/main/func_json.test b/mysql-test/main/func_json.test
index f8ef72ffca7..9459f586bce 100644
--- a/mysql-test/main/func_json.test
+++ b/mysql-test/main/func_json.test
@@ -1548,5 +1548,14 @@ SELECT JSON_EXISTS(@json, '$[2][2][1 to 4]');
SELECT * FROM JSON_TABLE('{"foo":["bar","qux"]}','$**.*[0]' COLUMNS(col1 CHAR(8) PATH '$[0]')) AS jt;
--echo #
+--echo # MDEV-29212: json_overlaps() does not check nested key-value pair correctly
+--echo #
+
+SET @json1 = '{"kk":{"k1":"v1","k2":"v2"}}';
+SET @json2 = '{"kk":{"k1":"v1","k2":"v2","k3":"v3"}}';
+SELECT JSON_OVERLAPS(@json2, @json1);
+SELECT JSON_OVERLAPS(@json1, @json2);
+
+--echo #
--echo # End of 10.9 Test
--echo #
diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc
index 8f751717570..97cae6cc0a9 100644
--- a/sql/item_jsonfunc.cc
+++ b/sql/item_jsonfunc.cc
@@ -4360,7 +4360,7 @@ bool json_compare_arr_and_obj(json_engine_t *js, json_engine_t *value)
return TRUE;
*value= loc_val;
}
- if (!json_value_scalar(js))
+ if (js->value_type == JSON_VALUE_ARRAY)
json_skip_level(js);
}
return FALSE;
@@ -4446,76 +4446,131 @@ int json_find_overlap_with_array(json_engine_t *js, json_engine_t *value,
}
+int compare_nested_object(json_engine_t *js, json_engine_t *value)
+{
+ int result= 0;
+ const char *value_begin= (const char*)value->s.c_str-1;
+ const char *js_begin= (const char*)js->s.c_str-1;
+ json_skip_level(value);
+ json_skip_level(js);
+ const char *value_end= (const char*)value->s.c_str;
+ const char *js_end= (const char*)js->s.c_str;
+
+ String a(value_begin, value_end-value_begin,value->s.cs);
+ String b(js_begin, js_end-js_begin, js->s.cs);
+
+ DYNAMIC_STRING a_res, b_res;
+ if (init_dynamic_string(&a_res, NULL, 4096, 1024) ||
+ init_dynamic_string(&b_res, NULL, 4096, 1024))
+ {
+ goto error;
+ }
+ if (json_normalize(&a_res, a.ptr(), a.length(), value->s.cs) ||
+ json_normalize(&b_res, b.ptr(), b.length(), value->s.cs))
+ {
+ goto error;
+ }
+
+ result= strcmp(a_res.str, b_res.str) ? 0 : 1;
+
+ error:
+ dynstr_free(&a_res);
+ dynstr_free(&b_res);
+
+ return MY_TEST(result);
+}
int json_find_overlap_with_object(json_engine_t *js, json_engine_t *value,
bool compare_whole)
{
if (value->value_type == JSON_VALUE_OBJECT)
{
- /* Find at least one common key-value pair */
- json_string_t key_name;
- bool found_key= false, found_value= false;
- json_engine_t loc_js= *js;
- const uchar *k_start, *k_end;
-
- json_string_set_cs(&key_name, value->s.cs);
-
- while (json_scan_next(value) == 0 && value->state == JST_KEY)
+ if (compare_whole)
{
- k_start= value->s.c_str;
- do
- {
- k_end= value->s.c_str;
- } while (json_read_keyname_chr(value) == 0);
-
- if (unlikely(value->s.error))
- return FALSE;
+ return compare_nested_object(js, value);
+ }
+ else
+ {
+ /* Find at least one common key-value pair */
+ json_string_t key_name;
+ bool found_key= false, found_value= false;
+ json_engine_t loc_js= *js;
+ const uchar *k_start, *k_end;
- json_string_set_str(&key_name, k_start, k_end);
- found_key= find_key_in_object(js, &key_name);
- found_value= 0;
+ json_string_set_cs(&key_name, value->s.cs);
- if (found_key)
+ while (json_scan_next(value) == 0 && value->state == JST_KEY)
{
- if (json_read_value(js) || json_read_value(value))
+ k_start= value->s.c_str;
+ do
+ {
+ k_end= value->s.c_str;
+ } while (json_read_keyname_chr(value) == 0);
+
+ if (unlikely(value->s.error))
return FALSE;
- /*
- The value of key-value pair can be an be anything. If it is an object
- then we need to compare the whole value and if it is an array then
- we need to compare the elements in that order. So set compare_whole
- to true.
- */
- if (js->value_type == value->value_type)
- found_value= check_overlaps(js, value, true);
- if (found_value)
+ json_string_set_str(&key_name, k_start, k_end);
+ found_key= find_key_in_object(js, &key_name);
+ found_value= 0;
+
+ if (found_key)
{
- if (!compare_whole)
+ if (json_read_value(js) || json_read_value(value))
+ return FALSE;
+
+ /*
+ The value of key-value pair can be an be anything. If it is an object
+ then we need to compare the whole value and if it is an array then
+ we need to compare the elements in that order. So set compare_whole
+ to true.
+ */
+ if (js->value_type == value->value_type)
+ found_value= check_overlaps(js, value, true);
+ if (found_value)
+ {
+ /*
+ We have found at least one common key-value pair now.
+ No need to check for more key-value pairs. So skip remaining
+ jsons and return TRUE.
+ */
+ json_skip_current_level(js, value);
return TRUE;
- *js= loc_js;
+ }
+ else
+ {
+ /*
+ Key is found but value is not found. We have already
+ exhausted both values for current key. Hence "reset"
+ only js (first argument i.e json document) and
+ continue.
+ */
+ *js= loc_js;
+ continue;
+ }
}
else
{
- if (compare_whole)
- {
- json_skip_current_level(js, value);
+ /*
+ key is not found. So no need to check for value for that key.
+ Read the value anyway so we get the "type" of json value.
+ If is is non-scalar then skip the entire value
+ (scalar values get exhausted while reading so no need to skip them).
+ Then reset the json doc again.
+ */
+ if (json_read_value(value))
return FALSE;
- }
+ if (!json_value_scalar(value))
+ json_skip_level(value);
*js= loc_js;
}
}
- else
- {
- if (compare_whole)
- {
- json_skip_current_level(js, value);
- return FALSE;
- }
- json_skip_key(value);
- *js= loc_js;
- }
+ /*
+ At this point we have already returned true if any intersection exists.
+ So skip jsons if not exhausted and return false.
+ */
+ json_skip_current_level(js, value);
+ return FALSE;
}
- json_skip_current_level(js, value);
- return compare_whole ? TRUE : FALSE;
}
else if (value->value_type == JSON_VALUE_ARRAY)
{