1 /*-------------------------------------------------------------------------
4 * Routines for SQL/JSON path execution.
6 * Jsonpath is executed in the global context stored in JsonPathExecContext,
7 * which is passed to almost every function involved into execution. Entry
8 * point for jsonpath execution is executeJsonPath() function, which
9 * initializes execution context including initial JsonPathItem and JsonbValue,
10 * flags, stack for calculation of @ in filters.
12 * The result of jsonpath query execution is enum JsonPathExecResult and
13 * if succeeded sequence of JsonbValue, written to JsonValueList *found, which
14 * is passed through the jsonpath items. When found == NULL, we're inside
15 * exists-query and we're interested only in whether result is empty. In this
16 * case execution is stopped once first result item is found, and the only
17 * execution result is JsonPathExecResult. The values of JsonPathExecResult
19 * - jperOk -- result sequence is not empty
20 * - jperNotFound -- result sequence is empty
21 * - jperError -- error occurred during execution
23 * Jsonpath is executed recursively (see executeItem()) starting form the
24 * first path item (which in turn might be, for instance, an arithmetic
25 * expression evaluated separately). On each step single JsonbValue obtained
26 * from previous path item is processed. The result of processing is a
27 * sequence of JsonbValue (probably empty), which is passed to the next path
28 * item one by one. When there is no next path item, then JsonbValue is added
29 * to the 'found' list. When found == NULL, then execution functions just
30 * return jperOk (see executeNextItem()).
32 * Many of jsonpath operations require automatic unwrapping of arrays in lax
33 * mode. So, if input value is array, then corresponding operation is
34 * processed not on array itself, but on all of its members one by one.
35 * executeItemOptUnwrapTarget() function have 'unwrap' argument, which indicates
36 * whether unwrapping of array is needed. When unwrap == true, each of array
37 * members is passed to executeItemOptUnwrapTarget() again but with unwrap == false
38 * in order to evade subsequent array unwrapping.
40 * All boolean expressions (predicates) are evaluated by executeBoolItem()
41 * function, which returns tri-state JsonPathBool. When error is occurred
42 * during predicate execution, it returns jpbUnknown. According to standard
43 * predicates can be only inside filters. But we support their usage as
44 * jsonpath expression. This helps us to implement @@ operator. In this case
45 * resulting JsonPathBool is transformed into jsonb bool or null.
47 * Arithmetic and boolean expression are evaluated recursively from expression
48 * tree top down to the leaves. Therefore, for binary arithmetic expressions
49 * we calculate operands first. Then we check that results are numeric
50 * singleton lists, calculate the result and pass it to the next path item.
52 * Copyright (c) 2019, PostgreSQL Global Development Group
55 * src/backend/utils/adt/jsonpath_exec.c
57 *-------------------------------------------------------------------------
62 #include "catalog/pg_collation.h"
63 #include "catalog/pg_type.h"
65 #include "lib/stringinfo.h"
66 #include "miscadmin.h"
67 #include "regex/regex.h"
68 #include "utils/builtins.h"
69 #include "utils/datum.h"
70 #include "utils/formatting.h"
71 #include "utils/float.h"
72 #include "utils/guc.h"
73 #include "utils/json.h"
74 #include "utils/jsonpath.h"
75 #include "utils/date.h"
76 #include "utils/timestamp.h"
77 #include "utils/varlena.h"
81 * Represents "base object" and it's "id" for .keyvalue() evaluation.
83 typedef struct JsonBaseObjectInfo
90 * Context of jsonpath execution.
92 typedef struct JsonPathExecContext
94 Jsonb *vars; /* variables to substitute into jsonpath */
95 JsonbValue *root; /* for $ evaluation */
96 JsonbValue *current; /* for @ evaluation */
97 JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue()
99 int lastGeneratedObjectId; /* "id" counter for .keyvalue()
101 int innermostArraySize; /* for LAST array index evaluation */
102 bool laxMode; /* true for "lax" mode, false for "strict"
104 bool ignoreStructuralErrors; /* with "true" structural errors such
105 * as absence of required json item or
106 * unexpected json item type are
108 bool throwErrors; /* with "false" all suppressible errors are
110 } JsonPathExecContext;
112 /* Context for LIKE_REGEX execution. */
113 typedef struct JsonLikeRegexContext
117 } JsonLikeRegexContext;
119 /* Result of jsonpath predicate evaluation */
120 typedef enum JsonPathBool
127 /* Result of jsonpath expression evaluation */
128 typedef enum JsonPathExecResult
133 } JsonPathExecResult;
135 #define jperIsError(jper) ((jper) == jperError)
138 * List of jsonb values with shortcut for single-value list.
140 typedef struct JsonValueList
142 JsonbValue *singleton;
146 typedef struct JsonValueListIterator
151 } JsonValueListIterator;
153 /* strict/lax flags is decomposed into four [un]wrap/error flags */
154 #define jspStrictAbsenseOfErrors(cxt) (!(cxt)->laxMode)
155 #define jspAutoUnwrap(cxt) ((cxt)->laxMode)
156 #define jspAutoWrap(cxt) ((cxt)->laxMode)
157 #define jspIgnoreStructuralErrors(cxt) ((cxt)->ignoreStructuralErrors)
158 #define jspThrowErrors(cxt) ((cxt)->throwErrors)
160 /* Convenience macro: return or throw error depending on context */
161 #define RETURN_ERROR(throw_error) \
163 if (jspThrowErrors(cxt)) \
169 typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
173 typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
175 static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
176 Jsonb *json, bool throwErrors, JsonValueList *result);
177 static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
178 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
179 static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
180 JsonPathItem *jsp, JsonbValue *jb,
181 JsonValueList *found, bool unwrap);
182 static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
183 JsonPathItem *jsp, JsonbValue *jb,
184 JsonValueList *found, bool unwrapElements);
185 static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
186 JsonPathItem *cur, JsonPathItem *next,
187 JsonbValue *v, JsonValueList *found, bool copy);
188 static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
189 bool unwrap, JsonValueList *found);
190 static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp,
191 JsonbValue *jb, bool unwrap, JsonValueList *found);
192 static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
193 JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
194 static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
195 JsonPathItem *jsp, JsonbValue *jb);
196 static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
197 JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
198 uint32 level, uint32 first, uint32 last,
199 bool ignoreStructuralErrors, bool unwrapNext);
200 static JsonPathBool executePredicate(JsonPathExecContext *cxt,
201 JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
202 JsonbValue *jb, bool unwrapRightArg,
203 JsonPathPredicateCallback exec, void *param);
204 static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
205 JsonPathItem *jsp, JsonbValue *jb,
206 BinaryArithmFunc func, JsonValueList *found);
207 static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
208 JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
209 JsonValueList *found);
210 static JsonPathBool executeStartsWith(JsonPathItem *jsp,
211 JsonbValue *whole, JsonbValue *initial, void *param);
212 static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
213 JsonbValue *rarg, void *param);
214 static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
215 JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
216 JsonValueList *found);
217 static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
218 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
219 static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
220 JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
221 static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
223 static void getJsonPathVariable(JsonPathExecContext *cxt,
224 JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
225 static int JsonbArraySize(JsonbValue *jb);
226 static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
227 JsonbValue *rv, void *p);
228 static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2);
229 static int compareNumeric(Numeric a, Numeric b);
230 static JsonbValue *copyJsonbValue(JsonbValue *src);
231 static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
232 JsonPathItem *jsp, JsonbValue *jb, int32 *index);
233 static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
234 JsonbValue *jbv, int32 id);
235 static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
236 static int JsonValueListLength(const JsonValueList *jvl);
237 static bool JsonValueListIsEmpty(JsonValueList *jvl);
238 static JsonbValue *JsonValueListHead(JsonValueList *jvl);
239 static List *JsonValueListGetList(JsonValueList *jvl);
240 static void JsonValueListInitIterator(const JsonValueList *jvl,
241 JsonValueListIterator *it);
242 static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
243 JsonValueListIterator *it);
244 static int JsonbType(JsonbValue *jb);
245 static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
246 static int JsonbType(JsonbValue *jb);
247 static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
248 static JsonbValue *wrapItemsInArray(const JsonValueList *items);
250 /****************** User interface to JsonPath executor ********************/
254 * Returns true if jsonpath returns at least one item for the specified
255 * jsonb value. This function and jsonb_path_match() are used to
256 * implement @? and @@ operators, which in turn are intended to have an
257 * index support. Thus, it's desirable to make it easier to achieve
258 * consistency between index scan results and sequential scan results.
259 * So, we throw as less errors as possible. Regarding this function,
260 * such behavior also matches behavior of JSON_EXISTS() clause of
261 * SQL/JSON. Regarding jsonb_path_match(), this function doesn't have
262 * an analogy in SQL/JSON, so we define its behavior on our own.
265 jsonb_path_exists(PG_FUNCTION_ARGS)
267 Jsonb *jb = PG_GETARG_JSONB_P(0);
268 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
269 JsonPathExecResult res;
275 vars = PG_GETARG_JSONB_P(2);
276 silent = PG_GETARG_BOOL(3);
279 res = executeJsonPath(jp, vars, jb, !silent, NULL);
281 PG_FREE_IF_COPY(jb, 0);
282 PG_FREE_IF_COPY(jp, 1);
284 if (jperIsError(res))
287 PG_RETURN_BOOL(res == jperOk);
291 * jsonb_path_exists_opr
292 * Implementation of operator "jsonb @? jsonpath" (2-argument version of
293 * jsonb_path_exists()).
296 jsonb_path_exists_opr(PG_FUNCTION_ARGS)
298 /* just call the other one -- it can handle both cases */
299 return jsonb_path_exists(fcinfo);
304 * Returns jsonpath predicate result item for the specified jsonb value.
305 * See jsonb_path_exists() comment for details regarding error handling.
308 jsonb_path_match(PG_FUNCTION_ARGS)
310 Jsonb *jb = PG_GETARG_JSONB_P(0);
311 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
312 JsonValueList found = {0};
318 vars = PG_GETARG_JSONB_P(2);
319 silent = PG_GETARG_BOOL(3);
322 (void) executeJsonPath(jp, vars, jb, !silent, &found);
324 PG_FREE_IF_COPY(jb, 0);
325 PG_FREE_IF_COPY(jp, 1);
327 if (JsonValueListLength(&found) == 1)
329 JsonbValue *jbv = JsonValueListHead(&found);
331 if (jbv->type == jbvBool)
332 PG_RETURN_BOOL(jbv->val.boolean);
334 if (jbv->type == jbvNull)
340 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
341 errmsg("single boolean result is expected")));
347 * jsonb_path_match_opr
348 * Implementation of operator "jsonb @@ jsonpath" (2-argument version of
349 * jsonb_path_match()).
352 jsonb_path_match_opr(PG_FUNCTION_ARGS)
354 /* just call the other one -- it can handle both cases */
355 return jsonb_path_match(fcinfo);
360 * Executes jsonpath for given jsonb document and returns result as
364 jsonb_path_query(PG_FUNCTION_ARGS)
366 FuncCallContext *funcctx;
371 if (SRF_IS_FIRSTCALL())
375 MemoryContext oldcontext;
378 JsonValueList found = {0};
380 funcctx = SRF_FIRSTCALL_INIT();
381 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
383 jb = PG_GETARG_JSONB_P_COPY(0);
384 jp = PG_GETARG_JSONPATH_P_COPY(1);
385 vars = PG_GETARG_JSONB_P_COPY(2);
386 silent = PG_GETARG_BOOL(3);
388 (void) executeJsonPath(jp, vars, jb, !silent, &found);
390 funcctx->user_fctx = JsonValueListGetList(&found);
392 MemoryContextSwitchTo(oldcontext);
395 funcctx = SRF_PERCALL_SETUP();
396 found = funcctx->user_fctx;
398 c = list_head(found);
401 SRF_RETURN_DONE(funcctx);
404 funcctx->user_fctx = list_delete_first(found);
406 SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
410 * jsonb_path_query_array
411 * Executes jsonpath for given jsonb document and returns result as
415 jsonb_path_query_array(PG_FUNCTION_ARGS)
417 Jsonb *jb = PG_GETARG_JSONB_P(0);
418 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
419 JsonValueList found = {0};
420 Jsonb *vars = PG_GETARG_JSONB_P(2);
421 bool silent = PG_GETARG_BOOL(3);
423 (void) executeJsonPath(jp, vars, jb, !silent, &found);
425 PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
429 * jsonb_path_query_first
430 * Executes jsonpath for given jsonb document and returns first result
431 * item. If there are no items, NULL returned.
434 jsonb_path_query_first(PG_FUNCTION_ARGS)
436 Jsonb *jb = PG_GETARG_JSONB_P(0);
437 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
438 JsonValueList found = {0};
439 Jsonb *vars = PG_GETARG_JSONB_P(2);
440 bool silent = PG_GETARG_BOOL(3);
442 (void) executeJsonPath(jp, vars, jb, !silent, &found);
444 if (JsonValueListLength(&found) >= 1)
445 PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
450 /********************Execute functions for JsonPath**************************/
453 * Interface to jsonpath executor
455 * 'path' - jsonpath to be executed
456 * 'vars' - variables to be substituted to jsonpath
457 * 'json' - target document for jsonpath evaluation
458 * 'throwErrors' - whether we should throw suppressible errors
459 * 'result' - list to store result items into
461 * Returns an error if a recoverable error happens during processing, or NULL
464 * Note, jsonb and jsonpath values should be available and untoasted during
465 * work because JsonPathItem, JsonbValue and result item could have pointers
466 * into input values. If caller needs to just check if document matches
467 * jsonpath, then it doesn't provide a result arg. In this case executor
468 * works till first positive result and does not check the rest if possible.
469 * In other case it tries to find all the satisfied result items.
471 static JsonPathExecResult
472 executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
473 JsonValueList *result)
475 JsonPathExecContext cxt;
476 JsonPathExecResult res;
482 if (!JsonbExtractScalar(&json->root, &jbv))
483 JsonbInitBinary(&jbv, json);
485 if (vars && !JsonContainerIsObject(&vars->root))
488 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
489 errmsg("\"vars\" argument is not an object"),
490 errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
494 cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
495 cxt.ignoreStructuralErrors = cxt.laxMode;
498 cxt.baseObject.jbc = NULL;
499 cxt.baseObject.id = 0;
500 cxt.lastGeneratedObjectId = vars ? 2 : 1;
501 cxt.innermostArraySize = -1;
502 cxt.throwErrors = throwErrors;
504 if (jspStrictAbsenseOfErrors(&cxt) && !result)
507 * In strict mode we must get a complete list of values to check that
508 * there are no errors at all.
510 JsonValueList vals = {0};
512 res = executeItem(&cxt, &jsp, &jbv, &vals);
514 if (jperIsError(res))
517 return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
520 res = executeItem(&cxt, &jsp, &jbv, result);
522 Assert(!throwErrors || !jperIsError(res));
528 * Execute jsonpath with automatic unwrapping of current item in lax mode.
530 static JsonPathExecResult
531 executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
532 JsonbValue *jb, JsonValueList *found)
534 return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
538 * Main jsonpath executor function: walks on jsonpath structure, finds
539 * relevant parts of jsonb and evaluates expressions over them.
540 * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
542 static JsonPathExecResult
543 executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
544 JsonbValue *jb, JsonValueList *found, bool unwrap)
547 JsonPathExecResult res = jperNotFound;
548 JsonBaseObjectInfo baseObject;
551 CHECK_FOR_INTERRUPTS();
555 /* all boolean item types: */
565 case jpiGreaterOrEqual:
570 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
572 res = appendBoolResult(cxt, jsp, found, st);
577 if (JsonbType(jb) == jbvObject)
582 key.type = jbvString;
583 key.val.string.val = jspGetString(jsp, &key.val.string.len);
585 v = findJsonbValueFromContainer(jb->val.binary.data,
590 res = executeNextItem(cxt, jsp, NULL,
593 /* free value if it was not added to found list */
594 if (jspHasNext(jsp) || !found)
597 else if (!jspIgnoreStructuralErrors(cxt))
601 if (!jspThrowErrors(cxt))
605 (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
606 errmsg("JSON object does not contain key \"%s\"",
607 pnstrdup(key.val.string.val,
608 key.val.string.len))));
611 else if (unwrap && JsonbType(jb) == jbvArray)
612 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
613 else if (!jspIgnoreStructuralErrors(cxt))
616 RETURN_ERROR(ereport(ERROR,
617 (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
618 errmsg("jsonpath member accessor can only be applied to an object"))));
624 baseObject = setBaseObject(cxt, jb, 0);
625 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
626 cxt->baseObject = baseObject;
630 res = executeNextItem(cxt, jsp, NULL, cxt->current,
635 if (JsonbType(jb) == jbvArray)
637 bool hasNext = jspGetNext(jsp, &elem);
639 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
640 jb, found, jspAutoUnwrap(cxt));
642 else if (jspAutoWrap(cxt))
643 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
644 else if (!jspIgnoreStructuralErrors(cxt))
645 RETURN_ERROR(ereport(ERROR,
646 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
647 errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
651 if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
653 int innermostArraySize = cxt->innermostArraySize;
655 int size = JsonbArraySize(jb);
656 bool singleton = size < 0;
657 bool hasNext = jspGetNext(jsp, &elem);
662 cxt->innermostArraySize = size; /* for LAST evaluation */
664 for (i = 0; i < jsp->content.array.nelems; i++)
671 bool range = jspGetArraySubscript(jsp, &from,
674 res = getArrayIndex(cxt, &from, jb, &index_from);
676 if (jperIsError(res))
681 res = getArrayIndex(cxt, &to, jb, &index_to);
683 if (jperIsError(res))
687 index_to = index_from;
689 if (!jspIgnoreStructuralErrors(cxt) &&
691 index_from > index_to ||
693 RETURN_ERROR(ereport(ERROR,
694 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
695 errmsg("jsonpath array subscript is out of bounds"))));
700 if (index_to >= size)
705 for (index = index_from; index <= index_to; index++)
717 v = getIthJsonbValueFromContainer(jb->val.binary.data,
726 if (!hasNext && !found)
729 res = executeNextItem(cxt, jsp, &elem, v, found,
732 if (jperIsError(res))
735 if (res == jperOk && !found)
739 if (jperIsError(res))
742 if (res == jperOk && !found)
746 cxt->innermostArraySize = innermostArraySize;
748 else if (!jspIgnoreStructuralErrors(cxt))
750 RETURN_ERROR(ereport(ERROR,
751 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
752 errmsg("jsonpath array accessor can only be applied to an array"))));
761 bool hasNext = jspGetNext(jsp, &elem);
763 if (cxt->innermostArraySize < 0)
764 elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
766 if (!hasNext && !found)
772 last = cxt->innermostArraySize - 1;
774 lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
776 lastjbv->type = jbvNumeric;
777 lastjbv->val.numeric =
778 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
779 Int32GetDatum(last)));
781 res = executeNextItem(cxt, jsp, &elem,
782 lastjbv, found, hasNext);
787 if (JsonbType(jb) == jbvObject)
789 bool hasNext = jspGetNext(jsp, &elem);
791 if (jb->type != jbvBinary)
792 elog(ERROR, "invalid jsonb object type: %d", jb->type);
794 return executeAnyItem
795 (cxt, hasNext ? &elem : NULL,
796 jb->val.binary.data, found, 1, 1, 1,
797 false, jspAutoUnwrap(cxt));
799 else if (unwrap && JsonbType(jb) == jbvArray)
800 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
801 else if (!jspIgnoreStructuralErrors(cxt))
804 RETURN_ERROR(ereport(ERROR,
805 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
806 errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
811 return executeBinaryArithmExpr(cxt, jsp, jb,
812 numeric_add_opt_error, found);
815 return executeBinaryArithmExpr(cxt, jsp, jb,
816 numeric_sub_opt_error, found);
819 return executeBinaryArithmExpr(cxt, jsp, jb,
820 numeric_mul_opt_error, found);
823 return executeBinaryArithmExpr(cxt, jsp, jb,
824 numeric_div_opt_error, found);
827 return executeBinaryArithmExpr(cxt, jsp, jb,
828 numeric_mod_opt_error, found);
831 return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
834 return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
841 if (unwrap && JsonbType(jb) == jbvArray)
842 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
845 jspGetArg(jsp, &elem);
846 st = executeNestedBoolItem(cxt, &elem, jb);
850 res = executeNextItem(cxt, jsp, NULL,
857 bool hasNext = jspGetNext(jsp, &elem);
859 /* first try without any intermediate steps */
860 if (jsp->content.anybounds.first == 0)
862 bool savedIgnoreStructuralErrors;
864 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
865 cxt->ignoreStructuralErrors = true;
866 res = executeNextItem(cxt, jsp, &elem,
868 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
870 if (res == jperOk && !found)
874 if (jb->type == jbvBinary)
876 (cxt, hasNext ? &elem : NULL,
877 jb->val.binary.data, found,
879 jsp->content.anybounds.first,
880 jsp->content.anybounds.last,
881 true, jspAutoUnwrap(cxt));
893 bool hasNext = jspGetNext(jsp, &elem);
895 if (!hasNext && !found)
897 res = jperOk; /* skip evaluation */
901 v = hasNext ? &vbuf : palloc(sizeof(*v));
903 baseObject = cxt->baseObject;
904 getJsonPathItem(cxt, jsp, v);
906 res = executeNextItem(cxt, jsp, &elem,
908 cxt->baseObject = baseObject;
914 JsonbValue *jbv = palloc(sizeof(*jbv));
916 jbv->type = jbvString;
917 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
918 jbv->val.string.len = strlen(jbv->val.string.val);
920 res = executeNextItem(cxt, jsp, NULL, jbv,
927 int size = JsonbArraySize(jb);
931 if (!jspAutoWrap(cxt))
933 if (!jspIgnoreStructuralErrors(cxt))
934 RETURN_ERROR(ereport(ERROR,
935 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
936 errmsg("jsonpath item method .%s() can only be applied to an array",
937 jspOperationName(jsp->type)))));
944 jb = palloc(sizeof(*jb));
946 jb->type = jbvNumeric;
948 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
949 Int32GetDatum(size)));
951 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
956 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
960 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
964 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
971 if (unwrap && JsonbType(jb) == jbvArray)
972 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
975 if (jb->type == jbvNumeric)
977 char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
978 NumericGetDatum(jb->val.numeric)));
979 bool have_error = false;
981 (void) float8in_internal_opt_error(tmp,
988 RETURN_ERROR(ereport(ERROR,
989 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
990 errmsg("jsonpath item method .%s() can only be applied to a numeric value",
991 jspOperationName(jsp->type)))));
994 else if (jb->type == jbvString)
996 /* cast string as double */
998 char *tmp = pnstrdup(jb->val.string.val,
1000 bool have_error = false;
1002 val = float8in_internal_opt_error(tmp,
1008 if (have_error || isinf(val))
1009 RETURN_ERROR(ereport(ERROR,
1010 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1011 errmsg("jsonpath item method .%s() can only be applied to a numeric value",
1012 jspOperationName(jsp->type)))));
1015 jb->type = jbvNumeric;
1016 jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1017 Float8GetDatum(val)));
1021 if (res == jperNotFound)
1022 RETURN_ERROR(ereport(ERROR,
1023 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1024 errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1025 jspOperationName(jsp->type)))));
1027 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1032 if (unwrap && JsonbType(jb) == jbvArray)
1033 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1035 return executeKeyValueMethod(cxt, jsp, jb, found);
1038 elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1045 * Unwrap current array item and execute jsonpath for each of its elements.
1047 static JsonPathExecResult
1048 executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1049 JsonbValue *jb, JsonValueList *found,
1050 bool unwrapElements)
1052 if (jb->type != jbvBinary)
1054 Assert(jb->type != jbvArray);
1055 elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1058 return executeAnyItem
1059 (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1060 false, unwrapElements);
1064 * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1067 static JsonPathExecResult
1068 executeNextItem(JsonPathExecContext *cxt,
1069 JsonPathItem *cur, JsonPathItem *next,
1070 JsonbValue *v, JsonValueList *found, bool copy)
1076 hasNext = next != NULL;
1078 hasNext = jspHasNext(cur);
1082 hasNext = jspGetNext(cur, next);
1086 return executeItem(cxt, next, v, found);
1089 JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1095 * Same as executeItem(), but when "unwrap == true" automatically unwraps
1096 * each array item from the resulting sequence in lax mode.
1098 static JsonPathExecResult
1099 executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1100 JsonbValue *jb, bool unwrap,
1101 JsonValueList *found)
1103 if (unwrap && jspAutoUnwrap(cxt))
1105 JsonValueList seq = {0};
1106 JsonValueListIterator it;
1107 JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1110 if (jperIsError(res))
1113 JsonValueListInitIterator(&seq, &it);
1114 while ((item = JsonValueListNext(&seq, &it)))
1116 Assert(item->type != jbvArray);
1118 if (JsonbType(item) == jbvArray)
1119 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1121 JsonValueListAppend(found, item);
1127 return executeItem(cxt, jsp, jb, found);
1131 * Same as executeItemOptUnwrapResult(), but with error suppression.
1133 static JsonPathExecResult
1134 executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1136 JsonbValue *jb, bool unwrap,
1137 JsonValueList *found)
1139 JsonPathExecResult res;
1140 bool throwErrors = cxt->throwErrors;
1142 cxt->throwErrors = false;
1143 res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1144 cxt->throwErrors = throwErrors;
1149 /* Execute boolean-valued jsonpath expression. */
1151 executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1152 JsonbValue *jb, bool canHaveNext)
1159 if (!canHaveNext && jspHasNext(jsp))
1160 elog(ERROR, "boolean jsonpath item cannot have next item");
1165 jspGetLeftArg(jsp, &larg);
1166 res = executeBoolItem(cxt, &larg, jb, false);
1168 if (res == jpbFalse)
1172 * SQL/JSON says that we should check second arg in case of
1176 jspGetRightArg(jsp, &rarg);
1177 res2 = executeBoolItem(cxt, &rarg, jb, false);
1179 return res2 == jpbTrue ? res : res2;
1182 jspGetLeftArg(jsp, &larg);
1183 res = executeBoolItem(cxt, &larg, jb, false);
1188 jspGetRightArg(jsp, &rarg);
1189 res2 = executeBoolItem(cxt, &rarg, jb, false);
1191 return res2 == jpbFalse ? res : res2;
1194 jspGetArg(jsp, &larg);
1196 res = executeBoolItem(cxt, &larg, jb, false);
1198 if (res == jpbUnknown)
1201 return res == jpbTrue ? jpbFalse : jpbTrue;
1204 jspGetArg(jsp, &larg);
1205 res = executeBoolItem(cxt, &larg, jb, false);
1206 return res == jpbUnknown ? jpbTrue : jpbFalse;
1212 case jpiLessOrEqual:
1213 case jpiGreaterOrEqual:
1214 jspGetLeftArg(jsp, &larg);
1215 jspGetRightArg(jsp, &rarg);
1216 return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1217 executeComparison, NULL);
1219 case jpiStartsWith: /* 'whole STARTS WITH initial' */
1220 jspGetLeftArg(jsp, &larg); /* 'whole' */
1221 jspGetRightArg(jsp, &rarg); /* 'initial' */
1222 return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1223 executeStartsWith, NULL);
1225 case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1228 * 'expr' is a sequence-returning expression. 'pattern' is a
1229 * regex string literal. SQL/JSON standard requires XQuery
1230 * regexes, but we use Postgres regexes here. 'flags' is a
1231 * string literal converted to integer flags at compile-time.
1233 JsonLikeRegexContext lrcxt = {0};
1235 jspInitByBuffer(&larg, jsp->base,
1236 jsp->content.like_regex.expr);
1238 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1239 executeLikeRegex, &lrcxt);
1243 jspGetArg(jsp, &larg);
1245 if (jspStrictAbsenseOfErrors(cxt))
1248 * In strict mode we must get a complete list of values to
1249 * check that there are no errors at all.
1251 JsonValueList vals = {0};
1252 JsonPathExecResult res =
1253 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1256 if (jperIsError(res))
1259 return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1263 JsonPathExecResult res =
1264 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1267 if (jperIsError(res))
1270 return res == jperOk ? jpbTrue : jpbFalse;
1274 elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1280 * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1281 * item onto the stack.
1284 executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1290 prev = cxt->current;
1292 res = executeBoolItem(cxt, jsp, jb, false);
1293 cxt->current = prev;
1299 * Implementation of several jsonpath nodes:
1300 * - jpiAny (.** accessor),
1301 * - jpiAnyKey (.* accessor),
1302 * - jpiAnyArray ([*] accessor)
1304 static JsonPathExecResult
1305 executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1306 JsonValueList *found, uint32 level, uint32 first, uint32 last,
1307 bool ignoreStructuralErrors, bool unwrapNext)
1309 JsonPathExecResult res = jperNotFound;
1314 check_stack_depth();
1319 it = JsonbIteratorInit(jbc);
1322 * Recursively iterate over jsonb objects/arrays
1324 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1328 r = JsonbIteratorNext(&it, &v, true);
1329 Assert(r == WJB_VALUE);
1332 if (r == WJB_VALUE || r == WJB_ELEM)
1335 if (level >= first ||
1336 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1337 v.type != jbvBinary)) /* leaves only requested */
1339 /* check expression */
1342 if (ignoreStructuralErrors)
1344 bool savedIgnoreStructuralErrors;
1346 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1347 cxt->ignoreStructuralErrors = true;
1348 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1349 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1352 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1354 if (jperIsError(res))
1357 if (res == jperOk && !found)
1361 JsonValueListAppend(found, copyJsonbValue(&v));
1366 if (level < last && v.type == jbvBinary)
1368 res = executeAnyItem
1369 (cxt, jsp, v.val.binary.data, found,
1370 level + 1, first, last,
1371 ignoreStructuralErrors, unwrapNext);
1373 if (jperIsError(res))
1376 if (res == jperOk && found == NULL)
1386 * Execute unary or binary predicate.
1388 * Predicates have existence semantics, because their operands are item
1389 * sequences. Pairs of items from the left and right operand's sequences are
1390 * checked. TRUE returned only if any pair satisfying the condition is found.
1391 * In strict mode, even if the desired pair has already been found, all pairs
1392 * still need to be examined to check the absence of errors. If any error
1393 * occurs, UNKNOWN (analogous to SQL NULL) is returned.
1396 executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
1397 JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
1398 bool unwrapRightArg, JsonPathPredicateCallback exec,
1401 JsonPathExecResult res;
1402 JsonValueListIterator lseqit;
1403 JsonValueList lseq = {0};
1404 JsonValueList rseq = {0};
1409 /* Left argument is always auto-unwrapped. */
1410 res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
1411 if (jperIsError(res))
1416 /* Right argument is conditionally auto-unwrapped. */
1417 res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
1418 unwrapRightArg, &rseq);
1419 if (jperIsError(res))
1423 JsonValueListInitIterator(&lseq, &lseqit);
1424 while ((lval = JsonValueListNext(&lseq, &lseqit)))
1426 JsonValueListIterator rseqit;
1430 JsonValueListInitIterator(&rseq, &rseqit);
1432 rval = JsonValueListNext(&rseq, &rseqit);
1436 /* Loop over right arg sequence or do single pass otherwise */
1437 while (rarg ? (rval != NULL) : first)
1439 JsonPathBool res = exec(pred, lval, rval, param);
1441 if (res == jpbUnknown)
1443 if (jspStrictAbsenseOfErrors(cxt))
1448 else if (res == jpbTrue)
1450 if (!jspStrictAbsenseOfErrors(cxt))
1458 rval = JsonValueListNext(&rseq, &rseqit);
1462 if (found) /* possible only in strict mode */
1465 if (error) /* possible only in lax mode */
1472 * Execute binary arithmetic expression on singleton numeric operands.
1473 * Array operands are automatically unwrapped in lax mode.
1475 static JsonPathExecResult
1476 executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1477 JsonbValue *jb, BinaryArithmFunc func,
1478 JsonValueList *found)
1480 JsonPathExecResult jper;
1482 JsonValueList lseq = {0};
1483 JsonValueList rseq = {0};
1488 jspGetLeftArg(jsp, &elem);
1491 * XXX: By standard only operands of multiplicative expressions are
1492 * unwrapped. We extend it to other binary arithmetic expressions too.
1494 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
1495 if (jperIsError(jper))
1498 jspGetRightArg(jsp, &elem);
1500 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
1501 if (jperIsError(jper))
1504 if (JsonValueListLength(&lseq) != 1 ||
1505 !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
1506 RETURN_ERROR(ereport(ERROR,
1507 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1508 errmsg("left operand of jsonpath operator %s is not a single numeric value",
1509 jspOperationName(jsp->type)))));
1511 if (JsonValueListLength(&rseq) != 1 ||
1512 !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
1513 RETURN_ERROR(ereport(ERROR,
1514 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1515 errmsg("right operand of jsonpath operator %s is not a single numeric value",
1516 jspOperationName(jsp->type)))));
1518 if (jspThrowErrors(cxt))
1520 res = func(lval->val.numeric, rval->val.numeric, NULL);
1526 res = func(lval->val.numeric, rval->val.numeric, &error);
1532 if (!jspGetNext(jsp, &elem) && !found)
1535 lval = palloc(sizeof(*lval));
1536 lval->type = jbvNumeric;
1537 lval->val.numeric = res;
1539 return executeNextItem(cxt, jsp, &elem, lval, found, false);
1543 * Execute unary arithmetic expression for each numeric item in its operand's
1544 * sequence. Array operand is automatically unwrapped in lax mode.
1546 static JsonPathExecResult
1547 executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1548 JsonbValue *jb, PGFunction func, JsonValueList *found)
1550 JsonPathExecResult jper;
1551 JsonPathExecResult jper2;
1553 JsonValueList seq = {0};
1554 JsonValueListIterator it;
1558 jspGetArg(jsp, &elem);
1559 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
1561 if (jperIsError(jper))
1564 jper = jperNotFound;
1566 hasNext = jspGetNext(jsp, &elem);
1568 JsonValueListInitIterator(&seq, &it);
1569 while ((val = JsonValueListNext(&seq, &it)))
1571 if ((val = getScalar(val, jbvNumeric)))
1573 if (!found && !hasNext)
1578 if (!found && !hasNext)
1579 continue; /* skip non-numerics processing */
1581 RETURN_ERROR(ereport(ERROR,
1582 (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
1583 errmsg("operand of unary jsonpath operator %s is not a numeric value",
1584 jspOperationName(jsp->type)))));
1589 DatumGetNumeric(DirectFunctionCall1(func,
1590 NumericGetDatum(val->val.numeric)));
1592 jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
1594 if (jperIsError(jper2))
1597 if (jper2 == jperOk)
1609 * STARTS_WITH predicate callback.
1611 * Check if the 'whole' string starts from 'initial' string.
1614 executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
1617 if (!(whole = getScalar(whole, jbvString)))
1618 return jpbUnknown; /* error */
1620 if (!(initial = getScalar(initial, jbvString)))
1621 return jpbUnknown; /* error */
1623 if (whole->val.string.len >= initial->val.string.len &&
1624 !memcmp(whole->val.string.val,
1625 initial->val.string.val,
1626 initial->val.string.len))
1633 * LIKE_REGEX predicate callback.
1635 * Check if the string matches regex pattern.
1638 executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
1641 JsonLikeRegexContext *cxt = param;
1643 if (!(str = getScalar(str, jbvString)))
1646 /* Cache regex text and converted flags. */
1649 uint32 flags = jsp->content.like_regex.flags;
1652 cstring_to_text_with_len(jsp->content.like_regex.pattern,
1653 jsp->content.like_regex.patternlen);
1655 /* Convert regex flags. */
1656 cxt->cflags = REG_ADVANCED;
1658 if (flags & JSP_REGEX_ICASE)
1659 cxt->cflags |= REG_ICASE;
1660 if (flags & JSP_REGEX_MLINE)
1661 cxt->cflags |= REG_NEWLINE;
1662 if (flags & JSP_REGEX_SLINE)
1663 cxt->cflags &= ~REG_NEWLINE;
1664 if (flags & JSP_REGEX_WSPACE)
1665 cxt->cflags |= REG_EXPANDED;
1668 * 'q' flag can work together only with 'i'. When other is specified,
1669 * then 'q' has no effect.
1671 if ((flags & JSP_REGEX_QUOTE) &&
1672 !(flags & (JSP_REGEX_MLINE | JSP_REGEX_SLINE | JSP_REGEX_WSPACE)))
1674 cxt->cflags &= ~REG_ADVANCED;
1675 cxt->cflags |= REG_QUOTE;
1679 if (RE_compile_and_execute(cxt->regex, str->val.string.val,
1680 str->val.string.len,
1681 cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
1688 * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
1689 * user function 'func'.
1691 static JsonPathExecResult
1692 executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1693 JsonbValue *jb, bool unwrap, PGFunction func,
1694 JsonValueList *found)
1699 if (unwrap && JsonbType(jb) == jbvArray)
1700 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1702 if (!(jb = getScalar(jb, jbvNumeric)))
1703 RETURN_ERROR(ereport(ERROR,
1704 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1705 errmsg("jsonpath item method .%s() can only be applied to a numeric value",
1706 jspOperationName(jsp->type)))));
1708 datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
1710 if (!jspGetNext(jsp, &next) && !found)
1713 jb = palloc(sizeof(*jb));
1714 jb->type = jbvNumeric;
1715 jb->val.numeric = DatumGetNumeric(datum);
1717 return executeNextItem(cxt, jsp, &next, jb, found, false);
1721 * Implementation of .keyvalue() method.
1723 * .keyvalue() method returns a sequence of object's key-value pairs in the
1724 * following format: '{ "key": key, "value": value, "id": id }'.
1726 * "id" field is an object identifier which is constructed from the two parts:
1727 * base object id and its binary offset in base object's jsonb:
1728 * id = 10000000000 * base_object_id + obj_offset_in_base_object
1730 * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1731 * (maximal offset in jsonb). Decimal multiplier is used here to improve the
1732 * readability of identifiers.
1734 * Base object is usually a root object of the path: context item '$' or path
1735 * variable '$var', literals can't produce objects for now. But if the path
1736 * contains generated objects (.keyvalue() itself, for example), then they
1737 * become base object for the subsequent .keyvalue().
1739 * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
1740 * of variables (see getJsonPathVariable()). Ids for generated objects
1741 * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
1743 static JsonPathExecResult
1744 executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1745 JsonbValue *jb, JsonValueList *found)
1747 JsonPathExecResult res = jperNotFound;
1749 JsonbContainer *jbc;
1757 JsonbIteratorToken tok;
1761 if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
1762 RETURN_ERROR(ereport(ERROR,
1763 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
1764 errmsg("jsonpath item method .%s() can only be applied to an object",
1765 jspOperationName(jsp->type)))));
1767 jbc = jb->val.binary.data;
1769 if (!JsonContainerSize(jbc))
1770 return jperNotFound; /* no key-value pairs */
1772 hasNext = jspGetNext(jsp, &next);
1774 keystr.type = jbvString;
1775 keystr.val.string.val = "key";
1776 keystr.val.string.len = 3;
1778 valstr.type = jbvString;
1779 valstr.val.string.val = "value";
1780 valstr.val.string.len = 5;
1782 idstr.type = jbvString;
1783 idstr.val.string.val = "id";
1784 idstr.val.string.len = 2;
1786 /* construct object id from its base object and offset inside that */
1787 id = jb->type != jbvBinary ? 0 :
1788 (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
1789 id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
1791 idval.type = jbvNumeric;
1792 idval.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
1793 Int64GetDatum(id)));
1795 it = JsonbIteratorInit(jbc);
1797 while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
1799 JsonBaseObjectInfo baseObject;
1801 JsonbParseState *ps;
1810 if (!hasNext && !found)
1813 tok = JsonbIteratorNext(&it, &val, true);
1814 Assert(tok == WJB_VALUE);
1817 pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
1819 pushJsonbValue(&ps, WJB_KEY, &keystr);
1820 pushJsonbValue(&ps, WJB_VALUE, &key);
1822 pushJsonbValue(&ps, WJB_KEY, &valstr);
1823 pushJsonbValue(&ps, WJB_VALUE, &val);
1825 pushJsonbValue(&ps, WJB_KEY, &idstr);
1826 pushJsonbValue(&ps, WJB_VALUE, &idval);
1828 keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
1830 jsonb = JsonbValueToJsonb(keyval);
1832 JsonbInitBinary(&obj, jsonb);
1834 baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
1836 res = executeNextItem(cxt, jsp, &next, &obj, found, true);
1838 cxt->baseObject = baseObject;
1840 if (jperIsError(res))
1843 if (res == jperOk && !found)
1851 * Convert boolean execution status 'res' to a boolean JSON item and execute
1854 static JsonPathExecResult
1855 appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1856 JsonValueList *found, JsonPathBool res)
1861 if (!jspGetNext(jsp, &next) && !found)
1862 return jperOk; /* found singleton boolean value */
1864 if (res == jpbUnknown)
1871 jbv.val.boolean = res == jpbTrue;
1874 return executeNextItem(cxt, jsp, &next, &jbv, found, true);
1878 * Convert jsonpath's scalar or variable node to actual jsonb value.
1880 * If node is a variable then its id returned, otherwise 0 returned.
1883 getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
1889 value->type = jbvNull;
1892 value->type = jbvBool;
1893 value->val.boolean = jspGetBool(item);
1896 value->type = jbvNumeric;
1897 value->val.numeric = jspGetNumeric(item);
1900 value->type = jbvString;
1901 value->val.string.val = jspGetString(item,
1902 &value->val.string.len);
1905 getJsonPathVariable(cxt, item, cxt->vars, value);
1908 elog(ERROR, "unexpected jsonpath item type");
1913 * Get the value of variable passed to jsonpath executor
1916 getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
1917 Jsonb *vars, JsonbValue *value)
1926 value->type = jbvNull;
1930 Assert(variable->type == jpiVariable);
1931 varName = jspGetString(variable, &varNameLength);
1932 tmp.type = jbvString;
1933 tmp.val.string.val = varName;
1934 tmp.val.string.len = varNameLength;
1936 v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
1946 (errcode(ERRCODE_UNDEFINED_OBJECT),
1947 errmsg("could not find jsonpath variable \"%s\"",
1948 pnstrdup(varName, varNameLength))));
1951 JsonbInitBinary(&tmp, vars);
1952 setBaseObject(cxt, &tmp, 1);
1955 /**************** Support functions for JsonPath execution *****************/
1958 * Returns the size of an array item, or -1 if item is not an array.
1961 JsonbArraySize(JsonbValue *jb)
1963 Assert(jb->type != jbvArray);
1965 if (jb->type == jbvBinary)
1967 JsonbContainer *jbc = jb->val.binary.data;
1969 if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
1970 return JsonContainerSize(jbc);
1976 /* Comparison predicate callback. */
1978 executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
1980 return compareItems(cmp->type, lv, rv);
1984 * Perform per-byte comparison of two strings.
1987 binaryCompareStrings(const char *s1, int len1,
1988 const char *s2, int len2)
1992 cmp = memcmp(s1, s2, Min(len1, len2));
2000 return len1 < len2 ? -1 : 1;
2004 * Compare two strings in the current server encoding using Unicode codepoint
2008 compareStrings(const char *mbstr1, int mblen1,
2009 const char *mbstr2, int mblen2)
2011 if (GetDatabaseEncoding() == PG_SQL_ASCII ||
2012 GetDatabaseEncoding() == PG_UTF8)
2015 * It's known property of UTF-8 strings that their per-byte comparison
2016 * result matches codepoints comparison result. ASCII can be
2017 * considered as special case of UTF-8.
2019 return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2030 * We have to convert other encodings to UTF-8 first, then compare.
2031 * Input strings may be not null-terminated and pg_server_to_any() may
2032 * return them "as is". So, use strlen() only if there is real
2035 utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
2036 utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
2037 utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
2038 utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
2040 cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
2043 * If pg_server_to_any() did no real conversion, then we actually
2044 * compared original strings. So, we already done.
2046 if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
2049 /* Free memory if needed */
2050 if (mbstr1 != utf8str1)
2052 if (mbstr2 != utf8str2)
2056 * When all Unicode codepoints are equal, return result of binary
2057 * comparison. In some edge cases, same characters may have different
2058 * representations in encoding. Then our behavior could diverge from
2059 * standard. However, that allow us to do simple binary comparison
2060 * for "==" operator, which is performance critical in typical cases.
2061 * In future to implement strict standard conformance, we can do
2062 * normalization of input JSON strings.
2065 return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2072 * Compare two SQL/JSON items using comparison operation 'op'.
2075 compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2)
2080 if (jb1->type != jb2->type)
2082 if (jb1->type == jbvNull || jb2->type == jbvNull)
2085 * Equality and order comparison of nulls to non-nulls returns
2086 * always false, but inequality comparison returns true.
2088 return op == jpiNotEqual ? jpbTrue : jpbFalse;
2090 /* Non-null items of different types are not comparable. */
2100 cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2101 jb1->val.boolean ? 1 : -1;
2104 cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2108 return jb1->val.string.len != jb2->val.string.len ||
2109 memcmp(jb1->val.string.val,
2110 jb2->val.string.val,
2111 jb1->val.string.len) ? jpbFalse : jpbTrue;
2113 cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
2114 jb2->val.string.val, jb2->val.string.len);
2120 return jpbUnknown; /* non-scalars are not comparable */
2123 elog(ERROR, "invalid jsonb value type %d", jb1->type);
2140 case jpiLessOrEqual:
2143 case jpiGreaterOrEqual:
2147 elog(ERROR, "unrecognized jsonpath operation: %d", op);
2151 return res ? jpbTrue : jpbFalse;
2154 /* Compare two numerics */
2156 compareNumeric(Numeric a, Numeric b)
2158 return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2160 NumericGetDatum(b)));
2164 copyJsonbValue(JsonbValue *src)
2166 JsonbValue *dst = palloc(sizeof(*dst));
2174 * Execute array subscript expression and convert resulting numeric item to
2175 * the integer type with truncation.
2177 static JsonPathExecResult
2178 getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2182 JsonValueList found = {0};
2183 JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2184 Datum numeric_index;
2185 bool have_error = false;
2187 if (jperIsError(res))
2190 if (JsonValueListLength(&found) != 1 ||
2191 !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2192 RETURN_ERROR(ereport(ERROR,
2193 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2194 errmsg("jsonpath array subscript is not a single numeric value"))));
2196 numeric_index = DirectFunctionCall2(numeric_trunc,
2197 NumericGetDatum(jbv->val.numeric),
2200 *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
2204 RETURN_ERROR(ereport(ERROR,
2205 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2206 errmsg("jsonpath array subscript is out of integer range"))));
2211 /* Save base object and its id needed for the execution of .keyvalue(). */
2212 static JsonBaseObjectInfo
2213 setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2215 JsonBaseObjectInfo baseObject = cxt->baseObject;
2217 cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2218 (JsonbContainer *) jbv->val.binary.data;
2219 cxt->baseObject.id = id;
2225 JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2229 jvl->list = list_make2(jvl->singleton, jbv);
2230 jvl->singleton = NULL;
2232 else if (!jvl->list)
2233 jvl->singleton = jbv;
2235 jvl->list = lappend(jvl->list, jbv);
2239 JsonValueListLength(const JsonValueList *jvl)
2241 return jvl->singleton ? 1 : list_length(jvl->list);
2245 JsonValueListIsEmpty(JsonValueList *jvl)
2247 return !jvl->singleton && list_length(jvl->list) <= 0;
2251 JsonValueListHead(JsonValueList *jvl)
2253 return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2257 JsonValueListGetList(JsonValueList *jvl)
2260 return list_make1(jvl->singleton);
2266 JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2270 it->value = jvl->singleton;
2274 else if (jvl->list != NIL)
2276 it->value = (JsonbValue *) linitial(jvl->list);
2277 it->list = jvl->list;
2278 it->next = list_second_cell(jvl->list);
2289 * Get the next item from the sequence advancing iterator.
2292 JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2294 JsonbValue *result = it->value;
2298 it->value = lfirst(it->next);
2299 it->next = lnext(it->list, it->next);
2310 * Initialize a binary JsonbValue with the given jsonb container.
2313 JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2315 jbv->type = jbvBinary;
2316 jbv->val.binary.data = &jb->root;
2317 jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2323 * Returns jbv* type of of JsonbValue. Note, it never returns jbvBinary as is.
2326 JsonbType(JsonbValue *jb)
2328 int type = jb->type;
2330 if (jb->type == jbvBinary)
2332 JsonbContainer *jbc = (void *) jb->val.binary.data;
2334 /* Scalars should be always extracted during jsonpath execution. */
2335 Assert(!JsonContainerIsScalar(jbc));
2337 if (JsonContainerIsObject(jbc))
2339 else if (JsonContainerIsArray(jbc))
2342 elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2348 /* Get scalar of given type or NULL on type mismatch */
2350 getScalar(JsonbValue *scalar, enum jbvType type)
2352 /* Scalars should be always extracted during jsonpath execution. */
2353 Assert(scalar->type != jbvBinary ||
2354 !JsonContainerIsScalar(scalar->val.binary.data));
2356 return scalar->type == type ? scalar : NULL;
2359 /* Construct a JSON array from the item list */
2361 wrapItemsInArray(const JsonValueList *items)
2363 JsonbParseState *ps = NULL;
2364 JsonValueListIterator it;
2367 pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2369 JsonValueListInitIterator(items, &it);
2370 while ((jbv = JsonValueListNext(items, &it)))
2371 pushJsonbValue(&ps, WJB_ELEM, jbv);
2373 return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);