Skip to content

Commit faa2b95

Browse files
committed
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. Extracted from a much larger patch to add SQL/JSON query functions. Author: Nikita Glukhov <n.gluhov@postgrespro.ru> Author: Teodor Sigaev <teodor@sigaev.ru> Author: Oleg Bartunov <obartunov@gmail.com> Author: Alexander Korotkov <aekorotkov@gmail.com> Author: Andrew Dunstan <andrew@dunslane.net> Author: Amit Langote <amitlangote09@gmail.com> Reviewers have included (in no particular order) Andres Freund, Alexander Korotkov, Pavel Stehule, Andrew Alsup, Erik Rijkers, Zihong Yu, Himanshu Upadhyaya, Daniel Gustafsson, Justin Pryzby, Álvaro Herrera, Jian He, Peter Eisentraut Discussion: https://postgr.es/m/cd0bb935-0158-78a7-08b5-904886deac4b@postgrespro.ru Discussion: https://postgr.es/m/20220616233130.rparivafipt6doj3@alap3.anarazel.de Discussion: https://postgr.es/m/abd9b83b-aa66-f230-3d6d-734817f0995d%40postgresql.org Discussion: https://postgr.es/m/CA+HiwqHROpf9e644D8BRqYvaAPmgBZVup-xKMDPk-nd4EpgzHw@mail.gmail.com Discussion: https://postgr.es/m/CA+HiwqE4XTdfb1nW=Ojoy_tQSRhYt-q_kb6i5d4xcKyrLC1Nbg@mail.gmail.com
1 parent 1edb3b4 commit faa2b95

File tree

1 file changed

+99
-37
lines changed

1 file changed

+99
-37
lines changed

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 99 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,19 @@ typedef struct JsonBaseObjectInfo
8787
int id;
8888
} JsonBaseObjectInfo;
8989

90+
/* Callbacks for executeJsonPath() */
91+
typedef JsonbValue *(*JsonPathGetVarCallback) (void *vars, char *varName, int varNameLen,
92+
JsonbValue *baseObject, int *baseObjectId);
93+
typedef int (*JsonPathCountVarsCallback) (void *vars);
94+
9095
/*
9196
* Context of jsonpath execution.
9297
*/
9398
typedef struct JsonPathExecContext
9499
{
95-
Jsonb *vars; /* variables to substitute into jsonpath */
100+
void *vars; /* variables to substitute into jsonpath */
101+
JsonPathGetVarCallback getVar; /* callback to extract a given variable
102+
* from 'vars' */
96103
JsonbValue *root; /* for $ evaluation */
97104
JsonbValue *current; /* for @ evaluation */
98105
JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue()
@@ -174,7 +181,9 @@ typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
174181
void *param);
175182
typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
176183

177-
static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
184+
static JsonPathExecResult executeJsonPath(JsonPath *path, void *vars,
185+
JsonPathGetVarCallback getVar,
186+
JsonPathCountVarsCallback countVars,
178187
Jsonb *json, bool throwErrors,
179188
JsonValueList *result, bool useTz);
180189
static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
@@ -226,7 +235,12 @@ static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
226235
static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
227236
JsonbValue *value);
228237
static void getJsonPathVariable(JsonPathExecContext *cxt,
229-
JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
238+
JsonPathItem *variable, JsonbValue *value);
239+
static int countVariablesFromJsonb(void *varsJsonb);
240+
static JsonbValue *getJsonPathVariableFromJsonb(void *varsJsonb, char *varName,
241+
int varNameLen,
242+
JsonbValue *baseObject,
243+
int *baseObjectId);
230244
static int JsonbArraySize(JsonbValue *jb);
231245
static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
232246
JsonbValue *rv, void *p);
@@ -284,7 +298,9 @@ jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
284298
silent = PG_GETARG_BOOL(3);
285299
}
286300

