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"
81 * Represents "base object" and it's "id" for .keyvalue() evaluation.
83 typedef struct JsonBaseObjectInfo
90 * Context of jsonpath execution.
92 typedef struct JsonPathExecContext
94 Jsonb *vars; /* variables to substitute into jsonpath */
95 JsonbValue *root; /* for $ evaluation */
96 JsonbValue *current; /* for @ evaluation */
97 JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue()
99 int lastGeneratedObjectId; /* "id" counter for .keyvalue()
101 int innermostArraySize; /* for LAST array index evaluation */
102 bool laxMode; /* true for "lax" mode, false for "strict"
104 bool ignoreStructuralErrors; /* with "true" structural errors such
105 * as absence of required json item or
106 * unexpected json item type are
108 bool throwErrors; /* with "false" all suppressible errors are
110 } JsonPathExecContext;
112 /* Context for LIKE_REGEX execution. */
113 typedef struct JsonLikeRegexContext
117 } JsonLikeRegexContext;
119 /* Result of jsonpath predicate evaluation */
120 typedef enum JsonPathBool
127 /* Result of jsonpath expression evaluation */
128 typedef enum JsonPathExecResult
133 } JsonPathExecResult;
135 #define jperIsError(jper) ((jper) == jperError)
138 * List of jsonb values with shortcut for single-value list.
140 typedef struct JsonValueList
142 JsonbValue *singleton;
146 typedef struct JsonValueListIterator
150 } JsonValueListIterator;
152 /* strict/lax flags is decomposed into four [un]wrap/error flags */
153 #define jspStrictAbsenseOfErrors(cxt) (!(cxt)->laxMode)
154 #define jspAutoUnwrap(cxt) ((cxt)->laxMode)
155 #define jspAutoWrap(cxt) ((cxt)->laxMode)
156 #define jspIgnoreStructuralErrors(cxt) ((cxt)->ignoreStructuralErrors)
157 #define jspThrowErrors(cxt) ((cxt)->throwErrors)
159 /* Convenience macro: return or throw error depending on context */
160 #define RETURN_ERROR(throw_error) \
162 if (jspThrowErrors(cxt)) \
168 typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
172 typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
174 static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
175 Jsonb *json, bool throwErrors, JsonValueList *result);
176 static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
177 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
178 static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
179 JsonPathItem *jsp, JsonbValue *jb,
180 JsonValueList *found, bool unwrap);
181 static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
182 JsonPathItem *jsp, JsonbValue *jb,
183 JsonValueList *found, bool unwrapElements);
184 static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
185 JsonPathItem *cur, JsonPathItem *next,
186 JsonbValue *v, JsonValueList *found, bool copy);
187 static JsonPathExecResult executeItemOptUnwrapResult(
188 JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
189 bool unwrap, JsonValueList *found);
190 static JsonPathExecResult executeItemOptUnwrapResultNoThrow(
191 JsonPathExecContext *cxt, JsonPathItem *jsp,
192 JsonbValue *jb, bool unwrap, JsonValueList *found);
193 static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
194 JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
195 static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
196 JsonPathItem *jsp, JsonbValue *jb);
197 static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
198 JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
199 uint32 level, uint32 first, uint32 last,
200 bool ignoreStructuralErrors, bool unwrapNext);
201 static JsonPathBool executePredicate(JsonPathExecContext *cxt,
202 JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
203 JsonbValue *jb, bool unwrapRightArg,
204 JsonPathPredicateCallback exec, void *param);
205 static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
206 JsonPathItem *jsp, JsonbValue *jb,
207 BinaryArithmFunc func, JsonValueList *found);
208 static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
209 JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
210 JsonValueList *found);
211 static JsonPathBool executeStartsWith(JsonPathItem *jsp,
212 JsonbValue *whole, JsonbValue *initial, void *param);
213 static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
214 JsonbValue *rarg, void *param);
215 static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
216 JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
217 JsonValueList *found);
218 static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
219 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
220 static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
221 JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
222 static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
224 static void getJsonPathVariable(JsonPathExecContext *cxt,
225 JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
226 static int JsonbArraySize(JsonbValue *jb);
227 static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
228 JsonbValue *rv, void *p);
229 static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2);
230 static int compareNumeric(Numeric a, Numeric b);
231 static JsonbValue *copyJsonbValue(JsonbValue *src);
232 static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
233 JsonPathItem *jsp, JsonbValue *jb, int32 *index);
234 static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
235 JsonbValue *jbv, int32 id);
236 static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
237 static int JsonValueListLength(const JsonValueList *jvl);
238 static bool JsonValueListIsEmpty(JsonValueList *jvl);
239 static JsonbValue *JsonValueListHead(JsonValueList *jvl);
240 static List *JsonValueListGetList(JsonValueList *jvl);
241 static void JsonValueListInitIterator(const JsonValueList *jvl,
242 JsonValueListIterator *it);
243 static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
244 JsonValueListIterator *it);
245 static int JsonbType(JsonbValue *jb);
246 static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
247 static int JsonbType(JsonbValue *jb);
248 static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
249 static JsonbValue *wrapItemsInArray(const JsonValueList *items);
251 /****************** User interface to JsonPath executor ********************/
255 * Returns true if jsonpath returns at least one item for the specified
256 * jsonb value. This function and jsonb_path_match() are used to
257 * implement @? and @@ operators, which in turn are intended to have an
258 * index support. Thus, it's desirable to make it easier to achieve
259 * consistency between index scan results and sequential scan results.
260 * So, we throw as less errors as possible. Regarding this function,
261 * such behavior also matches behavior of JSON_EXISTS() clause of
262 * SQL/JSON. Regarding jsonb_path_match(), this function doesn't have
263 * an analogy in SQL/JSON, so we define its behavior on our own.
266 jsonb_path_exists(PG_FUNCTION_ARGS)
268 Jsonb *jb = PG_GETARG_JSONB_P(0);
269 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
270 JsonPathExecResult res;
276 vars = PG_GETARG_JSONB_P(2);
277 silent = PG_GETARG_BOOL(3);
280 res = executeJsonPath(jp, vars, jb, !silent, NULL);
282 PG_FREE_IF_COPY(jb, 0);
283 PG_FREE_IF_COPY(jp, 1);
285 if (jperIsError(res))
288 PG_RETURN_BOOL(res == jperOk);
292 * jsonb_path_exists_opr
293 * Implementation of operator "jsonb @? jsonpath" (2-argument version of
294 * jsonb_path_exists()).
297 jsonb_path_exists_opr(PG_FUNCTION_ARGS)
299 /* just call the other one -- it can handle both cases */
300 return jsonb_path_exists(fcinfo);
305 * Returns jsonpath predicate result item for the specified jsonb value.
306 * See jsonb_path_exists() comment for details regarding error handling.
309 jsonb_path_match(PG_FUNCTION_ARGS)
311 Jsonb *jb = PG_GETARG_JSONB_P(0);
312 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
313 JsonValueList found = {0};
319 vars = PG_GETARG_JSONB_P(2);
320 silent = PG_GETARG_BOOL(3);
323 (void) executeJsonPath(jp, vars, jb, !silent, &found);
325 PG_FREE_IF_COPY(jb, 0);
326 PG_FREE_IF_COPY(jp, 1);
328 if (JsonValueListLength(&found) == 1)
330 JsonbValue *jbv = JsonValueListHead(&found);
332 if (jbv->type == jbvBool)
333 PG_RETURN_BOOL(jbv->val.boolean);
335 if (jbv->type == jbvNull)
341 (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
342 errmsg("single boolean result is expected")));
348 * jsonb_path_match_opr
349 * Implementation of operator "jsonb @@ jsonpath" (2-argument version of
350 * jsonb_path_match()).
353 jsonb_path_match_opr(PG_FUNCTION_ARGS)
355 /* just call the other one -- it can handle both cases */
356 return jsonb_path_match(fcinfo);
361 * Executes jsonpath for given jsonb document and returns result as
365 jsonb_path_query(PG_FUNCTION_ARGS)
367 FuncCallContext *funcctx;
372 if (SRF_IS_FIRSTCALL())
376 MemoryContext oldcontext;
379 JsonValueList found = {0};
381 funcctx = SRF_FIRSTCALL_INIT();
382 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
384 jb = PG_GETARG_JSONB_P_COPY(0);
385 jp = PG_GETARG_JSONPATH_P_COPY(1);
386 vars = PG_GETARG_JSONB_P_COPY(2);
387 silent = PG_GETARG_BOOL(3);
389 (void) executeJsonPath(jp, vars, jb, !silent, &found);
391 funcctx->user_fctx = JsonValueListGetList(&found);
393 MemoryContextSwitchTo(oldcontext);
396 funcctx = SRF_PERCALL_SETUP();
397 found = funcctx->user_fctx;
399 c = list_head(found);
402 SRF_RETURN_DONE(funcctx);
405 funcctx->user_fctx = list_delete_first(found);
407 SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
411 * jsonb_path_query_array
412 * Executes jsonpath for given jsonb document and returns result as
416 jsonb_path_query_array(FunctionCallInfo fcinfo)
418 Jsonb *jb = PG_GETARG_JSONB_P(0);
419 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
420 JsonValueList found = {0};
421 Jsonb *vars = PG_GETARG_JSONB_P(2);
422 bool silent = PG_GETARG_BOOL(3);
424 (void) executeJsonPath(jp, vars, jb, !silent, &found);
426 PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
430 * jsonb_path_query_first
431 * Executes jsonpath for given jsonb document and returns first result
432 * item. If there are no items, NULL returned.
435 jsonb_path_query_first(FunctionCallInfo fcinfo)
437 Jsonb *jb = PG_GETARG_JSONB_P(0);
438 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
439 JsonValueList found = {0};
440 Jsonb *vars = PG_GETARG_JSONB_P(2);
441 bool silent = PG_GETARG_BOOL(3);
443 (void) executeJsonPath(jp, vars, jb, !silent, &found);
445 if (JsonValueListLength(&found) >= 1)
446 PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
451 /********************Execute functions for JsonPath**************************/
454 * Interface to jsonpath executor
456 * 'path' - jsonpath to be executed
457 * 'vars' - variables to be substituted to jsonpath
458 * 'json' - target document for jsonpath evaluation
459 * 'throwErrors' - whether we should throw suppressible errors
460 * 'result' - list to store result items into
462 * Returns an error if a recoverable error happens during processing, or NULL
465 * Note, jsonb and jsonpath values should be available and untoasted during
466 * work because JsonPathItem, JsonbValue and result item could have pointers
467 * into input values. If caller needs to just check if document matches
468 * jsonpath, then it doesn't provide a result arg. In this case executor
469 * works till first positive result and does not check the rest if possible.
470 * In other case it tries to find all the satisfied result items.
472 static JsonPathExecResult
473 executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
474 JsonValueList *result)
476 JsonPathExecContext cxt;
477 JsonPathExecResult res;
483 if (!JsonbExtractScalar(&json->root, &jbv))
484 JsonbInitBinary(&jbv, json);
486 if (vars && !JsonContainerIsObject(&vars->root))
489 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
490 errmsg("\"vars\" argument is not an object"),
491 errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
495 cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
496 cxt.ignoreStructuralErrors = cxt.laxMode;
499 cxt.baseObject.jbc = NULL;
500 cxt.baseObject.id = 0;
501 cxt.lastGeneratedObjectId = vars ? 2 : 1;
502 cxt.innermostArraySize = -1;
503 cxt.throwErrors = throwErrors;
505 if (jspStrictAbsenseOfErrors(&cxt) && !result)
508 * In strict mode we must get a complete list of values to check that
509 * there are no errors at all.
511 JsonValueList vals = {0};
513 res = executeItem(&cxt, &jsp, &jbv, &vals);
515 if (jperIsError(res))
518 return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
521 res = executeItem(&cxt, &jsp, &jbv, result);
523 Assert(!throwErrors || !jperIsError(res));
529 * Execute jsonpath with automatic unwrapping of current item in lax mode.
531 static JsonPathExecResult
532 executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
533 JsonbValue *jb, JsonValueList *found)
535 return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
539 * Main jsonpath executor function: walks on jsonpath structure, finds
540 * relevant parts of jsonb and evaluates expressions over them.
541 * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
543 static JsonPathExecResult
544 executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
545 JsonbValue *jb, JsonValueList *found, bool unwrap)
548 JsonPathExecResult res = jperNotFound;
549 JsonBaseObjectInfo baseObject;
552 CHECK_FOR_INTERRUPTS();
556 /* all boolean item types: */
566 case jpiGreaterOrEqual:
571 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
573 res = appendBoolResult(cxt, jsp, found, st);
578 if (JsonbType(jb) == jbvObject)
583 key.type = jbvString;
584 key.val.string.val = jspGetString(jsp, &key.val.string.len);
586 v = findJsonbValueFromContainer(jb->val.binary.data,
591 res = executeNextItem(cxt, jsp, NULL,
594 /* free value if it was not added to found list */
595 if (jspHasNext(jsp) || !found)
598 else if (!jspIgnoreStructuralErrors(cxt))
602 if (!jspThrowErrors(cxt))
606 (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND), \
607 errmsg("JSON object does not contain key \"%s\"",
608 pnstrdup(key.val.string.val,
609 key.val.string.len))));
612 else if (unwrap && JsonbType(jb) == jbvArray)
613 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
614 else if (!jspIgnoreStructuralErrors(cxt))
617 RETURN_ERROR(ereport(ERROR,
618 (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND),
619 errmsg("jsonpath member accessor can only be applied to an object"))));
625 baseObject = setBaseObject(cxt, jb, 0);
626 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
627 cxt->baseObject = baseObject;
631 res = executeNextItem(cxt, jsp, NULL, cxt->current,
636 if (JsonbType(jb) == jbvArray)
638 bool hasNext = jspGetNext(jsp, &elem);
640 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
641 jb, found, jspAutoUnwrap(cxt));
643 else if (jspAutoWrap(cxt))
644 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
645 else if (!jspIgnoreStructuralErrors(cxt))
646 RETURN_ERROR(ereport(ERROR,
647 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
648 errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
652 if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
654 int innermostArraySize = cxt->innermostArraySize;
656 int size = JsonbArraySize(jb);
657 bool singleton = size < 0;
658 bool hasNext = jspGetNext(jsp, &elem);
663 cxt->innermostArraySize = size; /* for LAST evaluation */
665 for (i = 0; i < jsp->content.array.nelems; i++)
672 bool range = jspGetArraySubscript(jsp, &from,
675 res = getArrayIndex(cxt, &from, jb, &index_from);
677 if (jperIsError(res))
682 res = getArrayIndex(cxt, &to, jb, &index_to);
684 if (jperIsError(res))
688 index_to = index_from;
690 if (!jspIgnoreStructuralErrors(cxt) &&
692 index_from > index_to ||
694 RETURN_ERROR(ereport(ERROR,
695 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
696 errmsg("jsonpath array subscript is out of bounds"))));
701 if (index_to >= size)
706 for (index = index_from; index <= index_to; index++)
718 v = getIthJsonbValueFromContainer(jb->val.binary.data,
727 if (!hasNext && !found)
730 res = executeNextItem(cxt, jsp, &elem, v, found,
733 if (jperIsError(res))
736 if (res == jperOk && !found)
740 if (jperIsError(res))
743 if (res == jperOk && !found)
747 cxt->innermostArraySize = innermostArraySize;
749 else if (!jspIgnoreStructuralErrors(cxt))
751 RETURN_ERROR(ereport(ERROR,
752 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
753 errmsg("jsonpath array accessor can only be applied to an array"))));
762 bool hasNext = jspGetNext(jsp, &elem);
764 if (cxt->innermostArraySize < 0)
765 elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
767 if (!hasNext && !found)
773 last = cxt->innermostArraySize - 1;
775 lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
777 lastjbv->type = jbvNumeric;
778 lastjbv->val.numeric =
779 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
780 Int32GetDatum(last)));
782 res = executeNextItem(cxt, jsp, &elem,
783 lastjbv, found, hasNext);
788 if (JsonbType(jb) == jbvObject)
790 bool hasNext = jspGetNext(jsp, &elem);
792 if (jb->type != jbvBinary)
793 elog(ERROR, "invalid jsonb object type: %d", jb->type);
795 return executeAnyItem
796 (cxt, hasNext ? &elem : NULL,
797 jb->val.binary.data, found, 1, 1, 1,
798 false, jspAutoUnwrap(cxt));
800 else if (unwrap && JsonbType(jb) == jbvArray)
801 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
802 else if (!jspIgnoreStructuralErrors(cxt))
805 RETURN_ERROR(ereport(ERROR,
806 (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
807 errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
812 return executeBinaryArithmExpr(cxt, jsp, jb,
813 numeric_add_opt_error, found);
816 return executeBinaryArithmExpr(cxt, jsp, jb,
817 numeric_sub_opt_error, found);
820 return executeBinaryArithmExpr(cxt, jsp, jb,
821 numeric_mul_opt_error, found);
824 return executeBinaryArithmExpr(cxt, jsp, jb,
825 numeric_div_opt_error, found);
828 return executeBinaryArithmExpr(cxt, jsp, jb,
829 numeric_mod_opt_error, found);
832 return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
835 return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
842 if (unwrap && JsonbType(jb) == jbvArray)
843 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
846 jspGetArg(jsp, &elem);
847 st = executeNestedBoolItem(cxt, &elem, jb);
851 res = executeNextItem(cxt, jsp, NULL,
858 bool hasNext = jspGetNext(jsp, &elem);
860 /* first try without any intermediate steps */
861 if (jsp->content.anybounds.first == 0)
863 bool savedIgnoreStructuralErrors;
865 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
866 cxt->ignoreStructuralErrors = true;
867 res = executeNextItem(cxt, jsp, &elem,
869 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
871 if (res == jperOk && !found)
875 if (jb->type == jbvBinary)
877 (cxt, hasNext ? &elem : NULL,
878 jb->val.binary.data, found,
880 jsp->content.anybounds.first,
881 jsp->content.anybounds.last,
882 true, jspAutoUnwrap(cxt));
894 bool hasNext = jspGetNext(jsp, &elem);
896 if (!hasNext && !found)
898 res = jperOk; /* skip evaluation */
902 v = hasNext ? &vbuf : palloc(sizeof(*v));
904 baseObject = cxt->baseObject;
905 getJsonPathItem(cxt, jsp, v);
907 res = executeNextItem(cxt, jsp, &elem,
909 cxt->baseObject = baseObject;
915 JsonbValue *jbv = palloc(sizeof(*jbv));
917 jbv->type = jbvString;
918 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
919 jbv->val.string.len = strlen(jbv->val.string.val);
921 res = executeNextItem(cxt, jsp, NULL, jbv,
928 int size = JsonbArraySize(jb);
932 if (!jspAutoWrap(cxt))
934 if (!jspIgnoreStructuralErrors(cxt))
935 RETURN_ERROR(ereport(ERROR,
936 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
937 errmsg("jsonpath item method .%s() can only be applied to an array",
938 jspOperationName(jsp->type)))));
945 jb = palloc(sizeof(*jb));
947 jb->type = jbvNumeric;
949 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
950 Int32GetDatum(size)));
952 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
957 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
961 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
965 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
972 if (unwrap && JsonbType(jb) == jbvArray)
973 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
976 if (jb->type == jbvNumeric)
978 char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
979 NumericGetDatum(jb->val.numeric)));
980 bool have_error = false;
982 (void) float8in_internal_opt_error(tmp,
989 RETURN_ERROR(ereport(ERROR,
990 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
991 errmsg("jsonpath item method .%s() can only be applied to a numeric value",
992 jspOperationName(jsp->type)))));
995 else if (jb->type == jbvString)
997 /* cast string as double */
999 char *tmp = pnstrdup(jb->val.string.val,
1000 jb->val.string.len);
1001 bool have_error = false;
1003 val = float8in_internal_opt_error(tmp,
1009 if (have_error || isinf(val))
1010 RETURN_ERROR(ereport(ERROR,
1011 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1012 errmsg("jsonpath item method .%s() can only be applied to a numeric value",
1013 jspOperationName(jsp->type)))));
1016 jb->type = jbvNumeric;
1017 jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1018 Float8GetDatum(val)));
1022 if (res == jperNotFound)
1023 RETURN_ERROR(ereport(ERROR,
1024 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1025 errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1026 jspOperationName(jsp->type)))));
1028 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1033 if (unwrap && JsonbType(jb) == jbvArray)
1034 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1036 return executeKeyValueMethod(cxt, jsp, jb, found);
1039 elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1046 * Unwrap current array item and execute jsonpath for each of its elements.
1048 static JsonPathExecResult
1049 executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1050 JsonbValue *jb, JsonValueList *found,
1051 bool unwrapElements)
1053 if (jb->type != jbvBinary)
1055 Assert(jb->type != jbvArray);
1056 elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1059 return executeAnyItem
1060 (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1061 false, unwrapElements);
1065 * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1068 static JsonPathExecResult
1069 executeNextItem(JsonPathExecContext *cxt,
1070 JsonPathItem *cur, JsonPathItem *next,
1071 JsonbValue *v, JsonValueList *found, bool copy)
1077 hasNext = next != NULL;
1079 hasNext = jspHasNext(cur);
1083 hasNext = jspGetNext(cur, next);
1087 return executeItem(cxt, next, v, found);
1090 JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1096 * Same as executeItem(), but when "unwrap == true" automatically unwraps
1097 * each array item from the resulting sequence in lax mode.
1099 static JsonPathExecResult
1100 executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1101 JsonbValue *jb, bool unwrap,
1102 JsonValueList *found)
1104 if (unwrap && jspAutoUnwrap(cxt))
1106 JsonValueList seq = {0};
1107 JsonValueListIterator it;
1108 JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1111 if (jperIsError(res))
1114 JsonValueListInitIterator(&seq, &it);
1115 while ((item = JsonValueListNext(&seq, &it)))
1117 Assert(item->type != jbvArray);
1119 if (JsonbType(item) == jbvArray)
1120 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1122 JsonValueListAppend(found, item);
1128 return executeItem(cxt, jsp, jb, found);
1132 * Same as executeItemOptUnwrapResult(), but with error suppression.
1134 static JsonPathExecResult
1135 executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1137 JsonbValue *jb, bool unwrap,
1138 JsonValueList *found)
1140 JsonPathExecResult res;
1141 bool throwErrors = cxt->throwErrors;
1143 cxt->throwErrors = false;
1144 res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1145 cxt->throwErrors = throwErrors;
1150 /* Execute boolean-valued jsonpath expression. */
1152 executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1153 JsonbValue *jb, bool canHaveNext)
1160 if (!canHaveNext && jspHasNext(jsp))
1161 elog(ERROR, "boolean jsonpath item cannot have next item");
1166 jspGetLeftArg(jsp, &larg);
1167 res = executeBoolItem(cxt, &larg, jb, false);
1169 if (res == jpbFalse)
1173 * SQL/JSON says that we should check second arg in case of
1177 jspGetRightArg(jsp, &rarg);
1178 res2 = executeBoolItem(cxt, &rarg, jb, false);
1180 return res2 == jpbTrue ? res : res2;
1183 jspGetLeftArg(jsp, &larg);
1184 res = executeBoolItem(cxt, &larg, jb, false);
1189 jspGetRightArg(jsp, &rarg);
1190 res2 = executeBoolItem(cxt, &rarg, jb, false);
1192 return res2 == jpbFalse ? res : res2;
1195 jspGetArg(jsp, &larg);
1197 res = executeBoolItem(cxt, &larg, jb, false);
1199 if (res == jpbUnknown)
1202 return res == jpbTrue ? jpbFalse : jpbTrue;
1205 jspGetArg(jsp, &larg);
1206 res = executeBoolItem(cxt, &larg, jb, false);
1207 return res == jpbUnknown ? jpbTrue : jpbFalse;
1213 case jpiLessOrEqual:
1214 case jpiGreaterOrEqual:
1215 jspGetLeftArg(jsp, &larg);
1216 jspGetRightArg(jsp, &rarg);
1217 return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1218 executeComparison, NULL);
1220 case jpiStartsWith: /* 'whole STARTS WITH initial' */
1221 jspGetLeftArg(jsp, &larg); /* 'whole' */
1222 jspGetRightArg(jsp, &rarg); /* 'initial' */
1223 return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1224 executeStartsWith, NULL);
1226 case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1229 * 'expr' is a sequence-returning expression. 'pattern' is a
1230 * regex string literal. SQL/JSON standard requires XQuery
1231 * regexes, but we use Postgres regexes here. 'flags' is a
1232 * string literal converted to integer flags at compile-time.
1234 JsonLikeRegexContext lrcxt = {0};
1236 jspInitByBuffer(&larg, jsp->base,
1237 jsp->content.like_regex.expr);
1239 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1240 executeLikeRegex, &lrcxt);
1244 jspGetArg(jsp, &larg);
1246 if (jspStrictAbsenseOfErrors(cxt))
1249 * In strict mode we must get a complete list of values to
1250 * check that there are no errors at all.
1252 JsonValueList vals = {0};
1253 JsonPathExecResult res =
1254 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1257 if (jperIsError(res))
1260 return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1264 JsonPathExecResult res =
1265 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1268 if (jperIsError(res))
1271 return res == jperOk ? jpbTrue : jpbFalse;
1275 elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1281 * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1282 * item onto the stack.
1285 executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1291 prev = cxt->current;
1293 res = executeBoolItem(cxt, jsp, jb, false);
1294 cxt->current = prev;
1300 * Implementation of several jsonpath nodes:
1301 * - jpiAny (.** accessor),
1302 * - jpiAnyKey (.* accessor),
1303 * - jpiAnyArray ([*] accessor)
1305 static JsonPathExecResult
1306 executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1307 JsonValueList *found, uint32 level, uint32 first, uint32 last,
1308 bool ignoreStructuralErrors, bool unwrapNext)
1310 JsonPathExecResult res = jperNotFound;
1315 check_stack_depth();
1320 it = JsonbIteratorInit(jbc);
1323 * Recursively iterate over jsonb objects/arrays
1325 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1329 r = JsonbIteratorNext(&it, &v, true);
1330 Assert(r == WJB_VALUE);
1333 if (r == WJB_VALUE || r == WJB_ELEM)
1336 if (level >= first ||
1337 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1338 v.type != jbvBinary)) /* leaves only requested */
1340 /* check expression */
1343 if (ignoreStructuralErrors)
1345 bool savedIgnoreStructuralErrors;
1347 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1348 cxt->ignoreStructuralErrors = true;
1349 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1350 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1353 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1355 if (jperIsError(res))
1358 if (res == jperOk && !found)
1362 JsonValueListAppend(found, copyJsonbValue(&v));
1367 if (level < last && v.type == jbvBinary)
1369 res = executeAnyItem
1370 (cxt, jsp, v.val.binary.data, found,
1371 level + 1, first, last,
1372 ignoreStructuralErrors, unwrapNext);
1374 if (jperIsError(res))
1377 if (res == jperOk && found == NULL)
1387 * Execute unary or binary predicate.
1389 * Predicates have existence semantics, because their operands are item
1390 * sequences. Pairs of items from the left and right operand's sequences are
1391 * checked. TRUE returned only if any pair satisfying the condition is found.
1392 * In strict mode, even if the desired pair has already been found, all pairs
1393 * still need to be examined to check the absence of errors. If any error
1394 * occurs, UNKNOWN (analogous to SQL NULL) is returned.
1397 executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
1398 JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
1399 bool unwrapRightArg, JsonPathPredicateCallback exec,
1402 JsonPathExecResult res;
1403 JsonValueListIterator lseqit;
1404 JsonValueList lseq = {0};
1405 JsonValueList rseq = {0};
1410 /* Left argument is always auto-unwrapped. */
1411 res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
1412 if (jperIsError(res))
1417 /* Right argument is conditionally auto-unwrapped. */
1418 res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
1419 unwrapRightArg, &rseq);
1420 if (jperIsError(res))
1424 JsonValueListInitIterator(&lseq, &lseqit);
1425 while ((lval = JsonValueListNext(&lseq, &lseqit)))
1427 JsonValueListIterator rseqit;
1431 JsonValueListInitIterator(&rseq, &rseqit);
1433 rval = JsonValueListNext(&rseq, &rseqit);
1437 /* Loop over right arg sequence or do single pass otherwise */
1438 while (rarg ? (rval != NULL) : first)
1440 JsonPathBool res = exec(pred, lval, rval, param);
1442 if (res == jpbUnknown)
1444 if (jspStrictAbsenseOfErrors(cxt))
1449 else if (res == jpbTrue)
1451 if (!jspStrictAbsenseOfErrors(cxt))
1459 rval = JsonValueListNext(&rseq, &rseqit);
1463 if (found) /* possible only in strict mode */
1466 if (error) /* possible only in lax mode */
1473 * Execute binary arithmetic expression on singleton numeric operands.
1474 * Array operands are automatically unwrapped in lax mode.
1476 static JsonPathExecResult
1477 executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1478 JsonbValue *jb, BinaryArithmFunc func,
1479 JsonValueList *found)
1481 JsonPathExecResult jper;
1483 JsonValueList lseq = {0};
1484 JsonValueList rseq = {0};
1489 jspGetLeftArg(jsp, &elem);
1492 * XXX: By standard only operands of multiplicative expressions are
1493 * unwrapped. We extend it to other binary arithmetic expressions too.
1495 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
1496 if (jperIsError(jper))
1499 jspGetRightArg(jsp, &elem);
1501 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
1502 if (jperIsError(jper))
1505 if (JsonValueListLength(&lseq) != 1 ||
1506 !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
1507 RETURN_ERROR(ereport(ERROR,
1508 (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
1509 errmsg("left operand of jsonpath operator %s is not a single numeric value",
1510 jspOperationName(jsp->type)))));
1512 if (JsonValueListLength(&rseq) != 1 ||
1513 !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
1514 RETURN_ERROR(ereport(ERROR,
1515 (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
1516 errmsg("right operand of jsonpath operator %s is not a single numeric value",
1517 jspOperationName(jsp->type)))));
1519 if (jspThrowErrors(cxt))
1521 res = func(lval->val.numeric, rval->val.numeric, NULL);
1527 res = func(lval->val.numeric, rval->val.numeric, &error);
1533 if (!jspGetNext(jsp, &elem) && !found)
1536 lval = palloc(sizeof(*lval));
1537 lval->type = jbvNumeric;
1538 lval->val.numeric = res;
1540 return executeNextItem(cxt, jsp, &elem, lval, found, false);
1544 * Execute unary arithmetic expression for each numeric item in its operand's
1545 * sequence. Array operand is automatically unwrapped in lax mode.
1547 static JsonPathExecResult
1548 executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1549 JsonbValue *jb, PGFunction func, JsonValueList *found)
1551 JsonPathExecResult jper;
1552 JsonPathExecResult jper2;
1554 JsonValueList seq = {0};
1555 JsonValueListIterator it;
1559 jspGetArg(jsp, &elem);
1560 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
1562 if (jperIsError(jper))
1565 jper = jperNotFound;
1567 hasNext = jspGetNext(jsp, &elem);
1569 JsonValueListInitIterator(&seq, &it);
1570 while ((val = JsonValueListNext(&seq, &it)))
1572 if ((val = getScalar(val, jbvNumeric)))
1574 if (!found && !hasNext)
1579 if (!found && !hasNext)
1580 continue; /* skip non-numerics processing */
1582 RETURN_ERROR(ereport(ERROR,
1583 (errcode(ERRCODE_JSON_NUMBER_NOT_FOUND),
1584 errmsg("operand of unary jsonpath operator %s is not a numeric value",
1585 jspOperationName(jsp->type)))));
1590 DatumGetNumeric(DirectFunctionCall1(func,
1591 NumericGetDatum(val->val.numeric)));
1593 jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
1595 if (jperIsError(jper2))
1598 if (jper2 == jperOk)
1610 * STARTS_WITH predicate callback.
1612 * Check if the 'whole' string starts from 'initial' string.
1615 executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
1618 if (!(whole = getScalar(whole, jbvString)))
1619 return jpbUnknown; /* error */
1621 if (!(initial = getScalar(initial, jbvString)))
1622 return jpbUnknown; /* error */
1624 if (whole->val.string.len >= initial->val.string.len &&
1625 !memcmp(whole->val.string.val,
1626 initial->val.string.val,
1627 initial->val.string.len))
1634 * LIKE_REGEX predicate callback.
1636 * Check if the string matches regex pattern.
1639 executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
1642 JsonLikeRegexContext *cxt = param;
1644 if (!(str = getScalar(str, jbvString)))
1647 /* Cache regex text and converted flags. */
1650 uint32 flags = jsp->content.like_regex.flags;
1653 cstring_to_text_with_len(jsp->content.like_regex.pattern,
1654 jsp->content.like_regex.patternlen);
1656 /* Convert regex flags. */
1657 cxt->cflags = REG_ADVANCED;
1659 if (flags & JSP_REGEX_ICASE)
1660 cxt->cflags |= REG_ICASE;
1661 if (flags & JSP_REGEX_MLINE)
1662 cxt->cflags |= REG_NEWLINE;
1663 if (flags & JSP_REGEX_SLINE)
1664 cxt->cflags &= ~REG_NEWLINE;
1665 if (flags & JSP_REGEX_WSPACE)
1666 cxt->cflags |= REG_EXPANDED;
1669 if (RE_compile_and_execute(cxt->regex, str->val.string.val,
1670 str->val.string.len,
1671 cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
1678 * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
1679 * user function 'func'.
1681 static JsonPathExecResult
1682 executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1683 JsonbValue *jb, bool unwrap, PGFunction func,
1684 JsonValueList *found)
1689 if (unwrap && JsonbType(jb) == jbvArray)
1690 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1692 if (!(jb = getScalar(jb, jbvNumeric)))
1693 RETURN_ERROR(ereport(ERROR,
1694 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1695 errmsg("jsonpath item method .%s() can only be applied to a numeric value",
1696 jspOperationName(jsp->type)))));
1698 datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
1700 if (!jspGetNext(jsp, &next) && !found)
1703 jb = palloc(sizeof(*jb));
1704 jb->type = jbvNumeric;
1705 jb->val.numeric = DatumGetNumeric(datum);
1707 return executeNextItem(cxt, jsp, &next, jb, found, false);
1711 * Implementation of .keyvalue() method.
1713 * .keyvalue() method returns a sequence of object's key-value pairs in the
1714 * following format: '{ "key": key, "value": value, "id": id }'.
1716 * "id" field is an object identifier which is constructed from the two parts:
1717 * base object id and its binary offset in base object's jsonb:
1718 * id = 10000000000 * base_object_id + obj_offset_in_base_object
1720 * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1721 * (maximal offset in jsonb). Decimal multiplier is used here to improve the
1722 * readability of identifiers.
1724 * Base object is usually a root object of the path: context item '$' or path
1725 * variable '$var', literals can't produce objects for now. But if the path
1726 * contains generated objects (.keyvalue() itself, for example), then they
1727 * become base object for the subsequent .keyvalue().
1729 * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
1730 * of variables (see getJsonPathVariable()). Ids for generated objects
1731 * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
1733 static JsonPathExecResult
1734 executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1735 JsonbValue *jb, JsonValueList *found)
1737 JsonPathExecResult res = jperNotFound;
1739 JsonbContainer *jbc;
1747 JsonbIteratorToken tok;
1751 if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
1752 RETURN_ERROR(ereport(ERROR,
1753 (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
1754 errmsg("jsonpath item method .%s() can only be applied to an object",
1755 jspOperationName(jsp->type)))));
1757 jbc = jb->val.binary.data;
1759 if (!JsonContainerSize(jbc))
1760 return jperNotFound; /* no key-value pairs */
1762 hasNext = jspGetNext(jsp, &next);
1764 keystr.type = jbvString;
1765 keystr.val.string.val = "key";
1766 keystr.val.string.len = 3;
1768 valstr.type = jbvString;
1769 valstr.val.string.val = "value";
1770 valstr.val.string.len = 5;
1772 idstr.type = jbvString;
1773 idstr.val.string.val = "id";
1774 idstr.val.string.len = 2;
1776 /* construct object id from its base object and offset inside that */
1777 id = jb->type != jbvBinary ? 0 :
1778 (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
1779 id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
1781 idval.type = jbvNumeric;
1782 idval.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
1783 Int64GetDatum(id)));
1785 it = JsonbIteratorInit(jbc);
1787 while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
1789 JsonBaseObjectInfo baseObject;
1791 JsonbParseState *ps;
1800 if (!hasNext && !found)
1803 tok = JsonbIteratorNext(&it, &val, true);
1804 Assert(tok == WJB_VALUE);
1807 pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
1809 pushJsonbValue(&ps, WJB_KEY, &keystr);
1810 pushJsonbValue(&ps, WJB_VALUE, &key);
1812 pushJsonbValue(&ps, WJB_KEY, &valstr);
1813 pushJsonbValue(&ps, WJB_VALUE, &val);
1815 pushJsonbValue(&ps, WJB_KEY, &idstr);
1816 pushJsonbValue(&ps, WJB_VALUE, &idval);
1818 keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
1820 jsonb = JsonbValueToJsonb(keyval);
1822 JsonbInitBinary(&obj, jsonb);
1824 baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
1826 res = executeNextItem(cxt, jsp, &next, &obj, found, true);
1828 cxt->baseObject = baseObject;
1830 if (jperIsError(res))
1833 if (res == jperOk && !found)
1841 * Convert boolean execution status 'res' to a boolean JSON item and execute
1844 static JsonPathExecResult
1845 appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1846 JsonValueList *found, JsonPathBool res)
1851 if (!jspGetNext(jsp, &next) && !found)
1852 return jperOk; /* found singleton boolean value */
1854 if (res == jpbUnknown)
1861 jbv.val.boolean = res == jpbTrue;
1864 return executeNextItem(cxt, jsp, &next, &jbv, found, true);
1868 * Convert jsonpath's scalar or variable node to actual jsonb value.
1870 * If node is a variable then its id returned, otherwise 0 returned.
1873 getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
1879 value->type = jbvNull;
1882 value->type = jbvBool;
1883 value->val.boolean = jspGetBool(item);
1886 value->type = jbvNumeric;
1887 value->val.numeric = jspGetNumeric(item);
1890 value->type = jbvString;
1891 value->val.string.val = jspGetString(item,
1892 &value->val.string.len);
1895 getJsonPathVariable(cxt, item, cxt->vars, value);
1898 elog(ERROR, "unexpected jsonpath item type");
1903 * Get the value of variable passed to jsonpath executor
1906 getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
1907 Jsonb *vars, JsonbValue *value)
1916 value->type = jbvNull;
1920 Assert(variable->type == jpiVariable);
1921 varName = jspGetString(variable, &varNameLength);
1922 tmp.type = jbvString;
1923 tmp.val.string.val = varName;
1924 tmp.val.string.len = varNameLength;
1926 v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
1936 (errcode(ERRCODE_UNDEFINED_OBJECT),
1937 errmsg("could not find jsonpath variable \"%s\"",
1938 pnstrdup(varName, varNameLength))));
1941 JsonbInitBinary(&tmp, vars);
1942 setBaseObject(cxt, &tmp, 1);
1945 /**************** Support functions for JsonPath execution *****************/
1948 * Returns the size of an array item, or -1 if item is not an array.
1951 JsonbArraySize(JsonbValue *jb)
1953 Assert(jb->type != jbvArray);
1955 if (jb->type == jbvBinary)
1957 JsonbContainer *jbc = jb->val.binary.data;
1959 if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
1960 return JsonContainerSize(jbc);
1966 /* Comparison predicate callback. */
1968 executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
1970 return compareItems(cmp->type, lv, rv);
1974 * Compare two SQL/JSON items using comparison operation 'op'.
1977 compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2)
1982 if (jb1->type != jb2->type)
1984 if (jb1->type == jbvNull || jb2->type == jbvNull)
1987 * Equality and order comparison of nulls to non-nulls returns
1988 * always false, but inequality comparison returns true.
1990 return op == jpiNotEqual ? jpbTrue : jpbFalse;
1992 /* Non-null items of different types are not comparable. */
2002 cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2003 jb1->val.boolean ? 1 : -1;
2006 cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2010 return jb1->val.string.len != jb2->val.string.len ||
2011 memcmp(jb1->val.string.val,
2012 jb2->val.string.val,
2013 jb1->val.string.len) ? jpbFalse : jpbTrue;
2015 cmp = varstr_cmp(jb1->val.string.val, jb1->val.string.len,
2016 jb2->val.string.val, jb2->val.string.len,
2017 DEFAULT_COLLATION_OID);
2023 return jpbUnknown; /* non-scalars are not comparable */
2026 elog(ERROR, "invalid jsonb value type %d", jb1->type);
2043 case jpiLessOrEqual:
2046 case jpiGreaterOrEqual:
2050 elog(ERROR, "unrecognized jsonpath operation: %d", op);
2054 return res ? jpbTrue : jpbFalse;
2057 /* Compare two numerics */
2059 compareNumeric(Numeric a, Numeric b)
2061 return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2063 NumericGetDatum(b)));
2067 copyJsonbValue(JsonbValue *src)
2069 JsonbValue *dst = palloc(sizeof(*dst));
2077 * Execute array subscript expression and convert resulting numeric item to
2078 * the integer type with truncation.
2080 static JsonPathExecResult
2081 getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2085 JsonValueList found = {0};
2086 JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2087 Datum numeric_index;
2088 bool have_error = false;
2090 if (jperIsError(res))
2093 if (JsonValueListLength(&found) != 1 ||
2094 !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2095 RETURN_ERROR(ereport(ERROR,
2096 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
2097 errmsg("jsonpath array subscript is not a single numeric value"))));
2099 numeric_index = DirectFunctionCall2(numeric_trunc,
2100 NumericGetDatum(jbv->val.numeric),
2103 *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
2107 RETURN_ERROR(ereport(ERROR,
2108 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
2109 errmsg("jsonpath array subscript is out of integer range"))));
2114 /* Save base object and its id needed for the execution of .keyvalue(). */
2115 static JsonBaseObjectInfo
2116 setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2118 JsonBaseObjectInfo baseObject = cxt->baseObject;
2120 cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2121 (JsonbContainer *) jbv->val.binary.data;
2122 cxt->baseObject.id = id;
2128 JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2132 jvl->list = list_make2(jvl->singleton, jbv);
2133 jvl->singleton = NULL;
2135 else if (!jvl->list)
2136 jvl->singleton = jbv;
2138 jvl->list = lappend(jvl->list, jbv);
2142 JsonValueListLength(const JsonValueList *jvl)
2144 return jvl->singleton ? 1 : list_length(jvl->list);
2148 JsonValueListIsEmpty(JsonValueList *jvl)
2150 return !jvl->singleton && list_length(jvl->list) <= 0;
2154 JsonValueListHead(JsonValueList *jvl)
2156 return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2160 JsonValueListGetList(JsonValueList *jvl)
2163 return list_make1(jvl->singleton);
2169 JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2173 it->value = jvl->singleton;
2176 else if (list_head(jvl->list) != NULL)
2178 it->value = (JsonbValue *) linitial(jvl->list);
2179 it->next = lnext(list_head(jvl->list));
2189 * Get the next item from the sequence advancing iterator.
2192 JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2194 JsonbValue *result = it->value;
2198 it->value = lfirst(it->next);
2199 it->next = lnext(it->next);
2210 * Initialize a binary JsonbValue with the given jsonb container.
2213 JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2215 jbv->type = jbvBinary;
2216 jbv->val.binary.data = &jb->root;
2217 jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2223 * Returns jbv* type of of JsonbValue. Note, it never returns jbvBinary as is.
2226 JsonbType(JsonbValue *jb)
2228 int type = jb->type;
2230 if (jb->type == jbvBinary)
2232 JsonbContainer *jbc = (void *) jb->val.binary.data;
2234 /* Scalars should be always extracted during jsonpath execution. */
2235 Assert(!JsonContainerIsScalar(jbc));
2237 if (JsonContainerIsObject(jbc))
2239 else if (JsonContainerIsArray(jbc))
2242 elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2248 /* Get scalar of given type or NULL on type mismatch */
2250 getScalar(JsonbValue *scalar, enum jbvType type)
2252 /* Scalars should be always extracted during jsonpath execution. */
2253 Assert(scalar->type != jbvBinary ||
2254 !JsonContainerIsScalar(scalar->val.binary.data));
2256 return scalar->type == type ? scalar : NULL;
2259 /* Construct a JSON array from the item list */
2261 wrapItemsInArray(const JsonValueList *items)
2263 JsonbParseState *ps = NULL;
2264 JsonValueListIterator it;
2267 pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2269 JsonValueListInitIterator(items, &it);
2270 while ((jbv = JsonValueListNext(items, &it)))
2271 pushJsonbValue(&ps, WJB_ELEM, jbv);
2273 return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);