From eaaa52928c7facfa065d36de566ce891a2a7c55e Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Sat, 15 Jun 2024 19:44:21 +0530 Subject: [PATCH] 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 Signed-off-by: David E. Wheeler --- doc/src/sgml/func.sgml | 5 ++++- src/backend/utils/adt/jsonpath_exec.c | 3 +++ src/test/regress/expected/jsonb_jsonpath.out | 12 +++++++++++- src/test/regress/sql/jsonb_jsonpath.sql | 1 + 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 17c44bc338464..2609269610b27 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 ceb30033e1c66..c30d059a76272 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 c3f8e8249dbfe..a6112e86fa7ae 100644 --- a/src/test/regress/expected/jsonb_jsonpath.out +++ b/src/test/regress/expected/jsonb_jsonpath.out @@ -2525,7 +2525,10 @@ 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 + jsonb_path_query +------------------ +(0 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()'); @@ -2576,6 +2579,13 @@ select jsonb_path_query('1234', '$.string().type()'); "string" (1 row) +select jsonb_path_query('[2, true]', '$.string()'); + jsonb_path_query +------------------ + "2" + "true" +(2 rows) + select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()'); ERROR: cannot convert value from timestamptz to timestamp without time zone usage HINT: Use *_tz() function for time zone support. diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql index cbd2db533d40f..5e14f7759bb64 100644 --- a/src/test/regress/sql/jsonb_jsonpath.sql +++ b/src/test/regress/sql/jsonb_jsonpath.sql @@ -586,6 +586,7 @@ select jsonb_path_query('"1.23aaa"', '$.string()'); select jsonb_path_query('1234', '$.string()'); select jsonb_path_query('true', '$.string()'); select jsonb_path_query('1234', '$.string().type()'); +select jsonb_path_query('[2, true]', '$.string()'); select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()'); select jsonb_path_query_tz('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()'); -- should work select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()');