287-
res = executeJsonPath(jp, vars, jb, !silent, NULL, tz);
301+
res = executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
302+
countVariablesFromJsonb,
303+
jb, !silent, NULL, tz);
288304

289305
PG_FREE_IF_COPY(jb, 0);
290306
PG_FREE_IF_COPY(jp, 1);
@@ -339,7 +355,9 @@ jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
339355
silent = PG_GETARG_BOOL(3);
340356
}
341357

342-
(void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
358+
(void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
359+
countVariablesFromJsonb,
360+
jb, !silent, &found, tz);
343361

344362
PG_FREE_IF_COPY(jb, 0);
345363
PG_FREE_IF_COPY(jp, 1);
@@ -417,7 +435,9 @@ jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
417435
vars = PG_GETARG_JSONB_P_COPY(2);
418436
silent = PG_GETARG_BOOL(3);
419437

420-
(void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
438+
(void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
439+
countVariablesFromJsonb,
440+
jb, !silent, &found, tz);
421441

422442
funcctx->user_fctx = JsonValueListGetList(&found);
423443

@@ -464,7 +484,9 @@ jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
464484
Jsonb *vars = PG_GETARG_JSONB_P(2);
465485
bool silent = PG_GETARG_BOOL(3);
466486

467-
(void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
487+
(void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
488+
countVariablesFromJsonb,
489+
jb, !silent, &found, tz);
468490

469491
PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
470492
}
@@ -495,7 +517,9 @@ jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
495517
Jsonb *vars = PG_GETARG_JSONB_P(2);
496518
bool silent = PG_GETARG_BOOL(3);
497519

498-
(void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
520+
(void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
521+
countVariablesFromJsonb,
522+
jb, !silent, &found, tz);
499523

500524
if (JsonValueListLength(&found) >= 1)
501525
PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
@@ -522,6 +546,9 @@ jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
522546
*
523547
* 'path' - jsonpath to be executed
524548
* 'vars' - variables to be substituted to jsonpath
549+
* 'getVar' - callback used by getJsonPathVariable() to extract variables from
550+
* 'vars'
551+
* 'countVars' - callback to count the number of jsonpath variables in 'vars'
525552
* 'json' - target document for jsonpath evaluation
526553
* 'throwErrors' - whether we should throw suppressible errors
527554
* 'result' - list to store result items into
@@ -537,8 +564,10 @@ jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
537564
* In other case it tries to find all the satisfied result items.
538565
*/
539566
static JsonPathExecResult
540-
executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
541-
JsonValueList *result, bool useTz)
567+
executeJsonPath(JsonPath *path, void *vars, JsonPathGetVarCallback getVar,
568+
JsonPathCountVarsCallback countVars,
569+
Jsonb *json, bool throwErrors, JsonValueList *result,
570+
bool useTz)
542571
{
543572
JsonPathExecContext cxt;
544573
JsonPathExecResult res;
@@ -550,22 +579,16 @@ executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
550579
if (!JsonbExtractScalar(&json->root, &jbv))
551580
JsonbInitBinary(&jbv, json);
552581

553-
if (vars && !JsonContainerIsObject(&vars->root))
554-
{
555-
ereport(ERROR,
556-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
557-
errmsg("\"vars\" argument is not an object"),
558-
errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
559-
}
560-
561582
cxt.vars = vars;
583+
cxt.getVar = getVar;
562584
cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
563585
cxt.ignoreStructuralErrors = cxt.laxMode;
564586
cxt.root = &jbv;
565587
cxt.current = &jbv;
566588
cxt.baseObject.jbc = NULL;
567589
cxt.baseObject.id = 0;
568-
cxt.lastGeneratedObjectId = vars ? 2 : 1;
590+
/* 1 + number of base objects in vars */
591+
cxt.lastGeneratedObjectId = 1 + countVars(vars);
569592
cxt.innermostArraySize = -1;
570593
cxt.throwErrors = throwErrors;
571594
cxt.useTz = useTz;
@@ -2108,7 +2131,7 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
21082131
&value->val.string.len);
21092132
break;
21102133
case jpiVariable:
2111-
getJsonPathVariable(cxt, item, cxt->vars, value);
2134+
getJsonPathVariable(cxt, item, value);
21122135
return;
21132136
default:
21142137
elog(ERROR, "unexpected jsonpath item type");
@@ -2120,42 +2143,81 @@ getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
21202143
*/
21212144
static void
21222145
getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
2123-
Jsonb *vars, JsonbValue *value)
2146+
JsonbValue *value)
21242147
{
21252148
char *varName;
21262149
int varNameLength;
2127-
JsonbValue tmp;
2150+
JsonbValue baseObject;
2151+
int baseObjectId;
21282152
JsonbValue *v;
21292153

2130-
if (!vars)
2154+
Assert(variable->type == jpiVariable);
2155+
varName = jspGetString(variable, &varNameLength);
2156+
2157+
if (cxt->vars == NULL ||
2158+
(v = cxt->getVar(cxt->vars, varName, varNameLength,
2159+
&baseObject, &baseObjectId)) == NULL)
2160+
ereport(ERROR,
2161+
(errcode(ERRCODE_UNDEFINED_OBJECT),
2162+
errmsg("could not find jsonpath variable \"%s\"",
2163+
pnstrdup(varName, varNameLength))));
2164+
2165+
if (baseObjectId > 0)
21312166
{
2132-
value->type = jbvNull;
2133-
return;
2167+
*value = *v;
2168+
setBaseObject(cxt, &baseObject, baseObjectId);
21342169
}
2170+
}
2171+
2172+
/*
2173+
* Definition of JsonPathGetVarCallback for when JsonPathExecContext.vars
2174+
* is specified as a jsonb value.
2175+
*/
2176+
static JsonbValue *
2177+
getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, int varNameLength,
2178+
JsonbValue *baseObject, int *baseObjectId)
2179+
{
2180+
Jsonb *vars = varsJsonb;
2181+
JsonbValue tmp;
2182+
JsonbValue *result;
21352183

2136-
Assert(variable->type == jpiVariable);
2137-
varName = jspGetString(variable, &varNameLength);
21382184
tmp.type = jbvString;
21392185
tmp.val.string.val = varName;
21402186
tmp.val.string.len = varNameLength;
21412187

2142-
v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
2188+
result = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
21432189

2144-
if (v)
2190+
if (result == NULL)
21452191
{
2146-
*value = *v;
2147-
pfree(v);
2192+
*baseObjectId = -1;
2193+
return NULL;
21482194
}
2149-
else
2195+
2196+
*baseObjectId = 1;
2197+
JsonbInitBinary(baseObject, vars);
2198+
2199+
return result;
2200+
}
2201+
2202+
/*
2203+
* Definition of JsonPathCountVarsCallback for when JsonPathExecContext.vars
2204+
* is specified as a jsonb value.
2205+
*/
2206+
static int
2207+
countVariablesFromJsonb(void *varsJsonb)
2208+
{
2209+
Jsonb *vars = varsJsonb;
2210+
2211+
if (vars && !JsonContainerIsObject(&vars->root))
21502212
{
21512213
ereport(ERROR,
2152-
(errcode(ERRCODE_UNDEFINED_OBJECT),
2153-
errmsg("could not find jsonpath variable \"%s\"",
2154-
pnstrdup(varName, varNameLength))));
2214+
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2215+
errmsg("\"vars\" argument is not an object"),
2216+
errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object."));
21552217
}
21562218

2157-
JsonbInitBinary(&tmp, vars);
2158-
setBaseObject(cxt, &tmp, 1);
2219+
/* count of base objects */
2220+
return vars != NULL ? 1 : 0;
21592221
}
21602222

21612223
/**************** Support functions for JsonPath execution *****************/

0 commit comments

Comments
 (0)