mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-11557 port MySQL-5.7 JSON tests to MariaDB.
Fixes for issues found.
This commit is contained in:
@ -409,7 +409,12 @@ int json_get_path_start(json_engine_t *je, CHARSET_INFO *i_cs,
|
||||
int json_get_path_next(json_engine_t *je, json_path_t *p);
|
||||
|
||||
|
||||
int json_path_compare(const json_path_t *a, const json_path_t *b);
|
||||
int json_path_parts_compare(
|
||||
const json_path_step_t *a, const json_path_step_t *a_end,
|
||||
const json_path_step_t *b, const json_path_step_t *b_end,
|
||||
enum json_value_types vt);
|
||||
int json_path_compare(const json_path_t *a, const json_path_t *b,
|
||||
enum json_value_types vt);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -491,9 +491,7 @@ json_set('{"a":12}', '$[0][0].a', 100)
|
||||
{"a": 100}
|
||||
select json_set('{"a":12}', '$[0][1].a', 100);
|
||||
json_set('{"a":12}', '$[0][1].a', 100)
|
||||
NULL
|
||||
Warnings:
|
||||
Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_set'
|
||||
{"a": 12}
|
||||
select json_value('{"\\"key1":123}', '$."\\"key1"');
|
||||
json_value('{"\\"key1":123}', '$."\\"key1"')
|
||||
123
|
||||
@ -503,3 +501,54 @@ json_value('{"\\"key1\\"":123}', '$."\\"key1\\""')
|
||||
select json_value('{"key 1":123}', '$."key 1"');
|
||||
json_value('{"key 1":123}', '$."key 1"')
|
||||
123
|
||||
select json_contains_path('{"a":[{"c":[1,{"a":[0,1,2]},3]}], "b":[1,2,3]}', 'one', "$**.a[2]");
|
||||
json_contains_path('{"a":[{"c":[1,{"a":[0,1,2]},3]}], "b":[1,2,3]}', 'one', "$**.a[2]")
|
||||
1
|
||||
select json_contains_path('{"a":[{"c":[1,{"a":[0,1,2]},3]}], "b":[1,2,3]}', 'one', "$**.a[3]");
|
||||
json_contains_path('{"a":[{"c":[1,{"a":[0,1,2]},3]}], "b":[1,2,3]}', 'one', "$**.a[3]")
|
||||
0
|
||||
select json_extract( '[1]', '$[0][0]' );
|
||||
json_extract( '[1]', '$[0][0]' )
|
||||
1
|
||||
select json_extract( '[1]', '$[1][0]' );
|
||||
json_extract( '[1]', '$[1][0]' )
|
||||
NULL
|
||||
select json_extract( '[1]', '$**[0]' );
|
||||
json_extract( '[1]', '$**[0]' )
|
||||
[1]
|
||||
select json_extract( '[1]', '$**[0][0]' );
|
||||
json_extract( '[1]', '$**[0][0]' )
|
||||
[1]
|
||||
select json_insert('1', '$[0]', 4);
|
||||
json_insert('1', '$[0]', 4)
|
||||
1
|
||||
select json_replace('1', '$[0]', 4);
|
||||
json_replace('1', '$[0]', 4)
|
||||
4
|
||||
select json_set('1', '$[0]', 4);
|
||||
json_set('1', '$[0]', 4)
|
||||
4
|
||||
select json_set('1', '$[1]', 4);
|
||||
json_set('1', '$[1]', 4)
|
||||
[1, 4]
|
||||
select json_replace('1', '$[1]', 4);
|
||||
json_replace('1', '$[1]', 4)
|
||||
1
|
||||
SELECT json_insert('[]', '$[0][0]', 100);
|
||||
json_insert('[]', '$[0][0]', 100)
|
||||
[]
|
||||
SELECT json_insert('1', '$[0][0]', 100);
|
||||
json_insert('1', '$[0][0]', 100)
|
||||
1
|
||||
SELECT json_replace('1', '$[0][0]', 100);
|
||||
json_replace('1', '$[0][0]', 100)
|
||||
100
|
||||
SELECT json_replace('[]', '$[0][0]', 100);
|
||||
json_replace('[]', '$[0][0]', 100)
|
||||
[]
|
||||
SELECT json_set('[]', '$[0][0]', 100);
|
||||
json_set('[]', '$[0][0]', 100)
|
||||
[]
|
||||
SELECT json_set('[]', '$[0][0][0]', 100);
|
||||
json_set('[]', '$[0][0][0]', 100)
|
||||
[]
|
||||
|
@ -751,7 +751,7 @@ JSON_REMOVE
|
||||
'{"a" : "foo", "b" : [true, {"c" : 123}]}',
|
||||
'$.b[ 1 ]'
|
||||
)
|
||||
{"a" : "foo", "b" : [true, {"c" : 123}]}
|
||||
{"a": "foo", "b": [true]}
|
||||
SELECT JSON_REMOVE
|
||||
(
|
||||
'{ "a" : "foo", "b" : [ true, { "c" : 123, "c" : 456 } ] }',
|
||||
@ -762,9 +762,7 @@ JSON_REMOVE
|
||||
'{ "a" : "foo", "b" : [ true, { "c" : 123, "c" : 456 } ] }',
|
||||
'$.b[ 1 ].c'
|
||||
)
|
||||
NULL
|
||||
Warnings:
|
||||
Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_remove'
|
||||
{"a": "foo", "b": [true, {"c": 456}]}
|
||||
SELECT JSON_REMOVE
|
||||
(
|
||||
'{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
|
||||
@ -775,9 +773,7 @@ JSON_REMOVE
|
||||
'{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
|
||||
'$.b[ 1 ].c'
|
||||
)
|
||||
NULL
|
||||
Warnings:
|
||||
Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_remove'
|
||||
{"a": "foo", "b": [true, {}]}
|
||||
SELECT JSON_REMOVE
|
||||
(
|
||||
'{ "a" : "foo", "b" : [ true, { "c" : 123, "d" : 456 } ] }',
|
||||
@ -788,9 +784,7 @@ JSON_REMOVE
|
||||
'{ "a" : "foo", "b" : [ true, { "c" : 123, "d" : 456 } ] }',
|
||||
'$.b[ 1 ].e'
|
||||
)
|
||||
NULL
|
||||
Warnings:
|
||||
Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_remove'
|
||||
{"a": "foo", "b": [true, {"c": 123, "d": 456}]}
|
||||
# ----------------------------------------------------------------------
|
||||
# Test of JSON_MERGE function.
|
||||
# ----------------------------------------------------------------------
|
||||
@ -823,7 +817,9 @@ NULL
|
||||
error ER_INVALID_JSON_TEXT_IN_PARAM
|
||||
select json_merge( '[1, 2]', '[3, 4' );
|
||||
json_merge( '[1, 2]', '[3, 4' )
|
||||
[1, 2, 3, 4
|
||||
NULL
|
||||
Warnings:
|
||||
Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_merge'
|
||||
error ER_INVALID_JSON_TEXT_IN_PARAM
|
||||
select json_merge( '[1, 2', '[3, 4]' );
|
||||
json_merge( '[1, 2', '[3, 4]' )
|
||||
@ -1359,13 +1355,13 @@ SELECT JSON_EXTRACT('{ "a" : "foo", "b" : [ true, { "c" : "123" } ] }',
|
||||
'$.b[ 1 ].c');
|
||||
JSON_EXTRACT('{ "a" : "foo", "b" : [ true, { "c" : "123" } ] }',
|
||||
'$.b[ 1 ].c')
|
||||
NULL
|
||||
"123"
|
||||
# returns a JSON value containing just the number 123
|
||||
SELECT JSON_EXTRACT('{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
|
||||
'$.b[ 1 ].c');
|
||||
JSON_EXTRACT('{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
|
||||
'$.b[ 1 ].c')
|
||||
NULL
|
||||
123
|
||||
# raises an error because the document is not valid
|
||||
error ER_INVALID_JSON_TEXT_IN_PARAM
|
||||
SELECT JSON_EXTRACT('{ "a" : [ }',
|
||||
@ -1390,13 +1386,13 @@ SELECT JSON_EXTRACT('{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
|
||||
'$.b[ 1 ].c[ 0 ]');
|
||||
JSON_EXTRACT('{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
|
||||
'$.b[ 1 ].c[ 0 ]')
|
||||
NULL
|
||||
123
|
||||
# returns a JSON value containing the object because of auto-wrapping
|
||||
SELECT JSON_EXTRACT('{ "a" : "foo", "b" : [ true, { "c" : {"not array": 4} } ] }',
|
||||
'$.b[ 1 ].c[ 0 ]');
|
||||
JSON_EXTRACT('{ "a" : "foo", "b" : [ true, { "c" : {"not array": 4} } ] }',
|
||||
'$.b[ 1 ].c[ 0 ]')
|
||||
NULL
|
||||
{"not array": 4}
|
||||
# returns null because the path, although valid, does not identify a value
|
||||
SELECT JSON_EXTRACT('{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
|
||||
'$.b[ 1 ].c[ 1 ]');
|
||||
@ -1408,7 +1404,7 @@ SELECT JSON_EXTRACT('{ "a" : "foo", "b" : [ true, { "c" : 123, "c" : 456 } ] }',
|
||||
'$.b[ 1 ].c');
|
||||
JSON_EXTRACT('{ "a" : "foo", "b" : [ true, { "c" : 123, "c" : 456 } ] }',
|
||||
'$.b[ 1 ].c')
|
||||
NULL
|
||||
123
|
||||
# returns a JSON array [ "foo", true ]
|
||||
SELECT JSON_EXTRACT('{ "a" : "foo", "b" : [ true, { "c" : 123, "c" : 456 } ] }',
|
||||
'$.a', '$.b[0]');
|
||||
@ -1423,40 +1419,40 @@ JSON_EXTRACT('{ "a" : "foo", "b" : [ true, { "c" : 123, "c" : 456 } ] }',
|
||||
[true]
|
||||
select json_extract( '[1]', '$[0][0]' );
|
||||
json_extract( '[1]', '$[0][0]' )
|
||||
NULL
|
||||
1
|
||||
select json_extract( '[1]', '$**[0]' );
|
||||
json_extract( '[1]', '$**[0]' )
|
||||
[1]
|
||||
select json_extract( '{ "a": 1 }', '$.a[0]' );
|
||||
json_extract( '{ "a": 1 }', '$.a[0]' )
|
||||
NULL
|
||||
1
|
||||
select json_extract( '{ "a": 1 }', '$**[0]' );
|
||||
json_extract( '{ "a": 1 }', '$**[0]' )
|
||||
NULL
|
||||
[{"a": 1}, 1]
|
||||
select json_extract( '{ "a": 1 }', '$[0].a' );
|
||||
json_extract( '{ "a": 1 }', '$[0].a' )
|
||||
NULL
|
||||
1
|
||||
select json_extract( '{ "a": 1 }', '$**.a' );
|
||||
json_extract( '{ "a": 1 }', '$**.a' )
|
||||
[1]
|
||||
select json_extract( '{ "a": 1 }', '$[0].a[0]' );
|
||||
json_extract( '{ "a": 1 }', '$[0].a[0]' )
|
||||
NULL
|
||||
1
|
||||
select json_extract( '{ "a": 1 }', '$**[0]' );
|
||||
json_extract( '{ "a": 1 }', '$**[0]' )
|
||||
NULL
|
||||
[{"a": 1}, 1]
|
||||
select json_extract( '{ "a": 1 }', '$[0].a' );
|
||||
json_extract( '{ "a": 1 }', '$[0].a' )
|
||||
NULL
|
||||
1
|
||||
select json_extract( '{ "a": 1 }', '$**.a' );
|
||||
json_extract( '{ "a": 1 }', '$**.a' )
|
||||
[1]
|
||||
select json_extract( '{ "a": 1 }', '$[0][0].a' );
|
||||
json_extract( '{ "a": 1 }', '$[0][0].a' )
|
||||
NULL
|
||||
1
|
||||
select json_extract( '{ "a": 1 }', '$[0][0][0].a' );
|
||||
json_extract( '{ "a": 1 }', '$[0][0][0].a' )
|
||||
NULL
|
||||
1
|
||||
SELECT JSON_EXTRACT('[1, [[{"x": [{"a":{"b":{"c":42}}}]}]]]', '$**.a.*');
|
||||
JSON_EXTRACT('[1, [[{"x": [{"a":{"b":{"c":42}}}]}]]]', '$**.a.*')
|
||||
[{"c": 42}]
|
||||
@ -1475,7 +1471,7 @@ JSON_EXTRACT
|
||||
'{ "a" : "foo", "b" : [ true, { "c" : "123" } ] }',
|
||||
'$.b[ 1 ].c'
|
||||
)
|
||||
NULL
|
||||
"123"
|
||||
SELECT JSON_EXTRACT
|
||||
(
|
||||
'{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
|
||||
@ -1486,7 +1482,7 @@ JSON_EXTRACT
|
||||
'{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
|
||||
'$.b[ 1 ].c'
|
||||
)
|
||||
NULL
|
||||
123
|
||||
error ER_INVALID_JSON_TEXT_IN_PARAM
|
||||
SELECT JSON_EXTRACT
|
||||
(
|
||||
@ -1525,7 +1521,7 @@ JSON_EXTRACT
|
||||
'{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
|
||||
'$.b[ 1 ].c[ 0 ]'
|
||||
)
|
||||
NULL
|
||||
123
|
||||
SELECT JSON_EXTRACT
|
||||
(
|
||||
'{ "a" : "foo", "b" : [ true, { "c" : 123 } ] }',
|
||||
@ -1547,7 +1543,7 @@ JSON_EXTRACT
|
||||
'{ "a" : "foo", "b" : [ true, { "c" : 123, "c" : 456 } ] }',
|
||||
'$.b[ 1 ].c'
|
||||
)
|
||||
NULL
|
||||
123
|
||||
SELECT JSON_EXTRACT
|
||||
(
|
||||
'{ "a" : "foo", "b" : [ true, { "c" : 123, "c" : 456 } ] }',
|
||||
@ -1737,7 +1733,7 @@ json_insert('1', '$', 4)
|
||||
1
|
||||
select json_insert('1', '$[0]', 4);
|
||||
json_insert('1', '$[0]', 4)
|
||||
[1, 4]
|
||||
1
|
||||
select json_insert('1', '$[1]', 4);
|
||||
json_insert('1', '$[1]', 4)
|
||||
[1, 4]
|
||||
@ -1746,13 +1742,13 @@ json_insert('1', '$[10]', '4', '$[11]', 5)
|
||||
[1, "4", 5]
|
||||
select json_insert('[1,2,3]', '$[2][0]', 4);
|
||||
json_insert('[1,2,3]', '$[2][0]', 4)
|
||||
[1,2,[3, 4]]
|
||||
[1, 2, 3]
|
||||
select json_insert('[1,2,3]', '$[2][2]', 4);
|
||||
json_insert('[1,2,3]', '$[2][2]', 4)
|
||||
[1, 2, [3, 4]]
|
||||
select json_insert('{"a": 3}', '$.a[0]', 4);
|
||||
json_insert('{"a": 3}', '$.a[0]', 4)
|
||||
{"a": [3, 4]}
|
||||
{"a": 3}
|
||||
select json_insert('{"a": 3}', '$.a[1]', 4, '$.a[2]', '5');
|
||||
json_insert('{"a": 3}', '$.a[1]', 4, '$.a[2]', '5')
|
||||
{"a": [3, 4, "5"]}
|
||||
@ -2093,9 +2089,10 @@ ERROR 42000: Incorrect parameter count in the call to native function 'json_set'
|
||||
error ER_INVALID_JSON_TEXT_IN_PARAM
|
||||
SELECT JSON_SET('{}', '$.name', JSON_EXTRACT('', '$'));
|
||||
JSON_SET('{}', '$.name', JSON_EXTRACT('', '$'))
|
||||
{, "name":null}
|
||||
NULL
|
||||
Warnings:
|
||||
Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_extract'
|
||||
Warning 4038 Syntax error in JSON text in argument 1 to function 'json_set' at position 2
|
||||
select json_set('[1,2,3]', '$[2]', 4);
|
||||
json_set('[1,2,3]', '$[2]', 4)
|
||||
[1, 2, 4]
|
||||
@ -3502,14 +3499,10 @@ JSON_SET('[]', '$[0]', 100)
|
||||
[100]
|
||||
SELECT JSON_SET('[]', '$[0][0]', 100);
|
||||
JSON_SET('[]', '$[0][0]', 100)
|
||||
NULL
|
||||
Warnings:
|
||||
Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_set'
|
||||
[]
|
||||
SELECT JSON_SET('[]', '$[0][0][0]', 100);
|
||||
JSON_SET('[]', '$[0][0][0]', 100)
|
||||
NULL
|
||||
Warnings:
|
||||
Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_set'
|
||||
[]
|
||||
SELECT JSON_SET('[1]', '$', 100);
|
||||
JSON_SET('[1]', '$', 100)
|
||||
100
|
||||
@ -3566,14 +3559,10 @@ JSON_REPLACE('[]', '$[0]', 100)
|
||||
[]
|
||||
SELECT JSON_REPLACE('[]', '$[0][0]', 100);
|
||||
JSON_REPLACE('[]', '$[0][0]', 100)
|
||||
NULL
|
||||
Warnings:
|
||||
Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_update'
|
||||
[]
|
||||
SELECT JSON_REPLACE('[]', '$[0][0][0]', 100);
|
||||
JSON_REPLACE('[]', '$[0][0][0]', 100)
|
||||
NULL
|
||||
Warnings:
|
||||
Warning 4037 Unexpected end of JSON text in argument 1 to function 'json_update'
|
||||
[]
|
||||
SELECT JSON_REPLACE('[1]', '$', 100);
|
||||
JSON_REPLACE('[1]', '$', 100)
|
||||
100
|
||||
|
@ -200,3 +200,23 @@ select json_set('{"a":12}', '$[0][1].a', 100);
|
||||
select json_value('{"\\"key1":123}', '$."\\"key1"');
|
||||
select json_value('{"\\"key1\\"":123}', '$."\\"key1\\""');
|
||||
select json_value('{"key 1":123}', '$."key 1"');
|
||||
|
||||
select json_contains_path('{"a":[{"c":[1,{"a":[0,1,2]},3]}], "b":[1,2,3]}', 'one', "$**.a[2]");
|
||||
select json_contains_path('{"a":[{"c":[1,{"a":[0,1,2]},3]}], "b":[1,2,3]}', 'one', "$**.a[3]");
|
||||
|
||||
select json_extract( '[1]', '$[0][0]' );
|
||||
select json_extract( '[1]', '$[1][0]' );
|
||||
select json_extract( '[1]', '$**[0]' );
|
||||
select json_extract( '[1]', '$**[0][0]' );
|
||||
|
||||
select json_insert('1', '$[0]', 4);
|
||||
select json_replace('1', '$[0]', 4);
|
||||
select json_set('1', '$[0]', 4);
|
||||
select json_set('1', '$[1]', 4);
|
||||
select json_replace('1', '$[1]', 4);
|
||||
SELECT json_insert('[]', '$[0][0]', 100);
|
||||
SELECT json_insert('1', '$[0][0]', 100);
|
||||
SELECT json_replace('1', '$[0][0]', 100);
|
||||
SELECT json_replace('[]', '$[0][0]', 100);
|
||||
SELECT json_set('[]', '$[0][0]', 100);
|
||||
SELECT json_set('[]', '$[0][0][0]', 100);
|
||||
|
@ -1762,6 +1762,46 @@ protected:
|
||||
};
|
||||
|
||||
|
||||
class Create_func_json_compact : public Create_func_arg1
|
||||
{
|
||||
public:
|
||||
virtual Item *create_1_arg(THD *thd, Item *arg1);
|
||||
|
||||
static Create_func_json_compact s_singleton;
|
||||
|
||||
protected:
|
||||
Create_func_json_compact() {}
|
||||
virtual ~Create_func_json_compact() {}
|
||||
};
|
||||
|
||||
|
||||
class Create_func_json_loose : public Create_func_arg1
|
||||
{
|
||||
public:
|
||||
virtual Item *create_1_arg(THD *thd, Item *arg1);
|
||||
|
||||
static Create_func_json_loose s_singleton;
|
||||
|
||||
protected:
|
||||
Create_func_json_loose() {}
|
||||
virtual ~Create_func_json_loose() {}
|
||||
};
|
||||
|
||||
|
||||
class Create_func_json_detailed : public Create_func_arg2
|
||||
{
|
||||
public:
|
||||
virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
|
||||
|
||||
static Create_func_json_detailed s_singleton;
|
||||
|
||||
protected:
|
||||
Create_func_json_detailed() {}
|
||||
virtual ~Create_func_json_detailed() {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Create_func_json_type : public Create_func_arg1
|
||||
{
|
||||
public:
|
||||
@ -5003,6 +5043,35 @@ Create_func_json_exists::create_2_arg(THD *thd, Item *arg1, Item *arg2)
|
||||
}
|
||||
|
||||
|
||||
Create_func_json_detailed Create_func_json_detailed::s_singleton;
|
||||
|
||||
Item*
|
||||
Create_func_json_detailed::create_2_arg(THD *thd, Item *arg1, Item *arg2)
|
||||
{
|
||||
return new (thd->mem_root) Item_func_json_format(thd, arg1, arg2);
|
||||
}
|
||||
|
||||
|
||||
Create_func_json_loose Create_func_json_loose::s_singleton;
|
||||
|
||||
Item*
|
||||
Create_func_json_loose::create_1_arg(THD *thd, Item *arg1)
|
||||
{
|
||||
return new (thd->mem_root) Item_func_json_format(thd, arg1,
|
||||
Item_func_json_format::LOOSE);
|
||||
}
|
||||
|
||||
|
||||
Create_func_json_compact Create_func_json_compact::s_singleton;
|
||||
|
||||
Item*
|
||||
Create_func_json_compact::create_1_arg(THD *thd, Item *arg1)
|
||||
{
|
||||
return new (thd->mem_root) Item_func_json_format(thd, arg1,
|
||||
Item_func_json_format::COMPACT);
|
||||
}
|
||||
|
||||
|
||||
Create_func_json_valid Create_func_json_valid::s_singleton;
|
||||
|
||||
Item*
|
||||
@ -6728,14 +6797,17 @@ static Native_func_registry func_array[] =
|
||||
{ { C_STRING_WITH_LEN("JSON_ARRAY") }, BUILDER(Create_func_json_array)},
|
||||
{ { C_STRING_WITH_LEN("JSON_ARRAY_APPEND") }, BUILDER(Create_func_json_array_append)},
|
||||
{ { C_STRING_WITH_LEN("JSON_ARRAY_INSERT") }, BUILDER(Create_func_json_array_insert)},
|
||||
{ { C_STRING_WITH_LEN("JSON_COMPACT") }, BUILDER(Create_func_json_compact)},
|
||||
{ { C_STRING_WITH_LEN("JSON_CONTAINS") }, BUILDER(Create_func_json_contains)},
|
||||
{ { C_STRING_WITH_LEN("JSON_CONTAINS_PATH") }, BUILDER(Create_func_json_contains_path)},
|
||||
{ { C_STRING_WITH_LEN("JSON_DEPTH") }, BUILDER(Create_func_json_depth)},
|
||||
{ { C_STRING_WITH_LEN("JSON_DETAILED") }, BUILDER(Create_func_json_detailed)},
|
||||
{ { C_STRING_WITH_LEN("JSON_EXISTS") }, BUILDER(Create_func_json_exists)},
|
||||
{ { C_STRING_WITH_LEN("JSON_EXTRACT") }, BUILDER(Create_func_json_extract)},
|
||||
{ { C_STRING_WITH_LEN("JSON_INSERT") }, BUILDER(Create_func_json_insert)},
|
||||
{ { C_STRING_WITH_LEN("JSON_KEYS") }, BUILDER(Create_func_json_keys)},
|
||||
{ { C_STRING_WITH_LEN("JSON_LENGTH") }, BUILDER(Create_func_json_length)},
|
||||
{ { C_STRING_WITH_LEN("JSON_LOOSE") }, BUILDER(Create_func_json_loose)},
|
||||
{ { C_STRING_WITH_LEN("JSON_MERGE") }, BUILDER(Create_func_json_merge)},
|
||||
{ { C_STRING_WITH_LEN("JSON_QUERY") }, BUILDER(Create_func_json_query)},
|
||||
{ { C_STRING_WITH_LEN("JSON_QUOTE") }, BUILDER(Create_func_json_quote)},
|
||||
|
@ -113,6 +113,100 @@ static int st_append_escaped(String *s, const String *a)
|
||||
}
|
||||
|
||||
|
||||
static int json_nice(json_engine_t *je, String *nice_js,
|
||||
Item_func_json_format::formats mode)
|
||||
{
|
||||
int depth= 0;
|
||||
const char *comma, *colon;
|
||||
uint comma_len, colon_len;
|
||||
int first_value= 1;
|
||||
|
||||
DBUG_ASSERT(je->s.cs == nice_js->charset());
|
||||
|
||||
if (mode == Item_func_json_format::LOOSE)
|
||||
{
|
||||
comma= ", ";
|
||||
comma_len= 2;
|
||||
colon= "\": ";
|
||||
colon_len= 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
comma= ",";
|
||||
comma_len= 1;
|
||||
colon= "\":";
|
||||
colon_len= 2;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
switch (je->state)
|
||||
{
|
||||
case JST_KEY:
|
||||
{
|
||||
const uchar *key_start= je->s.c_str;
|
||||
const uchar *key_end;
|
||||
|
||||
while (json_read_keyname_chr(je) == 0)
|
||||
key_end= je->s.c_str;
|
||||
|
||||
if (je->s.error)
|
||||
goto error;
|
||||
|
||||
if (!first_value)
|
||||
nice_js->append(comma, comma_len);
|
||||
|
||||
nice_js->append("\"", 1);
|
||||
append_simple(nice_js, key_start, key_end - key_start);
|
||||
nice_js->append(colon, colon_len);
|
||||
}
|
||||
/* now we have key value to handle, so no 'break'. */
|
||||
DBUG_ASSERT(je->state == JST_VALUE);
|
||||
goto handle_value;
|
||||
|
||||
case JST_VALUE:
|
||||
if (!first_value)
|
||||
nice_js->append(comma, comma_len);
|
||||
|
||||
handle_value:
|
||||
if (json_read_value(je))
|
||||
goto error;
|
||||
if (json_value_scalar(je))
|
||||
{
|
||||
if (append_simple(nice_js, je->value_begin,
|
||||
je->value_end - je->value_begin))
|
||||
goto error;
|
||||
|
||||
first_value= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nice_js->append((je->value_type == JSON_VALUE_OBJECT) ? "{" : "[", 1);
|
||||
first_value= 1;
|
||||
depth++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case JST_OBJ_END:
|
||||
case JST_ARRAY_END:
|
||||
depth--;
|
||||
nice_js->append((je->state == JST_OBJ_END) ? "}": "]", 1);
|
||||
first_value= 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
};
|
||||
} while (json_scan_next(je) == 0);
|
||||
|
||||
return je->s.error;
|
||||
|
||||
error:
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#define report_json_error(js, je, n_param) \
|
||||
report_json_error_ex(js, je, func_name(), n_param, \
|
||||
Sql_condition::WARN_LEVEL_WARN)
|
||||
@ -554,11 +648,11 @@ void Item_func_json_extract::fix_length_and_dec()
|
||||
|
||||
|
||||
static bool path_exact(const json_path_with_flags *paths_list, int n_paths,
|
||||
const json_path_t *p)
|
||||
const json_path_t *p, enum json_value_types vt)
|
||||
{
|
||||
for (; n_paths > 0; n_paths--, paths_list++)
|
||||
{
|
||||
if (json_path_compare(&paths_list->p, p) == 0)
|
||||
if (json_path_compare(&paths_list->p, p, vt) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@ -566,11 +660,11 @@ static bool path_exact(const json_path_with_flags *paths_list, int n_paths,
|
||||
|
||||
|
||||
static bool path_ok(const json_path_with_flags *paths_list, int n_paths,
|
||||
const json_path_t *p)
|
||||
const json_path_t *p, enum json_value_types vt)
|
||||
{
|
||||
for (; n_paths > 0; n_paths--, paths_list++)
|
||||
{
|
||||
if (json_path_compare(&paths_list->p, p) >= 0)
|
||||
if (json_path_compare(&paths_list->p, p, vt) >= 0)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@ -624,7 +718,7 @@ String *Item_func_json_extract::val_str(String *str)
|
||||
|
||||
while (json_get_path_next(&je, &p) == 0)
|
||||
{
|
||||
if (!path_exact(paths, arg_count-1, &p))
|
||||
if (!path_exact(paths, arg_count-1, &p, je.value_type))
|
||||
continue;
|
||||
|
||||
value= je.value_begin;
|
||||
@ -661,10 +755,18 @@ String *Item_func_json_extract::val_str(String *str)
|
||||
goto return_null;
|
||||
}
|
||||
|
||||
if (possible_multiple_values && str->append("]"))
|
||||
if (possible_multiple_values && str->append("]", 1))
|
||||
goto error; /* Out of memory. */
|
||||
|
||||
return str;
|
||||
js= str;
|
||||
json_scan_start(&je, js->charset(),(const uchar *) js->ptr(),
|
||||
(const uchar *) js->ptr() + js->length());
|
||||
tmp_js.length(0);
|
||||
tmp_js.set_charset(js->charset());
|
||||
if (json_nice(&je, &tmp_js, Item_func_json_format::LOOSE))
|
||||
goto error;
|
||||
|
||||
return &tmp_js;
|
||||
|
||||
error:
|
||||
report_json_error(js, &je, 0);
|
||||
@ -1153,7 +1255,7 @@ longlong Item_func_json_contains_path::val_int()
|
||||
json_path_with_flags *c_path= paths;
|
||||
for (; n_path > 0; n_path--, c_path++)
|
||||
{
|
||||
if (json_path_compare(&c_path->p, &p) >= 0)
|
||||
if (json_path_compare(&c_path->p, &p, je.value_type) >= 0)
|
||||
{
|
||||
if (mode_one)
|
||||
{
|
||||
@ -1424,7 +1526,14 @@ String *Item_func_json_array_append::val_str(String *str)
|
||||
}
|
||||
}
|
||||
|
||||
return js;
|
||||
json_scan_start(&je, js->charset(),(const uchar *) js->ptr(),
|
||||
(const uchar *) js->ptr() + js->length());
|
||||
str->length(0);
|
||||
str->set_charset(js->charset());
|
||||
if (json_nice(&je, str, Item_func_json_format::LOOSE))
|
||||
goto js_error;
|
||||
|
||||
return str;
|
||||
|
||||
js_error:
|
||||
report_json_error(js, &je, 0);
|
||||
@ -1558,7 +1667,14 @@ String *Item_func_json_array_insert::val_str(String *str)
|
||||
}
|
||||
}
|
||||
|
||||
return js;
|
||||
json_scan_start(&je, js->charset(),(const uchar *) js->ptr(),
|
||||
(const uchar *) js->ptr() + js->length());
|
||||
str->length(0);
|
||||
str->set_charset(js->charset());
|
||||
if (json_nice(&je, str, Item_func_json_format::LOOSE))
|
||||
goto js_error;
|
||||
|
||||
return str;
|
||||
|
||||
js_error:
|
||||
report_json_error(js, &je, 0);
|
||||
@ -1690,8 +1806,15 @@ String *Item_func_json_merge::val_str(String *str)
|
||||
}
|
||||
}
|
||||
|
||||
json_scan_start(&je1, js1->charset(),(const uchar *) js1->ptr(),
|
||||
(const uchar *) js1->ptr() + js1->length());
|
||||
str->length(0);
|
||||
str->set_charset(js1->charset());
|
||||
if (json_nice(&je1, str, Item_func_json_format::LOOSE))
|
||||
goto error_return;
|
||||
|
||||
null_value= 0;
|
||||
return js1;
|
||||
return str;
|
||||
|
||||
error_return:
|
||||
if (je1.s.error)
|
||||
@ -1970,6 +2093,7 @@ String *Item_func_json_insert::val_str(String *str)
|
||||
{
|
||||
if (je.s.error)
|
||||
goto js_error;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (json_read_value(&je))
|
||||
@ -1986,7 +2110,16 @@ String *Item_func_json_insert::val_str(String *str)
|
||||
int do_array_autowrap;
|
||||
|
||||
if (mode_insert)
|
||||
do_array_autowrap= !mode_replace || lp->n_item;
|
||||
{
|
||||
if (mode_replace)
|
||||
do_array_autowrap= lp->n_item > 0;
|
||||
else
|
||||
{
|
||||
if (lp->n_item == 0)
|
||||
continue;
|
||||
do_array_autowrap= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lp->n_item)
|
||||
@ -2124,7 +2257,14 @@ continue_point:
|
||||
}
|
||||
}
|
||||
|
||||
return js;
|
||||
json_scan_start(&je, js->charset(),(const uchar *) js->ptr(),
|
||||
(const uchar *) js->ptr() + js->length());
|
||||
str->length(0);
|
||||
str->set_charset(js->charset());
|
||||
if (json_nice(&je, str, Item_func_json_format::LOOSE))
|
||||
goto js_error;
|
||||
|
||||
return str;
|
||||
|
||||
js_error:
|
||||
report_json_error(js, &je, 0);
|
||||
@ -2300,7 +2440,14 @@ v_found:
|
||||
}
|
||||
}
|
||||
|
||||
return js;
|
||||
json_scan_start(&je, js->charset(),(const uchar *) js->ptr(),
|
||||
(const uchar *) js->ptr() + js->length());
|
||||
str->length(0);
|
||||
str->set_charset(js->charset());
|
||||
if (json_nice(&je, str, Item_func_json_format::LOOSE))
|
||||
goto js_error;
|
||||
|
||||
return str;
|
||||
|
||||
js_error:
|
||||
report_json_error(js, &je, 0);
|
||||
@ -2540,7 +2687,7 @@ String *Item_func_json_search::val_str(String *str)
|
||||
{
|
||||
if (json_value_scalar(&je))
|
||||
{
|
||||
if ((arg_count < 5 || path_ok(paths, arg_count - 4, &p)) &&
|
||||
if ((arg_count < 5 || path_ok(paths, arg_count - 4, &p, je.value_type)) &&
|
||||
compare_json_value_wild(&je, s_str) != 0)
|
||||
{
|
||||
++n_path_found;
|
||||
@ -2609,3 +2756,52 @@ String *Item_json_typecast::val_str(String *str)
|
||||
return vs;
|
||||
}
|
||||
|
||||
|
||||
const char *Item_func_json_format::func_name() const
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
case COMPACT:
|
||||
return "json_compact";
|
||||
case LOOSE:
|
||||
return "json_loose";
|
||||
case DETAILED:
|
||||
return "json_detailed";
|
||||
default:
|
||||
DBUG_ASSERT(0);
|
||||
};
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
void Item_func_json_format::fix_length_and_dec()
|
||||
{
|
||||
decimals= 0;
|
||||
max_length= args[0]->max_length;
|
||||
}
|
||||
|
||||
|
||||
String *Item_func_json_format::val_str(String *str)
|
||||
{
|
||||
String *js= args[0]->val_str(&tmp_js);
|
||||
json_engine_t je;
|
||||
if ((null_value= args[0]->null_value))
|
||||
return 0;
|
||||
|
||||
json_scan_start(&je, js->charset(), (const uchar *) js->ptr(),
|
||||
(const uchar *) js->ptr()+js->length());
|
||||
|
||||
str->length(0);
|
||||
str->set_charset(js->charset());
|
||||
if (json_nice(&je, str, fmt))
|
||||
{
|
||||
null_value= 1;
|
||||
report_json_error(js, &je, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
|
@ -427,4 +427,30 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class Item_func_json_format: public Item_str_func
|
||||
{
|
||||
public:
|
||||
enum formats
|
||||
{
|
||||
NONE,
|
||||
COMPACT,
|
||||
LOOSE,
|
||||
DETAILED
|
||||
};
|
||||
protected:
|
||||
formats fmt;
|
||||
String tmp_js;
|
||||
public:
|
||||
Item_func_json_format(THD *thd, Item *js, formats format):
|
||||
Item_str_func(thd, js), fmt(format) {}
|
||||
Item_func_json_format(THD *thd, Item *js, Item *tabsize):
|
||||
Item_str_func(thd, js, tabsize), fmt(DETAILED) {}
|
||||
const char *func_name() const;
|
||||
void fix_length_and_dec();
|
||||
String *val_str(String *str);
|
||||
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
|
||||
{ return get_item_copy<Item_func_json_format>(thd, mem_root, this); }
|
||||
};
|
||||
|
||||
|
||||
#endif /* ITEM_JSONFUNC_INCLUDED */
|
||||
|
@ -1001,6 +1001,7 @@ enum json_path_states {
|
||||
PS_LAX, /* Parse the 'lax' keyword. */
|
||||
PS_PT, /* New path's step begins. */
|
||||
PS_AR, /* Parse array step. */
|
||||
PS_SAR, /* space after the '['. */
|
||||
PS_AWD, /* Array wildcard. */
|
||||
PS_Z, /* '0' (as an array item number). */
|
||||
PS_INT, /* Parse integer (as an array item number). */
|
||||
@ -1041,7 +1042,10 @@ static int json_path_transitions[N_PATH_STATES][N_PATH_CLASSES]=
|
||||
JE_SYN, JE_SYN, JE_SYN, JE_SYN, JE_SYN, JE_SYN,
|
||||
JE_NOT_JSON_CHR, JE_BAD_CHR},
|
||||
/* AR */ { JE_EOS, JE_SYN, PS_AWD, JE_SYN, PS_PT, JE_SYN, PS_Z,
|
||||
PS_INT, JE_SYN, JE_SYN, PS_AR, JE_SYN, JE_SYN, JE_SYN,
|
||||
PS_INT, JE_SYN, JE_SYN, PS_SAR, JE_SYN, JE_SYN, JE_SYN,
|
||||
JE_NOT_JSON_CHR, JE_BAD_CHR},
|
||||
/* SAR */ { JE_EOS, JE_SYN, PS_AWD, JE_SYN, PS_PT, JE_SYN, PS_Z,
|
||||
PS_INT, JE_SYN, JE_SYN, PS_SAR, JE_SYN, JE_SYN, JE_SYN,
|
||||
JE_NOT_JSON_CHR, JE_BAD_CHR},
|
||||
/* AWD */ { JE_EOS, JE_SYN, JE_SYN, JE_SYN, PS_PT, JE_SYN, JE_SYN,
|
||||
JE_SYN, JE_SYN, JE_SYN, PS_AS, JE_SYN, JE_SYN, JE_SYN,
|
||||
@ -1722,45 +1726,103 @@ int json_get_path_next(json_engine_t *je, json_path_t *p)
|
||||
}
|
||||
|
||||
|
||||
int json_path_compare(const json_path_t *a, const json_path_t *b)
|
||||
int json_path_parts_compare(
|
||||
const json_path_step_t *a, const json_path_step_t *a_end,
|
||||
const json_path_step_t *b, const json_path_step_t *b_end,
|
||||
enum json_value_types vt)
|
||||
{
|
||||
const json_path_step_t *sa= a->steps + 1;
|
||||
const json_path_step_t *sb= b->steps + 1;
|
||||
int res, res2;
|
||||
|
||||
if (a->last_step - sa > b->last_step - sb)
|
||||
return -2;
|
||||
|
||||
while (sa <= a->last_step)
|
||||
while (a <= a_end)
|
||||
{
|
||||
if (sb > b->last_step)
|
||||
if (b > b_end)
|
||||
{
|
||||
while (vt != JSON_VALUE_ARRAY &&
|
||||
(a->type & JSON_PATH_ARRAY_WILD) == JSON_PATH_ARRAY &&
|
||||
a->n_item == 0)
|
||||
{
|
||||
if (++a > a_end)
|
||||
return 0;
|
||||
}
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (!((sa->type & sb->type) & JSON_PATH_KEY_OR_ARRAY))
|
||||
DBUG_ASSERT((b->type & (JSON_PATH_WILD | JSON_PATH_DOUBLE_WILD)) == 0);
|
||||
|
||||
|
||||
if (a->type & JSON_PATH_ARRAY)
|
||||
{
|
||||
if (b->type & JSON_PATH_ARRAY)
|
||||
{
|
||||
if ((a->type & JSON_PATH_WILD) || a->n_item == b->n_item)
|
||||
goto step_fits;
|
||||
goto step_failed;
|
||||
|
||||
if (sa->type & JSON_PATH_ARRAY)
|
||||
{
|
||||
if (!(sa->type & JSON_PATH_WILD) && sa->n_item != sb->n_item)
|
||||
}
|
||||
if (a->n_item == 0)
|
||||
goto step_fits_autowrap;
|
||||
goto step_failed;
|
||||
}
|
||||
else /* JSON_PATH_KEY */
|
||||
{
|
||||
if (!(sa->type & JSON_PATH_WILD) &&
|
||||
(sa->key_end - sa->key != sb->key_end - sb->key ||
|
||||
memcmp(sa->key, sb->key, sa->key_end - sa->key) != 0))
|
||||
if (!(b->type & JSON_PATH_KEY))
|
||||
goto step_failed;
|
||||
|
||||
if (!(a->type & JSON_PATH_WILD) &&
|
||||
(a->key_end - a->key != b->key_end - b->key ||
|
||||
memcmp(a->key, b->key, a->key_end - a->key) != 0))
|
||||
goto step_failed;
|
||||
|
||||
goto step_fits;
|
||||
}
|
||||
sb++;
|
||||
sa++;
|
||||
step_failed:
|
||||
if (!(a->type & JSON_PATH_DOUBLE_WILD))
|
||||
return -1;
|
||||
b++;
|
||||
continue;
|
||||
|
||||
step_failed:
|
||||
if (!(sa->type & JSON_PATH_DOUBLE_WILD))
|
||||
return -1;
|
||||
sb++;
|
||||
step_fits:
|
||||
b++;
|
||||
if (!(a->type & JSON_PATH_DOUBLE_WILD))
|
||||
{
|
||||
a++;
|
||||
continue;
|
||||
}
|
||||
|
||||
return sb <= b->last_step;
|
||||
/* Double wild handling needs recursions. */
|
||||
res= json_path_parts_compare(a+1, a_end, b, b_end, vt);
|
||||
if (res == 0)
|
||||
return 0;
|
||||
|
||||
res2= json_path_parts_compare(a, a_end, b, b_end, vt);
|
||||
|
||||
return (res2 >= 0) ? res2 : res;
|
||||
|
||||
step_fits_autowrap:
|
||||
if (!(a->type & JSON_PATH_DOUBLE_WILD))
|
||||
{
|
||||
a++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Double wild handling needs recursions. */
|
||||
res= json_path_parts_compare(a+1, a_end, b+1, b_end, vt);
|
||||
if (res == 0)
|
||||
return 0;
|
||||
|
||||
res2= json_path_parts_compare(a, a_end, b+1, b_end, vt);
|
||||
|
||||
return (res2 >= 0) ? res2 : res;
|
||||
|
||||
}
|
||||
|
||||
return b <= b_end;
|
||||
}
|
||||
|
||||
|
||||
int json_path_compare(const json_path_t *a, const json_path_t *b,
|
||||
enum json_value_types vt)
|
||||
{
|
||||
return json_path_parts_compare(a->steps+1, a->last_step,
|
||||
b->steps+1, b->last_step, vt);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user