Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e6c45d8

Browse files
committed
SQL/JSON: Fix JSON_QUERY(... WITH CONDITIONAL WRAPPER)
Currently, when WITH CONDITIONAL WRAPPER is specified, array wrappers are applied even to a single SQL/JSON item if it is a scalar JSON value, but this behavior does not comply with the standard. To fix, apply wrappers only when there are multiple SQL/JSON items in the result. Reported-by: Peter Eisentraut <peter@eisentraut.org> Author: Peter Eisentraut <peter@eisentraut.org> Author: Amit Langote <amitlangote09@gmail.com> Reviewed-by: Andrew Dunstan <andrew@dunslane.net> Discussion: https://postgr.es/m/8022e067-818b-45d3-8fab-6e0d94d03626%40eisentraut.org Backpatch-through: 17
1 parent 77761ee commit e6c45d8

File tree

3 files changed

+48
-38
lines changed

3 files changed

+48
-38
lines changed

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3947,7 +3947,24 @@ JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
39473947
return (Datum) 0;
39483948
}
39493949

3950-
/* WRAP or not? */
3950+
/*
3951+
* Determine whether to wrap the result in a JSON array or not.
3952+
*
3953+
* First, count the number of SQL/JSON items in the returned
3954+
* JsonValueList. If the list is empty (singleton == NULL), no wrapping is
3955+
* necessary.
3956+
*
3957+
* If the wrapper mode is JSW_NONE or JSW_UNSPEC, wrapping is explicitly
3958+
* disabled. This enforces a WITHOUT WRAPPER clause, which is also the
3959+
* default when no WRAPPER clause is specified.
3960+
*
3961+
* If the mode is JSW_UNCONDITIONAL, wrapping is enforced regardless of
3962+
* the number of SQL/JSON items, enforcing a WITH WRAPPER or WITH
3963+
* UNCONDITIONAL WRAPPER clause.
3964+
*
3965+
* For JSW_CONDITIONAL, wrapping occurs only if there is more than one
3966+
* SQL/JSON item in the list, enforcing a WITH CONDITIONAL WRAPPER clause.
3967+
*/
39513968
count = JsonValueListLength(&found);
39523969
singleton = count > 0 ? JsonValueListHead(&found) : NULL;
39533970
if (singleton == NULL)
@@ -3957,10 +3974,7 @@ JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
39573974
else if (wrapper == JSW_UNCONDITIONAL)
39583975
wrap = true;
39593976
else if (wrapper == JSW_CONDITIONAL)
3960-
wrap = count > 1 ||
3961-
IsAJsonbScalar(singleton) ||
3962-
(singleton->type == jbvBinary &&
3963-
JsonContainerIsScalar(singleton->val.binary.data));
3977+
wrap = count > 1;
39643978
else
39653979
{
39663980
elog(ERROR, "unrecognized json wrapper %d", (int) wrapper);

src/test/regress/expected/sqljson_queryfuncs.out

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -541,11 +541,11 @@ SELECT JSON_VALUE(NULL::jsonb, '$');
541541
(1 row)
542542

543543
SELECT
544-
JSON_QUERY(js, '$'),
545-
JSON_QUERY(js, '$' WITHOUT WRAPPER),
546-
JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER),
547-
JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER),
548-
JSON_QUERY(js, '$' WITH ARRAY WRAPPER)
544+
JSON_QUERY(js, '$') AS "unspec",
545+
JSON_QUERY(js, '$' WITHOUT WRAPPER) AS "without",
546+
JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER) AS "with cond",
547+
JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER) AS "with uncond",
548+
JSON_QUERY(js, '$' WITH ARRAY WRAPPER) AS "with"
549549
FROM
550550
(VALUES
551551
(jsonb 'null'),
@@ -555,12 +555,12 @@ FROM
555555
('[1, null, "2"]'),
556556
('{"a": 1, "b": [2]}')
557557
) foo(js);
558-
json_query | json_query | json_query | json_query | json_query
558+
unspec | without | with cond | with uncond | with
559559
--------------------+--------------------+--------------------+----------------------+----------------------
560-
null | null | [null] | [null] | [null]
561-
12.3 | 12.3 | [12.3] | [12.3] | [12.3]
562-
true | true | [true] | [true] | [true]
563-
"aaa" | "aaa" | ["aaa"] | ["aaa"] | ["aaa"]
560+
null | null | null | [null] | [null]
561+
12.3 | 12.3 | 12.3 | [12.3] | [12.3]
562+
true | true | true | [true] | [true]
563+
"aaa" | "aaa" | "aaa" | ["aaa"] | ["aaa"]
564564
[1, null, "2"] | [1, null, "2"] | [1, null, "2"] | [[1, null, "2"]] | [[1, null, "2"]]
565565
{"a": 1, "b": [2]} | {"a": 1, "b": [2]} | {"a": 1, "b": [2]} | [{"a": 1, "b": [2]}] | [{"a": 1, "b": [2]}]
566566
(6 rows)
@@ -587,10 +587,10 @@ FROM
587587
--------------------+--------------------+---------------------+----------------------+----------------------
588588
| | | |
589589
| | | |
590-
null | null | [null] | [null] | [null]
591-
12.3 | 12.3 | [12.3] | [12.3] | [12.3]
592-
true | true | [true] | [true] | [true]
593-
"aaa" | "aaa" | ["aaa"] | ["aaa"] | ["aaa"]
590+
null | null | null | [null] | [null]
591+
12.3 | 12.3 | 12.3 | [12.3] | [12.3]
592+
true | true | true | [true] | [true]
593+
"aaa" | "aaa" | "aaa" | ["aaa"] | ["aaa"]
594594
[1, 2, 3] | [1, 2, 3] | [1, 2, 3] | [[1, 2, 3]] | [[1, 2, 3]]
595595
{"a": 1, "b": [2]} | {"a": 1, "b": [2]} | {"a": 1, "b": [2]} | [{"a": 1, "b": [2]}] | [{"a": 1, "b": [2]}]
596596
| | [1, "2", null, [3]] | [1, "2", null, [3]] | [1, "2", null, [3]]
@@ -681,7 +681,7 @@ LINE 1: SELECT JSON_QUERY(jsonb '[1]', '$' WITH CONDITIONAL WRAPPER ...
681681
SELECT JSON_QUERY(jsonb '["1"]', '$[*]' WITH CONDITIONAL WRAPPER KEEP QUOTES);
682682
json_query
683683
------------
684-
["1"]
684+
"1"
685685
(1 row)
686686

687687
SELECT JSON_QUERY(jsonb '["1"]', '$[*]' WITH UNCONDITIONAL WRAPPER KEEP QUOTES);
@@ -940,30 +940,30 @@ FROM
940940
x | y | list
941941
---+---+--------------
942942
0 | 0 | []
943-
0 | 1 | [1]
943+
0 | 1 | 1
944944
0 | 2 | [1, 2]
945945
0 | 3 | [1, 2, 3]
946946
0 | 4 | [1, 2, 3, 4]
947947
1 | 0 | []
948-
1 | 1 | [1]
948+
1 | 1 | 1
949949
1 | 2 | [1, 2]
950950
1 | 3 | [1, 2, 3]
951951
1 | 4 | [1, 2, 3, 4]
952952
2 | 0 | []
953953
2 | 1 | []
954-
2 | 2 | [2]
954+
2 | 2 | 2
955955
2 | 3 | [2, 3]
956956
2 | 4 | [2, 3, 4]
957957
3 | 0 | []
958958
3 | 1 | []
959959
3 | 2 | []
960-
3 | 3 | [3]
960+
3 | 3 | 3
961961
3 | 4 | [3, 4]
962962
4 | 0 | []
963963
4 | 1 | []
964964
4 | 2 | []
965965
4 | 3 | []
966-
4 | 4 | [4]
966+
4 | 4 | 4
967967
(25 rows)
968968

969969
-- record type returning with quotes behavior.
@@ -1088,7 +1088,7 @@ CREATE TABLE test_jsonb_constraints (
10881088
CONSTRAINT test_jsonb_constraint3
10891089
CHECK (JSON_VALUE(js::jsonb, '$.a' RETURNING int DEFAULT '12' ON EMPTY ERROR ON ERROR) > i)
10901090
CONSTRAINT test_jsonb_constraint4
1091-
CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < jsonb '[10]')
1091+
CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) = jsonb '[10]')
10921092
CONSTRAINT test_jsonb_constraint5
10931093
CHECK (JSON_QUERY(js::jsonb, '$.a' RETURNING char(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) > 'a' COLLATE "C")
10941094
);
@@ -1103,7 +1103,7 @@ Check constraints:
11031103
"test_jsonb_constraint1" CHECK (js IS JSON)
11041104
"test_jsonb_constraint2" CHECK (JSON_EXISTS(js::jsonb, '$."a"' PASSING i + 5 AS int, i::text AS txt, ARRAY[1, 2, 3] AS arr))
11051105
"test_jsonb_constraint3" CHECK (JSON_VALUE(js::jsonb, '$."a"' RETURNING integer DEFAULT 12 ON EMPTY ERROR ON ERROR) > i)
1106-
"test_jsonb_constraint4" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER KEEP QUOTES EMPTY OBJECT ON ERROR) < '[10]'::jsonb)
1106+
"test_jsonb_constraint4" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER KEEP QUOTES EMPTY OBJECT ON ERROR) = '[10]'::jsonb)
11071107
"test_jsonb_constraint5" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING character(5) WITHOUT WRAPPER OMIT QUOTES EMPTY ARRAY ON EMPTY) > ('a'::bpchar COLLATE "C"))
11081108

