Harmonize more parameter names in bulk.
[pgsql.git] / src / backend / utils / adt / jsonpath_exec.c
blob8d83b2edb357be4cb353abf4996505663073e38c
1 /*-------------------------------------------------------------------------
3 * jsonpath_exec.c
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
18 * are following:
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 avoid 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-2022, PostgreSQL Global Development Group
54 * IDENTIFICATION
55 * src/backend/utils/adt/jsonpath_exec.c
57 *-------------------------------------------------------------------------
60 #include "postgres.h"
62 #include "catalog/pg_collation.h"
63 #include "catalog/pg_type.h"
64 #include "funcapi.h"
65 #include "lib/stringinfo.h"
66 #include "miscadmin.h"
67 #include "regex/regex.h"
68 #include "utils/builtins.h"
69 #include "utils/date.h"
70 #include "utils/datetime.h"
71 #include "utils/datum.h"
72 #include "utils/float.h"
73 #include "utils/formatting.h"
74 #include "utils/guc.h"
75 #include "utils/json.h"
76 #include "utils/jsonpath.h"
77 #include "utils/timestamp.h"
78 #include "utils/varlena.h"
81 * Represents "base object" and it's "id" for .keyvalue() evaluation.
83 typedef struct JsonBaseObjectInfo
85 JsonbContainer *jbc;
86 int id;
87 } 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()
98 * evaluation */
99 int lastGeneratedObjectId; /* "id" counter for .keyvalue()
100 * evaluation */
101 int innermostArraySize; /* for LAST array index evaluation */
102 bool laxMode; /* true for "lax" mode, false for "strict"
103 * mode */
104 bool ignoreStructuralErrors; /* with "true" structural errors such
105 * as absence of required json item or
106 * unexpected json item type are
107 * ignored */
108 bool throwErrors; /* with "false" all suppressible errors are
109 * suppressed */
110 bool useTz;
111 } JsonPathExecContext;
113 /* Context for LIKE_REGEX execution. */
114 typedef struct JsonLikeRegexContext
116 text *regex;
117 int cflags;
118 } JsonLikeRegexContext;
120 /* Result of jsonpath predicate evaluation */
121 typedef enum JsonPathBool
123 jpbFalse = 0,
124 jpbTrue = 1,
125 jpbUnknown = 2
126 } JsonPathBool;
128 /* Result of jsonpath expression evaluation */
129 typedef enum JsonPathExecResult
131 jperOk = 0,
132 jperNotFound = 1,
133 jperError = 2
134 } JsonPathExecResult;
136 #define jperIsError(jper) ((jper) == jperError)
139 * List of jsonb values with shortcut for single-value list.
141 typedef struct JsonValueList
143 JsonbValue *singleton;
144 List *list;
145 } JsonValueList;
147 typedef struct JsonValueListIterator
149 JsonbValue *value;
150 List *list;
151 ListCell *next;
152 } JsonValueListIterator;
154 /* strict/lax flags is decomposed into four [un]wrap/error flags */
155 #define jspStrictAbsenseOfErrors(cxt) (!(cxt)->laxMode)
156 #define jspAutoUnwrap(cxt) ((cxt)->laxMode)
157 #define jspAutoWrap(cxt) ((cxt)->laxMode)
158 #define jspIgnoreStructuralErrors(cxt) ((cxt)->ignoreStructuralErrors)
159 #define jspThrowErrors(cxt) ((cxt)->throwErrors)
161 /* Convenience macro: return or throw error depending on context */
162 #define RETURN_ERROR(throw_error) \
163 do { \
164 if (jspThrowErrors(cxt)) \
165 throw_error; \
166 else \
167 return jperError; \
168 } while (0)
170 typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
171 JsonbValue *larg,
172 JsonbValue *rarg,
173 void *param);
174 typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
176 static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
177 Jsonb *json, bool throwErrors,
178 JsonValueList *result, bool useTz);
179 static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
180 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
181 static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
182 JsonPathItem *jsp, JsonbValue *jb,
183 JsonValueList *found, bool unwrap);
184 static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
185 JsonPathItem *jsp, JsonbValue *jb,
186 JsonValueList *found, bool unwrapElements);
187 static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
188 JsonPathItem *cur, JsonPathItem *next,
189 JsonbValue *v, JsonValueList *found, bool copy);
190 static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
191 bool unwrap, JsonValueList *found);
192 static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp,
193 JsonbValue *jb, bool unwrap, JsonValueList *found);
194 static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
195 JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
196 static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
197 JsonPathItem *jsp, JsonbValue *jb);
198 static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
199 JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
200 uint32 level, uint32 first, uint32 last,
201 bool ignoreStructuralErrors, bool unwrapNext);
202 static JsonPathBool executePredicate(JsonPathExecContext *cxt,
203 JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
204 JsonbValue *jb, bool unwrapRightArg,
205 JsonPathPredicateCallback exec, void *param);
206 static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
207 JsonPathItem *jsp, JsonbValue *jb,
208 BinaryArithmFunc func, JsonValueList *found);
209 static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
210 JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
211 JsonValueList *found);
212 static JsonPathBool executeStartsWith(JsonPathItem *jsp,
213 JsonbValue *whole, JsonbValue *initial, void *param);
214 static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
215 JsonbValue *rarg, void *param);
216 static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
217 JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
218 JsonValueList *found);
219 static JsonPathExecResult executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
220 JsonbValue *jb, JsonValueList *found);
221 static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
222 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
223 static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
224 JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
225 static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
226 JsonbValue *value);
227 static void getJsonPathVariable(JsonPathExecContext *cxt,
228 JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
229 static int JsonbArraySize(JsonbValue *jb);
230 static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
231 JsonbValue *rv, void *p);
232 static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2,
233 bool useTz);
234 static int compareNumeric(Numeric a, Numeric b);
235 static JsonbValue *copyJsonbValue(JsonbValue *src);
236 static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
237 JsonPathItem *jsp, JsonbValue *jb, int32 *index);
238 static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
239 JsonbValue *jbv, int32 id);
240 static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
241 static int JsonValueListLength(const JsonValueList *jvl);
242 static bool JsonValueListIsEmpty(JsonValueList *jvl);
243 static JsonbValue *JsonValueListHead(JsonValueList *jvl);
244 static List *JsonValueListGetList(JsonValueList *jvl);
245 static void JsonValueListInitIterator(const JsonValueList *jvl,
246 JsonValueListIterator *it);
247 static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
248 JsonValueListIterator *it);
249 static int JsonbType(JsonbValue *jb);
250 static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
251 static int JsonbType(JsonbValue *jb);
252 static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
253 static JsonbValue *wrapItemsInArray(const JsonValueList *items);
254 static int compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
255 bool useTz, bool *cast_error);
257 /****************** User interface to JsonPath executor ********************/
260 * jsonb_path_exists
261 * Returns true if jsonpath returns at least one item for the specified
262 * jsonb value. This function and jsonb_path_match() are used to
263 * implement @? and @@ operators, which in turn are intended to have an
264 * index support. Thus, it's desirable to make it easier to achieve
265 * consistency between index scan results and sequential scan results.
266 * So, we throw as few errors as possible. Regarding this function,
267 * such behavior also matches behavior of JSON_EXISTS() clause of
268 * SQL/JSON. Regarding jsonb_path_match(), this function doesn't have
269 * an analogy in SQL/JSON, so we define its behavior on our own.
271 static Datum
272 jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
274 Jsonb *jb = PG_GETARG_JSONB_P(0);
275 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
276 JsonPathExecResult res;
277 Jsonb *vars = NULL;
278 bool silent = true;
280 if (PG_NARGS() == 4)
282 vars = PG_GETARG_JSONB_P(2);
283 silent = PG_GETARG_BOOL(3);
286 res = executeJsonPath(jp, vars, jb, !silent, NULL, tz);
288 PG_FREE_IF_COPY(jb, 0);
289 PG_FREE_IF_COPY(jp, 1);
291 if (jperIsError(res))
292 PG_RETURN_NULL();
294 PG_RETURN_BOOL(res == jperOk);
297 Datum
298 jsonb_path_exists(PG_FUNCTION_ARGS)
300 return jsonb_path_exists_internal(fcinfo, false);
303 Datum
304 jsonb_path_exists_tz(PG_FUNCTION_ARGS)
306 return jsonb_path_exists_internal(fcinfo, true);
310 * jsonb_path_exists_opr
311 * Implementation of operator "jsonb @? jsonpath" (2-argument version of
312 * jsonb_path_exists()).
314 Datum
315 jsonb_path_exists_opr(PG_FUNCTION_ARGS)
317 /* just call the other one -- it can handle both cases */
318 return jsonb_path_exists_internal(fcinfo, false);
322 * jsonb_path_match
323 * Returns jsonpath predicate result item for the specified jsonb value.
324 * See jsonb_path_exists() comment for details regarding error handling.
326 static Datum
327 jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
329 Jsonb *jb = PG_GETARG_JSONB_P(0);
330 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
331 JsonValueList found = {0};
332 Jsonb *vars = NULL;
333 bool silent = true;
335 if (PG_NARGS() == 4)
337 vars = PG_GETARG_JSONB_P(2);
338 silent = PG_GETARG_BOOL(3);
341 (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
343 PG_FREE_IF_COPY(jb, 0);
344 PG_FREE_IF_COPY(jp, 1);
346 if (JsonValueListLength(&found) == 1)
348 JsonbValue *jbv = JsonValueListHead(&found);
350 if (jbv->type == jbvBool)
351 PG_RETURN_BOOL(jbv->val.boolean);
353 if (jbv->type == jbvNull)
354 PG_RETURN_NULL();
357 if (!silent)
358 ereport(ERROR,
359 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
360 errmsg("single boolean result is expected")));
362 PG_RETURN_NULL();
365 Datum
366 jsonb_path_match(PG_FUNCTION_ARGS)
368 return jsonb_path_match_internal(fcinfo, false);
371 Datum
372 jsonb_path_match_tz(PG_FUNCTION_ARGS)
374 return jsonb_path_match_internal(fcinfo, true);
378 * jsonb_path_match_opr
379 * Implementation of operator "jsonb @@ jsonpath" (2-argument version of
380 * jsonb_path_match()).
382 Datum
383 jsonb_path_match_opr(PG_FUNCTION_ARGS)
385 /* just call the other one -- it can handle both cases */
386 return jsonb_path_match_internal(fcinfo, false);
390 * jsonb_path_query
391 * Executes jsonpath for given jsonb document and returns result as
392 * rowset.
394 static Datum
395 jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
397 FuncCallContext *funcctx;
398 List *found;
399 JsonbValue *v;
400 ListCell *c;
402 if (SRF_IS_FIRSTCALL())
404 JsonPath *jp;
405 Jsonb *jb;
406 MemoryContext oldcontext;
407 Jsonb *vars;
408 bool silent;
409 JsonValueList found = {0};
411 funcctx = SRF_FIRSTCALL_INIT();
412 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
414 jb = PG_GETARG_JSONB_P_COPY(0);
415 jp = PG_GETARG_JSONPATH_P_COPY(1);
416 vars = PG_GETARG_JSONB_P_COPY(2);
417 silent = PG_GETARG_BOOL(3);
419 (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
421 funcctx->user_fctx = JsonValueListGetList(&found);
423 MemoryContextSwitchTo(oldcontext);
426 funcctx = SRF_PERCALL_SETUP();
427 found = funcctx->user_fctx;
429 c = list_head(found);
431 if (c == NULL)
432 SRF_RETURN_DONE(funcctx);
434 v = lfirst(c);
435 funcctx->user_fctx = list_delete_first(found);
437 SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
440 Datum
441 jsonb_path_query(PG_FUNCTION_ARGS)
443 return jsonb_path_query_internal(fcinfo, false);
446 Datum
447 jsonb_path_query_tz(PG_FUNCTION_ARGS)
449 return jsonb_path_query_internal(fcinfo, true);
453 * jsonb_path_query_array
454 * Executes jsonpath for given jsonb document and returns result as
455 * jsonb array.
457 static Datum
458 jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
460 Jsonb *jb = PG_GETARG_JSONB_P(0);
461 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
462 JsonValueList found = {0};
463 Jsonb *vars = PG_GETARG_JSONB_P(2);
464 bool silent = PG_GETARG_BOOL(3);
466 (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
468 PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
471 Datum
472 jsonb_path_query_array(PG_FUNCTION_ARGS)
474 return jsonb_path_query_array_internal(fcinfo, false);
477 Datum
478 jsonb_path_query_array_tz(PG_FUNCTION_ARGS)
480 return jsonb_path_query_array_internal(fcinfo, true);
484 * jsonb_path_query_first
485 * Executes jsonpath for given jsonb document and returns first result
486 * item. If there are no items, NULL returned.
488 static Datum
489 jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
491 Jsonb *jb = PG_GETARG_JSONB_P(0);
492 JsonPath *jp = PG_GETARG_JSONPATH_P(1);
493 JsonValueList found = {0};
494 Jsonb *vars = PG_GETARG_JSONB_P(2);
495 bool silent = PG_GETARG_BOOL(3);
497 (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
499 if (JsonValueListLength(&found) >= 1)
500 PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
501 else
502 PG_RETURN_NULL();
505 Datum
506 jsonb_path_query_first(PG_FUNCTION_ARGS)
508 return jsonb_path_query_first_internal(fcinfo, false);
511 Datum
512 jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
514 return jsonb_path_query_first_internal(fcinfo, true);
517 /********************Execute functions for JsonPath**************************/
520 * Interface to jsonpath executor
522 * 'path' - jsonpath to be executed
523 * 'vars' - variables to be substituted to jsonpath
524 * 'json' - target document for jsonpath evaluation
525 * 'throwErrors' - whether we should throw suppressible errors
526 * 'result' - list to store result items into
528 * Returns an error if a recoverable error happens during processing, or NULL
529 * on no error.
531 * Note, jsonb and jsonpath values should be available and untoasted during
532 * work because JsonPathItem, JsonbValue and result item could have pointers
533 * into input values. If caller needs to just check if document matches
534 * jsonpath, then it doesn't provide a result arg. In this case executor
535 * works till first positive result and does not check the rest if possible.
536 * In other case it tries to find all the satisfied result items.
538 static JsonPathExecResult
539 executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
540 JsonValueList *result, bool useTz)
542 JsonPathExecContext cxt;
543 JsonPathExecResult res;
544 JsonPathItem jsp;
545 JsonbValue jbv;
547 jspInit(&jsp, path);
549 if (!JsonbExtractScalar(&json->root, &jbv))
550 JsonbInitBinary(&jbv, json);
552 if (vars && !JsonContainerIsObject(&vars->root))
554 ereport(ERROR,
555 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
556 errmsg("\"vars\" argument is not an object"),
557 errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
560 cxt.vars = vars;
561 cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
562 cxt.ignoreStructuralErrors = cxt.laxMode;
563 cxt.root = &jbv;
564 cxt.current = &jbv;
565 cxt.baseObject.jbc = NULL;
566 cxt.baseObject.id = 0;
567 cxt.lastGeneratedObjectId = vars ? 2 : 1;
568 cxt.innermostArraySize = -1;
569 cxt.throwErrors = throwErrors;
570 cxt.useTz = useTz;
572 if (jspStrictAbsenseOfErrors(&cxt) && !result)
575 * In strict mode we must get a complete list of values to check that
576 * there are no errors at all.
578 JsonValueList vals = {0};
580 res = executeItem(&cxt, &jsp, &jbv, &vals);
582 if (jperIsError(res))
583 return res;
585 return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
588 res = executeItem(&cxt, &jsp, &jbv, result);
590 Assert(!throwErrors || !jperIsError(res));
592 return res;
596 * Execute jsonpath with automatic unwrapping of current item in lax mode.
598 static JsonPathExecResult
599 executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
600 JsonbValue *jb, JsonValueList *found)
602 return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
606 * Main jsonpath executor function: walks on jsonpath structure, finds
607 * relevant parts of jsonb and evaluates expressions over them.
608 * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
610 static JsonPathExecResult
611 executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
612 JsonbValue *jb, JsonValueList *found, bool unwrap)
614 JsonPathItem elem;
615 JsonPathExecResult res = jperNotFound;
616 JsonBaseObjectInfo baseObject;
618 check_stack_depth();
619 CHECK_FOR_INTERRUPTS();
621 switch (jsp->type)
623 /* all boolean item types: */
624 case jpiAnd:
625 case jpiOr:
626 case jpiNot:
627 case jpiIsUnknown:
628 case jpiEqual:
629 case jpiNotEqual:
630 case jpiLess:
631 case jpiGreater:
632 case jpiLessOrEqual:
633 case jpiGreaterOrEqual:
634 case jpiExists:
635 case jpiStartsWith:
636 case jpiLikeRegex:
638 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
640 res = appendBoolResult(cxt, jsp, found, st);
641 break;
644 case jpiKey:
645 if (JsonbType(jb) == jbvObject)
647 JsonbValue *v;
648 JsonbValue key;
650 key.type = jbvString;
651 key.val.string.val = jspGetString(jsp, &key.val.string.len);
653 v = findJsonbValueFromContainer(jb->val.binary.data,
654 JB_FOBJECT, &key);
656 if (v != NULL)
658 res = executeNextItem(cxt, jsp, NULL,
659 v, found, false);
661 /* free value if it was not added to found list */
662 if (jspHasNext(jsp) || !found)
663 pfree(v);
665 else if (!jspIgnoreStructuralErrors(cxt))
667 Assert(found);
669 if (!jspThrowErrors(cxt))
670 return jperError;
672 ereport(ERROR,
673 (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
674 errmsg("JSON object does not contain key \"%s\"",
675 pnstrdup(key.val.string.val,
676 key.val.string.len))));
679 else if (unwrap && JsonbType(jb) == jbvArray)
680 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
681 else if (!jspIgnoreStructuralErrors(cxt))
683 Assert(found);
684 RETURN_ERROR(ereport(ERROR,
685 (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
686 errmsg("jsonpath member accessor can only be applied to an object"))));
688 break;
690 case jpiRoot:
691 jb = cxt->root;
692 baseObject = setBaseObject(cxt, jb, 0);
693 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
694 cxt->baseObject = baseObject;
695 break;
697 case jpiCurrent:
698 res = executeNextItem(cxt, jsp, NULL, cxt->current,
699 found, true);
700 break;
702 case jpiAnyArray:
703 if (JsonbType(jb) == jbvArray)
705 bool hasNext = jspGetNext(jsp, &elem);
707 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
708 jb, found, jspAutoUnwrap(cxt));
710 else if (jspAutoWrap(cxt))
711 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
712 else if (!jspIgnoreStructuralErrors(cxt))
713 RETURN_ERROR(ereport(ERROR,
714 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
715 errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
716 break;
718 case jpiIndexArray:
719 if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
721 int innermostArraySize = cxt->innermostArraySize;
722 int i;
723 int size = JsonbArraySize(jb);
724 bool singleton = size < 0;
725 bool hasNext = jspGetNext(jsp, &elem);
727 if (singleton)
728 size = 1;
730 cxt->innermostArraySize = size; /* for LAST evaluation */
732 for (i = 0; i < jsp->content.array.nelems; i++)
734 JsonPathItem from;
735 JsonPathItem to;
736 int32 index;
737 int32 index_from;
738 int32 index_to;
739 bool range = jspGetArraySubscript(jsp, &from,
740 &to, i);
742 res = getArrayIndex(cxt, &from, jb, &index_from);
744 if (jperIsError(res))
745 break;
747 if (range)
749 res = getArrayIndex(cxt, &to, jb, &index_to);
751 if (jperIsError(res))
752 break;
754 else
755 index_to = index_from;
757 if (!jspIgnoreStructuralErrors(cxt) &&
758 (index_from < 0 ||
759 index_from > index_to ||
760 index_to >= size))
761 RETURN_ERROR(ereport(ERROR,
762 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
763 errmsg("jsonpath array subscript is out of bounds"))));
765 if (index_from < 0)
766 index_from = 0;
768 if (index_to >= size)
769 index_to = size - 1;
771 res = jperNotFound;
773 for (index = index_from; index <= index_to; index++)
775 JsonbValue *v;
776 bool copy;
778 if (singleton)
780 v = jb;
781 copy = true;
783 else
785 v = getIthJsonbValueFromContainer(jb->val.binary.data,
786 (uint32) index);
788 if (v == NULL)
789 continue;
791 copy = false;
794 if (!hasNext && !found)
795 return jperOk;
797 res = executeNextItem(cxt, jsp, &elem, v, found,
798 copy);
800 if (jperIsError(res))
801 break;
803 if (res == jperOk && !found)
804 break;
807 if (jperIsError(res))
808 break;
810 if (res == jperOk && !found)
811 break;
814 cxt->innermostArraySize = innermostArraySize;
816 else if (!jspIgnoreStructuralErrors(cxt))
818 RETURN_ERROR(ereport(ERROR,
819 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
820 errmsg("jsonpath array accessor can only be applied to an array"))));
822 break;
824 case jpiLast:
826 JsonbValue tmpjbv;
827 JsonbValue *lastjbv;
828 int last;
829 bool hasNext = jspGetNext(jsp, &elem);
831 if (cxt->innermostArraySize < 0)
832 elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
834 if (!hasNext && !found)
836 res = jperOk;
837 break;
840 last = cxt->innermostArraySize - 1;
842 lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
844 lastjbv->type = jbvNumeric;
845 lastjbv->val.numeric = int64_to_numeric(last);
847 res = executeNextItem(cxt, jsp, &elem,
848 lastjbv, found, hasNext);
850 break;
852 case jpiAnyKey:
853 if (JsonbType(jb) == jbvObject)
855 bool hasNext = jspGetNext(jsp, &elem);
857 if (jb->type != jbvBinary)
858 elog(ERROR, "invalid jsonb object type: %d", jb->type);
860 return executeAnyItem
861 (cxt, hasNext ? &elem : NULL,
862 jb->val.binary.data, found, 1, 1, 1,
863 false, jspAutoUnwrap(cxt));
865 else if (unwrap && JsonbType(jb) == jbvArray)
866 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
867 else if (!jspIgnoreStructuralErrors(cxt))
869 Assert(found);
870 RETURN_ERROR(ereport(ERROR,
871 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
872 errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
874 break;
876 case jpiAdd:
877 return executeBinaryArithmExpr(cxt, jsp, jb,
878 numeric_add_opt_error, found);
880 case jpiSub:
881 return executeBinaryArithmExpr(cxt, jsp, jb,
882 numeric_sub_opt_error, found);
884 case jpiMul:
885 return executeBinaryArithmExpr(cxt, jsp, jb,
886 numeric_mul_opt_error, found);
888 case jpiDiv:
889 return executeBinaryArithmExpr(cxt, jsp, jb,
890 numeric_div_opt_error, found);
892 case jpiMod:
893 return executeBinaryArithmExpr(cxt, jsp, jb,
894 numeric_mod_opt_error, found);
896 case jpiPlus:
897 return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
899 case jpiMinus:
900 return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
901 found);
903 case jpiFilter:
905 JsonPathBool st;
907 if (unwrap && JsonbType(jb) == jbvArray)
908 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
909 false);
911 jspGetArg(jsp, &elem);
912 st = executeNestedBoolItem(cxt, &elem, jb);
913 if (st != jpbTrue)
914 res = jperNotFound;
915 else
916 res = executeNextItem(cxt, jsp, NULL,
917 jb, found, true);
918 break;
921 case jpiAny:
923 bool hasNext = jspGetNext(jsp, &elem);
925 /* first try without any intermediate steps */
926 if (jsp->content.anybounds.first == 0)
928 bool savedIgnoreStructuralErrors;
930 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
931 cxt->ignoreStructuralErrors = true;
932 res = executeNextItem(cxt, jsp, &elem,
933 jb, found, true);
934 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
936 if (res == jperOk && !found)
937 break;
940 if (jb->type == jbvBinary)
941 res = executeAnyItem
942 (cxt, hasNext ? &elem : NULL,
943 jb->val.binary.data, found,
945 jsp->content.anybounds.first,
946 jsp->content.anybounds.last,
947 true, jspAutoUnwrap(cxt));
948 break;
951 case jpiNull:
952 case jpiBool:
953 case jpiNumeric:
954 case jpiString:
955 case jpiVariable:
957 JsonbValue vbuf;
958 JsonbValue *v;
959 bool hasNext = jspGetNext(jsp, &elem);
961 if (!hasNext && !found)
963 res = jperOk; /* skip evaluation */
964 break;
967 v = hasNext ? &vbuf : palloc(sizeof(*v));
969 baseObject = cxt->baseObject;
970 getJsonPathItem(cxt, jsp, v);
972 res = executeNextItem(cxt, jsp, &elem,
973 v, found, hasNext);
974 cxt->baseObject = baseObject;
976 break;
978 case jpiType:
980 JsonbValue *jbv = palloc(sizeof(*jbv));
982 jbv->type = jbvString;
983 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
984 jbv->val.string.len = strlen(jbv->val.string.val);
986 res = executeNextItem(cxt, jsp, NULL, jbv,
987 found, false);
989 break;
991 case jpiSize:
993 int size = JsonbArraySize(jb);
995 if (size < 0)
997 if (!jspAutoWrap(cxt))
999 if (!jspIgnoreStructuralErrors(cxt))
1000 RETURN_ERROR(ereport(ERROR,
1001 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
1002 errmsg("jsonpath item method .%s() can only be applied to an array",
1003 jspOperationName(jsp->type)))));
1004 break;
1007 size = 1;
1010 jb = palloc(sizeof(*jb));
1012 jb->type = jbvNumeric;
1013 jb->val.numeric = int64_to_numeric(size);
1015 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
1017 break;
1019 case jpiAbs:
1020 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
1021 found);
1023 case jpiFloor:
1024 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
1025 found);
1027 case jpiCeiling:
1028 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
1029 found);
1031 case jpiDouble:
1033 JsonbValue jbv;
1035 if (unwrap && JsonbType(jb) == jbvArray)
1036 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1037 false);
1039 if (jb->type == jbvNumeric)
1041 char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1042 NumericGetDatum(jb->val.numeric)));
1043 double val;
1044 bool have_error = false;
1046 val = float8in_internal_opt_error(tmp,
1047 NULL,
1048 "double precision",
1049 tmp,
1050 &have_error);
1052 if (have_error || isinf(val) || isnan(val))
1053 RETURN_ERROR(ereport(ERROR,
1054 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1055 errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
1056 jspOperationName(jsp->type)))));
1057 res = jperOk;
1059 else if (jb->type == jbvString)
1061 /* cast string as double */
1062 double val;
1063 char *tmp = pnstrdup(jb->val.string.val,
1064 jb->val.string.len);
1065 bool have_error = false;
1067 val = float8in_internal_opt_error(tmp,
1068 NULL,
1069 "double precision",
1070 tmp,
1071 &have_error);
1073 if (have_error || isinf(val) || isnan(val))
1074 RETURN_ERROR(ereport(ERROR,
1075 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1076 errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
1077 jspOperationName(jsp->type)))));
1079 jb = &jbv;
1080 jb->type = jbvNumeric;
1081 jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1082 Float8GetDatum(val)));
1083 res = jperOk;
1086 if (res == jperNotFound)
1087 RETURN_ERROR(ereport(ERROR,
1088 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1089 errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1090 jspOperationName(jsp->type)))));
1092 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1094 break;
1096 case jpiDatetime:
1097 if (unwrap && JsonbType(jb) == jbvArray)
1098 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1100 return executeDateTimeMethod(cxt, jsp, jb, found);
1102 case jpiKeyValue:
1103 if (unwrap && JsonbType(jb) == jbvArray)
1104 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1106 return executeKeyValueMethod(cxt, jsp, jb, found);
1108 default:
1109 elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1112 return res;
1116 * Unwrap current array item and execute jsonpath for each of its elements.
1118 static JsonPathExecResult
1119 executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1120 JsonbValue *jb, JsonValueList *found,
1121 bool unwrapElements)
1123 if (jb->type != jbvBinary)
1125 Assert(jb->type != jbvArray);
1126 elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1129 return executeAnyItem
1130 (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1131 false, unwrapElements);
1135 * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1136 * list if provided.
1138 static JsonPathExecResult
1139 executeNextItem(JsonPathExecContext *cxt,
1140 JsonPathItem *cur, JsonPathItem *next,
1141 JsonbValue *v, JsonValueList *found, bool copy)
1143 JsonPathItem elem;
1144 bool hasNext;
1146 if (!cur)
1147 hasNext = next != NULL;
1148 else if (next)
1149 hasNext = jspHasNext(cur);
1150 else
1152 next = &elem;
1153 hasNext = jspGetNext(cur, next);
1156 if (hasNext)
1157 return executeItem(cxt, next, v, found);
1159 if (found)
1160 JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1162 return jperOk;
1166 * Same as executeItem(), but when "unwrap == true" automatically unwraps
1167 * each array item from the resulting sequence in lax mode.
1169 static JsonPathExecResult
1170 executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1171 JsonbValue *jb, bool unwrap,
1172 JsonValueList *found)
1174 if (unwrap && jspAutoUnwrap(cxt))
1176 JsonValueList seq = {0};
1177 JsonValueListIterator it;
1178 JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1179 JsonbValue *item;
1181 if (jperIsError(res))
1182 return res;
1184 JsonValueListInitIterator(&seq, &it);
1185 while ((item = JsonValueListNext(&seq, &it)))
1187 Assert(item->type != jbvArray);
1189 if (JsonbType(item) == jbvArray)
1190 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1191 else
1192 JsonValueListAppend(found, item);
1195 return jperOk;
1198 return executeItem(cxt, jsp, jb, found);
1202 * Same as executeItemOptUnwrapResult(), but with error suppression.
1204 static JsonPathExecResult
1205 executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1206 JsonPathItem *jsp,
1207 JsonbValue *jb, bool unwrap,
1208 JsonValueList *found)
1210 JsonPathExecResult res;
1211 bool throwErrors = cxt->throwErrors;
1213 cxt->throwErrors = false;
1214 res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1215 cxt->throwErrors = throwErrors;
1217 return res;
1220 /* Execute boolean-valued jsonpath expression. */
1221 static JsonPathBool
1222 executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1223 JsonbValue *jb, bool canHaveNext)
1225 JsonPathItem larg;
1226 JsonPathItem rarg;
1227 JsonPathBool res;
1228 JsonPathBool res2;
1230 if (!canHaveNext && jspHasNext(jsp))
1231 elog(ERROR, "boolean jsonpath item cannot have next item");
1233 switch (jsp->type)
1235 case jpiAnd:
1236 jspGetLeftArg(jsp, &larg);
1237 res = executeBoolItem(cxt, &larg, jb, false);
1239 if (res == jpbFalse)
1240 return jpbFalse;
1243 * SQL/JSON says that we should check second arg in case of
1244 * jperError
1247 jspGetRightArg(jsp, &rarg);
1248 res2 = executeBoolItem(cxt, &rarg, jb, false);
1250 return res2 == jpbTrue ? res : res2;
1252 case jpiOr:
1253 jspGetLeftArg(jsp, &larg);
1254 res = executeBoolItem(cxt, &larg, jb, false);
1256 if (res == jpbTrue)
1257 return jpbTrue;
1259 jspGetRightArg(jsp, &rarg);
1260 res2 = executeBoolItem(cxt, &rarg, jb, false);
1262 return res2 == jpbFalse ? res : res2;
1264 case jpiNot:
1265 jspGetArg(jsp, &larg);
1267 res = executeBoolItem(cxt, &larg, jb, false);
1269 if (res == jpbUnknown)
1270 return jpbUnknown;
1272 return res == jpbTrue ? jpbFalse : jpbTrue;
1274 case jpiIsUnknown:
1275 jspGetArg(jsp, &larg);
1276 res = executeBoolItem(cxt, &larg, jb, false);
1277 return res == jpbUnknown ? jpbTrue : jpbFalse;
1279 case jpiEqual:
1280 case jpiNotEqual:
1281 case jpiLess:
1282 case jpiGreater:
1283 case jpiLessOrEqual:
1284 case jpiGreaterOrEqual:
1285 jspGetLeftArg(jsp, &larg);
1286 jspGetRightArg(jsp, &rarg);
1287 return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1288 executeComparison, cxt);
1290 case jpiStartsWith: /* 'whole STARTS WITH initial' */
1291 jspGetLeftArg(jsp, &larg); /* 'whole' */
1292 jspGetRightArg(jsp, &rarg); /* 'initial' */
1293 return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1294 executeStartsWith, NULL);
1296 case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1299 * 'expr' is a sequence-returning expression. 'pattern' is a
1300 * regex string literal. SQL/JSON standard requires XQuery
1301 * regexes, but we use Postgres regexes here. 'flags' is a
1302 * string literal converted to integer flags at compile-time.
1304 JsonLikeRegexContext lrcxt = {0};
1306 jspInitByBuffer(&larg, jsp->base,
1307 jsp->content.like_regex.expr);
1309 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1310 executeLikeRegex, &lrcxt);
1313 case jpiExists:
1314 jspGetArg(jsp, &larg);
1316 if (jspStrictAbsenseOfErrors(cxt))
1319 * In strict mode we must get a complete list of values to
1320 * check that there are no errors at all.
1322 JsonValueList vals = {0};
1323 JsonPathExecResult res =
1324 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1325 false, &vals);
1327 if (jperIsError(res))
1328 return jpbUnknown;
1330 return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1332 else
1334 JsonPathExecResult res =
1335 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1336 false, NULL);
1338 if (jperIsError(res))
1339 return jpbUnknown;
1341 return res == jperOk ? jpbTrue : jpbFalse;
1344 default:
1345 elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1346 return jpbUnknown;
1351 * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1352 * item onto the stack.
1354 static JsonPathBool
1355 executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1356 JsonbValue *jb)
1358 JsonbValue *prev;
1359 JsonPathBool res;
1361 prev = cxt->current;
1362 cxt->current = jb;
1363 res = executeBoolItem(cxt, jsp, jb, false);
1364 cxt->current = prev;
1366 return res;
1370 * Implementation of several jsonpath nodes:
1371 * - jpiAny (.** accessor),
1372 * - jpiAnyKey (.* accessor),
1373 * - jpiAnyArray ([*] accessor)
1375 static JsonPathExecResult
1376 executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1377 JsonValueList *found, uint32 level, uint32 first, uint32 last,
1378 bool ignoreStructuralErrors, bool unwrapNext)
1380 JsonPathExecResult res = jperNotFound;
1381 JsonbIterator *it;
1382 int32 r;
1383 JsonbValue v;
1385 check_stack_depth();
1387 if (level > last)
1388 return res;
1390 it = JsonbIteratorInit(jbc);
1393 * Recursively iterate over jsonb objects/arrays
1395 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1397 if (r == WJB_KEY)
1399 r = JsonbIteratorNext(&it, &v, true);
1400 Assert(r == WJB_VALUE);
1403 if (r == WJB_VALUE || r == WJB_ELEM)
1406 if (level >= first ||
1407 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1408 v.type != jbvBinary)) /* leaves only requested */
1410 /* check expression */
1411 if (jsp)
1413 if (ignoreStructuralErrors)
1415 bool savedIgnoreStructuralErrors;
1417 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1418 cxt->ignoreStructuralErrors = true;
1419 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1420 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1422 else
1423 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1425 if (jperIsError(res))
1426 break;
1428 if (res == jperOk && !found)
1429 break;
1431 else if (found)
1432 JsonValueListAppend(found, copyJsonbValue(&v));
1433 else
1434 return jperOk;
1437 if (level < last && v.type == jbvBinary)
1439 res = executeAnyItem
1440 (cxt, jsp, v.val.binary.data, found,
1441 level + 1, first, last,
1442 ignoreStructuralErrors, unwrapNext);
1444 if (jperIsError(res))
1445 break;
1447 if (res == jperOk && found == NULL)
1448 break;
1453 return res;
1457 * Execute unary or binary predicate.
1459 * Predicates have existence semantics, because their operands are item
1460 * sequences. Pairs of items from the left and right operand's sequences are
1461 * checked. TRUE returned only if any pair satisfying the condition is found.
1462 * In strict mode, even if the desired pair has already been found, all pairs
1463 * still need to be examined to check the absence of errors. If any error
1464 * occurs, UNKNOWN (analogous to SQL NULL) is returned.
1466 static JsonPathBool
1467 executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
1468 JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
1469 bool unwrapRightArg, JsonPathPredicateCallback exec,
1470 void *param)
1472 JsonPathExecResult res;
1473 JsonValueListIterator lseqit;
1474 JsonValueList lseq = {0};
1475 JsonValueList rseq = {0};
1476 JsonbValue *lval;
1477 bool error = false;
1478 bool found = false;
1480 /* Left argument is always auto-unwrapped. */
1481 res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
1482 if (jperIsError(res))
1483 return jpbUnknown;
1485 if (rarg)
1487 /* Right argument is conditionally auto-unwrapped. */
1488 res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
1489 unwrapRightArg, &rseq);
1490 if (jperIsError(res))
1491 return jpbUnknown;
1494 JsonValueListInitIterator(&lseq, &lseqit);
1495 while ((lval = JsonValueListNext(&lseq, &lseqit)))
1497 JsonValueListIterator rseqit;
1498 JsonbValue *rval;
1499 bool first = true;
1501 JsonValueListInitIterator(&rseq, &rseqit);
1502 if (rarg)
1503 rval = JsonValueListNext(&rseq, &rseqit);
1504 else
1505 rval = NULL;
1507 /* Loop over right arg sequence or do single pass otherwise */
1508 while (rarg ? (rval != NULL) : first)
1510 JsonPathBool res = exec(pred, lval, rval, param);
1512 if (res == jpbUnknown)
1514 if (jspStrictAbsenseOfErrors(cxt))
1515 return jpbUnknown;
1517 error = true;
1519 else if (res == jpbTrue)
1521 if (!jspStrictAbsenseOfErrors(cxt))
1522 return jpbTrue;
1524 found = true;
1527 first = false;
1528 if (rarg)
1529 rval = JsonValueListNext(&rseq, &rseqit);
1533 if (found) /* possible only in strict mode */
1534 return jpbTrue;
1536 if (error) /* possible only in lax mode */
1537 return jpbUnknown;
1539 return jpbFalse;
1543 * Execute binary arithmetic expression on singleton numeric operands.
1544 * Array operands are automatically unwrapped in lax mode.
1546 static JsonPathExecResult
1547 executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1548 JsonbValue *jb, BinaryArithmFunc func,
1549 JsonValueList *found)
1551 JsonPathExecResult jper;
1552 JsonPathItem elem;
1553 JsonValueList lseq = {0};
1554 JsonValueList rseq = {0};
1555 JsonbValue *lval;
1556 JsonbValue *rval;
1557 Numeric res;
1559 jspGetLeftArg(jsp, &elem);
1562 * XXX: By standard only operands of multiplicative expressions are
1563 * unwrapped. We extend it to other binary arithmetic expressions too.
1565 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
1566 if (jperIsError(jper))
1567 return jper;
1569 jspGetRightArg(jsp, &elem);
1571 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
1572 if (jperIsError(jper))
1573 return jper;
1575 if (JsonValueListLength(&lseq) != 1 ||
1576 !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
1577 RETURN_ERROR(ereport(ERROR,
1578 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1579 errmsg("left operand of jsonpath operator %s is not a single numeric value",
1580 jspOperationName(jsp->type)))));
1582 if (JsonValueListLength(&rseq) != 1 ||
1583 !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
1584 RETURN_ERROR(ereport(ERROR,
1585 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1586 errmsg("right operand of jsonpath operator %s is not a single numeric value",
1587 jspOperationName(jsp->type)))));
1589 if (jspThrowErrors(cxt))
1591 res = func(lval->val.numeric, rval->val.numeric, NULL);
1593 else
1595 bool error = false;
1597 res = func(lval->val.numeric, rval->val.numeric, &error);
1599 if (error)
1600 return jperError;
1603 if (!jspGetNext(jsp, &elem) && !found)
1604 return jperOk;
1606 lval = palloc(sizeof(*lval));
1607 lval->type = jbvNumeric;
1608 lval->val.numeric = res;
1610 return executeNextItem(cxt, jsp, &elem, lval, found, false);
1614 * Execute unary arithmetic expression for each numeric item in its operand's
1615 * sequence. Array operand is automatically unwrapped in lax mode.
1617 static JsonPathExecResult
1618 executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1619 JsonbValue *jb, PGFunction func, JsonValueList *found)
1621 JsonPathExecResult jper;
1622 JsonPathExecResult jper2;
1623 JsonPathItem elem;
1624 JsonValueList seq = {0};
1625 JsonValueListIterator it;
1626 JsonbValue *val;
1627 bool hasNext;
1629 jspGetArg(jsp, &elem);
1630 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
1632 if (jperIsError(jper))
1633 return jper;
1635 jper = jperNotFound;
1637 hasNext = jspGetNext(jsp, &elem);
1639 JsonValueListInitIterator(&seq, &it);
1640 while ((val = JsonValueListNext(&seq, &it)))
1642 if ((val = getScalar(val, jbvNumeric)))
1644 if (!found && !hasNext)
1645 return jperOk;
1647 else
1649 if (!found && !hasNext)
1650 continue; /* skip non-numerics processing */
1652 RETURN_ERROR(ereport(ERROR,
1653 (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
1654 errmsg("operand of unary jsonpath operator %s is not a numeric value",
1655 jspOperationName(jsp->type)))));
1658 if (func)
1659 val->val.numeric =
1660 DatumGetNumeric(DirectFunctionCall1(func,
1661 NumericGetDatum(val->val.numeric)));
1663 jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
1665 if (jperIsError(jper2))
1666 return jper2;
1668 if (jper2 == jperOk)
1670 if (!found)
1671 return jperOk;
1672 jper = jperOk;
1676 return jper;
1680 * STARTS_WITH predicate callback.
1682 * Check if the 'whole' string starts from 'initial' string.
1684 static JsonPathBool
1685 executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
1686 void *param)
1688 if (!(whole = getScalar(whole, jbvString)))
1689 return jpbUnknown; /* error */
1691 if (!(initial = getScalar(initial, jbvString)))
1692 return jpbUnknown; /* error */
1694 if (whole->val.string.len >= initial->val.string.len &&
1695 !memcmp(whole->val.string.val,
1696 initial->val.string.val,
1697 initial->val.string.len))
1698 return jpbTrue;
1700 return jpbFalse;
1704 * LIKE_REGEX predicate callback.
1706 * Check if the string matches regex pattern.
1708 static JsonPathBool
1709 executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
1710 void *param)
1712 JsonLikeRegexContext *cxt = param;
1714 if (!(str = getScalar(str, jbvString)))
1715 return jpbUnknown;
1717 /* Cache regex text and converted flags. */
1718 if (!cxt->regex)
1720 cxt->regex =
1721 cstring_to_text_with_len(jsp->content.like_regex.pattern,
1722 jsp->content.like_regex.patternlen);
1723 cxt->cflags = jspConvertRegexFlags(jsp->content.like_regex.flags);
1726 if (RE_compile_and_execute(cxt->regex, str->val.string.val,
1727 str->val.string.len,
1728 cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
1729 return jpbTrue;
1731 return jpbFalse;
1735 * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
1736 * user function 'func'.
1738 static JsonPathExecResult
1739 executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1740 JsonbValue *jb, bool unwrap, PGFunction func,
1741 JsonValueList *found)
1743 JsonPathItem next;
1744 Datum datum;
1746 if (unwrap && JsonbType(jb) == jbvArray)
1747 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1749 if (!(jb = getScalar(jb, jbvNumeric)))
1750 RETURN_ERROR(ereport(ERROR,
1751 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1752 errmsg("jsonpath item method .%s() can only be applied to a numeric value",
1753 jspOperationName(jsp->type)))));
1755 datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
1757 if (!jspGetNext(jsp, &next) && !found)
1758 return jperOk;
1760 jb = palloc(sizeof(*jb));
1761 jb->type = jbvNumeric;
1762 jb->val.numeric = DatumGetNumeric(datum);
1764 return executeNextItem(cxt, jsp, &next, jb, found, false);
1768 * Implementation of the .datetime() method.
1770 * Converts a string into a date/time value. The actual type is determined at run time.
1771 * If an argument is provided, this argument is used as a template string.
1772 * Otherwise, the first fitting ISO format is selected.
1774 static JsonPathExecResult
1775 executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1776 JsonbValue *jb, JsonValueList *found)
1778 JsonbValue jbvbuf;
1779 Datum value;
1780 text *datetime;
1781 Oid collid;
1782 Oid typid;
1783 int32 typmod = -1;
1784 int tz = 0;
1785 bool hasNext;
1786 JsonPathExecResult res = jperNotFound;
1787 JsonPathItem elem;
1789 if (!(jb = getScalar(jb, jbvString)))
1790 RETURN_ERROR(ereport(ERROR,
1791 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
1792 errmsg("jsonpath item method .%s() can only be applied to a string",
1793 jspOperationName(jsp->type)))));
1795 datetime = cstring_to_text_with_len(jb->val.string.val,
1796 jb->val.string.len);
1799 * At some point we might wish to have callers supply the collation to
1800 * use, but right now it's unclear that they'd be able to do better than
1801 * DEFAULT_COLLATION_OID anyway.
1803 collid = DEFAULT_COLLATION_OID;
1805 if (jsp->content.arg)
1807 text *template;
1808 char *template_str;
1809 int template_len;
1810 bool have_error = false;
1812 jspGetArg(jsp, &elem);
1814 if (elem.type != jpiString)
1815 elog(ERROR, "invalid jsonpath item type for .datetime() argument");
1817 template_str = jspGetString(&elem, &template_len);
1819 template = cstring_to_text_with_len(template_str,
1820 template_len);
1822 value = parse_datetime(datetime, template, collid, true,
1823 &typid, &typmod, &tz,
1824 jspThrowErrors(cxt) ? NULL : &have_error);
1826 if (have_error)
1827 res = jperError;
1828 else
1829 res = jperOk;
1831 else
1834 * According to SQL/JSON standard enumerate ISO formats for: date,
1835 * timetz, time, timestamptz, timestamp.
1837 * We also support ISO 8601 for timestamps, because to_json[b]()
1838 * functions use this format.
1840 static const char *fmt_str[] =
1842 "yyyy-mm-dd",
1843 "HH24:MI:SSTZH:TZM",
1844 "HH24:MI:SSTZH",
1845 "HH24:MI:SS",
1846 "yyyy-mm-dd HH24:MI:SSTZH:TZM",
1847 "yyyy-mm-dd HH24:MI:SSTZH",
1848 "yyyy-mm-dd HH24:MI:SS",
1849 "yyyy-mm-dd\"T\"HH24:MI:SSTZH:TZM",
1850 "yyyy-mm-dd\"T\"HH24:MI:SSTZH",
1851 "yyyy-mm-dd\"T\"HH24:MI:SS"
1854 /* cache for format texts */
1855 static text *fmt_txt[lengthof(fmt_str)] = {0};
1856 int i;
1858 /* loop until datetime format fits */
1859 for (i = 0; i < lengthof(fmt_str); i++)
1861 bool have_error = false;
1863 if (!fmt_txt[i])
1865 MemoryContext oldcxt =
1866 MemoryContextSwitchTo(TopMemoryContext);
1868 fmt_txt[i] = cstring_to_text(fmt_str[i]);
1869 MemoryContextSwitchTo(oldcxt);
1872 value = parse_datetime(datetime, fmt_txt[i], collid, true,
1873 &typid, &typmod, &tz,
1874 &have_error);
1876 if (!have_error)
1878 res = jperOk;
1879 break;
1883 if (res == jperNotFound)
1884 RETURN_ERROR(ereport(ERROR,
1885 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
1886 errmsg("datetime format is not recognized: \"%s\"",
1887 text_to_cstring(datetime)),
1888 errhint("Use a datetime template argument to specify the input data format."))));
1891 pfree(datetime);
1893 if (jperIsError(res))
1894 return res;
1896 hasNext = jspGetNext(jsp, &elem);
1898 if (!hasNext && !found)
1899 return res;
1901 jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
1903 jb->type = jbvDatetime;
1904 jb->val.datetime.value = value;
1905 jb->val.datetime.typid = typid;
1906 jb->val.datetime.typmod = typmod;
1907 jb->val.datetime.tz = tz;
1909 return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
1913 * Implementation of .keyvalue() method.
1915 * .keyvalue() method returns a sequence of object's key-value pairs in the
1916 * following format: '{ "key": key, "value": value, "id": id }'.
1918 * "id" field is an object identifier which is constructed from the two parts:
1919 * base object id and its binary offset in base object's jsonb:
1920 * id = 10000000000 * base_object_id + obj_offset_in_base_object
1922 * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1923 * (maximal offset in jsonb). Decimal multiplier is used here to improve the
1924 * readability of identifiers.
1926 * Base object is usually a root object of the path: context item '$' or path
1927 * variable '$var', literals can't produce objects for now. But if the path
1928 * contains generated objects (.keyvalue() itself, for example), then they
1929 * become base object for the subsequent .keyvalue().
1931 * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
1932 * of variables (see getJsonPathVariable()). Ids for generated objects
1933 * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
1935 static JsonPathExecResult
1936 executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1937 JsonbValue *jb, JsonValueList *found)
1939 JsonPathExecResult res = jperNotFound;
1940 JsonPathItem next;
1941 JsonbContainer *jbc;
1942 JsonbValue key;
1943 JsonbValue val;
1944 JsonbValue idval;
1945 JsonbValue keystr;
1946 JsonbValue valstr;
1947 JsonbValue idstr;
1948 JsonbIterator *it;
1949 JsonbIteratorToken tok;
1950 int64 id;
1951 bool hasNext;
1953 if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
1954 RETURN_ERROR(ereport(ERROR,
1955 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
1956 errmsg("jsonpath item method .%s() can only be applied to an object",
1957 jspOperationName(jsp->type)))));
1959 jbc = jb->val.binary.data;
1961 if (!JsonContainerSize(jbc))
1962 return jperNotFound; /* no key-value pairs */
1964 hasNext = jspGetNext(jsp, &next);
1966 keystr.type = jbvString;
1967 keystr.val.string.val = "key";
1968 keystr.val.string.len = 3;
1970 valstr.type = jbvString;
1971 valstr.val.string.val = "value";
1972 valstr.val.string.len = 5;
1974 idstr.type = jbvString;
1975 idstr.val.string.val = "id";
1976 idstr.val.string.len = 2;
1978 /* construct object id from its base object and offset inside that */
1979 id = jb->type != jbvBinary ? 0 :
1980 (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
1981 id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
1983 idval.type = jbvNumeric;
1984 idval.val.numeric = int64_to_numeric(id);
1986 it = JsonbIteratorInit(jbc);
1988 while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
1990 JsonBaseObjectInfo baseObject;
1991 JsonbValue obj;
1992 JsonbParseState *ps;
1993 JsonbValue *keyval;
1994 Jsonb *jsonb;
1996 if (tok != WJB_KEY)
1997 continue;
1999 res = jperOk;
2001 if (!hasNext && !found)
2002 break;
2004 tok = JsonbIteratorNext(&it, &val, true);
2005 Assert(tok == WJB_VALUE);
2007 ps = NULL;
2008 pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
2010 pushJsonbValue(&ps, WJB_KEY, &keystr);
2011 pushJsonbValue(&ps, WJB_VALUE, &key);
2013 pushJsonbValue(&ps, WJB_KEY, &valstr);
2014 pushJsonbValue(&ps, WJB_VALUE, &val);
2016 pushJsonbValue(&ps, WJB_KEY, &idstr);
2017 pushJsonbValue(&ps, WJB_VALUE, &idval);
2019 keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2021 jsonb = JsonbValueToJsonb(keyval);
2023 JsonbInitBinary(&obj, jsonb);
2025 baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
2027 res = executeNextItem(cxt, jsp, &next, &obj, found, true);
2029 cxt->baseObject = baseObject;
2031 if (jperIsError(res))
2032 return res;
2034 if (res == jperOk && !found)
2035 break;
2038 return res;
2042 * Convert boolean execution status 'res' to a boolean JSON item and execute
2043 * next jsonpath.
2045 static JsonPathExecResult
2046 appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
2047 JsonValueList *found, JsonPathBool res)
2049 JsonPathItem next;
2050 JsonbValue jbv;
2052 if (!jspGetNext(jsp, &next) && !found)
2053 return jperOk; /* found singleton boolean value */
2055 if (res == jpbUnknown)
2057 jbv.type = jbvNull;
2059 else
2061 jbv.type = jbvBool;
2062 jbv.val.boolean = res == jpbTrue;
2065 return executeNextItem(cxt, jsp, &next, &jbv, found, true);
2069 * Convert jsonpath's scalar or variable node to actual jsonb value.
2071 * If node is a variable then its id returned, otherwise 0 returned.
2073 static void
2074 getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
2075 JsonbValue *value)
2077 switch (item->type)
2079 case jpiNull:
2080 value->type = jbvNull;
2081 break;
2082 case jpiBool:
2083 value->type = jbvBool;
2084 value->val.boolean = jspGetBool(item);
2085 break;
2086 case jpiNumeric:
2087 value->type = jbvNumeric;
2088 value->val.numeric = jspGetNumeric(item);
2089 break;
2090 case jpiString:
2091 value->type = jbvString;
2092 value->val.string.val = jspGetString(item,
2093 &value->val.string.len);
2094 break;
2095 case jpiVariable:
2096 getJsonPathVariable(cxt, item, cxt->vars, value);
2097 return;
2098 default:
2099 elog(ERROR, "unexpected jsonpath item type");
2104 * Get the value of variable passed to jsonpath executor
2106 static void
2107 getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
2108 Jsonb *vars, JsonbValue *value)
2110 char *varName;
2111 int varNameLength;
2112 JsonbValue tmp;
2113 JsonbValue *v;
2115 if (!vars)
2117 value->type = jbvNull;
2118 return;
2121 Assert(variable->type == jpiVariable);
2122 varName = jspGetString(variable, &varNameLength);
2123 tmp.type = jbvString;
2124 tmp.val.string.val = varName;
2125 tmp.val.string.len = varNameLength;
2127 v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
2129 if (v)
2131 *value = *v;
2132 pfree(v);
2134 else
2136 ereport(ERROR,
2137 (errcode(ERRCODE_UNDEFINED_OBJECT),
2138 errmsg("could not find jsonpath variable \"%s\"",
2139 pnstrdup(varName, varNameLength))));
2142 JsonbInitBinary(&tmp, vars);
2143 setBaseObject(cxt, &tmp, 1);
2146 /**************** Support functions for JsonPath execution *****************/
2149 * Returns the size of an array item, or -1 if item is not an array.
2151 static int
2152 JsonbArraySize(JsonbValue *jb)
2154 Assert(jb->type != jbvArray);
2156 if (jb->type == jbvBinary)
2158 JsonbContainer *jbc = jb->val.binary.data;
2160 if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
2161 return JsonContainerSize(jbc);
2164 return -1;
2167 /* Comparison predicate callback. */
2168 static JsonPathBool
2169 executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
2171 JsonPathExecContext *cxt = (JsonPathExecContext *) p;
2173 return compareItems(cmp->type, lv, rv, cxt->useTz);
2177 * Perform per-byte comparison of two strings.
2179 static int
2180 binaryCompareStrings(const char *s1, int len1,
2181 const char *s2, int len2)
2183 int cmp;
2185 cmp = memcmp(s1, s2, Min(len1, len2));
2187 if (cmp != 0)
2188 return cmp;
2190 if (len1 == len2)
2191 return 0;
2193 return len1 < len2 ? -1 : 1;
2197 * Compare two strings in the current server encoding using Unicode codepoint
2198 * collation.
2200 static int
2201 compareStrings(const char *mbstr1, int mblen1,
2202 const char *mbstr2, int mblen2)
2204 if (GetDatabaseEncoding() == PG_SQL_ASCII ||
2205 GetDatabaseEncoding() == PG_UTF8)
2208 * It's known property of UTF-8 strings that their per-byte comparison
2209 * result matches codepoints comparison result. ASCII can be
2210 * considered as special case of UTF-8.
2212 return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2214 else
2216 char *utf8str1,
2217 *utf8str2;
2218 int cmp,
2219 utf8len1,
2220 utf8len2;
2223 * We have to convert other encodings to UTF-8 first, then compare.
2224 * Input strings may be not null-terminated and pg_server_to_any() may
2225 * return them "as is". So, use strlen() only if there is real
2226 * conversion.
2228 utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
2229 utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
2230 utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
2231 utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
2233 cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
2236 * If pg_server_to_any() did no real conversion, then we actually
2237 * compared original strings. So, we already done.
2239 if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
2240 return cmp;
2242 /* Free memory if needed */
2243 if (mbstr1 != utf8str1)
2244 pfree(utf8str1);
2245 if (mbstr2 != utf8str2)
2246 pfree(utf8str2);
2249 * When all Unicode codepoints are equal, return result of binary
2250 * comparison. In some edge cases, same characters may have different
2251 * representations in encoding. Then our behavior could diverge from
2252 * standard. However, that allow us to do simple binary comparison
2253 * for "==" operator, which is performance critical in typical cases.
2254 * In future to implement strict standard conformance, we can do
2255 * normalization of input JSON strings.
2257 if (cmp == 0)
2258 return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2259 else
2260 return cmp;
2265 * Compare two SQL/JSON items using comparison operation 'op'.
2267 static JsonPathBool
2268 compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
2270 int cmp;
2271 bool res;
2273 if (jb1->type != jb2->type)
2275 if (jb1->type == jbvNull || jb2->type == jbvNull)
2278 * Equality and order comparison of nulls to non-nulls returns
2279 * always false, but inequality comparison returns true.
2281 return op == jpiNotEqual ? jpbTrue : jpbFalse;
2283 /* Non-null items of different types are not comparable. */
2284 return jpbUnknown;
2287 switch (jb1->type)
2289 case jbvNull:
2290 cmp = 0;
2291 break;
2292 case jbvBool:
2293 cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2294 jb1->val.boolean ? 1 : -1;
2295 break;
2296 case jbvNumeric:
2297 cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2298 break;
2299 case jbvString:
2300 if (op == jpiEqual)
2301 return jb1->val.string.len != jb2->val.string.len ||
2302 memcmp(jb1->val.string.val,
2303 jb2->val.string.val,
2304 jb1->val.string.len) ? jpbFalse : jpbTrue;
2306 cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
2307 jb2->val.string.val, jb2->val.string.len);
2308 break;
2309 case jbvDatetime:
2311 bool cast_error;
2313 cmp = compareDatetime(jb1->val.datetime.value,
2314 jb1->val.datetime.typid,
2315 jb2->val.datetime.value,
2316 jb2->val.datetime.typid,
2317 useTz,
2318 &cast_error);
2320 if (cast_error)
2321 return jpbUnknown;
2323 break;
2325 case jbvBinary:
2326 case jbvArray:
2327 case jbvObject:
2328 return jpbUnknown; /* non-scalars are not comparable */
2330 default:
2331 elog(ERROR, "invalid jsonb value type %d", jb1->type);
2334 switch (op)
2336 case jpiEqual:
2337 res = (cmp == 0);
2338 break;
2339 case jpiNotEqual:
2340 res = (cmp != 0);
2341 break;
2342 case jpiLess:
2343 res = (cmp < 0);
2344 break;
2345 case jpiGreater:
2346 res = (cmp > 0);
2347 break;
2348 case jpiLessOrEqual:
2349 res = (cmp <= 0);
2350 break;
2351 case jpiGreaterOrEqual:
2352 res = (cmp >= 0);
2353 break;
2354 default:
2355 elog(ERROR, "unrecognized jsonpath operation: %d", op);
2356 return jpbUnknown;
2359 return res ? jpbTrue : jpbFalse;
2362 /* Compare two numerics */
2363 static int
2364 compareNumeric(Numeric a, Numeric b)
2366 return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2367 NumericGetDatum(a),
2368 NumericGetDatum(b)));
2371 static JsonbValue *
2372 copyJsonbValue(JsonbValue *src)
2374 JsonbValue *dst = palloc(sizeof(*dst));
2376 *dst = *src;
2378 return dst;
2382 * Execute array subscript expression and convert resulting numeric item to
2383 * the integer type with truncation.
2385 static JsonPathExecResult
2386 getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2387 int32 *index)
2389 JsonbValue *jbv;
2390 JsonValueList found = {0};
2391 JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2392 Datum numeric_index;
2393 bool have_error = false;
2395 if (jperIsError(res))
2396 return res;
2398 if (JsonValueListLength(&found) != 1 ||
2399 !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2400 RETURN_ERROR(ereport(ERROR,
2401 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2402 errmsg("jsonpath array subscript is not a single numeric value"))));
2404 numeric_index = DirectFunctionCall2(numeric_trunc,
2405 NumericGetDatum(jbv->val.numeric),
2406 Int32GetDatum(0));
2408 *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
2409 &have_error);
2411 if (have_error)
2412 RETURN_ERROR(ereport(ERROR,
2413 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2414 errmsg("jsonpath array subscript is out of integer range"))));
2416 return jperOk;
2419 /* Save base object and its id needed for the execution of .keyvalue(). */
2420 static JsonBaseObjectInfo
2421 setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2423 JsonBaseObjectInfo baseObject = cxt->baseObject;
2425 cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2426 (JsonbContainer *) jbv->val.binary.data;
2427 cxt->baseObject.id = id;
2429 return baseObject;
2432 static void
2433 JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2435 if (jvl->singleton)
2437 jvl->list = list_make2(jvl->singleton, jbv);
2438 jvl->singleton = NULL;
2440 else if (!jvl->list)
2441 jvl->singleton = jbv;
2442 else
2443 jvl->list = lappend(jvl->list, jbv);
2446 static int
2447 JsonValueListLength(const JsonValueList *jvl)
2449 return jvl->singleton ? 1 : list_length(jvl->list);
2452 static bool
2453 JsonValueListIsEmpty(JsonValueList *jvl)
2455 return !jvl->singleton && (jvl->list == NIL);
2458 static JsonbValue *
2459 JsonValueListHead(JsonValueList *jvl)
2461 return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2464 static List *
2465 JsonValueListGetList(JsonValueList *jvl)
2467 if (jvl->singleton)
2468 return list_make1(jvl->singleton);
2470 return jvl->list;
2473 static void
2474 JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2476 if (jvl->singleton)
2478 it->value = jvl->singleton;
2479 it->list = NIL;
2480 it->next = NULL;
2482 else if (jvl->list != NIL)
2484 it->value = (JsonbValue *) linitial(jvl->list);
2485 it->list = jvl->list;
2486 it->next = list_second_cell(jvl->list);
2488 else
2490 it->value = NULL;
2491 it->list = NIL;
2492 it->next = NULL;
2497 * Get the next item from the sequence advancing iterator.
2499 static JsonbValue *
2500 JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2502 JsonbValue *result = it->value;
2504 if (it->next)
2506 it->value = lfirst(it->next);
2507 it->next = lnext(it->list, it->next);
2509 else
2511 it->value = NULL;
2514 return result;
2518 * Initialize a binary JsonbValue with the given jsonb container.
2520 static JsonbValue *
2521 JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2523 jbv->type = jbvBinary;
2524 jbv->val.binary.data = &jb->root;
2525 jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2527 return jbv;
2531 * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
2533 static int
2534 JsonbType(JsonbValue *jb)
2536 int type = jb->type;
2538 if (jb->type == jbvBinary)
2540 JsonbContainer *jbc = (void *) jb->val.binary.data;
2542 /* Scalars should be always extracted during jsonpath execution. */
2543 Assert(!JsonContainerIsScalar(jbc));
2545 if (JsonContainerIsObject(jbc))
2546 type = jbvObject;
2547 else if (JsonContainerIsArray(jbc))
2548 type = jbvArray;
2549 else
2550 elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2553 return type;
2556 /* Get scalar of given type or NULL on type mismatch */
2557 static JsonbValue *
2558 getScalar(JsonbValue *scalar, enum jbvType type)
2560 /* Scalars should be always extracted during jsonpath execution. */
2561 Assert(scalar->type != jbvBinary ||
2562 !JsonContainerIsScalar(scalar->val.binary.data));
2564 return scalar->type == type ? scalar : NULL;
2567 /* Construct a JSON array from the item list */
2568 static JsonbValue *
2569 wrapItemsInArray(const JsonValueList *items)
2571 JsonbParseState *ps = NULL;
2572 JsonValueListIterator it;
2573 JsonbValue *jbv;
2575 pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2577 JsonValueListInitIterator(items, &it);
2578 while ((jbv = JsonValueListNext(items, &it)))
2579 pushJsonbValue(&ps, WJB_ELEM, jbv);
2581 return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
2584 /* Check if the timezone required for casting from type1 to type2 is used */
2585 static void
2586 checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
2588 if (!useTz)
2589 ereport(ERROR,
2590 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2591 errmsg("cannot convert value from %s to %s without time zone usage",
2592 type1, type2),
2593 errhint("Use *_tz() function for time zone support.")));
2596 /* Convert time datum to timetz datum */
2597 static Datum
2598 castTimeToTimeTz(Datum time, bool useTz)
2600 checkTimezoneIsUsedForCast(useTz, "time", "timetz");
2602 return DirectFunctionCall1(time_timetz, time);
2606 * Compare date to timestamp.
2607 * Note that this doesn't involve any timezone considerations.
2609 static int
2610 cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
2612 return date_cmp_timestamp_internal(date1, ts2);
2616 * Compare date to timestamptz.
2618 static int
2619 cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
2621 checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
2623 return date_cmp_timestamptz_internal(date1, tstz2);
2627 * Compare timestamp to timestamptz.
2629 static int
2630 cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
2632 checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
2634 return timestamp_cmp_timestamptz_internal(ts1, tstz2);
2638 * Cross-type comparison of two datetime SQL/JSON items. If items are
2639 * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
2640 * If the cast requires timezone and it is not used, then explicit error is thrown.
2642 static int
2643 compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
2644 bool useTz, bool *cast_error)
2646 PGFunction cmpfunc;
2648 *cast_error = false;
2650 switch (typid1)
2652 case DATEOID:
2653 switch (typid2)
2655 case DATEOID:
2656 cmpfunc = date_cmp;
2658 break;
2660 case TIMESTAMPOID:
2661 return cmpDateToTimestamp(DatumGetDateADT(val1),
2662 DatumGetTimestamp(val2),
2663 useTz);
2665 case TIMESTAMPTZOID:
2666 return cmpDateToTimestampTz(DatumGetDateADT(val1),
2667 DatumGetTimestampTz(val2),
2668 useTz);
2670 case TIMEOID:
2671 case TIMETZOID:
2672 *cast_error = true; /* uncomparable types */
2673 return 0;
2675 default:
2676 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2677 typid2);
2679 break;
2681 case TIMEOID:
2682 switch (typid2)
2684 case TIMEOID:
2685 cmpfunc = time_cmp;
2687 break;
2689 case TIMETZOID:
2690 val1 = castTimeToTimeTz(val1, useTz);
2691 cmpfunc = timetz_cmp;
2693 break;
2695 case DATEOID:
2696 case TIMESTAMPOID:
2697 case TIMESTAMPTZOID:
2698 *cast_error = true; /* uncomparable types */
2699 return 0;
2701 default:
2702 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2703 typid2);
2705 break;
2707 case TIMETZOID:
2708 switch (typid2)
2710 case TIMEOID:
2711 val2 = castTimeToTimeTz(val2, useTz);
2712 cmpfunc = timetz_cmp;
2714 break;
2716 case TIMETZOID:
2717 cmpfunc = timetz_cmp;
2719 break;
2721 case DATEOID:
2722 case TIMESTAMPOID:
2723 case TIMESTAMPTZOID:
2724 *cast_error = true; /* uncomparable types */
2725 return 0;
2727 default:
2728 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2729 typid2);
2731 break;
2733 case TIMESTAMPOID:
2734 switch (typid2)
2736 case DATEOID:
2737 return -cmpDateToTimestamp(DatumGetDateADT(val2),
2738 DatumGetTimestamp(val1),
2739 useTz);
2741 case TIMESTAMPOID:
2742 cmpfunc = timestamp_cmp;
2744 break;
2746 case TIMESTAMPTZOID:
2747 return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
2748 DatumGetTimestampTz(val2),
2749 useTz);
2751 case TIMEOID:
2752 case TIMETZOID:
2753 *cast_error = true; /* uncomparable types */
2754 return 0;
2756 default:
2757 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2758 typid2);
2760 break;
2762 case TIMESTAMPTZOID:
2763 switch (typid2)
2765 case DATEOID:
2766 return -cmpDateToTimestampTz(DatumGetDateADT(val2),
2767 DatumGetTimestampTz(val1),
2768 useTz);
2770 case TIMESTAMPOID:
2771 return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
2772 DatumGetTimestampTz(val1),
2773 useTz);
2775 case TIMESTAMPTZOID:
2776 cmpfunc = timestamp_cmp;
2778 break;
2780 case TIMEOID:
2781 case TIMETZOID:
2782 *cast_error = true; /* uncomparable types */
2783 return 0;
2785 default:
2786 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2787 typid2);
2789 break;
2791 default:
2792 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
2795 if (*cast_error)
2796 return 0; /* cast error */
2798 return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));