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