From 92ff39dca968c5b0c7ac31b0b495780c9a9482a7 Mon Sep 17 00:00:00 2001
From: Nikita Glukhov <n.gluhov@postgrespro.ru>
Date: Fri, 17 Apr 2020 18:05:48 +0300
Subject: [PATCH] Return FALSE instead of NULL in jsonpath operators

---
 src/backend/utils/adt/jsonpath_exec.c        | 25 ++++++++++++++-----------
 src/test/regress/expected/jsonb_jsonpath.out | 28 ++++++++++++++--------------
 2 files changed, 28 insertions(+), 25 deletions(-)

diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index bc06306..2c216ff 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -269,7 +269,7 @@ static int	compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
  *		an analogy in SQL/JSON, so we define its behavior on our own.
  */
 static Datum
-jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
+jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz, bool null_on_error)
 {
 	Jsonb	   *jb = PG_GETARG_JSONB_P(0);
 	JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
@@ -288,7 +288,7 @@ jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
 	PG_FREE_IF_COPY(jb, 0);
 	PG_FREE_IF_COPY(jp, 1);
 
-	if (jperIsError(res))
+	if (jperIsError(res) && null_on_error)
 		PG_RETURN_NULL();
 
 	PG_RETURN_BOOL(res == jperOk);
@@ -297,13 +297,13 @@ jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
 Datum
 jsonb_path_exists(PG_FUNCTION_ARGS)
 {
-	return jsonb_path_exists_internal(fcinfo, false);
+	return jsonb_path_exists_internal(fcinfo, false, true);
 }
 
 Datum
 jsonb_path_exists_tz(PG_FUNCTION_ARGS)
 {
-	return jsonb_path_exists_internal(fcinfo, true);
+	return jsonb_path_exists_internal(fcinfo, true, true);
 }
 
 /*
@@ -315,7 +315,7 @@ Datum
 jsonb_path_exists_opr(PG_FUNCTION_ARGS)
 {
 	/* just call the other one -- it can handle both cases */
-	return jsonb_path_exists_internal(fcinfo, false);
+	return jsonb_path_exists_internal(fcinfo, false, false);
 }
 
 /*
@@ -324,7 +324,7 @@ jsonb_path_exists_opr(PG_FUNCTION_ARGS)
  *		See jsonb_path_exists() comment for details regarding error handling.
  */
 static Datum
-jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
+jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz, bool null_on_error)
 {
 	Jsonb	   *jb = PG_GETARG_JSONB_P(0);
 	JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
@@ -351,7 +351,7 @@ jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
 			PG_RETURN_BOOL(jbv->val.boolean);
 
 		if (jbv->type == jbvNull)
-			PG_RETURN_NULL();
+			silent = true;		/* supress errors and return NULL/FALSE */
 	}
 
 	if (!silent)
@@ -359,19 +359,22 @@ jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
 				(errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
 				 errmsg("single boolean result is expected")));
 
-	PG_RETURN_NULL();
+	if (null_on_error)
+		PG_RETURN_NULL();
+	else
+		PG_RETURN_BOOL(false);
 }
 
 Datum
 jsonb_path_match(PG_FUNCTION_ARGS)
 {
-	return jsonb_path_match_internal(fcinfo, false);
+	return jsonb_path_match_internal(fcinfo, false, true);
 }
 
 Datum
 jsonb_path_match_tz(PG_FUNCTION_ARGS)
 {
-	return jsonb_path_match_internal(fcinfo, true);
+	return jsonb_path_match_internal(fcinfo, true, true);
 }
 
 /*
@@ -383,7 +386,7 @@ Datum
 jsonb_path_match_opr(PG_FUNCTION_ARGS)
 {
 	/* just call the other one -- it can handle both cases */
-	return jsonb_path_match_internal(fcinfo, false);
+	return jsonb_path_match_internal(fcinfo, false, false);
 }
 
 /*
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 83a050d..75d75cd 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -31,7 +31,7 @@ select jsonb '{"a": 12}' @? '$.a + 2';
 select jsonb '{"a": 12}' @? '$.b + 2';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 select jsonb '{"a": {"a": 12}}' @? '$.a.a';
@@ -61,7 +61,7 @@ select jsonb '{"b": {"a": 12}}' @? '$.*.b';
 select jsonb '{"b": {"a": 12}}' @? 'strict $.*.b';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 select jsonb '{}' @? '$.*';
@@ -115,7 +115,7 @@ select jsonb '[1]' @? '$[1]';
 select jsonb '[1]' @? 'strict $[1]';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 select jsonb_path_query('[1]', 'strict $[1]');
@@ -128,13 +128,13 @@ select jsonb_path_query('[1]', 'strict $[1]', silent => true);
 select jsonb '[1]' @? 'lax $[10000000000000000]';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 select jsonb '[1]' @? 'strict $[10000000000000000]';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 select jsonb_path_query('[1]', 'lax $[10000000000000000]');
@@ -174,7 +174,7 @@ select jsonb '[1]' @? '$[1.2]';
 select jsonb '[1]' @? 'strict $[1.2]';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 select jsonb '{"a": [1,2,3], "b": [3,4,5]}' @? '$ ? (@.a[*] >  @.b[*])';
@@ -1097,13 +1097,13 @@ select jsonb '[1,"2",0,3]' @? '-$[*]';
 select jsonb '["1",2,0,3]' @? 'strict -$[*]';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 select jsonb '[1,"2",0,3]' @? 'strict -$[*]';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 -- unwrapping of operator arguments in lax mode
@@ -1175,37 +1175,37 @@ select jsonb '2' @@ '$ <= 1';
 select jsonb '2' @@ '$ == "2"';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 select jsonb '2' @@ '1';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 select jsonb '{}' @@ '$';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 select jsonb '[]' @@ '$';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 select jsonb '[1,2,3]' @@ '$[*]';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 select jsonb '[]' @@ '$[*]';
  ?column? 
 ----------
- 
+ f
 (1 row)
 
 select jsonb_path_match('[[1, true], [2, false]]', 'strict $[*] ? (@[0] > $x) [1]', '{"x": 1}');
-- 
2.7.4

