TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * jsonpath_exec.c
4 : * Routines for SQL/JSON path execution.
5 : *
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.
11 : *
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
22 : *
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()).
31 : *
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.
39 : *
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.
46 : *
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.
51 : *
52 : * Copyright (c) 2019-2023, PostgreSQL Global Development Group
53 : *
54 : * IDENTIFICATION
55 : * src/backend/utils/adt/jsonpath_exec.c
56 : *
57 : *-------------------------------------------------------------------------
58 : */
59 :
60 : #include "postgres.h"
61 :
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 "nodes/miscnodes.h"
68 : #include "regex/regex.h"
69 : #include "utils/builtins.h"
70 : #include "utils/date.h"
71 : #include "utils/datetime.h"
72 : #include "utils/datum.h"
73 : #include "utils/float.h"
74 : #include "utils/formatting.h"
75 : #include "utils/guc.h"
76 : #include "utils/json.h"
77 : #include "utils/jsonpath.h"
78 : #include "utils/timestamp.h"
79 : #include "utils/varlena.h"
80 :
81 : /*
82 : * Represents "base object" and it's "id" for .keyvalue() evaluation.
83 : */
84 : typedef struct JsonBaseObjectInfo
85 : {
86 : JsonbContainer *jbc;
87 : int id;
88 : } JsonBaseObjectInfo;
89 :
90 : /*
91 : * Context of jsonpath execution.
92 : */
93 : typedef struct JsonPathExecContext
94 : {
95 : Jsonb *vars; /* variables to substitute into jsonpath */
96 : JsonbValue *root; /* for $ evaluation */
97 : JsonbValue *current; /* for @ evaluation */
98 : JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue()
99 : * evaluation */
100 : int lastGeneratedObjectId; /* "id" counter for .keyvalue()
101 : * evaluation */
102 : int innermostArraySize; /* for LAST array index evaluation */
103 : bool laxMode; /* true for "lax" mode, false for "strict"
104 : * mode */
105 : bool ignoreStructuralErrors; /* with "true" structural errors such
106 : * as absence of required json item or
107 : * unexpected json item type are
108 : * ignored */
109 : bool throwErrors; /* with "false" all suppressible errors are
110 : * suppressed */
111 : bool useTz;
112 : } JsonPathExecContext;
113 :
114 : /* Context for LIKE_REGEX execution. */
115 : typedef struct JsonLikeRegexContext
116 : {
117 : text *regex;
118 : int cflags;
119 : } JsonLikeRegexContext;
120 :
121 : /* Result of jsonpath predicate evaluation */
122 : typedef enum JsonPathBool
123 : {
124 : jpbFalse = 0,
125 : jpbTrue = 1,
126 : jpbUnknown = 2
127 : } JsonPathBool;
128 :
129 : /* Result of jsonpath expression evaluation */
130 : typedef enum JsonPathExecResult
131 : {
132 : jperOk = 0,
133 : jperNotFound = 1,
134 : jperError = 2
135 : } JsonPathExecResult;
136 :
137 : #define jperIsError(jper) ((jper) == jperError)
138 :
139 : /*
140 : * List of jsonb values with shortcut for single-value list.
141 : */
142 : typedef struct JsonValueList
143 : {
144 : JsonbValue *singleton;
145 : List *list;
146 : } JsonValueList;
147 :
148 : typedef struct JsonValueListIterator
149 : {
150 : JsonbValue *value;
151 : List *list;
152 : ListCell *next;
153 : } JsonValueListIterator;
154 :
155 : /* strict/lax flags is decomposed into four [un]wrap/error flags */
156 : #define jspStrictAbsenseOfErrors(cxt) (!(cxt)->laxMode)
157 : #define jspAutoUnwrap(cxt) ((cxt)->laxMode)
158 : #define jspAutoWrap(cxt) ((cxt)->laxMode)
159 : #define jspIgnoreStructuralErrors(cxt) ((cxt)->ignoreStructuralErrors)
160 : #define jspThrowErrors(cxt) ((cxt)->throwErrors)
161 :
162 : /* Convenience macro: return or throw error depending on context */
163 : #define RETURN_ERROR(throw_error) \
164 : do { \
165 : if (jspThrowErrors(cxt)) \
166 : throw_error; \
167 : else \
168 : return jperError; \
169 : } while (0)
170 :
171 : typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
172 : JsonbValue *larg,
173 : JsonbValue *rarg,
174 : void *param);
175 : typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
176 :
177 : static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
178 : Jsonb *json, bool throwErrors,
179 : JsonValueList *result, bool useTz);
180 : static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
181 : JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
182 : static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
183 : JsonPathItem *jsp, JsonbValue *jb,
184 : JsonValueList *found, bool unwrap);
185 : static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
186 : JsonPathItem *jsp, JsonbValue *jb,
187 : JsonValueList *found, bool unwrapElements);
188 : static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
189 : JsonPathItem *cur, JsonPathItem *next,
190 : JsonbValue *v, JsonValueList *found, bool copy);
191 : static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
192 : bool unwrap, JsonValueList *found);
193 : static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp,
194 : JsonbValue *jb, bool unwrap, JsonValueList *found);
195 : static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
196 : JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
197 : static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
198 : JsonPathItem *jsp, JsonbValue *jb);
199 : static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
200 : JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
201 : uint32 level, uint32 first, uint32 last,
202 : bool ignoreStructuralErrors, bool unwrapNext);
203 : static JsonPathBool executePredicate(JsonPathExecContext *cxt,
204 : JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
205 : JsonbValue *jb, bool unwrapRightArg,
206 : JsonPathPredicateCallback exec, void *param);
207 : static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
208 : JsonPathItem *jsp, JsonbValue *jb,
209 : BinaryArithmFunc func, JsonValueList *found);
210 : static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
211 : JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
212 : JsonValueList *found);
213 : static JsonPathBool executeStartsWith(JsonPathItem *jsp,
214 : JsonbValue *whole, JsonbValue *initial, void *param);
215 : static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
216 : JsonbValue *rarg, void *param);
217 : static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
218 : JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
219 : JsonValueList *found);
220 : static JsonPathExecResult executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
221 : JsonbValue *jb, JsonValueList *found);
222 : static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
223 : JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
224 : static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
225 : JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
226 : static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
227 : JsonbValue *value);
228 : static void getJsonPathVariable(JsonPathExecContext *cxt,
229 : JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
230 : static int JsonbArraySize(JsonbValue *jb);
231 : static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
232 : JsonbValue *rv, void *p);
233 : static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2,
234 : bool useTz);
235 : static int compareNumeric(Numeric a, Numeric b);
236 : static JsonbValue *copyJsonbValue(JsonbValue *src);
237 : static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
238 : JsonPathItem *jsp, JsonbValue *jb, int32 *index);
239 : static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
240 : JsonbValue *jbv, int32 id);
241 : static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
242 : static int JsonValueListLength(const JsonValueList *jvl);
243 : static bool JsonValueListIsEmpty(JsonValueList *jvl);
244 : static JsonbValue *JsonValueListHead(JsonValueList *jvl);
245 : static List *JsonValueListGetList(JsonValueList *jvl);
246 : static void JsonValueListInitIterator(const JsonValueList *jvl,
247 : JsonValueListIterator *it);
248 : static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
249 : JsonValueListIterator *it);
250 : static int JsonbType(JsonbValue *jb);
251 : static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
252 : static int JsonbType(JsonbValue *jb);
253 : static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
254 : static JsonbValue *wrapItemsInArray(const JsonValueList *items);
255 : static int compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
256 : bool useTz, bool *cast_error);
257 :
258 : /****************** User interface to JsonPath executor ********************/
259 :
260 : /*
261 : * jsonb_path_exists
262 : * Returns true if jsonpath returns at least one item for the specified
263 : * jsonb value. This function and jsonb_path_match() are used to
264 : * implement @? and @@ operators, which in turn are intended to have an
265 : * index support. Thus, it's desirable to make it easier to achieve
266 : * consistency between index scan results and sequential scan results.
267 : * So, we throw as few errors as possible. Regarding this function,
268 : * such behavior also matches behavior of JSON_EXISTS() clause of
269 : * SQL/JSON. Regarding jsonb_path_match(), this function doesn't have
270 : * an analogy in SQL/JSON, so we define its behavior on our own.
271 : */
272 : static Datum
273 GIC 43005 : jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
274 ECB : {
275 GIC 43005 : Jsonb *jb = PG_GETARG_JSONB_P(0);
276 CBC 43005 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
277 ECB : JsonPathExecResult res;
278 GIC 43005 : Jsonb *vars = NULL;
279 CBC 43005 : bool silent = true;
280 ECB :
281 GIC 43005 : if (PG_NARGS() == 4)
282 ECB : {
283 GIC 27 : vars = PG_GETARG_JSONB_P(2);
284 CBC 27 : silent = PG_GETARG_BOOL(3);
285 ECB : }
286 :
287 GIC 43005 : res = executeJsonPath(jp, vars, jb, !silent, NULL, tz);
288 ECB :
289 GIC 42999 : PG_FREE_IF_COPY(jb, 0);
290 CBC 42999 : PG_FREE_IF_COPY(jp, 1);
291 ECB :
292 GIC 42999 : if (jperIsError(res))
293 CBC 27 : PG_RETURN_NULL();
294 ECB :
295 GIC 42972 : PG_RETURN_BOOL(res == jperOk);
296 ECB : }
297 :
298 : Datum
299 GIC 27 : jsonb_path_exists(PG_FUNCTION_ARGS)
300 ECB : {
301 GIC 27 : return jsonb_path_exists_internal(fcinfo, false);
302 ECB : }
303 :
304 : Datum
305 UIC 0 : jsonb_path_exists_tz(PG_FUNCTION_ARGS)
306 EUB : {
307 UIC 0 : return jsonb_path_exists_internal(fcinfo, true);
308 EUB : }
309 :
310 : /*
311 : * jsonb_path_exists_opr
312 : * Implementation of operator "jsonb @? jsonpath" (2-argument version of
313 : * jsonb_path_exists()).
314 : */
315 : Datum
316 GIC 42978 : jsonb_path_exists_opr(PG_FUNCTION_ARGS)
317 ECB : {
318 : /* just call the other one -- it can handle both cases */
319 GIC 42978 : return jsonb_path_exists_internal(fcinfo, false);
320 ECB : }
321 :
322 : /*
323 : * jsonb_path_match
324 : * Returns jsonpath predicate result item for the specified jsonb value.
325 : * See jsonb_path_exists() comment for details regarding error handling.
326 : */
327 : static Datum
328 GIC 48957 : jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
329 ECB : {
330 GIC 48957 : Jsonb *jb = PG_GETARG_JSONB_P(0);
331 CBC 48957 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
332 48957 : JsonValueList found = {0};
333 48957 : Jsonb *vars = NULL;
334 48957 : bool silent = true;
335 ECB :
336 GIC 48957 : if (PG_NARGS() == 4)
337 ECB : {
338 GIC 63 : vars = PG_GETARG_JSONB_P(2);
339 CBC 63 : silent = PG_GETARG_BOOL(3);
340 ECB : }
341 :
342 GIC 48957 : (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
343 ECB :
344 GIC 48951 : PG_FREE_IF_COPY(jb, 0);
345 CBC 48951 : PG_FREE_IF_COPY(jp, 1);
346 ECB :
347 GIC 48951 : if (JsonValueListLength(&found) == 1)
348 ECB : {
349 GIC 48936 : JsonbValue *jbv = JsonValueListHead(&found);
350 ECB :
351 GIC 48936 : if (jbv->type == jbvBool)
352 CBC 48900 : PG_RETURN_BOOL(jbv->val.boolean);
353 ECB :
354 GIC 36 : if (jbv->type == jbvNull)
355 CBC 12 : PG_RETURN_NULL();
356 ECB : }
357 :
358 GIC 39 : if (!silent)
359 CBC 18 : ereport(ERROR,
360 ECB : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
361 : errmsg("single boolean result is expected")));
362 :
363 GIC 21 : PG_RETURN_NULL();
364 ECB : }
365 :
366 : Datum
367 GIC 63 : jsonb_path_match(PG_FUNCTION_ARGS)
368 ECB : {
369 GIC 63 : return jsonb_path_match_internal(fcinfo, false);
370 ECB : }
371 :
372 : Datum
373 UIC 0 : jsonb_path_match_tz(PG_FUNCTION_ARGS)
374 EUB : {
375 UIC 0 : return jsonb_path_match_internal(fcinfo, true);
376 EUB : }
377 :
378 : /*
379 : * jsonb_path_match_opr
380 : * Implementation of operator "jsonb @@ jsonpath" (2-argument version of
381 : * jsonb_path_match()).
382 : */
383 : Datum
384 GIC 48894 : jsonb_path_match_opr(PG_FUNCTION_ARGS)
385 ECB : {
386 : /* just call the other one -- it can handle both cases */
387 GIC 48894 : return jsonb_path_match_internal(fcinfo, false);
388 ECB : }
389 :
390 : /*
391 : * jsonb_path_query
392 : * Executes jsonpath for given jsonb document and returns result as
393 : * rowset.
394 : */
395 : static Datum
396 GIC 1809 : jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
397 ECB : {
398 : FuncCallContext *funcctx;
399 : List *found;
400 : JsonbValue *v;
401 : ListCell *c;
402 :
403 GIC 1809 : if (SRF_IS_FIRSTCALL())
404 ECB : {
405 : JsonPath *jp;
406 : Jsonb *jb;
407 : MemoryContext oldcontext;
408 : Jsonb *vars;
409 : bool silent;
410 GIC 936 : JsonValueList found = {0};
411 ECB :
412 GIC 936 : funcctx = SRF_FIRSTCALL_INIT();
413 CBC 936 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
414 ECB :
415 GIC 936 : jb = PG_GETARG_JSONB_P_COPY(0);
416 CBC 936 : jp = PG_GETARG_JSONPATH_P_COPY(1);
417 936 : vars = PG_GETARG_JSONB_P_COPY(2);
418 936 : silent = PG_GETARG_BOOL(3);
419 ECB :
420 GIC 936 : (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
421 ECB :
422 GIC 708 : funcctx->user_fctx = JsonValueListGetList(&found);
423 ECB :
424 GIC 708 : MemoryContextSwitchTo(oldcontext);
425 ECB : }
426 :
427 GIC 1581 : funcctx = SRF_PERCALL_SETUP();
428 CBC 1581 : found = funcctx->user_fctx;
429 ECB :
430 GIC 1581 : c = list_head(found);
431 ECB :
432 GIC 1581 : if (c == NULL)
433 CBC 708 : SRF_RETURN_DONE(funcctx);
434 ECB :
435 GIC 873 : v = lfirst(c);
436 CBC 873 : funcctx->user_fctx = list_delete_first(found);
437 ECB :
438 GIC 873 : SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
439 ECB : }
440 :
441 : Datum
442 GIC 1617 : jsonb_path_query(PG_FUNCTION_ARGS)
443 ECB : {
444 GIC 1617 : return jsonb_path_query_internal(fcinfo, false);
445 ECB : }
446 :
447 : Datum
448 GIC 192 : jsonb_path_query_tz(PG_FUNCTION_ARGS)
449 ECB : {
450 GIC 192 : return jsonb_path_query_internal(fcinfo, true);
451 ECB : }
452 :
453 : /*
454 : * jsonb_path_query_array
455 : * Executes jsonpath for given jsonb document and returns result as
456 : * jsonb array.
457 : */
458 : static Datum
459 GIC 24 : jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
460 ECB : {
461 GIC 24 : Jsonb *jb = PG_GETARG_JSONB_P(0);
462 CBC 24 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
463 24 : JsonValueList found = {0};
464 24 : Jsonb *vars = PG_GETARG_JSONB_P(2);
465 24 : bool silent = PG_GETARG_BOOL(3);
466 ECB :
467 GIC 24 : (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
468 ECB :
469 GIC 21 : PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
470 ECB : }
471 :
472 : Datum
473 GIC 24 : jsonb_path_query_array(PG_FUNCTION_ARGS)
474 ECB : {
475 GIC 24 : return jsonb_path_query_array_internal(fcinfo, false);
476 ECB : }
477 :
478 : Datum
479 UIC 0 : jsonb_path_query_array_tz(PG_FUNCTION_ARGS)
480 EUB : {
481 UIC 0 : return jsonb_path_query_array_internal(fcinfo, true);
482 EUB : }
483 :
484 : /*
485 : * jsonb_path_query_first
486 : * Executes jsonpath for given jsonb document and returns first result
487 : * item. If there are no items, NULL returned.
488 : */
489 : static Datum
490 GIC 2187 : jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
491 ECB : {
492 GIC 2187 : Jsonb *jb = PG_GETARG_JSONB_P(0);
493 CBC 2187 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
494 2187 : JsonValueList found = {0};
495 2187 : Jsonb *vars = PG_GETARG_JSONB_P(2);
496 2187 : bool silent = PG_GETARG_BOOL(3);
497 ECB :
498 GIC 2187 : (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
499 ECB :
500 GIC 2181 : if (JsonValueListLength(&found) >= 1)
501 CBC 2175 : PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
502 ECB : else
503 GIC 6 : PG_RETURN_NULL();
504 ECB : }
505 :
506 : Datum
507 GIC 2187 : jsonb_path_query_first(PG_FUNCTION_ARGS)
508 ECB : {
509 GIC 2187 : return jsonb_path_query_first_internal(fcinfo, false);
510 ECB : }
511 :
512 : Datum
513 UIC 0 : jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
514 EUB : {
515 UIC 0 : return jsonb_path_query_first_internal(fcinfo, true);
516 EUB : }
517 :
518 : /********************Execute functions for JsonPath**************************/
519 :
520 : /*
521 : * Interface to jsonpath executor
522 : *
523 : * 'path' - jsonpath to be executed
524 : * 'vars' - variables to be substituted to jsonpath
525 : * 'json' - target document for jsonpath evaluation
526 : * 'throwErrors' - whether we should throw suppressible errors
527 : * 'result' - list to store result items into
528 : *
529 : * Returns an error if a recoverable error happens during processing, or NULL
530 : * on no error.
531 : *
532 : * Note, jsonb and jsonpath values should be available and untoasted during
533 : * work because JsonPathItem, JsonbValue and result item could have pointers
534 : * into input values. If caller needs to just check if document matches
535 : * jsonpath, then it doesn't provide a result arg. In this case executor
536 : * works till first positive result and does not check the rest if possible.
537 : * In other case it tries to find all the satisfied result items.
538 : */
539 : static JsonPathExecResult
540 GIC 95109 : executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
541 ECB : JsonValueList *result, bool useTz)
542 : {
543 : JsonPathExecContext cxt;
544 : JsonPathExecResult res;
545 : JsonPathItem jsp;
546 : JsonbValue jbv;
547 :
548 GIC 95109 : jspInit(&jsp, path);
549 ECB :
550 GIC 95109 : if (!JsonbExtractScalar(&json->root, &jbv))
551 CBC 94761 : JsonbInitBinary(&jbv, json);
552 ECB :
553 GIC 95109 : if (vars && !JsonContainerIsObject(&vars->root))
554 ECB : {
555 GIC 6 : ereport(ERROR,
556 ECB : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
557 : errmsg("\"vars\" argument is not an object"),
558 : errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
559 : }
560 :
561 GIC 95103 : cxt.vars = vars;
562 CBC 95103 : cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
563 95103 : cxt.ignoreStructuralErrors = cxt.laxMode;
564 95103 : cxt.root = &jbv;
565 95103 : cxt.current = &jbv;
566 95103 : cxt.baseObject.jbc = NULL;
567 95103 : cxt.baseObject.id = 0;
568 95103 : cxt.lastGeneratedObjectId = vars ? 2 : 1;
569 95103 : cxt.innermostArraySize = -1;
570 95103 : cxt.throwErrors = throwErrors;
571 95103 : cxt.useTz = useTz;
572 ECB :
573 GIC 95103 : if (jspStrictAbsenseOfErrors(&cxt) && !result)
574 ECB : {
575 : /*
576 : * In strict mode we must get a complete list of values to check that
577 : * there are no errors at all.
578 : */
579 GIC 27 : JsonValueList vals = {0};
580 ECB :
581 GIC 27 : res = executeItem(&cxt, &jsp, &jbv, &vals);
582 ECB :
583 GIC 24 : if (jperIsError(res))
584 CBC 21 : return res;
585 ECB :
586 GIC 3 : return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
587 ECB : }
588 :
589 GIC 95076 : res = executeItem(&cxt, &jsp, &jbv, result);
590 ECB :
591 GIC 94836 : Assert(!throwErrors || !jperIsError(res));
592 ECB :
593 GIC 94836 : return res;
594 ECB : }
595 :
596 : /*
597 : * Execute jsonpath with automatic unwrapping of current item in lax mode.
598 : */
599 : static JsonPathExecResult
600 GIC 282933 : executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
601 ECB : JsonbValue *jb, JsonValueList *found)
602 : {
603 GIC 282933 : return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
604 ECB : }
605 :
606 : /*
607 : * Main jsonpath executor function: walks on jsonpath structure, finds
608 : * relevant parts of jsonb and evaluates expressions over them.
609 : * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
610 : */
611 : static JsonPathExecResult
612 GIC 284925 : executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
613 ECB : JsonbValue *jb, JsonValueList *found, bool unwrap)
614 : {
615 : JsonPathItem elem;
616 GIC 284925 : JsonPathExecResult res = jperNotFound;
617 ECB : JsonBaseObjectInfo baseObject;
618 :
619 GIC 284925 : check_stack_depth();
620 CBC 284925 : CHECK_FOR_INTERRUPTS();
621 ECB :
622 GIC 284925 : switch (jsp->type)
623 ECB : {
624 : /* all boolean item types: */
625 GIC 51081 : case jpiAnd:
626 ECB : case jpiOr:
627 : case jpiNot:
628 : case jpiIsUnknown:
629 : case jpiEqual:
630 : case jpiNotEqual:
631 : case jpiLess:
632 : case jpiGreater:
633 : case jpiLessOrEqual:
634 : case jpiGreaterOrEqual:
635 : case jpiExists:
636 : case jpiStartsWith:
637 : case jpiLikeRegex:
638 : {
639 GIC 51081 : JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
640 ECB :
641 GIC 51081 : res = appendBoolResult(cxt, jsp, found, st);
642 CBC 51081 : break;
643 ECB : }
644 :
645 GIC 82614 : case jpiKey:
646 CBC 82614 : if (JsonbType(jb) == jbvObject)
647 ECB : {
648 : JsonbValue *v;
649 : JsonbValue key;
650 :
651 GIC 82488 : key.type = jbvString;
652 CBC 82488 : key.val.string.val = jspGetString(jsp, &key.val.string.len);
653 ECB :
654 GIC 82488 : v = findJsonbValueFromContainer(jb->val.binary.data,
655 ECB : JB_FOBJECT, &key);
656 :
657 GIC 82488 : if (v != NULL)
658 ECB : {
659 GIC 13074 : res = executeNextItem(cxt, jsp, NULL,
660 ECB : v, found, false);
661 :
662 : /* free value if it was not added to found list */
663 GIC 13074 : if (jspHasNext(jsp) || !found)
664 CBC 8052 : pfree(v);
665 ECB : }
666 GIC 69414 : else if (!jspIgnoreStructuralErrors(cxt))
667 ECB : {
668 GIC 36 : Assert(found);
669 ECB :
670 GIC 36 : if (!jspThrowErrors(cxt))
671 CBC 24 : return jperError;
672 ECB :
673 GIC 12 : ereport(ERROR,
674 ECB : (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
675 : errmsg("JSON object does not contain key \"%s\"",
676 : pnstrdup(key.val.string.val,
677 : key.val.string.len))));
678 : }
679 : }
680 GIC 126 : else if (unwrap && JsonbType(jb) == jbvArray)
681 CBC 3 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
682 123 : else if (!jspIgnoreStructuralErrors(cxt))
683 ECB : {
684 GIC 33 : Assert(found);
685 CBC 33 : RETURN_ERROR(ereport(ERROR,
686 ECB : (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
687 : errmsg("jsonpath member accessor can only be applied to an object"))));
688 : }
689 GIC 82542 : break;
690 ECB :
691 GIC 101205 : case jpiRoot:
692 CBC 101205 : jb = cxt->root;
693 101205 : baseObject = setBaseObject(cxt, jb, 0);
694 101205 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
695 100998 : cxt->baseObject = baseObject;
696 100998 : break;
697 ECB :
698 GIC 9678 : case jpiCurrent:
699 CBC 9678 : res = executeNextItem(cxt, jsp, NULL, cxt->current,
700 ECB : found, true);
701 GIC 9678 : break;
702 ECB :
703 GIC 663 : case jpiAnyArray:
704 CBC 663 : if (JsonbType(jb) == jbvArray)
705 ECB : {
706 GIC 570 : bool hasNext = jspGetNext(jsp, &elem);
707 ECB :
708 GIC 570 : res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
709 CBC 570 : jb, found, jspAutoUnwrap(cxt));
710 ECB : }
711 GIC 93 : else if (jspAutoWrap(cxt))
712 CBC 87 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
713 6 : else if (!jspIgnoreStructuralErrors(cxt))
714 6 : RETURN_ERROR(ereport(ERROR,
715 ECB : (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
716 : errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
717 GIC 594 : break;
718 ECB :
719 GIC 144 : case jpiIndexArray:
720 CBC 144 : if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
721 87 : {
722 138 : int innermostArraySize = cxt->innermostArraySize;
723 ECB : int i;
724 GIC 138 : int size = JsonbArraySize(jb);
725 CBC 138 : bool singleton = size < 0;
726 138 : bool hasNext = jspGetNext(jsp, &elem);
727 ECB :
728 GIC 138 : if (singleton)
729 CBC 3 : size = 1;
730 ECB :
731 GIC 138 : cxt->innermostArraySize = size; /* for LAST evaluation */
732 ECB :
733 GIC 216 : for (i = 0; i < jsp->content.array.nelems; i++)
734 ECB : {
735 : JsonPathItem from;
736 : JsonPathItem to;
737 : int32 index;
738 : int32 index_from;
739 : int32 index_to;
740 GIC 144 : bool range = jspGetArraySubscript(jsp, &from,
741 ECB : &to, i);
742 :
743 GIC 144 : res = getArrayIndex(cxt, &from, jb, &index_from);
744 ECB :
745 GIC 132 : if (jperIsError(res))
746 CBC 15 : break;
747 ECB :
748 GIC 120 : if (range)
749 ECB : {
750 GIC 15 : res = getArrayIndex(cxt, &to, jb, &index_to);
751 ECB :
752 GIC 12 : if (jperIsError(res))
753 LBC 0 : break;
754 EUB : }
755 : else
756 GIC 105 : index_to = index_from;
757 ECB :
758 GIC 117 : if (!jspIgnoreStructuralErrors(cxt) &&
759 CBC 42 : (index_from < 0 ||
760 36 : index_from > index_to ||
761 36 : index_to >= size))
762 36 : RETURN_ERROR(ereport(ERROR,
763 ECB : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
764 : errmsg("jsonpath array subscript is out of bounds"))));
765 :
766 GIC 93 : if (index_from < 0)
767 CBC 6 : index_from = 0;
768 ECB :
769 GIC 93 : if (index_to >= size)
770 CBC 9 : index_to = size - 1;
771 ECB :
772 GIC 93 : res = jperNotFound;
773 ECB :
774 GIC 180 : for (index = index_from; index <= index_to; index++)
775 ECB : {
776 : JsonbValue *v;
777 : bool copy;
778 :
779 GIC 102 : if (singleton)
780 ECB : {
781 GIC 3 : v = jb;
782 CBC 3 : copy = true;
783 ECB : }
784 : else
785 : {
786 GIC 99 : v = getIthJsonbValueFromContainer(jb->val.binary.data,
787 ECB : (uint32) index);
788 :
789 GIC 99 : if (v == NULL)
790 LBC 0 : continue;
791 EUB :
792 GIC 99 : copy = false;
793 ECB : }
794 :
795 GIC 102 : if (!hasNext && !found)
796 CBC 12 : return jperOk;
797 ECB :
798 GIC 90 : res = executeNextItem(cxt, jsp, &elem, v, found,
799 ECB : copy);
800 :
801 GIC 90 : if (jperIsError(res))
802 LBC 0 : break;
803 EUB :
804 GIC 90 : if (res == jperOk && !found)
805 CBC 3 : break;
806 ECB : }
807 :
808 GIC 81 : if (jperIsError(res))
809 LBC 0 : break;
810 EUB :
811 GIC 81 : if (res == jperOk && !found)
812 CBC 3 : break;
813 ECB : }
814 :
815 GIC 87 : cxt->innermostArraySize = innermostArraySize;
816 ECB : }
817 GIC 6 : else if (!jspIgnoreStructuralErrors(cxt))
818 ECB : {
819 GIC 6 : RETURN_ERROR(ereport(ERROR,
820 ECB : (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
821 : errmsg("jsonpath array accessor can only be applied to an array"))));
822 : }
823 GIC 87 : break;
824 ECB :
825 GIC 33 : case jpiLast:
826 ECB : {
827 : JsonbValue tmpjbv;
828 : JsonbValue *lastjbv;
829 : int last;
830 GIC 33 : bool hasNext = jspGetNext(jsp, &elem);
831 ECB :
832 GIC 33 : if (cxt->innermostArraySize < 0)
833 LBC 0 : elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
834 EUB :
835 GIC 33 : if (!hasNext && !found)
836 ECB : {
837 GIC 3 : res = jperOk;
838 CBC 3 : break;
839 ECB : }
840 :
841 GIC 30 : last = cxt->innermostArraySize - 1;
842 ECB :
843 GIC 30 : lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
844 ECB :
845 GIC 30 : lastjbv->type = jbvNumeric;
846 CBC 30 : lastjbv->val.numeric = int64_to_numeric(last);
847 ECB :
848 GIC 30 : res = executeNextItem(cxt, jsp, &elem,
849 ECB : lastjbv, found, hasNext);
850 : }
851 GIC 30 : break;
852 ECB :
853 GIC 42 : case jpiAnyKey:
854 CBC 42 : if (JsonbType(jb) == jbvObject)
855 ECB : {
856 GIC 33 : bool hasNext = jspGetNext(jsp, &elem);
857 ECB :
858 GIC 33 : if (jb->type != jbvBinary)
859 LBC 0 : elog(ERROR, "invalid jsonb object type: %d", jb->type);
860 EUB :
861 GIC 33 : return executeAnyItem
862 ECB : (cxt, hasNext ? &elem : NULL,
863 : jb->val.binary.data, found, 1, 1, 1,
864 GIC 33 : false, jspAutoUnwrap(cxt));
865 ECB : }
866 GIC 9 : else if (unwrap && JsonbType(jb) == jbvArray)
867 LBC 0 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
868 GBC 9 : else if (!jspIgnoreStructuralErrors(cxt))
869 ECB : {
870 GIC 6 : Assert(found);
871 CBC 6 : RETURN_ERROR(ereport(ERROR,
872 ECB : (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
873 : errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
874 : }
875 GIC 3 : break;
876 ECB :
877 GIC 84 : case jpiAdd:
878 CBC 84 : return executeBinaryArithmExpr(cxt, jsp, jb,
879 ECB : numeric_add_opt_error, found);
880 :
881 GIC 30 : case jpiSub:
882 CBC 30 : return executeBinaryArithmExpr(cxt, jsp, jb,
883 ECB : numeric_sub_opt_error, found);
884 :
885 GIC 18 : case jpiMul:
886 CBC 18 : return executeBinaryArithmExpr(cxt, jsp, jb,
887 ECB : numeric_mul_opt_error, found);
888 :
889 GIC 33 : case jpiDiv:
890 CBC 33 : return executeBinaryArithmExpr(cxt, jsp, jb,
891 ECB : numeric_div_opt_error, found);
892 :
893 GIC 6 : case jpiMod:
894 CBC 6 : return executeBinaryArithmExpr(cxt, jsp, jb,
895 ECB : numeric_mod_opt_error, found);
896 :
897 GIC 30 : case jpiPlus:
898 CBC 30 : return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
899 ECB :
900 GIC 63 : case jpiMinus:
901 CBC 63 : return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
902 ECB : found);
903 :
904 GIC 9291 : case jpiFilter:
905 ECB : {
906 : JsonPathBool st;
907 :
908 GIC 9291 : if (unwrap && JsonbType(jb) == jbvArray)
909 CBC 63 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
910 ECB : false);
911 :
912 GIC 9228 : jspGetArg(jsp, &elem);
913 CBC 9228 : st = executeNestedBoolItem(cxt, &elem, jb);
914 9180 : if (st != jpbTrue)
915 8241 : res = jperNotFound;
916 ECB : else
917 GIC 939 : res = executeNextItem(cxt, jsp, NULL,
918 ECB : jb, found, true);
919 GIC 9180 : break;
920 ECB : }
921 :
922 GIC 153 : case jpiAny:
923 ECB : {
924 GIC 153 : bool hasNext = jspGetNext(jsp, &elem);
925 ECB :
926 : /* first try without any intermediate steps */
927 GIC 153 : if (jsp->content.anybounds.first == 0)
928 ECB : {
929 : bool savedIgnoreStructuralErrors;
930 :
931 GIC 84 : savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
932 CBC 84 : cxt->ignoreStructuralErrors = true;
933 84 : res = executeNextItem(cxt, jsp, &elem,
934 ECB : jb, found, true);
935 GIC 84 : cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
936 ECB :
937 GIC 84 : if (res == jperOk && !found)
938 CBC 3 : break;
939 ECB : }
940 :
941 GIC 150 : if (jb->type == jbvBinary)
942 CBC 150 : res = executeAnyItem
943 ECB : (cxt, hasNext ? &elem : NULL,
944 : jb->val.binary.data, found,
945 : 1,
946 : jsp->content.anybounds.first,
947 : jsp->content.anybounds.last,
948 GIC 150 : true, jspAutoUnwrap(cxt));
949 CBC 150 : break;
950 ECB : }
951 :
952 GIC 27774 : case jpiNull:
953 ECB : case jpiBool:
954 : case jpiNumeric:
955 : case jpiString:
956 : case jpiVariable:
957 : {
958 : JsonbValue vbuf;
959 : JsonbValue *v;
960 GIC 27774 : bool hasNext = jspGetNext(jsp, &elem);
961 ECB :
962 GIC 27774 : if (!hasNext && !found && jsp->type != jpiVariable)
963 ECB : {
964 : /*
965 : * Skip evaluation, but not for variables. We must
966 : * trigger an error for the missing variable.
967 : */
968 GIC 6 : res = jperOk;
969 CBC 6 : break;
970 ECB : }
971 :
972 GIC 27768 : v = hasNext ? &vbuf : palloc(sizeof(*v));
973 ECB :
974 GIC 27768 : baseObject = cxt->baseObject;
975 CBC 27768 : getJsonPathItem(cxt, jsp, v);
976 ECB :
977 GIC 27753 : res = executeNextItem(cxt, jsp, &elem,
978 ECB : v, found, hasNext);
979 GIC 27753 : cxt->baseObject = baseObject;
980 ECB : }
981 GIC 27753 : break;
982 ECB :
983 GIC 105 : case jpiType:
984 ECB : {
985 GIC 105 : JsonbValue *jbv = palloc(sizeof(*jbv));
986 ECB :
987 GIC 105 : jbv->type = jbvString;
988 CBC 105 : jbv->val.string.val = pstrdup(JsonbTypeName(jb));
989 105 : jbv->val.string.len = strlen(jbv->val.string.val);
990 ECB :
991 GIC 105 : res = executeNextItem(cxt, jsp, NULL, jbv,
992 ECB : found, false);
993 : }
994 GIC 105 : break;
995 ECB :
996 GIC 36 : case jpiSize:
997 ECB : {
998 GIC 36 : int size = JsonbArraySize(jb);
999 ECB :
1000 GIC 36 : if (size < 0)
1001 ECB : {
1002 GIC 24 : if (!jspAutoWrap(cxt))
1003 ECB : {
1004 GIC 6 : if (!jspIgnoreStructuralErrors(cxt))
1005 CBC 6 : RETURN_ERROR(ereport(ERROR,
1006 ECB : (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
1007 : errmsg("jsonpath item method .%s() can only be applied to an array",
1008 : jspOperationName(jsp->type)))));
1009 UIC 0 : break;
1010 EUB : }
1011 :
1012 GIC 18 : size = 1;
1013 ECB : }
1014 :
1015 GIC 30 : jb = palloc(sizeof(*jb));
1016 ECB :
1017 GIC 30 : jb->type = jbvNumeric;
1018 CBC 30 : jb->val.numeric = int64_to_numeric(size);
1019 ECB :
1020 GIC 30 : res = executeNextItem(cxt, jsp, NULL, jb, found, false);
1021 ECB : }
1022 GIC 30 : break;
1023 ECB :
1024 GIC 54 : case jpiAbs:
1025 CBC 54 : return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
1026 ECB : found);
1027 :
1028 GIC 24 : case jpiFloor:
1029 CBC 24 : return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
1030 ECB : found);
1031 :
1032 GIC 51 : case jpiCeiling:
1033 CBC 51 : return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
1034 ECB : found);
1035 :
1036 GIC 57 : case jpiDouble:
1037 ECB : {
1038 : JsonbValue jbv;
1039 :
1040 GIC 57 : if (unwrap && JsonbType(jb) == jbvArray)
1041 CBC 21 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1042 ECB : false);
1043 :
1044 GIC 54 : if (jb->type == jbvNumeric)
1045 ECB : {
1046 GIC 6 : char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1047 ECB : NumericGetDatum(jb->val.numeric)));
1048 : double val;
1049 GNC 6 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1050 ECB :
1051 GNC 6 : val = float8in_internal(tmp,
1052 : NULL,
1053 : "double precision",
1054 : tmp,
1055 : (Node *) &escontext);
1056 :
1057 6 : if (escontext.error_occurred || isinf(val) || isnan(val))
1058 CBC 3 : RETURN_ERROR(ereport(ERROR,
1059 ECB : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1060 : errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
1061 : jspOperationName(jsp->type)))));
1062 GIC 3 : res = jperOk;
1063 ECB : }
1064 GIC 48 : else if (jb->type == jbvString)
1065 ECB : {
1066 : /* cast string as double */
1067 : double val;
1068 GIC 24 : char *tmp = pnstrdup(jb->val.string.val,
1069 CBC 24 : jb->val.string.len);
1070 GNC 24 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1071 ECB :
1072 GNC 24 : val = float8in_internal(tmp,
1073 : NULL,
1074 : "double precision",
1075 : tmp,
1076 : (Node *) &escontext);
1077 :
1078 24 : if (escontext.error_occurred || isinf(val) || isnan(val))
1079 CBC 21 : RETURN_ERROR(ereport(ERROR,
1080 ECB : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1081 : errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
1082 : jspOperationName(jsp->type)))));
1083 :
1084 GIC 3 : jb = &jbv;
1085 CBC 3 : jb->type = jbvNumeric;
1086 3 : jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1087 ECB : Float8GetDatum(val)));
1088 GIC 3 : res = jperOk;
1089 ECB : }
1090 :
1091 GIC 30 : if (res == jperNotFound)
1092 CBC 24 : RETURN_ERROR(ereport(ERROR,
1093 ECB : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1094 : errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1095 : jspOperationName(jsp->type)))));
1096 :
1097 GIC 6 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1098 ECB : }
1099 GIC 6 : break;
1100 ECB :
1101 GIC 1611 : case jpiDatetime:
1102 CBC 1611 : if (unwrap && JsonbType(jb) == jbvArray)
1103 3 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1104 ECB :
1105 GIC 1608 : return executeDateTimeMethod(cxt, jsp, jb, found);
1106 ECB :
1107 GIC 45 : case jpiKeyValue:
1108 CBC 45 : if (unwrap && JsonbType(jb) == jbvArray)
1109 3 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1110 ECB :
1111 GIC 42 : return executeKeyValueMethod(cxt, jsp, jb, found);
1112 ECB :
1113 UIC 0 : default:
1114 UBC 0 : elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1115 EUB : }
1116 :
1117 GIC 282249 : return res;
1118 ECB : }
1119 :
1120 : /*
1121 : * Unwrap current array item and execute jsonpath for each of its elements.
1122 : */
1123 : static JsonPathExecResult
1124 GIC 672 : executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1125 ECB : JsonbValue *jb, JsonValueList *found,
1126 : bool unwrapElements)
1127 : {
1128 GIC 672 : if (jb->type != jbvBinary)
1129 ECB : {
1130 UIC 0 : Assert(jb->type != jbvArray);
1131 UBC 0 : elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1132 EUB : }
1133 :
1134 GIC 672 : return executeAnyItem
1135 ECB : (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1136 : false, unwrapElements);
1137 : }
1138 :
1139 : /*
1140 : * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1141 : * list if provided.
1142 : */
1143 : static JsonPathExecResult
1144 GIC 206007 : executeNextItem(JsonPathExecContext *cxt,
1145 ECB : JsonPathItem *cur, JsonPathItem *next,
1146 : JsonbValue *v, JsonValueList *found, bool copy)
1147 : {
1148 : JsonPathItem elem;
1149 : bool hasNext;
1150 :
1151 GIC 206007 : if (!cur)
1152 LBC 0 : hasNext = next != NULL;
1153 GBC 206007 : else if (next)
1154 CBC 80883 : hasNext = jspHasNext(cur);
1155 ECB : else
1156 : {
1157 GIC 125124 : next = &elem;
1158 CBC 125124 : hasNext = jspGetNext(cur, next);
1159 ECB : }
1160 :
1161 GIC 206007 : if (hasNext)
1162 CBC 92898 : return executeItem(cxt, next, v, found);
1163 ECB :
1164 GIC 113109 : if (found)
1165 CBC 88089 : JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1166 ECB :
1167 GIC 113109 : return jperOk;
1168 ECB : }
1169 :
1170 : /*
1171 : * Same as executeItem(), but when "unwrap == true" automatically unwraps
1172 : * each array item from the resulting sequence in lax mode.
1173 : */
1174 : static JsonPathExecResult
1175 GIC 94773 : executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1176 ECB : JsonbValue *jb, bool unwrap,
1177 : JsonValueList *found)
1178 : {
1179 GIC 94773 : if (unwrap && jspAutoUnwrap(cxt))
1180 ECB : {
1181 GIC 55080 : JsonValueList seq = {0};
1182 ECB : JsonValueListIterator it;
1183 GIC 55080 : JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1184 ECB : JsonbValue *item;
1185 :
1186 GIC 55068 : if (jperIsError(res))
1187 CBC 33 : return res;
1188 ECB :
1189 GIC 55035 : JsonValueListInitIterator(&seq, &it);
1190 CBC 91149 : while ((item = JsonValueListNext(&seq, &it)))
1191 ECB : {
1192 GIC 36114 : Assert(item->type != jbvArray);
1193 ECB :
1194 GIC 36114 : if (JsonbType(item) == jbvArray)
1195 CBC 27 : executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1196 ECB : else
1197 GIC 36087 : JsonValueListAppend(found, item);
1198 ECB : }
1199 :
1200 GIC 55035 : return jperOk;
1201 ECB : }
1202 :
1203 GIC 39693 : return executeItem(cxt, jsp, jb, found);
1204 ECB : }
1205 :
1206 : /*
1207 : * Same as executeItemOptUnwrapResult(), but with error suppression.
1208 : */
1209 : static JsonPathExecResult
1210 GIC 94341 : executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1211 ECB : JsonPathItem *jsp,
1212 : JsonbValue *jb, bool unwrap,
1213 : JsonValueList *found)
1214 : {
1215 : JsonPathExecResult res;
1216 GIC 94341 : bool throwErrors = cxt->throwErrors;
1217 ECB :
1218 GIC 94341 : cxt->throwErrors = false;
1219 CBC 94341 : res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1220 94338 : cxt->throwErrors = throwErrors;
1221 ECB :
1222 GIC 94338 : return res;
1223 ECB : }
1224 :
1225 : /* Execute boolean-valued jsonpath expression. */
1226 : static JsonPathBool
1227 GIC 86472 : executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1228 ECB : JsonbValue *jb, bool canHaveNext)
1229 : {
1230 : JsonPathItem larg;
1231 : JsonPathItem rarg;
1232 : JsonPathBool res;
1233 : JsonPathBool res2;
1234 :
1235 GIC 86472 : if (!canHaveNext && jspHasNext(jsp))
1236 LBC 0 : elog(ERROR, "boolean jsonpath item cannot have next item");
1237 EUB :
1238 GIC 86472 : switch (jsp->type)
1239 ECB : {
1240 GIC 12765 : case jpiAnd:
1241 CBC 12765 : jspGetLeftArg(jsp, &larg);
1242 12765 : res = executeBoolItem(cxt, &larg, jb, false);
1243 ECB :
1244 GIC 12765 : if (res == jpbFalse)
1245 CBC 11235 : return jpbFalse;
1246 ECB :
1247 : /*
1248 : * SQL/JSON says that we should check second arg in case of
1249 : * jperError
1250 : */
1251 :
1252 GIC 1530 : jspGetRightArg(jsp, &rarg);
1253 CBC 1530 : res2 = executeBoolItem(cxt, &rarg, jb, false);
1254 ECB :
1255 GIC 1530 : return res2 == jpbTrue ? res : res2;
1256 ECB :
1257 GIC 6477 : case jpiOr:
1258 CBC 6477 : jspGetLeftArg(jsp, &larg);
1259 6477 : res = executeBoolItem(cxt, &larg, jb, false);
1260 ECB :
1261 GIC 6477 : if (res == jpbTrue)
1262 CBC 1245 : return jpbTrue;
1263 ECB :
1264 GIC 5232 : jspGetRightArg(jsp, &rarg);
1265 CBC 5232 : res2 = executeBoolItem(cxt, &rarg, jb, false);
1266 ECB :
1267 GIC 5232 : return res2 == jpbFalse ? res : res2;
1268 ECB :
1269 GIC 54 : case jpiNot:
1270 CBC 54 : jspGetArg(jsp, &larg);
1271 ECB :
1272 GIC 54 : res = executeBoolItem(cxt, &larg, jb, false);
1273 ECB :
1274 GIC 54 : if (res == jpbUnknown)
1275 CBC 18 : return jpbUnknown;
1276 ECB :
1277 GIC 36 : return res == jpbTrue ? jpbFalse : jpbTrue;
1278 ECB :
1279 GIC 105 : case jpiIsUnknown:
1280 CBC 105 : jspGetArg(jsp, &larg);
1281 105 : res = executeBoolItem(cxt, &larg, jb, false);
1282 105 : return res == jpbUnknown ? jpbTrue : jpbFalse;
1283 ECB :
1284 GIC 27237 : case jpiEqual:
1285 ECB : case jpiNotEqual:
1286 : case jpiLess:
1287 : case jpiGreater:
1288 : case jpiLessOrEqual:
1289 : case jpiGreaterOrEqual:
1290 GIC 27237 : jspGetLeftArg(jsp, &larg);
1291 CBC 27237 : jspGetRightArg(jsp, &rarg);
1292 27237 : return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1293 ECB : executeComparison, cxt);
1294 :
1295 GIC 42 : case jpiStartsWith: /* 'whole STARTS WITH initial' */
1296 CBC 42 : jspGetLeftArg(jsp, &larg); /* 'whole' */
1297 42 : jspGetRightArg(jsp, &rarg); /* 'initial' */
1298 42 : return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1299 ECB : executeStartsWith, NULL);
1300 :
1301 GIC 198 : case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1302 ECB : {
1303 : /*
1304 : * 'expr' is a sequence-returning expression. 'pattern' is a
1305 : * regex string literal. SQL/JSON standard requires XQuery
1306 : * regexes, but we use Postgres regexes here. 'flags' is a
1307 : * string literal converted to integer flags at compile-time.
1308 : */
1309 GIC 198 : JsonLikeRegexContext lrcxt = {0};
1310 ECB :
1311 GIC 198 : jspInitByBuffer(&larg, jsp->base,
1312 ECB : jsp->content.like_regex.expr);
1313 :
1314 GIC 198 : return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1315 ECB : executeLikeRegex, &lrcxt);
1316 : }
1317 :
1318 GIC 39594 : case jpiExists:
1319 CBC 39594 : jspGetArg(jsp, &larg);
1320 ECB :
1321 GIC 39594 : if (jspStrictAbsenseOfErrors(cxt))
1322 ECB : {
1323 : /*
1324 : * In strict mode we must get a complete list of values to
1325 : * check that there are no errors at all.
1326 : */
1327 GIC 24 : JsonValueList vals = {0};
1328 ECB : JsonPathExecResult res =
1329 GIC 24 : executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1330 ECB : false, &vals);
1331 :
1332 GIC 24 : if (jperIsError(res))
1333 CBC 18 : return jpbUnknown;
1334 ECB :
1335 GIC 6 : return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1336 ECB : }
1337 : else
1338 : {
1339 : JsonPathExecResult res =
1340 GIC 39570 : executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1341 ECB : false, NULL);
1342 :
1343 GIC 39570 : if (jperIsError(res))
1344 CBC 12 : return jpbUnknown;
1345 ECB :
1346 GIC 39558 : return res == jperOk ? jpbTrue : jpbFalse;
1347 ECB : }
1348 :
1349 UIC 0 : default:
1350 UBC 0 : elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1351 EUB : return jpbUnknown;
1352 : }
1353 : }
1354 :
1355 : /*
1356 : * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1357 : * item onto the stack.
1358 : */
1359 : static JsonPathBool
1360 GIC 9228 : executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1361 ECB : JsonbValue *jb)
1362 : {
1363 : JsonbValue *prev;
1364 : JsonPathBool res;
1365 :
1366 GIC 9228 : prev = cxt->current;
1367 CBC 9228 : cxt->current = jb;
1368 9228 : res = executeBoolItem(cxt, jsp, jb, false);
1369 9180 : cxt->current = prev;
1370 ECB :
1371 GIC 9180 : return res;
1372 ECB : }
1373 :
1374 : /*
1375 : * Implementation of several jsonpath nodes:
1376 : * - jpiAny (.** accessor),
1377 : * - jpiAnyKey (.* accessor),
1378 : * - jpiAnyArray ([*] accessor)
1379 : */
1380 : static JsonPathExecResult
1381 GIC 948 : executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1382 ECB : JsonValueList *found, uint32 level, uint32 first, uint32 last,
1383 : bool ignoreStructuralErrors, bool unwrapNext)
1384 : {
1385 GIC 948 : JsonPathExecResult res = jperNotFound;
1386 ECB : JsonbIterator *it;
1387 : int32 r;
1388 : JsonbValue v;
1389 :
1390 GIC 948 : check_stack_depth();
1391 ECB :
1392 GIC 948 : if (level > last)
1393 CBC 15 : return res;
1394 ECB :
1395 GIC 933 : it = JsonbIteratorInit(jbc);
1396 ECB :
1397 : /*
1398 : * Recursively iterate over jsonb objects/arrays
1399 : */
1400 GIC 4815 : while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1401 ECB : {
1402 GIC 4170 : if (r == WJB_KEY)
1403 ECB : {
1404 GIC 276 : r = JsonbIteratorNext(&it, &v, true);
1405 CBC 276 : Assert(r == WJB_VALUE);
1406 ECB : }
1407 :
1408 GIC 4170 : if (r == WJB_VALUE || r == WJB_ELEM)
1409 ECB : {
1410 :
1411 GIC 2592 : if (level >= first ||
1412 CBC 6 : (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1413 6 : v.type != jbvBinary)) /* leaves only requested */
1414 ECB : {
1415 : /* check expression */
1416 GIC 2562 : if (jsp)
1417 ECB : {
1418 GIC 1992 : if (ignoreStructuralErrors)
1419 ECB : {
1420 : bool savedIgnoreStructuralErrors;
1421 :
1422 GIC 174 : savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1423 CBC 174 : cxt->ignoreStructuralErrors = true;
1424 174 : res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1425 174 : cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1426 ECB : }
1427 : else
1428 GIC 1818 : res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1429 ECB :
1430 GIC 1929 : if (jperIsError(res))
1431 CBC 33 : break;
1432 ECB :
1433 GIC 1896 : if (res == jperOk && !found)
1434 CBC 162 : break;
1435 ECB : }
1436 GIC 570 : else if (found)
1437 CBC 558 : JsonValueListAppend(found, copyJsonbValue(&v));
1438 ECB : else
1439 GIC 12 : return jperOk;
1440 ECB : }
1441 :
1442 GIC 2322 : if (level < last && v.type == jbvBinary)
1443 ECB : {
1444 GIC 93 : res = executeAnyItem
1445 ECB : (cxt, jsp, v.val.binary.data, found,
1446 : level + 1, first, last,
1447 : ignoreStructuralErrors, unwrapNext);
1448 :
1449 GIC 93 : if (jperIsError(res))
1450 LBC 0 : break;
1451 EUB :
1452 GIC 93 : if (res == jperOk && found == NULL)
1453 CBC 18 : break;
1454 ECB : }
1455 : }
1456 : }
1457 :
1458 GIC 858 : return res;
1459 ECB : }
1460 :
1461 : /*
1462 : * Execute unary or binary predicate.
1463 : *
1464 : * Predicates have existence semantics, because their operands are item
1465 : * sequences. Pairs of items from the left and right operand's sequences are
1466 : * checked. TRUE returned only if any pair satisfying the condition is found.
1467 : * In strict mode, even if the desired pair has already been found, all pairs
1468 : * still need to be examined to check the absence of errors. If any error
1469 : * occurs, UNKNOWN (analogous to SQL NULL) is returned.
1470 : */
1471 : static JsonPathBool
1472 GIC 27477 : executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
1473 ECB : JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
1474 : bool unwrapRightArg, JsonPathPredicateCallback exec,
1475 : void *param)
1476 : {
1477 : JsonPathExecResult res;
1478 : JsonValueListIterator lseqit;
1479 GIC 27477 : JsonValueList lseq = {0};
1480 CBC 27477 : JsonValueList rseq = {0};
1481 ECB : JsonbValue *lval;
1482 GIC 27477 : bool error = false;
1483 CBC 27477 : bool found = false;
1484 ECB :
1485 : /* Left argument is always auto-unwrapped. */
1486 GIC 27477 : res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
1487 CBC 27477 : if (jperIsError(res))
1488 9 : return jpbUnknown;
1489 ECB :
1490 GIC 27468 : if (rarg)
1491 ECB : {
1492 : /* Right argument is conditionally auto-unwrapped. */
1493 GIC 27270 : res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
1494 ECB : unwrapRightArg, &rseq);
1495 GIC 27267 : if (jperIsError(res))
1496 CBC 27 : return jpbUnknown;
1497 ECB : }
1498 :
1499 GIC 27438 : JsonValueListInitIterator(&lseq, &lseqit);
1500 CBC 36021 : while ((lval = JsonValueListNext(&lseq, &lseqit)))
1501 ECB : {
1502 : JsonValueListIterator rseqit;
1503 : JsonbValue *rval;
1504 GIC 11037 : bool first = true;
1505 ECB :
1506 GIC 11037 : JsonValueListInitIterator(&rseq, &rseqit);
1507 CBC 11037 : if (rarg)
1508 10839 : rval = JsonValueListNext(&rseq, &rseqit);
1509 ECB : else
1510 GIC 198 : rval = NULL;
1511 ECB :
1512 : /* Loop over right arg sequence or do single pass otherwise */
1513 GIC 17148 : while (rarg ? (rval != NULL) : first)
1514 ECB : {
1515 GIC 8565 : JsonPathBool res = exec(pred, lval, rval, param);
1516 ECB :
1517 GIC 8520 : if (res == jpbUnknown)
1518 ECB : {
1519 GIC 339 : if (jspStrictAbsenseOfErrors(cxt))
1520 CBC 2409 : return jpbUnknown;
1521 ECB :
1522 GIC 327 : error = true;
1523 ECB : }
1524 GIC 8181 : else if (res == jpbTrue)
1525 ECB : {
1526 GIC 2421 : if (!jspStrictAbsenseOfErrors(cxt))
1527 CBC 2397 : return jpbTrue;
1528 ECB :
1529 GIC 24 : found = true;
1530 ECB : }
1531 :
1532 GIC 6111 : first = false;
1533 CBC 6111 : if (rarg)
1534 5964 : rval = JsonValueListNext(&rseq, &rseqit);
1535 ECB : }
1536 : }
1537 :
1538 GIC 24984 : if (found) /* possible only in strict mode */
1539 CBC 9 : return jpbTrue;
1540 ECB :
1541 GIC 24975 : if (error) /* possible only in lax mode */
1542 CBC 312 : return jpbUnknown;
1543 ECB :
1544 GIC 24663 : return jpbFalse;
1545 ECB : }
1546 :
1547 : /*
1548 : * Execute binary arithmetic expression on singleton numeric operands.
1549 : * Array operands are automatically unwrapped in lax mode.
1550 : */
1551 : static JsonPathExecResult
1552 GIC 171 : executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1553 ECB : JsonbValue *jb, BinaryArithmFunc func,
1554 : JsonValueList *found)
1555 : {
1556 : JsonPathExecResult jper;
1557 : JsonPathItem elem;
1558 GIC 171 : JsonValueList lseq = {0};
1559 CBC 171 : JsonValueList rseq = {0};
1560 ECB : JsonbValue *lval;
1561 : JsonbValue *rval;
1562 : Numeric res;
1563 :
1564 GIC 171 : jspGetLeftArg(jsp, &elem);
1565 ECB :
1566 : /*
1567 : * XXX: By standard only operands of multiplicative expressions are
1568 : * unwrapped. We extend it to other binary arithmetic expressions too.
1569 : */
1570 GIC 171 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
1571 CBC 168 : if (jperIsError(jper))
1572 LBC 0 : return jper;
1573 EUB :
1574 GIC 168 : jspGetRightArg(jsp, &elem);
1575 ECB :
1576 GIC 168 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
1577 CBC 165 : if (jperIsError(jper))
1578 LBC 0 : return jper;
1579 EUB :
1580 GIC 297 : if (JsonValueListLength(&lseq) != 1 ||
1581 CBC 132 : !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
1582 33 : RETURN_ERROR(ereport(ERROR,
1583 ECB : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1584 : errmsg("left operand of jsonpath operator %s is not a single numeric value",
1585 : jspOperationName(jsp->type)))));
1586 :
1587 GIC 249 : if (JsonValueListLength(&rseq) != 1 ||
1588 CBC 117 : !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
1589 27 : RETURN_ERROR(ereport(ERROR,
1590 ECB : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1591 : errmsg("right operand of jsonpath operator %s is not a single numeric value",
1592 : jspOperationName(jsp->type)))));
1593 :
1594 GIC 105 : if (jspThrowErrors(cxt))
1595 ECB : {
1596 GIC 39 : res = func(lval->val.numeric, rval->val.numeric, NULL);
1597 ECB : }
1598 : else
1599 : {
1600 GIC 66 : bool error = false;
1601 ECB :
1602 GIC 66 : res = func(lval->val.numeric, rval->val.numeric, &error);
1603 ECB :
1604 GIC 66 : if (error)
1605 CBC 6 : return jperError;
1606 ECB : }
1607 :
1608 GIC 87 : if (!jspGetNext(jsp, &elem) && !found)
1609 CBC 3 : return jperOk;
1610 ECB :
1611 GIC 84 : lval = palloc(sizeof(*lval));
1612 CBC 84 : lval->type = jbvNumeric;
1613 84 : lval->val.numeric = res;
1614 ECB :
1615 GIC 84 : return executeNextItem(cxt, jsp, &elem, lval, found, false);
1616 ECB : }
1617 :
1618 : /*
1619 : * Execute unary arithmetic expression for each numeric item in its operand's
1620 : * sequence. Array operand is automatically unwrapped in lax mode.
1621 : */
1622 : static JsonPathExecResult
1623 GIC 93 : executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1624 ECB : JsonbValue *jb, PGFunction func, JsonValueList *found)
1625 : {
1626 : JsonPathExecResult jper;
1627 : JsonPathExecResult jper2;
1628 : JsonPathItem elem;
1629 GIC 93 : JsonValueList seq = {0};
1630 ECB : JsonValueListIterator it;
1631 : JsonbValue *val;
1632 : bool hasNext;
1633 :
1634 GIC 93 : jspGetArg(jsp, &elem);
1635 CBC 93 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
1636 ECB :
1637 GIC 90 : if (jperIsError(jper))
1638 LBC 0 : return jper;
1639 EUB :
1640 GIC 90 : jper = jperNotFound;
1641 ECB :
1642 GIC 90 : hasNext = jspGetNext(jsp, &elem);
1643 ECB :
1644 GIC 90 : JsonValueListInitIterator(&seq, &it);
1645 CBC 162 : while ((val = JsonValueListNext(&seq, &it)))
1646 ECB : {
1647 GIC 96 : if ((val = getScalar(val, jbvNumeric)))
1648 ECB : {
1649 GIC 75 : if (!found && !hasNext)
1650 CBC 6 : return jperOk;
1651 ECB : }
1652 : else
1653 : {
1654 GIC 21 : if (!found && !hasNext)
1655 CBC 3 : continue; /* skip non-numerics processing */
1656 ECB :
1657 GIC 18 : RETURN_ERROR(ereport(ERROR,
1658 ECB : (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
1659 : errmsg("operand of unary jsonpath operator %s is not a numeric value",
1660 : jspOperationName(jsp->type)))));
1661 : }
1662 :
1663 GIC 69 : if (func)
1664 CBC 42 : val->val.numeric =
1665 42 : DatumGetNumeric(DirectFunctionCall1(func,
1666 ECB : NumericGetDatum(val->val.numeric)));
1667 :
1668 GIC 69 : jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
1669 ECB :
1670 GIC 69 : if (jperIsError(jper2))
1671 LBC 0 : return jper2;
1672 EUB :
1673 GIC 69 : if (jper2 == jperOk)
1674 ECB : {
1675 GIC 69 : if (!found)
1676 LBC 0 : return jperOk;
1677 GBC 69 : jper = jperOk;
1678 ECB : }
1679 : }
1680 :
1681 GIC 66 : return jper;
1682 ECB : }
1683 :
1684 : /*
1685 : * STARTS_WITH predicate callback.
1686 : *
1687 : * Check if the 'whole' string starts from 'initial' string.
1688 : */
1689 : static JsonPathBool
1690 GIC 87 : executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
1691 ECB : void *param)
1692 : {
1693 GIC 87 : if (!(whole = getScalar(whole, jbvString)))
1694 CBC 24 : return jpbUnknown; /* error */
1695 ECB :
1696 GIC 63 : if (!(initial = getScalar(initial, jbvString)))
1697 LBC 0 : return jpbUnknown; /* error */
1698 EUB :
1699 GIC 63 : if (whole->val.string.len >= initial->val.string.len &&
1700 CBC 45 : !memcmp(whole->val.string.val,
1701 45 : initial->val.string.val,
1702 45 : initial->val.string.len))
1703 27 : return jpbTrue;
1704 ECB :
1705 GIC 36 : return jpbFalse;
1706 ECB : }
1707 :
1708 : /*
1709 : * LIKE_REGEX predicate callback.
1710 : *
1711 : * Check if the string matches regex pattern.
1712 : */
1713 : static JsonPathBool
1714 GIC 198 : executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
1715 ECB : void *param)
1716 : {
1717 GIC 198 : JsonLikeRegexContext *cxt = param;
1718 ECB :
1719 GIC 198 : if (!(str = getScalar(str, jbvString)))
1720 CBC 60 : return jpbUnknown;
1721 ECB :
1722 : /* Cache regex text and converted flags. */
1723 GIC 138 : if (!cxt->regex)
1724 ECB : {
1725 GIC 138 : cxt->regex =
1726 CBC 138 : cstring_to_text_with_len(jsp->content.like_regex.pattern,
1727 ECB : jsp->content.like_regex.patternlen);
1728 GNC 138 : (void) jspConvertRegexFlags(jsp->content.like_regex.flags,
1729 : &(cxt->cflags), NULL);
1730 ECB : }
1731 :
1732 GIC 138 : if (RE_compile_and_execute(cxt->regex, str->val.string.val,
1733 : str->val.string.len,
1734 ECB : cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
1735 GIC 51 : return jpbTrue;
1736 :
1737 CBC 87 : return jpbFalse;
1738 : }
1739 ECB :
1740 : /*
1741 : * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
1742 : * user function 'func'.
1743 : */
1744 : static JsonPathExecResult
1745 GIC 129 : executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1746 : JsonbValue *jb, bool unwrap, PGFunction func,
1747 ECB : JsonValueList *found)
1748 : {
1749 : JsonPathItem next;
1750 : Datum datum;
1751 :
1752 GIC 129 : if (unwrap && JsonbType(jb) == jbvArray)
1753 UIC 0 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1754 ECB :
1755 GBC 129 : if (!(jb = getScalar(jb, jbvNumeric)))
1756 GIC 18 : RETURN_ERROR(ereport(ERROR,
1757 ECB : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1758 : errmsg("jsonpath item method .%s() can only be applied to a numeric value",
1759 : jspOperationName(jsp->type)))));
1760 :
1761 GIC 111 : datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
1762 :
1763 CBC 111 : if (!jspGetNext(jsp, &next) && !found)
1764 UIC 0 : return jperOk;
1765 ECB :
1766 GBC 111 : jb = palloc(sizeof(*jb));
1767 GIC 111 : jb->type = jbvNumeric;
1768 CBC 111 : jb->val.numeric = DatumGetNumeric(datum);
1769 ECB :
1770 CBC 111 : return executeNextItem(cxt, jsp, &next, jb, found, false);
1771 : }
1772 ECB :
1773 : /*
1774 : * Implementation of the .datetime() method.
1775 : *
1776 : * Converts a string into a date/time value. The actual type is determined at run time.
1777 : * If an argument is provided, this argument is used as a template string.
1778 : * Otherwise, the first fitting ISO format is selected.
1779 : */
1780 : static JsonPathExecResult
1781 GIC 1608 : executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1782 : JsonbValue *jb, JsonValueList *found)
1783 ECB : {
1784 : JsonbValue jbvbuf;
1785 : Datum value;
1786 : text *datetime;
1787 : Oid collid;
1788 : Oid typid;
1789 GIC 1608 : int32 typmod = -1;
1790 1608 : int tz = 0;
1791 ECB : bool hasNext;
1792 CBC 1608 : JsonPathExecResult res = jperNotFound;
1793 : JsonPathItem elem;
1794 ECB :
1795 GIC 1608 : if (!(jb = getScalar(jb, jbvString)))
1796 15 : RETURN_ERROR(ereport(ERROR,
1797 ECB : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
1798 : errmsg("jsonpath item method .%s() can only be applied to a string",
1799 : jspOperationName(jsp->type)))));
1800 :
1801 GIC 1593 : datetime = cstring_to_text_with_len(jb->val.string.val,
1802 : jb->val.string.len);
1803 ECB :
1804 : /*
1805 : * At some point we might wish to have callers supply the collation to
1806 : * use, but right now it's unclear that they'd be able to do better than
1807 : * DEFAULT_COLLATION_OID anyway.
1808 : */
1809 GIC 1593 : collid = DEFAULT_COLLATION_OID;
1810 :
1811 CBC 1593 : if (jsp->content.arg)
1812 : {
1813 ECB : text *template;
1814 : char *template_str;
1815 : int template_len;
1816 GNC 825 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1817 :
1818 CBC 825 : jspGetArg(jsp, &elem);
1819 :
1820 825 : if (elem.type != jpiString)
1821 UIC 0 : elog(ERROR, "invalid jsonpath item type for .datetime() argument");
1822 ECB :
1823 GBC 825 : template_str = jspGetString(&elem, &template_len);
1824 :
1825 CBC 825 : template = cstring_to_text_with_len(template_str,
1826 : template_len);
1827 ECB :
1828 GIC 825 : value = parse_datetime(datetime, template, collid, true,
1829 : &typid, &typmod, &tz,
1830 GNC 825 : jspThrowErrors(cxt) ? NULL : (Node *) &escontext);
1831 :
1832 795 : if (escontext.error_occurred)
1833 UIC 0 : res = jperError;
1834 ECB : else
1835 GBC 795 : res = jperOk;
1836 : }
1837 ECB : else
1838 : {
1839 : /*
1840 : * According to SQL/JSON standard enumerate ISO formats for: date,
1841 : * timetz, time, timestamptz, timestamp.
1842 : *
1843 : * We also support ISO 8601 for timestamps, because to_json[b]()
1844 : * functions use this format.
1845 : */
1846 : static const char *fmt_str[] =
1847 : {
1848 : "yyyy-mm-dd",
1849 : "HH24:MI:SSTZH:TZM",
1850 : "HH24:MI:SSTZH",
1851 : "HH24:MI:SS",
1852 : "yyyy-mm-dd HH24:MI:SSTZH:TZM",
1853 : "yyyy-mm-dd HH24:MI:SSTZH",
1854 : "yyyy-mm-dd HH24:MI:SS",
1855 : "yyyy-mm-dd\"T\"HH24:MI:SSTZH:TZM",
1856 : "yyyy-mm-dd\"T\"HH24:MI:SSTZH",
1857 : "yyyy-mm-dd\"T\"HH24:MI:SS"
1858 : };
1859 :
1860 : /* cache for format texts */
1861 : static text *fmt_txt[lengthof(fmt_str)] = {0};
1862 : int i;
1863 :
1864 : /* loop until datetime format fits */
1865 GIC 3414 : for (i = 0; i < lengthof(fmt_str); i++)
1866 : {
1867 GNC 3408 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1868 :
1869 CBC 3408 : if (!fmt_txt[i])
1870 : {
1871 ECB : MemoryContext oldcxt =
1872 GIC 30 : MemoryContextSwitchTo(TopMemoryContext);
1873 :
1874 CBC 30 : fmt_txt[i] = cstring_to_text(fmt_str[i]);
1875 GIC 30 : MemoryContextSwitchTo(oldcxt);
1876 ECB : }
1877 :
1878 GIC 3408 : value = parse_datetime(datetime, fmt_txt[i], collid, true,
1879 : &typid, &typmod, &tz,
1880 : (Node *) &escontext);
1881 :
1882 GNC 3408 : if (!escontext.error_occurred)
1883 : {
1884 CBC 762 : res = jperOk;
1885 GIC 762 : break;
1886 ECB : }
1887 : }
1888 :
1889 GIC 768 : if (res == jperNotFound)
1890 6 : RETURN_ERROR(ereport(ERROR,
1891 ECB : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
1892 : errmsg("datetime format is not recognized: \"%s\"",
1893 : text_to_cstring(datetime)),
1894 : errhint("Use a datetime template argument to specify the input data format."))));
1895 : }
1896 :
1897 GIC 1557 : pfree(datetime);
1898 :
1899 CBC 1557 : if (jperIsError(res))
1900 UIC 0 : return res;
1901 ECB :
1902 GBC 1557 : hasNext = jspGetNext(jsp, &elem);
1903 :
1904 CBC 1557 : if (!hasNext && !found)
1905 GIC 3 : return res;
1906 ECB :
1907 CBC 1554 : jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
1908 :
1909 1554 : jb->type = jbvDatetime;
1910 GIC 1554 : jb->val.datetime.value = value;
1911 CBC 1554 : jb->val.datetime.typid = typid;
1912 1554 : jb->val.datetime.typmod = typmod;
1913 1554 : jb->val.datetime.tz = tz;
1914 ECB :
1915 CBC 1554 : return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
1916 : }
1917 ECB :
1918 : /*
1919 : * Implementation of .keyvalue() method.
1920 : *
1921 : * .keyvalue() method returns a sequence of object's key-value pairs in the
1922 : * following format: '{ "key": key, "value": value, "id": id }'.
1923 : *
1924 : * "id" field is an object identifier which is constructed from the two parts:
1925 : * base object id and its binary offset in base object's jsonb:
1926 : * id = 10000000000 * base_object_id + obj_offset_in_base_object
1927 : *
1928 : * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1929 : * (maximal offset in jsonb). Decimal multiplier is used here to improve the
1930 : * readability of identifiers.
1931 : *
1932 : * Base object is usually a root object of the path: context item '$' or path
1933 : * variable '$var', literals can't produce objects for now. But if the path
1934 : * contains generated objects (.keyvalue() itself, for example), then they
1935 : * become base object for the subsequent .keyvalue().
1936 : *
1937 : * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
1938 : * of variables (see getJsonPathVariable()). Ids for generated objects
1939 : * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
1940 : */
1941 : static JsonPathExecResult
1942 GIC 42 : executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1943 : JsonbValue *jb, JsonValueList *found)
1944 ECB : {
1945 GIC 42 : JsonPathExecResult res = jperNotFound;
1946 : JsonPathItem next;
1947 ECB : JsonbContainer *jbc;
1948 : JsonbValue key;
1949 : JsonbValue val;
1950 : JsonbValue idval;
1951 : JsonbValue keystr;
1952 : JsonbValue valstr;
1953 : JsonbValue idstr;
1954 : JsonbIterator *it;
1955 : JsonbIteratorToken tok;
1956 : int64 id;
1957 : bool hasNext;
1958 :
1959 GIC 42 : if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
1960 12 : RETURN_ERROR(ereport(ERROR,
1961 ECB : (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
1962 : errmsg("jsonpath item method .%s() can only be applied to an object",
1963 : jspOperationName(jsp->type)))));
1964 :
1965 GIC 30 : jbc = jb->val.binary.data;
1966 :
1967 CBC 30 : if (!JsonContainerSize(jbc))
1968 GIC 9 : return jperNotFound; /* no key-value pairs */
1969 ECB :
1970 CBC 21 : hasNext = jspGetNext(jsp, &next);
1971 :
1972 21 : keystr.type = jbvString;
1973 GIC 21 : keystr.val.string.val = "key";
1974 CBC 21 : keystr.val.string.len = 3;
1975 ECB :
1976 CBC 21 : valstr.type = jbvString;
1977 GIC 21 : valstr.val.string.val = "value";
1978 CBC 21 : valstr.val.string.len = 5;
1979 ECB :
1980 CBC 21 : idstr.type = jbvString;
1981 GIC 21 : idstr.val.string.val = "id";
1982 CBC 21 : idstr.val.string.len = 2;
1983 ECB :
1984 : /* construct object id from its base object and offset inside that */
1985 GIC 21 : id = jb->type != jbvBinary ? 0 :
1986 21 : (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
1987 CBC 21 : id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
1988 ECB :
1989 CBC 21 : idval.type = jbvNumeric;
1990 GIC 21 : idval.val.numeric = int64_to_numeric(id);
1991 ECB :
1992 CBC 21 : it = JsonbIteratorInit(jbc);
1993 :
1994 84 : while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
1995 : {
1996 ECB : JsonBaseObjectInfo baseObject;
1997 : JsonbValue obj;
1998 : JsonbParseState *ps;
1999 : JsonbValue *keyval;
2000 : Jsonb *jsonb;
2001 :
2002 GIC 69 : if (tok != WJB_KEY)
2003 36 : continue;
2004 ECB :
2005 CBC 33 : res = jperOk;
2006 :
2007 33 : if (!hasNext && !found)
2008 GIC 6 : break;
2009 ECB :
2010 CBC 30 : tok = JsonbIteratorNext(&it, &val, true);
2011 GIC 30 : Assert(tok == WJB_VALUE);
2012 ECB :
2013 CBC 30 : ps = NULL;
2014 GIC 30 : pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
2015 ECB :
2016 CBC 30 : pushJsonbValue(&ps, WJB_KEY, &keystr);
2017 GIC 30 : pushJsonbValue(&ps, WJB_VALUE, &key);
2018 ECB :
2019 CBC 30 : pushJsonbValue(&ps, WJB_KEY, &valstr);
2020 GIC 30 : pushJsonbValue(&ps, WJB_VALUE, &val);
2021 ECB :
2022 CBC 30 : pushJsonbValue(&ps, WJB_KEY, &idstr);
2023 GIC 30 : pushJsonbValue(&ps, WJB_VALUE, &idval);
2024 ECB :
2025 CBC 30 : keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2026 :
2027 30 : jsonb = JsonbValueToJsonb(keyval);
2028 :
2029 30 : JsonbInitBinary(&obj, jsonb);
2030 :
2031 30 : baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
2032 :
2033 30 : res = executeNextItem(cxt, jsp, &next, &obj, found, true);
2034 :
2035 30 : cxt->baseObject = baseObject;
2036 :
2037 30 : if (jperIsError(res))
2038 UIC 0 : return res;
2039 ECB :
2040 GBC 30 : if (res == jperOk && !found)
2041 GIC 3 : break;
2042 ECB : }
2043 :
2044 GIC 21 : return res;
2045 : }
2046 ECB :
2047 : /*
2048 : * Convert boolean execution status 'res' to a boolean JSON item and execute
2049 : * next jsonpath.
2050 : */
2051 : static JsonPathExecResult
2052 GIC 51081 : appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
2053 : JsonValueList *found, JsonPathBool res)
2054 ECB : {
2055 : JsonPathItem next;
2056 : JsonbValue jbv;
2057 :
2058 GIC 51081 : if (!jspGetNext(jsp, &next) && !found)
2059 3 : return jperOk; /* found singleton boolean value */
2060 ECB :
2061 CBC 51078 : if (res == jpbUnknown)
2062 : {
2063 15 : jbv.type = jbvNull;
2064 : }
2065 ECB : else
2066 : {
2067 GIC 51063 : jbv.type = jbvBool;
2068 51063 : jbv.val.boolean = res == jpbTrue;
2069 ECB : }
2070 :
2071 GIC 51078 : return executeNextItem(cxt, jsp, &next, &jbv, found, true);
2072 : }
2073 ECB :
2074 : /*
2075 : * Convert jsonpath's scalar or variable node to actual jsonb value.
2076 : *
2077 : * If node is a variable then its id returned, otherwise 0 returned.
2078 : */
2079 : static void
2080 GIC 27768 : getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
2081 : JsonbValue *value)
2082 ECB : {
2083 GIC 27768 : switch (item->type)
2084 : {
2085 CBC 3777 : case jpiNull:
2086 GIC 3777 : value->type = jbvNull;
2087 CBC 3777 : break;
2088 711 : case jpiBool:
2089 711 : value->type = jbvBool;
2090 711 : value->val.boolean = jspGetBool(item);
2091 711 : break;
2092 9678 : case jpiNumeric:
2093 9678 : value->type = jbvNumeric;
2094 9678 : value->val.numeric = jspGetNumeric(item);
2095 9678 : break;
2096 10977 : case jpiString:
2097 10977 : value->type = jbvString;
2098 21954 : value->val.string.val = jspGetString(item,
2099 10977 : &value->val.string.len);
2100 10977 : break;
2101 2625 : case jpiVariable:
2102 2625 : getJsonPathVariable(cxt, item, cxt->vars, value);
2103 2610 : return;
2104 LBC 0 : default:
2105 0 : elog(ERROR, "unexpected jsonpath item type");
2106 EUB : }
2107 : }
2108 :
2109 : /*
2110 : * Get the value of variable passed to jsonpath executor
2111 : */
2112 : static void
2113 GIC 2625 : getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
2114 : Jsonb *vars, JsonbValue *value)
2115 ECB : {
2116 : char *varName;
2117 : int varNameLength;
2118 : JsonbValue tmp;
2119 : JsonbValue *v;
2120 :
2121 GIC 2625 : if (!vars)
2122 : {
2123 LBC 0 : value->type = jbvNull;
2124 UIC 0 : return;
2125 EUB : }
2126 :
2127 GIC 2625 : Assert(variable->type == jpiVariable);
2128 2625 : varName = jspGetString(variable, &varNameLength);
2129 CBC 2625 : tmp.type = jbvString;
2130 2625 : tmp.val.string.val = varName;
2131 2625 : tmp.val.string.len = varNameLength;
2132 ECB :
2133 CBC 2625 : v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
2134 :
2135 2625 : if (v)
2136 : {
2137 2610 : *value = *v;
2138 GIC 2610 : pfree(v);
2139 ECB : }
2140 : else
2141 : {
2142 GIC 15 : ereport(ERROR,
2143 : (errcode(ERRCODE_UNDEFINED_OBJECT),
2144 ECB : errmsg("could not find jsonpath variable \"%s\"",
2145 : pnstrdup(varName, varNameLength))));
2146 : }
2147 :
2148 GIC 2610 : JsonbInitBinary(&tmp, vars);
2149 2610 : setBaseObject(cxt, &tmp, 1);
2150 ECB : }
2151 :
2152 : /**************** Support functions for JsonPath execution *****************/
2153 :
2154 : /*
2155 : * Returns the size of an array item, or -1 if item is not an array.
2156 : */
2157 : static int
2158 GIC 174 : JsonbArraySize(JsonbValue *jb)
2159 : {
2160 CBC 174 : Assert(jb->type != jbvArray);
2161 :
2162 174 : if (jb->type == jbvBinary)
2163 : {
2164 153 : JsonbContainer *jbc = jb->val.binary.data;
2165 :
2166 153 : if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
2167 GIC 147 : return JsonContainerSize(jbc);
2168 ECB : }
2169 :
2170 GIC 27 : return -1;
2171 : }
2172 ECB :
2173 : /* Comparison predicate callback. */
2174 : static JsonPathBool
2175 GIC 8280 : executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
2176 : {
2177 CBC 8280 : JsonPathExecContext *cxt = (JsonPathExecContext *) p;
2178 :
2179 8280 : return compareItems(cmp->type, lv, rv, cxt->useTz);
2180 : }
2181 ECB :
2182 : /*
2183 : * Perform per-byte comparison of two strings.
2184 : */
2185 : static int
2186 GIC 1728 : binaryCompareStrings(const char *s1, int len1,
2187 : const char *s2, int len2)
2188 ECB : {
2189 : int cmp;
2190 :
2191 GIC 1728 : cmp = memcmp(s1, s2, Min(len1, len2));
2192 :
2193 CBC 1728 : if (cmp != 0)
2194 GIC 984 : return cmp;
2195 ECB :
2196 CBC 744 : if (len1 == len2)
2197 GIC 144 : return 0;
2198 ECB :
2199 CBC 600 : return len1 < len2 ? -1 : 1;
2200 : }
2201 ECB :
2202 : /*
2203 : * Compare two strings in the current server encoding using Unicode codepoint
2204 : * collation.
2205 : */
2206 : static int
2207 GIC 1728 : compareStrings(const char *mbstr1, int mblen1,
2208 : const char *mbstr2, int mblen2)
2209 ECB : {
2210 GIC 3456 : if (GetDatabaseEncoding() == PG_SQL_ASCII ||
2211 1728 : GetDatabaseEncoding() == PG_UTF8)
2212 ECB : {
2213 : /*
2214 : * It's known property of UTF-8 strings that their per-byte comparison
2215 : * result matches codepoints comparison result. ASCII can be
2216 : * considered as special case of UTF-8.
2217 : */
2218 GIC 1728 : return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2219 : }
2220 ECB : else
2221 : {
2222 : char *utf8str1,
2223 : *utf8str2;
2224 : int cmp,
2225 : utf8len1,
2226 : utf8len2;
2227 :
2228 : /*
2229 : * We have to convert other encodings to UTF-8 first, then compare.
2230 : * Input strings may be not null-terminated and pg_server_to_any() may
2231 : * return them "as is". So, use strlen() only if there is real
2232 : * conversion.
2233 : */
2234 UIC 0 : utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
2235 0 : utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
2236 UBC 0 : utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
2237 0 : utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
2238 EUB :
2239 UBC 0 : cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
2240 :
2241 EUB : /*
2242 : * If pg_server_to_any() did no real conversion, then we actually
2243 : * compared original strings. So, we already done.
2244 : */
2245 UIC 0 : if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
2246 0 : return cmp;
2247 EUB :
2248 : /* Free memory if needed */
2249 UIC 0 : if (mbstr1 != utf8str1)
2250 0 : pfree(utf8str1);
2251 UBC 0 : if (mbstr2 != utf8str2)
2252 0 : pfree(utf8str2);
2253 EUB :
2254 : /*
2255 : * When all Unicode codepoints are equal, return result of binary
2256 : * comparison. In some edge cases, same characters may have different
2257 : * representations in encoding. Then our behavior could diverge from
2258 : * standard. However, that allow us to do simple binary comparison
2259 : * for "==" operator, which is performance critical in typical cases.
2260 : * In future to implement strict standard conformance, we can do
2261 : * normalization of input JSON strings.
2262 : */
2263 UIC 0 : if (cmp == 0)
2264 0 : return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2265 EUB : else
2266 UBC 0 : return cmp;
2267 : }
2268 EUB : }
2269 :
2270 : /*
2271 : * Compare two SQL/JSON items using comparison operation 'op'.
2272 : */
2273 : static JsonPathBool
2274 GIC 8280 : compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
2275 : {
2276 ECB : int cmp;
2277 : bool res;
2278 :
2279 GIC 8280 : if (jb1->type != jb2->type)
2280 : {
2281 CBC 1485 : if (jb1->type == jbvNull || jb2->type == jbvNull)
2282 :
2283 ECB : /*
2284 : * Equality and order comparison of nulls to non-nulls returns
2285 : * always false, but inequality comparison returns true.
2286 : */
2287 GIC 1362 : return op == jpiNotEqual ? jpbTrue : jpbFalse;
2288 :
2289 ECB : /* Non-null items of different types are not comparable. */
2290 GIC 123 : return jpbUnknown;
2291 : }
2292 ECB :
2293 GIC 6795 : switch (jb1->type)
2294 : {
2295 CBC 93 : case jbvNull:
2296 GIC 93 : cmp = 0;
2297 CBC 93 : break;
2298 435 : case jbvBool:
2299 633 : cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2300 198 : jb1->val.boolean ? 1 : -1;
2301 435 : break;
2302 582 : case jbvNumeric:
2303 582 : cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2304 582 : break;
2305 4965 : case jbvString:
2306 4965 : if (op == jpiEqual)
2307 3237 : return jb1->val.string.len != jb2->val.string.len ||
2308 1791 : memcmp(jb1->val.string.val,
2309 1791 : jb2->val.string.val,
2310 3237 : jb1->val.string.len) ? jpbFalse : jpbTrue;
2311 ECB :
2312 CBC 1728 : cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
2313 GIC 1728 : jb2->val.string.val, jb2->val.string.len);
2314 CBC 1728 : break;
2315 714 : case jbvDatetime:
2316 ECB : {
2317 : bool cast_error;
2318 :
2319 GIC 714 : cmp = compareDatetime(jb1->val.datetime.value,
2320 : jb1->val.datetime.typid,
2321 ECB : jb2->val.datetime.value,
2322 : jb2->val.datetime.typid,
2323 : useTz,
2324 : &cast_error);
2325 :
2326 GIC 669 : if (cast_error)
2327 126 : return jpbUnknown;
2328 ECB : }
2329 CBC 543 : break;
2330 :
2331 6 : case jbvBinary:
2332 : case jbvArray:
2333 ECB : case jbvObject:
2334 GIC 6 : return jpbUnknown; /* non-scalars are not comparable */
2335 :
2336 LBC 0 : default:
2337 UIC 0 : elog(ERROR, "invalid jsonb value type %d", jb1->type);
2338 EUB : }
2339 :
2340 GIC 3381 : switch (op)
2341 : {
2342 CBC 843 : case jpiEqual:
2343 GIC 843 : res = (cmp == 0);
2344 CBC 843 : break;
2345 3 : case jpiNotEqual:
2346 3 : res = (cmp != 0);
2347 3 : break;
2348 708 : case jpiLess:
2349 708 : res = (cmp < 0);
2350 708 : break;
2351 717 : case jpiGreater:
2352 717 : res = (cmp > 0);
2353 717 : break;
2354 438 : case jpiLessOrEqual:
2355 438 : res = (cmp <= 0);
2356 438 : break;
2357 672 : case jpiGreaterOrEqual:
2358 672 : res = (cmp >= 0);
2359 672 : break;
2360 LBC 0 : default:
2361 0 : elog(ERROR, "unrecognized jsonpath operation: %d", op);
2362 EUB : return jpbUnknown;
2363 : }
2364 :
2365 GIC 3381 : return res ? jpbTrue : jpbFalse;
2366 : }
2367 ECB :
2368 : /* Compare two numerics */
2369 : static int
2370 GIC 582 : compareNumeric(Numeric a, Numeric b)
2371 : {
2372 CBC 582 : return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2373 : NumericGetDatum(a),
2374 ECB : NumericGetDatum(b)));
2375 : }
2376 :
2377 : static JsonbValue *
2378 GIC 55389 : copyJsonbValue(JsonbValue *src)
2379 : {
2380 CBC 55389 : JsonbValue *dst = palloc(sizeof(*dst));
2381 :
2382 55389 : *dst = *src;
2383 :
2384 55389 : return dst;
2385 : }
2386 ECB :
2387 : /*
2388 : * Execute array subscript expression and convert resulting numeric item to
2389 : * the integer type with truncation.
2390 : */
2391 : static JsonPathExecResult
2392 GIC 159 : getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2393 : int32 *index)
2394 ECB : {
2395 : JsonbValue *jbv;
2396 GIC 159 : JsonValueList found = {0};
2397 159 : JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2398 ECB : Datum numeric_index;
2399 CBC 156 : bool have_error = false;
2400 :
2401 156 : if (jperIsError(res))
2402 UIC 0 : return res;
2403 ECB :
2404 GBC 306 : if (JsonValueListLength(&found) != 1 ||
2405 GIC 150 : !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2406 CBC 12 : RETURN_ERROR(ereport(ERROR,
2407 ECB : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2408 : errmsg("jsonpath array subscript is not a single numeric value"))));
2409 :
2410 GIC 144 : numeric_index = DirectFunctionCall2(numeric_trunc,
2411 : NumericGetDatum(jbv->val.numeric),
2412 ECB : Int32GetDatum(0));
2413 :
2414 GIC 144 : *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
2415 : &have_error);
2416 ECB :
2417 GIC 144 : if (have_error)
2418 12 : RETURN_ERROR(ereport(ERROR,
2419 ECB : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2420 : errmsg("jsonpath array subscript is out of integer range"))));
2421 :
2422 GIC 132 : return jperOk;
2423 : }
2424 ECB :
2425 : /* Save base object and its id needed for the execution of .keyvalue(). */
2426 : static JsonBaseObjectInfo
2427 GIC 103845 : setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2428 : {
2429 CBC 103845 : JsonBaseObjectInfo baseObject = cxt->baseObject;
2430 :
2431 103845 : cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2432 : (JsonbContainer *) jbv->val.binary.data;
2433 103845 : cxt->baseObject.id = id;
2434 :
2435 103845 : return baseObject;
2436 : }
2437 ECB :
2438 : static void
2439 GIC 124734 : JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2440 : {
2441 CBC 124734 : if (jvl->singleton)
2442 : {
2443 492 : jvl->list = list_make2(jvl->singleton, jbv);
2444 GIC 492 : jvl->singleton = NULL;
2445 ECB : }
2446 CBC 124242 : else if (!jvl->list)
2447 GIC 123792 : jvl->singleton = jbv;
2448 ECB : else
2449 CBC 450 : jvl->list = lappend(jvl->list, jbv);
2450 GIC 124734 : }
2451 ECB :
2452 : static int
2453 GIC 51585 : JsonValueListLength(const JsonValueList *jvl)
2454 : {
2455 CBC 51585 : return jvl->singleton ? 1 : list_length(jvl->list);
2456 : }
2457 ECB :
2458 : static bool
2459 GIC 9 : JsonValueListIsEmpty(JsonValueList *jvl)
2460 : {
2461 GNC 9 : return !jvl->singleton && (jvl->list == NIL);
2462 : }
2463 ECB :
2464 : static JsonbValue *
2465 GIC 51510 : JsonValueListHead(JsonValueList *jvl)
2466 : {
2467 CBC 51510 : return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2468 : }
2469 ECB :
2470 : static List *
2471 GIC 708 : JsonValueListGetList(JsonValueList *jvl)
2472 : {
2473 CBC 708 : if (jvl->singleton)
2474 GIC 417 : return list_make1(jvl->singleton);
2475 ECB :
2476 CBC 291 : return jvl->list;
2477 : }
2478 ECB :
2479 : static void
2480 GIC 93621 : JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2481 : {
2482 CBC 93621 : if (jvl->singleton)
2483 : {
2484 54891 : it->value = jvl->singleton;
2485 GIC 54891 : it->list = NIL;
2486 CBC 54891 : it->next = NULL;
2487 ECB : }
2488 CBC 38730 : else if (jvl->list != NIL)
2489 : {
2490 309 : it->value = (JsonbValue *) linitial(jvl->list);
2491 GIC 309 : it->list = jvl->list;
2492 CBC 309 : it->next = list_second_cell(jvl->list);
2493 ECB : }
2494 : else
2495 : {
2496 GIC 38421 : it->value = NULL;
2497 38421 : it->list = NIL;
2498 CBC 38421 : it->next = NULL;
2499 ECB : }
2500 CBC 93621 : }
2501 :
2502 ECB : /*
2503 : * Get the next item from the sequence advancing iterator.
2504 : */
2505 : static JsonbValue *
2506 GIC 144177 : JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2507 : {
2508 CBC 144177 : JsonbValue *result = it->value;
2509 :
2510 144177 : if (it->next)
2511 : {
2512 534 : it->value = lfirst(it->next);
2513 GIC 534 : it->next = lnext(it->list, it->next);
2514 ECB : }
2515 : else
2516 : {
2517 GIC 143643 : it->value = NULL;
2518 : }
2519 ECB :
2520 GIC 144177 : return result;
2521 : }
2522 ECB :
2523 : /*
2524 : * Initialize a binary JsonbValue with the given jsonb container.
2525 : */
2526 : static JsonbValue *
2527 GIC 97401 : JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2528 : {
2529 CBC 97401 : jbv->type = jbvBinary;
2530 GIC 97401 : jbv->val.binary.data = &jb->root;
2531 CBC 97401 : jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2532 ECB :
2533 CBC 97401 : return jbv;
2534 : }
2535 ECB :
2536 : /*
2537 : * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
2538 : */
2539 : static int
2540 GIC 130671 : JsonbType(JsonbValue *jb)
2541 : {
2542 CBC 130671 : int type = jb->type;
2543 :
2544 130671 : if (jb->type == jbvBinary)
2545 : {
2546 89724 : JsonbContainer *jbc = (void *) jb->val.binary.data;
2547 :
2548 ECB : /* Scalars should be always extracted during jsonpath execution. */
2549 GIC 89724 : Assert(!JsonContainerIsScalar(jbc));
2550 :
2551 CBC 89724 : if (JsonContainerIsObject(jbc))
2552 GIC 88902 : type = jbvObject;
2553 CBC 822 : else if (JsonContainerIsArray(jbc))
2554 822 : type = jbvArray;
2555 ECB : else
2556 LBC 0 : elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2557 : }
2558 EUB :
2559 GIC 130671 : return type;
2560 : }
2561 ECB :
2562 : /* Get scalar of given type or NULL on type mismatch */
2563 : static JsonbValue *
2564 GIC 2580 : getScalar(JsonbValue *scalar, enum jbvType type)
2565 : {
2566 ECB : /* Scalars should be always extracted during jsonpath execution. */
2567 GIC 2580 : Assert(scalar->type != jbvBinary ||
2568 : !JsonContainerIsScalar(scalar->val.binary.data));
2569 ECB :
2570 GIC 2580 : return scalar->type == type ? scalar : NULL;
2571 : }
2572 ECB :
2573 : /* Construct a JSON array from the item list */
2574 : static JsonbValue *
2575 GIC 21 : wrapItemsInArray(const JsonValueList *items)
2576 : {
2577 CBC 21 : JsonbParseState *ps = NULL;
2578 : JsonValueListIterator it;
2579 ECB : JsonbValue *jbv;
2580 :
2581 GIC 21 : pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2582 :
2583 CBC 21 : JsonValueListInitIterator(items, &it);
2584 GIC 42 : while ((jbv = JsonValueListNext(items, &it)))
2585 CBC 21 : pushJsonbValue(&ps, WJB_ELEM, jbv);
2586 ECB :
2587 CBC 21 : return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
2588 : }
2589 ECB :
2590 : /* Check if the timezone required for casting from type1 to type2 is used */
2591 : static void
2592 GIC 189 : checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
2593 : {
2594 CBC 189 : if (!useTz)
2595 GIC 45 : ereport(ERROR,
2596 ECB : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2597 : errmsg("cannot convert value from %s to %s without time zone usage",
2598 : type1, type2),
2599 : errhint("Use *_tz() function for time zone support.")));
2600 GIC 144 : }
2601 :
2602 ECB : /* Convert time datum to timetz datum */
2603 : static Datum
2604 GIC 72 : castTimeToTimeTz(Datum time, bool useTz)
2605 : {
2606 CBC 72 : checkTimezoneIsUsedForCast(useTz, "time", "timetz");
2607 :
2608 54 : return DirectFunctionCall1(time_timetz, time);
2609 : }
2610 ECB :
2611 : /*
2612 : * Compare date to timestamp.
2613 : * Note that this doesn't involve any timezone considerations.
2614 : */
2615 : static int
2616 GIC 57 : cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
2617 : {
2618 CBC 57 : return date_cmp_timestamp_internal(date1, ts2);
2619 : }
2620 ECB :
2621 : /*
2622 : * Compare date to timestamptz.
2623 : */
2624 : static int
2625 GIC 45 : cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
2626 : {
2627 CBC 45 : checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
2628 :
2629 36 : return date_cmp_timestamptz_internal(date1, tstz2);
2630 : }
2631 ECB :
2632 : /*
2633 : * Compare timestamp to timestamptz.
2634 : */
2635 : static int
2636 GIC 72 : cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
2637 : {
2638 CBC 72 : checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
2639 :
2640 54 : return timestamp_cmp_timestamptz_internal(ts1, tstz2);
2641 : }
2642 ECB :
2643 : /*
2644 : * Cross-type comparison of two datetime SQL/JSON items. If items are
2645 : * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
2646 : * If the cast requires timezone and it is not used, then explicit error is thrown.
2647 : */
2648 : static int
2649 GIC 714 : compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
2650 : bool useTz, bool *cast_error)
2651 ECB : {
2652 : PGFunction cmpfunc;
2653 :
2654 GIC 714 : *cast_error = false;
2655 :
2656 CBC 714 : switch (typid1)
2657 : {
2658 111 : case DATEOID:
2659 : switch (typid2)
2660 ECB : {
2661 GIC 54 : case DATEOID:
2662 54 : cmpfunc = date_cmp;
2663 ECB :
2664 CBC 54 : break;
2665 :
2666 21 : case TIMESTAMPOID:
2667 GIC 21 : return cmpDateToTimestamp(DatumGetDateADT(val1),
2668 ECB : DatumGetTimestamp(val2),
2669 : useTz);
2670 :
2671 GIC 18 : case TIMESTAMPTZOID:
2672 18 : return cmpDateToTimestampTz(DatumGetDateADT(val1),
2673 ECB : DatumGetTimestampTz(val2),
2674 : useTz);
2675 :
2676 GIC 18 : case TIMEOID:
2677 : case TIMETZOID:
2678 CBC 18 : *cast_error = true; /* uncomparable types */
2679 GIC 18 : return 0;
2680 ECB :
2681 LBC 0 : default:
2682 UIC 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2683 EUB : typid2);
2684 : }
2685 GIC 54 : break;
2686 :
2687 CBC 126 : case TIMEOID:
2688 : switch (typid2)
2689 ECB : {
2690 GIC 54 : case TIMEOID:
2691 54 : cmpfunc = time_cmp;
2692 ECB :
2693 CBC 54 : break;
2694 :
2695 36 : case TIMETZOID:
2696 GIC 36 : val1 = castTimeToTimeTz(val1, useTz);
2697 CBC 27 : cmpfunc = timetz_cmp;
2698 ECB :
2699 CBC 27 : break;
2700 :
2701 36 : case DATEOID:
2702 : case TIMESTAMPOID:
2703 ECB : case TIMESTAMPTZOID:
2704 GIC 36 : *cast_error = true; /* uncomparable types */
2705 36 : return 0;
2706 ECB :
2707 LBC 0 : default:
2708 UIC 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2709 EUB : typid2);
2710 : }
2711 GIC 81 : break;
2712 :
2713 CBC 162 : case TIMETZOID:
2714 : switch (typid2)
2715 ECB : {
2716 GIC 36 : case TIMEOID:
2717 36 : val2 = castTimeToTimeTz(val2, useTz);
2718 CBC 27 : cmpfunc = timetz_cmp;
2719 ECB :
2720 CBC 27 : break;
2721 :
2722 90 : case TIMETZOID:
2723 GIC 90 : cmpfunc = timetz_cmp;
2724 ECB :
2725 CBC 90 : break;
2726 :
2727 36 : case DATEOID:
2728 : case TIMESTAMPOID:
2729 ECB : case TIMESTAMPTZOID:
2730 GIC 36 : *cast_error = true; /* uncomparable types */
2731 36 : return 0;
2732 ECB :
2733 LBC 0 : default:
2734 UIC 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2735 EUB : typid2);
2736 : }
2737 GIC 117 : break;
2738 :
2739 CBC 144 : case TIMESTAMPOID:
2740 : switch (typid2)
2741 ECB : {
2742 GIC 36 : case DATEOID:
2743 36 : return -cmpDateToTimestamp(DatumGetDateADT(val2),
2744 ECB : DatumGetTimestamp(val1),
2745 : useTz);
2746 :
2747 GIC 54 : case TIMESTAMPOID:
2748 54 : cmpfunc = timestamp_cmp;
2749 ECB :
2750 CBC 54 : break;
2751 :
2752 36 : case TIMESTAMPTZOID:
2753 GIC 36 : return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
2754 ECB : DatumGetTimestampTz(val2),
2755 : useTz);
2756 :
2757 GIC 18 : case TIMEOID:
2758 : case TIMETZOID:
2759 CBC 18 : *cast_error = true; /* uncomparable types */
2760 GIC 18 : return 0;
2761 ECB :
2762 LBC 0 : default:
2763 UIC 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2764 EUB : typid2);
2765 : }
2766 GIC 54 : break;
2767 :
2768 CBC 171 : case TIMESTAMPTZOID:
2769 : switch (typid2)
2770 ECB : {
2771 GIC 27 : case DATEOID:
2772 27 : return -cmpDateToTimestampTz(DatumGetDateADT(val2),
2773 ECB : DatumGetTimestampTz(val1),
2774 : useTz);
2775 :
2776 GIC 36 : case TIMESTAMPOID:
2777 36 : return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
2778 ECB : DatumGetTimestampTz(val1),
2779 : useTz);
2780 :
2781 GIC 90 : case TIMESTAMPTZOID:
2782 90 : cmpfunc = timestamp_cmp;
2783 ECB :
2784 CBC 90 : break;
2785 :
2786 18 : case TIMEOID:
2787 : case TIMETZOID:
2788 18 : *cast_error = true; /* uncomparable types */
2789 GIC 18 : return 0;
2790 ECB :
2791 LBC 0 : default:
2792 UIC 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2793 EUB : typid2);
2794 : }
2795 GIC 90 : break;
2796 :
2797 LBC 0 : default:
2798 UIC 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
2799 EUB : }
2800 :
2801 GIC 396 : if (*cast_error)
2802 UIC 0 : return 0; /* cast error */
2803 ECB :
2804 GBC 396 : return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
2805 : }
|