11091109
SELECT check_clause
@@ -1113,7 +1113,7 @@ ORDER BY 1;
11131113
check_clause
11141114
----------------------------------------------------------------------------------------------------------------------------------------
11151115
(JSON_QUERY((js)::jsonb, '$."a"' RETURNING character(5) WITHOUT WRAPPER OMIT QUOTES EMPTY ARRAY ON EMPTY) > ('a'::bpchar COLLATE "C"))
1116-
(JSON_QUERY((js)::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER KEEP QUOTES EMPTY OBJECT ON ERROR) < '[10]'::jsonb)
1116+
(JSON_QUERY((js)::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER KEEP QUOTES EMPTY OBJECT ON ERROR) = '[10]'::jsonb)
11171117
(JSON_VALUE((js)::jsonb, '$."a"' RETURNING integer DEFAULT 12 ON EMPTY ERROR ON ERROR) > i)
11181118
(js IS JSON)
11191119
JSON_EXISTS((js)::jsonb, '$."a"' PASSING (i + 5) AS int, (i)::text AS txt, ARRAY[1, 2, 3] AS arr)
@@ -1143,9 +1143,6 @@ DETAIL: Failing row contains ({"b": 1}, 1, [1, 2]).
11431143
INSERT INTO test_jsonb_constraints VALUES ('{"a": 1}', 1);
11441144
ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint3"
11451145
DETAIL: Failing row contains ({"a": 1}, 1, [1, 2]).
1146-
INSERT INTO test_jsonb_constraints VALUES ('{"a": 7}', 1);
1147-
ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint5"
1148-
DETAIL: Failing row contains ({"a": 7}, 1, [1, 2]).
11491146
INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1);
11501147
ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint4"
11511148
DETAIL: Failing row contains ({"a": 10}, 1, [1, 2]).

