From 042ae7d0cfdb8683e1a07f691a21e47d17ea2b39 Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Mon, 5 Dec 2022 17:02:29 +0300 Subject: [PATCH] Teach jsonpath to handle missing variables The current jsonpath code assumes that the referenced variable always exists. It could only throw an error at the value valuation time. At the same time existence checking assumes variable is present without valuation, and error suppression doesn't work for missing variables. This commit makes jsonpath code to handle variables in the similar way to key accessors. The existence is now checked runtime, and error suppression is also applied. Backpatch to 12 where jsonpath was introduced. Reported-by: David G. Johnston Discussion: https://postgr.es/m/CAKFQuwbeytffJkVnEqDyLZ%3DrQsznoTh1OgDoOF3VmOMkxcTMjA%40mail.gmail.com Author: Alexander Korotkov Backpatch-through: 12 --- src/backend/utils/adt/jsonpath_exec.c | 47 +++++++++++++++----- src/test/regress/expected/jsonb_jsonpath.out | 5 ++- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c index 8d83b2edb35..e15ced19c4e 100644 --- a/src/backend/utils/adt/jsonpath_exec.c +++ b/src/backend/utils/adt/jsonpath_exec.c @@ -224,8 +224,9 @@ static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonValueList *found, JsonPathBool res); static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item, JsonbValue *value); -static void getJsonPathVariable(JsonPathExecContext *cxt, - JsonPathItem *variable, Jsonb *vars, JsonbValue *value); +static JsonPathExecResult getJsonPathVariable(JsonPathExecContext *cxt, + JsonPathItem *variable, + JsonbValue *value); static int JsonbArraySize(JsonbValue *jb); static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p); @@ -952,7 +953,6 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp, case jpiBool: case jpiNumeric: case jpiString: - case jpiVariable: { JsonbValue vbuf; JsonbValue *v; @@ -975,6 +975,24 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp, } break; + case jpiVariable: + { + JsonbValue vbuf; + JsonbValue *v; + bool hasNext = jspGetNext(jsp, &elem); + + v = hasNext ? &vbuf : palloc(sizeof(*v)); + + baseObject = cxt->baseObject; + res = getJsonPathVariable(cxt, jsp, v); + + if (res == jperOk) + res = executeNextItem(cxt, jsp, &elem, + v, found, hasNext); + cxt->baseObject = baseObject; + } + break; + case jpiType: { JsonbValue *jbv = palloc(sizeof(*jbv)); @@ -2092,9 +2110,6 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item, value->val.string.val = jspGetString(item, &value->val.string.len); break; - case jpiVariable: - getJsonPathVariable(cxt, item, cxt->vars, value); - return; default: elog(ERROR, "unexpected jsonpath item type"); } @@ -2103,19 +2118,20 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item, /* * Get the value of variable passed to jsonpath executor */ -static void +static JsonPathExecResult getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable, - Jsonb *vars, JsonbValue *value) + JsonbValue *value) { char *varName; int varNameLength; JsonbValue tmp; JsonbValue *v; + Jsonb *vars = cxt->vars; + JsonPathExecResult res; if (!vars) { - value->type = jbvNull; - return; + return jperNotFound; } Assert(variable->type == jpiVariable); @@ -2130,17 +2146,26 @@ getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable, { *value = *v; pfree(v); + res = jperOk; } - else + else if (!jspIgnoreStructuralErrors(cxt)) { + if (!jspThrowErrors(cxt)) + res = jperError; ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find jsonpath variable \"%s\"", pnstrdup(varName, varNameLength)))); } + else + { + res = jperNotFound; + } JsonbInitBinary(&tmp, vars); setBaseObject(cxt, &tmp, 1); + + return res; } /**************** Support functions for JsonPath execution *****************/ diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out index 508ddd797ed..19c6bdc72c7 100644 --- a/src/test/regress/expected/jsonb_jsonpath.out +++ b/src/test/regress/expected/jsonb_jsonpath.out @@ -486,7 +486,10 @@ select * from jsonb_path_query('{"a": 10}', '$'); (1 row) select * from jsonb_path_query('{"a": 10}', '$ ? (@.a < $value)'); -ERROR: could not find jsonpath variable "value" + jsonb_path_query +------------------ +(0 rows) + select * from jsonb_path_query('{"a": 10}', '$ ? (@.a < $value)', '1'); ERROR: "vars" argument is not an object DETAIL: Jsonpath parameters should be encoded as key-value pairs of "vars" object. -- 2.24.3 (Apple Git-128)