From 403ab2f7da315069017f22ebd46bb5eb38ae1f4e Mon Sep 17 00:00:00 2001
From: "David E. Wheeler" <david@justatheory.com>
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

