From 403ab2f7da315069017f22ebd46bb5eb38ae1f4e Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Fri, 7 Jun 2024 10:21:06 -0400 Subject: [PATCH v3] Add tests for jsonpath `.*` on arrays There was no coverage for the path to unwrap an array before applying `.*` to it, so add tests that explicitly test `.*` for both objects and arrays, showing how no results are returned for an array of scalars, but results are returned when the array contains an object. Also test the behavior in strict mode and with the `@?` operator. While at it, teach `executeAnyItem()` to return `jperOk` when `found` exist, not because it will be used (the result and `found` are inspected by different functions), but because it seems like the proper thing to return from `executeAnyItem()` when considered in isolation. Unrelated but potentially useful to future source readers: document `GetJsonPathVar` and `CountJsonPathVars`. --- src/backend/utils/adt/jsonpath_exec.c | 11 ++++- src/test/regress/expected/jsonb_jsonpath.out | 45 ++++++++++++++++++++ src/test/regress/sql/jsonb_jsonpath.sql | 10 +++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c index 8a0a2dbc85..5ef168e978 100644 --- a/src/backend/utils/adt/jsonpath_exec.c +++ b/src/backend/utils/adt/jsonpath_exec.c @@ -2002,8 +2002,10 @@ executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc, if (res == jperOk && !found) break; } - else if (found) + else if (found) { JsonValueListAppend(found, copyJsonbValue(&v)); + res = jperOk; + } else return jperOk; } @@ -2976,7 +2978,8 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item, } /* - * Returns the computed value of a JSON path variable with given name. + * Definition of JsonPathGetVarCallback for when JsonPathExecContext.vars + * is specified as a List value. */ static JsonbValue * GetJsonPathVar(void *cxt, char *varName, int varNameLen, @@ -3022,6 +3025,10 @@ GetJsonPathVar(void *cxt, char *varName, int varNameLen, return result; } +/* + * Definition of JsonPathCountVarsCallback for when JsonPathExecContext.vars + * is specified as a List value. + */ static int CountJsonPathVars(void *cxt) { diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out index c3f8e8249d..0611830842 100644 --- a/src/test/regress/expected/jsonb_jsonpath.out +++ b/src/test/regress/expected/jsonb_jsonpath.out @@ -4384,3 +4384,48 @@ ORDER BY s1.num, s2.num; {"s": "B"} | {"s": "B"} | false | true | true | true | false (144 rows) +-- Test any key on arrays with and without unwrapping. +select jsonb_path_query('{"a": [1,2,3], "b": [3,4,5]}', '$.*'); + jsonb_path_query +------------------ + [1, 2, 3] + [3, 4, 5] +(2 rows) + +select jsonb_path_query('[1,2,3]', '$.*'); + jsonb_path_query +------------------ +(0 rows) + +select jsonb_path_query('[1,2,3,{"b": [3,4,5]}]', '$.*'); + jsonb_path_query +------------------ + [3, 4, 5] +(1 row) + +select jsonb_path_query('[1,2,3,{"b": [3,4,5]}]', 'strict $.*'); +ERROR: jsonpath wildcard member accessor can only be applied to an object +select jsonb '{"a": [1,2,3], "b": [3,4,5]}' @? '$.*'; + ?column? +---------- + t +(1 row) + +select jsonb '[1,2,3]' @? '$.*'; + ?column? +---------- + f +(1 row) + +select jsonb '[1,2,3,{"b": [3,4,5]}]' @? '$.*'; + ?column? +---------- + t +(1 row) + +select jsonb '[1,2,3,{"b": [3,4,5]}]' @? 'strict $.*'; + ?column? +---------- + +(1 row) + diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql index cbd2db533d..4f6139b7ef 100644 --- a/src/test/regress/sql/jsonb_jsonpath.sql +++ b/src/test/regress/sql/jsonb_jsonpath.sql @@ -1115,3 +1115,13 @@ SELECT jsonb_path_query_first(s1.j, '$.s > $s', vars => s2.j) gt FROM str s1, str s2 ORDER BY s1.num, s2.num; + +-- Test any key on arrays with and without unwrapping. +select jsonb_path_query('{"a": [1,2,3], "b": [3,4,5]}', '$.*'); +select jsonb_path_query('[1,2,3]', '$.*'); +select jsonb_path_query('[1,2,3,{"b": [3,4,5]}]', '$.*'); +select jsonb_path_query('[1,2,3,{"b": [3,4,5]}]', 'strict $.*'); +select jsonb '{"a": [1,2,3], "b": [3,4,5]}' @? '$.*'; +select jsonb '[1,2,3]' @? '$.*'; +select jsonb '[1,2,3,{"b": [3,4,5]}]' @? '$.*'; +select jsonb '[1,2,3,{"b": [3,4,5]}]' @? 'strict $.*'; -- 2.45.2