1 /*-------------------------------------------------------------------------
4 * Routines for SQL/JSON path execution.
6 * Jsonpath is executed in the global context stored in JsonPathExecContext,
7 * which is passed to almost every function involved into execution. Entry
8 * point for jsonpath execution is executeJsonPath() function, which
9 * initializes execution context including initial JsonPathItem and JsonbValue,
10 * flags, stack for calculation of @ in filters.
12 * The result of jsonpath query execution is enum JsonPathExecResult and
13 * if succeeded sequence of JsonbValue, written to JsonValueList *found, which
14 * is passed through the jsonpath items. When found == NULL, we're inside
15 * exists-query and we're interested only in whether result is empty. In this
16 * case execution is stopped once first result item is found, and the only
17 * execution result is JsonPathExecResult. The values of JsonPathExecResult
19 * - jperOk -- result sequence is not empty
20 * - jperNotFound -- result sequence is empty
21 * - jperError -- error occurred during execution
23 * Jsonpath is executed recursively (see executeItem()) starting form the
24 * first path item (which in turn might be, for instance, an arithmetic
25 * expression evaluated separately). On each step single JsonbValue obtained
26 * from previous path item is processed. The result of processing is a
27 * sequence of JsonbValue (probably empty), which is passed to the next path
28 * item one by one. When there is no next path item, then JsonbValue is added
29 * to the 'found' list. When found == NULL, then execution functions just
30 * return jperOk (see executeNextItem()).
32 * Many of jsonpath operations require automatic unwrapping of arrays in lax
33 * mode. So, if input value is array, then corresponding operation is
34 * processed not on array itself, but on all of its members one by one.
35 * executeItemOptUnwrapTarget() function have 'unwrap' argument, which indicates
36 * whether unwrapping of array is needed. When unwrap == true, each of array
37 * members is passed to executeItemOptUnwrapTarget() again but with unwrap == false
38 * in order to evade subsequent array unwrapping.
40 * All boolean expressions (predicates) are evaluated by executeBoolItem()
41 * function, which returns tri-state JsonPathBool. When error is occurred
42 * during predicate execution, it returns jpbUnknown. According to standard
43 * predicates can be only inside filters. But we support their usage as
44 * jsonpath expression. This helps us to implement @@ operator. In this case
45 * resulting JsonPathBool is transformed into jsonb bool or null.
47 * Arithmetic and boolean expression are evaluated recursively from expression
48 * tree top down to the leaves. Therefore, for binary arithmetic expressions
49 * we calculate operands first. Then we check that results are numeric
50 * singleton lists, calculate the result and pass it to the next path item.
52 * Copyright (c) 2019, PostgreSQL Global Development Group
55 * src/backend/utils/adt/jsonpath_exec.c
57 *-------------------------------------------------------------------------
62 #include "catalog/pg_collation.h"
63 #include "catalog/pg_type.h"
65 #include "lib/stringinfo.h"
66 #include "miscadmin.h"
67 #include "regex/regex.h"
68 #include "utils/builtins.h"
69 #include "utils/datum.h"
70 #include "utils/formatting.h"
71 #include "utils/float.h"
72 #include "utils/guc.h"
73 #include "utils/json.h"
74 #include "utils/jsonpath.h"
75 #include "utils/date.h"
76 #include "utils/timestamp.h"
77 #include "utils/varlena.h"
80 /* Standard error message for SQL/JSON errors */
81 #define ERRMSG_JSON_ARRAY_NOT_FOUND "SQL/JSON array not found"
82 #define ERRMSG_JSON_OBJECT_NOT_FOUND "SQL/JSON object not found"
83 #define ERRMSG_JSON_MEMBER_NOT_FOUND "SQL/JSON member not found"
84 #define ERRMSG_JSON_NUMBER_NOT_FOUND "SQL/JSON number not found"
85 #define ERRMSG_JSON_SCALAR_REQUIRED "SQL/JSON scalar required"
86 #define ERRMSG_SINGLETON_JSON_ITEM_REQUIRED "singleton SQL/JSON item required"
87 #define ERRMSG_NON_NUMERIC_JSON_ITEM "non-numeric SQL/JSON item"
88 #define ERRMSG_INVALID_JSON_SUBSCRIPT "invalid SQL/JSON subscript"
91 * Represents "base object" and it's "id" for .keyvalue() evaluation.
93 typedef struct JsonBaseObjectInfo
100 * Context of jsonpath execution.
102 typedef struct JsonPathExecContext
104 Jsonb *vars; /* variables to substitute into jsonpath */
105 JsonbValue *root; /* for $ evaluation */
106 JsonbValue *current; /* for @ evaluation */
107 JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue()
109 int lastGeneratedObjectId; /* "id" counter for .keyvalue()
111 int innermostArraySize; /* for LAST array index evaluation */
112 bool laxMode; /* true for "lax" mode, false for "strict"
114 bool ignoreStructuralErrors; /* with "true" structural errors such
115 * as absence of required json item or
116 * unexpected json item type are
118 bool throwErrors; /* with "false" all suppressible errors are
120 } JsonPathExecContext;
122 /* Context for LIKE_REGEX execution. */
123 typedef struct JsonLikeRegexContext
127 } JsonLikeRegexContext;
129 /* Result of jsonpath predicate evaluation */
130 typedef enum JsonPathBool
137 /* Result of jsonpath expression evaluation */
138 typedef enum JsonPathExecResult
143 } JsonPathExecResult;
145 #define jperIsError(jper) ((jper) == jperError)
148 * List of jsonb values with shortcut for single-value list.
150 typedef struct JsonValueList
152 JsonbValue *singleton;
156 typedef struct JsonValueListIterator
160 } JsonValueListIterator;
162 /* strict/lax flags is decomposed into four [un]wrap/error flags */
163 #define jspStrictAbsenseOfErrors(cxt) (!(cxt)->laxMode)
164 #define jspAutoUnwrap(cxt) ((cxt)->laxMode)
165 #define jspAutoWrap(cxt) ((cxt)->laxMode)
166 #define jspIgnoreStructuralErrors(cxt) ((cxt)->ignoreStructuralErrors)
167 #define jspThrowErrors(cxt) ((cxt)->throwErrors)
169 /* Convenience macro: return or throw error depending on context */
170 #define RETURN_ERROR(throw_error) \
172 if (jspThrowErrors(cxt)) \
178 typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
182 typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
184 static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
185 Jsonb *json, bool throwErrors, JsonValueList *result);
186 static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
187 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
188 static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
189 JsonPathItem *jsp, JsonbValue *jb,
190 JsonValueList *found, bool unwrap);
191 static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
192 JsonPathItem *jsp, JsonbValue *jb,
193 JsonValueList *found, bool unwrapElements);
194 static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
195 JsonPathItem *cur, JsonPathItem *next,
196 JsonbValue *v, JsonValueList *found, bool copy);
197 static JsonPathExecResult executeItemOptUnwrapResult(
198 JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
199 bool unwrap, JsonValueList *found);
200 static JsonPathExecResult executeItemOptUnwrapResultNoThrow(
201 JsonPathExecContext *cxt, JsonPathItem *jsp,
202 JsonbValue *jb, bool unwrap, JsonValueList *found);
203 static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
204 JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
205 static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
206 JsonPathItem *jsp, JsonbValue *jb);
207 static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
208 JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
209 uint32 level, uint32 first, uint32 last,
210 bool ignoreStructuralErrors, bool unwrapNext);
211 static JsonPathBool executePredicate(JsonPathExecContext *cxt,
212 JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
213 JsonbValue *jb, bool unwrapRightArg,
214 JsonPathPredicateCallback exec, void *param);
215 static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
216 JsonPathItem *jsp, JsonbValue *jb,
217 BinaryArithmFunc func, JsonValueList *found);
218 static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
219 JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
220 JsonValueList *found);
221 static JsonPathBool executeStartsWith(JsonPathItem *jsp,
222 JsonbValue *whole, JsonbValue *initial, void *param);
223 static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
224 JsonbValue *rarg, void *param);
225 static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
226 JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
227 JsonValueList *found);
228 static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
229 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
230 static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
231 JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
232 static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
234 static void getJsonPathVariable(JsonPathExecContext *cxt,
235 JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
236 static int JsonbArraySize(JsonbValue *jb);
237 static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
238 JsonbValue *rv, void *p);
239 static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2);
240 static int compareNumeric(Numeric a, Numeric b);
241 static JsonbValue *copyJsonbValue(JsonbValue *src);
242 static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
243 JsonPathItem *jsp, JsonbValue *jb, int32 *index);
244 static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
245 JsonbValue *jbv, int32 id);
246 static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
247 static int JsonValueListLength(const JsonValueList *jvl);
248 static bool JsonValueListIsEmpty(JsonValueList *jvl);
249 static JsonbValue *JsonValueListHead(JsonValueList *jvl);
250 static List *JsonValueListGetList(JsonValueList *jvl);
251 static void JsonValueListInitIterator(const JsonValueList *jvl,
252 JsonValueListIterator *it);
253 static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
254 JsonValueListIterator *it);
255 static int JsonbType(JsonbValue *jb);
256 static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
257 static int JsonbType(JsonbValue *jb);
258 static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
259 static JsonbValue *wrapItemsInArray(const JsonValueList *items);
261 /****************** User interface to JsonPath executor ********************/
265 * Returns true if jsonpath returns at least one item for the specified
266 * jsonb value. This function and jsonb_path_match() are used to
267 * implement @? and @@ operators, which in turn are intended to have an
268 * index support. Thus, it's desirable to make it easier to achieve
269 * consistency between index scan results and sequential scan results.
270 * So, we throw as less errors as possible. Regarding this function,
271 * such behavior also matches behavior of JSON_EXISTS() clause of
272 * SQL/JSON. Regarding jsonb_path_match(), this function doesn't have
273 * an analogy in SQL/JSON, so we define its behavior on our own.
276 jsonb_path_exists(PG_FUNCTION_ARGS)
278 Jsonb *jb = PG_GETARG_JSONB_P(0);
279 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
280 JsonPathExecResult res;
286 vars = PG_GETARG_JSONB_P(2);
287 silent = PG_GETARG_BOOL(3);
290 res = executeJsonPath(jp, vars, jb, !silent, NULL);
292 PG_FREE_IF_COPY(jb, 0);
293 PG_FREE_IF_COPY(jp, 1);
295 if (jperIsError(res))
298 PG_RETURN_BOOL(res == jperOk);
302 * jsonb_path_exists_opr
303 * Implementation of operator "jsonb @? jsonpath" (2-argument version of
304 * jsonb_path_exists()).
307 jsonb_path_exists_opr(PG_FUNCTION_ARGS)
309 /* just call the other one -- it can handle both cases */
310 return jsonb_path_exists(fcinfo);
315 * Returns jsonpath predicate result item for the specified jsonb value.
316 * See jsonb_path_exists() comment for details regarding error handling.
319 jsonb_path_match(PG_FUNCTION_ARGS)
321 Jsonb *jb = PG_GETARG_JSONB_P(0);
322 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
324 JsonValueList found = {0};
330 vars = PG_GETARG_JSONB_P(2);
331 silent = PG_GETARG_BOOL(3);
334 (void) executeJsonPath(jp, vars, jb, !silent, &found);
336 if (JsonValueListLength(&found) < 1)
339 jbv = JsonValueListHead(&found);
341 PG_FREE_IF_COPY(jb, 0);
342 PG_FREE_IF_COPY(jp, 1);
344 if (jbv->type != jbvBool)
347 PG_RETURN_BOOL(jbv->val.boolean);
351 * jsonb_path_match_opr
352 * Implementation of operator "jsonb @@ jsonpath" (2-argument version of
353 * jsonb_path_match()).
356 jsonb_path_match_opr(PG_FUNCTION_ARGS)
358 /* just call the other one -- it can handle both cases */
359 return jsonb_path_match(fcinfo);
364 * Executes jsonpath for given jsonb document and returns result as
368 jsonb_path_query(PG_FUNCTION_ARGS)
370 FuncCallContext *funcctx;
375 if (SRF_IS_FIRSTCALL())
379 MemoryContext oldcontext;
382 JsonValueList found = {0};
384 funcctx = SRF_FIRSTCALL_INIT();
385 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
387 jb = PG_GETARG_JSONB_P_COPY(0);
388 jp = PG_GETARG_JSONPATH_P_COPY(1);
389 vars = PG_GETARG_JSONB_P_COPY(2);
390 silent = PG_GETARG_BOOL(3);
392 (void) executeJsonPath(jp, vars, jb, !silent, &found);
394 funcctx->user_fctx = JsonValueListGetList(&found);
396 MemoryContextSwitchTo(oldcontext);
399 funcctx = SRF_PERCALL_SETUP();
400 found = funcctx->user_fctx;
402 c = list_head(found);
405 SRF_RETURN_DONE(funcctx);
408 funcctx->user_fctx = list_delete_first(found);
410 SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
414 * jsonb_path_query_array
415 * Executes jsonpath for given jsonb document and returns result as
419 jsonb_path_query_array(FunctionCallInfo fcinfo)
421 Jsonb *jb = PG_GETARG_JSONB_P(0);
422 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
423 JsonValueList found = {0};
424 Jsonb *vars = PG_GETARG_JSONB_P(2);
425 bool silent = PG_GETARG_BOOL(3);
427 (void) executeJsonPath(jp, vars, jb, !silent, &found);
429 PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
433 * jsonb_path_query_first
434 * Executes jsonpath for given jsonb document and returns first result
435 * item. If there are no items, NULL returned.
438 jsonb_path_query_first(FunctionCallInfo fcinfo)
440 Jsonb *jb = PG_GETARG_JSONB_P(0);
441 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
442 JsonValueList found = {0};
443 Jsonb *vars = PG_GETARG_JSONB_P(2);
444 bool silent = PG_GETARG_BOOL(3);
446 (void) executeJsonPath(jp, vars, jb, !silent, &found);
448 if (JsonValueListLength(&found) >= 1)
449 PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
454 /********************Execute functions for JsonPath**************************/
457 * Interface to jsonpath executor
459 * 'path' - jsonpath to be executed
460 * 'vars' - variables to be substituted to jsonpath
461 * 'json' - target document for jsonpath evaluation
462 * 'throwErrors' - whether we should throw suppressible errors
463 * 'result' - list to store result items into
465 * Returns an error happens during processing or NULL on no error.
467 * Note, jsonb and jsonpath values should be avaliable and untoasted during
468 * work because JsonPathItem, JsonbValue and result item could have pointers
469 * into input values. If caller needs to just check if document matches
470 * jsonpath, then it doesn't provide a result arg. In this case executor
471 * works till first positive result and does not check the rest if possible.
472 * In other case it tries to find all the satisfied result items.
474 static JsonPathExecResult
475 executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
476 JsonValueList *result)
478 JsonPathExecContext cxt;
479 JsonPathExecResult res;
485 if (!JsonbExtractScalar(&json->root, &jbv))
486 JsonbInitBinary(&jbv, json);
488 if (vars && !JsonContainerIsObject(&vars->root))
491 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
492 errmsg("jsonb containing jsonpath variables "
493 "is not an object")));
497 cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
498 cxt.ignoreStructuralErrors = cxt.laxMode;
501 cxt.baseObject.jbc = NULL;
502 cxt.baseObject.id = 0;
503 cxt.lastGeneratedObjectId = vars ? 2 : 1;
504 cxt.innermostArraySize = -1;
505 cxt.throwErrors = throwErrors;
507 if (jspStrictAbsenseOfErrors(&cxt) && !result)
510 * In strict mode we must get a complete list of values to check that
511 * there are no errors at all.
513 JsonValueList vals = {0};
515 res = executeItem(&cxt, &jsp, &jbv, &vals);
517 if (jperIsError(res))
520 return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
523 res = executeItem(&cxt, &jsp, &jbv, result);
525 Assert(!throwErrors || !jperIsError(res));
531 * Execute jsonpath with automatic unwrapping of current item in lax mode.
533 static JsonPathExecResult
534 executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
535 JsonbValue *jb, JsonValueList *found)
537 return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
541 * Main jsonpath executor function: walks on jsonpath structure, finds
542 * relevant parts of jsonb and evaluates expressions over them.
543 * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
545 static JsonPathExecResult
546 executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
547 JsonbValue *jb, JsonValueList *found, bool unwrap)
550 JsonPathExecResult res = jperNotFound;
551 JsonBaseObjectInfo baseObject;
554 CHECK_FOR_INTERRUPTS();
558 /* all boolean item types: */
568 case jpiGreaterOrEqual:
573 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
575 res = appendBoolResult(cxt, jsp, found, st);
580 if (JsonbType(jb) == jbvObject)
585 key.type = jbvString;
586 key.val.string.val = jspGetString(jsp, &key.val.string.len);
588 v = findJsonbValueFromContainer(jb->val.binary.data,
593 res = executeNextItem(cxt, jsp, NULL,
596 /* free value if it was not added to found list */
597 if (jspHasNext(jsp) || !found)
600 else if (!jspIgnoreStructuralErrors(cxt))
602 StringInfoData keybuf;
607 if (!jspThrowErrors(cxt))
610 initStringInfo(&keybuf);
612 keystr = pnstrdup(key.val.string.val, key.val.string.len);
613 escape_json(&keybuf, keystr);
616 (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND), \
617 errmsg(ERRMSG_JSON_MEMBER_NOT_FOUND),
618 errdetail("JSON object does not contain key %s",
622 else if (unwrap && JsonbType(jb) == jbvArray)
623 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
624 else if (!jspIgnoreStructuralErrors(cxt))
627 RETURN_ERROR(ereport(ERROR,
628 (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND),
629 errmsg(ERRMSG_JSON_MEMBER_NOT_FOUND),
630 errdetail("jsonpath member accessor can "
631 "only be applied to an object"))));
637 baseObject = setBaseObject(cxt, jb, 0);
638 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
639 cxt->baseObject = baseObject;
643 res = executeNextItem(cxt, jsp, NULL, cxt->current,
648 if (JsonbType(jb) == jbvArray)
650 bool hasNext = jspGetNext(jsp, &elem);
652 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
653 jb, found, jspAutoUnwrap(cxt));
655 else if (jspAutoWrap(cxt))
656 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
657 else if (!jspIgnoreStructuralErrors(cxt))
658 RETURN_ERROR(ereport(ERROR,
659 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
660 errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
661 errdetail("jsonpath wildcard array accessor "
662 "can only be applied to an array"))));
666 if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
668 int innermostArraySize = cxt->innermostArraySize;
670 int size = JsonbArraySize(jb);
671 bool singleton = size < 0;
672 bool hasNext = jspGetNext(jsp, &elem);
677 cxt->innermostArraySize = size; /* for LAST evaluation */
679 for (i = 0; i < jsp->content.array.nelems; i++)
686 bool range = jspGetArraySubscript(jsp, &from,
689 res = getArrayIndex(cxt, &from, jb, &index_from);
691 if (jperIsError(res))
696 res = getArrayIndex(cxt, &to, jb, &index_to);
698 if (jperIsError(res))
702 index_to = index_from;
704 if (!jspIgnoreStructuralErrors(cxt) &&
706 index_from > index_to ||
708 RETURN_ERROR(ereport(ERROR,
709 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
710 errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
711 errdetail("jsonpath array subscript is "
717 if (index_to >= size)
722 for (index = index_from; index <= index_to; index++)
734 v = getIthJsonbValueFromContainer(jb->val.binary.data,
743 if (!hasNext && !found)
746 res = executeNextItem(cxt, jsp, &elem, v, found,
749 if (jperIsError(res))
752 if (res == jperOk && !found)
756 if (jperIsError(res))
759 if (res == jperOk && !found)
763 cxt->innermostArraySize = innermostArraySize;
765 else if (!jspIgnoreStructuralErrors(cxt))
767 RETURN_ERROR(ereport(ERROR,
768 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
769 errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
770 errdetail("jsonpath array accessor can "
771 "only be applied to an array"))));
780 bool hasNext = jspGetNext(jsp, &elem);
782 if (cxt->innermostArraySize < 0)
783 elog(ERROR, "evaluating jsonpath LAST outside of "
786 if (!hasNext && !found)
792 last = cxt->innermostArraySize - 1;
794 lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
796 lastjbv->type = jbvNumeric;
797 lastjbv->val.numeric =
798 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
799 Int32GetDatum(last)));
801 res = executeNextItem(cxt, jsp, &elem,
802 lastjbv, found, hasNext);
807 if (JsonbType(jb) == jbvObject)
809 bool hasNext = jspGetNext(jsp, &elem);
811 if (jb->type != jbvBinary)
812 elog(ERROR, "invalid jsonb object type: %d", jb->type);
814 return executeAnyItem
815 (cxt, hasNext ? &elem : NULL,
816 jb->val.binary.data, found, 1, 1, 1,
817 false, jspAutoUnwrap(cxt));
819 else if (unwrap && JsonbType(jb) == jbvArray)
820 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
821 else if (!jspIgnoreStructuralErrors(cxt))
824 RETURN_ERROR(ereport(ERROR,
825 (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
826 errmsg(ERRMSG_JSON_OBJECT_NOT_FOUND),
827 errdetail("jsonpath wildcard member accessor "
828 "can only be applied to an object"))));
833 return executeBinaryArithmExpr(cxt, jsp, jb,
834 numeric_add_opt_error, found);
837 return executeBinaryArithmExpr(cxt, jsp, jb,
838 numeric_sub_opt_error, found);
841 return executeBinaryArithmExpr(cxt, jsp, jb,
842 numeric_mul_opt_error, found);
845 return executeBinaryArithmExpr(cxt, jsp, jb,
846 numeric_div_opt_error, found);
849 return executeBinaryArithmExpr(cxt, jsp, jb,
850 numeric_mod_opt_error, found);
853 return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
856 return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
863 if (unwrap && JsonbType(jb) == jbvArray)
864 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
867 jspGetArg(jsp, &elem);
868 st = executeNestedBoolItem(cxt, &elem, jb);
872 res = executeNextItem(cxt, jsp, NULL,
879 bool hasNext = jspGetNext(jsp, &elem);
881 /* first try without any intermediate steps */
882 if (jsp->content.anybounds.first == 0)
884 bool savedIgnoreStructuralErrors;
886 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
887 cxt->ignoreStructuralErrors = true;
888 res = executeNextItem(cxt, jsp, &elem,
890 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
892 if (res == jperOk && !found)
896 if (jb->type == jbvBinary)
898 (cxt, hasNext ? &elem : NULL,
899 jb->val.binary.data, found,
901 jsp->content.anybounds.first,
902 jsp->content.anybounds.last,
903 true, jspAutoUnwrap(cxt));
915 bool hasNext = jspGetNext(jsp, &elem);
917 if (!hasNext && !found)
919 res = jperOk; /* skip evaluation */
923 v = hasNext ? &vbuf : palloc(sizeof(*v));
925 baseObject = cxt->baseObject;
926 getJsonPathItem(cxt, jsp, v);
928 res = executeNextItem(cxt, jsp, &elem,
930 cxt->baseObject = baseObject;
936 JsonbValue *jbv = palloc(sizeof(*jbv));
938 jbv->type = jbvString;
939 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
940 jbv->val.string.len = strlen(jbv->val.string.val);
942 res = executeNextItem(cxt, jsp, NULL, jbv,
949 int size = JsonbArraySize(jb);
953 if (!jspAutoWrap(cxt))
955 if (!jspIgnoreStructuralErrors(cxt))
956 RETURN_ERROR(ereport(ERROR,
957 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
958 errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
959 errdetail("jsonpath item method .%s() "
960 "can only be applied to an array",
961 jspOperationName(jsp->type)))));
968 jb = palloc(sizeof(*jb));
970 jb->type = jbvNumeric;
972 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
973 Int32GetDatum(size)));
975 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
980 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
984 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
988 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
995 if (unwrap && JsonbType(jb) == jbvArray)
996 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
999 if (jb->type == jbvNumeric)
1001 char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1002 NumericGetDatum(jb->val.numeric)));
1003 bool have_error = false;
1005 (void) float8in_internal_opt_error(tmp,
1012 RETURN_ERROR(ereport(ERROR,
1013 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1014 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1015 errdetail("jsonpath item method .%s() "
1016 "can only be applied to "
1018 jspOperationName(jsp->type)))));
1021 else if (jb->type == jbvString)
1023 /* cast string as double */
1025 char *tmp = pnstrdup(jb->val.string.val,
1026 jb->val.string.len);
1027 bool have_error = false;
1029 val = float8in_internal_opt_error(tmp,
1035 if (have_error || isinf(val))
1036 RETURN_ERROR(ereport(ERROR,
1037 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1038 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1039 errdetail("jsonpath item method .%s() can "
1040 "only be applied to a numeric value",
1041 jspOperationName(jsp->type)))));
1044 jb->type = jbvNumeric;
1045 jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1046 Float8GetDatum(val)));
1050 if (res == jperNotFound)
1051 RETURN_ERROR(ereport(ERROR,
1052 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1053 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1054 errdetail("jsonpath item method .%s() "
1055 "can only be applied to a "
1056 "string or numeric value",
1057 jspOperationName(jsp->type)))));
1059 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1064 if (unwrap && JsonbType(jb) == jbvArray)
1065 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1067 return executeKeyValueMethod(cxt, jsp, jb, found);
1070 elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1077 * Unwrap current array item and execute jsonpath for each of its elements.
1079 static JsonPathExecResult
1080 executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1081 JsonbValue *jb, JsonValueList *found,
1082 bool unwrapElements)
1084 if (jb->type != jbvBinary)
1086 Assert(jb->type != jbvArray);
1087 elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1090 return executeAnyItem
1091 (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1092 false, unwrapElements);
1096 * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1099 static JsonPathExecResult
1100 executeNextItem(JsonPathExecContext *cxt,
1101 JsonPathItem *cur, JsonPathItem *next,
1102 JsonbValue *v, JsonValueList *found, bool copy)
1108 hasNext = next != NULL;
1110 hasNext = jspHasNext(cur);
1114 hasNext = jspGetNext(cur, next);
1118 return executeItem(cxt, next, v, found);
1121 JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1127 * Same as executeItem(), but when "unwrap == true" automatically unwraps
1128 * each array item from the resulting sequence in lax mode.
1130 static JsonPathExecResult
1131 executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1132 JsonbValue *jb, bool unwrap,
1133 JsonValueList *found)
1135 if (unwrap && jspAutoUnwrap(cxt))
1137 JsonValueList seq = {0};
1138 JsonValueListIterator it;
1139 JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1142 if (jperIsError(res))
1145 JsonValueListInitIterator(&seq, &it);
1146 while ((item = JsonValueListNext(&seq, &it)))
1148 Assert(item->type != jbvArray);
1150 if (JsonbType(item) == jbvArray)
1151 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1153 JsonValueListAppend(found, item);
1159 return executeItem(cxt, jsp, jb, found);
1163 * Same as executeItemOptUnwrapResult(), but with error suppression.
1165 static JsonPathExecResult
1166 executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1168 JsonbValue *jb, bool unwrap,
1169 JsonValueList *found)
1171 JsonPathExecResult res;
1172 bool throwErrors = cxt->throwErrors;
1174 cxt->throwErrors = false;
1175 res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1176 cxt->throwErrors = throwErrors;
1181 /* Execute boolean-valued jsonpath expression. */
1183 executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1184 JsonbValue *jb, bool canHaveNext)
1191 if (!canHaveNext && jspHasNext(jsp))
1192 elog(ERROR, "boolean jsonpath item cannot have next item");
1197 jspGetLeftArg(jsp, &larg);
1198 res = executeBoolItem(cxt, &larg, jb, false);
1200 if (res == jpbFalse)
1204 * SQL/JSON says that we should check second arg in case of
1208 jspGetRightArg(jsp, &rarg);
1209 res2 = executeBoolItem(cxt, &rarg, jb, false);
1211 return res2 == jpbTrue ? res : res2;
1214 jspGetLeftArg(jsp, &larg);
1215 res = executeBoolItem(cxt, &larg, jb, false);
1220 jspGetRightArg(jsp, &rarg);
1221 res2 = executeBoolItem(cxt, &rarg, jb, false);
1223 return res2 == jpbFalse ? res : res2;
1226 jspGetArg(jsp, &larg);
1228 res = executeBoolItem(cxt, &larg, jb, false);
1230 if (res == jpbUnknown)
1233 return res == jpbTrue ? jpbFalse : jpbTrue;
1236 jspGetArg(jsp, &larg);
1237 res = executeBoolItem(cxt, &larg, jb, false);
1238 return res == jpbUnknown ? jpbTrue : jpbFalse;
1244 case jpiLessOrEqual:
1245 case jpiGreaterOrEqual:
1246 jspGetLeftArg(jsp, &larg);
1247 jspGetRightArg(jsp, &rarg);
1248 return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1249 executeComparison, NULL);
1251 case jpiStartsWith: /* 'whole STARTS WITH initial' */
1252 jspGetLeftArg(jsp, &larg); /* 'whole' */
1253 jspGetRightArg(jsp, &rarg); /* 'initial' */
1254 return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1255 executeStartsWith, NULL);
1257 case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1260 * 'expr' is a sequence-returning expression. 'pattern' is a
1261 * regex string literal. SQL/JSON standard requires XQuery
1262 * regexes, but we use Postgres regexes here. 'flags' is a
1263 * string literal converted to integer flags at compile-time.
1265 JsonLikeRegexContext lrcxt = {0};
1267 jspInitByBuffer(&larg, jsp->base,
1268 jsp->content.like_regex.expr);
1270 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1271 executeLikeRegex, &lrcxt);
1275 jspGetArg(jsp, &larg);
1277 if (jspStrictAbsenseOfErrors(cxt))
1280 * In strict mode we must get a complete list of values to
1281 * check that there are no errors at all.
1283 JsonValueList vals = {0};
1284 JsonPathExecResult res =
1285 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1288 if (jperIsError(res))
1291 return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1295 JsonPathExecResult res =
1296 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1299 if (jperIsError(res))
1302 return res == jperOk ? jpbTrue : jpbFalse;
1306 elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1312 * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1313 * item onto the stack.
1316 executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1322 prev = cxt->current;
1324 res = executeBoolItem(cxt, jsp, jb, false);
1325 cxt->current = prev;
1331 * Implementation of several jsonpath nodes:
1332 * - jpiAny (.** accessor),
1333 * - jpiAnyKey (.* accessor),
1334 * - jpiAnyArray ([*] accessor)
1336 static JsonPathExecResult
1337 executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1338 JsonValueList *found, uint32 level, uint32 first, uint32 last,
1339 bool ignoreStructuralErrors, bool unwrapNext)
1341 JsonPathExecResult res = jperNotFound;
1346 check_stack_depth();
1351 it = JsonbIteratorInit(jbc);
1354 * Recursively iterate over jsonb objects/arrays
1356 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1360 r = JsonbIteratorNext(&it, &v, true);
1361 Assert(r == WJB_VALUE);
1364 if (r == WJB_VALUE || r == WJB_ELEM)
1367 if (level >= first ||
1368 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1369 v.type != jbvBinary)) /* leaves only requested */
1371 /* check expression */
1374 if (ignoreStructuralErrors)
1376 bool savedIgnoreStructuralErrors;
1378 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1379 cxt->ignoreStructuralErrors = true;
1380 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1381 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1384 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1386 if (jperIsError(res))
1389 if (res == jperOk && !found)
1393 JsonValueListAppend(found, copyJsonbValue(&v));
1398 if (level < last && v.type == jbvBinary)
1400 res = executeAnyItem
1401 (cxt, jsp, v.val.binary.data, found,
1402 level + 1, first, last,
1403 ignoreStructuralErrors, unwrapNext);
1405 if (jperIsError(res))
1408 if (res == jperOk && found == NULL)
1418 * Execute unary or binary predicate.
1420 * Predicates have existence semantics, because their operands are item
1421 * sequences. Pairs of items from the left and right operand's sequences are
1422 * checked. TRUE returned only if any pair satisfying the condition is found.
1423 * In strict mode, even if the desired pair has already been found, all pairs
1424 * still need to be examined to check the absence of errors. If any error
1425 * occurs, UNKNOWN (analogous to SQL NULL) is returned.
1428 executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
1429 JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
1430 bool unwrapRightArg, JsonPathPredicateCallback exec,
1433 JsonPathExecResult res;
1434 JsonValueListIterator lseqit;
1435 JsonValueList lseq = {0};
1436 JsonValueList rseq = {0};
1441 /* Left argument is always auto-unwrapped. */
1442 res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
1443 if (jperIsError(res))
1448 /* Right argument is conditionally auto-unwrapped. */
1449 res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
1450 unwrapRightArg, &rseq);
1451 if (jperIsError(res))
1455 JsonValueListInitIterator(&lseq, &lseqit);
1456 while ((lval = JsonValueListNext(&lseq, &lseqit)))
1458 JsonValueListIterator rseqit;
1462 JsonValueListInitIterator(&rseq, &rseqit);
1464 rval = JsonValueListNext(&rseq, &rseqit);
1468 /* Loop over right arg sequence or do single pass otherwise */
1469 while (rarg ? (rval != NULL) : first)
1471 JsonPathBool res = exec(pred, lval, rval, param);
1473 if (res == jpbUnknown)
1475 if (jspStrictAbsenseOfErrors(cxt))
1480 else if (res == jpbTrue)
1482 if (!jspStrictAbsenseOfErrors(cxt))
1490 rval = JsonValueListNext(&rseq, &rseqit);
1494 if (found) /* possible only in strict mode */
1497 if (error) /* possible only in lax mode */
1504 * Execute binary arithmetic expression on singleton numeric operands.
1505 * Array operands are automatically unwrapped in lax mode.
1507 static JsonPathExecResult
1508 executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1509 JsonbValue *jb, BinaryArithmFunc func,
1510 JsonValueList *found)
1512 JsonPathExecResult jper;
1514 JsonValueList lseq = {0};
1515 JsonValueList rseq = {0};
1520 jspGetLeftArg(jsp, &elem);
1523 * XXX: By standard only operands of multiplicative expressions are
1524 * unwrapped. We extend it to other binary arithmetics expressions too.
1526 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
1527 if (jperIsError(jper))
1530 jspGetRightArg(jsp, &elem);
1532 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
1533 if (jperIsError(jper))
1536 if (JsonValueListLength(&lseq) != 1 ||
1537 !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
1538 RETURN_ERROR(ereport(ERROR,
1539 (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
1540 errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
1541 errdetail("left operand of binary jsonpath operator %s "
1542 "is not a singleton numeric value",
1543 jspOperationName(jsp->type)))));
1545 if (JsonValueListLength(&rseq) != 1 ||
1546 !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
1547 RETURN_ERROR(ereport(ERROR,
1548 (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
1549 errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
1550 errdetail("right operand of binary jsonpath operator %s "
1551 "is not a singleton numeric value",
1552 jspOperationName(jsp->type)))));
1554 if (jspThrowErrors(cxt))
1556 res = func(lval->val.numeric, rval->val.numeric, NULL);
1562 res = func(lval->val.numeric, rval->val.numeric, &error);
1568 if (!jspGetNext(jsp, &elem) && !found)
1571 lval = palloc(sizeof(*lval));
1572 lval->type = jbvNumeric;
1573 lval->val.numeric = res;
1575 return executeNextItem(cxt, jsp, &elem, lval, found, false);
1579 * Execute unary arithmetic expression for each numeric item in its operand's
1580 * sequence. Array operand is automatically unwrapped in lax mode.
1582 static JsonPathExecResult
1583 executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1584 JsonbValue *jb, PGFunction func, JsonValueList *found)
1586 JsonPathExecResult jper;
1587 JsonPathExecResult jper2;
1589 JsonValueList seq = {0};
1590 JsonValueListIterator it;
1594 jspGetArg(jsp, &elem);
1595 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
1597 if (jperIsError(jper))
1600 jper = jperNotFound;
1602 hasNext = jspGetNext(jsp, &elem);
1604 JsonValueListInitIterator(&seq, &it);
1605 while ((val = JsonValueListNext(&seq, &it)))
1607 if ((val = getScalar(val, jbvNumeric)))
1609 if (!found && !hasNext)
1614 if (!found && !hasNext)
1615 continue; /* skip non-numerics processing */
1617 RETURN_ERROR(ereport(ERROR,
1618 (errcode(ERRCODE_JSON_NUMBER_NOT_FOUND),
1619 errmsg(ERRMSG_JSON_NUMBER_NOT_FOUND),
1620 errdetail("operand of unary jsonpath operator %s "
1621 "is not a numeric value",
1622 jspOperationName(jsp->type)))));
1627 DatumGetNumeric(DirectFunctionCall1(func,
1628 NumericGetDatum(val->val.numeric)));
1630 jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
1632 if (jperIsError(jper2))
1635 if (jper2 == jperOk)
1647 * STARTS_WITH predicate callback.
1649 * Check if the 'whole' string starts from 'initial' string.
1652 executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
1655 if (!(whole = getScalar(whole, jbvString)))
1656 return jpbUnknown; /* error */
1658 if (!(initial = getScalar(initial, jbvString)))
1659 return jpbUnknown; /* error */
1661 if (whole->val.string.len >= initial->val.string.len &&
1662 !memcmp(whole->val.string.val,
1663 initial->val.string.val,
1664 initial->val.string.len))
1671 * LIKE_REGEX predicate callback.
1673 * Check if the string matches regex pattern.
1676 executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
1679 JsonLikeRegexContext *cxt = param;
1681 if (!(str = getScalar(str, jbvString)))
1684 /* Cache regex text and converted flags. */
1687 uint32 flags = jsp->content.like_regex.flags;
1690 cstring_to_text_with_len(jsp->content.like_regex.pattern,
1691 jsp->content.like_regex.patternlen);
1693 /* Convert regex flags. */
1694 cxt->cflags = REG_ADVANCED;
1696 if (flags & JSP_REGEX_ICASE)
1697 cxt->cflags |= REG_ICASE;
1698 if (flags & JSP_REGEX_MLINE)
1699 cxt->cflags |= REG_NEWLINE;
1700 if (flags & JSP_REGEX_SLINE)
1701 cxt->cflags &= ~REG_NEWLINE;
1702 if (flags & JSP_REGEX_WSPACE)
1703 cxt->cflags |= REG_EXPANDED;
1706 if (RE_compile_and_execute(cxt->regex, str->val.string.val,
1707 str->val.string.len,
1708 cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
1715 * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
1716 * user function 'func'.
1718 static JsonPathExecResult
1719 executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1720 JsonbValue *jb, bool unwrap, PGFunction func,
1721 JsonValueList *found)
1726 if (unwrap && JsonbType(jb) == jbvArray)
1727 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1729 if (!(jb = getScalar(jb, jbvNumeric)))
1730 RETURN_ERROR(ereport(ERROR,
1731 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1732 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1733 errdetail("jsonpath item method .%s() can only "
1734 "be applied to a numeric value",
1735 jspOperationName(jsp->type)))));
1737 datum = NumericGetDatum(jb->val.numeric);
1738 datum = DirectFunctionCall1(func, datum);
1740 if (!jspGetNext(jsp, &next) && !found)
1743 jb = palloc(sizeof(*jb));
1744 jb->type = jbvNumeric;
1745 jb->val.numeric = DatumGetNumeric(datum);
1747 return executeNextItem(cxt, jsp, &next, jb, found, false);
1751 * Implementation of .keyvalue() method.
1753 * .keyvalue() method returns a sequence of object's key-value pairs in the
1754 * following format: '{ "key": key, "value": value, "id": id }'.
1756 * "id" field is an object identifier which is constructed from the two parts:
1757 * base object id and its binary offset in base object's jsonb:
1758 * id = 10000000000 * base_object_id + obj_offset_in_base_object
1760 * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1761 * (maximal offset in jsonb). Decimal multiplier is used here to improve the
1762 * readability of identifiers.
1764 * Base object is usually a root object of the path: context item '$' or path
1765 * variable '$var', literals can't produce objects for now. But if the path
1766 * contains generated objects (.keyvalue() itself, for example), then they
1767 * become base object for the subsequent .keyvalue().
1769 * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
1770 * of variables (see getJsonPathVariable()). Ids for generated objects
1771 * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
1773 static JsonPathExecResult
1774 executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1775 JsonbValue *jb, JsonValueList *found)
1777 JsonPathExecResult res = jperNotFound;
1779 JsonbContainer *jbc;
1787 JsonbIteratorToken tok;
1791 if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
1792 RETURN_ERROR(ereport(ERROR,
1793 (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
1794 errmsg(ERRMSG_JSON_OBJECT_NOT_FOUND),
1795 errdetail("jsonpath item method .%s() "
1796 "can only be applied to an object",
1797 jspOperationName(jsp->type)))));
1799 jbc = jb->val.binary.data;
1801 if (!JsonContainerSize(jbc))
1802 return jperNotFound; /* no key-value pairs */
1804 hasNext = jspGetNext(jsp, &next);
1806 keystr.type = jbvString;
1807 keystr.val.string.val = "key";
1808 keystr.val.string.len = 3;
1810 valstr.type = jbvString;
1811 valstr.val.string.val = "value";
1812 valstr.val.string.len = 5;
1814 idstr.type = jbvString;
1815 idstr.val.string.val = "id";
1816 idstr.val.string.len = 2;
1818 /* construct object id from its base object and offset inside that */
1819 id = jb->type != jbvBinary ? 0 :
1820 (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
1821 id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
1823 idval.type = jbvNumeric;
1824 idval.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
1825 Int64GetDatum(id)));
1827 it = JsonbIteratorInit(jbc);
1829 while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
1831 JsonBaseObjectInfo baseObject;
1833 JsonbParseState *ps;
1842 if (!hasNext && !found)
1845 tok = JsonbIteratorNext(&it, &val, true);
1846 Assert(tok == WJB_VALUE);
1849 pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
1851 pushJsonbValue(&ps, WJB_KEY, &keystr);
1852 pushJsonbValue(&ps, WJB_VALUE, &key);
1854 pushJsonbValue(&ps, WJB_KEY, &valstr);
1855 pushJsonbValue(&ps, WJB_VALUE, &val);
1857 pushJsonbValue(&ps, WJB_KEY, &idstr);
1858 pushJsonbValue(&ps, WJB_VALUE, &idval);
1860 keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
1862 jsonb = JsonbValueToJsonb(keyval);
1864 JsonbInitBinary(&obj, jsonb);
1866 baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
1868 res = executeNextItem(cxt, jsp, &next, &obj, found, true);
1870 cxt->baseObject = baseObject;
1872 if (jperIsError(res))
1875 if (res == jperOk && !found)
1883 * Convert boolean execution status 'res' to a boolean JSON item and execute
1886 static JsonPathExecResult
1887 appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1888 JsonValueList *found, JsonPathBool res)
1893 if (!jspGetNext(jsp, &next) && !found)
1894 return jperOk; /* found singleton boolean value */
1896 if (res == jpbUnknown)
1903 jbv.val.boolean = res == jpbTrue;
1906 return executeNextItem(cxt, jsp, &next, &jbv, found, true);
1910 * Convert jsonpath's scalar or variable node to actual jsonb value.
1912 * If node is a variable then its id returned, otherwise 0 returned.
1915 getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
1921 value->type = jbvNull;
1924 value->type = jbvBool;
1925 value->val.boolean = jspGetBool(item);
1928 value->type = jbvNumeric;
1929 value->val.numeric = jspGetNumeric(item);
1932 value->type = jbvString;
1933 value->val.string.val = jspGetString(item,
1934 &value->val.string.len);
1937 getJsonPathVariable(cxt, item, cxt->vars, value);
1940 elog(ERROR, "unexpected jsonpath item type");
1945 * Get the value of variable passed to jsonpath executor
1948 getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
1949 Jsonb *vars, JsonbValue *value)
1958 value->type = jbvNull;
1962 Assert(variable->type == jpiVariable);
1963 varName = jspGetString(variable, &varNameLength);
1964 tmp.type = jbvString;
1965 tmp.val.string.val = varName;
1966 tmp.val.string.len = varNameLength;
1968 v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
1978 (errcode(ERRCODE_UNDEFINED_OBJECT),
1979 errmsg("cannot find jsonpath variable '%s'",
1980 pnstrdup(varName, varNameLength))));
1983 JsonbInitBinary(&tmp, vars);
1984 setBaseObject(cxt, &tmp, 1);
1987 /**************** Support functions for JsonPath execution *****************/
1990 * Returns the size of an array item, or -1 if item is not an array.
1993 JsonbArraySize(JsonbValue *jb)
1995 Assert(jb->type != jbvArray);
1997 if (jb->type == jbvBinary)
1999 JsonbContainer *jbc = jb->val.binary.data;
2001 if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
2002 return JsonContainerSize(jbc);
2008 /* Comparison predicate callback. */
2010 executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
2012 return compareItems(cmp->type, lv, rv);
2016 * Compare two SQL/JSON items using comparison operation 'op'.
2019 compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2)
2024 if (jb1->type != jb2->type)
2026 if (jb1->type == jbvNull || jb2->type == jbvNull)
2029 * Equality and order comparison of nulls to non-nulls returns
2030 * always false, but inequality comparison returns true.
2032 return op == jpiNotEqual ? jpbTrue : jpbFalse;
2034 /* Non-null items of different types are not comparable. */
2044 cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2045 jb1->val.boolean ? 1 : -1;
2048 cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2052 return jb1->val.string.len != jb2->val.string.len ||
2053 memcmp(jb1->val.string.val,
2054 jb2->val.string.val,
2055 jb1->val.string.len) ? jpbFalse : jpbTrue;
2057 cmp = varstr_cmp(jb1->val.string.val, jb1->val.string.len,
2058 jb2->val.string.val, jb2->val.string.len,
2059 DEFAULT_COLLATION_OID);
2065 return jpbUnknown; /* non-scalars are not comparable */
2068 elog(ERROR, "invalid jsonb value type %d", jb1->type);
2085 case jpiLessOrEqual:
2088 case jpiGreaterOrEqual:
2092 elog(ERROR, "unrecognized jsonpath operation: %d", op);
2096 return res ? jpbTrue : jpbFalse;
2099 /* Compare two numerics */
2101 compareNumeric(Numeric a, Numeric b)
2103 return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2105 PointerGetDatum(b)));
2109 copyJsonbValue(JsonbValue *src)
2111 JsonbValue *dst = palloc(sizeof(*dst));
2119 * Execute array subscript expression and convert resulting numeric item to
2120 * the integer type with truncation.
2122 static JsonPathExecResult
2123 getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2127 JsonValueList found = {0};
2128 JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2129 Datum numeric_index;
2130 bool have_error = false;
2132 if (jperIsError(res))
2135 if (JsonValueListLength(&found) != 1 ||
2136 !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2137 RETURN_ERROR(ereport(ERROR,
2138 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
2139 errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
2140 errdetail("jsonpath array subscript is not a "
2141 "singleton numeric value"))));
2143 numeric_index = DirectFunctionCall2(numeric_trunc,
2144 NumericGetDatum(jbv->val.numeric),
2147 *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
2151 RETURN_ERROR(ereport(ERROR,
2152 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
2153 errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
2154 errdetail("jsonpath array subscript is "
2155 "out of integer range"))));
2160 /* Save base object and its id needed for the execution of .keyvalue(). */
2161 static JsonBaseObjectInfo
2162 setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2164 JsonBaseObjectInfo baseObject = cxt->baseObject;
2166 cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2167 (JsonbContainer *) jbv->val.binary.data;
2168 cxt->baseObject.id = id;
2174 JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2178 jvl->list = list_make2(jvl->singleton, jbv);
2179 jvl->singleton = NULL;
2181 else if (!jvl->list)
2182 jvl->singleton = jbv;
2184 jvl->list = lappend(jvl->list, jbv);
2188 JsonValueListLength(const JsonValueList *jvl)
2190 return jvl->singleton ? 1 : list_length(jvl->list);
2194 JsonValueListIsEmpty(JsonValueList *jvl)
2196 return !jvl->singleton && list_length(jvl->list) <= 0;
2200 JsonValueListHead(JsonValueList *jvl)
2202 return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2206 JsonValueListGetList(JsonValueList *jvl)
2209 return list_make1(jvl->singleton);
2215 JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2219 it->value = jvl->singleton;
2222 else if (list_head(jvl->list) != NULL)
2224 it->value = (JsonbValue *) linitial(jvl->list);
2225 it->next = lnext(list_head(jvl->list));
2235 * Get the next item from the sequence advancing iterator.
2238 JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2240 JsonbValue *result = it->value;
2244 it->value = lfirst(it->next);
2245 it->next = lnext(it->next);
2256 * Initialize a binary JsonbValue with the given jsonb container.
2259 JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2261 jbv->type = jbvBinary;
2262 jbv->val.binary.data = &jb->root;
2263 jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2269 * Returns jbv* type of of JsonbValue. Note, it never returns jbvBinary as is.
2272 JsonbType(JsonbValue *jb)
2274 int type = jb->type;
2276 if (jb->type == jbvBinary)
2278 JsonbContainer *jbc = (void *) jb->val.binary.data;
2280 /* Scalars should be always extracted during jsonpath execution. */
2281 Assert(!JsonContainerIsScalar(jbc));
2283 if (JsonContainerIsObject(jbc))
2285 else if (JsonContainerIsArray(jbc))
2288 elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2294 /* Get scalar of given type or NULL on type mismatch */
2296 getScalar(JsonbValue *scalar, enum jbvType type)
2298 /* Scalars should be always extracted during jsonpath execution. */
2299 Assert(scalar->type != jbvBinary ||
2300 !JsonContainerIsScalar(scalar->val.binary.data));
2302 return scalar->type == type ? scalar : NULL;
2305 /* Construct a JSON array from the item list */
2307 wrapItemsInArray(const JsonValueList *items)
2309 JsonbParseState *ps = NULL;
2310 JsonValueListIterator it;
2313 pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2315 JsonValueListInitIterator(items, &it);
2316 while ((jbv = JsonValueListNext(items, &it)))
2317 pushJsonbValue(&ps, WJB_ELEM, jbv);
2319 return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);