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,
183 static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
184 Jsonb *json, bool throwErrors, JsonValueList *result);
185 static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
186 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
187 static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
188 JsonPathItem *jsp, JsonbValue *jb,
189 JsonValueList *found, bool unwrap);
190 static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
191 JsonPathItem *jsp, JsonbValue *jb,
192 JsonValueList *found, bool unwrapElements);
193 static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
194 JsonPathItem *cur, JsonPathItem *next,
195 JsonbValue *v, JsonValueList *found, bool copy);
196 static JsonPathExecResult executeItemOptUnwrapResult(
197 JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
198 bool unwrap, JsonValueList *found);
199 static JsonPathExecResult executeItemOptUnwrapResultNoThrow(
200 JsonPathExecContext *cxt, JsonPathItem *jsp,
201 JsonbValue *jb, bool unwrap, JsonValueList *found);
202 static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
203 JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
204 static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
205 JsonPathItem *jsp, JsonbValue *jb);
206 static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
207 JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
208 uint32 level, uint32 first, uint32 last,
209 bool ignoreStructuralErrors, bool unwrapNext);
210 static JsonPathBool executePredicate(JsonPathExecContext *cxt,
211 JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
212 JsonbValue *jb, bool unwrapRightArg,
213 JsonPathPredicateCallback exec, void *param);
214 static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
215 JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
216 JsonValueList *found);
217 static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
218 JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
219 JsonValueList *found);
220 static JsonPathBool executeStartsWith(JsonPathItem *jsp,
221 JsonbValue *whole, JsonbValue *initial, void *param);
222 static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
223 JsonbValue *rarg, void *param);
224 static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
225 JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
226 JsonValueList *found);
227 static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
228 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
229 static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
230 JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
231 static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
233 static void getJsonPathVariable(JsonPathExecContext *cxt,
234 JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
235 static int JsonbArraySize(JsonbValue *jb);
236 static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
237 JsonbValue *rv, void *p);
238 static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2);
239 static int compareNumeric(Numeric a, Numeric b);
240 static JsonbValue *copyJsonbValue(JsonbValue *src);
241 static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
242 JsonPathItem *jsp, JsonbValue *jb, int32 *index);
243 static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
244 JsonbValue *jbv, int32 id);
245 static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
246 static int JsonValueListLength(const JsonValueList *jvl);
247 static bool JsonValueListIsEmpty(JsonValueList *jvl);
248 static JsonbValue *JsonValueListHead(JsonValueList *jvl);
249 static List *JsonValueListGetList(JsonValueList *jvl);
250 static void JsonValueListInitIterator(const JsonValueList *jvl,
251 JsonValueListIterator *it);
252 static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
253 JsonValueListIterator *it);
254 static int JsonbType(JsonbValue *jb);
255 static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
256 static int JsonbType(JsonbValue *jb);
257 static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
258 static JsonbValue *wrapItemsInArray(const JsonValueList *items);
260 /****************** User interface to JsonPath executor ********************/
264 * Returns true if jsonpath returns at least one item for the specified
265 * jsonb value. This function and jsonb_path_match() are used to
266 * implement @? and @@ operators, which in turn are intended to have an
267 * index support. Thus, it's desirable to make it easier to achieve
268 * consistency between index scan results and sequential scan results.
269 * So, we throw as less errors as possible. Regarding this function,
270 * such behavior also matches behavior of JSON_EXISTS() clause of
271 * SQL/JSON. Regarding jsonb_path_match(), this function doesn't have
272 * an analogy in SQL/JSON, so we define its behavior on our own.
275 jsonb_path_exists(PG_FUNCTION_ARGS)
277 Jsonb *jb = PG_GETARG_JSONB_P(0);
278 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
279 JsonPathExecResult res;
285 vars = PG_GETARG_JSONB_P(2);
286 silent = PG_GETARG_BOOL(3);
289 res = executeJsonPath(jp, vars, jb, !silent, NULL);
291 PG_FREE_IF_COPY(jb, 0);
292 PG_FREE_IF_COPY(jp, 1);
294 if (jperIsError(res))
297 PG_RETURN_BOOL(res == jperOk);
301 * jsonb_path_exists_opr
302 * Implementation of operator "jsonb @? jsonpath" (2-argument version of
303 * jsonb_path_exists()).
306 jsonb_path_exists_opr(PG_FUNCTION_ARGS)
308 /* just call the other one -- it can handle both cases */
309 return jsonb_path_exists(fcinfo);
314 * Returns jsonpath predicate result item for the specified jsonb value.
315 * See jsonb_path_exists() comment for details regarding error handling.
318 jsonb_path_match(PG_FUNCTION_ARGS)
320 Jsonb *jb = PG_GETARG_JSONB_P(0);
321 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 if (JsonValueListLength(&found) < 1)
338 jbv = JsonValueListHead(&found);
340 PG_FREE_IF_COPY(jb, 0);
341 PG_FREE_IF_COPY(jp, 1);
343 if (jbv->type != jbvBool)
346 PG_RETURN_BOOL(jbv->val.boolean);
350 * jsonb_path_match_opr
351 * Implementation of operator "jsonb @@ jsonpath" (2-argument version of
352 * jsonb_path_match()).
355 jsonb_path_match_opr(PG_FUNCTION_ARGS)
357 /* just call the other one -- it can handle both cases */
358 return jsonb_path_match(fcinfo);
363 * Executes jsonpath for given jsonb document and returns result as
367 jsonb_path_query(PG_FUNCTION_ARGS)
369 FuncCallContext *funcctx;
374 if (SRF_IS_FIRSTCALL())
378 MemoryContext oldcontext;
381 JsonValueList found = {0};
383 funcctx = SRF_FIRSTCALL_INIT();
384 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
386 jb = PG_GETARG_JSONB_P_COPY(0);
387 jp = PG_GETARG_JSONPATH_P_COPY(1);
388 vars = PG_GETARG_JSONB_P_COPY(2);
389 silent = PG_GETARG_BOOL(3);
391 (void) executeJsonPath(jp, vars, jb, !silent, &found);
393 funcctx->user_fctx = JsonValueListGetList(&found);
395 MemoryContextSwitchTo(oldcontext);
398 funcctx = SRF_PERCALL_SETUP();
399 found = funcctx->user_fctx;
401 c = list_head(found);
404 SRF_RETURN_DONE(funcctx);
407 funcctx->user_fctx = list_delete_first(found);
409 SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
413 * jsonb_path_query_array
414 * Executes jsonpath for given jsonb document and returns result as
418 jsonb_path_query_array(FunctionCallInfo fcinfo)
420 Jsonb *jb = PG_GETARG_JSONB_P(0);
421 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
422 JsonValueList found = {0};
423 Jsonb *vars = PG_GETARG_JSONB_P(2);
424 bool silent = PG_GETARG_BOOL(3);
426 (void) executeJsonPath(jp, vars, jb, !silent, &found);
428 PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
432 * jsonb_path_query_first
433 * Executes jsonpath for given jsonb document and returns first result
434 * item. If there are no items, NULL returned.
437 jsonb_path_query_first(FunctionCallInfo fcinfo)
439 Jsonb *jb = PG_GETARG_JSONB_P(0);
440 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
441 JsonValueList found = {0};
442 Jsonb *vars = PG_GETARG_JSONB_P(2);
443 bool silent = PG_GETARG_BOOL(3);
445 (void) executeJsonPath(jp, vars, jb, !silent, &found);
447 if (JsonValueListLength(&found) >= 1)
448 PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
453 /********************Execute functions for JsonPath**************************/
456 * Interface to jsonpath executor
458 * 'path' - jsonpath to be executed
459 * 'vars' - variables to be substituted to jsonpath
460 * 'json' - target document for jsonpath evaluation
461 * 'throwErrors' - whether we should throw suppressible errors
462 * 'result' - list to store result items into
464 * Returns an error happens during processing or NULL on no error.
466 * Note, jsonb and jsonpath values should be avaliable and untoasted during
467 * work because JsonPathItem, JsonbValue and result item could have pointers
468 * into input values. If caller needs to just check if document matches
469 * jsonpath, then it doesn't provide a result arg. In this case executor
470 * works till first positive result and does not check the rest if possible.
471 * In other case it tries to find all the satisfied result items.
473 static JsonPathExecResult
474 executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
475 JsonValueList *result)
477 JsonPathExecContext cxt;
478 JsonPathExecResult res;
484 if (!JsonbExtractScalar(&json->root, &jbv))
485 JsonbInitBinary(&jbv, json);
487 if (vars && !JsonContainerIsObject(&vars->root))
490 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
491 errmsg("jsonb containing jsonpath variables "
492 "is not an object")));
496 cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
497 cxt.ignoreStructuralErrors = cxt.laxMode;
500 cxt.baseObject.jbc = NULL;
501 cxt.baseObject.id = 0;
502 cxt.lastGeneratedObjectId = vars ? 2 : 1;
503 cxt.innermostArraySize = -1;
504 cxt.throwErrors = throwErrors;
506 if (jspStrictAbsenseOfErrors(&cxt) && !result)
509 * In strict mode we must get a complete list of values to check that
510 * there are no errors at all.
512 JsonValueList vals = {0};
514 res = executeItem(&cxt, &jsp, &jbv, &vals);
516 if (jperIsError(res))
519 return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
522 res = executeItem(&cxt, &jsp, &jbv, result);
524 Assert(!throwErrors || !jperIsError(res));
530 * Execute jsonpath with automatic unwrapping of current item in lax mode.
532 static JsonPathExecResult
533 executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
534 JsonbValue *jb, JsonValueList *found)
536 return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
540 * Main jsonpath executor function: walks on jsonpath structure, finds
541 * relevant parts of jsonb and evaluates expressions over them.
542 * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
544 static JsonPathExecResult
545 executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
546 JsonbValue *jb, JsonValueList *found, bool unwrap)
549 JsonPathExecResult res = jperNotFound;
550 JsonBaseObjectInfo baseObject;
553 CHECK_FOR_INTERRUPTS();
557 /* all boolean item types: */
567 case jpiGreaterOrEqual:
572 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
574 res = appendBoolResult(cxt, jsp, found, st);
579 if (JsonbType(jb) == jbvObject)
584 key.type = jbvString;
585 key.val.string.val = jspGetString(jsp, &key.val.string.len);
587 v = findJsonbValueFromContainer(jb->val.binary.data,
592 res = executeNextItem(cxt, jsp, NULL,
595 /* free value if it was not added to found list */
596 if (jspHasNext(jsp) || !found)
599 else if (!jspIgnoreStructuralErrors(cxt))
601 StringInfoData keybuf;
606 if (!jspThrowErrors(cxt))
609 initStringInfo(&keybuf);
611 keystr = pnstrdup(key.val.string.val, key.val.string.len);
612 escape_json(&keybuf, keystr);
615 (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND), \
616 errmsg(ERRMSG_JSON_MEMBER_NOT_FOUND),
617 errdetail("JSON object does not contain key %s",
621 else if (unwrap && JsonbType(jb) == jbvArray)
622 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
623 else if (!jspIgnoreStructuralErrors(cxt))
626 RETURN_ERROR(ereport(ERROR,
627 (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND),
628 errmsg(ERRMSG_JSON_MEMBER_NOT_FOUND),
629 errdetail("jsonpath member accessor can "
630 "only be applied to an object"))));
636 baseObject = setBaseObject(cxt, jb, 0);
637 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
638 cxt->baseObject = baseObject;
642 res = executeNextItem(cxt, jsp, NULL, cxt->current,
647 if (JsonbType(jb) == jbvArray)
649 bool hasNext = jspGetNext(jsp, &elem);
651 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
652 jb, found, jspAutoUnwrap(cxt));
654 else if (jspAutoWrap(cxt))
655 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
656 else if (!jspIgnoreStructuralErrors(cxt))
657 RETURN_ERROR(ereport(ERROR,
658 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
659 errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
660 errdetail("jsonpath wildcard array accessor "
661 "can only be applied to an array"))));
665 if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
667 int innermostArraySize = cxt->innermostArraySize;
669 int size = JsonbArraySize(jb);
670 bool singleton = size < 0;
671 bool hasNext = jspGetNext(jsp, &elem);
676 cxt->innermostArraySize = size; /* for LAST evaluation */
678 for (i = 0; i < jsp->content.array.nelems; i++)
685 bool range = jspGetArraySubscript(jsp, &from,
688 res = getArrayIndex(cxt, &from, jb, &index_from);
690 if (jperIsError(res))
695 res = getArrayIndex(cxt, &to, jb, &index_to);
697 if (jperIsError(res))
701 index_to = index_from;
703 if (!jspIgnoreStructuralErrors(cxt) &&
705 index_from > index_to ||
707 RETURN_ERROR(ereport(ERROR,
708 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
709 errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
710 errdetail("jsonpath array subscript is "
716 if (index_to >= size)
721 for (index = index_from; index <= index_to; index++)
733 v = getIthJsonbValueFromContainer(jb->val.binary.data,
742 if (!hasNext && !found)
745 res = executeNextItem(cxt, jsp, &elem, v, found,
748 if (jperIsError(res))
751 if (res == jperOk && !found)
755 if (jperIsError(res))
758 if (res == jperOk && !found)
762 cxt->innermostArraySize = innermostArraySize;
764 else if (!jspIgnoreStructuralErrors(cxt))
766 RETURN_ERROR(ereport(ERROR,
767 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
768 errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
769 errdetail("jsonpath array accessor can "
770 "only be applied to an array"))));
779 bool hasNext = jspGetNext(jsp, &elem);
781 if (cxt->innermostArraySize < 0)
782 elog(ERROR, "evaluating jsonpath LAST outside of "
785 if (!hasNext && !found)
791 last = cxt->innermostArraySize - 1;
793 lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
795 lastjbv->type = jbvNumeric;
796 lastjbv->val.numeric =
797 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
798 Int32GetDatum(last)));
800 res = executeNextItem(cxt, jsp, &elem,
801 lastjbv, found, hasNext);
806 if (JsonbType(jb) == jbvObject)
808 bool hasNext = jspGetNext(jsp, &elem);
810 if (jb->type != jbvBinary)
811 elog(ERROR, "invalid jsonb object type: %d", jb->type);
813 return executeAnyItem
814 (cxt, hasNext ? &elem : NULL,
815 jb->val.binary.data, found, 1, 1, 1,
816 false, jspAutoUnwrap(cxt));
818 else if (unwrap && JsonbType(jb) == jbvArray)
819 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
820 else if (!jspIgnoreStructuralErrors(cxt))
823 RETURN_ERROR(ereport(ERROR,
824 (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
825 errmsg(ERRMSG_JSON_OBJECT_NOT_FOUND),
826 errdetail("jsonpath wildcard member accessor "
827 "can only be applied to an object"))));
832 return executeBinaryArithmExpr(cxt, jsp, jb,
836 return executeBinaryArithmExpr(cxt, jsp, jb,
840 return executeBinaryArithmExpr(cxt, jsp, jb,
844 return executeBinaryArithmExpr(cxt, jsp, jb,
848 return executeBinaryArithmExpr(cxt, jsp, jb,
852 return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
855 return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
862 if (unwrap && JsonbType(jb) == jbvArray)
863 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
866 jspGetArg(jsp, &elem);
867 st = executeNestedBoolItem(cxt, &elem, jb);
871 res = executeNextItem(cxt, jsp, NULL,
878 bool hasNext = jspGetNext(jsp, &elem);
880 /* first try without any intermediate steps */
881 if (jsp->content.anybounds.first == 0)
883 bool savedIgnoreStructuralErrors;
885 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
886 cxt->ignoreStructuralErrors = true;
887 res = executeNextItem(cxt, jsp, &elem,
889 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
891 if (res == jperOk && !found)
895 if (jb->type == jbvBinary)
897 (cxt, hasNext ? &elem : NULL,
898 jb->val.binary.data, found,
900 jsp->content.anybounds.first,
901 jsp->content.anybounds.last,
902 true, jspAutoUnwrap(cxt));
914 bool hasNext = jspGetNext(jsp, &elem);
916 if (!hasNext && !found)
918 res = jperOk; /* skip evaluation */
922 v = hasNext ? &vbuf : palloc(sizeof(*v));
924 baseObject = cxt->baseObject;
925 getJsonPathItem(cxt, jsp, v);
927 res = executeNextItem(cxt, jsp, &elem,
929 cxt->baseObject = baseObject;
935 JsonbValue *jbv = palloc(sizeof(*jbv));
937 jbv->type = jbvString;
938 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
939 jbv->val.string.len = strlen(jbv->val.string.val);
941 res = executeNextItem(cxt, jsp, NULL, jbv,
948 int size = JsonbArraySize(jb);
952 if (!jspAutoWrap(cxt))
954 if (!jspIgnoreStructuralErrors(cxt))
955 RETURN_ERROR(ereport(ERROR,
956 (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
957 errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
958 errdetail("jsonpath item method .%s() "
959 "can only be applied to an array",
960 jspOperationName(jsp->type)))));
967 jb = palloc(sizeof(*jb));
969 jb->type = jbvNumeric;
971 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
972 Int32GetDatum(size)));
974 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
979 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
983 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
987 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
994 if (unwrap && JsonbType(jb) == jbvArray)
995 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
998 if (jb->type == jbvNumeric)
1000 char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1001 NumericGetDatum(jb->val.numeric)));
1003 (void) float8in_internal(tmp,
1010 else if (jb->type == jbvString)
1012 /* cast string as double */
1014 char *tmp = pnstrdup(jb->val.string.val,
1015 jb->val.string.len);
1017 val = float8in_internal(tmp,
1023 RETURN_ERROR(ereport(ERROR,
1024 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1025 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1026 errdetail("jsonpath item method .%s() can "
1027 "only be applied to a numeric value",
1028 jspOperationName(jsp->type)))));
1031 jb->type = jbvNumeric;
1032 jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1033 Float8GetDatum(val)));
1037 if (res == jperNotFound)
1038 RETURN_ERROR(ereport(ERROR,
1039 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1040 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1041 errdetail("jsonpath item method .%s() "
1042 "can only be applied to a "
1043 "string or numeric value",
1044 jspOperationName(jsp->type)))));
1046 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1051 if (unwrap && JsonbType(jb) == jbvArray)
1052 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1054 return executeKeyValueMethod(cxt, jsp, jb, found);
1057 elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1064 * Unwrap current array item and execute jsonpath for each of its elements.
1066 static JsonPathExecResult
1067 executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1068 JsonbValue *jb, JsonValueList *found,
1069 bool unwrapElements)
1071 if (jb->type != jbvBinary)
1073 Assert(jb->type != jbvArray);
1074 elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1077 return executeAnyItem
1078 (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1079 false, unwrapElements);
1083 * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1086 static JsonPathExecResult
1087 executeNextItem(JsonPathExecContext *cxt,
1088 JsonPathItem *cur, JsonPathItem *next,
1089 JsonbValue *v, JsonValueList *found, bool copy)
1095 hasNext = next != NULL;
1097 hasNext = jspHasNext(cur);
1101 hasNext = jspGetNext(cur, next);
1105 return executeItem(cxt, next, v, found);
1108 JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1114 * Same as executeItem(), but when "unwrap == true" automatically unwraps
1115 * each array item from the resulting sequence in lax mode.
1117 static JsonPathExecResult
1118 executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1119 JsonbValue *jb, bool unwrap,
1120 JsonValueList *found)
1122 if (unwrap && jspAutoUnwrap(cxt))
1124 JsonValueList seq = {0};
1125 JsonValueListIterator it;
1126 JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1129 if (jperIsError(res))
1132 JsonValueListInitIterator(&seq, &it);
1133 while ((item = JsonValueListNext(&seq, &it)))
1135 Assert(item->type != jbvArray);
1137 if (JsonbType(item) == jbvArray)
1138 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1140 JsonValueListAppend(found, item);
1146 return executeItem(cxt, jsp, jb, found);
1150 * Same as executeItemOptUnwrapResult(), but with error suppression.
1152 static JsonPathExecResult
1153 executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1155 JsonbValue *jb, bool unwrap,
1156 JsonValueList *found)
1158 JsonPathExecResult res;
1159 bool throwErrors = cxt->throwErrors;
1161 cxt->throwErrors = false;
1162 res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1163 cxt->throwErrors = throwErrors;
1168 /* Execute boolean-valued jsonpath expression. */
1170 executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1171 JsonbValue *jb, bool canHaveNext)
1178 if (!canHaveNext && jspHasNext(jsp))
1179 elog(ERROR, "boolean jsonpath item cannot have next item");
1184 jspGetLeftArg(jsp, &larg);
1185 res = executeBoolItem(cxt, &larg, jb, false);
1187 if (res == jpbFalse)
1191 * SQL/JSON says that we should check second arg in case of
1195 jspGetRightArg(jsp, &rarg);
1196 res2 = executeBoolItem(cxt, &rarg, jb, false);
1198 return res2 == jpbTrue ? res : res2;
1201 jspGetLeftArg(jsp, &larg);
1202 res = executeBoolItem(cxt, &larg, jb, false);
1207 jspGetRightArg(jsp, &rarg);
1208 res2 = executeBoolItem(cxt, &rarg, jb, false);
1210 return res2 == jpbFalse ? res : res2;
1213 jspGetArg(jsp, &larg);
1215 res = executeBoolItem(cxt, &larg, jb, false);
1217 if (res == jpbUnknown)
1220 return res == jpbTrue ? jpbFalse : jpbTrue;
1223 jspGetArg(jsp, &larg);
1224 res = executeBoolItem(cxt, &larg, jb, false);
1225 return res == jpbUnknown ? jpbTrue : jpbFalse;
1231 case jpiLessOrEqual:
1232 case jpiGreaterOrEqual:
1233 jspGetLeftArg(jsp, &larg);
1234 jspGetRightArg(jsp, &rarg);
1235 return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1236 executeComparison, NULL);
1238 case jpiStartsWith: /* 'whole STARTS WITH initial' */
1239 jspGetLeftArg(jsp, &larg); /* 'whole' */
1240 jspGetRightArg(jsp, &rarg); /* 'initial' */
1241 return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1242 executeStartsWith, NULL);
1244 case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1247 * 'expr' is a sequence-returning expression. 'pattern' is a
1248 * regex string literal. SQL/JSON standard requires XQuery
1249 * regexes, but we use Postgres regexes here. 'flags' is a
1250 * string literal converted to integer flags at compile-time.
1252 JsonLikeRegexContext lrcxt = {0};
1254 jspInitByBuffer(&larg, jsp->base,
1255 jsp->content.like_regex.expr);
1257 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1258 executeLikeRegex, &lrcxt);
1262 jspGetArg(jsp, &larg);
1264 if (jspStrictAbsenseOfErrors(cxt))
1267 * In strict mode we must get a complete list of values to
1268 * check that there are no errors at all.
1270 JsonValueList vals = {0};
1271 JsonPathExecResult res =
1272 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1275 if (jperIsError(res))
1278 return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1282 JsonPathExecResult res =
1283 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1286 if (jperIsError(res))
1289 return res == jperOk ? jpbTrue : jpbFalse;
1293 elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1299 * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1300 * item onto the stack.
1303 executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1309 prev = cxt->current;
1311 res = executeBoolItem(cxt, jsp, jb, false);
1312 cxt->current = prev;
1318 * Implementation of several jsonpath nodes:
1319 * - jpiAny (.** accessor),
1320 * - jpiAnyKey (.* accessor),
1321 * - jpiAnyArray ([*] accessor)
1323 static JsonPathExecResult
1324 executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1325 JsonValueList *found, uint32 level, uint32 first, uint32 last,
1326 bool ignoreStructuralErrors, bool unwrapNext)
1328 JsonPathExecResult res = jperNotFound;
1333 check_stack_depth();
1338 it = JsonbIteratorInit(jbc);
1341 * Recursively iterate over jsonb objects/arrays
1343 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1347 r = JsonbIteratorNext(&it, &v, true);
1348 Assert(r == WJB_VALUE);
1351 if (r == WJB_VALUE || r == WJB_ELEM)
1354 if (level >= first ||
1355 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1356 v.type != jbvBinary)) /* leaves only requested */
1358 /* check expression */
1361 if (ignoreStructuralErrors)
1363 bool savedIgnoreStructuralErrors;
1365 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1366 cxt->ignoreStructuralErrors = true;
1367 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1368 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1371 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1373 if (jperIsError(res))
1376 if (res == jperOk && !found)
1380 JsonValueListAppend(found, copyJsonbValue(&v));
1385 if (level < last && v.type == jbvBinary)
1387 res = executeAnyItem
1388 (cxt, jsp, v.val.binary.data, found,
1389 level + 1, first, last,
1390 ignoreStructuralErrors, unwrapNext);
1392 if (jperIsError(res))
1395 if (res == jperOk && found == NULL)
1405 * Execute unary or binary predicate.
1407 * Predicates have existence semantics, because their operands are item
1408 * sequences. Pairs of items from the left and right operand's sequences are
1409 * checked. TRUE returned only if any pair satisfying the condition is found.
1410 * In strict mode, even if the desired pair has already been found, all pairs
1411 * still need to be examined to check the absence of errors. If any error
1412 * occurs, UNKNOWN (analogous to SQL NULL) is returned.
1415 executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
1416 JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
1417 bool unwrapRightArg, JsonPathPredicateCallback exec,
1420 JsonPathExecResult res;
1421 JsonValueListIterator lseqit;
1422 JsonValueList lseq = {0};
1423 JsonValueList rseq = {0};
1428 /* Left argument is always auto-unwrapped. */
1429 res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
1430 if (jperIsError(res))
1435 /* Right argument is conditionally auto-unwrapped. */
1436 res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
1437 unwrapRightArg, &rseq);
1438 if (jperIsError(res))
1442 JsonValueListInitIterator(&lseq, &lseqit);
1443 while ((lval = JsonValueListNext(&lseq, &lseqit)))
1445 JsonValueListIterator rseqit;
1451 JsonValueListInitIterator(&rseq, &rseqit);
1452 rval = JsonValueListNext(&rseq, &rseqit);
1459 /* Loop over right arg sequence or do single pass otherwise */
1460 while (rarg ? (rval != NULL) : first)
1462 JsonPathBool res = exec(pred, lval, rval, param);
1464 if (res == jpbUnknown)
1466 if (jspStrictAbsenseOfErrors(cxt))
1471 else if (res == jpbTrue)
1473 if (!jspStrictAbsenseOfErrors(cxt))
1481 rval = JsonValueListNext(&rseq, &rseqit);
1485 if (found) /* possible only in strict mode */
1488 if (error) /* possible only in lax mode */
1495 * Execute binary arithmetic expression on singleton numeric operands.
1496 * Array operands are automatically unwrapped in lax mode.
1498 static JsonPathExecResult
1499 executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1500 JsonbValue *jb, PGFunction func,
1501 JsonValueList *found)
1503 JsonPathExecResult jper;
1505 JsonValueList lseq = {0};
1506 JsonValueList rseq = {0};
1511 jspGetLeftArg(jsp, &elem);
1514 * XXX: By standard only operands of multiplicative expressions are
1515 * unwrapped. We extend it to other binary arithmetics expressions too.
1517 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
1518 if (jperIsError(jper))
1521 jspGetRightArg(jsp, &elem);
1523 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
1524 if (jperIsError(jper))
1527 if (JsonValueListLength(&lseq) != 1 ||
1528 !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
1529 RETURN_ERROR(ereport(ERROR,
1530 (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
1531 errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
1532 errdetail("left operand of binary jsonpath operator %s "
1533 "is not a singleton numeric value",
1534 jspOperationName(jsp->type)))));
1536 if (JsonValueListLength(&rseq) != 1 ||
1537 !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
1538 RETURN_ERROR(ereport(ERROR,
1539 (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
1540 errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
1541 errdetail("right operand of binary jsonpath operator %s "
1542 "is not a singleton numeric value",
1543 jspOperationName(jsp->type)))));
1545 res = DirectFunctionCall2(func,
1546 NumericGetDatum(lval->val.numeric),
1547 NumericGetDatum(rval->val.numeric));
1549 if (!jspGetNext(jsp, &elem) && !found)
1552 lval = palloc(sizeof(*lval));
1553 lval->type = jbvNumeric;
1554 lval->val.numeric = DatumGetNumeric(res);
1556 return executeNextItem(cxt, jsp, &elem, lval, found, false);
1560 * Execute unary arithmetic expression for each numeric item in its operand's
1561 * sequence. Array operand is automatically unwrapped in lax mode.
1563 static JsonPathExecResult
1564 executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1565 JsonbValue *jb, PGFunction func, JsonValueList *found)
1567 JsonPathExecResult jper;
1568 JsonPathExecResult jper2;
1570 JsonValueList seq = {0};
1571 JsonValueListIterator it;
1575 jspGetArg(jsp, &elem);
1576 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
1578 if (jperIsError(jper))
1581 jper = jperNotFound;
1583 hasNext = jspGetNext(jsp, &elem);
1585 JsonValueListInitIterator(&seq, &it);
1586 while ((val = JsonValueListNext(&seq, &it)))
1588 if ((val = getScalar(val, jbvNumeric)))
1590 if (!found && !hasNext)
1595 if (!found && !hasNext)
1596 continue; /* skip non-numerics processing */
1598 RETURN_ERROR(ereport(ERROR,
1599 (errcode(ERRCODE_JSON_NUMBER_NOT_FOUND),
1600 errmsg(ERRMSG_JSON_NUMBER_NOT_FOUND),
1601 errdetail("operand of unary jsonpath operator %s "
1602 "is not a numeric value",
1603 jspOperationName(jsp->type)))));
1608 DatumGetNumeric(DirectFunctionCall1(func,
1609 NumericGetDatum(val->val.numeric)));
1611 jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
1613 if (jperIsError(jper2))
1616 if (jper2 == jperOk)
1628 * STARTS_WITH predicate callback.
1630 * Check if the 'whole' string starts from 'initial' string.
1633 executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
1636 if (!(whole = getScalar(whole, jbvString)))
1637 return jpbUnknown; /* error */
1639 if (!(initial = getScalar(initial, jbvString)))
1640 return jpbUnknown; /* error */
1642 if (whole->val.string.len >= initial->val.string.len &&
1643 !memcmp(whole->val.string.val,
1644 initial->val.string.val,
1645 initial->val.string.len))
1652 * LIKE_REGEX predicate callback.
1654 * Check if the string matches regex pattern.
1657 executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
1660 JsonLikeRegexContext *cxt = param;
1662 if (!(str = getScalar(str, jbvString)))
1665 /* Cache regex text and converted flags. */
1668 uint32 flags = jsp->content.like_regex.flags;
1671 cstring_to_text_with_len(jsp->content.like_regex.pattern,
1672 jsp->content.like_regex.patternlen);
1674 /* Convert regex flags. */
1675 cxt->cflags = REG_ADVANCED;
1677 if (flags & JSP_REGEX_ICASE)
1678 cxt->cflags |= REG_ICASE;
1679 if (flags & JSP_REGEX_MLINE)
1680 cxt->cflags |= REG_NEWLINE;
1681 if (flags & JSP_REGEX_SLINE)
1682 cxt->cflags &= ~REG_NEWLINE;
1683 if (flags & JSP_REGEX_WSPACE)
1684 cxt->cflags |= REG_EXPANDED;
1687 if (RE_compile_and_execute(cxt->regex, str->val.string.val,
1688 str->val.string.len,
1689 cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
1696 * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
1697 * user function 'func'.
1699 static JsonPathExecResult
1700 executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1701 JsonbValue *jb, bool unwrap, PGFunction func,
1702 JsonValueList *found)
1707 if (unwrap && JsonbType(jb) == jbvArray)
1708 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1710 if (!(jb = getScalar(jb, jbvNumeric)))
1711 RETURN_ERROR(ereport(ERROR,
1712 (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
1713 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
1714 errdetail("jsonpath item method .%s() can only "
1715 "be applied to a numeric value",
1716 jspOperationName(jsp->type)))));
1718 datum = NumericGetDatum(jb->val.numeric);
1719 datum = DirectFunctionCall1(func, datum);
1721 if (!jspGetNext(jsp, &next) && !found)
1724 jb = palloc(sizeof(*jb));
1725 jb->type = jbvNumeric;
1726 jb->val.numeric = DatumGetNumeric(datum);
1728 return executeNextItem(cxt, jsp, &next, jb, found, false);
1732 * Implementation of .keyvalue() method.
1734 * .keyvalue() method returns a sequence of object's key-value pairs in the
1735 * following format: '{ "key": key, "value": value, "id": id }'.
1737 * "id" field is an object identifier which is constructed from the two parts:
1738 * base object id and its binary offset in base object's jsonb:
1739 * id = 10000000000 * base_object_id + obj_offset_in_base_object
1741 * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1742 * (maximal offset in jsonb). Decimal multiplier is used here to improve the
1743 * readability of identifiers.
1745 * Base object is usually a root object of the path: context item '$' or path
1746 * variable '$var', literals can't produce objects for now. But if the path
1747 * contains generated objects (.keyvalue() itself, for example), then they
1748 * become base object for the subsequent .keyvalue().
1750 * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
1751 * of variables (see getJsonPathVariable()). Ids for generated objects
1752 * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
1754 static JsonPathExecResult
1755 executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1756 JsonbValue *jb, JsonValueList *found)
1758 JsonPathExecResult res = jperNotFound;
1760 JsonbContainer *jbc;
1768 JsonbIteratorToken tok;
1772 if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
1773 RETURN_ERROR(ereport(ERROR,
1774 (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
1775 errmsg(ERRMSG_JSON_OBJECT_NOT_FOUND),
1776 errdetail("jsonpath item method .%s() "
1777 "can only be applied to an object",
1778 jspOperationName(jsp->type)))));
1780 jbc = jb->val.binary.data;
1782 if (!JsonContainerSize(jbc))
1783 return jperNotFound; /* no key-value pairs */
1785 hasNext = jspGetNext(jsp, &next);
1787 keystr.type = jbvString;
1788 keystr.val.string.val = "key";
1789 keystr.val.string.len = 3;
1791 valstr.type = jbvString;
1792 valstr.val.string.val = "value";
1793 valstr.val.string.len = 5;
1795 idstr.type = jbvString;
1796 idstr.val.string.val = "id";
1797 idstr.val.string.len = 2;
1799 /* construct object id from its base object and offset inside that */
1800 id = jb->type != jbvBinary ? 0 :
1801 (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
1802 id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
1804 idval.type = jbvNumeric;
1805 idval.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
1806 Int64GetDatum(id)));
1808 it = JsonbIteratorInit(jbc);
1810 while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
1812 JsonBaseObjectInfo baseObject;
1814 JsonbParseState *ps;
1823 if (!hasNext && !found)
1826 tok = JsonbIteratorNext(&it, &val, true);
1827 Assert(tok == WJB_VALUE);
1830 pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
1832 pushJsonbValue(&ps, WJB_KEY, &keystr);
1833 pushJsonbValue(&ps, WJB_VALUE, &key);
1835 pushJsonbValue(&ps, WJB_KEY, &valstr);
1836 pushJsonbValue(&ps, WJB_VALUE, &val);
1838 pushJsonbValue(&ps, WJB_KEY, &idstr);
1839 pushJsonbValue(&ps, WJB_VALUE, &idval);
1841 keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
1843 jsonb = JsonbValueToJsonb(keyval);
1845 JsonbInitBinary(&obj, jsonb);
1847 baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
1849 res = executeNextItem(cxt, jsp, &next, &obj, found, true);
1851 cxt->baseObject = baseObject;
1853 if (jperIsError(res))
1856 if (res == jperOk && !found)
1864 * Convert boolean execution status 'res' to a boolean JSON item and execute
1867 static JsonPathExecResult
1868 appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1869 JsonValueList *found, JsonPathBool res)
1874 if (!jspGetNext(jsp, &next) && !found)
1875 return jperOk; /* found singleton boolean value */
1877 if (res == jpbUnknown)
1884 jbv.val.boolean = res == jpbTrue;
1887 return executeNextItem(cxt, jsp, &next, &jbv, found, true);
1891 * Convert jsonpath's scalar or variable node to actual jsonb value.
1893 * If node is a variable then its id returned, otherwise 0 returned.
1896 getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
1902 value->type = jbvNull;
1905 value->type = jbvBool;
1906 value->val.boolean = jspGetBool(item);
1909 value->type = jbvNumeric;
1910 value->val.numeric = jspGetNumeric(item);
1913 value->type = jbvString;
1914 value->val.string.val = jspGetString(item,
1915 &value->val.string.len);
1918 getJsonPathVariable(cxt, item, cxt->vars, value);
1921 elog(ERROR, "unexpected jsonpath item type");
1926 * Get the value of variable passed to jsonpath executor
1929 getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
1930 Jsonb *vars, JsonbValue *value)
1939 value->type = jbvNull;
1943 Assert(variable->type == jpiVariable);
1944 varName = jspGetString(variable, &varNameLength);
1945 tmp.type = jbvString;
1946 tmp.val.string.val = varName;
1947 tmp.val.string.len = varNameLength;
1949 v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
1959 (errcode(ERRCODE_UNDEFINED_OBJECT),
1960 errmsg("cannot find jsonpath variable '%s'",
1961 pnstrdup(varName, varNameLength))));
1964 JsonbInitBinary(&tmp, vars);
1965 setBaseObject(cxt, &tmp, 1);
1968 /**************** Support functions for JsonPath execution *****************/
1971 * Returns the size of an array item, or -1 if item is not an array.
1974 JsonbArraySize(JsonbValue *jb)
1976 Assert(jb->type != jbvArray);
1978 if (jb->type == jbvBinary)
1980 JsonbContainer *jbc = jb->val.binary.data;
1982 if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
1983 return JsonContainerSize(jbc);
1989 /* Comparison predicate callback. */
1991 executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
1993 return compareItems(cmp->type, lv, rv);
1997 * Compare two SQL/JSON items using comparison operation 'op'.
2000 compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2)
2005 if (jb1->type != jb2->type)
2007 if (jb1->type == jbvNull || jb2->type == jbvNull)
2010 * Equality and order comparison of nulls to non-nulls returns
2011 * always false, but inequality comparison returns true.
2013 return op == jpiNotEqual ? jpbTrue : jpbFalse;
2015 /* Non-null items of different types are not comparable. */
2025 cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2026 jb1->val.boolean ? 1 : -1;
2029 cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2033 return jb1->val.string.len != jb2->val.string.len ||
2034 memcmp(jb1->val.string.val,
2035 jb2->val.string.val,
2036 jb1->val.string.len) ? jpbFalse : jpbTrue;
2038 cmp = varstr_cmp(jb1->val.string.val, jb1->val.string.len,
2039 jb2->val.string.val, jb2->val.string.len,
2040 DEFAULT_COLLATION_OID);
2046 return jpbUnknown; /* non-scalars are not comparable */
2049 elog(ERROR, "invalid jsonb value type %d", jb1->type);
2066 case jpiLessOrEqual:
2069 case jpiGreaterOrEqual:
2073 elog(ERROR, "unrecognized jsonpath operation: %d", op);
2077 return res ? jpbTrue : jpbFalse;
2080 /* Compare two numerics */
2082 compareNumeric(Numeric a, Numeric b)
2084 return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2086 PointerGetDatum(b)));
2090 copyJsonbValue(JsonbValue *src)
2092 JsonbValue *dst = palloc(sizeof(*dst));
2100 * Execute array subscript expression and convert resulting numeric item to
2101 * the integer type with truncation.
2103 static JsonPathExecResult
2104 getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2108 JsonValueList found = {0};
2109 JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2110 Datum numeric_index;
2112 if (jperIsError(res))
2115 if (JsonValueListLength(&found) != 1 ||
2116 !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2117 RETURN_ERROR(ereport(ERROR,
2118 (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
2119 errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
2120 errdetail("jsonpath array subscript is not a "
2121 "singleton numeric value"))));
2123 numeric_index = DirectFunctionCall2(numeric_trunc,
2124 NumericGetDatum(jbv->val.numeric),
2127 *index = DatumGetInt32(DirectFunctionCall1(numeric_int4, numeric_index));
2132 /* Save base object and its id needed for the execution of .keyvalue(). */
2133 static JsonBaseObjectInfo
2134 setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2136 JsonBaseObjectInfo baseObject = cxt->baseObject;
2138 cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2139 (JsonbContainer *) jbv->val.binary.data;
2140 cxt->baseObject.id = id;
2146 JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2150 jvl->list = list_make2(jvl->singleton, jbv);
2151 jvl->singleton = NULL;
2153 else if (!jvl->list)
2154 jvl->singleton = jbv;
2156 jvl->list = lappend(jvl->list, jbv);
2160 JsonValueListLength(const JsonValueList *jvl)
2162 return jvl->singleton ? 1 : list_length(jvl->list);
2166 JsonValueListIsEmpty(JsonValueList *jvl)
2168 return !jvl->singleton && list_length(jvl->list) <= 0;
2172 JsonValueListHead(JsonValueList *jvl)
2174 return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2178 JsonValueListGetList(JsonValueList *jvl)
2181 return list_make1(jvl->singleton);
2187 JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2191 it->value = jvl->singleton;
2194 else if (list_head(jvl->list) != NULL)
2196 it->value = (JsonbValue *) linitial(jvl->list);
2197 it->next = lnext(list_head(jvl->list));
2207 * Get the next item from the sequence advancing iterator.
2210 JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2212 JsonbValue *result = it->value;
2216 it->value = lfirst(it->next);
2217 it->next = lnext(it->next);
2228 * Initialize a binary JsonbValue with the given jsonb container.
2231 JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2233 jbv->type = jbvBinary;
2234 jbv->val.binary.data = &jb->root;
2235 jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2241 * Returns jbv* type of of JsonbValue. Note, it never returns jbvBinary as is.
2244 JsonbType(JsonbValue *jb)
2246 int type = jb->type;
2248 if (jb->type == jbvBinary)
2250 JsonbContainer *jbc = (void *) jb->val.binary.data;
2252 /* Scalars should be always extracted during jsonpath execution. */
2253 Assert(!JsonContainerIsScalar(jbc));
2255 if (JsonContainerIsObject(jbc))
2257 else if (JsonContainerIsArray(jbc))
2260 elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2266 /* Get scalar of given type or NULL on type mismatch */
2268 getScalar(JsonbValue *scalar, enum jbvType type)
2270 /* Scalars should be always extracted during jsonpath execution. */
2271 Assert(scalar->type != jbvBinary ||
2272 !JsonContainerIsScalar(scalar->val.binary.data));
2274 return scalar->type == type ? scalar : NULL;
2277 /* Construct a JSON array from the item list */
2279 wrapItemsInArray(const JsonValueList *items)
2281 JsonbParseState *ps = NULL;
2282 JsonValueListIterator it;
2285 pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2287 JsonValueListInitIterator(items, &it);
2288 while ((jbv = JsonValueListNext(items, &it)))
2289 pushJsonbValue(&ps, WJB_ELEM, jbv);
2291 return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);