src/test/regress/sql/sqljson_queryfuncs.sql

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,11 @@ select json_value('{"a": "1.234"}', '$.a' returning int error on error);
146146
SELECT JSON_VALUE(NULL::jsonb, '$');
147147

148148
SELECT
149-
JSON_QUERY(js, '$'),
150-
JSON_QUERY(js, '$' WITHOUT WRAPPER),
151-
JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER),
152-
JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER),
153-
JSON_QUERY(js, '$' WITH ARRAY WRAPPER)
149+
JSON_QUERY(js, '$') AS "unspec",
150+
JSON_QUERY(js, '$' WITHOUT WRAPPER) AS "without",
151+
JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER) AS "with cond",
152+
JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER) AS "with uncond",
153+
JSON_QUERY(js, '$' WITH ARRAY WRAPPER) AS "with"
154154
FROM
155155
(VALUES
156156
(jsonb 'null'),
@@ -331,7 +331,7 @@ CREATE TABLE test_jsonb_constraints (
331331
CONSTRAINT test_jsonb_constraint3
332332
CHECK (JSON_VALUE(js::jsonb, '$.a' RETURNING int DEFAULT '12' ON EMPTY ERROR ON ERROR) > i)
333333
CONSTRAINT test_jsonb_constraint4
334-
CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < jsonb '[10]')
334+
CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) = jsonb '[10]')
335335
CONSTRAINT test_jsonb_constraint5
336336
CHECK (JSON_QUERY(js::jsonb, '$.a' RETURNING char(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) > 'a' COLLATE "C")
337337
);
@@ -353,7 +353,6 @@ INSERT INTO test_jsonb_constraints VALUES ('1', 1);
353353
INSERT INTO test_jsonb_constraints VALUES ('[]');
354354
INSERT INTO test_jsonb_constraints VALUES ('{"b": 1}', 1);
355355
INSERT INTO test_jsonb_constraints VALUES ('{"a": 1}', 1);
356-
INSERT INTO test_jsonb_constraints VALUES ('{"a": 7}', 1);
357356
INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1);
358357

359358
DROP TABLE test_jsonb_constraints;

0 commit comments

Comments
 (0)