From e0926386dee0abdfb94881231537c830f4b2422f Mon Sep 17 00:00:00 2001 From: Amit Langote Date: Fri, 8 Dec 2023 17:06:17 +0900 Subject: [PATCH v33 3/8] Refactor code used by jsonpath executor to fetch variables Currently, getJsonPathVariable() directly extracts a named variable/key from the source Jsonb value. This commit puts that logic into a callback function called by getJsonPathVariable(). Other implementations of the callback may accept different forms of the source value(s), for example, a List of values passed from outside jsonpath_exec.c. Discussion: https://postgr.es/m/CA+HiwqE4XTdfb1nW=Ojoy_tQSRhYt-q_kb6i5d4xcKyrLC1Nbg@mail.gmail.com --- src/backend/utils/adt/jsonpath_exec.c | 132 ++++++++++++++++++-------- 1 file changed, 92 insertions(+), 40 deletions(-) diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c index 2d0599b4aa..ca095f844c 100644 --- a/src/backend/utils/adt/jsonpath_exec.c +++ b/src/backend/utils/adt/jsonpath_exec.c @@ -87,12 +87,17 @@ typedef struct JsonBaseObjectInfo int id; } JsonBaseObjectInfo; +typedef JsonbValue *(*JsonPathVarCallback) (void *vars, char *varName, int varNameLen, + JsonbValue *baseObject, int *baseObjectId); +typedef int (*JsonPathCountVarsCallback) (void *vars); + /* * Context of jsonpath execution. */ typedef struct JsonPathExecContext { - Jsonb *vars; /* variables to substitute into jsonpath */ + void *vars; /* variables to substitute into jsonpath */ + JsonPathVarCallback getVar; JsonbValue *root; /* for $ evaluation */ JsonbValue *current; /* for @ evaluation */ JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue() @@ -174,7 +179,9 @@ typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp, void *param); typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error); -static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars, +static JsonPathExecResult executeJsonPath(JsonPath *path, void *vars, + JsonPathVarCallback getVar, + JsonPathCountVarsCallback countVars, Jsonb *json, bool throwErrors, JsonValueList *result, bool useTz); static JsonPathExecResult executeItem(JsonPathExecContext *cxt, @@ -226,7 +233,12 @@ static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt, static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item, JsonbValue *value); static void getJsonPathVariable(JsonPathExecContext *cxt, - JsonPathItem *variable, Jsonb *vars, JsonbValue *value); + JsonPathItem *variable, JsonbValue *value); +static int countVariablesFromJsonb(void *varsJsonb); +static JsonbValue *getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, + int varNameLen, + JsonbValue *baseObject, + int *baseObjectId); static int JsonbArraySize(JsonbValue *jb); static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p); @@ -284,7 +296,9 @@ jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz) silent = PG_GETARG_BOOL(3); } - res = executeJsonPath(jp, vars, jb, !silent, NULL, tz); + res = executeJsonPath(jp, vars, getJsonPathVariableFromJsonb, + countVariablesFromJsonb, + jb, !silent, NULL, tz); PG_FREE_IF_COPY(jb, 0); PG_FREE_IF_COPY(jp, 1); @@ -339,7 +353,9 @@ jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz) silent = PG_GETARG_BOOL(3); } - (void) executeJsonPath(jp, vars, jb, !silent, &found, tz); + (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb, + countVariablesFromJsonb, + jb, !silent, &found, tz); PG_FREE_IF_COPY(jb, 0); PG_FREE_IF_COPY(jp, 1); @@ -417,7 +433,9 @@ jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz) vars = PG_GETARG_JSONB_P_COPY(2); silent = PG_GETARG_BOOL(3); - (void) executeJsonPath(jp, vars, jb, !silent, &found, tz); + (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb, + countVariablesFromJsonb, + jb, !silent, &found, tz); funcctx->user_fctx = JsonValueListGetList(&found); @@ -464,7 +482,9 @@ jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz) Jsonb *vars = PG_GETARG_JSONB_P(2); bool silent = PG_GETARG_BOOL(3); - (void) executeJsonPath(jp, vars, jb, !silent, &found, tz); + (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb, + countVariablesFromJsonb, + jb, !silent, &found, tz); PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found))); } @@ -495,7 +515,9 @@ jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz) Jsonb *vars = PG_GETARG_JSONB_P(2); bool silent = PG_GETARG_BOOL(3); - (void) executeJsonPath(jp, vars, jb, !silent, &found, tz); + (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb, + countVariablesFromJsonb, + jb, !silent, &found, tz); if (JsonValueListLength(&found) >= 1) PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found))); @@ -522,6 +544,9 @@ jsonb_path_query_first_tz(PG_FUNCTION_ARGS) * * 'path' - jsonpath to be executed * 'vars' - variables to be substituted to jsonpath + * 'getVar' - callback used by getJsonPathVariable() to extract variables from + * 'vars' + * 'countVars' - callback to count the number of jsonpath variables in 'vars' * 'json' - target document for jsonpath evaluation * 'throwErrors' - whether we should throw suppressible errors * 'result' - list to store result items into @@ -537,8 +562,10 @@ jsonb_path_query_first_tz(PG_FUNCTION_ARGS) * In other case it tries to find all the satisfied result items. */ static JsonPathExecResult -executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors, - JsonValueList *result, bool useTz) +executeJsonPath(JsonPath *path, void *vars, JsonPathVarCallback getVar, + JsonPathCountVarsCallback countVars, + Jsonb *json, bool throwErrors, JsonValueList *result, + bool useTz) { JsonPathExecContext cxt; JsonPathExecResult res; @@ -550,22 +577,16 @@ executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors, if (!JsonbExtractScalar(&json->root, &jbv)) JsonbInitBinary(&jbv, json); - if (vars && !JsonContainerIsObject(&vars->root)) - { - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("\"vars\" argument is not an object"), - errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object."))); - } - cxt.vars = vars; + cxt.getVar = getVar; cxt.laxMode = (path->header & JSONPATH_LAX) != 0; cxt.ignoreStructuralErrors = cxt.laxMode; cxt.root = &jbv; cxt.current = &jbv; cxt.baseObject.jbc = NULL; cxt.baseObject.id = 0; - cxt.lastGeneratedObjectId = vars ? 2 : 1; + /* 1 + number of base objects in vars */ + cxt.lastGeneratedObjectId = 1 + countVars(vars); cxt.innermostArraySize = -1; cxt.throwErrors = throwErrors; cxt.useTz = useTz; @@ -2108,7 +2129,7 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item, &value->val.string.len); break; case jpiVariable: - getJsonPathVariable(cxt, item, cxt->vars, value); + getJsonPathVariable(cxt, item, value); return; default: elog(ERROR, "unexpected jsonpath item type"); @@ -2120,42 +2141,73 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item, */ static void getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable, - Jsonb *vars, JsonbValue *value) + JsonbValue *value) { char *varName; int varNameLength; - JsonbValue tmp; + JsonbValue baseObject; + int baseObjectId; JsonbValue *v; - if (!vars) - { - value->type = jbvNull; - return; - } - Assert(variable->type == jpiVariable); varName = jspGetString(variable, &varNameLength); - tmp.type = jbvString; - tmp.val.string.val = varName; - tmp.val.string.len = varNameLength; - v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp); + if (cxt->vars == NULL || + (v = cxt->getVar(cxt->vars, varName, varNameLength, + &baseObject, &baseObjectId)) == NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find jsonpath variable \"%s\"", + pnstrdup(varName, varNameLength)))); - if (v) + if (baseObjectId > 0) { *value = *v; - pfree(v); + setBaseObject(cxt, &baseObject, baseObjectId); } - else +} + +static int +countVariablesFromJsonb(void *varsJsonb) +{ + Jsonb *vars = varsJsonb; + + if (vars && !JsonContainerIsObject(&vars->root)) { ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("could not find jsonpath variable \"%s\"", - pnstrdup(varName, varNameLength)))); + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("\"vars\" argument is not an object"), + errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")); } - JsonbInitBinary(&tmp, vars); - setBaseObject(cxt, &tmp, 1); + /* count of base objects */ + return vars ? 1 : 0; +} + +static JsonbValue * +getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, int varNameLength, + JsonbValue *baseObject, int *baseObjectId) +{ + Jsonb *vars = varsJsonb; + JsonbValue tmp; + JsonbValue *result; + + tmp.type = jbvString; + tmp.val.string.val = varName; + tmp.val.string.len = varNameLength; + + result = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp); + + if (result == NULL) + { + *baseObjectId = -1; + return NULL; + } + + *baseObjectId = 1; + JsonbInitBinary(baseObject, vars); + + return result; } /**************** Support functions for JsonPath execution *****************/ -- 2.35.3