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 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
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/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
90 * Context of jsonpath execution.
92 typedef struct JsonPathExecContext
94 Jsonb
*vars
; /* variables to substitute into jsonpath */
95 JsonbValue
*root
; /* for $ evaluation */
96 JsonbValue
*current
; /* for @ evaluation */
97 JsonBaseObjectInfo baseObject
; /* "base object" for .keyvalue()
99 int lastGeneratedObjectId
; /* "id" counter for .keyvalue()
101 int innermostArraySize
; /* for LAST array index evaluation */
102 bool laxMode
; /* true for "lax" mode, false for "strict"
104 bool ignoreStructuralErrors
; /* with "true" structural errors such
105 * as absence of required json item or
106 * unexpected json item type are
108 bool throwErrors
; /* with "false" all suppressible errors are
111 } JsonPathExecContext
;
113 /* Context for LIKE_REGEX execution. */
114 typedef struct JsonLikeRegexContext
118 } JsonLikeRegexContext
;
120 /* Result of jsonpath predicate evaluation */
121 typedef enum JsonPathBool
128 /* Result of jsonpath expression evaluation */
129 typedef enum JsonPathExecResult
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
;
147 typedef struct JsonValueListIterator
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) \
164 if (jspThrowErrors(cxt)) \
170 typedef JsonPathBool (*JsonPathPredicateCallback
) (JsonPathItem
*jsp
,
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
,
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
,
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 ********************/
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.
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
;
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
))
294 PG_RETURN_BOOL(res
== jperOk
);
298 jsonb_path_exists(PG_FUNCTION_ARGS
)
300 return jsonb_path_exists_internal(fcinfo
, false);
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()).
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);
323 * Returns jsonpath predicate result item for the specified jsonb value.
324 * See jsonb_path_exists() comment for details regarding error handling.
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};
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
)
359 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED
),
360 errmsg("single boolean result is expected")));
366 jsonb_path_match(PG_FUNCTION_ARGS
)
368 return jsonb_path_match_internal(fcinfo
, false);
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()).
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);
391 * Executes jsonpath for given jsonb document and returns result as
395 jsonb_path_query_internal(FunctionCallInfo fcinfo
, bool tz
)
397 FuncCallContext
*funcctx
;
402 if (SRF_IS_FIRSTCALL())
406 MemoryContext oldcontext
;
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
);
432 SRF_RETURN_DONE(funcctx
);
435 funcctx
->user_fctx
= list_delete_first(found
);
437 SRF_RETURN_NEXT(funcctx
, JsonbPGetDatum(JsonbValueToJsonb(v
)));
441 jsonb_path_query(PG_FUNCTION_ARGS
)
443 return jsonb_path_query_internal(fcinfo
, false);
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
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
)));
472 jsonb_path_query_array(PG_FUNCTION_ARGS
)
474 return jsonb_path_query_array_internal(fcinfo
, false);
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.
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
)));
506 jsonb_path_query_first(PG_FUNCTION_ARGS
)
508 return jsonb_path_query_first_internal(fcinfo
, false);
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
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
;
549 if (!JsonbExtractScalar(&json
->root
, &jbv
))
550 JsonbInitBinary(&jbv
, json
);
552 if (vars
&& !JsonContainerIsObject(&vars
->root
))
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.")));
561 cxt
.laxMode
= (path
->header
& JSONPATH_LAX
) != 0;
562 cxt
.ignoreStructuralErrors
= cxt
.laxMode
;
565 cxt
.baseObject
.jbc
= NULL
;
566 cxt
.baseObject
.id
= 0;
567 cxt
.lastGeneratedObjectId
= vars
? 2 : 1;
568 cxt
.innermostArraySize
= -1;
569 cxt
.throwErrors
= throwErrors
;
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
))
585 return JsonValueListIsEmpty(&vals
) ? jperNotFound
: jperOk
;
588 res
= executeItem(&cxt
, &jsp
, &jbv
, result
);
590 Assert(!throwErrors
|| !jperIsError(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
)
615 JsonPathExecResult res
= jperNotFound
;
616 JsonBaseObjectInfo baseObject
;
619 CHECK_FOR_INTERRUPTS();
623 /* all boolean item types: */
633 case jpiGreaterOrEqual
:
638 JsonPathBool st
= executeBoolItem(cxt
, jsp
, jb
, true);
640 res
= appendBoolResult(cxt
, jsp
, found
, st
);
645 if (JsonbType(jb
) == jbvObject
)
650 key
.type
= jbvString
;
651 key
.val
.string
.val
= jspGetString(jsp
, &key
.val
.string
.len
);
653 v
= findJsonbValueFromContainer(jb
->val
.binary
.data
,
658 res
= executeNextItem(cxt
, jsp
, NULL
,
661 /* free value if it was not added to found list */
662 if (jspHasNext(jsp
) || !found
)
665 else if (!jspIgnoreStructuralErrors(cxt
))
669 if (!jspThrowErrors(cxt
))
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
))
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"))));
692 baseObject
= setBaseObject(cxt
, jb
, 0);
693 res
= executeNextItem(cxt
, jsp
, NULL
, jb
, found
, true);
694 cxt
->baseObject
= baseObject
;
698 res
= executeNextItem(cxt
, jsp
, NULL
, cxt
->current
,
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"))));
719 if (JsonbType(jb
) == jbvArray
|| jspAutoWrap(cxt
))
721 int innermostArraySize
= cxt
->innermostArraySize
;
723 int size
= JsonbArraySize(jb
);
724 bool singleton
= size
< 0;
725 bool hasNext
= jspGetNext(jsp
, &elem
);
730 cxt
->innermostArraySize
= size
; /* for LAST evaluation */
732 for (i
= 0; i
< jsp
->content
.array
.nelems
; i
++)
739 bool range
= jspGetArraySubscript(jsp
, &from
,
742 res
= getArrayIndex(cxt
, &from
, jb
, &index_from
);
744 if (jperIsError(res
))
749 res
= getArrayIndex(cxt
, &to
, jb
, &index_to
);
751 if (jperIsError(res
))
755 index_to
= index_from
;
757 if (!jspIgnoreStructuralErrors(cxt
) &&
759 index_from
> index_to
||
761 RETURN_ERROR(ereport(ERROR
,
762 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT
),
763 errmsg("jsonpath array subscript is out of bounds"))));
768 if (index_to
>= size
)
773 for (index
= index_from
; index
<= index_to
; index
++)
785 v
= getIthJsonbValueFromContainer(jb
->val
.binary
.data
,
794 if (!hasNext
&& !found
)
797 res
= executeNextItem(cxt
, jsp
, &elem
, v
, found
,
800 if (jperIsError(res
))
803 if (res
== jperOk
&& !found
)
807 if (jperIsError(res
))
810 if (res
== jperOk
&& !found
)
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"))));
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
)
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
);
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
))
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"))));
877 return executeBinaryArithmExpr(cxt
, jsp
, jb
,
878 numeric_add_opt_error
, found
);
881 return executeBinaryArithmExpr(cxt
, jsp
, jb
,
882 numeric_sub_opt_error
, found
);
885 return executeBinaryArithmExpr(cxt
, jsp
, jb
,
886 numeric_mul_opt_error
, found
);
889 return executeBinaryArithmExpr(cxt
, jsp
, jb
,
890 numeric_div_opt_error
, found
);
893 return executeBinaryArithmExpr(cxt
, jsp
, jb
,
894 numeric_mod_opt_error
, found
);
897 return executeUnaryArithmExpr(cxt
, jsp
, jb
, NULL
, found
);
900 return executeUnaryArithmExpr(cxt
, jsp
, jb
, numeric_uminus
,
907 if (unwrap
&& JsonbType(jb
) == jbvArray
)
908 return executeItemUnwrapTargetArray(cxt
, jsp
, jb
, found
,
911 jspGetArg(jsp
, &elem
);
912 st
= executeNestedBoolItem(cxt
, &elem
, jb
);
916 res
= executeNextItem(cxt
, jsp
, NULL
,
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
,
934 cxt
->ignoreStructuralErrors
= savedIgnoreStructuralErrors
;
936 if (res
== jperOk
&& !found
)
940 if (jb
->type
== jbvBinary
)
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
));
959 bool hasNext
= jspGetNext(jsp
, &elem
);
961 if (!hasNext
&& !found
)
963 res
= jperOk
; /* skip evaluation */
967 v
= hasNext
? &vbuf
: palloc(sizeof(*v
));
969 baseObject
= cxt
->baseObject
;
970 getJsonPathItem(cxt
, jsp
, v
);
972 res
= executeNextItem(cxt
, jsp
, &elem
,
974 cxt
->baseObject
= baseObject
;
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
,
993 int size
= JsonbArraySize(jb
);
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
)))));
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);
1020 return executeNumericItemMethod(cxt
, jsp
, jb
, unwrap
, numeric_abs
,
1024 return executeNumericItemMethod(cxt
, jsp
, jb
, unwrap
, numeric_floor
,
1028 return executeNumericItemMethod(cxt
, jsp
, jb
, unwrap
, numeric_ceil
,
1035 if (unwrap
&& JsonbType(jb
) == jbvArray
)
1036 return executeItemUnwrapTargetArray(cxt
, jsp
, jb
, found
,
1039 if (jb
->type
== jbvNumeric
)
1041 char *tmp
= DatumGetCString(DirectFunctionCall1(numeric_out
,
1042 NumericGetDatum(jb
->val
.numeric
)));
1044 bool have_error
= false;
1046 val
= float8in_internal_opt_error(tmp
,
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
)))));
1059 else if (jb
->type
== jbvString
)
1061 /* cast string as double */
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
,
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
)))));
1080 jb
->type
= jbvNumeric
;
1081 jb
->val
.numeric
= DatumGetNumeric(DirectFunctionCall1(float8_numeric
,
1082 Float8GetDatum(val
)));
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);
1097 if (unwrap
&& JsonbType(jb
) == jbvArray
)
1098 return executeItemUnwrapTargetArray(cxt
, jsp
, jb
, found
, false);
1100 return executeDateTimeMethod(cxt
, jsp
, jb
, found
);
1103 if (unwrap
&& JsonbType(jb
) == jbvArray
)
1104 return executeItemUnwrapTargetArray(cxt
, jsp
, jb
, found
, false);
1106 return executeKeyValueMethod(cxt
, jsp
, jb
, found
);
1109 elog(ERROR
, "unrecognized jsonpath item type: %d", jsp
->type
);
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"
1138 static JsonPathExecResult
1139 executeNextItem(JsonPathExecContext
*cxt
,
1140 JsonPathItem
*cur
, JsonPathItem
*next
,
1141 JsonbValue
*v
, JsonValueList
*found
, bool copy
)
1147 hasNext
= next
!= NULL
;
1149 hasNext
= jspHasNext(cur
);
1153 hasNext
= jspGetNext(cur
, next
);
1157 return executeItem(cxt
, next
, v
, found
);
1160 JsonValueListAppend(found
, copy
? copyJsonbValue(v
) : v
);
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
);
1181 if (jperIsError(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);
1192 JsonValueListAppend(found
, item
);
1198 return executeItem(cxt
, jsp
, jb
, found
);
1202 * Same as executeItemOptUnwrapResult(), but with error suppression.
1204 static JsonPathExecResult
1205 executeItemOptUnwrapResultNoThrow(JsonPathExecContext
*cxt
,
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
;
1220 /* Execute boolean-valued jsonpath expression. */
1222 executeBoolItem(JsonPathExecContext
*cxt
, JsonPathItem
*jsp
,
1223 JsonbValue
*jb
, bool canHaveNext
)
1230 if (!canHaveNext
&& jspHasNext(jsp
))
1231 elog(ERROR
, "boolean jsonpath item cannot have next item");
1236 jspGetLeftArg(jsp
, &larg
);
1237 res
= executeBoolItem(cxt
, &larg
, jb
, false);
1239 if (res
== jpbFalse
)
1243 * SQL/JSON says that we should check second arg in case of
1247 jspGetRightArg(jsp
, &rarg
);
1248 res2
= executeBoolItem(cxt
, &rarg
, jb
, false);
1250 return res2
== jpbTrue
? res
: res2
;
1253 jspGetLeftArg(jsp
, &larg
);
1254 res
= executeBoolItem(cxt
, &larg
, jb
, false);
1259 jspGetRightArg(jsp
, &rarg
);
1260 res2
= executeBoolItem(cxt
, &rarg
, jb
, false);
1262 return res2
== jpbFalse
? res
: res2
;
1265 jspGetArg(jsp
, &larg
);
1267 res
= executeBoolItem(cxt
, &larg
, jb
, false);
1269 if (res
== jpbUnknown
)
1272 return res
== jpbTrue
? jpbFalse
: jpbTrue
;
1275 jspGetArg(jsp
, &larg
);
1276 res
= executeBoolItem(cxt
, &larg
, jb
, false);
1277 return res
== jpbUnknown
? jpbTrue
: jpbFalse
;
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
);
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
,
1327 if (jperIsError(res
))
1330 return JsonValueListIsEmpty(&vals
) ? jpbFalse
: jpbTrue
;
1334 JsonPathExecResult res
=
1335 executeItemOptUnwrapResultNoThrow(cxt
, &larg
, jb
,
1338 if (jperIsError(res
))
1341 return res
== jperOk
? jpbTrue
: jpbFalse
;
1345 elog(ERROR
, "invalid boolean jsonpath item type: %d", jsp
->type
);
1351 * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1352 * item onto the stack.
1355 executeNestedBoolItem(JsonPathExecContext
*cxt
, JsonPathItem
*jsp
,
1361 prev
= cxt
->current
;
1363 res
= executeBoolItem(cxt
, jsp
, jb
, false);
1364 cxt
->current
= prev
;
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
;
1385 check_stack_depth();
1390 it
= JsonbIteratorInit(jbc
);
1393 * Recursively iterate over jsonb objects/arrays
1395 while ((r
= JsonbIteratorNext(&it
, &v
, true)) != WJB_DONE
)
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 */
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
;
1423 res
= executeItemOptUnwrapTarget(cxt
, jsp
, &v
, found
, unwrapNext
);
1425 if (jperIsError(res
))
1428 if (res
== jperOk
&& !found
)
1432 JsonValueListAppend(found
, copyJsonbValue(&v
));
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
))
1447 if (res
== jperOk
&& found
== NULL
)
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.
1467 executePredicate(JsonPathExecContext
*cxt
, JsonPathItem
*pred
,
1468 JsonPathItem
*larg
, JsonPathItem
*rarg
, JsonbValue
*jb
,
1469 bool unwrapRightArg
, JsonPathPredicateCallback exec
,
1472 JsonPathExecResult res
;
1473 JsonValueListIterator lseqit
;
1474 JsonValueList lseq
= {0};
1475 JsonValueList rseq
= {0};
1480 /* Left argument is always auto-unwrapped. */
1481 res
= executeItemOptUnwrapResultNoThrow(cxt
, larg
, jb
, true, &lseq
);
1482 if (jperIsError(res
))
1487 /* Right argument is conditionally auto-unwrapped. */
1488 res
= executeItemOptUnwrapResultNoThrow(cxt
, rarg
, jb
,
1489 unwrapRightArg
, &rseq
);
1490 if (jperIsError(res
))
1494 JsonValueListInitIterator(&lseq
, &lseqit
);
1495 while ((lval
= JsonValueListNext(&lseq
, &lseqit
)))
1497 JsonValueListIterator rseqit
;
1501 JsonValueListInitIterator(&rseq
, &rseqit
);
1503 rval
= JsonValueListNext(&rseq
, &rseqit
);
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
))
1519 else if (res
== jpbTrue
)
1521 if (!jspStrictAbsenseOfErrors(cxt
))
1529 rval
= JsonValueListNext(&rseq
, &rseqit
);
1533 if (found
) /* possible only in strict mode */
1536 if (error
) /* possible only in lax mode */
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
;
1553 JsonValueList lseq
= {0};
1554 JsonValueList rseq
= {0};
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
))
1569 jspGetRightArg(jsp
, &elem
);
1571 jper
= executeItemOptUnwrapResult(cxt
, &elem
, jb
, true, &rseq
);
1572 if (jperIsError(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
);
1597 res
= func(lval
->val
.numeric
, rval
->val
.numeric
, &error
);
1603 if (!jspGetNext(jsp
, &elem
) && !found
)
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
;
1624 JsonValueList seq
= {0};
1625 JsonValueListIterator it
;
1629 jspGetArg(jsp
, &elem
);
1630 jper
= executeItemOptUnwrapResult(cxt
, &elem
, jb
, true, &seq
);
1632 if (jperIsError(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
)
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
)))));
1660 DatumGetNumeric(DirectFunctionCall1(func
,
1661 NumericGetDatum(val
->val
.numeric
)));
1663 jper2
= executeNextItem(cxt
, jsp
, &elem
, val
, found
, false);
1665 if (jperIsError(jper2
))
1668 if (jper2
== jperOk
)
1680 * STARTS_WITH predicate callback.
1682 * Check if the 'whole' string starts from 'initial' string.
1685 executeStartsWith(JsonPathItem
*jsp
, JsonbValue
*whole
, JsonbValue
*initial
,
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
))
1704 * LIKE_REGEX predicate callback.
1706 * Check if the string matches regex pattern.
1709 executeLikeRegex(JsonPathItem
*jsp
, JsonbValue
*str
, JsonbValue
*rarg
,
1712 JsonLikeRegexContext
*cxt
= param
;
1714 if (!(str
= getScalar(str
, jbvString
)))
1717 /* Cache regex text and converted flags. */
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
))
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
)
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
)
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
)
1786 JsonPathExecResult res
= jperNotFound
;
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
)
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
,
1822 value
= parse_datetime(datetime
, template, collid
, true,
1823 &typid
, &typmod
, &tz
,
1824 jspThrowErrors(cxt
) ? NULL
: &have_error
);
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
[] =
1843 "HH24:MI:SSTZH:TZM",
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};
1858 /* loop until datetime format fits */
1859 for (i
= 0; i
< lengthof(fmt_str
); i
++)
1861 bool have_error
= false;
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
,
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."))));
1893 if (jperIsError(res
))
1896 hasNext
= jspGetNext(jsp
, &elem
);
1898 if (!hasNext
&& !found
)
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
;
1941 JsonbContainer
*jbc
;
1949 JsonbIteratorToken tok
;
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
;
1992 JsonbParseState
*ps
;
2001 if (!hasNext
&& !found
)
2004 tok
= JsonbIteratorNext(&it
, &val
, true);
2005 Assert(tok
== WJB_VALUE
);
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
))
2034 if (res
== jperOk
&& !found
)
2042 * Convert boolean execution status 'res' to a boolean JSON item and execute
2045 static JsonPathExecResult
2046 appendBoolResult(JsonPathExecContext
*cxt
, JsonPathItem
*jsp
,
2047 JsonValueList
*found
, JsonPathBool res
)
2052 if (!jspGetNext(jsp
, &next
) && !found
)
2053 return jperOk
; /* found singleton boolean value */
2055 if (res
== jpbUnknown
)
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.
2074 getJsonPathItem(JsonPathExecContext
*cxt
, JsonPathItem
*item
,
2080 value
->type
= jbvNull
;
2083 value
->type
= jbvBool
;
2084 value
->val
.boolean
= jspGetBool(item
);
2087 value
->type
= jbvNumeric
;
2088 value
->val
.numeric
= jspGetNumeric(item
);
2091 value
->type
= jbvString
;
2092 value
->val
.string
.val
= jspGetString(item
,
2093 &value
->val
.string
.len
);
2096 getJsonPathVariable(cxt
, item
, cxt
->vars
, value
);
2099 elog(ERROR
, "unexpected jsonpath item type");
2104 * Get the value of variable passed to jsonpath executor
2107 getJsonPathVariable(JsonPathExecContext
*cxt
, JsonPathItem
*variable
,
2108 Jsonb
*vars
, JsonbValue
*value
)
2117 value
->type
= jbvNull
;
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
);
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.
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
);
2167 /* Comparison predicate callback. */
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.
2180 binaryCompareStrings(const char *s1
, int len1
,
2181 const char *s2
, int len2
)
2185 cmp
= memcmp(s1
, s2
, Min(len1
, len2
));
2193 return len1
< len2
? -1 : 1;
2197 * Compare two strings in the current server encoding using Unicode codepoint
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
);
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
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
)
2242 /* Free memory if needed */
2243 if (mbstr1
!= utf8str1
)
2245 if (mbstr2
!= 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.
2258 return binaryCompareStrings(mbstr1
, mblen1
, mbstr2
, mblen2
);
2265 * Compare two SQL/JSON items using comparison operation 'op'.
2268 compareItems(int32 op
, JsonbValue
*jb1
, JsonbValue
*jb2
, bool useTz
)
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. */
2293 cmp
= jb1
->val
.boolean
== jb2
->val
.boolean
? 0 :
2294 jb1
->val
.boolean
? 1 : -1;
2297 cmp
= compareNumeric(jb1
->val
.numeric
, jb2
->val
.numeric
);
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
);
2313 cmp
= compareDatetime(jb1
->val
.datetime
.value
,
2314 jb1
->val
.datetime
.typid
,
2315 jb2
->val
.datetime
.value
,
2316 jb2
->val
.datetime
.typid
,
2328 return jpbUnknown
; /* non-scalars are not comparable */
2331 elog(ERROR
, "invalid jsonb value type %d", jb1
->type
);
2348 case jpiLessOrEqual
:
2351 case jpiGreaterOrEqual
:
2355 elog(ERROR
, "unrecognized jsonpath operation: %d", op
);
2359 return res
? jpbTrue
: jpbFalse
;
2362 /* Compare two numerics */
2364 compareNumeric(Numeric a
, Numeric b
)
2366 return DatumGetInt32(DirectFunctionCall2(numeric_cmp
,
2368 NumericGetDatum(b
)));
2372 copyJsonbValue(JsonbValue
*src
)
2374 JsonbValue
*dst
= palloc(sizeof(*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
,
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
))
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
),
2408 *index
= numeric_int4_opt_error(DatumGetNumeric(numeric_index
),
2412 RETURN_ERROR(ereport(ERROR
,
2413 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT
),
2414 errmsg("jsonpath array subscript is out of integer range"))));
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
;
2433 JsonValueListAppend(JsonValueList
*jvl
, JsonbValue
*jbv
)
2437 jvl
->list
= list_make2(jvl
->singleton
, jbv
);
2438 jvl
->singleton
= NULL
;
2440 else if (!jvl
->list
)
2441 jvl
->singleton
= jbv
;
2443 jvl
->list
= lappend(jvl
->list
, jbv
);
2447 JsonValueListLength(const JsonValueList
*jvl
)
2449 return jvl
->singleton
? 1 : list_length(jvl
->list
);
2453 JsonValueListIsEmpty(JsonValueList
*jvl
)
2455 return !jvl
->singleton
&& (jvl
->list
== NIL
);
2459 JsonValueListHead(JsonValueList
*jvl
)
2461 return jvl
->singleton
? jvl
->singleton
: linitial(jvl
->list
);
2465 JsonValueListGetList(JsonValueList
*jvl
)
2468 return list_make1(jvl
->singleton
);
2474 JsonValueListInitIterator(const JsonValueList
*jvl
, JsonValueListIterator
*it
)
2478 it
->value
= jvl
->singleton
;
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
);
2497 * Get the next item from the sequence advancing iterator.
2500 JsonValueListNext(const JsonValueList
*jvl
, JsonValueListIterator
*it
)
2502 JsonbValue
*result
= it
->value
;
2506 it
->value
= lfirst(it
->next
);
2507 it
->next
= lnext(it
->list
, it
->next
);
2518 * Initialize a binary JsonbValue with the given jsonb container.
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
);
2531 * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
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
))
2547 else if (JsonContainerIsArray(jbc
))
2550 elog(ERROR
, "invalid jsonb container type: 0x%08x", jbc
->header
);
2556 /* Get scalar of given type or NULL on type mismatch */
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 */
2569 wrapItemsInArray(const JsonValueList
*items
)
2571 JsonbParseState
*ps
= NULL
;
2572 JsonValueListIterator it
;
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 */
2586 checkTimezoneIsUsedForCast(bool useTz
, const char *type1
, const char *type2
)
2590 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED
),
2591 errmsg("cannot convert value from %s to %s without time zone usage",
2593 errhint("Use *_tz() function for time zone support.")));
2596 /* Convert time datum to timetz 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.
2610 cmpDateToTimestamp(DateADT date1
, Timestamp ts2
, bool useTz
)
2612 return date_cmp_timestamp_internal(date1
, ts2
);
2616 * Compare date to timestamptz.
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.
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.
2643 compareDatetime(Datum val1
, Oid typid1
, Datum val2
, Oid typid2
,
2644 bool useTz
, bool *cast_error
)
2648 *cast_error
= false;
2661 return cmpDateToTimestamp(DatumGetDateADT(val1
),
2662 DatumGetTimestamp(val2
),
2665 case TIMESTAMPTZOID
:
2666 return cmpDateToTimestampTz(DatumGetDateADT(val1
),
2667 DatumGetTimestampTz(val2
),
2672 *cast_error
= true; /* uncomparable types */
2676 elog(ERROR
, "unrecognized SQL/JSON datetime type oid: %u",
2690 val1
= castTimeToTimeTz(val1
, useTz
);
2691 cmpfunc
= timetz_cmp
;
2697 case TIMESTAMPTZOID
:
2698 *cast_error
= true; /* uncomparable types */
2702 elog(ERROR
, "unrecognized SQL/JSON datetime type oid: %u",
2711 val2
= castTimeToTimeTz(val2
, useTz
);
2712 cmpfunc
= timetz_cmp
;
2717 cmpfunc
= timetz_cmp
;
2723 case TIMESTAMPTZOID
:
2724 *cast_error
= true; /* uncomparable types */
2728 elog(ERROR
, "unrecognized SQL/JSON datetime type oid: %u",
2737 return -cmpDateToTimestamp(DatumGetDateADT(val2
),
2738 DatumGetTimestamp(val1
),
2742 cmpfunc
= timestamp_cmp
;
2746 case TIMESTAMPTZOID
:
2747 return cmpTimestampToTimestampTz(DatumGetTimestamp(val1
),
2748 DatumGetTimestampTz(val2
),
2753 *cast_error
= true; /* uncomparable types */
2757 elog(ERROR
, "unrecognized SQL/JSON datetime type oid: %u",
2762 case TIMESTAMPTZOID
:
2766 return -cmpDateToTimestampTz(DatumGetDateADT(val2
),
2767 DatumGetTimestampTz(val1
),
2771 return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2
),
2772 DatumGetTimestampTz(val1
),
2775 case TIMESTAMPTZOID
:
2776 cmpfunc
= timestamp_cmp
;
2782 *cast_error
= true; /* uncomparable types */
2786 elog(ERROR
, "unrecognized SQL/JSON datetime type oid: %u",
2792 elog(ERROR
, "unrecognized SQL/JSON datetime type oid: %u", typid1
);
2796 return 0; /* cast error */
2798 return DatumGetInt32(DirectFunctionCall2(cmpfunc
, val1
, val2
));