summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRucha Deodhar <rucha.deodhar@mariadb.com>2022-01-30 15:45:25 +0530
committerRucha Deodhar <rucha.deodhar@mariadb.com>2022-03-30 15:09:01 +0530
commita653dde279a3d35c0f7862e14b94d89025a121f1 (patch)
treec3be0cfe8ff7c54abbff88465bddbbb0f2f95e5e
parent8680eedb2650ba017305cf8db26c96d056196375 (diff)
downloadmariadb-git-a653dde279a3d35c0f7862e14b94d89025a121f1.tar.gz
MDEV-27677: Implement JSON_OVERLAPS()
1) When at least one of the two json documents is of scalar type: 1.a) If value and json document both are scalar, then return true if they have same type and value. 1.b) If json document is scalar but other is array (or vice versa), then return true if array has at least one element of same type and value as scalar. 1.c) If one is scalar and other is object, then return false because it can't be compared. 2) When both arguments are of non-scalar type and below conditons are satisfied then return true: 2.a) When both arguments are arrays: Iterate over the value and json document. If there exists at least one element in other array of same type and value as that of element in value. 2.b) If both arguments are objects: Iterate over value and json document and if there exists at least one key-value pair common between two objects. 2.c) If either of json document or value is array and other is object: Iterate over the array, if an element of type object is found, then compare it with the object (which is the other arguemnt). If the entire object matches i.e all they key value pairs match.
-rw-r--r--mysql-test/main/func_json.result320
-rw-r--r--mysql-test/main/func_json.test171
-rw-r--r--sql/item_create.cc24
-rw-r--r--sql/item_jsonfunc.cc367
-rw-r--r--sql/item_jsonfunc.h32
5 files changed, 914 insertions, 0 deletions
diff --git a/mysql-test/main/func_json.result b/mysql-test/main/func_json.result
index f9845357d51..54cabeaa2c3 100644
--- a/mysql-test/main/func_json.result
+++ b/mysql-test/main/func_json.result
@@ -1428,3 +1428,323 @@ DROP TABLE t;
#
# End of 10.5 tests
#
+#
+# Beginning of 10.9 tests
+#
+# MDEV-27677: Implement JSON_OVERLAPS()
+#
+# Testing scalar json datatypes
+# Comparing scalar json datatypes with itself
+SELECT JSON_OVERLAPS('true', 'true');
+JSON_OVERLAPS('true', 'true')
+1
+SELECT JSON_OVERLAPS('false', 'false');
+JSON_OVERLAPS('false', 'false')
+1
+SELECT JSON_OVERLAPS('1', '1');
+JSON_OVERLAPS('1', '1')
+1
+SELECT JSON_OVERLAPS('"string1"', '"string1"');
+JSON_OVERLAPS('"string1"', '"string1"')
+1
+SELECT JSON_OVERLAPS('null', 'null');
+JSON_OVERLAPS('null', 'null')
+1
+# Comparing scalar json datatypes with other scalar datatype
+SELECT JSON_OVERLAPS('true', 'false');
+JSON_OVERLAPS('true', 'false')
+0
+SELECT JSON_OVERLAPS('1', '"1"');
+JSON_OVERLAPS('1', '"1"')
+0
+SELECT JSON_OVERLAPS('1', '0');
+JSON_OVERLAPS('1', '0')
+0
+SELECT JSON_OVERLAPS('null', '0');
+JSON_OVERLAPS('null', '0')
+0
+SELECT JSON_OVERLAPS('"string1"', '"string2"');
+JSON_OVERLAPS('"string1"', '"string2"')
+0
+SELECT JSON_OVERLAPS('true','["abc", 1, 2, true, false]');
+JSON_OVERLAPS('true','["abc", 1, 2, true, false]')
+1
+SELECT JSON_OVERLAPS('true','["abc", 1, 2, [true]]');
+JSON_OVERLAPS('true','["abc", 1, 2, [true]]')
+0
+SELECT JSON_OVERLAPS('true','{"A":true}');
+JSON_OVERLAPS('true','{"A":true}')
+0
+# Testing non-scalar json data types
+# Comparing object with object (non-nested)
+SELECT JSON_OVERLAPS('{"A":[1, 2, 3]}','{}');
+JSON_OVERLAPS('{"A":[1, 2, 3]}','{}')
+0
+SELECT JSON_OVERLAPS('{"A": 1}',
+'{"A": 1}');
+JSON_OVERLAPS('{"A": 1}',
+'{"A": 1}')
+1
+SELECT JSON_OVERLAPS('{"A": 1}',
+'{"B": 1}');
+JSON_OVERLAPS('{"A": 1}',
+'{"B": 1}')
+0
+SELECT JSON_OVERLAPS('{
+ "A": 1,
+ "B": "string1"
+ }',
+'{
+ "A": 2,
+ "B": "string1"
+ }');
+JSON_OVERLAPS('{
+ "A": 1,
+ "B": "string1"
+ }',
+'{
+ "A": 2,
+ "B": "string1"
+ }')
+1
+SELECT JSON_OVERLAPS('{
+ "A": 1,
+ "B": "string1"
+ }',
+'{
+ "A": 2,
+ "B": "string2"
+ }');
+JSON_OVERLAPS('{
+ "A": 1,
+ "B": "string1"
+ }',
+'{
+ "A": 2,
+ "B": "string2"
+ }')
+0
+# Comparing nested object with other nested object
+SELECT JSON_OVERLAPS('{
+ "A": 1,
+ "B": {"C":2}
+ }',
+'{
+ "A": 2,
+ "B": {"C":1}
+ }');
+JSON_OVERLAPS('{
+ "A": 1,
+ "B": {"C":2}
+ }',
+'{
+ "A": 2,
+ "B": {"C":1}
+ }')
+0
+SELECT JSON_OVERLAPS('{
+ "A": 1,
+ "B": {"C":2}
+ }',
+'{
+ "A": 2,
+ "B": {"C":2}
+ }');
+JSON_OVERLAPS('{
+ "A": 1,
+ "B": {"C":2}
+ }',
+'{
+ "A": 2,
+ "B": {"C":2}
+ }')
+1
+SELECT JSON_OVERLAPS('{
+ "A": {
+ "B": true
+ }
+ }',
+'{
+ "A": {
+ "B": true,
+ "C": false
+ }
+ }');
+JSON_OVERLAPS('{
+ "A": {
+ "B": true
+ }
+ }',
+'{
+ "A": {
+ "B": true,
+ "C": false
+
+0
+SELECT JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":5}}',
+'{"C":3, "B":{"E":5, "D":4}}');
+JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":5}}',
+'{"C":3, "B":{"E":5, "D":4}}')
+1
+SELECT JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":[5, 6, 7]}}',
+'{"C":3, "B":{"E":5, "D":4}}');
+JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":[5, 6, 7]}}',
+'{"C":3, "B":{"E":5, "D":4}}')
+0
+SELECT JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":[5, 6, 7]}}',
+'{"C":3, "B":{"E":[5, 6, 7], "D":4}}');
+JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":[5, 6, 7]}}',
+'{"C":3, "B":{"E":[5, 6, 7], "D":4}}')
+1
+SELECT JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":[5, 6, 7]}}',
+'{"C":3, "B":{"E":[7, 6 ,5], "D":4}}');
+JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":[5, 6, 7]}}',
+'{"C":3, "B":{"E":[7, 6 ,5], "D":4}}')
+0
+SELECT JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":[5, 6, 7]}}',
+'{"C":3, "F":{"E":[5, 6, 7], "D":4}}');
+JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":[5, 6, 7]}}',
+'{"C":3, "F":{"E":[5, 6, 7], "D":4}}')
+0
+# Comparing array with array (non-nested)
+SELECT JSON_OVERLAPS('[1, 2, true, false, null]',
+'[3, 4, 1]');
+JSON_OVERLAPS('[1, 2, true, false, null]',
+'[3, 4, 1]')
+1
+SELECT JSON_OVERLAPS('[1, 2, true, false, null]',
+'[3, 4, 5]');
+JSON_OVERLAPS('[1, 2, true, false, null]',
+'[3, 4, 5]')
+0
+SELECT JSON_OVERLAPS('[1,2,3]','[]');
+JSON_OVERLAPS('[1,2,3]','[]')
+0
+# Comparing nested arrays
+SELECT JSON_OVERLAPS('[1, 2, true, false, null]',
+'[3, 4, [1]]');
+JSON_OVERLAPS('[1, 2, true, false, null]',
+'[3, 4, [1]]')
+0
+SELECT JSON_OVERLAPS('[1, 2, [true, false], null]',
+'[[1], [true, false]]');
+JSON_OVERLAPS('[1, 2, [true, false], null]',
+'[[1], [true, false]]')
+1
+SELECT JSON_OVERLAPS('[1, 2, 3, [4, 5, 6]]','[7, 8, 9, [6, 5, 4]]');
+JSON_OVERLAPS('[1, 2, 3, [4, 5, 6]]','[7, 8, 9, [6, 5, 4]]')
+0
+# Comparing one non-scalar json datatypes with another non-scalar
+# json datatype
+# Comparing array with object
+SELECT JSON_OVERLAPS('[1, 2, true, false, null]',
+'{"A": 1}');
+JSON_OVERLAPS('[1, 2, true, false, null]',
+'{"A": 1}')
+0
+SELECT JSON_OVERLAPS('[1, 2, true, false, null, {"A":2}]',
+'{"A": 1}');
+JSON_OVERLAPS('[1, 2, true, false, null, {"A":2}]',
+'{"A": 1}')
+0
+SELECT JSON_OVERLAPS('[1, {"A": 2}, {"A": 1}]',
+'{"A": 1}');
+JSON_OVERLAPS('[1, {"A": 2}, {"A": 1}]',
+'{"A": 1}')
+1
+SELECT JSON_OVERLAPS('[1, 2, true, false, {"A": 1, "B": 2}]',
+'{"A": 1, "B": 2}');
+JSON_OVERLAPS('[1, 2, true, false, {"A": 1, "B": 2}]',
+'{"A": 1, "B": 2}')
+1
+SELECT JSON_OVERLAPS('[1, 2, true, false, {"A": 1, "B": 2}]',
+'{"A": 1, "B": 3}');
+JSON_OVERLAPS('[1, 2, true, false, {"A": 1, "B": 2}]',
+'{"A": 1, "B": 3}')
+0
+# Comparing nested array with object
+SELECT JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": 2}]]',
+'{"A": 1, "B": 2}');
+JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": 2}]]',
+'{"A": 1, "B": 2}')
+0
+SELECT JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": 2}]]',
+'{"A": 1, "B": 3}');
+JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": 2}]]',
+'{"A": 1, "B": 3}')
+0
+SELECT JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": 2}]]',
+'{"A": 1}');
+JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": 2}]]',
+'{"A": 1}')
+0
+# Comparing array with nested object
+SELECT JSON_OVERLAPS('[1, 2, true, false, {"A": 1, "B": {"C": 12}}]',
+'{"A": 1, "B": {"C": 12}}');
+JSON_OVERLAPS('[1, 2, true, false, {"A": 1, "B": {"C": 12}}]',
+'{"A": 1, "B": {"C": 12}}')
+1
+SELECT JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": {"C": 12}}]]',
+'{"A": 1, "B": {"C": 12}}');
+JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": {"C": 12}}]]',
+'{"A": 1, "B": {"C": 12}}')
+0
+# Comparing nested array with nested objects
+SELECT JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": {"C": 12}}]]',
+'{"A": 1, "B":{"C": 12}}');
+JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": {"C": 12}}]]',
+'{"A": 1, "B":{"C": 12}}')
+0
+SELECT JSON_OVERLAPS('[[1, 2, true, false, {"A": 1, "B": {"C": 12}}]]',
+'{"A": 1, "B": {"C": 12}}');
+JSON_OVERLAPS('[[1, 2, true, false, {"A": 1, "B": {"C": 12}}]]',
+'{"A": 1, "B": {"C": 12}}')
+0
+# Comparing object with array
+SELECT JSON_OVERLAPS('{"A": 1, "B": 3}',
+'[1, 2, true, false, {"A": 1, "B": 2}]');
+JSON_OVERLAPS('{"A": 1, "B": 3}',
+'[1, 2, true, false, {"A": 1, "B": 2}]')
+0
+SELECT JSON_OVERLAPS('{"A": 1, "B": 3}',
+'[1, 2, true, false, {"A": 1, "B": 3}]');
+JSON_OVERLAPS('{"A": 1, "B": 3}',
+'[1, 2, true, false, {"A": 1, "B": 3}]')
+1
+SELECT JSON_OVERLAPS('{"A": 1, "B": 3}',
+'[1, 2, true, false, {"A": 1, "B": 2}, {"A": 1, "B": 3}]');
+JSON_OVERLAPS('{"A": 1, "B": 3}',
+'[1, 2, true, false, {"A": 1, "B": 2}, {"A": 1, "B": 3}]')
+1
+SELECT JSON_OVERLAPS('{"A": 1, "B": [1, 2, 3]}',
+'[1, 2, true, false, {"A": 1, "B": 2}, {"A": 1, "B": [1, 2, 3]}]');
+JSON_OVERLAPS('{"A": 1, "B": [1, 2, 3]}',
+'[1, 2, true, false, {"A": 1, "B": 2}, {"A": 1, "B": [1, 2, 3]}]')
+1
+SELECT JSON_OVERLAPS('{"A": 1, "B": [1, 2, {"C": 3, "D": 5}]}',
+'[1, 2, true, false, {"A": 1, "B": 2}, {"A":1, "B":[1, 2, {"C": 3, "D": 5}]}]');
+JSON_OVERLAPS('{"A": 1, "B": [1, 2, {"C": 3, "D": 5}]}',
+'[1, 2, true, false, {"A": 1, "B": 2}, {"A":1, "B":[1, 2, {"C": 3, "D": 5}]}]')
+1
+SELECT JSON_OVERLAPS('{"A": 1, "B": [1, 2, {"C": 3, "D": 5}]}',
+'[1, 2, true, false, {"A": 1, "B": 2},{"A": 1, "B": [1, 2, {"C": 3, "D": 4}]}]');
+JSON_OVERLAPS('{"A": 1, "B": [1, 2, {"C": 3, "D": 5}]}',
+'[1, 2, true, false, {"A": 1, "B": 2},{"A": 1, "B": [1, 2, {"C": 3, "D": 4}]}]')
+0
+# Comparing object with nested array
+SELECT JSON_OVERLAPS('{"A": 1, "B": 3}','[1, 2, true, false, [{"A": 1, "B": 2}, {"A": 1, "B": 3}]]');
+JSON_OVERLAPS('{"A": 1, "B": 3}','[1, 2, true, false, [{"A": 1, "B": 2}, {"A": 1, "B": 3}]]')
+0
+# Checking errors and warnings
+SELECT JSON_OVERLAPS('[1,2,{"A":B}]', '{"A":B}', '{"C":"string1"}');
+ERROR 42000: Incorrect parameter count in the call to native function 'JSON_OVERLAPS'
+SELECT JSON_OVERLAPS('[1,2,{"A":B}]');
+ERROR 42000: Incorrect parameter count in the call to native function 'JSON_OVERLAPS'
+SELECT JSON_OVERLAPS('','');
+JSON_OVERLAPS('','')
+1
+Warnings:
+Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_overlaps'
+#
+# End of 10.9 test
+#
diff --git a/mysql-test/main/func_json.test b/mysql-test/main/func_json.test
index 939f19b0b15..b7c8826550d 100644
--- a/mysql-test/main/func_json.test
+++ b/mysql-test/main/func_json.test
@@ -917,3 +917,174 @@ DROP TABLE t;
--echo # End of 10.5 tests
--echo #
+--echo #
+--echo # Beginning of 10.9 tests
+--echo #
+--echo # MDEV-27677: Implement JSON_OVERLAPS()
+--echo #
+
+
+--echo # Testing scalar json datatypes
+
+--echo # Comparing scalar json datatypes with itself
+SELECT JSON_OVERLAPS('true', 'true');
+SELECT JSON_OVERLAPS('false', 'false');
+SELECT JSON_OVERLAPS('1', '1');
+SELECT JSON_OVERLAPS('"string1"', '"string1"');
+SELECT JSON_OVERLAPS('null', 'null');
+
+--echo # Comparing scalar json datatypes with other scalar datatype
+SELECT JSON_OVERLAPS('true', 'false');
+SELECT JSON_OVERLAPS('1', '"1"');
+SELECT JSON_OVERLAPS('1', '0');
+SELECT JSON_OVERLAPS('null', '0');
+SELECT JSON_OVERLAPS('"string1"', '"string2"');
+
+SELECT JSON_OVERLAPS('true','["abc", 1, 2, true, false]');
+SELECT JSON_OVERLAPS('true','["abc", 1, 2, [true]]');
+SELECT JSON_OVERLAPS('true','{"A":true}');
+
+
+--echo # Testing non-scalar json data types
+
+--echo # Comparing object with object (non-nested)
+SELECT JSON_OVERLAPS('{"A":[1, 2, 3]}','{}');
+SELECT JSON_OVERLAPS('{"A": 1}',
+ '{"A": 1}');
+SELECT JSON_OVERLAPS('{"A": 1}',
+ '{"B": 1}');
+SELECT JSON_OVERLAPS('{
+ "A": 1,
+ "B": "string1"
+ }',
+ '{
+ "A": 2,
+ "B": "string1"
+ }');
+SELECT JSON_OVERLAPS('{
+ "A": 1,
+ "B": "string1"
+ }',
+ '{
+ "A": 2,
+ "B": "string2"
+ }');
+
+--echo # Comparing nested object with other nested object
+SELECT JSON_OVERLAPS('{
+ "A": 1,
+ "B": {"C":2}
+ }',
+ '{
+ "A": 2,
+ "B": {"C":1}
+ }');
+SELECT JSON_OVERLAPS('{
+ "A": 1,
+ "B": {"C":2}
+ }',
+ '{
+ "A": 2,
+ "B": {"C":2}
+ }');
+SELECT JSON_OVERLAPS('{
+ "A": {
+ "B": true
+ }
+ }',
+ '{
+ "A": {
+ "B": true,
+ "C": false
+ }
+ }');
+SELECT JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":5}}',
+ '{"C":3, "B":{"E":5, "D":4}}');
+SELECT JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":[5, 6, 7]}}',
+ '{"C":3, "B":{"E":5, "D":4}}');
+SELECT JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":[5, 6, 7]}}',
+ '{"C":3, "B":{"E":[5, 6, 7], "D":4}}');
+SELECT JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":[5, 6, 7]}}',
+ '{"C":3, "B":{"E":[7, 6 ,5], "D":4}}');
+SELECT JSON_OVERLAPS('{"A":1, "B":{"D":4, "E":[5, 6, 7]}}',
+ '{"C":3, "F":{"E":[5, 6, 7], "D":4}}');
+
+
+--echo # Comparing array with array (non-nested)
+SELECT JSON_OVERLAPS('[1, 2, true, false, null]',
+ '[3, 4, 1]');
+SELECT JSON_OVERLAPS('[1, 2, true, false, null]',
+ '[3, 4, 5]');
+SELECT JSON_OVERLAPS('[1,2,3]','[]');
+
+--echo # Comparing nested arrays
+SELECT JSON_OVERLAPS('[1, 2, true, false, null]',
+ '[3, 4, [1]]');
+SELECT JSON_OVERLAPS('[1, 2, [true, false], null]',
+ '[[1], [true, false]]');
+SELECT JSON_OVERLAPS('[1, 2, 3, [4, 5, 6]]','[7, 8, 9, [6, 5, 4]]');
+
+
+--echo # Comparing one non-scalar json datatypes with another non-scalar
+--echo # json datatype
+
+--echo # Comparing array with object
+SELECT JSON_OVERLAPS('[1, 2, true, false, null]',
+ '{"A": 1}');
+SELECT JSON_OVERLAPS('[1, 2, true, false, null, {"A":2}]',
+ '{"A": 1}');
+SELECT JSON_OVERLAPS('[1, {"A": 2}, {"A": 1}]',
+ '{"A": 1}');
+SELECT JSON_OVERLAPS('[1, 2, true, false, {"A": 1, "B": 2}]',
+ '{"A": 1, "B": 2}');
+SELECT JSON_OVERLAPS('[1, 2, true, false, {"A": 1, "B": 2}]',
+ '{"A": 1, "B": 3}');
+
+-- echo # Comparing nested array with object
+SELECT JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": 2}]]',
+ '{"A": 1, "B": 2}');
+SELECT JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": 2}]]',
+ '{"A": 1, "B": 3}');
+SELECT JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": 2}]]',
+ '{"A": 1}');
+
+--echo # Comparing array with nested object
+SELECT JSON_OVERLAPS('[1, 2, true, false, {"A": 1, "B": {"C": 12}}]',
+ '{"A": 1, "B": {"C": 12}}');
+SELECT JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": {"C": 12}}]]',
+ '{"A": 1, "B": {"C": 12}}');
+
+--echo # Comparing nested array with nested objects
+SELECT JSON_OVERLAPS('[1, 2, true, false, [{"A": 1, "B": {"C": 12}}]]',
+ '{"A": 1, "B":{"C": 12}}');
+SELECT JSON_OVERLAPS('[[1, 2, true, false, {"A": 1, "B": {"C": 12}}]]',
+ '{"A": 1, "B": {"C": 12}}');
+
+--echo # Comparing object with array
+SELECT JSON_OVERLAPS('{"A": 1, "B": 3}',
+ '[1, 2, true, false, {"A": 1, "B": 2}]');
+SELECT JSON_OVERLAPS('{"A": 1, "B": 3}',
+ '[1, 2, true, false, {"A": 1, "B": 3}]');
+SELECT JSON_OVERLAPS('{"A": 1, "B": 3}',
+ '[1, 2, true, false, {"A": 1, "B": 2}, {"A": 1, "B": 3}]');
+SELECT JSON_OVERLAPS('{"A": 1, "B": [1, 2, 3]}',
+ '[1, 2, true, false, {"A": 1, "B": 2}, {"A": 1, "B": [1, 2, 3]}]');
+SELECT JSON_OVERLAPS('{"A": 1, "B": [1, 2, {"C": 3, "D": 5}]}',
+ '[1, 2, true, false, {"A": 1, "B": 2}, {"A":1, "B":[1, 2, {"C": 3, "D": 5}]}]');
+SELECT JSON_OVERLAPS('{"A": 1, "B": [1, 2, {"C": 3, "D": 5}]}',
+ '[1, 2, true, false, {"A": 1, "B": 2},{"A": 1, "B": [1, 2, {"C": 3, "D": 4}]}]');
+
+--echo # Comparing object with nested array
+SELECT JSON_OVERLAPS('{"A": 1, "B": 3}','[1, 2, true, false, [{"A": 1, "B": 2}, {"A": 1, "B": 3}]]');
+
+--echo # Checking errors and warnings
+
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT JSON_OVERLAPS('[1,2,{"A":B}]', '{"A":B}', '{"C":"string1"}');
+--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
+SELECT JSON_OVERLAPS('[1,2,{"A":B}]');
+SELECT JSON_OVERLAPS('','');
+
+--echo #
+--echo # End of 10.9 test
+--echo #
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 2d76b04ac5d..390c80c64ca 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -1292,6 +1292,19 @@ protected:
};
+class Create_func_json_overlaps: public Create_func_arg2
+{
+public:
+ virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
+
+ static Create_func_json_overlaps s_singleton;
+
+protected:
+ Create_func_json_overlaps() {}
+ virtual ~Create_func_json_overlaps() {}
+};
+
+
class Create_func_last_day : public Create_func_arg1
{
public:
@@ -4249,6 +4262,16 @@ Create_func_json_search::create_native(THD *thd, LEX_CSTRING *name,
}
+Create_func_json_overlaps Create_func_json_overlaps::s_singleton;
+
+Item*
+Create_func_json_overlaps::create_2_arg(THD *thd, Item *arg1, Item *arg2)
+{
+ status_var_increment(thd->status_var.feature_json);
+ return new (thd->mem_root) Item_func_json_overlaps(thd, arg1, arg2);
+}
+
+
Create_func_last_insert_id Create_func_last_insert_id::s_singleton;
Item*
@@ -5667,6 +5690,7 @@ Native_func_registry func_array[] =
{ { STRING_WITH_LEN("JSON_QUERY") }, BUILDER(Create_func_json_query)},
{ { STRING_WITH_LEN("JSON_QUOTE") }, BUILDER(Create_func_json_quote)},
{ { STRING_WITH_LEN("JSON_OBJECT") }, BUILDER(Create_func_json_object)},
+ { { STRING_WITH_LEN("JSON_OVERLAPS") }, BUILDER(Create_func_json_overlaps)},
{ { STRING_WITH_LEN("JSON_REMOVE") }, BUILDER(Create_func_json_remove)},
{ { STRING_WITH_LEN("JSON_REPLACE") }, BUILDER(Create_func_json_replace)},
{ { STRING_WITH_LEN("JSON_SET") }, BUILDER(Create_func_json_set)},
diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc
index 89ac59098ac..a0dfcaae6e3 100644
--- a/sql/item_jsonfunc.cc
+++ b/sql/item_jsonfunc.cc
@@ -4031,3 +4031,370 @@ bool Item_func_json_normalize::fix_length_and_dec()
set_maybe_null();
return FALSE;
}
+
+
+/*
+ When the two values match or don't match we need to return true or false.
+ But we can have some more elements in the array left or some more keys
+ left in the object that we no longer want to compare. In this case,
+ we want to skip the current item.
+*/
+void json_skip_current_level(json_engine_t *js, json_engine_t *value)
+{
+ json_skip_level(js);
+ json_skip_level(value);
+}
+
+
+/* At least one of the two arguments is a scalar. */
+bool json_find_overlap_with_scalar(json_engine_t *js, json_engine_t *value)
+{
+ if (json_value_scalar(value))
+ {
+ if (js->value_type == value->value_type)
+ {
+ if (js->value_type == JSON_VALUE_NUMBER)
+ {
+ double d_j, d_v;
+ char *end;
+ int err;
+
+ d_j= js->s.cs->strntod((char *) js->value, js->value_len, &end, &err);
+ d_v= value->s.cs->strntod((char *) value->value, value->value_len,
+ &end, &err);
+
+ return (fabs(d_j - d_v) < 1e-12);
+ }
+ else if (js->value_type == JSON_VALUE_STRING)
+ {
+ return value->value_len == js->value_len &&
+ memcmp(value->value, js->value, value->value_len) == 0;
+ }
+ }
+ return value->value_type == js->value_type;
+ }
+ else if (value->value_type == JSON_VALUE_ARRAY)
+ {
+ while (json_scan_next(value) == 0 && value->state == JST_VALUE)
+ {
+ if (json_read_value(value))
+ return FALSE;
+ if (js->value_type == value->value_type)
+ {
+ int res1= json_find_overlap_with_scalar(js, value);
+ if (res1)
+ return TRUE;
+ }
+ if (!json_value_scalar(value))
+ json_skip_level(value);
+ }
+ }
+ return FALSE;
+}
+
+
+/*
+ Compare when one is object and other is array. This means we are looking
+ for the object in the array. Hence, when value type of an element of the
+ array is object, then compare the two objects entirely. If they are
+ equal return true else return false.
+*/
+bool json_compare_arr_and_obj(json_engine_t *js, json_engine_t *value)
+{
+ st_json_engine_t loc_val= *value;
+ while (json_scan_next(js) == 0 && js->state == JST_VALUE)
+ {
+ if (json_read_value(js))
+ return FALSE;
+ if (js->value_type == JSON_VALUE_OBJECT)
+ {
+ int res1= json_find_overlap_with_object(js, value, true);
+ if (res1)
+ return TRUE;
+ *value= loc_val;
+ }
+ if (!json_value_scalar(js))
+ json_skip_level(js);
+ }
+ return FALSE;
+}
+
+
+bool json_compare_arrays_in_order(json_engine_t *js, json_engine_t *value)
+{
+ bool res= false;
+ while (json_scan_next(js) == 0 && json_scan_next(value) == 0 &&
+ js->state == JST_VALUE && value->state == JST_VALUE)
+ {
+ if (json_read_value(js) || json_read_value(value))
+ return FALSE;
+ if (js->value_type != value->value_type)
+ {
+ json_skip_current_level(js, value);
+ return FALSE;
+ }
+ res= check_overlaps(js, value, true);
+ if (!res)
+ {
+ json_skip_current_level(js, value);
+ return FALSE;
+ }
+ }
+ res= (value->state == JST_ARRAY_END || value->state == JST_OBJ_END ?
+ TRUE : FALSE);
+ json_skip_current_level(js, value);
+ return res;
+}
+
+
+int json_find_overlap_with_array(json_engine_t *js, json_engine_t *value,
+ bool compare_whole)
+{
+ if (value->value_type == JSON_VALUE_ARRAY)
+ {
+ if (compare_whole)
+ return json_compare_arrays_in_order(js, value);
+
+ json_engine_t loc_value= *value, current_js= *js;
+
+ while (json_scan_next(js) == 0 && js->state == JST_VALUE)
+ {
+ if (json_read_value(js))
+ return FALSE;
+ current_js= *js;
+ while (json_scan_next(value) == 0 && value->state == JST_VALUE)
+ {
+ if (json_read_value(value))
+ return FALSE;
+ if (js->value_type == value->value_type)
+ {
+ int res1= check_overlaps(js, value, true);
+ if (res1)
+ return TRUE;
+ }
+ else
+ {
+ if (!json_value_scalar(value))
+ json_skip_level(value);
+ }
+ *js= current_js;
+ }
+ *value= loc_value;
+ if (!json_value_scalar(js))
+ json_skip_level(js);
+ }
+ return FALSE;
+ }
+ else if (value->value_type == JSON_VALUE_OBJECT)
+ {
+ if (compare_whole)
+ {
+ json_skip_current_level(js, value);
+ return FALSE;
+ }
+ return json_compare_arr_and_obj(js, value);
+ }
+ else
+ return json_find_overlap_with_scalar(value, js);
+}
+
+
+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)
+ {
+ 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;
+
+ 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 (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)
+ {
+ if (!compare_whole)
+ return TRUE;
+ *js= loc_js;
+ }
+ else
+ {
+ if (compare_whole)
+ {
+ json_skip_current_level(js, value);
+ return FALSE;
+ }
+ *js= loc_js;
+ }
+ }
+ else
+ {
+ if (compare_whole)
+ {
+ json_skip_current_level(js, value);
+ return FALSE;
+ }
+ json_skip_key(value);
+ *js= loc_js;
+ }
+ }
+ json_skip_current_level(js, value);
+ return compare_whole ? TRUE : FALSE;
+ }
+ else if (value->value_type == JSON_VALUE_ARRAY)
+ {
+ if (compare_whole)
+ {
+ json_skip_current_level(js, value);
+ return FALSE;
+ }
+ return json_compare_arr_and_obj(value, js);
+ }
+ return FALSE;
+}
+
+
+/*
+ Find if two json documents overlap
+
+ SYNOPSIS
+ check_overlaps()
+ js - json document
+ value - value
+ compare_whole - If true then find full overlap with the document in case of
+ object and comparing in-order in case of array.
+ Else find at least one match between two objects or array.
+
+ IMPLEMENTATION
+ We can compare two json datatypes if they are of same type to check if
+ they are equal. When comparing between a json document and json value,
+ there can be following cases:
+ 1) When at least one of the two json documents is of scalar type:
+ 1.a) If value and json document both are scalar, then return true
+ if they have same type and value.
+ 1.b) If json document is scalar but other is array (or vice versa),
+ then return true if array has at least one element of same type
+ and value as scalar.
+ 1.c) If one is scalar and other is object, then return false because
+ it can't be compared.
+
+ 2) When both arguments are of non-scalar type:
+ 2.a) If both arguments are arrays:
+ Iterate over the value and json document. If there exists at least
+ one element in other array of same type and value as that of
+ element in value, then return true else return false.
+ 2.b) If both arguments are objects:
+ Iterate over value and json document and if there exists at least
+ one key-value pair common between two objects, then return true,
+ else return false.
+ 2.c) If either of json document or value is array and other is object:
+ Iterate over the array, if an element of type object is found,
+ then compare it with the object (which is the other arguemnt).
+ If the entire object matches i.e all they key value pairs match,
+ then return true else return false.
+
+ When we are comparing an object which is nested in other object or nested
+ in an array, we need to compare all the key-value pairs, irrespective of
+ what order they are in as opposed to non-nested where we return true if
+ at least one match is found. However, if we have an array nested in another
+ array, then we compare two arrays in that order i.e we compare
+ i-th element of array 1 with i-th element of array 2.
+
+ RETURN
+ FALSE - If two json documents do not overlap
+ TRUE - if two json documents overlap
+*/
+int check_overlaps(json_engine_t *js, json_engine_t *value, bool compare_whole)
+{
+ switch (js->value_type)
+ {
+ case JSON_VALUE_OBJECT:
+ return json_find_overlap_with_object(js, value, compare_whole);
+ case JSON_VALUE_ARRAY:
+ return json_find_overlap_with_array(js, value, compare_whole);
+ default:
+ return json_find_overlap_with_scalar(js, value);
+ }
+}
+
+longlong Item_func_json_overlaps::val_int()
+{
+ String *js= args[0]->val_json(&tmp_js);
+ json_engine_t je, ve;
+ int result;
+
+ if ((null_value= args[0]->null_value))
+ return 0;
+
+ if (!a2_parsed)
+ {
+ val= args[1]->val_json(&tmp_val);
+ a2_parsed= a2_constant;
+ }
+
+ if (val == 0)
+ {
+ null_value= 1;
+ return 0;
+ }
+
+ json_scan_start(&je, js->charset(), (const uchar *) js->ptr(),
+ (const uchar *) js->ptr() + js->length());
+
+ json_scan_start(&ve, val->charset(), (const uchar *) val->ptr(),
+ (const uchar *) val->end());
+
+ if (json_read_value(&je) || json_read_value(&ve))
+ goto error;
+
+ result= check_overlaps(&je, &ve, false);
+ if (unlikely(je.s.error || ve.s.error))
+ goto error;
+
+ return result;
+
+error:
+ if (je.s.error)
+ report_json_error(js, &je, 0);
+ if (ve.s.error)
+ report_json_error(val, &ve, 1);
+ return 1;
+}
+
+bool Item_func_json_overlaps::fix_length_and_dec()
+{
+ a2_constant= args[1]->const_item();
+ a2_parsed= FALSE;
+ set_maybe_null();
+
+ return Item_bool_func::fix_length_and_dec();
+}
diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h
index 70993228241..db2fd3da78f 100644
--- a/sql/item_jsonfunc.h
+++ b/sql/item_jsonfunc.h
@@ -47,6 +47,19 @@ void report_path_error_ex(const char *ps, json_path_t *p,
void report_json_error_ex(const char *js, json_engine_t *je,
const char *fname, int n_param,
Sql_condition::enum_warning_level lv);
+int check_overlaps(json_engine_t *js, json_engine_t *value, bool compare_whole);
+int json_find_overlap_with_object(json_engine_t *js,
+ json_engine_t *value,
+ bool compare_whole);
+void json_skip_current_level(json_engine_t *js, json_engine_t *value);
+bool json_find_overlap_with_scalar(json_engine_t *js, json_engine_t *value);
+bool json_compare_arrays_in_order_in_order(json_engine_t *js, json_engine_t *value);
+bool json_compare_arr_and_obj(json_engine_t *js, json_engine_t* value);
+int json_find_overlap_with_array(json_engine_t *js,
+ json_engine_t *value,
+ bool compare_whole);
+
+
class Json_engine_scan: public json_engine_t
{
@@ -759,4 +772,23 @@ public:
extern bool is_json_type(const Item *item);
+class Item_func_json_overlaps: public Item_bool_func
+{
+ String tmp_js;
+ bool a2_constant, a2_parsed;
+ String tmp_val, *val;
+public:
+ Item_func_json_overlaps(THD *thd, Item *a, Item *b):
+ Item_bool_func(thd, a, b) {}
+ LEX_CSTRING func_name_cstring() const override
+ {
+ static LEX_CSTRING name= {STRING_WITH_LEN("json_overlaps") };
+ return name;
+ }
+ bool fix_length_and_dec() override;
+ longlong val_int() override;
+ Item *get_copy(THD *thd) override
+ { return get_item_copy<Item_func_json_overlaps>(thd, this); }
+};
+
#endif /* ITEM_JSONFUNC_INCLUDED */