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/datetime.h"
70 #include "utils/datum.h"
71 #include "utils/formatting.h"
72 #include "utils/float.h"
73 #include "utils/guc.h"
74 #include "utils/json.h"
75 #include "utils/jsonpath.h"
76 #include "utils/date.h"
77 #include "utils/timestamp.h"
78 #include "utils/varlena.h"
82 * Represents "base object" and it's "id" for .keyvalue() evaluation.
84 typedef struct JsonBaseObjectInfo
91 * Context of jsonpath execution.
93 typedef struct JsonPathExecContext
95 Jsonb *vars; /* variables to substitute into jsonpath */
96 JsonbValue *root; /* for $ evaluation */
97 JsonbValue *current; /* for @ evaluation */
98 JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue()
100 int lastGeneratedObjectId; /* "id" counter for .keyvalue()
102 int innermostArraySize; /* for LAST array index evaluation */
103 bool laxMode; /* true for "lax" mode, false for "strict"
105 bool ignoreStructuralErrors; /* with "true" structural errors such
106 * as absence of required json item or
107 * unexpected json item type are
109 bool throwErrors; /* with "false" all suppressible errors are
112 } JsonPathExecContext;
114 /* Context for LIKE_REGEX execution. */
115 typedef struct JsonLikeRegexContext
119 } JsonLikeRegexContext;
121 /* Result of jsonpath predicate evaluation */
122 typedef enum JsonPathBool
129 /* Result of jsonpath expression evaluation */
130 typedef enum JsonPathExecResult
135 } JsonPathExecResult;
137 #define jperIsError(jper) ((jper) == jperError)
140 * List of jsonb values with shortcut for single-value list.
142 typedef struct JsonValueList
144 JsonbValue *singleton;
148 typedef struct JsonValueListIterator
153 } JsonValueListIterator;
155 /* strict/lax flags is decomposed into four [un]wrap/error flags */
156 #define jspStrictAbsenseOfErrors(cxt) (!(cxt)->laxMode)
157 #define jspAutoUnwrap(cxt) ((cxt)->laxMode)
158 #define jspAutoWrap(cxt) ((cxt)->laxMode)
159 #define jspIgnoreStructuralErrors(cxt) ((cxt)->ignoreStructuralErrors)
160 #define jspThrowErrors(cxt) ((cxt)->throwErrors)
162 /* Convenience macro: return or throw error depending on context */
163 #define RETURN_ERROR(throw_error) \
165 if (jspThrowErrors(cxt)) \
171 typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
175 typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
177 static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
178 Jsonb *json, bool throwErrors,
179 JsonValueList *result, bool useTz);
180 static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
181 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
182 static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
183 JsonPathItem *jsp, JsonbValue *jb,
184 JsonValueList *found, bool unwrap);
185 static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
186 JsonPathItem *jsp, JsonbValue *jb,
187 JsonValueList *found, bool unwrapElements);
188 static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
189 JsonPathItem *cur, JsonPathItem *next,
190 JsonbValue *v, JsonValueList *found, bool copy);
191 static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
192 bool unwrap, JsonValueList *found);
193 static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp,
194 JsonbValue *jb, bool unwrap, JsonValueList *found);
195 static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
196 JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
197 static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
198 JsonPathItem *jsp, JsonbValue *jb);
199 static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
200 JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
201 uint32 level, uint32 first, uint32 last,
202 bool ignoreStructuralErrors, bool unwrapNext);
203 static JsonPathBool executePredicate(JsonPathExecContext *cxt,
204 JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
205 JsonbValue *jb, bool unwrapRightArg,
206 JsonPathPredicateCallback exec, void *param);
207 static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
208 JsonPathItem *jsp, JsonbValue *jb,
209 BinaryArithmFunc func, JsonValueList *found);
210 static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
211 JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
212 JsonValueList *found);
213 static JsonPathBool executeStartsWith(JsonPathItem *jsp,
214 JsonbValue *whole, JsonbValue *initial, void *param);
215 static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
216 JsonbValue *rarg, void *param);
217 static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
218 JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
219 JsonValueList *found);
220 static JsonPathExecResult executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
221 JsonbValue *jb, JsonValueList *found);
222 static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
223 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
224 static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
225 JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
226 static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
228 static void getJsonPathVariable(JsonPathExecContext *cxt,
229 JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
230 static int JsonbArraySize(JsonbValue *jb);
231 static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
232 JsonbValue *rv, void *p);
233 static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2,
235 static int compareNumeric(Numeric a, Numeric b);
236 static JsonbValue *copyJsonbValue(JsonbValue *src);
237 static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
238 JsonPathItem *jsp, JsonbValue *jb, int32 *index);
239 static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
240 JsonbValue *jbv, int32 id);
241 static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
242 static int JsonValueListLength(const JsonValueList *jvl);
243 static bool JsonValueListIsEmpty(JsonValueList *jvl);
244 static JsonbValue *JsonValueListHead(JsonValueList *jvl);
245 static List *JsonValueListGetList(JsonValueList *jvl);
246 static void JsonValueListInitIterator(const JsonValueList *jvl,
247 JsonValueListIterator *it);
248 static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
249 JsonValueListIterator *it);
250 static int JsonbType(JsonbValue *jb);
251 static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
252 static int JsonbType(JsonbValue *jb);
253 static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
254 static JsonbValue *wrapItemsInArray(const JsonValueList *items);
255 static int compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
256 bool useTz, bool *have_error);
258 /****************** User interface to JsonPath executor ********************/
262 * Returns true if jsonpath returns at least one item for the specified
263 * jsonb value. This function and jsonb_path_match() are used to
264 * implement @? and @@ operators, which in turn are intended to have an
265 * index support. Thus, it's desirable to make it easier to achieve
266 * consistency between index scan results and sequential scan results.
267 * So, we throw as less errors as possible. Regarding this function,
268 * such behavior also matches behavior of JSON_EXISTS() clause of
269 * SQL/JSON. Regarding jsonb_path_match(), this function doesn't have
270 * an analogy in SQL/JSON, so we define its behavior on our own.
273 jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
275 Jsonb *jb = PG_GETARG_JSONB_P(0);
276 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
277 JsonPathExecResult res;
283 vars = PG_GETARG_JSONB_P(2);
284 silent = PG_GETARG_BOOL(3);
287 res = executeJsonPath(jp, vars, jb, !silent, NULL, tz);
289 PG_FREE_IF_COPY(jb, 0);
290 PG_FREE_IF_COPY(jp, 1);
292 if (jperIsError(res))
295 PG_RETURN_BOOL(res == jperOk);
299 jsonb_path_exists(PG_FUNCTION_ARGS)
301 return jsonb_path_exists_internal(fcinfo, false);
305 jsonb_path_exists_tz(PG_FUNCTION_ARGS)
307 return jsonb_path_exists_internal(fcinfo, true);
311 * jsonb_path_exists_opr
312 * Implementation of operator "jsonb @? jsonpath" (2-argument version of
313 * jsonb_path_exists()).
316 jsonb_path_exists_opr(PG_FUNCTION_ARGS)
318 /* just call the other one -- it can handle both cases */
319 return jsonb_path_exists_internal(fcinfo, false);
324 * Returns jsonpath predicate result item for the specified jsonb value.
325 * See jsonb_path_exists() comment for details regarding error handling.
328 jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
330 Jsonb *jb = PG_GETARG_JSONB_P(0);
331 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
332 JsonValueList found = {0};
338 vars = PG_GETARG_JSONB_P(2);
339 silent = PG_GETARG_BOOL(3);
342 (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
344 PG_FREE_IF_COPY(jb, 0);
345 PG_FREE_IF_COPY(jp, 1);
347 if (JsonValueListLength(&found) == 1)
349 JsonbValue *jbv = JsonValueListHead(&found);
351 if (jbv->type == jbvBool)
352 PG_RETURN_BOOL(jbv->val.boolean);
354 if (jbv->type == jbvNull)
360 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
361 errmsg("single boolean result is expected")));
367 jsonb_path_match(PG_FUNCTION_ARGS)
369 return jsonb_path_match_internal(fcinfo, false);
373 jsonb_path_match_tz(PG_FUNCTION_ARGS)
375 return jsonb_path_match_internal(fcinfo, true);
379 * jsonb_path_match_opr
380 * Implementation of operator "jsonb @@ jsonpath" (2-argument version of
381 * jsonb_path_match()).
384 jsonb_path_match_opr(PG_FUNCTION_ARGS)
386 /* just call the other one -- it can handle both cases */
387 return jsonb_path_match_internal(fcinfo, false);
392 * Executes jsonpath for given jsonb document and returns result as
396 jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
398 FuncCallContext *funcctx;
403 if (SRF_IS_FIRSTCALL())
407 MemoryContext oldcontext;
410 JsonValueList found = {0};
412 funcctx = SRF_FIRSTCALL_INIT();
413 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
415 jb = PG_GETARG_JSONB_P_COPY(0);
416 jp = PG_GETARG_JSONPATH_P_COPY(1);
417 vars = PG_GETARG_JSONB_P_COPY(2);
418 silent = PG_GETARG_BOOL(3);
420 (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
422 funcctx->user_fctx = JsonValueListGetList(&found);
424 MemoryContextSwitchTo(oldcontext);
427 funcctx = SRF_PERCALL_SETUP();
428 found = funcctx->user_fctx;
430 c = list_head(found);
433 SRF_RETURN_DONE(funcctx);
436 funcctx->user_fctx = list_delete_first(found);
438 SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
442 jsonb_path_query(PG_FUNCTION_ARGS)
444 return jsonb_path_query_internal(fcinfo, false);
448 jsonb_path_query_tz(PG_FUNCTION_ARGS)
450 return jsonb_path_query_internal(fcinfo, true);
454 * jsonb_path_query_array
455 * Executes jsonpath for given jsonb document and returns result as
459 jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
461 Jsonb *jb = PG_GETARG_JSONB_P(0);
462 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
463 JsonValueList found = {0};
464 Jsonb *vars = PG_GETARG_JSONB_P(2);
465 bool silent = PG_GETARG_BOOL(3);
467 (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
469 PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
473 jsonb_path_query_array(PG_FUNCTION_ARGS)
475 return jsonb_path_query_array_internal(fcinfo, false);
479 jsonb_path_query_array_tz(PG_FUNCTION_ARGS)
481 return jsonb_path_query_array_internal(fcinfo, true);
485 * jsonb_path_query_first
486 * Executes jsonpath for given jsonb document and returns first result
487 * item. If there are no items, NULL returned.
490 jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
492 Jsonb *jb = PG_GETARG_JSONB_P(0);
493 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
494 JsonValueList found = {0};
495 Jsonb *vars = PG_GETARG_JSONB_P(2);
496 bool silent = PG_GETARG_BOOL(3);
498 (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
500 if (JsonValueListLength(&found) >= 1)
501 PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
507 jsonb_path_query_first(PG_FUNCTION_ARGS)
509 return jsonb_path_query_first_internal(fcinfo, false);
513 jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
515 return jsonb_path_query_first_internal(fcinfo, true);
518 /********************Execute functions for JsonPath**************************/
521 * Interface to jsonpath executor
523 * 'path' - jsonpath to be executed
524 * 'vars' - variables to be substituted to jsonpath
525 * 'json' - target document for jsonpath evaluation
526 * 'throwErrors' - whether we should throw suppressible errors
527 * 'result' - list to store result items into
529 * Returns an error if a recoverable error happens during processing, or NULL
532 * Note, jsonb and jsonpath values should be available and untoasted during
533 * work because JsonPathItem, JsonbValue and result item could have pointers
534 * into input values. If caller needs to just check if document matches
535 * jsonpath, then it doesn't provide a result arg. In this case executor
536 * works till first positive result and does not check the rest if possible.
537 * In other case it tries to find all the satisfied result items.
539 static JsonPathExecResult
540 executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
541 JsonValueList *result, bool useTz)
543 JsonPathExecContext cxt;
544 JsonPathExecResult res;
550 if (!JsonbExtractScalar(&json->root, &jbv))
551 JsonbInitBinary(&jbv, json);
553 if (vars && !JsonContainerIsObject(&vars->root))
556 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
557 errmsg("\"vars\" argument is not an object"),
558 errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
562 cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
563 cxt.ignoreStructuralErrors = cxt.laxMode;
566 cxt.baseObject.jbc = NULL;
567 cxt.baseObject.id = 0;
568 cxt.lastGeneratedObjectId = vars ? 2 : 1;
569 cxt.innermostArraySize = -1;
570 cxt.throwErrors = throwErrors;
573 if (jspStrictAbsenseOfErrors(&cxt) && !result)
576 * In strict mode we must get a complete list of values to check that
577 * there are no errors at all.
579 JsonValueList vals = {0};
581 res = executeItem(&cxt, &jsp, &jbv, &vals);
583 if (jperIsError(res))
586 return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
589 res = executeItem(&cxt, &jsp, &jbv, result);
591 Assert(!throwErrors || !jperIsError(res));
597 * Execute jsonpath with automatic unwrapping of current item in lax mode.
599 static JsonPathExecResult
600 executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
601 JsonbValue *jb, JsonValueList *found)
603 return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
607 * Main jsonpath executor function: walks on jsonpath structure, finds
608 * relevant parts of jsonb and evaluates expressions over them.
609 * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
611 static JsonPathExecResult
612 executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
613 JsonbValue *jb, JsonValueList *found, bool unwrap)
616 JsonPathExecResult res = jperNotFound;
617 JsonBaseObjectInfo baseObject;
620 CHECK_FOR_INTERRUPTS();
624 /* all boolean item types: */
634 case jpiGreaterOrEqual:
639 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
641 res = appendBoolResult(cxt, jsp, found, st);
646 if (JsonbType(jb) == jbvObject)
651 key.type = jbvString;
652 key.val.string.val = jspGetString(jsp, &key.val.string.len);
654 v = findJsonbValueFromContainer(jb->val.binary.data,
659 res = executeNextItem(cxt, jsp, NULL,
662 /* free value if it was not added to found list */
663 if (jspHasNext(jsp) || !found)
666 else if (!jspIgnoreStructuralErrors(cxt))
670 if (!jspThrowErrors(cxt))
674 (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
675 errmsg("JSON object does not contain key \"%s\"",
676 pnstrdup(key.val.string.val,
677 key.val.string.len))));
680 else if (unwrap && JsonbType(jb) == jbvArray)
681 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
682 else if (!jspIgnoreStructuralErrors(cxt))
685 RETURN_ERROR(ereport(ERROR,
686 (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
687 errmsg("jsonpath member accessor can only be applied to an object"))));
693 baseObject = setBaseObject(cxt, jb, 0);
694 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
695 cxt->baseObject = baseObject;
699 res = executeNextItem(cxt, jsp, NULL, cxt->current,
704 if (JsonbType(jb) == jbvArray)
706 bool hasNext = jspGetNext(jsp, &elem);
708 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
709 jb, found, jspAutoUnwrap(cxt));
711 else if (jspAutoWrap(cxt))
712 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
713 else if (!jspIgnoreStructuralErrors(cxt))
714 RETURN_ERROR(ereport(ERROR,
715 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
716 errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
720 if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
722 int innermostArraySize = cxt->innermostArraySize;
724 int size = JsonbArraySize(jb);
725 bool singleton = size < 0;
726 bool hasNext = jspGetNext(jsp, &elem);
731 cxt->innermostArraySize = size; /* for LAST evaluation */
733 for (i = 0; i < jsp->content.array.nelems; i++)
740 bool range = jspGetArraySubscript(jsp, &from,
743 res = getArrayIndex(cxt, &from, jb, &index_from);
745 if (jperIsError(res))
750 res = getArrayIndex(cxt, &to, jb, &index_to);
752 if (jperIsError(res))
756 index_to = index_from;
758 if (!jspIgnoreStructuralErrors(cxt) &&
760 index_from > index_to ||
762 RETURN_ERROR(ereport(ERROR,
763 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
764 errmsg("jsonpath array subscript is out of bounds"))));
769 if (index_to >= size)
774 for (index = index_from; index <= index_to; index++)
786 v = getIthJsonbValueFromContainer(jb->val.binary.data,
795 if (!hasNext && !found)
798 res = executeNextItem(cxt, jsp, &elem, v, found,
801 if (jperIsError(res))
804 if (res == jperOk && !found)
808 if (jperIsError(res))
811 if (res == jperOk && !found)
815 cxt->innermostArraySize = innermostArraySize;
817 else if (!jspIgnoreStructuralErrors(cxt))
819 RETURN_ERROR(ereport(ERROR,
820 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
821 errmsg("jsonpath array accessor can only be applied to an array"))));
830 bool hasNext = jspGetNext(jsp, &elem);
832 if (cxt->innermostArraySize < 0)
833 elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
835 if (!hasNext && !found)
841 last = cxt->innermostArraySize - 1;
843 lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
845 lastjbv->type = jbvNumeric;
846 lastjbv->val.numeric =
847 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
848 Int32GetDatum(last)));
850 res = executeNextItem(cxt, jsp, &elem,
851 lastjbv, found, hasNext);
856 if (JsonbType(jb) == jbvObject)
858 bool hasNext = jspGetNext(jsp, &elem);
860 if (jb->type != jbvBinary)
861 elog(ERROR, "invalid jsonb object type: %d", jb->type);
863 return executeAnyItem
864 (cxt, hasNext ? &elem : NULL,
865 jb->val.binary.data, found, 1, 1, 1,
866 false, jspAutoUnwrap(cxt));
868 else if (unwrap && JsonbType(jb) == jbvArray)
869 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
870 else if (!jspIgnoreStructuralErrors(cxt))
873 RETURN_ERROR(ereport(ERROR,
874 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
875 errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
880 return executeBinaryArithmExpr(cxt, jsp, jb,
881 numeric_add_opt_error, found);
884 return executeBinaryArithmExpr(cxt, jsp, jb,
885 numeric_sub_opt_error, found);
888 return executeBinaryArithmExpr(cxt, jsp, jb,
889 numeric_mul_opt_error, found);
892 return executeBinaryArithmExpr(cxt, jsp, jb,
893 numeric_div_opt_error, found);
896 return executeBinaryArithmExpr(cxt, jsp, jb,
897 numeric_mod_opt_error, found);
900 return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
903 return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
910 if (unwrap && JsonbType(jb) == jbvArray)
911 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
914 jspGetArg(jsp, &elem);
915 st = executeNestedBoolItem(cxt, &elem, jb);
919 res = executeNextItem(cxt, jsp, NULL,
926 bool hasNext = jspGetNext(jsp, &elem);
928 /* first try without any intermediate steps */
929 if (jsp->content.anybounds.first == 0)
931 bool savedIgnoreStructuralErrors;
933 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
934 cxt->ignoreStructuralErrors = true;
935 res = executeNextItem(cxt, jsp, &elem,
937 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
939 if (res == jperOk && !found)
943 if (jb->type == jbvBinary)
945 (cxt, hasNext ? &elem : NULL,
946 jb->val.binary.data, found,
948 jsp->content.anybounds.first,
949 jsp->content.anybounds.last,
950 true, jspAutoUnwrap(cxt));
962 bool hasNext = jspGetNext(jsp, &elem);
964 if (!hasNext && !found)
966 res = jperOk; /* skip evaluation */
970 v = hasNext ? &vbuf : palloc(sizeof(*v));
972 baseObject = cxt->baseObject;
973 getJsonPathItem(cxt, jsp, v);
975 res = executeNextItem(cxt, jsp, &elem,
977 cxt->baseObject = baseObject;
983 JsonbValue *jbv = palloc(sizeof(*jbv));
985 jbv->type = jbvString;
986 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
987 jbv->val.string.len = strlen(jbv->val.string.val);
989 res = executeNextItem(cxt, jsp, NULL, jbv,
996 int size = JsonbArraySize(jb);
1000 if (!jspAutoWrap(cxt))
1002 if (!jspIgnoreStructuralErrors(cxt))
1003 RETURN_ERROR(ereport(ERROR,
1004 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
1005 errmsg("jsonpath item method .%s() can only be applied to an array",
1006 jspOperationName(jsp->type)))));
1013 jb = palloc(sizeof(*jb));
1015 jb->type = jbvNumeric;
1017 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
1018 Int32GetDatum(size)));
1020 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
1025 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
1029 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
1033 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
1040 if (unwrap && JsonbType(jb) == jbvArray)
1041 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1044 if (jb->type == jbvNumeric)
1046 char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1047 NumericGetDatum(jb->val.numeric)));
1048 bool have_error = false;
1050 (void) float8in_internal_opt_error(tmp,
1057 RETURN_ERROR(ereport(ERROR,
1058 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1059 errmsg("jsonpath item method .%s() can only be applied to a numeric value",
1060 jspOperationName(jsp->type)))));
1063 else if (jb->type == jbvString)
1065 /* cast string as double */
1067 char *tmp = pnstrdup(jb->val.string.val,
1068 jb->val.string.len);
1069 bool have_error = false;
1071 val = float8in_internal_opt_error(tmp,
1077 if (have_error || isinf(val))
1078 RETURN_ERROR(ereport(ERROR,
1079 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1080 errmsg("jsonpath item method .%s() can only be applied to a numeric value",
1081 jspOperationName(jsp->type)))));
1084 jb->type = jbvNumeric;
1085 jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1086 Float8GetDatum(val)));
1090 if (res == jperNotFound)
1091 RETURN_ERROR(ereport(ERROR,
1092 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1093 errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1094 jspOperationName(jsp->type)))));
1096 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1101 if (unwrap && JsonbType(jb) == jbvArray)
1102 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1104 return executeDateTimeMethod(cxt, jsp, jb, found);
1107 if (unwrap && JsonbType(jb) == jbvArray)
1108 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1110 return executeKeyValueMethod(cxt, jsp, jb, found);
1113 elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1120 * Unwrap current array item and execute jsonpath for each of its elements.
1122 static JsonPathExecResult
1123 executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1124 JsonbValue *jb, JsonValueList *found,
1125 bool unwrapElements)
1127 if (jb->type != jbvBinary)
1129 Assert(jb->type != jbvArray);
1130 elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1133 return executeAnyItem
1134 (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1135 false, unwrapElements);
1139 * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1142 static JsonPathExecResult
1143 executeNextItem(JsonPathExecContext *cxt,
1144 JsonPathItem *cur, JsonPathItem *next,
1145 JsonbValue *v, JsonValueList *found, bool copy)
1151 hasNext = next != NULL;
1153 hasNext = jspHasNext(cur);
1157 hasNext = jspGetNext(cur, next);
1161 return executeItem(cxt, next, v, found);
1164 JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1170 * Same as executeItem(), but when "unwrap == true" automatically unwraps
1171 * each array item from the resulting sequence in lax mode.
1173 static JsonPathExecResult
1174 executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1175 JsonbValue *jb, bool unwrap,
1176 JsonValueList *found)
1178 if (unwrap && jspAutoUnwrap(cxt))
1180 JsonValueList seq = {0};
1181 JsonValueListIterator it;
1182 JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1185 if (jperIsError(res))
1188 JsonValueListInitIterator(&seq, &it);
1189 while ((item = JsonValueListNext(&seq, &it)))
1191 Assert(item->type != jbvArray);
1193 if (JsonbType(item) == jbvArray)
1194 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1196 JsonValueListAppend(found, item);
1202 return executeItem(cxt, jsp, jb, found);
1206 * Same as executeItemOptUnwrapResult(), but with error suppression.
1208 static JsonPathExecResult
1209 executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1211 JsonbValue *jb, bool unwrap,
1212 JsonValueList *found)
1214 JsonPathExecResult res;
1215 bool throwErrors = cxt->throwErrors;
1217 cxt->throwErrors = false;
1218 res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1219 cxt->throwErrors = throwErrors;
1224 /* Execute boolean-valued jsonpath expression. */
1226 executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1227 JsonbValue *jb, bool canHaveNext)
1234 if (!canHaveNext && jspHasNext(jsp))
1235 elog(ERROR, "boolean jsonpath item cannot have next item");
1240 jspGetLeftArg(jsp, &larg);
1241 res = executeBoolItem(cxt, &larg, jb, false);
1243 if (res == jpbFalse)
1247 * SQL/JSON says that we should check second arg in case of
1251 jspGetRightArg(jsp, &rarg);
1252 res2 = executeBoolItem(cxt, &rarg, jb, false);
1254 return res2 == jpbTrue ? res : res2;
1257 jspGetLeftArg(jsp, &larg);
1258 res = executeBoolItem(cxt, &larg, jb, false);
1263 jspGetRightArg(jsp, &rarg);
1264 res2 = executeBoolItem(cxt, &rarg, jb, false);
1266 return res2 == jpbFalse ? res : res2;
1269 jspGetArg(jsp, &larg);
1271 res = executeBoolItem(cxt, &larg, jb, false);
1273 if (res == jpbUnknown)
1276 return res == jpbTrue ? jpbFalse : jpbTrue;
1279 jspGetArg(jsp, &larg);
1280 res = executeBoolItem(cxt, &larg, jb, false);
1281 return res == jpbUnknown ? jpbTrue : jpbFalse;
1287 case jpiLessOrEqual:
1288 case jpiGreaterOrEqual:
1289 jspGetLeftArg(jsp, &larg);
1290 jspGetRightArg(jsp, &rarg);
1291 return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1292 executeComparison, cxt);
1294 case jpiStartsWith: /* 'whole STARTS WITH initial' */
1295 jspGetLeftArg(jsp, &larg); /* 'whole' */
1296 jspGetRightArg(jsp, &rarg); /* 'initial' */
1297 return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1298 executeStartsWith, NULL);
1300 case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1303 * 'expr' is a sequence-returning expression. 'pattern' is a
1304 * regex string literal. SQL/JSON standard requires XQuery
1305 * regexes, but we use Postgres regexes here. 'flags' is a
1306 * string literal converted to integer flags at compile-time.
1308 JsonLikeRegexContext lrcxt = {0};
1310 jspInitByBuffer(&larg, jsp->base,
1311 jsp->content.like_regex.expr);
1313 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1314 executeLikeRegex, &lrcxt);
1318 jspGetArg(jsp, &larg);
1320 if (jspStrictAbsenseOfErrors(cxt))
1323 * In strict mode we must get a complete list of values to
1324 * check that there are no errors at all.
1326 JsonValueList vals = {0};
1327 JsonPathExecResult res =
1328 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1331 if (jperIsError(res))
1334 return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1338 JsonPathExecResult res =
1339 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1342 if (jperIsError(res))
1345 return res == jperOk ? jpbTrue : jpbFalse;
1349 elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1355 * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1356 * item onto the stack.
1359 executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1365 prev = cxt->current;
1367 res = executeBoolItem(cxt, jsp, jb, false);
1368 cxt->current = prev;
1374 * Implementation of several jsonpath nodes:
1375 * - jpiAny (.** accessor),
1376 * - jpiAnyKey (.* accessor),
1377 * - jpiAnyArray ([*] accessor)
1379 static JsonPathExecResult
1380 executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1381 JsonValueList *found, uint32 level, uint32 first, uint32 last,
1382 bool ignoreStructuralErrors, bool unwrapNext)
1384 JsonPathExecResult res = jperNotFound;
1389 check_stack_depth();
1394 it = JsonbIteratorInit(jbc);
1397 * Recursively iterate over jsonb objects/arrays
1399 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1403 r = JsonbIteratorNext(&it, &v, true);
1404 Assert(r == WJB_VALUE);
1407 if (r == WJB_VALUE || r == WJB_ELEM)
1410 if (level >= first ||
1411 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1412 v.type != jbvBinary)) /* leaves only requested */
1414 /* check expression */
1417 if (ignoreStructuralErrors)
1419 bool savedIgnoreStructuralErrors;
1421 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1422 cxt->ignoreStructuralErrors = true;
1423 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1424 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1427 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1429 if (jperIsError(res))
1432 if (res == jperOk && !found)
1436 JsonValueListAppend(found, copyJsonbValue(&v));
1441 if (level < last && v.type == jbvBinary)
1443 res = executeAnyItem
1444 (cxt, jsp, v.val.binary.data, found,
1445 level + 1, first, last,
1446 ignoreStructuralErrors, unwrapNext);
1448 if (jperIsError(res))
1451 if (res == jperOk && found == NULL)
1461 * Execute unary or binary predicate.
1463 * Predicates have existence semantics, because their operands are item
1464 * sequences. Pairs of items from the left and right operand's sequences are
1465 * checked. TRUE returned only if any pair satisfying the condition is found.
1466 * In strict mode, even if the desired pair has already been found, all pairs
1467 * still need to be examined to check the absence of errors. If any error
1468 * occurs, UNKNOWN (analogous to SQL NULL) is returned.
1471 executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
1472 JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
1473 bool unwrapRightArg, JsonPathPredicateCallback exec,
1476 JsonPathExecResult res;
1477 JsonValueListIterator lseqit;
1478 JsonValueList lseq = {0};
1479 JsonValueList rseq = {0};
1484 /* Left argument is always auto-unwrapped. */
1485 res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
1486 if (jperIsError(res))
1491 /* Right argument is conditionally auto-unwrapped. */
1492 res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
1493 unwrapRightArg, &rseq);
1494 if (jperIsError(res))
1498 JsonValueListInitIterator(&lseq, &lseqit);
1499 while ((lval = JsonValueListNext(&lseq, &lseqit)))
1501 JsonValueListIterator rseqit;
1505 JsonValueListInitIterator(&rseq, &rseqit);
1507 rval = JsonValueListNext(&rseq, &rseqit);
1511 /* Loop over right arg sequence or do single pass otherwise */
1512 while (rarg ? (rval != NULL) : first)
1514 JsonPathBool res = exec(pred, lval, rval, param);
1516 if (res == jpbUnknown)
1518 if (jspStrictAbsenseOfErrors(cxt))
1523 else if (res == jpbTrue)
1525 if (!jspStrictAbsenseOfErrors(cxt))
1533 rval = JsonValueListNext(&rseq, &rseqit);
1537 if (found) /* possible only in strict mode */
1540 if (error) /* possible only in lax mode */
1547 * Execute binary arithmetic expression on singleton numeric operands.
1548 * Array operands are automatically unwrapped in lax mode.
1550 static JsonPathExecResult
1551 executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1552 JsonbValue *jb, BinaryArithmFunc func,
1553 JsonValueList *found)
1555 JsonPathExecResult jper;
1557 JsonValueList lseq = {0};
1558 JsonValueList rseq = {0};
1563 jspGetLeftArg(jsp, &elem);
1566 * XXX: By standard only operands of multiplicative expressions are
1567 * unwrapped. We extend it to other binary arithmetic expressions too.
1569 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
1570 if (jperIsError(jper))
1573 jspGetRightArg(jsp, &elem);
1575 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
1576 if (jperIsError(jper))
1579 if (JsonValueListLength(&lseq) != 1 ||
1580 !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
1581 RETURN_ERROR(ereport(ERROR,
1582 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1583 errmsg("left operand of jsonpath operator %s is not a single numeric value",
1584 jspOperationName(jsp->type)))));
1586 if (JsonValueListLength(&rseq) != 1 ||
1587 !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
1588 RETURN_ERROR(ereport(ERROR,
1589 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1590 errmsg("right operand of jsonpath operator %s is not a single numeric value",
1591 jspOperationName(jsp->type)))));
1593 if (jspThrowErrors(cxt))
1595 res = func(lval->val.numeric, rval->val.numeric, NULL);
1601 res = func(lval->val.numeric, rval->val.numeric, &error);
1607 if (!jspGetNext(jsp, &elem) && !found)
1610 lval = palloc(sizeof(*lval));
1611 lval->type = jbvNumeric;
1612 lval->val.numeric = res;
1614 return executeNextItem(cxt, jsp, &elem, lval, found, false);
1618 * Execute unary arithmetic expression for each numeric item in its operand's
1619 * sequence. Array operand is automatically unwrapped in lax mode.
1621 static JsonPathExecResult
1622 executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1623 JsonbValue *jb, PGFunction func, JsonValueList *found)
1625 JsonPathExecResult jper;
1626 JsonPathExecResult jper2;
1628 JsonValueList seq = {0};
1629 JsonValueListIterator it;
1633 jspGetArg(jsp, &elem);
1634 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
1636 if (jperIsError(jper))
1639 jper = jperNotFound;
1641 hasNext = jspGetNext(jsp, &elem);
1643 JsonValueListInitIterator(&seq, &it);
1644 while ((val = JsonValueListNext(&seq, &it)))
1646 if ((val = getScalar(val, jbvNumeric)))
1648 if (!found && !hasNext)
1653 if (!found && !hasNext)
1654 continue; /* skip non-numerics processing */
1656 RETURN_ERROR(ereport(ERROR,
1657 (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
1658 errmsg("operand of unary jsonpath operator %s is not a numeric value",
1659 jspOperationName(jsp->type)))));
1664 DatumGetNumeric(DirectFunctionCall1(func,
1665 NumericGetDatum(val->val.numeric)));
1667 jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
1669 if (jperIsError(jper2))
1672 if (jper2 == jperOk)
1684 * STARTS_WITH predicate callback.
1686 * Check if the 'whole' string starts from 'initial' string.
1689 executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
1692 if (!(whole = getScalar(whole, jbvString)))
1693 return jpbUnknown; /* error */
1695 if (!(initial = getScalar(initial, jbvString)))
1696 return jpbUnknown; /* error */
1698 if (whole->val.string.len >= initial->val.string.len &&
1699 !memcmp(whole->val.string.val,
1700 initial->val.string.val,
1701 initial->val.string.len))
1708 * LIKE_REGEX predicate callback.
1710 * Check if the string matches regex pattern.
1713 executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
1716 JsonLikeRegexContext *cxt = param;
1718 if (!(str = getScalar(str, jbvString)))
1721 /* Cache regex text and converted flags. */
1725 cstring_to_text_with_len(jsp->content.like_regex.pattern,
1726 jsp->content.like_regex.patternlen);
1727 cxt->cflags = jspConvertRegexFlags(jsp->content.like_regex.flags);
1730 if (RE_compile_and_execute(cxt->regex, str->val.string.val,
1731 str->val.string.len,
1732 cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
1739 * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
1740 * user function 'func'.
1742 static JsonPathExecResult
1743 executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1744 JsonbValue *jb, bool unwrap, PGFunction func,
1745 JsonValueList *found)
1750 if (unwrap && JsonbType(jb) == jbvArray)
1751 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1753 if (!(jb = getScalar(jb, jbvNumeric)))
1754 RETURN_ERROR(ereport(ERROR,
1755 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1756 errmsg("jsonpath item method .%s() can only be applied to a numeric value",
1757 jspOperationName(jsp->type)))));
1759 datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
1761 if (!jspGetNext(jsp, &next) && !found)
1764 jb = palloc(sizeof(*jb));
1765 jb->type = jbvNumeric;
1766 jb->val.numeric = DatumGetNumeric(datum);
1768 return executeNextItem(cxt, jsp, &next, jb, found, false);
1772 * Implementation of the .datetime() method.
1774 * Converts a string into a date/time value. The actual type is determined at run time.
1775 * If an argument is provided, this argument is used as a template string.
1776 * Otherwise, the first fitting ISO format is selected.
1778 static JsonPathExecResult
1779 executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1780 JsonbValue *jb, JsonValueList *found)
1789 JsonPathExecResult res = jperNotFound;
1792 if (!(jb = getScalar(jb, jbvString)))
1793 RETURN_ERROR(ereport(ERROR,
1794 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_JSON_DATETIME_FUNCTION),
1795 errmsg("jsonpath item method .%s() can only be applied to a string",
1796 jspOperationName(jsp->type)))));
1798 datetime = cstring_to_text_with_len(jb->val.string.val,
1799 jb->val.string.len);
1801 if (jsp->content.arg)
1806 bool have_error = false;
1808 jspGetArg(jsp, &elem);
1810 if (elem.type != jpiString)
1811 elog(ERROR, "invalid jsonpath item type for .datetime() argument");
1813 template_str = jspGetString(&elem, &template_len);
1815 template = cstring_to_text_with_len(template_str,
1818 value = parse_datetime(datetime, template, true,
1819 &typid, &typmod, &tz,
1820 jspThrowErrors(cxt) ? NULL : &have_error);
1830 * According to SQL/JSON standard enumerate ISO formats for: date,
1831 * timetz, time, timestamptz, timestamp.
1833 static const char *fmt_str[] =
1836 "HH24:MI:SS TZH:TZM",
1839 "yyyy-mm-dd HH24:MI:SS TZH:TZM",
1840 "yyyy-mm-dd HH24:MI:SS TZH",
1841 "yyyy-mm-dd HH24:MI:SS"
1844 /* cache for format texts */
1845 static text *fmt_txt[lengthof(fmt_str)] = {0};
1848 /* loop until datetime format fits */
1849 for (i = 0; i < lengthof(fmt_str); i++)
1851 bool have_error = false;
1855 MemoryContext oldcxt =
1856 MemoryContextSwitchTo(TopMemoryContext);
1858 fmt_txt[i] = cstring_to_text(fmt_str[i]);
1859 MemoryContextSwitchTo(oldcxt);
1862 value = parse_datetime(datetime, fmt_txt[i], true,
1863 &typid, &typmod, &tz,
1873 if (res == jperNotFound)
1874 RETURN_ERROR(ereport(ERROR,
1875 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_JSON_DATETIME_FUNCTION),
1876 errmsg("datetime format is not unrecognized"),
1877 errhint("use datetime template argument for explicit format specification"))));
1882 if (jperIsError(res))
1885 hasNext = jspGetNext(jsp, &elem);
1887 if (!hasNext && !found)
1890 jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
1892 jb->type = jbvDatetime;
1893 jb->val.datetime.value = value;
1894 jb->val.datetime.typid = typid;
1895 jb->val.datetime.typmod = typmod;
1896 jb->val.datetime.tz = tz;
1898 return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
1902 * Implementation of .keyvalue() method.
1904 * .keyvalue() method returns a sequence of object's key-value pairs in the
1905 * following format: '{ "key": key, "value": value, "id": id }'.
1907 * "id" field is an object identifier which is constructed from the two parts:
1908 * base object id and its binary offset in base object's jsonb:
1909 * id = 10000000000 * base_object_id + obj_offset_in_base_object
1911 * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1912 * (maximal offset in jsonb). Decimal multiplier is used here to improve the
1913 * readability of identifiers.
1915 * Base object is usually a root object of the path: context item '$' or path
1916 * variable '$var', literals can't produce objects for now. But if the path
1917 * contains generated objects (.keyvalue() itself, for example), then they
1918 * become base object for the subsequent .keyvalue().
1920 * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
1921 * of variables (see getJsonPathVariable()). Ids for generated objects
1922 * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
1924 static JsonPathExecResult
1925 executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1926 JsonbValue *jb, JsonValueList *found)
1928 JsonPathExecResult res = jperNotFound;
1930 JsonbContainer *jbc;
1938 JsonbIteratorToken tok;
1942 if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
1943 RETURN_ERROR(ereport(ERROR,
1944 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
1945 errmsg("jsonpath item method .%s() can only be applied to an object",
1946 jspOperationName(jsp->type)))));
1948 jbc = jb->val.binary.data;
1950 if (!JsonContainerSize(jbc))
1951 return jperNotFound; /* no key-value pairs */
1953 hasNext = jspGetNext(jsp, &next);
1955 keystr.type = jbvString;
1956 keystr.val.string.val = "key";
1957 keystr.val.string.len = 3;
1959 valstr.type = jbvString;
1960 valstr.val.string.val = "value";
1961 valstr.val.string.len = 5;
1963 idstr.type = jbvString;
1964 idstr.val.string.val = "id";
1965 idstr.val.string.len = 2;
1967 /* construct object id from its base object and offset inside that */
1968 id = jb->type != jbvBinary ? 0 :
1969 (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
1970 id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
1972 idval.type = jbvNumeric;
1973 idval.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
1974 Int64GetDatum(id)));
1976 it = JsonbIteratorInit(jbc);
1978 while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
1980 JsonBaseObjectInfo baseObject;
1982 JsonbParseState *ps;
1991 if (!hasNext && !found)
1994 tok = JsonbIteratorNext(&it, &val, true);
1995 Assert(tok == WJB_VALUE);
1998 pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
2000 pushJsonbValue(&ps, WJB_KEY, &keystr);
2001 pushJsonbValue(&ps, WJB_VALUE, &key);
2003 pushJsonbValue(&ps, WJB_KEY, &valstr);
2004 pushJsonbValue(&ps, WJB_VALUE, &val);
2006 pushJsonbValue(&ps, WJB_KEY, &idstr);
2007 pushJsonbValue(&ps, WJB_VALUE, &idval);
2009 keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2011 jsonb = JsonbValueToJsonb(keyval);
2013 JsonbInitBinary(&obj, jsonb);
2015 baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
2017 res = executeNextItem(cxt, jsp, &next, &obj, found, true);
2019 cxt->baseObject = baseObject;
2021 if (jperIsError(res))
2024 if (res == jperOk && !found)
2032 * Convert boolean execution status 'res' to a boolean JSON item and execute
2035 static JsonPathExecResult
2036 appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
2037 JsonValueList *found, JsonPathBool res)
2042 if (!jspGetNext(jsp, &next) && !found)
2043 return jperOk; /* found singleton boolean value */
2045 if (res == jpbUnknown)
2052 jbv.val.boolean = res == jpbTrue;
2055 return executeNextItem(cxt, jsp, &next, &jbv, found, true);
2059 * Convert jsonpath's scalar or variable node to actual jsonb value.
2061 * If node is a variable then its id returned, otherwise 0 returned.
2064 getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
2070 value->type = jbvNull;
2073 value->type = jbvBool;
2074 value->val.boolean = jspGetBool(item);
2077 value->type = jbvNumeric;
2078 value->val.numeric = jspGetNumeric(item);
2081 value->type = jbvString;
2082 value->val.string.val = jspGetString(item,
2083 &value->val.string.len);
2086 getJsonPathVariable(cxt, item, cxt->vars, value);
2089 elog(ERROR, "unexpected jsonpath item type");
2094 * Get the value of variable passed to jsonpath executor
2097 getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
2098 Jsonb *vars, JsonbValue *value)
2107 value->type = jbvNull;
2111 Assert(variable->type == jpiVariable);
2112 varName = jspGetString(variable, &varNameLength);
2113 tmp.type = jbvString;
2114 tmp.val.string.val = varName;
2115 tmp.val.string.len = varNameLength;
2117 v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
2127 (errcode(ERRCODE_UNDEFINED_OBJECT),
2128 errmsg("could not find jsonpath variable \"%s\"",
2129 pnstrdup(varName, varNameLength))));
2132 JsonbInitBinary(&tmp, vars);
2133 setBaseObject(cxt, &tmp, 1);
2136 /**************** Support functions for JsonPath execution *****************/
2139 * Returns the size of an array item, or -1 if item is not an array.
2142 JsonbArraySize(JsonbValue *jb)
2144 Assert(jb->type != jbvArray);
2146 if (jb->type == jbvBinary)
2148 JsonbContainer *jbc = jb->val.binary.data;
2150 if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
2151 return JsonContainerSize(jbc);
2157 /* Comparison predicate callback. */
2159 executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
2161 JsonPathExecContext *cxt = (JsonPathExecContext *) p;
2163 return compareItems(cmp->type, lv, rv, cxt->useTz);
2167 * Perform per-byte comparison of two strings.
2170 binaryCompareStrings(const char *s1, int len1,
2171 const char *s2, int len2)
2175 cmp = memcmp(s1, s2, Min(len1, len2));
2183 return len1 < len2 ? -1 : 1;
2187 * Compare two strings in the current server encoding using Unicode codepoint
2191 compareStrings(const char *mbstr1, int mblen1,
2192 const char *mbstr2, int mblen2)
2194 if (GetDatabaseEncoding() == PG_SQL_ASCII ||
2195 GetDatabaseEncoding() == PG_UTF8)
2198 * It's known property of UTF-8 strings that their per-byte comparison
2199 * result matches codepoints comparison result. ASCII can be
2200 * considered as special case of UTF-8.
2202 return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2213 * We have to convert other encodings to UTF-8 first, then compare.
2214 * Input strings may be not null-terminated and pg_server_to_any() may
2215 * return them "as is". So, use strlen() only if there is real
2218 utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
2219 utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
2220 utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
2221 utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
2223 cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
2226 * If pg_server_to_any() did no real conversion, then we actually
2227 * compared original strings. So, we already done.
2229 if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
2232 /* Free memory if needed */
2233 if (mbstr1 != utf8str1)
2235 if (mbstr2 != utf8str2)
2239 * When all Unicode codepoints are equal, return result of binary
2240 * comparison. In some edge cases, same characters may have different
2241 * representations in encoding. Then our behavior could diverge from
2242 * standard. However, that allow us to do simple binary comparison
2243 * for "==" operator, which is performance critical in typical cases.
2244 * In future to implement strict standard conformance, we can do
2245 * normalization of input JSON strings.
2248 return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2255 * Compare two SQL/JSON items using comparison operation 'op'.
2258 compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
2263 if (jb1->type != jb2->type)
2265 if (jb1->type == jbvNull || jb2->type == jbvNull)
2268 * Equality and order comparison of nulls to non-nulls returns
2269 * always false, but inequality comparison returns true.
2271 return op == jpiNotEqual ? jpbTrue : jpbFalse;
2273 /* Non-null items of different types are not comparable. */
2283 cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2284 jb1->val.boolean ? 1 : -1;
2287 cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2291 return jb1->val.string.len != jb2->val.string.len ||
2292 memcmp(jb1->val.string.val,
2293 jb2->val.string.val,
2294 jb1->val.string.len) ? jpbFalse : jpbTrue;
2296 cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
2297 jb2->val.string.val, jb2->val.string.len);
2301 bool have_error = false;
2303 cmp = compareDatetime(jb1->val.datetime.value,
2304 jb1->val.datetime.typid,
2305 jb2->val.datetime.value,
2306 jb2->val.datetime.typid,
2318 return jpbUnknown; /* non-scalars are not comparable */
2321 elog(ERROR, "invalid jsonb value type %d", jb1->type);
2338 case jpiLessOrEqual:
2341 case jpiGreaterOrEqual:
2345 elog(ERROR, "unrecognized jsonpath operation: %d", op);
2349 return res ? jpbTrue : jpbFalse;
2352 /* Compare two numerics */
2354 compareNumeric(Numeric a, Numeric b)
2356 return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2358 NumericGetDatum(b)));
2362 copyJsonbValue(JsonbValue *src)
2364 JsonbValue *dst = palloc(sizeof(*dst));
2372 * Execute array subscript expression and convert resulting numeric item to
2373 * the integer type with truncation.
2375 static JsonPathExecResult
2376 getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2380 JsonValueList found = {0};
2381 JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2382 Datum numeric_index;
2383 bool have_error = false;
2385 if (jperIsError(res))
2388 if (JsonValueListLength(&found) != 1 ||
2389 !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2390 RETURN_ERROR(ereport(ERROR,
2391 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2392 errmsg("jsonpath array subscript is not a single numeric value"))));
2394 numeric_index = DirectFunctionCall2(numeric_trunc,
2395 NumericGetDatum(jbv->val.numeric),
2398 *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
2402 RETURN_ERROR(ereport(ERROR,
2403 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2404 errmsg("jsonpath array subscript is out of integer range"))));
2409 /* Save base object and its id needed for the execution of .keyvalue(). */
2410 static JsonBaseObjectInfo
2411 setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2413 JsonBaseObjectInfo baseObject = cxt->baseObject;
2415 cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2416 (JsonbContainer *) jbv->val.binary.data;
2417 cxt->baseObject.id = id;
2423 JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2427 jvl->list = list_make2(jvl->singleton, jbv);
2428 jvl->singleton = NULL;
2430 else if (!jvl->list)
2431 jvl->singleton = jbv;
2433 jvl->list = lappend(jvl->list, jbv);
2437 JsonValueListLength(const JsonValueList *jvl)
2439 return jvl->singleton ? 1 : list_length(jvl->list);
2443 JsonValueListIsEmpty(JsonValueList *jvl)
2445 return !jvl->singleton && list_length(jvl->list) <= 0;
2449 JsonValueListHead(JsonValueList *jvl)
2451 return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2455 JsonValueListGetList(JsonValueList *jvl)
2458 return list_make1(jvl->singleton);
2464 JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2468 it->value = jvl->singleton;
2472 else if (jvl->list != NIL)
2474 it->value = (JsonbValue *) linitial(jvl->list);
2475 it->list = jvl->list;
2476 it->next = list_second_cell(jvl->list);
2487 * Get the next item from the sequence advancing iterator.
2490 JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2492 JsonbValue *result = it->value;
2496 it->value = lfirst(it->next);
2497 it->next = lnext(it->list, it->next);
2508 * Initialize a binary JsonbValue with the given jsonb container.
2511 JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2513 jbv->type = jbvBinary;
2514 jbv->val.binary.data = &jb->root;
2515 jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2521 * Returns jbv* type of of JsonbValue. Note, it never returns jbvBinary as is.
2524 JsonbType(JsonbValue *jb)
2526 int type = jb->type;
2528 if (jb->type == jbvBinary)
2530 JsonbContainer *jbc = (void *) jb->val.binary.data;
2532 /* Scalars should be always extracted during jsonpath execution. */
2533 Assert(!JsonContainerIsScalar(jbc));
2535 if (JsonContainerIsObject(jbc))
2537 else if (JsonContainerIsArray(jbc))
2540 elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2546 /* Get scalar of given type or NULL on type mismatch */
2548 getScalar(JsonbValue *scalar, enum jbvType type)
2550 /* Scalars should be always extracted during jsonpath execution. */
2551 Assert(scalar->type != jbvBinary ||
2552 !JsonContainerIsScalar(scalar->val.binary.data));
2554 return scalar->type == type ? scalar : NULL;
2557 /* Construct a JSON array from the item list */
2559 wrapItemsInArray(const JsonValueList *items)
2561 JsonbParseState *ps = NULL;
2562 JsonValueListIterator it;
2565 pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2567 JsonValueListInitIterator(items, &it);
2568 while ((jbv = JsonValueListNext(items, &it)))
2569 pushJsonbValue(&ps, WJB_ELEM, jbv);
2571 return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
2575 * Cross-type comparison of two datetime SQL/JSON items. If items are
2576 * uncomparable, 'error' flag is set.
2579 compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
2580 bool useTz, bool *have_error)
2582 PGFunction cmpfunc = NULL;
2595 val1 = date2timestamp_opt_error(val1, have_error);
2596 if (have_error && *have_error)
2598 cmpfunc = timestamp_cmp;
2602 case TIMESTAMPTZOID:
2605 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2606 errmsg("cannot convert value from %s to %s without timezone usage",
2607 "date", "timestamptz"),
2608 errhint("use *_tz() function for timezone support")));
2609 val1 = date2timestamptz_opt_error(val1, have_error);
2610 if (have_error && *have_error)
2612 cmpfunc = timestamp_cmp;
2634 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2635 errmsg("cannot convert value from %s to %s without timezone usage",
2637 errhint("use *_tz() function for timezone support")));
2638 val1 = DirectFunctionCall1(time_timetz, val1);
2639 cmpfunc = timetz_cmp;
2645 case TIMESTAMPTZOID:
2657 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2658 errmsg("cannot convert value from %s to %s without timezone usage",
2660 errhint("use *_tz() function for timezone support")));
2661 val2 = DirectFunctionCall1(time_timetz, val2);
2662 cmpfunc = timetz_cmp;
2667 cmpfunc = timetz_cmp;
2673 case TIMESTAMPTZOID:
2683 val2 = date2timestamp_opt_error(val2, have_error);
2684 if (have_error && *have_error)
2686 cmpfunc = timestamp_cmp;
2691 cmpfunc = timestamp_cmp;
2695 case TIMESTAMPTZOID:
2698 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2699 errmsg("cannot convert value from %s to %s without timezone usage",
2700 "timestamp", "timestamptz"),
2701 errhint("use *_tz() function for timezone support")));
2702 val1 = timestamp2timestamptz_opt_error(val1, have_error);
2703 if (have_error && *have_error)
2705 cmpfunc = timestamp_cmp;
2716 case TIMESTAMPTZOID:
2722 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2723 errmsg("cannot convert value from %s to %s without timezone usage",
2724 "date", "timestamptz"),
2725 errhint("use *_tz() function for timezone support")));
2726 val2 = date2timestamptz_opt_error(val2, have_error);
2727 if (have_error && *have_error)
2729 cmpfunc = timestamp_cmp;
2736 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2737 errmsg("cannot convert value from %s to %s without timezone usage",
2738 "timestamp", "timestamptz"),
2739 errhint("use *_tz() function for timezone support")));
2740 val2 = timestamp2timestamptz_opt_error(val2, have_error);
2741 if (have_error && *have_error)
2743 cmpfunc = timestamp_cmp;
2747 case TIMESTAMPTZOID:
2748 cmpfunc = timestamp_cmp;
2760 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %d",
2768 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %d",
2771 *have_error = false;
2773 return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));