From b4cc19ab01ffe6a72a915b21aa41536de80923f5 Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Mon, 1 Apr 2019 18:09:20 +0300 Subject: [PATCH] Throw error in jsonb_path_match() when result is not single boolean jsonb_path_match() checks if jsonb document matches jsonpath query. Therefore, jsonpath query should return single boolean. Currently, if result of jsonpath is not a single boolean, NULL is returned independently whether silent mode is on or off. But that appears to be wrong when silent mode is off. This commit makes jsonb_path_match() throw an error in this case. Author: Nikita Glukhov --- src/backend/utils/adt/jsonpath_exec.c | 26 ++++++---- src/test/regress/expected/jsonb_jsonpath.out | 51 ++++++++++++++++++++ src/test/regress/sql/jsonb_jsonpath.sql | 12 +++++ 3 files changed, 80 insertions(+), 9 deletions(-) diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c index c07225749e..074cea24ae 100644 --- a/src/backend/utils/adt/jsonpath_exec.c +++ b/src/backend/utils/adt/jsonpath_exec.c @@ -320,7 +320,6 @@ jsonb_path_match(PG_FUNCTION_ARGS) { Jsonb *jb = PG_GETARG_JSONB_P(0); JsonPath *jp = PG_GETARG_JSONPATH_P(1); - JsonbValue *jbv; JsonValueList found = {0}; Jsonb *vars = NULL; bool silent = true; @@ -333,18 +332,27 @@ jsonb_path_match(PG_FUNCTION_ARGS) (void) executeJsonPath(jp, vars, jb, !silent, &found); - if (JsonValueListLength(&found) < 1) - PG_RETURN_NULL(); - - jbv = JsonValueListHead(&found); - PG_FREE_IF_COPY(jb, 0); PG_FREE_IF_COPY(jp, 1); - if (jbv->type != jbvBool) - PG_RETURN_NULL(); + if (JsonValueListLength(&found) == 1) + { + JsonbValue *jbv = JsonValueListHead(&found); - PG_RETURN_BOOL(jbv->val.boolean); + if (jbv->type == jbvBool) + PG_RETURN_BOOL(jbv->val.boolean); + + if (jbv->type == jbvNull) + PG_RETURN_NULL(); + } + + if (!silent) + ereport(ERROR, + (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED), + errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED), + errdetail("expression should return a singleton boolean"))); + + PG_RETURN_NULL(); } /* diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out index 4a84d9157f..49a857bca6 100644 --- a/src/test/regress/expected/jsonb_jsonpath.out +++ b/src/test/regress/expected/jsonb_jsonpath.out @@ -1769,6 +1769,57 @@ SELECT jsonb_path_exists('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*] ? (@. f (1 row) +SELECT jsonb_path_match('true', '$', silent => false); + jsonb_path_match +------------------ + t +(1 row) + +SELECT jsonb_path_match('false', '$', silent => false); + jsonb_path_match +------------------ + f +(1 row) + +SELECT jsonb_path_match('null', '$', silent => false); + jsonb_path_match +------------------ + +(1 row) + +SELECT jsonb_path_match('1', '$', silent => true); + jsonb_path_match +------------------ + +(1 row) + +SELECT jsonb_path_match('1', '$', silent => false); +ERROR: singleton SQL/JSON item required +DETAIL: expression should return a singleton boolean +SELECT jsonb_path_match('"a"', '$', silent => false); +ERROR: singleton SQL/JSON item required +DETAIL: expression should return a singleton boolean +SELECT jsonb_path_match('{}', '$', silent => false); +ERROR: singleton SQL/JSON item required +DETAIL: expression should return a singleton boolean +SELECT jsonb_path_match('[true]', '$', silent => false); +ERROR: singleton SQL/JSON item required +DETAIL: expression should return a singleton boolean +SELECT jsonb_path_match('{}', 'lax $.a', silent => false); +ERROR: singleton SQL/JSON item required +DETAIL: expression should return a singleton boolean +SELECT jsonb_path_match('{}', 'strict $.a', silent => false); +ERROR: SQL/JSON member not found +DETAIL: JSON object does not contain key "a" +SELECT jsonb_path_match('{}', 'strict $.a', silent => true); + jsonb_path_match +------------------ + +(1 row) + +SELECT jsonb_path_match('[true, true]', '$[*]', silent => false); +ERROR: singleton SQL/JSON item required +DETAIL: expression should return a singleton boolean SELECT jsonb '[{"a": 1}, {"a": 2}]' @@ '$[*].a > 1'; ?column? ---------- diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql index 28c861bb17..464ff94be3 100644 --- a/src/test/regress/sql/jsonb_jsonpath.sql +++ b/src/test/regress/sql/jsonb_jsonpath.sql @@ -366,6 +366,18 @@ SELECT jsonb_path_exists('[{"a": 1}, {"a": 2}]', '$[*].a ? (@ > 1)'); SELECT jsonb_path_exists('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*] ? (@.a > $min && @.a < $max)', vars => '{"min": 1, "max": 4}'); SELECT jsonb_path_exists('[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 5}]', '$[*] ? (@.a > $min && @.a < $max)', vars => '{"min": 3, "max": 4}'); +SELECT jsonb_path_match('true', '$', silent => false); +SELECT jsonb_path_match('false', '$', silent => false); +SELECT jsonb_path_match('null', '$', silent => false); +SELECT jsonb_path_match('1', '$', silent => true); +SELECT jsonb_path_match('1', '$', silent => false); +SELECT jsonb_path_match('"a"', '$', silent => false); +SELECT jsonb_path_match('{}', '$', silent => false); +SELECT jsonb_path_match('[true]', '$', silent => false); +SELECT jsonb_path_match('{}', 'lax $.a', silent => false); +SELECT jsonb_path_match('{}', 'strict $.a', silent => false); +SELECT jsonb_path_match('{}', 'strict $.a', silent => true); +SELECT jsonb_path_match('[true, true]', '$[*]', silent => false); SELECT jsonb '[{"a": 1}, {"a": 2}]' @@ '$[*].a > 1'; SELECT jsonb '[{"a": 1}, {"a": 2}]' @@ '$[*].a > 2'; SELECT jsonb_path_match('[{"a": 1}, {"a": 2}]', '$[*].a > 1');