json_query conditional wrapper bug
These are ok:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' without wrapper);
json_query
------------
42
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with
unconditional wrapper);
json_query
------------
[42]: This should return an unwrapped 42.
But this appears to be wrong:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with conditional
wrapper);
json_query
------------
[42]: This should return an unwrapped 42.
This should return an unwrapped 42.
On 28.08.24 11:21, Peter Eisentraut wrote:
These are ok:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' without wrapper);
json_query
------------
42select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with
unconditional wrapper);
json_query
------------
[42]But this appears to be wrong:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with conditional
wrapper);
json_query
------------
[42]This should return an unwrapped 42.
If I make the code change illustrated in the attached patch, then I get
the correct result here. And various regression test results change,
which, to me, all look more correct after this patch. I don't know what
the code I removed was supposed to accomplish, but it seems to be wrong
somehow. In the current implementation, the WITH CONDITIONAL WRAPPER
clause doesn't appear to work correctly in any case I could identify.
Attachments:
0001-WIP-Fix-JSON_QUERY-WITH-CONDITIONAL-WRAPPER.patchtext/plain; charset=UTF-8; name=0001-WIP-Fix-JSON_QUERY-WITH-CONDITIONAL-WRAPPER.patchDownload
From ea8a36584abf6cea0a05ac43ad8d101012b14313 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Wed, 4 Sep 2024 12:09:35 +0200
Subject: [PATCH] WIP: Fix JSON_QUERY WITH CONDITIONAL WRAPPER
---
src/backend/utils/adt/jsonpath_exec.c | 5 +----
src/test/regress/sql/sqljson_queryfuncs.sql | 10 +++++-----
2 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index e3ee0093d4d..a55a34685f0 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -3957,10 +3957,7 @@ JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
else if (wrapper == JSW_UNCONDITIONAL)
wrap = true;
else if (wrapper == JSW_CONDITIONAL)
- wrap = count > 1 ||
- IsAJsonbScalar(singleton) ||
- (singleton->type == jbvBinary &&
- JsonContainerIsScalar(singleton->val.binary.data));
+ wrap = count > 1;
else
{
elog(ERROR, "unrecognized json wrapper %d", (int) wrapper);
diff --git a/src/test/regress/sql/sqljson_queryfuncs.sql b/src/test/regress/sql/sqljson_queryfuncs.sql
index 21ff7787a28..a7342416a9c 100644
--- a/src/test/regress/sql/sqljson_queryfuncs.sql
+++ b/src/test/regress/sql/sqljson_queryfuncs.sql
@@ -146,11 +146,11 @@ CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue'));
SELECT JSON_VALUE(NULL::jsonb, '$');
SELECT
- JSON_QUERY(js, '$'),
- JSON_QUERY(js, '$' WITHOUT WRAPPER),
- JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER),
- JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER),
- JSON_QUERY(js, '$' WITH ARRAY WRAPPER)
+ JSON_QUERY(js, '$') AS "unspec",
+ JSON_QUERY(js, '$' WITHOUT WRAPPER) AS "without",
+ JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER) AS "with cond",
+ JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER) AS "with uncond",
+ JSON_QUERY(js, '$' WITH ARRAY WRAPPER) AS "with"
FROM
(VALUES
(jsonb 'null'),
--
2.46.0
On 2024-09-04 We 6:16 AM, Peter Eisentraut wrote:
On 28.08.24 11:21, Peter Eisentraut wrote:
These are ok:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' without
wrapper);
json_query
------------
42select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with
unconditional wrapper);
json_query
------------
[42]But this appears to be wrong:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with
conditional wrapper);
json_query
------------
[42]This should return an unwrapped 42.
If I make the code change illustrated in the attached patch, then I
get the correct result here. And various regression test results
change, which, to me, all look more correct after this patch. I don't
know what the code I removed was supposed to accomplish, but it seems
to be wrong somehow. In the current implementation, the WITH
CONDITIONAL WRAPPER clause doesn't appear to work correctly in any
case I could identify.
Agree the code definitely looks wrong. If anything the test should
probably be reversed:
wrap = count > 1 || !(
IsAJsonbScalar(singleton) ||
(singleton->type == jbvBinary &&
JsonContainerIsScalar(singleton->val.binary.data)));
i.e. in the count = 1 case wrap unless it's a scalar or a binary
wrapping a scalar. The code could do with a comment about the logic.
I know we're very close to release but we should fix this as it's a new
feature.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On 2024-09-04 We 4:10 PM, Andrew Dunstan wrote:
On 2024-09-04 We 6:16 AM, Peter Eisentraut wrote:
On 28.08.24 11:21, Peter Eisentraut wrote:
These are ok:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' without
wrapper);
json_query
------------
42select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with
unconditional wrapper);
json_query
------------
[42]But this appears to be wrong:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with
conditional wrapper);
json_query
------------
[42]This should return an unwrapped 42.
If I make the code change illustrated in the attached patch, then I
get the correct result here. And various regression test results
change, which, to me, all look more correct after this patch. I
don't know what the code I removed was supposed to accomplish, but it
seems to be wrong somehow. In the current implementation, the WITH
CONDITIONAL WRAPPER clause doesn't appear to work correctly in any
case I could identify.Agree the code definitely looks wrong. If anything the test should
probably be reversed:wrap = count > 1 || !(
IsAJsonbScalar(singleton) ||
(singleton->type == jbvBinary &&
JsonContainerIsScalar(singleton->val.binary.data)));i.e. in the count = 1 case wrap unless it's a scalar or a binary
wrapping a scalar. The code could do with a comment about the logic.I know we're very close to release but we should fix this as it's a
new feature.
I thought about this again.
I don't know what the spec says, but the Oracle docs say:
Specify |WITH| |CONDITIONAL| |WRAPPER| to include the array wrapper
only if the path expression matches a single scalar value or
multiple values of any type. If the path expression matches a single
JSON object or JSON array, then the array wrapper is omitted.
So I now think the code that's there now is actually correct, and what
you say appears wrong is also correct.
cheers
andrew
--
Andrew Dunstan
EDB:https://www.enterprisedb.com
On 05.09.24 17:01, Andrew Dunstan wrote:
On 2024-09-04 We 4:10 PM, Andrew Dunstan wrote:
On 2024-09-04 We 6:16 AM, Peter Eisentraut wrote:
On 28.08.24 11:21, Peter Eisentraut wrote:
These are ok:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' without
wrapper);
json_query
------------
42select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with
unconditional wrapper);
json_query
------------
[42]But this appears to be wrong:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with
conditional wrapper);
json_query
------------
[42]This should return an unwrapped 42.
If I make the code change illustrated in the attached patch, then I
get the correct result here. And various regression test results
change, which, to me, all look more correct after this patch. I
don't know what the code I removed was supposed to accomplish, but it
seems to be wrong somehow. In the current implementation, the WITH
CONDITIONAL WRAPPER clause doesn't appear to work correctly in any
case I could identify.Agree the code definitely looks wrong. If anything the test should
probably be reversed:wrap = count > 1 || !(
IsAJsonbScalar(singleton) ||
(singleton->type == jbvBinary &&
JsonContainerIsScalar(singleton->val.binary.data)));i.e. in the count = 1 case wrap unless it's a scalar or a binary
wrapping a scalar. The code could do with a comment about the logic.I know we're very close to release but we should fix this as it's a
new feature.I thought about this again.
I don't know what the spec says,
Here is the relevant bit:
a) Case:
i) If the length of SEQ is 0 (zero), then let WRAPIT be False.
NOTE 479 — This ensures that the ON EMPTY behavior supersedes the
WRAPPER behavior.
ii) If WRAPPER is WITHOUT ARRAY, then let WRAPIT be False.
iii) If WRAPPER is WITH UNCONDITIONAL ARRAY, then let WRAPIT be True.
iv) If WRAPPER is WITH CONDITIONAL ARRAY, then
Case:
1) If SEQ has a single SQL/JSON item, then let WRAPIT be False.
2) Otherwise, let WRAPIT be True.
but the Oracle docs say:>
Specify |WITH| |CONDITIONAL| |WRAPPER| to include the array wrapper
only if the path expression matches a single scalar value or
multiple values of any type. If the path expression matches a single
JSON object or JSON array, then the array wrapper is omitted.So I now think the code that's there now is actually correct, and what
you say appears wrong is also correct.
I tested the above test expressions as well as the regression test case
against Oracle and it agrees with my solution. So it seems to me that
this piece of documentation is wrong.
On Sep 5, 2024, at 11:51 AM, Peter Eisentraut <peter@eisentraut.org> wrote:
On 05.09.24 17:01, Andrew Dunstan wrote:
On 2024-09-04 We 4:10 PM, Andrew Dunstan wrote:
On 2024-09-04 We 6:16 AM, Peter Eisentraut wrote:
On 28.08.24 11:21, Peter Eisentraut wrote:
These are ok:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' without wrapper);
json_query
------------
42select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with unconditional wrapper);
json_query
------------
[42]But this appears to be wrong:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with conditional wrapper);
json_query
------------
[42]This should return an unwrapped 42.
If I make the code change illustrated in the attached patch, then I get the correct result here. And various regression test results change, which, to me, all look more correct after this patch. I don't know what the code I removed was supposed to accomplish, but it seems to be wrong somehow. In the current implementation, the WITH CONDITIONAL WRAPPER clause doesn't appear to work correctly in any case I could identify.
Agree the code definitely looks wrong. If anything the test should probably be reversed:
wrap = count > 1 || !(
IsAJsonbScalar(singleton) ||
(singleton->type == jbvBinary &&
JsonContainerIsScalar(singleton->val.binary.data)));i.e. in the count = 1 case wrap unless it's a scalar or a binary wrapping a scalar. The code could do with a comment about the logic.
I know we're very close to release but we should fix this as it's a new feature.
I thought about this again.
I don't know what the spec says,Here is the relevant bit:
a) Case:
i) If the length of SEQ is 0 (zero), then let WRAPIT be False.
NOTE 479 — This ensures that the ON EMPTY behavior supersedes the WRAPPER behavior.
ii) If WRAPPER is WITHOUT ARRAY, then let WRAPIT be False.
iii) If WRAPPER is WITH UNCONDITIONAL ARRAY, then let WRAPIT be True.
iv) If WRAPPER is WITH CONDITIONAL ARRAY, then
Case:
1) If SEQ has a single SQL/JSON item, then let WRAPIT be False.
2) Otherwise, let WRAPIT be True.but the Oracle docs say:>
Specify |WITH| |CONDITIONAL| |WRAPPER| to include the array wrapper
only if the path expression matches a single scalar value or
multiple values of any type. If the path expression matches a single
JSON object or JSON array, then the array wrapper is omitted.
So I now think the code that's there now is actually correct, and what you say appears wrong is also correct.I tested the above test expressions as well as the regression test case against Oracle and it agrees with my solution. So it seems to me that this piece of documentation is wrong.
Oh, odd. Then assuming a scalar is an SQL/JSON item your patch appears correct.
Cheers
Andrew
Sorry for missing this report and thanks Andrew for the offlist heads up.
On Wed, Sep 4, 2024 at 7:16 PM Peter Eisentraut <peter@eisentraut.org> wrote:
On 28.08.24 11:21, Peter Eisentraut wrote:
These are ok:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' without wrapper);
json_query
------------
42select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with
unconditional wrapper);
json_query
------------
[42]But this appears to be wrong:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with conditional
wrapper);
json_query
------------
[42]This should return an unwrapped 42.
If I make the code change illustrated in the attached patch, then I get
the correct result here. And various regression test results change,
which, to me, all look more correct after this patch. I don't know what
the code I removed was supposed to accomplish, but it seems to be wrong
somehow. In the current implementation, the WITH CONDITIONAL WRAPPER
clause doesn't appear to work correctly in any case I could identify.
Agreed that this looks wrong.
I've wondered why the condition was like that but left it as-is,
because I thought at one point that that's needed to ensure that the
returned single scalar SQL/JSON item is valid jsonb.
I've updated your patch to include updated test outputs and a nearby
code comment expanded. Do you intend to commit it or do you prefer
that I do?
--
Thanks, Amit Langote
Attachments:
v2-0001-WIP-Fix-JSON_QUERY-WITH-CONDITIONAL-WRAPPER.patchapplication/octet-stream; name=v2-0001-WIP-Fix-JSON_QUERY-WITH-CONDITIONAL-WRAPPER.patchDownload
From 106f39fded2bcdfd9a7ff47bb56f35c806b94d24 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Wed, 4 Sep 2024 12:09:35 +0200
Subject: [PATCH v2] WIP: Fix JSON_QUERY WITH CONDITIONAL WRAPPER
---
src/backend/utils/adt/jsonpath_exec.c | 24 ++++++++---
.../regress/expected/sqljson_queryfuncs.out | 42 +++++++++----------
src/test/regress/sql/sqljson_queryfuncs.sql | 10 ++---
3 files changed, 45 insertions(+), 31 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index e3ee0093d4..e569c7efb8 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -3947,7 +3947,24 @@ JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
return (Datum) 0;
}
- /* WRAP or not? */
+ /*
+ * Determine whether to wrap the result in a JSON array or not.
+ *
+ * First, count the number of SQL/JSON items in the returned
+ * JsonValueList. If the list is empty (singleton == NULL), no wrapping is
+ * necessary.
+ *
+ * If the wrapper mode is JSW_NONE or JSW_UNSPEC, wrapping is explicitly
+ * disabled. This enforces a WITHOUT WRAPPER clause, which is also the
+ * default when no WRAPPER clause is specified.
+ *
+ * If the mode is JSW_UNCONDITIONAL, wrapping is enforced regardless of
+ * the number of SQL/JSON items, enforcing a WITH WRAPPER or WITH
+ * UNCONDITIONAL WRAPPER clause.
+ *
+ * For JSW_CONDITIONAL, wrapping occurs only if there is more than one
+ * SQL/JSON item in the list, enforcing a WITH CONDITIONAL WRAPPER clause.
+ */
count = JsonValueListLength(&found);
singleton = count > 0 ? JsonValueListHead(&found) : NULL;
if (singleton == NULL)
@@ -3957,10 +3974,7 @@ JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
else if (wrapper == JSW_UNCONDITIONAL)
wrap = true;
else if (wrapper == JSW_CONDITIONAL)
- wrap = count > 1 ||
- IsAJsonbScalar(singleton) ||
- (singleton->type == jbvBinary &&
- JsonContainerIsScalar(singleton->val.binary.data));
+ wrap = count > 1;
else
{
elog(ERROR, "unrecognized json wrapper %d", (int) wrapper);
diff --git a/src/test/regress/expected/sqljson_queryfuncs.out b/src/test/regress/expected/sqljson_queryfuncs.out
index 73d7d2117e..378cd9f4cc 100644
--- a/src/test/regress/expected/sqljson_queryfuncs.out
+++ b/src/test/regress/expected/sqljson_queryfuncs.out
@@ -541,11 +541,11 @@ SELECT JSON_VALUE(NULL::jsonb, '$');
(1 row)
SELECT
- JSON_QUERY(js, '$'),
- JSON_QUERY(js, '$' WITHOUT WRAPPER),
- JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER),
- JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER),
- JSON_QUERY(js, '$' WITH ARRAY WRAPPER)
+ JSON_QUERY(js, '$') AS "unspec",
+ JSON_QUERY(js, '$' WITHOUT WRAPPER) AS "without",
+ JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER) AS "with cond",
+ JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER) AS "with uncond",
+ JSON_QUERY(js, '$' WITH ARRAY WRAPPER) AS "with"
FROM
(VALUES
(jsonb 'null'),
@@ -555,12 +555,12 @@ FROM
('[1, null, "2"]'),
('{"a": 1, "b": [2]}')
) foo(js);
- json_query | json_query | json_query | json_query | json_query
+ unspec | without | with cond | with uncond | with
--------------------+--------------------+--------------------+----------------------+----------------------
- null | null | [null] | [null] | [null]
- 12.3 | 12.3 | [12.3] | [12.3] | [12.3]
- true | true | [true] | [true] | [true]
- "aaa" | "aaa" | ["aaa"] | ["aaa"] | ["aaa"]
+ null | null | null | [null] | [null]
+ 12.3 | 12.3 | 12.3 | [12.3] | [12.3]
+ true | true | true | [true] | [true]
+ "aaa" | "aaa" | "aaa" | ["aaa"] | ["aaa"]
[1, null, "2"] | [1, null, "2"] | [1, null, "2"] | [[1, null, "2"]] | [[1, null, "2"]]
{"a": 1, "b": [2]} | {"a": 1, "b": [2]} | {"a": 1, "b": [2]} | [{"a": 1, "b": [2]}] | [{"a": 1, "b": [2]}]
(6 rows)
@@ -587,10 +587,10 @@ FROM
--------------------+--------------------+---------------------+----------------------+----------------------
| | | |
| | | |
- null | null | [null] | [null] | [null]
- 12.3 | 12.3 | [12.3] | [12.3] | [12.3]
- true | true | [true] | [true] | [true]
- "aaa" | "aaa" | ["aaa"] | ["aaa"] | ["aaa"]
+ null | null | null | [null] | [null]
+ 12.3 | 12.3 | 12.3 | [12.3] | [12.3]
+ true | true | true | [true] | [true]
+ "aaa" | "aaa" | "aaa" | ["aaa"] | ["aaa"]
[1, 2, 3] | [1, 2, 3] | [1, 2, 3] | [[1, 2, 3]] | [[1, 2, 3]]
{"a": 1, "b": [2]} | {"a": 1, "b": [2]} | {"a": 1, "b": [2]} | [{"a": 1, "b": [2]}] | [{"a": 1, "b": [2]}]
| | [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 ...
SELECT JSON_QUERY(jsonb '["1"]', '$[*]' WITH CONDITIONAL WRAPPER KEEP QUOTES);
json_query
------------
- ["1"]
+ "1"
(1 row)
SELECT JSON_QUERY(jsonb '["1"]', '$[*]' WITH UNCONDITIONAL WRAPPER KEEP QUOTES);
@@ -940,30 +940,30 @@ FROM
x | y | list
---+---+--------------
0 | 0 | []
- 0 | 1 | [1]
+ 0 | 1 | 1
0 | 2 | [1, 2]
0 | 3 | [1, 2, 3]
0 | 4 | [1, 2, 3, 4]
1 | 0 | []
- 1 | 1 | [1]
+ 1 | 1 | 1
1 | 2 | [1, 2]
1 | 3 | [1, 2, 3]
1 | 4 | [1, 2, 3, 4]
2 | 0 | []
2 | 1 | []
- 2 | 2 | [2]
+ 2 | 2 | 2
2 | 3 | [2, 3]
2 | 4 | [2, 3, 4]
3 | 0 | []
3 | 1 | []
3 | 2 | []
- 3 | 3 | [3]
+ 3 | 3 | 3
3 | 4 | [3, 4]
4 | 0 | []
4 | 1 | []
4 | 2 | []
4 | 3 | []
- 4 | 4 | [4]
+ 4 | 4 | 4
(25 rows)
-- record type returning with quotes behavior.
@@ -1147,7 +1147,7 @@ INSERT INTO test_jsonb_constraints VALUES ('{"a": 7}', 1);
ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint5"
DETAIL: Failing row contains ({"a": 7}, 1, [1, 2]).
INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1);
-ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint4"
+ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint5"
DETAIL: Failing row contains ({"a": 10}, 1, [1, 2]).
DROP TABLE test_jsonb_constraints;
-- Test mutabilily of query functions
diff --git a/src/test/regress/sql/sqljson_queryfuncs.sql b/src/test/regress/sql/sqljson_queryfuncs.sql
index 21ff7787a2..a7342416a9 100644
--- a/src/test/regress/sql/sqljson_queryfuncs.sql
+++ b/src/test/regress/sql/sqljson_queryfuncs.sql
@@ -146,11 +146,11 @@ select json_value('{"a": "1.234"}', '$.a' returning int error on error);
SELECT JSON_VALUE(NULL::jsonb, '$');
SELECT
- JSON_QUERY(js, '$'),
- JSON_QUERY(js, '$' WITHOUT WRAPPER),
- JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER),
- JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER),
- JSON_QUERY(js, '$' WITH ARRAY WRAPPER)
+ JSON_QUERY(js, '$') AS "unspec",
+ JSON_QUERY(js, '$' WITHOUT WRAPPER) AS "without",
+ JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER) AS "with cond",
+ JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER) AS "with uncond",
+ JSON_QUERY(js, '$' WITH ARRAY WRAPPER) AS "with"
FROM
(VALUES
(jsonb 'null'),
--
2.43.0
On 10.09.24 10:00, Amit Langote wrote:
Sorry for missing this report and thanks Andrew for the offlist heads up.
On Wed, Sep 4, 2024 at 7:16 PM Peter Eisentraut <peter@eisentraut.org> wrote:
On 28.08.24 11:21, Peter Eisentraut wrote:
These are ok:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' without wrapper);
json_query
------------
42select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with
unconditional wrapper);
json_query
------------
[42]But this appears to be wrong:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with conditional
wrapper);
json_query
------------
[42]This should return an unwrapped 42.
If I make the code change illustrated in the attached patch, then I get
the correct result here. And various regression test results change,
which, to me, all look more correct after this patch. I don't know what
the code I removed was supposed to accomplish, but it seems to be wrong
somehow. In the current implementation, the WITH CONDITIONAL WRAPPER
clause doesn't appear to work correctly in any case I could identify.Agreed that this looks wrong.
I've wondered why the condition was like that but left it as-is,
because I thought at one point that that's needed to ensure that the
returned single scalar SQL/JSON item is valid jsonb.I've updated your patch to include updated test outputs and a nearby
code comment expanded. Do you intend to commit it or do you prefer
that I do?
This change looks unrelated:
-ERROR: new row for relation "test_jsonb_constraints" violates check
constraint "test_jsonb_constraint4"
+ERROR: new row for relation "test_jsonb_constraints" violates check
constraint "test_jsonb_constraint5"
Is this some randomness in the way these constraints are evaluated?
On Wed, Sep 11, 2024 at 5:15 AM Peter Eisentraut <peter@eisentraut.org> wrote:
On 10.09.24 10:00, Amit Langote wrote:
Sorry for missing this report and thanks Andrew for the offlist heads up.
On Wed, Sep 4, 2024 at 7:16 PM Peter Eisentraut <peter@eisentraut.org> wrote:
On 28.08.24 11:21, Peter Eisentraut wrote:
These are ok:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' without wrapper);
json_query
------------
42select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with
unconditional wrapper);
json_query
------------
[42]But this appears to be wrong:
select json_query('{"a": 1, "b": 42}'::jsonb, 'lax $.b' with conditional
wrapper);
json_query
------------
[42]This should return an unwrapped 42.
If I make the code change illustrated in the attached patch, then I get
the correct result here. And various regression test results change,
which, to me, all look more correct after this patch. I don't know what
the code I removed was supposed to accomplish, but it seems to be wrong
somehow. In the current implementation, the WITH CONDITIONAL WRAPPER
clause doesn't appear to work correctly in any case I could identify.Agreed that this looks wrong.
I've wondered why the condition was like that but left it as-is,
because I thought at one point that that's needed to ensure that the
returned single scalar SQL/JSON item is valid jsonb.I've updated your patch to include updated test outputs and a nearby
code comment expanded. Do you intend to commit it or do you prefer
that I do?This change looks unrelated:
-ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint4" +ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint5"Is this some randomness in the way these constraints are evaluated?
The result of JSON_QUERY() in the CHECK constraint changes, so the
constraint that previously failed now succeeds after this change,
because the comparison looked like this before and after:
-- before
postgres=# select jsonb '[10]' < jsonb '[10]';
?column?
----------
f
(1 row)
-- after
postgres=# select jsonb '10' < jsonb '[10]';
?column?
----------
t
(1 row)
That causes the next constraint to be evaluated and its failure
reported instead.
In the attached, I've adjusted the constraint for the test case to be
a bit more relevant and removed a nearby somewhat redundant test,
mainly because its output changes after the adjustment.
--
Thanks, Amit Langote
Attachments:
v3-0001-WIP-Fix-JSON_QUERY-WITH-CONDITIONAL-WRAPPER.patchapplication/octet-stream; name=v3-0001-WIP-Fix-JSON_QUERY-WITH-CONDITIONAL-WRAPPER.patchDownload
From cc297a66d86d11abaf1507a258e118de70dd5fab Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Wed, 4 Sep 2024 12:09:35 +0200
Subject: [PATCH v3] WIP: Fix JSON_QUERY WITH CONDITIONAL WRAPPER
---
src/backend/utils/adt/jsonpath_exec.c | 24 +++++++--
.../regress/expected/sqljson_queryfuncs.out | 49 +++++++++----------
src/test/regress/sql/sqljson_queryfuncs.sql | 13 +++--
3 files changed, 48 insertions(+), 38 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index e3ee0093d4..e569c7efb8 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -3947,7 +3947,24 @@ JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
return (Datum) 0;
}
- /* WRAP or not? */
+ /*
+ * Determine whether to wrap the result in a JSON array or not.
+ *
+ * First, count the number of SQL/JSON items in the returned
+ * JsonValueList. If the list is empty (singleton == NULL), no wrapping is
+ * necessary.
+ *
+ * If the wrapper mode is JSW_NONE or JSW_UNSPEC, wrapping is explicitly
+ * disabled. This enforces a WITHOUT WRAPPER clause, which is also the
+ * default when no WRAPPER clause is specified.
+ *
+ * If the mode is JSW_UNCONDITIONAL, wrapping is enforced regardless of
+ * the number of SQL/JSON items, enforcing a WITH WRAPPER or WITH
+ * UNCONDITIONAL WRAPPER clause.
+ *
+ * For JSW_CONDITIONAL, wrapping occurs only if there is more than one
+ * SQL/JSON item in the list, enforcing a WITH CONDITIONAL WRAPPER clause.
+ */
count = JsonValueListLength(&found);
singleton = count > 0 ? JsonValueListHead(&found) : NULL;
if (singleton == NULL)
@@ -3957,10 +3974,7 @@ JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
else if (wrapper == JSW_UNCONDITIONAL)
wrap = true;
else if (wrapper == JSW_CONDITIONAL)
- wrap = count > 1 ||
- IsAJsonbScalar(singleton) ||
- (singleton->type == jbvBinary &&
- JsonContainerIsScalar(singleton->val.binary.data));
+ wrap = count > 1;
else
{
elog(ERROR, "unrecognized json wrapper %d", (int) wrapper);
diff --git a/src/test/regress/expected/sqljson_queryfuncs.out b/src/test/regress/expected/sqljson_queryfuncs.out
index 73d7d2117e..175349f7dc 100644
--- a/src/test/regress/expected/sqljson_queryfuncs.out
+++ b/src/test/regress/expected/sqljson_queryfuncs.out
@@ -541,11 +541,11 @@ SELECT JSON_VALUE(NULL::jsonb, '$');
(1 row)
SELECT
- JSON_QUERY(js, '$'),
- JSON_QUERY(js, '$' WITHOUT WRAPPER),
- JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER),
- JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER),
- JSON_QUERY(js, '$' WITH ARRAY WRAPPER)
+ JSON_QUERY(js, '$') AS "unspec",
+ JSON_QUERY(js, '$' WITHOUT WRAPPER) AS "without",
+ JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER) AS "with cond",
+ JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER) AS "with uncond",
+ JSON_QUERY(js, '$' WITH ARRAY WRAPPER) AS "with"
FROM
(VALUES
(jsonb 'null'),
@@ -555,12 +555,12 @@ FROM
('[1, null, "2"]'),
('{"a": 1, "b": [2]}')
) foo(js);
- json_query | json_query | json_query | json_query | json_query
+ unspec | without | with cond | with uncond | with
--------------------+--------------------+--------------------+----------------------+----------------------
- null | null | [null] | [null] | [null]
- 12.3 | 12.3 | [12.3] | [12.3] | [12.3]
- true | true | [true] | [true] | [true]
- "aaa" | "aaa" | ["aaa"] | ["aaa"] | ["aaa"]
+ null | null | null | [null] | [null]
+ 12.3 | 12.3 | 12.3 | [12.3] | [12.3]
+ true | true | true | [true] | [true]
+ "aaa" | "aaa" | "aaa" | ["aaa"] | ["aaa"]
[1, null, "2"] | [1, null, "2"] | [1, null, "2"] | [[1, null, "2"]] | [[1, null, "2"]]
{"a": 1, "b": [2]} | {"a": 1, "b": [2]} | {"a": 1, "b": [2]} | [{"a": 1, "b": [2]}] | [{"a": 1, "b": [2]}]
(6 rows)
@@ -587,10 +587,10 @@ FROM
--------------------+--------------------+---------------------+----------------------+----------------------
| | | |
| | | |
- null | null | [null] | [null] | [null]
- 12.3 | 12.3 | [12.3] | [12.3] | [12.3]
- true | true | [true] | [true] | [true]
- "aaa" | "aaa" | ["aaa"] | ["aaa"] | ["aaa"]
+ null | null | null | [null] | [null]
+ 12.3 | 12.3 | 12.3 | [12.3] | [12.3]
+ true | true | true | [true] | [true]
+ "aaa" | "aaa" | "aaa" | ["aaa"] | ["aaa"]
[1, 2, 3] | [1, 2, 3] | [1, 2, 3] | [[1, 2, 3]] | [[1, 2, 3]]
{"a": 1, "b": [2]} | {"a": 1, "b": [2]} | {"a": 1, "b": [2]} | [{"a": 1, "b": [2]}] | [{"a": 1, "b": [2]}]
| | [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 ...
SELECT JSON_QUERY(jsonb '["1"]', '$[*]' WITH CONDITIONAL WRAPPER KEEP QUOTES);
json_query
------------
- ["1"]
+ "1"
(1 row)
SELECT JSON_QUERY(jsonb '["1"]', '$[*]' WITH UNCONDITIONAL WRAPPER KEEP QUOTES);
@@ -940,30 +940,30 @@ FROM
x | y | list
---+---+--------------
0 | 0 | []
- 0 | 1 | [1]
+ 0 | 1 | 1
0 | 2 | [1, 2]
0 | 3 | [1, 2, 3]
0 | 4 | [1, 2, 3, 4]
1 | 0 | []
- 1 | 1 | [1]
+ 1 | 1 | 1
1 | 2 | [1, 2]
1 | 3 | [1, 2, 3]
1 | 4 | [1, 2, 3, 4]
2 | 0 | []
2 | 1 | []
- 2 | 2 | [2]
+ 2 | 2 | 2
2 | 3 | [2, 3]
2 | 4 | [2, 3, 4]
3 | 0 | []
3 | 1 | []
3 | 2 | []
- 3 | 3 | [3]
+ 3 | 3 | 3
3 | 4 | [3, 4]
4 | 0 | []
4 | 1 | []
4 | 2 | []
4 | 3 | []
- 4 | 4 | [4]
+ 4 | 4 | 4
(25 rows)
-- record type returning with quotes behavior.
@@ -1088,7 +1088,7 @@ CREATE TABLE test_jsonb_constraints (
CONSTRAINT test_jsonb_constraint3
CHECK (JSON_VALUE(js::jsonb, '$.a' RETURNING int DEFAULT '12' ON EMPTY ERROR ON ERROR) > i)
CONSTRAINT test_jsonb_constraint4
- CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < jsonb '[10]')
+ CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) = jsonb '[10]')
CONSTRAINT test_jsonb_constraint5
CHECK (JSON_QUERY(js::jsonb, '$.a' RETURNING char(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) > 'a' COLLATE "C")
);
@@ -1103,7 +1103,7 @@ Check constraints:
"test_jsonb_constraint1" CHECK (js IS JSON)
"test_jsonb_constraint2" CHECK (JSON_EXISTS(js::jsonb, '$."a"' PASSING i + 5 AS int, i::text AS txt, ARRAY[1, 2, 3] AS arr))
"test_jsonb_constraint3" CHECK (JSON_VALUE(js::jsonb, '$."a"' RETURNING integer DEFAULT 12 ON EMPTY ERROR ON ERROR) > i)
- "test_jsonb_constraint4" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER KEEP QUOTES EMPTY OBJECT ON ERROR) < '[10]'::jsonb)
+ "test_jsonb_constraint4" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER KEEP QUOTES EMPTY OBJECT ON ERROR) = '[10]'::jsonb)
"test_jsonb_constraint5" CHECK (JSON_QUERY(js::jsonb, '$."a"' RETURNING character(5) WITHOUT WRAPPER OMIT QUOTES EMPTY ARRAY ON EMPTY) > ('a'::bpchar COLLATE "C"))
SELECT check_clause
@@ -1113,7 +1113,7 @@ ORDER BY 1;
check_clause
----------------------------------------------------------------------------------------------------------------------------------------
(JSON_QUERY((js)::jsonb, '$."a"' RETURNING character(5) WITHOUT WRAPPER OMIT QUOTES EMPTY ARRAY ON EMPTY) > ('a'::bpchar COLLATE "C"))
- (JSON_QUERY((js)::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER KEEP QUOTES EMPTY OBJECT ON ERROR) < '[10]'::jsonb)
+ (JSON_QUERY((js)::jsonb, '$."a"' RETURNING jsonb WITH CONDITIONAL WRAPPER KEEP QUOTES EMPTY OBJECT ON ERROR) = '[10]'::jsonb)
(JSON_VALUE((js)::jsonb, '$."a"' RETURNING integer DEFAULT 12 ON EMPTY ERROR ON ERROR) > i)
(js IS JSON)
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]).
INSERT INTO test_jsonb_constraints VALUES ('{"a": 1}', 1);
ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint3"
DETAIL: Failing row contains ({"a": 1}, 1, [1, 2]).
-INSERT INTO test_jsonb_constraints VALUES ('{"a": 7}', 1);
-ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint5"
-DETAIL: Failing row contains ({"a": 7}, 1, [1, 2]).
INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1);
ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint4"
DETAIL: Failing row contains ({"a": 10}, 1, [1, 2]).
diff --git a/src/test/regress/sql/sqljson_queryfuncs.sql b/src/test/regress/sql/sqljson_queryfuncs.sql
index 21ff7787a2..21b5d49ece 100644
--- a/src/test/regress/sql/sqljson_queryfuncs.sql
+++ b/src/test/regress/sql/sqljson_queryfuncs.sql
@@ -146,11 +146,11 @@ select json_value('{"a": "1.234"}', '$.a' returning int error on error);
SELECT JSON_VALUE(NULL::jsonb, '$');
SELECT
- JSON_QUERY(js, '$'),
- JSON_QUERY(js, '$' WITHOUT WRAPPER),
- JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER),
- JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER),
- JSON_QUERY(js, '$' WITH ARRAY WRAPPER)
+ JSON_QUERY(js, '$') AS "unspec",
+ JSON_QUERY(js, '$' WITHOUT WRAPPER) AS "without",
+ JSON_QUERY(js, '$' WITH CONDITIONAL WRAPPER) AS "with cond",
+ JSON_QUERY(js, '$' WITH UNCONDITIONAL ARRAY WRAPPER) AS "with uncond",
+ JSON_QUERY(js, '$' WITH ARRAY WRAPPER) AS "with"
FROM
(VALUES
(jsonb 'null'),
@@ -331,7 +331,7 @@ CREATE TABLE test_jsonb_constraints (
CONSTRAINT test_jsonb_constraint3
CHECK (JSON_VALUE(js::jsonb, '$.a' RETURNING int DEFAULT '12' ON EMPTY ERROR ON ERROR) > i)
CONSTRAINT test_jsonb_constraint4
- CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) < jsonb '[10]')
+ CHECK (JSON_QUERY(js::jsonb, '$.a' WITH CONDITIONAL WRAPPER EMPTY OBJECT ON ERROR) = jsonb '[10]')
CONSTRAINT test_jsonb_constraint5
CHECK (JSON_QUERY(js::jsonb, '$.a' RETURNING char(5) OMIT QUOTES EMPTY ARRAY ON EMPTY) > 'a' COLLATE "C")
);
@@ -353,7 +353,6 @@ INSERT INTO test_jsonb_constraints VALUES ('1', 1);
INSERT INTO test_jsonb_constraints VALUES ('[]');
INSERT INTO test_jsonb_constraints VALUES ('{"b": 1}', 1);
INSERT INTO test_jsonb_constraints VALUES ('{"a": 1}', 1);
-INSERT INTO test_jsonb_constraints VALUES ('{"a": 7}', 1);
INSERT INTO test_jsonb_constraints VALUES ('{"a": 10}', 1);
DROP TABLE test_jsonb_constraints;
--
2.43.0
On 11.09.24 09:51, Amit Langote wrote:
I've updated your patch to include updated test outputs and a nearby
code comment expanded. Do you intend to commit it or do you prefer
that I do?This change looks unrelated:
-ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint4" +ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint5"Is this some randomness in the way these constraints are evaluated?
The result of JSON_QUERY() in the CHECK constraint changes, so the
constraint that previously failed now succeeds after this change,
because the comparison looked like this before and after:-- before
postgres=# select jsonb '[10]' < jsonb '[10]';
?column?
----------
f
(1 row)-- after
postgres=# select jsonb '10' < jsonb '[10]';
?column?
----------
t
(1 row)That causes the next constraint to be evaluated and its failure
reported instead.In the attached, I've adjusted the constraint for the test case to be
a bit more relevant and removed a nearby somewhat redundant test,
mainly because its output changes after the adjustment.
Ok, that looks good. Good that we could clear that up a bit.
On Wed, Sep 11, 2024 at 6:57 PM Peter Eisentraut <peter@eisentraut.org> wrote:
On 11.09.24 09:51, Amit Langote wrote:
I've updated your patch to include updated test outputs and a nearby
code comment expanded. Do you intend to commit it or do you prefer
that I do?This change looks unrelated:
-ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint4" +ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint5"Is this some randomness in the way these constraints are evaluated?
The result of JSON_QUERY() in the CHECK constraint changes, so the
constraint that previously failed now succeeds after this change,
because the comparison looked like this before and after:-- before
postgres=# select jsonb '[10]' < jsonb '[10]';
?column?
----------
f
(1 row)-- after
postgres=# select jsonb '10' < jsonb '[10]';
?column?
----------
t
(1 row)That causes the next constraint to be evaluated and its failure
reported instead.In the attached, I've adjusted the constraint for the test case to be
a bit more relevant and removed a nearby somewhat redundant test,
mainly because its output changes after the adjustment.Ok, that looks good. Good that we could clear that up a bit.
Thanks for checking. Would you like me to commit it?
--
Thanks, Amit Langote
On 11.09.24 13:25, Amit Langote wrote:
On Wed, Sep 11, 2024 at 6:57 PM Peter Eisentraut <peter@eisentraut.org> wrote:
On 11.09.24 09:51, Amit Langote wrote:
I've updated your patch to include updated test outputs and a nearby
code comment expanded. Do you intend to commit it or do you prefer
that I do?This change looks unrelated:
-ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint4" +ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint5"Is this some randomness in the way these constraints are evaluated?
The result of JSON_QUERY() in the CHECK constraint changes, so the
constraint that previously failed now succeeds after this change,
because the comparison looked like this before and after:-- before
postgres=# select jsonb '[10]' < jsonb '[10]';
?column?
----------
f
(1 row)-- after
postgres=# select jsonb '10' < jsonb '[10]';
?column?
----------
t
(1 row)That causes the next constraint to be evaluated and its failure
reported instead.In the attached, I've adjusted the constraint for the test case to be
a bit more relevant and removed a nearby somewhat redundant test,
mainly because its output changes after the adjustment.Ok, that looks good. Good that we could clear that up a bit.
Thanks for checking. Would you like me to commit it?
Please do.
On Wed, Sep 11, 2024 at 8:56 PM Peter Eisentraut <peter@eisentraut.org> wrote:
On 11.09.24 13:25, Amit Langote wrote:
On Wed, Sep 11, 2024 at 6:57 PM Peter Eisentraut <peter@eisentraut.org> wrote:
On 11.09.24 09:51, Amit Langote wrote:
I've updated your patch to include updated test outputs and a nearby
code comment expanded. Do you intend to commit it or do you prefer
that I do?This change looks unrelated:
-ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint4" +ERROR: new row for relation "test_jsonb_constraints" violates check constraint "test_jsonb_constraint5"Is this some randomness in the way these constraints are evaluated?
The result of JSON_QUERY() in the CHECK constraint changes, so the
constraint that previously failed now succeeds after this change,
because the comparison looked like this before and after:-- before
postgres=# select jsonb '[10]' < jsonb '[10]';
?column?
----------
f
(1 row)-- after
postgres=# select jsonb '10' < jsonb '[10]';
?column?
----------
t
(1 row)That causes the next constraint to be evaluated and its failure
reported instead.In the attached, I've adjusted the constraint for the test case to be
a bit more relevant and removed a nearby somewhat redundant test,
mainly because its output changes after the adjustment.Ok, that looks good. Good that we could clear that up a bit.
Thanks for checking. Would you like me to commit it?
Please do.
Done. Thanks for the report and the patch.
--
Thanks, Amit Langote
On Thu, Sep 12, 2024 at 8:12 PM Amit Langote <amitlangote09@gmail.com> wrote:
Hi Andreas,
On Thu, Sep 12, 2024 at 7:08 PM Andreas Ulbrich
<andreas.ulbrich@matheversum.de> wrote:Salvete!
Sorry for my out of the rules replay, but I'm not at home, and also I can't verify and check your patch.
But I think you have missed the docu in your fix:
Must the example there not also be changed:
From
JSON_QUERY(jsonb '[1,[2,3],null]', 'lax $[*][$off]' PASSING 1 AS off WITH CONDITIONAL WRAPPER) → [3]
to
JSON_QUERY(jsonb '[1,[2,3],null]', 'lax $[*][$off]' PASSING 1 AS off WITH CONDITIONAL WRAPPER) → 3
You're right, good catch.
I had checked whether the documentation text needed fixing, but failed
to notice that an example
is using CONDITIONAL. Will fix, thanks for the report.
I have pushed the fix. Thanks Andreas for the report.
--
Thanks, Amit Langote
Import Notes
Reply to msg id not found: CA+HiwqHR8hcSNZjtFxjukv3UJ-KMcuOkQ4Fd1KpPS10wdjT8w@mail.gmail.comReference msg id not found: d5e7279d-cd0a-4d62-8948-cca982816e82@matheversum.deReference msg id not found: CA+HiwqHR8hcSNZjtFxjukv3UJ-KMcuOkQ4Fd1KpPS10wdjT8w@mail.gmail.com | Resolved by subject fallback