Skip to content

Commit 0a75d8e

Browse files
committed
MDEV-37072: Implement IS JSON predicate
Add support for the SQL standard IS JSON predicate with the syntax: expr IS [ NOT ] JSON [ { VALUE | ARRAY | OBJECT | SCALAR } ] [ { WITH | WITHOUT } UNIQUE [ KEYS ] ] The predicate allows checking if an expression is valid JSON and optionally constrains the JSON type (VALUE, ARRAY, OBJECT, SCALAR) and whether object keys are unique. The implementation includes: - Basic IS JSON validation - Support for NOT operator - Type constraints (VALUE, ARRAY, OBJECT, SCALAR) - Unique keys constraint (WITH/WITHOUT UNIQUE KEYS)
1 parent e7bb12f commit 0a75d8e

File tree

6 files changed

+562
-2
lines changed

6 files changed

+562
-2
lines changed

mysql-test/main/func_json.result

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5263,4 +5263,199 @@ SET @obj1='{ "a": 1,"b": 2,"c": 3}';
52635263
SELECT JSON_OBJECT_FILTER_KEYS (@obj1,@arr1);
52645264
JSON_OBJECT_FILTER_KEYS (@obj1,@arr1)
52655265
NULL
5266+
#
5267+
# MDEV-37072: Implement IS JSON predicate
5268+
#
5269+
set names utf8mb4;
5270+
SELECT '[1, 2]' IS JSON;
5271+
'[1, 2]' IS JSON
5272+
1
5273+
SELECT '{"key1":1, "key2":[2,3]}' IS JSON;
5274+
'{"key1":1, "key2":[2,3]}' IS JSON
5275+
1
5276+
SELECT '123' IS JSON;
5277+
'123' IS JSON
5278+
1
5279+
SELECT 'null' IS JSON;
5280+
'null' IS JSON
5281+
1
5282+
SELECT 'invalid' IS JSON;
5283+
'invalid' IS JSON
5284+
0
5285+
SELECT '{"key1":1, "key2":[2,3]' IS JSON;
5286+
'{"key1":1, "key2":[2,3]' IS JSON
5287+
0
5288+
SELECT '[1, 2' IS JSON;
5289+
'[1, 2' IS JSON
5290+
0
5291+
SELECT 'NULL' IS JSON;
5292+
'NULL' IS JSON
5293+
0
5294+
SELECT 'invalid' IS NOT JSON;
5295+
'invalid' IS NOT JSON
5296+
1
5297+
SELECT '{"key1":1, "key2":[2,3]' IS NOT JSON;
5298+
'{"key1":1, "key2":[2,3]' IS NOT JSON
5299+
1
5300+
SELECT '[1, 2]' IS NOT JSON;
5301+
'[1, 2]' IS NOT JSON
5302+
0
5303+
SELECT '{"key1":1, "key2":[2,3]}' IS NOT JSON;
5304+
'{"key1":1, "key2":[2,3]}' IS NOT JSON
5305+
0
5306+
# Type constraints
5307+
SELECT js,
5308+
js IS JSON "json?",
5309+
js IS JSON VALUE "value?",
5310+
js IS JSON SCALAR "scalar?",
5311+
js IS JSON OBJECT "object?",
5312+
js IS JSON ARRAY "array?"
5313+
FROM (VALUES
5314+
('123'), ('"string"'), ('{"key":1}'), ('[1,2,3]'), ('[]'), ('{}'),
5315+
('true'), ('false'), ('null')) foo(js);
5316+
js json? value? scalar? object? array?
5317+
123 1 1 1 0 0
5318+
"string" 1 1 1 0 0
5319+
{"key":1} 1 1 0 1 0
5320+
[1,2,3] 1 1 0 0 1
5321+
[] 1 1 0 0 1
5322+
{} 1 1 0 1 0
5323+
true 1 1 1 0 0
5324+
false 1 1 1 0 0
5325+
null 1 1 1 0 0
5326+
# UNIQUE KEYS constraint
5327+
SELECT js,
5328+
js IS JSON OBJECT WITH UNIQUE KEYS "object_with_unique",
5329+
js IS JSON OBJECT WITHOUT UNIQUE KEYS "object_without_unique",
5330+
js IS JSON ARRAY WITH UNIQUE KEYS "array_with_unique",
5331+
js IS JSON ARRAY WITHOUT UNIQUE KEYS "array_without_unique"
5332+
FROM (VALUES
5333+
('{"a":1, "b":2, "c":3}'),
5334+
('{"a":1, "a":2, "a":3}'),
5335+
('[{"a":"1"},{"c":"2","c":"3"}]'),
5336+
('[{"a":"1"},{"b":"2","c":"3"}]'),
5337+
('[1,2,3]'), ('[1,1,1]'), ('[]'), ('123'),
5338+
('"string"'), ('true'), ('null')) foo(js);
5339+
js object_with_unique object_without_unique array_with_unique array_without_unique
5340+
{"a":1, "b":2, "c":3} 1 1 0 0
5341+
{"a":1, "a":2, "a":3} 0 1 0 0
5342+
[{"a":"1"},{"c":"2","c":"3"}] 0 0 0 1
5343+
[{"a":"1"},{"b":"2","c":"3"}] 0 0 1 1
5344+
[1,2,3] 0 0 1 1
5345+
[1,1,1] 0 0 1 1
5346+
[] 0 0 1 1
5347+
123 0 0 0 0
5348+
"string" 0 0 0 0
5349+
true 0 0 0 0
5350+
null 0 0 0 0
5351+
# Test with table data
5352+
CREATE TABLE test_json (
5353+
json_col JSON,
5354+
text_col TEXT
5355+
);
5356+
INSERT INTO test_json VALUES
5357+
('{"name":"Alice", "age":25}', '{"name":"Alice", "age":25}'),
5358+
('[1,2,3]', '[1,2,3]'),
5359+
('42', '42'),
5360+
('"hello"', '"hello"'),
5361+
('true', 'true'),
5362+
('null', 'null'),
5363+
('"invalid"', 'invalid'),
5364+
('{"key1":1, "key2":2}', '{"key1":1, "key2":2}'),
5365+
('{"key1":1, "key1":2}', '{"key1":1, "key1":2}');
5366+
SELECT
5367+
json_col,
5368+
json_col IS JSON as is_json,
5369+
json_col IS JSON VALUE as is_value,
5370+
json_col IS JSON SCALAR as is_scalar,
5371+
json_col IS JSON OBJECT as is_object,
5372+
json_col IS JSON ARRAY as is_array,
5373+
json_col IS JSON OBJECT WITH UNIQUE KEYS as object_with_unique,
5374+
json_col IS JSON OBJECT WITHOUT UNIQUE KEYS as object_without_unique
5375+
FROM test_json;
5376+
json_col is_json is_value is_scalar is_object is_array object_with_unique object_without_unique
5377+
{"name":"Alice", "age":25} 1 1 0 1 0 1 1
5378+
[1,2,3] 1 1 0 0 1 0 0
5379+
42 1 1 1 0 0 0 0
5380+
"hello" 1 1 1 0 0 0 0
5381+
true 1 1 1 0 0 0 0
5382+
null 1 1 1 0 0 0 0
5383+
"invalid" 1 1 1 0 0 0 0
5384+
{"key1":1, "key2":2} 1 1 0 1 0 1 1
5385+
{"key1":1, "key1":2} 1 1 0 1 0 0 1
5386+
SELECT
5387+
text_col,
5388+
text_col IS JSON as is_json,
5389+
text_col IS JSON VALUE as is_value,
5390+
text_col IS JSON SCALAR as is_scalar,
5391+
text_col IS JSON OBJECT as is_object,
5392+
text_col IS JSON ARRAY as is_array
5393+
FROM test_json;
5394+
text_col is_json is_value is_scalar is_object is_array
5395+
{"name":"Alice", "age":25} 1 1 0 1 0
5396+
[1,2,3] 1 1 0 0 1
5397+
42 1 1 1 0 0
5398+
"hello" 1 1 1 0 0
5399+
true 1 1 1 0 0
5400+
null 1 1 1 0 0
5401+
invalid 0 0 0 0 0
5402+
{"key1":1, "key2":2} 1 1 0 1 0
5403+
{"key1":1, "key1":2} 1 1 0 1 0
5404+
SELECT
5405+
COUNT(*) as total_rows,
5406+
SUM(json_col IS JSON) as valid_json_count,
5407+
SUM(json_col IS JSON OBJECT) as object_count,
5408+
SUM(json_col IS JSON ARRAY) as array_count,
5409+
SUM(json_col IS JSON SCALAR) as scalar_count
5410+
FROM test_json;
5411+
total_rows valid_json_count object_count array_count scalar_count
5412+
9 9 3 1 5
5413+
# Edge cases
5414+
SELECT js,
5415+
js IS JSON "json?",
5416+
js IS JSON VALUE "value?",
5417+
js IS JSON SCALAR "scalar?",
5418+
js IS JSON OBJECT "object?",
5419+
js IS JSON ARRAY "array?"
5420+
FROM (VALUES
5421+
(NULL), (''), (' '), ('\n'), ('\t'),
5422+
('{"key":1,}'), ('{"key":1, "key2":}'), ('{key:1}'),
5423+
('{"emoji":"😊"}'), ('{"unicode":"\\u0041"}'),
5424+
(x'48656C6C6F'), ('2'), (CONCAT('{"a":', 1, '}')),
5425+
(JSON_OBJECT('a', 1, 'b', 2)), (JSON_ARRAY(1, 2, 3)), (JSON_QUOTE('hello'))) foo(js);
5426+
js json? value? scalar? object? array?
5427+
NULL NULL NULL NULL NULL NULL
5428+
0 0 0 0 0
5429+
0 0 0 0 0
5430+
5431+
0 0 0 0 0
5432+
0 0 0 0 0
5433+
{"key":1,} 0 0 0 0 0
5434+
{"key":1, "key2":} 0 0 0 0 0
5435+
{key:1} 0 0 0 0 0
5436+
{"emoji":"😊"} 1 1 0 1 0
5437+
{"unicode":"\u0041"} 1 1 0 1 0
5438+
Hello 0 0 0 0 0
5439+
2 1 1 1 0 0
5440+
{"a":1} 1 1 0 1 0
5441+
{"a": 1, "b": 2} 1 1 0 1 0
5442+
[1, 2, 3] 1 1 0 0 1
5443+
"hello" 1 1 1 0 0
5444+
# Large json object with unique keys
5445+
CREATE PROCEDURE build_large_json()
5446+
BEGIN
5447+
SET @json='{"key1":1';
5448+
SET @i=2;
5449+
WHILE @i <= 1000 DO
5450+
SET @json = CONCAT(@json, ',"key', @i, '":', @i);
5451+
SET @i = @i + 1;
5452+
END WHILE;
5453+
SET @json = CONCAT(@json, '}');
5454+
END|
5455+
CALL build_large_json()|
5456+
SELECT @json IS JSON OBJECT WITH UNIQUE KEYS AS unique_keys|
5457+
unique_keys
5458+
1
5459+
DROP PROCEDURE build_large_json|
5460+
DROP TABLE test_json;
52665461
# End of 11.2 Test

