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 if a recoverable error happens during processing, or NULL
476 * Note, jsonb and jsonpath values should be available and untoasted during
477 * work because JsonPathItem, JsonbValue and result item could have pointers
478 * into input values. If caller needs to just check if document matches
479 * jsonpath, then it doesn't provide a result arg. In this case executor
480 * works till first positive result and does not check the rest if possible.
481 * In other case it tries to find all the satisfied result items.
483 static JsonPathExecResult
484 executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
485 JsonValueList *result)
487 JsonPathExecContext cxt;
488 JsonPathExecResult res;
494 if (!JsonbExtractScalar(&json->root, &jbv))
495 JsonbInitBinary(&jbv, json);
497 if (vars && !JsonContainerIsObject(&vars->root))
500 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
501 errmsg("jsonb containing jsonpath variables "
502 "is not an object")));
506 cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
507 cxt.ignoreStructuralErrors = cxt.laxMode;
510 cxt.baseObject.jbc = NULL;
511 cxt.baseObject.id = 0;
512 cxt.lastGeneratedObjectId = vars ? 2 : 1;
513 cxt.innermostArraySize = -1;
514 cxt.throwErrors = throwErrors;
516 if (jspStrictAbsenseOfErrors(&cxt) && !result)
519 * In strict mode we must get a complete list of values to check that
520 * there are no errors at all.
522 JsonValueList vals = {0};
524 res = executeItem(&cxt, &jsp, &jbv, &vals);
526 if (jperIsError(res))
529 return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
532 res = executeItem(&cxt, &jsp, &jbv, result);
534 Assert(!throwErrors || !jperIsError(res));
540 * Execute jsonpath with automatic unwrapping of current item in lax mode.
542 static JsonPathExecResult
543 executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
544 JsonbValue *jb, JsonValueList *found)
546 return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
550 * Main jsonpath executor function: walks on jsonpath structure, finds
551 * relevant parts of jsonb and evaluates expressions over them.
552 * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
554 static JsonPathExecResult
555 executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
556 JsonbValue *jb, JsonValueList *found, bool unwrap)
559 JsonPathExecResult res = jperNotFound;
560 JsonBaseObjectInfo baseObject;
563 CHECK_FOR_INTERRUPTS();
567 /* all boolean item types: */
577 case jpiGreaterOrEqual:
582 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
584 res = appendBoolResult(cxt, jsp, found, st);
589 if (JsonbType(jb) == jbvObject)
594 key.type = jbvString;
595 key.val.string.val = jspGetString(jsp, &key.val.string.len);
597 v = findJsonbValueFromContainer(jb->val.binary.data,
602 res = executeNextItem(cxt, jsp, NULL,
605 /* free value if it was not added to found list */
606 if (jspHasNext(jsp) || !found)
609 else if (!jspIgnoreStructuralErrors(cxt))
611 StringInfoData keybuf;
616 if (!jspThrowErrors(cxt))
619 initStringInfo(&keybuf);
621 keystr = pnstrdup(key.val.string.val, key.val.string.len);
622 escape_json(&keybuf, keystr);
625 (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND), \
626 errmsg(ERRMSG_JSON_MEMBER_NOT_FOUND),
627 errdetail("JSON object does not contain key %s",
631 else if (unwrap && JsonbType(jb) == jbvArray)
632 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
633 else if (!jspIgnoreStructuralErrors(cxt))
636 RETURN_ERROR(ereport(ERROR,
637 (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND),
638 errmsg(ERRMSG_JSON_MEMBER_NOT_FOUND),
639 errdetail("jsonpath member accessor can "
640 "only be applied to an object"))));
646 baseObject = setBaseObject(cxt, jb, 0);
647 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
648 cxt->baseObject = baseObject;
652 res = executeNextItem(cxt, jsp, NULL, cxt->current,
657 if (JsonbType(jb) == jbvArray)
659 bool hasNext = jspGetNext(jsp, &elem);
661 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
662 jb, found, jspAutoUnwrap(cxt));
664 else if (jspAutoWrap(cxt))
665 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
666 else if (!jspIgnoreStructuralErrors(cxt))
667 RETURN_ERROR(ereport(ERROR,
668 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
669 errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
670 errdetail("jsonpath wildcard array accessor "
671 "can only be applied to an array"))));
675 if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
677 int innermostArraySize = cxt->innermostArraySize;
679 int size = JsonbArraySize(jb);
680 bool singleton = size < 0;
681 bool hasNext = jspGetNext(jsp, &elem);
686 cxt->innermostArraySize = size; /* for LAST evaluation */
688 for (i = 0; i < jsp->content.array.nelems; i++)
695 bool range = jspGetArraySubscript(jsp, &from,
698 res = getArrayIndex(cxt, &from, jb, &index_from);
700 if (jperIsError(res))
705 res = getArrayIndex(cxt, &to, jb, &index_to);
707 if (jperIsError(res))
711 index_to = index_from;
713 if (!jspIgnoreStructuralErrors(cxt) &&
715 index_from > index_to ||
717 RETURN_ERROR(ereport(ERROR,
718 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
719 errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
720 errdetail("jsonpath array subscript is "
726 if (index_to >= size)
731 for (index = index_from; index <= index_to; index++)
743 v = getIthJsonbValueFromContainer(jb->val.binary.data,
752 if (!hasNext && !found)
755 res = executeNextItem(cxt, jsp, &elem, v, found,
758 if (jperIsError(res))
761 if (res == jperOk && !found)
765 if (jperIsError(res))
768 if (res == jperOk && !found)
772 cxt->innermostArraySize = innermostArraySize;
774 else if (!jspIgnoreStructuralErrors(cxt))
776 RETURN_ERROR(ereport(ERROR,
777 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
778 errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
779 errdetail("jsonpath array accessor can "
780 "only be applied to an array"))));
789 bool hasNext = jspGetNext(jsp, &elem);
791 if (cxt->innermostArraySize < 0)
792 elog(ERROR, "evaluating jsonpath LAST outside of "
795 if (!hasNext && !found)
801 last = cxt->innermostArraySize - 1;
803 lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
805 lastjbv->type = jbvNumeric;
806 lastjbv->val.numeric =
807 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
808 Int32GetDatum(last)));
810 res = executeNextItem(cxt, jsp, &elem,
811 lastjbv, found, hasNext);
816 if (JsonbType(jb) == jbvObject)
818 bool hasNext = jspGetNext(jsp, &elem);
820 if (jb->type != jbvBinary)
821 elog(ERROR, "invalid jsonb object type: %d", jb->type);
823 return executeAnyItem
824 (cxt, hasNext ? &elem : NULL,
825 jb->val.binary.data, found, 1, 1, 1,
826 false, jspAutoUnwrap(cxt));
828 else if (unwrap && JsonbType(jb) == jbvArray)
829 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
830 else if (!jspIgnoreStructuralErrors(cxt))
833 RETURN_ERROR(ereport(ERROR,
834 (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
835 errmsg(ERRMSG_JSON_OBJECT_NOT_FOUND),
836 errdetail("jsonpath wildcard member accessor "
837 "can only be applied to an object"))));
842 return executeBinaryArithmExpr(cxt, jsp, jb,
843 numeric_add_opt_error, found);
846 return executeBinaryArithmExpr(cxt, jsp, jb,
847 numeric_sub_opt_error, found);
850 return executeBinaryArithmExpr(cxt, jsp, jb,
851 numeric_mul_opt_error, found);
854 return executeBinaryArithmExpr(cxt, jsp, jb,
855 numeric_div_opt_error, found);
858 return executeBinaryArithmExpr(cxt, jsp, jb,
859 numeric_mod_opt_error, found);
862 return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
865 return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
872 if (unwrap && JsonbType(jb) == jbvArray)
873 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
876 jspGetArg(jsp, &elem);
877 st = executeNestedBoolItem(cxt, &elem, jb);
881 res = executeNextItem(cxt, jsp, NULL,
888 bool hasNext = jspGetNext(jsp, &elem);
890 /* first try without any intermediate steps */
891 if (jsp->content.anybounds.first == 0)
893 bool savedIgnoreStructuralErrors;
895 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
896 cxt->ignoreStructuralErrors = true;
897 res = executeNextItem(cxt, jsp, &elem,
899 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
901 if (res == jperOk && !found)
905 if (jb->type == jbvBinary)
907 (cxt, hasNext ? &elem : NULL,
908 jb->val.binary.data, found,
910 jsp->content.anybounds.first,
911 jsp->content.anybounds.last,
912 true, jspAutoUnwrap(cxt));
924 bool hasNext = jspGetNext(jsp, &elem);
926 if (!hasNext && !found)
928 res = jperOk; /* skip evaluation */
932 v = hasNext ? &vbuf : palloc(sizeof(*v));
934 baseObject = cxt->baseObject;
935 getJsonPathItem(cxt, jsp, v);
937 res = executeNextItem(cxt, jsp, &elem,
939 cxt->baseObject = baseObject;
945 JsonbValue *jbv = palloc(sizeof(*jbv));
947 jbv->type = jbvString;
948 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
949 jbv->val.string.len = strlen(jbv->val.string.val);
951 res = executeNextItem(cxt, jsp, NULL, jbv,
958 int size = JsonbArraySize(jb);
962 if (!jspAutoWrap(cxt))
964 if (!jspIgnoreStructuralErrors(cxt))
965 RETURN_ERROR(ereport(ERROR,
966 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
967 errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
968 errdetail("jsonpath item method .%s() "
969 "can only be applied to an array",
970 jspOperationName(jsp->type)))));
977 jb = palloc(sizeof(*jb));
979 jb->type = jbvNumeric;
981 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
982 Int32GetDatum(size)));
984 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
989 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
993 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
997 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
1004 if (unwrap && JsonbType(jb) == jbvArray)
1005 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1008 if (jb->type == jbvNumeric)
1010 char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1011 NumericGetDatum(jb->val.numeric)));
1012 bool have_error = false;
1014 (void) float8in_internal_opt_error(tmp,
1021 RETURN_ERROR(ereport(ERROR,
1022 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1023 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1024 errdetail("jsonpath item method .%s() "
1025 "can only be applied to "
1027 jspOperationName(jsp->type)))));
1030 else if (jb->type == jbvString)
1032 /* cast string as double */
1034 char *tmp = pnstrdup(jb->val.string.val,
1035 jb->val.string.len);
1036 bool have_error = false;
1038 val = float8in_internal_opt_error(tmp,
1044 if (have_error || isinf(val))
1045 RETURN_ERROR(ereport(ERROR,
1046 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1047 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1048 errdetail("jsonpath item method .%s() can "
1049 "only be applied to a numeric value",
1050 jspOperationName(jsp->type)))));
1053 jb->type = jbvNumeric;
1054 jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1055 Float8GetDatum(val)));
1059 if (res == jperNotFound)
1060 RETURN_ERROR(ereport(ERROR,
1061 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1062 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1063 errdetail("jsonpath item method .%s() "
1064 "can only be applied to a "
1065 "string or numeric value",
1066 jspOperationName(jsp->type)))));
1068 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1073 if (unwrap && JsonbType(jb) == jbvArray)
1074 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1076 return executeKeyValueMethod(cxt, jsp, jb, found);
1079 elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1086 * Unwrap current array item and execute jsonpath for each of its elements.
1088 static JsonPathExecResult
1089 executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1090 JsonbValue *jb, JsonValueList *found,
1091 bool unwrapElements)
1093 if (jb->type != jbvBinary)
1095 Assert(jb->type != jbvArray);
1096 elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1099 return executeAnyItem
1100 (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1101 false, unwrapElements);
1105 * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1108 static JsonPathExecResult
1109 executeNextItem(JsonPathExecContext *cxt,
1110 JsonPathItem *cur, JsonPathItem *next,
1111 JsonbValue *v, JsonValueList *found, bool copy)
1117 hasNext = next != NULL;
1119 hasNext = jspHasNext(cur);
1123 hasNext = jspGetNext(cur, next);
1127 return executeItem(cxt, next, v, found);
1130 JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1136 * Same as executeItem(), but when "unwrap == true" automatically unwraps
1137 * each array item from the resulting sequence in lax mode.
1139 static JsonPathExecResult
1140 executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1141 JsonbValue *jb, bool unwrap,
1142 JsonValueList *found)
1144 if (unwrap && jspAutoUnwrap(cxt))
1146 JsonValueList seq = {0};
1147 JsonValueListIterator it;
1148 JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1151 if (jperIsError(res))
1154 JsonValueListInitIterator(&seq, &it);
1155 while ((item = JsonValueListNext(&seq, &it)))
1157 Assert(item->type != jbvArray);
1159 if (JsonbType(item) == jbvArray)
1160 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1162 JsonValueListAppend(found, item);
1168 return executeItem(cxt, jsp, jb, found);
1172 * Same as executeItemOptUnwrapResult(), but with error suppression.
1174 static JsonPathExecResult
1175 executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1177 JsonbValue *jb, bool unwrap,
1178 JsonValueList *found)
1180 JsonPathExecResult res;
1181 bool throwErrors = cxt->throwErrors;
1183 cxt->throwErrors = false;
1184 res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1185 cxt->throwErrors = throwErrors;
1190 /* Execute boolean-valued jsonpath expression. */
1192 executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1193 JsonbValue *jb, bool canHaveNext)
1200 if (!canHaveNext && jspHasNext(jsp))
1201 elog(ERROR, "boolean jsonpath item cannot have next item");
1206 jspGetLeftArg(jsp, &larg);
1207 res = executeBoolItem(cxt, &larg, jb, false);
1209 if (res == jpbFalse)
1213 * SQL/JSON says that we should check second arg in case of
1217 jspGetRightArg(jsp, &rarg);
1218 res2 = executeBoolItem(cxt, &rarg, jb, false);
1220 return res2 == jpbTrue ? res : res2;
1223 jspGetLeftArg(jsp, &larg);
1224 res = executeBoolItem(cxt, &larg, jb, false);
1229 jspGetRightArg(jsp, &rarg);
1230 res2 = executeBoolItem(cxt, &rarg, jb, false);
1232 return res2 == jpbFalse ? res : res2;
1235 jspGetArg(jsp, &larg);
1237 res = executeBoolItem(cxt, &larg, jb, false);
1239 if (res == jpbUnknown)
1242 return res == jpbTrue ? jpbFalse : jpbTrue;
1245 jspGetArg(jsp, &larg);
1246 res = executeBoolItem(cxt, &larg, jb, false);
1247 return res == jpbUnknown ? jpbTrue : jpbFalse;
1253 case jpiLessOrEqual:
1254 case jpiGreaterOrEqual:
1255 jspGetLeftArg(jsp, &larg);
1256 jspGetRightArg(jsp, &rarg);
1257 return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1258 executeComparison, NULL);
1260 case jpiStartsWith: /* 'whole STARTS WITH initial' */
1261 jspGetLeftArg(jsp, &larg); /* 'whole' */
1262 jspGetRightArg(jsp, &rarg); /* 'initial' */
1263 return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1264 executeStartsWith, NULL);
1266 case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1269 * 'expr' is a sequence-returning expression. 'pattern' is a
1270 * regex string literal. SQL/JSON standard requires XQuery
1271 * regexes, but we use Postgres regexes here. 'flags' is a
1272 * string literal converted to integer flags at compile-time.
1274 JsonLikeRegexContext lrcxt = {0};
1276 jspInitByBuffer(&larg, jsp->base,
1277 jsp->content.like_regex.expr);
1279 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1280 executeLikeRegex, &lrcxt);
1284 jspGetArg(jsp, &larg);
1286 if (jspStrictAbsenseOfErrors(cxt))
1289 * In strict mode we must get a complete list of values to
1290 * check that there are no errors at all.
1292 JsonValueList vals = {0};
1293 JsonPathExecResult res =
1294 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1297 if (jperIsError(res))
1300 return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1304 JsonPathExecResult res =
1305 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1308 if (jperIsError(res))
1311 return res == jperOk ? jpbTrue : jpbFalse;
1315 elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1321 * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1322 * item onto the stack.
1325 executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1331 prev = cxt->current;
1333 res = executeBoolItem(cxt, jsp, jb, false);
1334 cxt->current = prev;
1340 * Implementation of several jsonpath nodes:
1341 * - jpiAny (.** accessor),
1342 * - jpiAnyKey (.* accessor),
1343 * - jpiAnyArray ([*] accessor)
1345 static JsonPathExecResult
1346 executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1347 JsonValueList *found, uint32 level, uint32 first, uint32 last,
1348 bool ignoreStructuralErrors, bool unwrapNext)
1350 JsonPathExecResult res = jperNotFound;
1355 check_stack_depth();
1360 it = JsonbIteratorInit(jbc);
1363 * Recursively iterate over jsonb objects/arrays
1365 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1369 r = JsonbIteratorNext(&it, &v, true);
1370 Assert(r == WJB_VALUE);
1373 if (r == WJB_VALUE || r == WJB_ELEM)
1376 if (level >= first ||
1377 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1378 v.type != jbvBinary)) /* leaves only requested */
1380 /* check expression */
1383 if (ignoreStructuralErrors)
1385 bool savedIgnoreStructuralErrors;
1387 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1388 cxt->ignoreStructuralErrors = true;
1389 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1390 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1393 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1395 if (jperIsError(res))
1398 if (res == jperOk && !found)
1402 JsonValueListAppend(found, copyJsonbValue(&v));
1407 if (level < last && v.type == jbvBinary)
1409 res = executeAnyItem
1410 (cxt, jsp, v.val.binary.data, found,
1411 level + 1, first, last,
1412 ignoreStructuralErrors, unwrapNext);
1414 if (jperIsError(res))
1417 if (res == jperOk && found == NULL)
1427 * Execute unary or binary predicate.
1429 * Predicates have existence semantics, because their operands are item
1430 * sequences. Pairs of items from the left and right operand's sequences are
1431 * checked. TRUE returned only if any pair satisfying the condition is found.
1432 * In strict mode, even if the desired pair has already been found, all pairs
1433 * still need to be examined to check the absence of errors. If any error
1434 * occurs, UNKNOWN (analogous to SQL NULL) is returned.
1437 executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
1438 JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
1439 bool unwrapRightArg, JsonPathPredicateCallback exec,
1442 JsonPathExecResult res;
1443 JsonValueListIterator lseqit;
1444 JsonValueList lseq = {0};
1445 JsonValueList rseq = {0};
1450 /* Left argument is always auto-unwrapped. */
1451 res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
1452 if (jperIsError(res))
1457 /* Right argument is conditionally auto-unwrapped. */
1458 res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
1459 unwrapRightArg, &rseq);
1460 if (jperIsError(res))
1464 JsonValueListInitIterator(&lseq, &lseqit);
1465 while ((lval = JsonValueListNext(&lseq, &lseqit)))
1467 JsonValueListIterator rseqit;
1471 JsonValueListInitIterator(&rseq, &rseqit);
1473 rval = JsonValueListNext(&rseq, &rseqit);
1477 /* Loop over right arg sequence or do single pass otherwise */
1478 while (rarg ? (rval != NULL) : first)
1480 JsonPathBool res = exec(pred, lval, rval, param);
1482 if (res == jpbUnknown)
1484 if (jspStrictAbsenseOfErrors(cxt))
1489 else if (res == jpbTrue)
1491 if (!jspStrictAbsenseOfErrors(cxt))
1499 rval = JsonValueListNext(&rseq, &rseqit);
1503 if (found) /* possible only in strict mode */
1506 if (error) /* possible only in lax mode */
1513 * Execute binary arithmetic expression on singleton numeric operands.
1514 * Array operands are automatically unwrapped in lax mode.
1516 static JsonPathExecResult
1517 executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1518 JsonbValue *jb, BinaryArithmFunc func,
1519 JsonValueList *found)
1521 JsonPathExecResult jper;
1523 JsonValueList lseq = {0};
1524 JsonValueList rseq = {0};
1529 jspGetLeftArg(jsp, &elem);
1532 * XXX: By standard only operands of multiplicative expressions are
1533 * unwrapped. We extend it to other binary arithmetic expressions too.
1535 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
1536 if (jperIsError(jper))
1539 jspGetRightArg(jsp, &elem);
1541 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
1542 if (jperIsError(jper))
1545 if (JsonValueListLength(&lseq) != 1 ||
1546 !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
1547 RETURN_ERROR(ereport(ERROR,
1548 (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
1549 errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
1550 errdetail("left operand of binary jsonpath operator %s "
1551 "is not a singleton numeric value",
1552 jspOperationName(jsp->type)))));
1554 if (JsonValueListLength(&rseq) != 1 ||
1555 !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
1556 RETURN_ERROR(ereport(ERROR,
1557 (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
1558 errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
1559 errdetail("right operand of binary jsonpath operator %s "
1560 "is not a singleton numeric value",
1561 jspOperationName(jsp->type)))));
1563 if (jspThrowErrors(cxt))
1565 res = func(lval->val.numeric, rval->val.numeric, NULL);
1571 res = func(lval->val.numeric, rval->val.numeric, &error);
1577 if (!jspGetNext(jsp, &elem) && !found)
1580 lval = palloc(sizeof(*lval));
1581 lval->type = jbvNumeric;
1582 lval->val.numeric = res;
1584 return executeNextItem(cxt, jsp, &elem, lval, found, false);
1588 * Execute unary arithmetic expression for each numeric item in its operand's
1589 * sequence. Array operand is automatically unwrapped in lax mode.
1591 static JsonPathExecResult
1592 executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1593 JsonbValue *jb, PGFunction func, JsonValueList *found)
1595 JsonPathExecResult jper;
1596 JsonPathExecResult jper2;
1598 JsonValueList seq = {0};
1599 JsonValueListIterator it;
1603 jspGetArg(jsp, &elem);
1604 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
1606 if (jperIsError(jper))
1609 jper = jperNotFound;
1611 hasNext = jspGetNext(jsp, &elem);
1613 JsonValueListInitIterator(&seq, &it);
1614 while ((val = JsonValueListNext(&seq, &it)))
1616 if ((val = getScalar(val, jbvNumeric)))
1618 if (!found && !hasNext)
1623 if (!found && !hasNext)
1624 continue; /* skip non-numerics processing */
1626 RETURN_ERROR(ereport(ERROR,
1627 (errcode(ERRCODE_JSON_NUMBER_NOT_FOUND),
1628 errmsg(ERRMSG_JSON_NUMBER_NOT_FOUND),
1629 errdetail("operand of unary jsonpath operator %s "
1630 "is not a numeric value",
1631 jspOperationName(jsp->type)))));
1636 DatumGetNumeric(DirectFunctionCall1(func,
1637 NumericGetDatum(val->val.numeric)));
1639 jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
1641 if (jperIsError(jper2))
1644 if (jper2 == jperOk)
1656 * STARTS_WITH predicate callback.
1658 * Check if the 'whole' string starts from 'initial' string.
1661 executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
1664 if (!(whole = getScalar(whole, jbvString)))
1665 return jpbUnknown; /* error */
1667 if (!(initial = getScalar(initial, jbvString)))
1668 return jpbUnknown; /* error */
1670 if (whole->val.string.len >= initial->val.string.len &&
1671 !memcmp(whole->val.string.val,
1672 initial->val.string.val,
1673 initial->val.string.len))
1680 * LIKE_REGEX predicate callback.
1682 * Check if the string matches regex pattern.
1685 executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
1688 JsonLikeRegexContext *cxt = param;
1690 if (!(str = getScalar(str, jbvString)))
1693 /* Cache regex text and converted flags. */
1696 uint32 flags = jsp->content.like_regex.flags;
1699 cstring_to_text_with_len(jsp->content.like_regex.pattern,
1700 jsp->content.like_regex.patternlen);
1702 /* Convert regex flags. */
1703 cxt->cflags = REG_ADVANCED;
1705 if (flags & JSP_REGEX_ICASE)
1706 cxt->cflags |= REG_ICASE;
1707 if (flags & JSP_REGEX_MLINE)
1708 cxt->cflags |= REG_NEWLINE;
1709 if (flags & JSP_REGEX_SLINE)
1710 cxt->cflags &= ~REG_NEWLINE;
1711 if (flags & JSP_REGEX_WSPACE)
1712 cxt->cflags |= REG_EXPANDED;
1715 if (RE_compile_and_execute(cxt->regex, str->val.string.val,
1716 str->val.string.len,
1717 cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
1724 * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
1725 * user function 'func'.
1727 static JsonPathExecResult
1728 executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1729 JsonbValue *jb, bool unwrap, PGFunction func,
1730 JsonValueList *found)
1735 if (unwrap && JsonbType(jb) == jbvArray)
1736 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1738 if (!(jb = getScalar(jb, jbvNumeric)))
1739 RETURN_ERROR(ereport(ERROR,
1740 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1741 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1742 errdetail("jsonpath item method .%s() can only "
1743 "be applied to a numeric value",
1744 jspOperationName(jsp->type)))));
1746 datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
1748 if (!jspGetNext(jsp, &next) && !found)
1751 jb = palloc(sizeof(*jb));
1752 jb->type = jbvNumeric;
1753 jb->val.numeric = DatumGetNumeric(datum);
1755 return executeNextItem(cxt, jsp, &next, jb, found, false);
1759 * Implementation of .keyvalue() method.
1761 * .keyvalue() method returns a sequence of object's key-value pairs in the
1762 * following format: '{ "key": key, "value": value, "id": id }'.
1764 * "id" field is an object identifier which is constructed from the two parts:
1765 * base object id and its binary offset in base object's jsonb:
1766 * id = 10000000000 * base_object_id + obj_offset_in_base_object
1768 * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1769 * (maximal offset in jsonb). Decimal multiplier is used here to improve the
1770 * readability of identifiers.
1772 * Base object is usually a root object of the path: context item '$' or path
1773 * variable '$var', literals can't produce objects for now. But if the path
1774 * contains generated objects (.keyvalue() itself, for example), then they
1775 * become base object for the subsequent .keyvalue().
1777 * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
1778 * of variables (see getJsonPathVariable()). Ids for generated objects
1779 * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
1781 static JsonPathExecResult
1782 executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1783 JsonbValue *jb, JsonValueList *found)
1785 JsonPathExecResult res = jperNotFound;
1787 JsonbContainer *jbc;
1795 JsonbIteratorToken tok;
1799 if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
1800 RETURN_ERROR(ereport(ERROR,
1801 (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
1802 errmsg(ERRMSG_JSON_OBJECT_NOT_FOUND),
1803 errdetail("jsonpath item method .%s() "
1804 "can only be applied to an object",
1805 jspOperationName(jsp->type)))));
1807 jbc = jb->val.binary.data;
1809 if (!JsonContainerSize(jbc))
1810 return jperNotFound; /* no key-value pairs */
1812 hasNext = jspGetNext(jsp, &next);
1814 keystr.type = jbvString;
1815 keystr.val.string.val = "key";
1816 keystr.val.string.len = 3;
1818 valstr.type = jbvString;
1819 valstr.val.string.val = "value";
1820 valstr.val.string.len = 5;
1822 idstr.type = jbvString;
1823 idstr.val.string.val = "id";
1824 idstr.val.string.len = 2;
1826 /* construct object id from its base object and offset inside that */
1827 id = jb->type != jbvBinary ? 0 :
1828 (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
1829 id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
1831 idval.type = jbvNumeric;
1832 idval.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
1833 Int64GetDatum(id)));
1835 it = JsonbIteratorInit(jbc);
1837 while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
1839 JsonBaseObjectInfo baseObject;
1841 JsonbParseState *ps;
1850 if (!hasNext && !found)
1853 tok = JsonbIteratorNext(&it, &val, true);
1854 Assert(tok == WJB_VALUE);
1857 pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
1859 pushJsonbValue(&ps, WJB_KEY, &keystr);
1860 pushJsonbValue(&ps, WJB_VALUE, &key);
1862 pushJsonbValue(&ps, WJB_KEY, &valstr);
1863 pushJsonbValue(&ps, WJB_VALUE, &val);
1865 pushJsonbValue(&ps, WJB_KEY, &idstr);
1866 pushJsonbValue(&ps, WJB_VALUE, &idval);
1868 keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
1870 jsonb = JsonbValueToJsonb(keyval);
1872 JsonbInitBinary(&obj, jsonb);
1874 baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
1876 res = executeNextItem(cxt, jsp, &next, &obj, found, true);
1878 cxt->baseObject = baseObject;
1880 if (jperIsError(res))
1883 if (res == jperOk && !found)
1891 * Convert boolean execution status 'res' to a boolean JSON item and execute
1894 static JsonPathExecResult
1895 appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1896 JsonValueList *found, JsonPathBool res)
1901 if (!jspGetNext(jsp, &next) && !found)
1902 return jperOk; /* found singleton boolean value */
1904 if (res == jpbUnknown)
1911 jbv.val.boolean = res == jpbTrue;
1914 return executeNextItem(cxt, jsp, &next, &jbv, found, true);
1918 * Convert jsonpath's scalar or variable node to actual jsonb value.
1920 * If node is a variable then its id returned, otherwise 0 returned.
1923 getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
1929 value->type = jbvNull;
1932 value->type = jbvBool;
1933 value->val.boolean = jspGetBool(item);
1936 value->type = jbvNumeric;
1937 value->val.numeric = jspGetNumeric(item);
1940 value->type = jbvString;
1941 value->val.string.val = jspGetString(item,
1942 &value->val.string.len);
1945 getJsonPathVariable(cxt, item, cxt->vars, value);
1948 elog(ERROR, "unexpected jsonpath item type");
1953 * Get the value of variable passed to jsonpath executor
1956 getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
1957 Jsonb *vars, JsonbValue *value)
1966 value->type = jbvNull;
1970 Assert(variable->type == jpiVariable);
1971 varName = jspGetString(variable, &varNameLength);
1972 tmp.type = jbvString;
1973 tmp.val.string.val = varName;
1974 tmp.val.string.len = varNameLength;
1976 v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
1986 (errcode(ERRCODE_UNDEFINED_OBJECT),
1987 errmsg("cannot find jsonpath variable '%s'",
1988 pnstrdup(varName, varNameLength))));
1991 JsonbInitBinary(&tmp, vars);
1992 setBaseObject(cxt, &tmp, 1);
1995 /**************** Support functions for JsonPath execution *****************/
1998 * Returns the size of an array item, or -1 if item is not an array.
2001 JsonbArraySize(JsonbValue *jb)
2003 Assert(jb->type != jbvArray);
2005 if (jb->type == jbvBinary)
2007 JsonbContainer *jbc = jb->val.binary.data;
2009 if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
2010 return JsonContainerSize(jbc);
2016 /* Comparison predicate callback. */
2018 executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
2020 return compareItems(cmp->type, lv, rv);
2024 * Compare two SQL/JSON items using comparison operation 'op'.
2027 compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2)
2032 if (jb1->type != jb2->type)
2034 if (jb1->type == jbvNull || jb2->type == jbvNull)
2037 * Equality and order comparison of nulls to non-nulls returns
2038 * always false, but inequality comparison returns true.
2040 return op == jpiNotEqual ? jpbTrue : jpbFalse;
2042 /* Non-null items of different types are not comparable. */
2052 cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2053 jb1->val.boolean ? 1 : -1;
2056 cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2060 return jb1->val.string.len != jb2->val.string.len ||
2061 memcmp(jb1->val.string.val,
2062 jb2->val.string.val,
2063 jb1->val.string.len) ? jpbFalse : jpbTrue;
2065 cmp = varstr_cmp(jb1->val.string.val, jb1->val.string.len,
2066 jb2->val.string.val, jb2->val.string.len,
2067 DEFAULT_COLLATION_OID);
2073 return jpbUnknown; /* non-scalars are not comparable */
2076 elog(ERROR, "invalid jsonb value type %d", jb1->type);
2093 case jpiLessOrEqual:
2096 case jpiGreaterOrEqual:
2100 elog(ERROR, "unrecognized jsonpath operation: %d", op);
2104 return res ? jpbTrue : jpbFalse;
2107 /* Compare two numerics */
2109 compareNumeric(Numeric a, Numeric b)
2111 return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2113 NumericGetDatum(b)));
2117 copyJsonbValue(JsonbValue *src)
2119 JsonbValue *dst = palloc(sizeof(*dst));
2127 * Execute array subscript expression and convert resulting numeric item to
2128 * the integer type with truncation.
2130 static JsonPathExecResult
2131 getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2135 JsonValueList found = {0};
2136 JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2137 Datum numeric_index;
2138 bool have_error = false;
2140 if (jperIsError(res))
2143 if (JsonValueListLength(&found) != 1 ||
2144 !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2145 RETURN_ERROR(ereport(ERROR,
2146 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
2147 errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
2148 errdetail("jsonpath array subscript is not a "
2149 "singleton numeric value"))));
2151 numeric_index = DirectFunctionCall2(numeric_trunc,
2152 NumericGetDatum(jbv->val.numeric),
2155 *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
2159 RETURN_ERROR(ereport(ERROR,
2160 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
2161 errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
2162 errdetail("jsonpath array subscript is "
2163 "out of integer range"))));
2168 /* Save base object and its id needed for the execution of .keyvalue(). */
2169 static JsonBaseObjectInfo
2170 setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2172 JsonBaseObjectInfo baseObject = cxt->baseObject;
2174 cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2175 (JsonbContainer *) jbv->val.binary.data;
2176 cxt->baseObject.id = id;
2182 JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2186 jvl->list = list_make2(jvl->singleton, jbv);
2187 jvl->singleton = NULL;
2189 else if (!jvl->list)
2190 jvl->singleton = jbv;
2192 jvl->list = lappend(jvl->list, jbv);
2196 JsonValueListLength(const JsonValueList *jvl)
2198 return jvl->singleton ? 1 : list_length(jvl->list);
2202 JsonValueListIsEmpty(JsonValueList *jvl)
2204 return !jvl->singleton && list_length(jvl->list) <= 0;
2208 JsonValueListHead(JsonValueList *jvl)
2210 return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2214 JsonValueListGetList(JsonValueList *jvl)
2217 return list_make1(jvl->singleton);
2223 JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2227 it->value = jvl->singleton;
2230 else if (list_head(jvl->list) != NULL)
2232 it->value = (JsonbValue *) linitial(jvl->list);
2233 it->next = lnext(list_head(jvl->list));
2243 * Get the next item from the sequence advancing iterator.
2246 JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2248 JsonbValue *result = it->value;
2252 it->value = lfirst(it->next);
2253 it->next = lnext(it->next);
2264 * Initialize a binary JsonbValue with the given jsonb container.
2267 JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2269 jbv->type = jbvBinary;
2270 jbv->val.binary.data = &jb->root;
2271 jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2277 * Returns jbv* type of of JsonbValue. Note, it never returns jbvBinary as is.
2280 JsonbType(JsonbValue *jb)
2282 int type = jb->type;
2284 if (jb->type == jbvBinary)
2286 JsonbContainer *jbc = (void *) jb->val.binary.data;
2288 /* Scalars should be always extracted during jsonpath execution. */
2289 Assert(!JsonContainerIsScalar(jbc));
2291 if (JsonContainerIsObject(jbc))
2293 else if (JsonContainerIsArray(jbc))
2296 elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2302 /* Get scalar of given type or NULL on type mismatch */
2304 getScalar(JsonbValue *scalar, enum jbvType type)
2306 /* Scalars should be always extracted during jsonpath execution. */
2307 Assert(scalar->type != jbvBinary ||
2308 !JsonContainerIsScalar(scalar->val.binary.data));
2310 return scalar->type == type ? scalar : NULL;
2313 /* Construct a JSON array from the item list */
2315 wrapItemsInArray(const JsonValueList *items)
2317 JsonbParseState *ps = NULL;
2318 JsonValueListIterator it;
2321 pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2323 JsonValueListInitIterator(items, &it);
2324 while ((jbv = JsonValueListNext(items, &it)))
2325 pushJsonbValue(&ps, WJB_ELEM, jbv);
2327 return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);