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);
323 JsonValueList found = {0};
329 vars = PG_GETARG_JSONB_P(2);
330 silent = PG_GETARG_BOOL(3);
333 (void) executeJsonPath(jp, vars, jb, !silent, &found);
335 PG_FREE_IF_COPY(jb, 0);
336 PG_FREE_IF_COPY(jp, 1);
338 if (JsonValueListLength(&found) == 1)
340 JsonbValue *jbv = JsonValueListHead(&found);
342 if (jbv->type == jbvBool)
343 PG_RETURN_BOOL(jbv->val.boolean);
345 if (jbv->type == jbvNull)
351 (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
352 errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
353 errdetail("expression should return a singleton boolean")));
359 * jsonb_path_match_opr
360 * Implementation of operator "jsonb @@ jsonpath" (2-argument version of
361 * jsonb_path_match()).
364 jsonb_path_match_opr(PG_FUNCTION_ARGS)
366 /* just call the other one -- it can handle both cases */
367 return jsonb_path_match(fcinfo);
372 * Executes jsonpath for given jsonb document and returns result as
376 jsonb_path_query(PG_FUNCTION_ARGS)
378 FuncCallContext *funcctx;
383 if (SRF_IS_FIRSTCALL())
387 MemoryContext oldcontext;
390 JsonValueList found = {0};
392 funcctx = SRF_FIRSTCALL_INIT();
393 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
395 jb = PG_GETARG_JSONB_P_COPY(0);
396 jp = PG_GETARG_JSONPATH_P_COPY(1);
397 vars = PG_GETARG_JSONB_P_COPY(2);
398 silent = PG_GETARG_BOOL(3);
400 (void) executeJsonPath(jp, vars, jb, !silent, &found);
402 funcctx->user_fctx = JsonValueListGetList(&found);
404 MemoryContextSwitchTo(oldcontext);
407 funcctx = SRF_PERCALL_SETUP();
408 found = funcctx->user_fctx;
410 c = list_head(found);
413 SRF_RETURN_DONE(funcctx);
416 funcctx->user_fctx = list_delete_first(found);
418 SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
422 * jsonb_path_query_array
423 * Executes jsonpath for given jsonb document and returns result as
427 jsonb_path_query_array(FunctionCallInfo fcinfo)
429 Jsonb *jb = PG_GETARG_JSONB_P(0);
430 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
431 JsonValueList found = {0};
432 Jsonb *vars = PG_GETARG_JSONB_P(2);
433 bool silent = PG_GETARG_BOOL(3);
435 (void) executeJsonPath(jp, vars, jb, !silent, &found);
437 PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
441 * jsonb_path_query_first
442 * Executes jsonpath for given jsonb document and returns first result
443 * item. If there are no items, NULL returned.
446 jsonb_path_query_first(FunctionCallInfo fcinfo)
448 Jsonb *jb = PG_GETARG_JSONB_P(0);
449 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
450 JsonValueList found = {0};
451 Jsonb *vars = PG_GETARG_JSONB_P(2);
452 bool silent = PG_GETARG_BOOL(3);
454 (void) executeJsonPath(jp, vars, jb, !silent, &found);
456 if (JsonValueListLength(&found) >= 1)
457 PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
462 /********************Execute functions for JsonPath**************************/
465 * Interface to jsonpath executor
467 * 'path' - jsonpath to be executed
468 * 'vars' - variables to be substituted to jsonpath
469 * 'json' - target document for jsonpath evaluation
470 * 'throwErrors' - whether we should throw suppressible errors
471 * 'result' - list to store result items into
473 * Returns an error happens during processing or NULL on no error.
475 * Note, jsonb and jsonpath values should be avaliable and untoasted during
476 * work because JsonPathItem, JsonbValue and result item could have pointers
477 * into input values. If caller needs to just check if document matches
478 * jsonpath, then it doesn't provide a result arg. In this case executor
479 * works till first positive result and does not check the rest if possible.
480 * In other case it tries to find all the satisfied result items.
482 static JsonPathExecResult
483 executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
484 JsonValueList *result)
486 JsonPathExecContext cxt;
487 JsonPathExecResult res;
493 if (!JsonbExtractScalar(&json->root, &jbv))
494 JsonbInitBinary(&jbv, json);
496 if (vars && !JsonContainerIsObject(&vars->root))
499 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
500 errmsg("jsonb containing jsonpath variables "
501 "is not an object")));
505 cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
506 cxt.ignoreStructuralErrors = cxt.laxMode;
509 cxt.baseObject.jbc = NULL;
510 cxt.baseObject.id = 0;
511 cxt.lastGeneratedObjectId = vars ? 2 : 1;
512 cxt.innermostArraySize = -1;
513 cxt.throwErrors = throwErrors;
515 if (jspStrictAbsenseOfErrors(&cxt) && !result)
518 * In strict mode we must get a complete list of values to check that
519 * there are no errors at all.
521 JsonValueList vals = {0};
523 res = executeItem(&cxt, &jsp, &jbv, &vals);
525 if (jperIsError(res))
528 return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
531 res = executeItem(&cxt, &jsp, &jbv, result);
533 Assert(!throwErrors || !jperIsError(res));
539 * Execute jsonpath with automatic unwrapping of current item in lax mode.
541 static JsonPathExecResult
542 executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
543 JsonbValue *jb, JsonValueList *found)
545 return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
549 * Main jsonpath executor function: walks on jsonpath structure, finds
550 * relevant parts of jsonb and evaluates expressions over them.
551 * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
553 static JsonPathExecResult
554 executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
555 JsonbValue *jb, JsonValueList *found, bool unwrap)
558 JsonPathExecResult res = jperNotFound;
559 JsonBaseObjectInfo baseObject;
562 CHECK_FOR_INTERRUPTS();
566 /* all boolean item types: */
576 case jpiGreaterOrEqual:
581 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
583 res = appendBoolResult(cxt, jsp, found, st);
588 if (JsonbType(jb) == jbvObject)
593 key.type = jbvString;
594 key.val.string.val = jspGetString(jsp, &key.val.string.len);
596 v = findJsonbValueFromContainer(jb->val.binary.data,
601 res = executeNextItem(cxt, jsp, NULL,
604 /* free value if it was not added to found list */
605 if (jspHasNext(jsp) || !found)
608 else if (!jspIgnoreStructuralErrors(cxt))
610 StringInfoData keybuf;
615 if (!jspThrowErrors(cxt))
618 initStringInfo(&keybuf);
620 keystr = pnstrdup(key.val.string.val, key.val.string.len);
621 escape_json(&keybuf, keystr);
624 (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND), \
625 errmsg(ERRMSG_JSON_MEMBER_NOT_FOUND),
626 errdetail("JSON object does not contain key %s",
630 else if (unwrap && JsonbType(jb) == jbvArray)
631 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
632 else if (!jspIgnoreStructuralErrors(cxt))
635 RETURN_ERROR(ereport(ERROR,
636 (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND),
637 errmsg(ERRMSG_JSON_MEMBER_NOT_FOUND),
638 errdetail("jsonpath member accessor can "
639 "only be applied to an object"))));
645 baseObject = setBaseObject(cxt, jb, 0);
646 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
647 cxt->baseObject = baseObject;
651 res = executeNextItem(cxt, jsp, NULL, cxt->current,
656 if (JsonbType(jb) == jbvArray)
658 bool hasNext = jspGetNext(jsp, &elem);
660 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
661 jb, found, jspAutoUnwrap(cxt));
663 else if (jspAutoWrap(cxt))
664 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
665 else if (!jspIgnoreStructuralErrors(cxt))
666 RETURN_ERROR(ereport(ERROR,
667 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
668 errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
669 errdetail("jsonpath wildcard array accessor "
670 "can only be applied to an array"))));
674 if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
676 int innermostArraySize = cxt->innermostArraySize;
678 int size = JsonbArraySize(jb);
679 bool singleton = size < 0;
680 bool hasNext = jspGetNext(jsp, &elem);
685 cxt->innermostArraySize = size; /* for LAST evaluation */
687 for (i = 0; i < jsp->content.array.nelems; i++)
694 bool range = jspGetArraySubscript(jsp, &from,
697 res = getArrayIndex(cxt, &from, jb, &index_from);
699 if (jperIsError(res))
704 res = getArrayIndex(cxt, &to, jb, &index_to);
706 if (jperIsError(res))
710 index_to = index_from;
712 if (!jspIgnoreStructuralErrors(cxt) &&
714 index_from > index_to ||
716 RETURN_ERROR(ereport(ERROR,
717 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
718 errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
719 errdetail("jsonpath array subscript is "
725 if (index_to >= size)
730 for (index = index_from; index <= index_to; index++)
742 v = getIthJsonbValueFromContainer(jb->val.binary.data,
751 if (!hasNext && !found)
754 res = executeNextItem(cxt, jsp, &elem, v, found,
757 if (jperIsError(res))
760 if (res == jperOk && !found)
764 if (jperIsError(res))
767 if (res == jperOk && !found)
771 cxt->innermostArraySize = innermostArraySize;
773 else if (!jspIgnoreStructuralErrors(cxt))
775 RETURN_ERROR(ereport(ERROR,
776 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
777 errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
778 errdetail("jsonpath array accessor can "
779 "only be applied to an array"))));
788 bool hasNext = jspGetNext(jsp, &elem);
790 if (cxt->innermostArraySize < 0)
791 elog(ERROR, "evaluating jsonpath LAST outside of "
794 if (!hasNext && !found)
800 last = cxt->innermostArraySize - 1;
802 lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
804 lastjbv->type = jbvNumeric;
805 lastjbv->val.numeric =
806 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
807 Int32GetDatum(last)));
809 res = executeNextItem(cxt, jsp, &elem,
810 lastjbv, found, hasNext);
815 if (JsonbType(jb) == jbvObject)
817 bool hasNext = jspGetNext(jsp, &elem);
819 if (jb->type != jbvBinary)
820 elog(ERROR, "invalid jsonb object type: %d", jb->type);
822 return executeAnyItem
823 (cxt, hasNext ? &elem : NULL,
824 jb->val.binary.data, found, 1, 1, 1,
825 false, jspAutoUnwrap(cxt));
827 else if (unwrap && JsonbType(jb) == jbvArray)
828 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
829 else if (!jspIgnoreStructuralErrors(cxt))
832 RETURN_ERROR(ereport(ERROR,
833 (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
834 errmsg(ERRMSG_JSON_OBJECT_NOT_FOUND),
835 errdetail("jsonpath wildcard member accessor "
836 "can only be applied to an object"))));
841 return executeBinaryArithmExpr(cxt, jsp, jb,
842 numeric_add_opt_error, found);
845 return executeBinaryArithmExpr(cxt, jsp, jb,
846 numeric_sub_opt_error, found);
849 return executeBinaryArithmExpr(cxt, jsp, jb,
850 numeric_mul_opt_error, found);
853 return executeBinaryArithmExpr(cxt, jsp, jb,
854 numeric_div_opt_error, found);
857 return executeBinaryArithmExpr(cxt, jsp, jb,
858 numeric_mod_opt_error, found);
861 return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
864 return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
871 if (unwrap && JsonbType(jb) == jbvArray)
872 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
875 jspGetArg(jsp, &elem);
876 st = executeNestedBoolItem(cxt, &elem, jb);
880 res = executeNextItem(cxt, jsp, NULL,
887 bool hasNext = jspGetNext(jsp, &elem);
889 /* first try without any intermediate steps */
890 if (jsp->content.anybounds.first == 0)
892 bool savedIgnoreStructuralErrors;
894 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
895 cxt->ignoreStructuralErrors = true;
896 res = executeNextItem(cxt, jsp, &elem,
898 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
900 if (res == jperOk && !found)
904 if (jb->type == jbvBinary)
906 (cxt, hasNext ? &elem : NULL,
907 jb->val.binary.data, found,
909 jsp->content.anybounds.first,
910 jsp->content.anybounds.last,
911 true, jspAutoUnwrap(cxt));
923 bool hasNext = jspGetNext(jsp, &elem);
925 if (!hasNext && !found)
927 res = jperOk; /* skip evaluation */
931 v = hasNext ? &vbuf : palloc(sizeof(*v));
933 baseObject = cxt->baseObject;
934 getJsonPathItem(cxt, jsp, v);
936 res = executeNextItem(cxt, jsp, &elem,
938 cxt->baseObject = baseObject;
944 JsonbValue *jbv = palloc(sizeof(*jbv));
946 jbv->type = jbvString;
947 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
948 jbv->val.string.len = strlen(jbv->val.string.val);
950 res = executeNextItem(cxt, jsp, NULL, jbv,
957 int size = JsonbArraySize(jb);
961 if (!jspAutoWrap(cxt))
963 if (!jspIgnoreStructuralErrors(cxt))
964 RETURN_ERROR(ereport(ERROR,
965 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
966 errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
967 errdetail("jsonpath item method .%s() "
968 "can only be applied to an array",
969 jspOperationName(jsp->type)))));
976 jb = palloc(sizeof(*jb));
978 jb->type = jbvNumeric;
980 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
981 Int32GetDatum(size)));
983 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
988 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
992 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
996 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
1003 if (unwrap && JsonbType(jb) == jbvArray)
1004 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1007 if (jb->type == jbvNumeric)
1009 char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1010 NumericGetDatum(jb->val.numeric)));
1011 bool have_error = false;
1013 (void) float8in_internal_opt_error(tmp,
1020 RETURN_ERROR(ereport(ERROR,
1021 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1022 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1023 errdetail("jsonpath item method .%s() "
1024 "can only be applied to "
1026 jspOperationName(jsp->type)))));
1029 else if (jb->type == jbvString)
1031 /* cast string as double */
1033 char *tmp = pnstrdup(jb->val.string.val,
1034 jb->val.string.len);
1035 bool have_error = false;
1037 val = float8in_internal_opt_error(tmp,
1043 if (have_error || isinf(val))
1044 RETURN_ERROR(ereport(ERROR,
1045 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1046 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1047 errdetail("jsonpath item method .%s() can "
1048 "only be applied to a numeric value",
1049 jspOperationName(jsp->type)))));
1052 jb->type = jbvNumeric;
1053 jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1054 Float8GetDatum(val)));
1058 if (res == jperNotFound)
1059 RETURN_ERROR(ereport(ERROR,
1060 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1061 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1062 errdetail("jsonpath item method .%s() "
1063 "can only be applied to a "
1064 "string or numeric value",
1065 jspOperationName(jsp->type)))));
1067 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1072 if (unwrap && JsonbType(jb) == jbvArray)
1073 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1075 return executeKeyValueMethod(cxt, jsp, jb, found);
1078 elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1085 * Unwrap current array item and execute jsonpath for each of its elements.
1087 static JsonPathExecResult
1088 executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1089 JsonbValue *jb, JsonValueList *found,
1090 bool unwrapElements)
1092 if (jb->type != jbvBinary)
1094 Assert(jb->type != jbvArray);
1095 elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1098 return executeAnyItem
1099 (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1100 false, unwrapElements);
1104 * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1107 static JsonPathExecResult
1108 executeNextItem(JsonPathExecContext *cxt,
1109 JsonPathItem *cur, JsonPathItem *next,
1110 JsonbValue *v, JsonValueList *found, bool copy)
1116 hasNext = next != NULL;
1118 hasNext = jspHasNext(cur);
1122 hasNext = jspGetNext(cur, next);
1126 return executeItem(cxt, next, v, found);
1129 JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1135 * Same as executeItem(), but when "unwrap == true" automatically unwraps
1136 * each array item from the resulting sequence in lax mode.
1138 static JsonPathExecResult
1139 executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1140 JsonbValue *jb, bool unwrap,
1141 JsonValueList *found)
1143 if (unwrap && jspAutoUnwrap(cxt))
1145 JsonValueList seq = {0};
1146 JsonValueListIterator it;
1147 JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1150 if (jperIsError(res))
1153 JsonValueListInitIterator(&seq, &it);
1154 while ((item = JsonValueListNext(&seq, &it)))
1156 Assert(item->type != jbvArray);
1158 if (JsonbType(item) == jbvArray)
1159 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1161 JsonValueListAppend(found, item);
1167 return executeItem(cxt, jsp, jb, found);
1171 * Same as executeItemOptUnwrapResult(), but with error suppression.
1173 static JsonPathExecResult
1174 executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1176 JsonbValue *jb, bool unwrap,
1177 JsonValueList *found)
1179 JsonPathExecResult res;
1180 bool throwErrors = cxt->throwErrors;
1182 cxt->throwErrors = false;
1183 res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1184 cxt->throwErrors = throwErrors;
1189 /* Execute boolean-valued jsonpath expression. */
1191 executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1192 JsonbValue *jb, bool canHaveNext)
1199 if (!canHaveNext && jspHasNext(jsp))
1200 elog(ERROR, "boolean jsonpath item cannot have next item");
1205 jspGetLeftArg(jsp, &larg);
1206 res = executeBoolItem(cxt, &larg, jb, false);
1208 if (res == jpbFalse)
1212 * SQL/JSON says that we should check second arg in case of
1216 jspGetRightArg(jsp, &rarg);
1217 res2 = executeBoolItem(cxt, &rarg, jb, false);
1219 return res2 == jpbTrue ? res : res2;
1222 jspGetLeftArg(jsp, &larg);
1223 res = executeBoolItem(cxt, &larg, jb, false);
1228 jspGetRightArg(jsp, &rarg);
1229 res2 = executeBoolItem(cxt, &rarg, jb, false);
1231 return res2 == jpbFalse ? res : res2;
1234 jspGetArg(jsp, &larg);
1236 res = executeBoolItem(cxt, &larg, jb, false);
1238 if (res == jpbUnknown)
1241 return res == jpbTrue ? jpbFalse : jpbTrue;
1244 jspGetArg(jsp, &larg);
1245 res = executeBoolItem(cxt, &larg, jb, false);
1246 return res == jpbUnknown ? jpbTrue : jpbFalse;
1252 case jpiLessOrEqual:
1253 case jpiGreaterOrEqual:
1254 jspGetLeftArg(jsp, &larg);
1255 jspGetRightArg(jsp, &rarg);
1256 return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1257 executeComparison, NULL);
1259 case jpiStartsWith: /* 'whole STARTS WITH initial' */
1260 jspGetLeftArg(jsp, &larg); /* 'whole' */
1261 jspGetRightArg(jsp, &rarg); /* 'initial' */
1262 return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1263 executeStartsWith, NULL);
1265 case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1268 * 'expr' is a sequence-returning expression. 'pattern' is a
1269 * regex string literal. SQL/JSON standard requires XQuery
1270 * regexes, but we use Postgres regexes here. 'flags' is a
1271 * string literal converted to integer flags at compile-time.
1273 JsonLikeRegexContext lrcxt = {0};
1275 jspInitByBuffer(&larg, jsp->base,
1276 jsp->content.like_regex.expr);
1278 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1279 executeLikeRegex, &lrcxt);
1283 jspGetArg(jsp, &larg);
1285 if (jspStrictAbsenseOfErrors(cxt))
1288 * In strict mode we must get a complete list of values to
1289 * check that there are no errors at all.
1291 JsonValueList vals = {0};
1292 JsonPathExecResult res =
1293 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1296 if (jperIsError(res))
1299 return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1303 JsonPathExecResult res =
1304 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1307 if (jperIsError(res))
1310 return res == jperOk ? jpbTrue : jpbFalse;
1314 elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1320 * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1321 * item onto the stack.
1324 executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1330 prev = cxt->current;
1332 res = executeBoolItem(cxt, jsp, jb, false);
1333 cxt->current = prev;
1339 * Implementation of several jsonpath nodes:
1340 * - jpiAny (.** accessor),
1341 * - jpiAnyKey (.* accessor),
1342 * - jpiAnyArray ([*] accessor)
1344 static JsonPathExecResult
1345 executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1346 JsonValueList *found, uint32 level, uint32 first, uint32 last,
1347 bool ignoreStructuralErrors, bool unwrapNext)
1349 JsonPathExecResult res = jperNotFound;
1354 check_stack_depth();
1359 it = JsonbIteratorInit(jbc);
1362 * Recursively iterate over jsonb objects/arrays
1364 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1368 r = JsonbIteratorNext(&it, &v, true);
1369 Assert(r == WJB_VALUE);
1372 if (r == WJB_VALUE || r == WJB_ELEM)
1375 if (level >= first ||
1376 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1377 v.type != jbvBinary)) /* leaves only requested */
1379 /* check expression */
1382 if (ignoreStructuralErrors)
1384 bool savedIgnoreStructuralErrors;
1386 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1387 cxt->ignoreStructuralErrors = true;
1388 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1389 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1392 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1394 if (jperIsError(res))
1397 if (res == jperOk && !found)
1401 JsonValueListAppend(found, copyJsonbValue(&v));
1406 if (level < last && v.type == jbvBinary)
1408 res = executeAnyItem
1409 (cxt, jsp, v.val.binary.data, found,
1410 level + 1, first, last,
1411 ignoreStructuralErrors, unwrapNext);
1413 if (jperIsError(res))
1416 if (res == jperOk && found == NULL)
1426 * Execute unary or binary predicate.
1428 * Predicates have existence semantics, because their operands are item
1429 * sequences. Pairs of items from the left and right operand's sequences are
1430 * checked. TRUE returned only if any pair satisfying the condition is found.
1431 * In strict mode, even if the desired pair has already been found, all pairs
1432 * still need to be examined to check the absence of errors. If any error
1433 * occurs, UNKNOWN (analogous to SQL NULL) is returned.
1436 executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
1437 JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
1438 bool unwrapRightArg, JsonPathPredicateCallback exec,
1441 JsonPathExecResult res;
1442 JsonValueListIterator lseqit;
1443 JsonValueList lseq = {0};
1444 JsonValueList rseq = {0};
1449 /* Left argument is always auto-unwrapped. */
1450 res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
1451 if (jperIsError(res))
1456 /* Right argument is conditionally auto-unwrapped. */
1457 res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
1458 unwrapRightArg, &rseq);
1459 if (jperIsError(res))
1463 JsonValueListInitIterator(&lseq, &lseqit);
1464 while ((lval = JsonValueListNext(&lseq, &lseqit)))
1466 JsonValueListIterator rseqit;
1470 JsonValueListInitIterator(&rseq, &rseqit);
1472 rval = JsonValueListNext(&rseq, &rseqit);
1476 /* Loop over right arg sequence or do single pass otherwise */
1477 while (rarg ? (rval != NULL) : first)
1479 JsonPathBool res = exec(pred, lval, rval, param);
1481 if (res == jpbUnknown)
1483 if (jspStrictAbsenseOfErrors(cxt))
1488 else if (res == jpbTrue)
1490 if (!jspStrictAbsenseOfErrors(cxt))
1498 rval = JsonValueListNext(&rseq, &rseqit);
1502 if (found) /* possible only in strict mode */
1505 if (error) /* possible only in lax mode */
1512 * Execute binary arithmetic expression on singleton numeric operands.
1513 * Array operands are automatically unwrapped in lax mode.
1515 static JsonPathExecResult
1516 executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1517 JsonbValue *jb, BinaryArithmFunc func,
1518 JsonValueList *found)
1520 JsonPathExecResult jper;
1522 JsonValueList lseq = {0};
1523 JsonValueList rseq = {0};
1528 jspGetLeftArg(jsp, &elem);
1531 * XXX: By standard only operands of multiplicative expressions are
1532 * unwrapped. We extend it to other binary arithmetics expressions too.
1534 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
1535 if (jperIsError(jper))
1538 jspGetRightArg(jsp, &elem);
1540 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
1541 if (jperIsError(jper))
1544 if (JsonValueListLength(&lseq) != 1 ||
1545 !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
1546 RETURN_ERROR(ereport(ERROR,
1547 (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
1548 errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
1549 errdetail("left operand of binary jsonpath operator %s "
1550 "is not a singleton numeric value",
1551 jspOperationName(jsp->type)))));
1553 if (JsonValueListLength(&rseq) != 1 ||
1554 !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
1555 RETURN_ERROR(ereport(ERROR,
1556 (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
1557 errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
1558 errdetail("right operand of binary jsonpath operator %s "
1559 "is not a singleton numeric value",
1560 jspOperationName(jsp->type)))));
1562 if (jspThrowErrors(cxt))
1564 res = func(lval->val.numeric, rval->val.numeric, NULL);
1570 res = func(lval->val.numeric, rval->val.numeric, &error);
1576 if (!jspGetNext(jsp, &elem) && !found)
1579 lval = palloc(sizeof(*lval));
1580 lval->type = jbvNumeric;
1581 lval->val.numeric = res;
1583 return executeNextItem(cxt, jsp, &elem, lval, found, false);
1587 * Execute unary arithmetic expression for each numeric item in its operand's
1588 * sequence. Array operand is automatically unwrapped in lax mode.
1590 static JsonPathExecResult
1591 executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1592 JsonbValue *jb, PGFunction func, JsonValueList *found)
1594 JsonPathExecResult jper;
1595 JsonPathExecResult jper2;
1597 JsonValueList seq = {0};
1598 JsonValueListIterator it;
1602 jspGetArg(jsp, &elem);
1603 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
1605 if (jperIsError(jper))
1608 jper = jperNotFound;
1610 hasNext = jspGetNext(jsp, &elem);
1612 JsonValueListInitIterator(&seq, &it);
1613 while ((val = JsonValueListNext(&seq, &it)))
1615 if ((val = getScalar(val, jbvNumeric)))
1617 if (!found && !hasNext)
1622 if (!found && !hasNext)
1623 continue; /* skip non-numerics processing */
1625 RETURN_ERROR(ereport(ERROR,
1626 (errcode(ERRCODE_JSON_NUMBER_NOT_FOUND),
1627 errmsg(ERRMSG_JSON_NUMBER_NOT_FOUND),
1628 errdetail("operand of unary jsonpath operator %s "
1629 "is not a numeric value",
1630 jspOperationName(jsp->type)))));
1635 DatumGetNumeric(DirectFunctionCall1(func,
1636 NumericGetDatum(val->val.numeric)));
1638 jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
1640 if (jperIsError(jper2))
1643 if (jper2 == jperOk)
1655 * STARTS_WITH predicate callback.
1657 * Check if the 'whole' string starts from 'initial' string.
1660 executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
1663 if (!(whole = getScalar(whole, jbvString)))
1664 return jpbUnknown; /* error */
1666 if (!(initial = getScalar(initial, jbvString)))
1667 return jpbUnknown; /* error */
1669 if (whole->val.string.len >= initial->val.string.len &&
1670 !memcmp(whole->val.string.val,
1671 initial->val.string.val,
1672 initial->val.string.len))
1679 * LIKE_REGEX predicate callback.
1681 * Check if the string matches regex pattern.
1684 executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
1687 JsonLikeRegexContext *cxt = param;
1689 if (!(str = getScalar(str, jbvString)))
1692 /* Cache regex text and converted flags. */
1695 uint32 flags = jsp->content.like_regex.flags;
1698 cstring_to_text_with_len(jsp->content.like_regex.pattern,
1699 jsp->content.like_regex.patternlen);
1701 /* Convert regex flags. */
1702 cxt->cflags = REG_ADVANCED;
1704 if (flags & JSP_REGEX_ICASE)
1705 cxt->cflags |= REG_ICASE;
1706 if (flags & JSP_REGEX_MLINE)
1707 cxt->cflags |= REG_NEWLINE;
1708 if (flags & JSP_REGEX_SLINE)
1709 cxt->cflags &= ~REG_NEWLINE;
1710 if (flags & JSP_REGEX_WSPACE)
1711 cxt->cflags |= REG_EXPANDED;
1714 if (RE_compile_and_execute(cxt->regex, str->val.string.val,
1715 str->val.string.len,
1716 cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
1723 * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
1724 * user function 'func'.
1726 static JsonPathExecResult
1727 executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1728 JsonbValue *jb, bool unwrap, PGFunction func,
1729 JsonValueList *found)
1734 if (unwrap && JsonbType(jb) == jbvArray)
1735 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1737 if (!(jb = getScalar(jb, jbvNumeric)))
1738 RETURN_ERROR(ereport(ERROR,
1739 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1740 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1741 errdetail("jsonpath item method .%s() can only "
1742 "be applied to a numeric value",
1743 jspOperationName(jsp->type)))));
1745 datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
1747 if (!jspGetNext(jsp, &next) && !found)
1750 jb = palloc(sizeof(*jb));
1751 jb->type = jbvNumeric;
1752 jb->val.numeric = DatumGetNumeric(datum);
1754 return executeNextItem(cxt, jsp, &next, jb, found, false);
1758 * Implementation of .keyvalue() method.
1760 * .keyvalue() method returns a sequence of object's key-value pairs in the
1761 * following format: '{ "key": key, "value": value, "id": id }'.
1763 * "id" field is an object identifier which is constructed from the two parts:
1764 * base object id and its binary offset in base object's jsonb:
1765 * id = 10000000000 * base_object_id + obj_offset_in_base_object
1767 * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1768 * (maximal offset in jsonb). Decimal multiplier is used here to improve the
1769 * readability of identifiers.
1771 * Base object is usually a root object of the path: context item '$' or path
1772 * variable '$var', literals can't produce objects for now. But if the path
1773 * contains generated objects (.keyvalue() itself, for example), then they
1774 * become base object for the subsequent .keyvalue().
1776 * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
1777 * of variables (see getJsonPathVariable()). Ids for generated objects
1778 * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
1780 static JsonPathExecResult
1781 executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1782 JsonbValue *jb, JsonValueList *found)
1784 JsonPathExecResult res = jperNotFound;
1786 JsonbContainer *jbc;
1794 JsonbIteratorToken tok;
1798 if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
1799 RETURN_ERROR(ereport(ERROR,
1800 (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
1801 errmsg(ERRMSG_JSON_OBJECT_NOT_FOUND),
1802 errdetail("jsonpath item method .%s() "
1803 "can only be applied to an object",
1804 jspOperationName(jsp->type)))));
1806 jbc = jb->val.binary.data;
1808 if (!JsonContainerSize(jbc))
1809 return jperNotFound; /* no key-value pairs */
1811 hasNext = jspGetNext(jsp, &next);
1813 keystr.type = jbvString;
1814 keystr.val.string.val = "key";
1815 keystr.val.string.len = 3;
1817 valstr.type = jbvString;
1818 valstr.val.string.val = "value";
1819 valstr.val.string.len = 5;
1821 idstr.type = jbvString;
1822 idstr.val.string.val = "id";
1823 idstr.val.string.len = 2;
1825 /* construct object id from its base object and offset inside that */
1826 id = jb->type != jbvBinary ? 0 :
1827 (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
1828 id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
1830 idval.type = jbvNumeric;
1831 idval.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
1832 Int64GetDatum(id)));
1834 it = JsonbIteratorInit(jbc);
1836 while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
1838 JsonBaseObjectInfo baseObject;
1840 JsonbParseState *ps;
1849 if (!hasNext && !found)
1852 tok = JsonbIteratorNext(&it, &val, true);
1853 Assert(tok == WJB_VALUE);
1856 pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
1858 pushJsonbValue(&ps, WJB_KEY, &keystr);
1859 pushJsonbValue(&ps, WJB_VALUE, &key);
1861 pushJsonbValue(&ps, WJB_KEY, &valstr);
1862 pushJsonbValue(&ps, WJB_VALUE, &val);
1864 pushJsonbValue(&ps, WJB_KEY, &idstr);
1865 pushJsonbValue(&ps, WJB_VALUE, &idval);
1867 keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
1869 jsonb = JsonbValueToJsonb(keyval);
1871 JsonbInitBinary(&obj, jsonb);
1873 baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
1875 res = executeNextItem(cxt, jsp, &next, &obj, found, true);
1877 cxt->baseObject = baseObject;
1879 if (jperIsError(res))
1882 if (res == jperOk && !found)
1890 * Convert boolean execution status 'res' to a boolean JSON item and execute
1893 static JsonPathExecResult
1894 appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1895 JsonValueList *found, JsonPathBool res)
1900 if (!jspGetNext(jsp, &next) && !found)
1901 return jperOk; /* found singleton boolean value */
1903 if (res == jpbUnknown)
1910 jbv.val.boolean = res == jpbTrue;
1913 return executeNextItem(cxt, jsp, &next, &jbv, found, true);
1917 * Convert jsonpath's scalar or variable node to actual jsonb value.
1919 * If node is a variable then its id returned, otherwise 0 returned.
1922 getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
1928 value->type = jbvNull;
1931 value->type = jbvBool;
1932 value->val.boolean = jspGetBool(item);
1935 value->type = jbvNumeric;
1936 value->val.numeric = jspGetNumeric(item);
1939 value->type = jbvString;
1940 value->val.string.val = jspGetString(item,
1941 &value->val.string.len);
1944 getJsonPathVariable(cxt, item, cxt->vars, value);
1947 elog(ERROR, "unexpected jsonpath item type");
1952 * Get the value of variable passed to jsonpath executor
1955 getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
1956 Jsonb *vars, JsonbValue *value)
1965 value->type = jbvNull;
1969 Assert(variable->type == jpiVariable);
1970 varName = jspGetString(variable, &varNameLength);
1971 tmp.type = jbvString;
1972 tmp.val.string.val = varName;
1973 tmp.val.string.len = varNameLength;
1975 v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
1985 (errcode(ERRCODE_UNDEFINED_OBJECT),
1986 errmsg("cannot find jsonpath variable '%s'",
1987 pnstrdup(varName, varNameLength))));
1990 JsonbInitBinary(&tmp, vars);
1991 setBaseObject(cxt, &tmp, 1);
1994 /**************** Support functions for JsonPath execution *****************/
1997 * Returns the size of an array item, or -1 if item is not an array.
2000 JsonbArraySize(JsonbValue *jb)
2002 Assert(jb->type != jbvArray);
2004 if (jb->type == jbvBinary)
2006 JsonbContainer *jbc = jb->val.binary.data;
2008 if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
2009 return JsonContainerSize(jbc);
2015 /* Comparison predicate callback. */
2017 executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
2019 return compareItems(cmp->type, lv, rv);
2023 * Compare two SQL/JSON items using comparison operation 'op'.
2026 compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2)
2031 if (jb1->type != jb2->type)
2033 if (jb1->type == jbvNull || jb2->type == jbvNull)
2036 * Equality and order comparison of nulls to non-nulls returns
2037 * always false, but inequality comparison returns true.
2039 return op == jpiNotEqual ? jpbTrue : jpbFalse;
2041 /* Non-null items of different types are not comparable. */
2051 cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2052 jb1->val.boolean ? 1 : -1;
2055 cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2059 return jb1->val.string.len != jb2->val.string.len ||
2060 memcmp(jb1->val.string.val,
2061 jb2->val.string.val,
2062 jb1->val.string.len) ? jpbFalse : jpbTrue;
2064 cmp = varstr_cmp(jb1->val.string.val, jb1->val.string.len,
2065 jb2->val.string.val, jb2->val.string.len,
2066 DEFAULT_COLLATION_OID);
2072 return jpbUnknown; /* non-scalars are not comparable */
2075 elog(ERROR, "invalid jsonb value type %d", jb1->type);
2092 case jpiLessOrEqual:
2095 case jpiGreaterOrEqual:
2099 elog(ERROR, "unrecognized jsonpath operation: %d", op);
2103 return res ? jpbTrue : jpbFalse;
2106 /* Compare two numerics */
2108 compareNumeric(Numeric a, Numeric b)
2110 return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2112 NumericGetDatum(b)));
2116 copyJsonbValue(JsonbValue *src)
2118 JsonbValue *dst = palloc(sizeof(*dst));
2126 * Execute array subscript expression and convert resulting numeric item to
2127 * the integer type with truncation.
2129 static JsonPathExecResult
2130 getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2134 JsonValueList found = {0};
2135 JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2136 Datum numeric_index;
2137 bool have_error = false;
2139 if (jperIsError(res))
2142 if (JsonValueListLength(&found) != 1 ||
2143 !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2144 RETURN_ERROR(ereport(ERROR,
2145 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
2146 errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
2147 errdetail("jsonpath array subscript is not a "
2148 "singleton numeric value"))));
2150 numeric_index = DirectFunctionCall2(numeric_trunc,
2151 NumericGetDatum(jbv->val.numeric),
2154 *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
2158 RETURN_ERROR(ereport(ERROR,
2159 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
2160 errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
2161 errdetail("jsonpath array subscript is "
2162 "out of integer range"))));
2167 /* Save base object and its id needed for the execution of .keyvalue(). */
2168 static JsonBaseObjectInfo
2169 setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2171 JsonBaseObjectInfo baseObject = cxt->baseObject;
2173 cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2174 (JsonbContainer *) jbv->val.binary.data;
2175 cxt->baseObject.id = id;
2181 JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2185 jvl->list = list_make2(jvl->singleton, jbv);
2186 jvl->singleton = NULL;
2188 else if (!jvl->list)
2189 jvl->singleton = jbv;
2191 jvl->list = lappend(jvl->list, jbv);
2195 JsonValueListLength(const JsonValueList *jvl)
2197 return jvl->singleton ? 1 : list_length(jvl->list);
2201 JsonValueListIsEmpty(JsonValueList *jvl)
2203 return !jvl->singleton && list_length(jvl->list) <= 0;
2207 JsonValueListHead(JsonValueList *jvl)
2209 return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2213 JsonValueListGetList(JsonValueList *jvl)
2216 return list_make1(jvl->singleton);
2222 JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2226 it->value = jvl->singleton;
2229 else if (list_head(jvl->list) != NULL)
2231 it->value = (JsonbValue *) linitial(jvl->list);
2232 it->next = lnext(list_head(jvl->list));
2242 * Get the next item from the sequence advancing iterator.
2245 JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2247 JsonbValue *result = it->value;
2251 it->value = lfirst(it->next);
2252 it->next = lnext(it->next);
2263 * Initialize a binary JsonbValue with the given jsonb container.
2266 JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2268 jbv->type = jbvBinary;
2269 jbv->val.binary.data = &jb->root;
2270 jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2276 * Returns jbv* type of of JsonbValue. Note, it never returns jbvBinary as is.
2279 JsonbType(JsonbValue *jb)
2281 int type = jb->type;
2283 if (jb->type == jbvBinary)
2285 JsonbContainer *jbc = (void *) jb->val.binary.data;
2287 /* Scalars should be always extracted during jsonpath execution. */
2288 Assert(!JsonContainerIsScalar(jbc));
2290 if (JsonContainerIsObject(jbc))
2292 else if (JsonContainerIsArray(jbc))
2295 elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2301 /* Get scalar of given type or NULL on type mismatch */
2303 getScalar(JsonbValue *scalar, enum jbvType type)
2305 /* Scalars should be always extracted during jsonpath execution. */
2306 Assert(scalar->type != jbvBinary ||
2307 !JsonContainerIsScalar(scalar->val.binary.data));
2309 return scalar->type == type ? scalar : NULL;
2312 /* Construct a JSON array from the item list */
2314 wrapItemsInArray(const JsonValueList *items)
2316 JsonbParseState *ps = NULL;
2317 JsonValueListIterator it;
2320 pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2322 JsonValueListInitIterator(items, &it);
2323 while ((jbv = JsonValueListNext(items, &it)))
2324 pushJsonbValue(&ps, WJB_ELEM, jbv);
2326 return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);