mysql-test/main/func_json.test

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4162,4 +4162,137 @@ SET CHARACTER SET utf8;
41624162
SET @obj1='{ "a": 1,"b": 2,"c": 3}';
41634163
SELECT JSON_OBJECT_FILTER_KEYS (@obj1,@arr1);
41644164

4165+
4166+
--echo #
4167+
--echo # MDEV-37072: Implement IS JSON predicate
4168+
--echo #
4169+
4170+
set names utf8mb4;
4171+
4172+
--disable_view_protocol
4173+
SELECT '[1, 2]' IS JSON;
4174+
SELECT '{"key1":1, "key2":[2,3]}' IS JSON;
4175+
SELECT '123' IS JSON;
4176+
SELECT 'null' IS JSON;
4177+
4178+
SELECT 'invalid' IS JSON;
4179+
SELECT '{"key1":1, "key2":[2,3]' IS JSON;
4180+
SELECT '[1, 2' IS JSON;
4181+
SELECT 'NULL' IS JSON;
4182+
4183+
SELECT 'invalid' IS NOT JSON;
4184+
SELECT '{"key1":1, "key2":[2,3]' IS NOT JSON;
4185+
SELECT '[1, 2]' IS NOT JSON;
4186+
SELECT '{"key1":1, "key2":[2,3]}' IS NOT JSON;
4187+
4188+
--echo # Type constraints
4189+
4190+
SELECT js,
4191+
js IS JSON "json?",
4192+
js IS JSON VALUE "value?",
4193+
js IS JSON SCALAR "scalar?",
4194+
js IS JSON OBJECT "object?",
4195+
js IS JSON ARRAY "array?"
4196+
FROM (VALUES
4197+
('123'), ('"string"'), ('{"key":1}'), ('[1,2,3]'), ('[]'), ('{}'),
4198+
('true'), ('false'), ('null')) foo(js);
4199+
4200+
--echo # UNIQUE KEYS constraint
4201+
4202+
SELECT js,
4203+
js IS JSON OBJECT WITH UNIQUE KEYS "object_with_unique",
4204+
js IS JSON OBJECT WITHOUT UNIQUE KEYS "object_without_unique",
4205+
js IS JSON ARRAY WITH UNIQUE KEYS "array_with_unique",
4206+
js IS JSON ARRAY WITHOUT UNIQUE KEYS "array_without_unique"
4207+
FROM (VALUES
4208+
('{"a":1, "b":2, "c":3}'),
4209+
('{"a":1, "a":2, "a":3}'),
4210+
('[{"a":"1"},{"c":"2","c":"3"}]'),
4211+
('[{"a":"1"},{"b":"2","c":"3"}]'),
4212+
('[1,2,3]'), ('[1,1,1]'), ('[]'), ('123'),
4213+
('"string"'), ('true'), ('null')) foo(js);
4214+
4215+
--echo # Test with table data
4216+
4217+
CREATE TABLE test_json (
4218+
json_col JSON,
4219+
text_col TEXT
4220+
);
4221+
4222+
INSERT INTO test_json VALUES
4223+
('{"name":"Alice", "age":25}', '{"name":"Alice", "age":25}'),
4224+
('[1,2,3]', '[1,2,3]'),
4225+
('42', '42'),
4226+
('"hello"', '"hello"'),
4227+
('true', 'true'),
4228+
('null', 'null'),
4229+
('"invalid"', 'invalid'),
4230+
('{"key1":1, "key2":2}', '{"key1":1, "key2":2}'),
4231+
('{"key1":1, "key1":2}', '{"key1":1, "key1":2}');
4232+
4233+
SELECT
4234+
json_col,
4235+
json_col IS JSON as is_json,
4236+
json_col IS JSON VALUE as is_value,
4237+
json_col IS JSON SCALAR as is_scalar,
4238+
json_col IS JSON OBJECT as is_object,
4239+
json_col IS JSON ARRAY as is_array,
4240+
json_col IS JSON OBJECT WITH UNIQUE KEYS as object_with_unique,
4241+
json_col IS JSON OBJECT WITHOUT UNIQUE KEYS as object_without_unique
4242+
FROM test_json;
4243+
4244+
SELECT
4245+
text_col,
4246+
text_col IS JSON as is_json,
4247+
text_col IS JSON VALUE as is_value,
4248+
text_col IS JSON SCALAR as is_scalar,
4249+
text_col IS JSON OBJECT as is_object,
4250+
text_col IS JSON ARRAY as is_array
4251+
FROM test_json;
4252+
4253+
SELECT
4254+
COUNT(*) as total_rows,
4255+
SUM(json_col IS JSON) as valid_json_count,
4256+
SUM(json_col IS JSON OBJECT) as object_count,
4257+
SUM(json_col IS JSON ARRAY) as array_count,
4258+
SUM(json_col IS JSON SCALAR) as scalar_count
4259+
FROM test_json;
4260+
4261+
--echo # Edge cases
4262+
4263+
SELECT js,
4264+
js IS JSON "json?",
4265+
js IS JSON VALUE "value?",
4266+
js IS JSON SCALAR "scalar?",
4267+
js IS JSON OBJECT "object?",
4268+
js IS JSON ARRAY "array?"
4269+
FROM (VALUES
4270+
(NULL), (''), (' '), ('\n'), ('\t'),
4271+
('{"key":1,}'), ('{"key":1, "key2":}'), ('{key:1}'),
4272+
('{"emoji":"😊"}'), ('{"unicode":"\\u0041"}'),
4273+
(x'48656C6C6F'), ('2'), (CONCAT('{"a":', 1, '}')),
4274+
(JSON_OBJECT('a', 1, 'b', 2)), (JSON_ARRAY(1, 2, 3)), (JSON_QUOTE('hello'))) foo(js);
4275+
--enable_view_protocol
4276+
4277+
--echo # Large json object with unique keys
4278+
4279+
DELIMITER |;
4280+
CREATE PROCEDURE build_large_json()
4281+
BEGIN
4282+
SET @json='{"key1":1';
4283+
SET @i=2;
4284+
WHILE @i <= 1000 DO
4285+
SET @json = CONCAT(@json, ',"key', @i, '":', @i);
4286+
SET @i = @i + 1;
4287+
END WHILE;
4288+
SET @json = CONCAT(@json, '}');
4289+
END|
4290+
CALL build_large_json()|
4291+
SELECT @json IS JSON OBJECT WITH UNIQUE KEYS AS unique_keys|
4292+
DROP PROCEDURE build_large_json|
4293+
DELIMITER ;|
4294+
4295+
DROP TABLE test_json;
4296+
4297+
41654298
--echo # End of 11.2 Test

0 commit comments

Comments
 (0)