From 62fd2860abdaaad97741670e80dcd84cc7222925 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Fri, 14 Jun 2024 12:02:05 -0400 Subject: [PATCH v2] Teach jsonpath string() to unwrap in lax mode All other jsonpath methods, aside from type() and .size(), will unwrap an array in lax mode, but string(), added in 66ea94e, overlooked this behavior. A discussion on pgsql-hackers[1] cites the SQL standard: > General Rule 11 g ii 6) A) says just "if MODE is lax and > is not type or size, then let BASE be Unwrap(BASE)." No special > exemption there for string(), nor further below at C) XV) for the > operation of string(). So teach string() to also unwrap in lax mode, update the test for this behavior, and add a line to the docs about the behavior of methods in lax mode. [1]: https://www.postgresql.org/message-id/flat/A64AE04F-4410-42B7-A141-7A7349260F4D%40justatheory.com --- doc/src/sgml/func.sgml | 5 ++++- src/backend/utils/adt/jsonpath_exec.c | 3 +++ src/test/regress/expected/jsonb_jsonpath.out | 9 +++++++-- src/test/regress/sql/jsonb_jsonpath.sql | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 17c44bc338..ededec932b 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -17792,7 +17792,10 @@ ERROR: jsonpath member accessor can only be applied to an object methods available in jsonpath. Note that while the unary operators and methods can be applied to multiple values resulting from a preceding path step, the binary operators (addition etc.) can only be - applied to single values. + applied to single values. In lax mode, methods applied to an array will be + executed for each value in the array. The exceptions are + .type() and .size(), which apply to + the array itself. diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c index ceb30033e1..c30d059a76 100644 --- a/src/backend/utils/adt/jsonpath_exec.c +++ b/src/backend/utils/adt/jsonpath_exec.c @@ -1606,6 +1606,9 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue jbv; char *tmp = NULL; + if (unwrap && JsonbType(jb) == jbvArray) + return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false); + switch (JsonbType(jb)) { case jbvString: diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out index c3f8e8249d..a13eee24fb 100644 --- a/src/test/regress/expected/jsonb_jsonpath.out +++ b/src/test/regress/expected/jsonb_jsonpath.out @@ -2524,8 +2524,13 @@ select jsonb_path_query('null', '$.string()', silent => true); ------------------ (0 rows) -select jsonb_path_query('[]', '$.string()'); -ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value +select jsonb_path_query('[2, true]', '$.string()'); + jsonb_path_query +------------------ + "2" + "true" +(2 rows) + select jsonb_path_query('[]', 'strict $.string()'); ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value select jsonb_path_query('{}', '$.string()'); diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql index cbd2db533d..9c9d327140 100644 --- a/src/test/regress/sql/jsonb_jsonpath.sql +++ b/src/test/regress/sql/jsonb_jsonpath.sql @@ -575,7 +575,7 @@ select jsonb_path_query('12.3', '$.number() * 2'); -- Test .string() select jsonb_path_query('null', '$.string()'); select jsonb_path_query('null', '$.string()', silent => true); -select jsonb_path_query('[]', '$.string()'); +select jsonb_path_query('[2, true]', '$.string()'); select jsonb_path_query('[]', 'strict $.string()'); select jsonb_path_query('{}', '$.string()'); select jsonb_path_query('[]', 'strict $.string()', silent => true); -- 2.45.2