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-2020, 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 less 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 =
846 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
847 Int32GetDatum(last)));
848
849 res = executeNextItem(cxt, jsp, &elem,
850 lastjbv, found, hasNext);
851 }
852 break;
853
854 case jpiAnyKey:
855 if (JsonbType(jb) == jbvObject)
856 {
857 bool hasNext = jspGetNext(jsp, &elem);
858
859 if (jb->type != jbvBinary)
860 elog(ERROR, "invalid jsonb object type: %d", jb->type);
861
862 return executeAnyItem
863 (cxt, hasNext ? &elem : NULL,
864 jb->val.binary.data, found, 1, 1, 1,
865 false, jspAutoUnwrap(cxt));
866 }
867 else if (unwrap && JsonbType(jb) == jbvArray)
868 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
869 else if (!jspIgnoreStructuralErrors(cxt))
870 {
871 Assert(found);
872 RETURN_ERROR(ereport(ERROR,
873 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
874 errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
875 }
876 break;
877
878 case jpiAdd:
879 return executeBinaryArithmExpr(cxt, jsp, jb,
880 numeric_add_opt_error, found);
881
882 case jpiSub:
883 return executeBinaryArithmExpr(cxt, jsp, jb,
884 numeric_sub_opt_error, found);
885
886 case jpiMul:
887 return executeBinaryArithmExpr(cxt, jsp, jb,
888 numeric_mul_opt_error, found);
889
890 case jpiDiv:
891 return executeBinaryArithmExpr(cxt, jsp, jb,
892 numeric_div_opt_error, found);
893
894 case jpiMod:
895 return executeBinaryArithmExpr(cxt, jsp, jb,
896 numeric_mod_opt_error, found);
897
898 case jpiPlus:
899 return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
900
901 case jpiMinus:
902 return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
903 found);
904
905 case jpiFilter:
906 {
907 JsonPathBool st;
908
909 if (unwrap && JsonbType(jb) == jbvArray)
910 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
911 false);
912
913 jspGetArg(jsp, &elem);
914 st = executeNestedBoolItem(cxt, &elem, jb);
915 if (st != jpbTrue)
916 res = jperNotFound;
917 else
918 res = executeNextItem(cxt, jsp, NULL,
919 jb, found, true);
920 break;
921 }
922
923 case jpiAny:
924 {
925 bool hasNext = jspGetNext(jsp, &elem);
926
927 /* first try without any intermediate steps */
928 if (jsp->content.anybounds.first == 0)
929 {
930 bool savedIgnoreStructuralErrors;
931
932 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
933 cxt->ignoreStructuralErrors = true;
934 res = executeNextItem(cxt, jsp, &elem,
935 jb, found, true);
936 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
937
938 if (res == jperOk && !found)
939 break;
940 }
941
942 if (jb->type == jbvBinary)
943 res = executeAnyItem
944 (cxt, hasNext ? &elem : NULL,
945 jb->val.binary.data, found,
946 1,
947 jsp->content.anybounds.first,
948 jsp->content.anybounds.last,
949 true, jspAutoUnwrap(cxt));
950 break;
951 }
952
953 case jpiNull:
954 case jpiBool:
955 case jpiNumeric:
956 case jpiString:
957 case jpiVariable:
958 {
959 JsonbValue vbuf;
960 JsonbValue *v;
961 bool hasNext = jspGetNext(jsp, &elem);
962
963 if (!hasNext && !found)
964 {
965 res = jperOk; /* skip evaluation */
966 break;
967 }
968
969 v = hasNext ? &vbuf : palloc(sizeof(*v));
970
971 baseObject = cxt->baseObject;
972 getJsonPathItem(cxt, jsp, v);
973
974 res = executeNextItem(cxt, jsp, &elem,
975 v, found, hasNext);
976 cxt->baseObject = baseObject;
977 }
978 break;
979
980 case jpiType:
981 {
982 JsonbValue *jbv = palloc(sizeof(*jbv));
983
984 jbv->type = jbvString;
985 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
986 jbv->val.string.len = strlen(jbv->val.string.val);
987
988 res = executeNextItem(cxt, jsp, NULL, jbv,
989 found, false);
990 }
991 break;
992
993 case jpiSize:
994 {
995 int size = JsonbArraySize(jb);
996
997 if (size < 0)
998 {
999 if (!jspAutoWrap(cxt))
1000 {
1001 if (!jspIgnoreStructuralErrors(cxt))
1002 RETURN_ERROR(ereport(ERROR,
1003 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
1004 errmsg("jsonpath item method .%s() can only be applied to an array",
1005 jspOperationName(jsp->type)))));
1006 break;
1007 }
1008
1009 size = 1;
1010 }
1011
1012 jb = palloc(sizeof(*jb));
1013
1014 jb->type = jbvNumeric;
1015 jb->val.numeric =
1016 DatumGetNumeric(DirectFunctionCall1(int4_numeric,
1017 Int32GetDatum(size)));
1018
1019 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
1020 }
1021 break;
1022
1023 case jpiAbs:
1024 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
1025 found);
1026
1027 case jpiFloor:
1028 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
1029 found);
1030
1031 case jpiCeiling:
1032 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
1033 found);
1034
1035 case jpiDouble:
1036 {
1037 JsonbValue jbv;
1038
1039 if (unwrap && JsonbType(jb) == jbvArray)
1040 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1041 false);
1042
1043 if (jb->type == jbvNumeric)
1044 {
1045 char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1046 NumericGetDatum(jb->val.numeric)));
1047 double val;
1048 bool have_error = false;
1049
1050 val = float8in_internal_opt_error(tmp,
1051 NULL,
1052 "double precision",
1053 tmp,
1054 &have_error);
1055
1056 if (have_error || isinf(val) || isnan(val))
1057 RETURN_ERROR(ereport(ERROR,
1058 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1059 errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
1060 jspOperationName(jsp->type)))));
1061 res = jperOk;
1062 }
1063 else if (jb->type == jbvString)
1064 {
1065 /* cast string as double */
1066 double val;
1067 char *tmp = pnstrdup(jb->val.string.val,
1068 jb->val.string.len);
1069 bool have_error = false;
1070
1071 val = float8in_internal_opt_error(tmp,
1072 NULL,
1073 "double precision",
1074 tmp,
1075 &have_error);
1076
1077 if (have_error || isinf(val) || isnan(val))
1078 RETURN_ERROR(ereport(ERROR,
1079 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1080 errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
1081 jspOperationName(jsp->type)))));
1082
1083 jb = &jbv;
1084 jb->type = jbvNumeric;
1085 jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1086 Float8GetDatum(val)));
1087 res = jperOk;
1088 }
1089
1090 if (res == jperNotFound)
1091 RETURN_ERROR(ereport(ERROR,
1092 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1093 errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1094 jspOperationName(jsp->type)))));
1095
1096 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1097 }
1098 break;
1099
1100 case jpiDatetime:
1101 if (unwrap && JsonbType(jb) == jbvArray)
1102 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1103
1104 return executeDateTimeMethod(cxt, jsp, jb, found);
1105
1106 case jpiKeyValue:
1107 if (unwrap && JsonbType(jb) == jbvArray)
1108 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1109
1110 return executeKeyValueMethod(cxt, jsp, jb, found);
1111
1112 default:
1113 elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1114 }
1115
1116 return res;
1117 }
1118
1119 /*
1120 * Unwrap current array item and execute jsonpath for each of its elements.
1121 */
1122 static JsonPathExecResult
executeItemUnwrapTargetArray(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,JsonValueList * found,bool unwrapElements)1123 executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1124 JsonbValue *jb, JsonValueList *found,
1125 bool unwrapElements)
1126 {
1127 if (jb->type != jbvBinary)
1128 {
1129 Assert(jb->type != jbvArray);
1130 elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1131 }
1132
1133 return executeAnyItem
1134 (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1135 false, unwrapElements);
1136 }
1137
1138 /*
1139 * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1140 * list if provided.
1141 */
1142 static JsonPathExecResult
executeNextItem(JsonPathExecContext * cxt,JsonPathItem * cur,JsonPathItem * next,JsonbValue * v,JsonValueList * found,bool copy)1143 executeNextItem(JsonPathExecContext *cxt,
1144 JsonPathItem *cur, JsonPathItem *next,
1145 JsonbValue *v, JsonValueList *found, bool copy)
1146 {
1147 JsonPathItem elem;
1148 bool hasNext;
1149
1150 if (!cur)
1151 hasNext = next != NULL;
1152 else if (next)
1153 hasNext = jspHasNext(cur);
1154 else
1155 {
1156 next = &elem;
1157 hasNext = jspGetNext(cur, next);
1158 }
1159
1160 if (hasNext)
1161 return executeItem(cxt, next, v, found);
1162
1163 if (found)
1164 JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1165
1166 return jperOk;
1167 }
1168
1169 /*
1170 * Same as executeItem(), but when "unwrap == true" automatically unwraps
1171 * each array item from the resulting sequence in lax mode.
1172 */
1173 static JsonPathExecResult
executeItemOptUnwrapResult(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,bool unwrap,JsonValueList * found)1174 executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1175 JsonbValue *jb, bool unwrap,
1176 JsonValueList *found)
1177 {
1178 if (unwrap && jspAutoUnwrap(cxt))
1179 {
1180 JsonValueList seq = {0};
1181 JsonValueListIterator it;
1182 JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1183 JsonbValue *item;
1184
1185 if (jperIsError(res))
1186 return res;
1187
1188 JsonValueListInitIterator(&seq, &it);
1189 while ((item = JsonValueListNext(&seq, &it)))
1190 {
1191 Assert(item->type != jbvArray);
1192
1193 if (JsonbType(item) == jbvArray)
1194 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1195 else
1196 JsonValueListAppend(found, item);
1197 }
1198
1199 return jperOk;
1200 }
1201
1202 return executeItem(cxt, jsp, jb, found);
1203 }
1204
1205 /*
1206 * Same as executeItemOptUnwrapResult(), but with error suppression.
1207 */
1208 static JsonPathExecResult
executeItemOptUnwrapResultNoThrow(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,bool unwrap,JsonValueList * found)1209 executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1210 JsonPathItem *jsp,
1211 JsonbValue *jb, bool unwrap,
1212 JsonValueList *found)
1213 {
1214 JsonPathExecResult res;
1215 bool throwErrors = cxt->throwErrors;
1216
1217 cxt->throwErrors = false;
1218 res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1219 cxt->throwErrors = throwErrors;
1220
1221 return res;
1222 }
1223
1224 /* Execute boolean-valued jsonpath expression. */
1225 static JsonPathBool
executeBoolItem(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,bool canHaveNext)1226 executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1227 JsonbValue *jb, bool canHaveNext)
1228 {
1229 JsonPathItem larg;
1230 JsonPathItem rarg;
1231 JsonPathBool res;
1232 JsonPathBool res2;
1233
1234 if (!canHaveNext && jspHasNext(jsp))
1235 elog(ERROR, "boolean jsonpath item cannot have next item");
1236
1237 switch (jsp->type)
1238 {
1239 case jpiAnd:
1240 jspGetLeftArg(jsp, &larg);
1241 res = executeBoolItem(cxt, &larg, jb, false);
1242
1243 if (res == jpbFalse)
1244 return jpbFalse;
1245
1246 /*
1247 * SQL/JSON says that we should check second arg in case of
1248 * jperError
1249 */
1250
1251 jspGetRightArg(jsp, &rarg);
1252 res2 = executeBoolItem(cxt, &rarg, jb, false);
1253
1254 return res2 == jpbTrue ? res : res2;
1255
1256 case jpiOr:
1257 jspGetLeftArg(jsp, &larg);
1258 res = executeBoolItem(cxt, &larg, jb, false);
1259
1260 if (res == jpbTrue)
1261 return jpbTrue;
1262
1263 jspGetRightArg(jsp, &rarg);
1264 res2 = executeBoolItem(cxt, &rarg, jb, false);
1265
1266 return res2 == jpbFalse ? res : res2;
1267
1268 case jpiNot:
1269 jspGetArg(jsp, &larg);
1270
1271 res = executeBoolItem(cxt, &larg, jb, false);
1272
1273 if (res == jpbUnknown)
1274 return jpbUnknown;
1275
1276 return res == jpbTrue ? jpbFalse : jpbTrue;
1277
1278 case jpiIsUnknown:
1279 jspGetArg(jsp, &larg);
1280 res = executeBoolItem(cxt, &larg, jb, false);
1281 return res == jpbUnknown ? jpbTrue : jpbFalse;
1282
1283 case jpiEqual:
1284 case jpiNotEqual:
1285 case jpiLess:
1286 case jpiGreater:
1287 case jpiLessOrEqual:
1288 case jpiGreaterOrEqual:
1289 jspGetLeftArg(jsp, &larg);
1290 jspGetRightArg(jsp, &rarg);
1291 return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1292 executeComparison, cxt);
1293
1294 case jpiStartsWith: /* 'whole STARTS WITH initial' */
1295 jspGetLeftArg(jsp, &larg); /* 'whole' */
1296 jspGetRightArg(jsp, &rarg); /* 'initial' */
1297 return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1298 executeStartsWith, NULL);
1299
1300 case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1301 {
1302 /*
1303 * 'expr' is a sequence-returning expression. 'pattern' is a
1304 * regex string literal. SQL/JSON standard requires XQuery
1305 * regexes, but we use Postgres regexes here. 'flags' is a
1306 * string literal converted to integer flags at compile-time.
1307 */
1308 JsonLikeRegexContext lrcxt = {0};
1309
1310 jspInitByBuffer(&larg, jsp->base,
1311 jsp->content.like_regex.expr);
1312
1313 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1314 executeLikeRegex, &lrcxt);
1315 }
1316
1317 case jpiExists:
1318 jspGetArg(jsp, &larg);
1319
1320 if (jspStrictAbsenseOfErrors(cxt))
1321 {
1322 /*
1323 * In strict mode we must get a complete list of values to
1324 * check that there are no errors at all.
1325 */
1326 JsonValueList vals = {0};
1327 JsonPathExecResult res =
1328 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1329 false, &vals);
1330
1331 if (jperIsError(res))
1332 return jpbUnknown;
1333
1334 return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1335 }
1336 else
1337 {
1338 JsonPathExecResult res =
1339 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1340 false, NULL);
1341
1342 if (jperIsError(res))
1343 return jpbUnknown;
1344
1345 return res == jperOk ? jpbTrue : jpbFalse;
1346 }
1347
1348 default:
1349 elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1350 return jpbUnknown;
1351 }
1352 }
1353
1354 /*
1355 * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1356 * item onto the stack.
1357 */
1358 static JsonPathBool
executeNestedBoolItem(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb)1359 executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1360 JsonbValue *jb)
1361 {
1362 JsonbValue *prev;
1363 JsonPathBool res;
1364
1365 prev = cxt->current;
1366 cxt->current = jb;
1367 res = executeBoolItem(cxt, jsp, jb, false);
1368 cxt->current = prev;
1369
1370 return res;
1371 }
1372
1373 /*
1374 * Implementation of several jsonpath nodes:
1375 * - jpiAny (.** accessor),
1376 * - jpiAnyKey (.* accessor),
1377 * - jpiAnyArray ([*] accessor)
1378 */
1379 static JsonPathExecResult
executeAnyItem(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbContainer * jbc,JsonValueList * found,uint32 level,uint32 first,uint32 last,bool ignoreStructuralErrors,bool unwrapNext)1380 executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1381 JsonValueList *found, uint32 level, uint32 first, uint32 last,
1382 bool ignoreStructuralErrors, bool unwrapNext)
1383 {
1384 JsonPathExecResult res = jperNotFound;
1385 JsonbIterator *it;
1386 int32 r;
1387 JsonbValue v;
1388
1389 check_stack_depth();
1390
1391 if (level > last)
1392 return res;
1393
1394 it = JsonbIteratorInit(jbc);
1395
1396 /*
1397 * Recursively iterate over jsonb objects/arrays
1398 */
1399 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1400 {
1401 if (r == WJB_KEY)
1402 {
1403 r = JsonbIteratorNext(&it, &v, true);
1404 Assert(r == WJB_VALUE);
1405 }
1406
1407 if (r == WJB_VALUE || r == WJB_ELEM)
1408 {
1409
1410 if (level >= first ||
1411 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1412 v.type != jbvBinary)) /* leaves only requested */
1413 {
1414 /* check expression */
1415 if (jsp)
1416 {
1417 if (ignoreStructuralErrors)
1418 {
1419 bool savedIgnoreStructuralErrors;
1420
1421 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1422 cxt->ignoreStructuralErrors = true;
1423 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1424 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1425 }
1426 else
1427 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1428
1429 if (jperIsError(res))
1430 break;
1431
1432 if (res == jperOk && !found)
1433 break;
1434 }
1435 else if (found)
1436 JsonValueListAppend(found, copyJsonbValue(&v));
1437 else
1438 return jperOk;
1439 }
1440
1441 if (level < last && v.type == jbvBinary)
1442 {
1443 res = executeAnyItem
1444 (cxt, jsp, v.val.binary.data, found,
1445 level + 1, first, last,
1446 ignoreStructuralErrors, unwrapNext);
1447
1448 if (jperIsError(res))
1449 break;
1450
1451 if (res == jperOk && found == NULL)
1452 break;
1453 }
1454 }
1455 }
1456
1457 return res;
1458 }
1459
1460 /*
1461 * Execute unary or binary predicate.
1462 *
1463 * Predicates have existence semantics, because their operands are item
1464 * sequences. Pairs of items from the left and right operand's sequences are
1465 * checked. TRUE returned only if any pair satisfying the condition is found.
1466 * In strict mode, even if the desired pair has already been found, all pairs
1467 * still need to be examined to check the absence of errors. If any error
1468 * occurs, UNKNOWN (analogous to SQL NULL) is returned.
1469 */
1470 static JsonPathBool
executePredicate(JsonPathExecContext * cxt,JsonPathItem * pred,JsonPathItem * larg,JsonPathItem * rarg,JsonbValue * jb,bool unwrapRightArg,JsonPathPredicateCallback exec,void * param)1471 executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
1472 JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
1473 bool unwrapRightArg, JsonPathPredicateCallback exec,
1474 void *param)
1475 {
1476 JsonPathExecResult res;
1477 JsonValueListIterator lseqit;
1478 JsonValueList lseq = {0};
1479 JsonValueList rseq = {0};
1480 JsonbValue *lval;
1481 bool error = false;
1482 bool found = false;
1483
1484 /* Left argument is always auto-unwrapped. */
1485 res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
1486 if (jperIsError(res))
1487 return jpbUnknown;
1488
1489 if (rarg)
1490 {
1491 /* Right argument is conditionally auto-unwrapped. */
1492 res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
1493 unwrapRightArg, &rseq);
1494 if (jperIsError(res))
1495 return jpbUnknown;
1496 }
1497
1498 JsonValueListInitIterator(&lseq, &lseqit);
1499 while ((lval = JsonValueListNext(&lseq, &lseqit)))
1500 {
1501 JsonValueListIterator rseqit;
1502 JsonbValue *rval;
1503 bool first = true;
1504
1505 JsonValueListInitIterator(&rseq, &rseqit);
1506 if (rarg)
1507 rval = JsonValueListNext(&rseq, &rseqit);
1508 else
1509 rval = NULL;
1510
1511 /* Loop over right arg sequence or do single pass otherwise */
1512 while (rarg ? (rval != NULL) : first)
1513 {
1514 JsonPathBool res = exec(pred, lval, rval, param);
1515
1516 if (res == jpbUnknown)
1517 {
1518 if (jspStrictAbsenseOfErrors(cxt))
1519 return jpbUnknown;
1520
1521 error = true;
1522 }
1523 else if (res == jpbTrue)
1524 {
1525 if (!jspStrictAbsenseOfErrors(cxt))
1526 return jpbTrue;
1527
1528 found = true;
1529 }
1530
1531 first = false;
1532 if (rarg)
1533 rval = JsonValueListNext(&rseq, &rseqit);
1534 }
1535 }
1536
1537 if (found) /* possible only in strict mode */
1538 return jpbTrue;
1539
1540 if (error) /* possible only in lax mode */
1541 return jpbUnknown;
1542
1543 return jpbFalse;
1544 }
1545
1546 /*
1547 * Execute binary arithmetic expression on singleton numeric operands.
1548 * Array operands are automatically unwrapped in lax mode.
1549 */
1550 static JsonPathExecResult
executeBinaryArithmExpr(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,BinaryArithmFunc func,JsonValueList * found)1551 executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1552 JsonbValue *jb, BinaryArithmFunc func,
1553 JsonValueList *found)
1554 {
1555 JsonPathExecResult jper;
1556 JsonPathItem elem;
1557 JsonValueList lseq = {0};
1558 JsonValueList rseq = {0};
1559 JsonbValue *lval;
1560 JsonbValue *rval;
1561 Numeric res;
1562
1563 jspGetLeftArg(jsp, &elem);
1564
1565 /*
1566 * XXX: By standard only operands of multiplicative expressions are
1567 * unwrapped. We extend it to other binary arithmetic expressions too.
1568 */
1569 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
1570 if (jperIsError(jper))
1571 return jper;
1572
1573 jspGetRightArg(jsp, &elem);
1574
1575 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
1576 if (jperIsError(jper))
1577 return jper;
1578
1579 if (JsonValueListLength(&lseq) != 1 ||
1580 !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
1581 RETURN_ERROR(ereport(ERROR,
1582 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1583 errmsg("left operand of jsonpath operator %s is not a single numeric value",
1584 jspOperationName(jsp->type)))));
1585
1586 if (JsonValueListLength(&rseq) != 1 ||
1587 !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
1588 RETURN_ERROR(ereport(ERROR,
1589 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1590 errmsg("right operand of jsonpath operator %s is not a single numeric value",
1591 jspOperationName(jsp->type)))));
1592
1593 if (jspThrowErrors(cxt))
1594 {
1595 res = func(lval->val.numeric, rval->val.numeric, NULL);
1596 }
1597 else
1598 {
1599 bool error = false;
1600
1601 res = func(lval->val.numeric, rval->val.numeric, &error);
1602
1603 if (error)
1604 return jperError;
1605 }
1606
1607 if (!jspGetNext(jsp, &elem) && !found)
1608 return jperOk;
1609
1610 lval = palloc(sizeof(*lval));
1611 lval->type = jbvNumeric;
1612 lval->val.numeric = res;
1613
1614 return executeNextItem(cxt, jsp, &elem, lval, found, false);
1615 }
1616
1617 /*
1618 * Execute unary arithmetic expression for each numeric item in its operand's
1619 * sequence. Array operand is automatically unwrapped in lax mode.
1620 */
1621 static JsonPathExecResult
executeUnaryArithmExpr(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,PGFunction func,JsonValueList * found)1622 executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1623 JsonbValue *jb, PGFunction func, JsonValueList *found)
1624 {
1625 JsonPathExecResult jper;
1626 JsonPathExecResult jper2;
1627 JsonPathItem elem;
1628 JsonValueList seq = {0};
1629 JsonValueListIterator it;
1630 JsonbValue *val;
1631 bool hasNext;
1632
1633 jspGetArg(jsp, &elem);
1634 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
1635
1636 if (jperIsError(jper))
1637 return jper;
1638
1639 jper = jperNotFound;
1640
1641 hasNext = jspGetNext(jsp, &elem);
1642
1643 JsonValueListInitIterator(&seq, &it);
1644 while ((val = JsonValueListNext(&seq, &it)))
1645 {
1646 if ((val = getScalar(val, jbvNumeric)))
1647 {
1648 if (!found && !hasNext)
1649 return jperOk;
1650 }
1651 else
1652 {
1653 if (!found && !hasNext)
1654 continue; /* skip non-numerics processing */
1655
1656 RETURN_ERROR(ereport(ERROR,
1657 (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
1658 errmsg("operand of unary jsonpath operator %s is not a numeric value",
1659 jspOperationName(jsp->type)))));
1660 }
1661
1662 if (func)
1663 val->val.numeric =
1664 DatumGetNumeric(DirectFunctionCall1(func,
1665 NumericGetDatum(val->val.numeric)));
1666
1667 jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
1668
1669 if (jperIsError(jper2))
1670 return jper2;
1671
1672 if (jper2 == jperOk)
1673 {
1674 if (!found)
1675 return jperOk;
1676 jper = jperOk;
1677 }
1678 }
1679
1680 return jper;
1681 }
1682
1683 /*
1684 * STARTS_WITH predicate callback.
1685 *
1686 * Check if the 'whole' string starts from 'initial' string.
1687 */
1688 static JsonPathBool
executeStartsWith(JsonPathItem * jsp,JsonbValue * whole,JsonbValue * initial,void * param)1689 executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
1690 void *param)
1691 {
1692 if (!(whole = getScalar(whole, jbvString)))
1693 return jpbUnknown; /* error */
1694
1695 if (!(initial = getScalar(initial, jbvString)))
1696 return jpbUnknown; /* error */
1697
1698 if (whole->val.string.len >= initial->val.string.len &&
1699 !memcmp(whole->val.string.val,
1700 initial->val.string.val,
1701 initial->val.string.len))
1702 return jpbTrue;
1703
1704 return jpbFalse;
1705 }
1706
1707 /*
1708 * LIKE_REGEX predicate callback.
1709 *
1710 * Check if the string matches regex pattern.
1711 */
1712 static JsonPathBool
executeLikeRegex(JsonPathItem * jsp,JsonbValue * str,JsonbValue * rarg,void * param)1713 executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
1714 void *param)
1715 {
1716 JsonLikeRegexContext *cxt = param;
1717
1718 if (!(str = getScalar(str, jbvString)))
1719 return jpbUnknown;
1720
1721 /* Cache regex text and converted flags. */
1722 if (!cxt->regex)
1723 {
1724 cxt->regex =
1725 cstring_to_text_with_len(jsp->content.like_regex.pattern,
1726 jsp->content.like_regex.patternlen);
1727 cxt->cflags = jspConvertRegexFlags(jsp->content.like_regex.flags);
1728 }
1729
1730 if (RE_compile_and_execute(cxt->regex, str->val.string.val,
1731 str->val.string.len,
1732 cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
1733 return jpbTrue;
1734
1735 return jpbFalse;
1736 }
1737
1738 /*
1739 * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
1740 * user function 'func'.
1741 */
1742 static JsonPathExecResult
executeNumericItemMethod(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,bool unwrap,PGFunction func,JsonValueList * found)1743 executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1744 JsonbValue *jb, bool unwrap, PGFunction func,
1745 JsonValueList *found)
1746 {
1747 JsonPathItem next;
1748 Datum datum;
1749
1750 if (unwrap && JsonbType(jb) == jbvArray)
1751 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1752
1753 if (!(jb = getScalar(jb, jbvNumeric)))
1754 RETURN_ERROR(ereport(ERROR,
1755 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1756 errmsg("jsonpath item method .%s() can only be applied to a numeric value",
1757 jspOperationName(jsp->type)))));
1758
1759 datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
1760
1761 if (!jspGetNext(jsp, &next) && !found)
1762 return jperOk;
1763
1764 jb = palloc(sizeof(*jb));
1765 jb->type = jbvNumeric;
1766 jb->val.numeric = DatumGetNumeric(datum);
1767
1768 return executeNextItem(cxt, jsp, &next, jb, found, false);
1769 }
1770
1771 /*
1772 * Implementation of the .datetime() method.
1773 *
1774 * Converts a string into a date/time value. The actual type is determined at run time.
1775 * If an argument is provided, this argument is used as a template string.
1776 * Otherwise, the first fitting ISO format is selected.
1777 */
1778 static JsonPathExecResult
executeDateTimeMethod(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,JsonValueList * found)1779 executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1780 JsonbValue *jb, JsonValueList *found)
1781 {
1782 JsonbValue jbvbuf;
1783 Datum value;
1784 text *datetime;
1785 Oid collid;
1786 Oid typid;
1787 int32 typmod = -1;
1788 int tz = 0;
1789 bool hasNext;
1790 JsonPathExecResult res = jperNotFound;
1791 JsonPathItem elem;
1792
1793 if (!(jb = getScalar(jb, jbvString)))
1794 RETURN_ERROR(ereport(ERROR,
1795 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
1796 errmsg("jsonpath item method .%s() can only be applied to a string",
1797 jspOperationName(jsp->type)))));
1798
1799 datetime = cstring_to_text_with_len(jb->val.string.val,
1800 jb->val.string.len);
1801
1802 /*
1803 * At some point we might wish to have callers supply the collation to
1804 * use, but right now it's unclear that they'd be able to do better than
1805 * DEFAULT_COLLATION_OID anyway.
1806 */
1807 collid = DEFAULT_COLLATION_OID;
1808
1809 if (jsp->content.arg)
1810 {
1811 text *template;
1812 char *template_str;
1813 int template_len;
1814 bool have_error = false;
1815
1816 jspGetArg(jsp, &elem);
1817
1818 if (elem.type != jpiString)
1819 elog(ERROR, "invalid jsonpath item type for .datetime() argument");
1820
1821 template_str = jspGetString(&elem, &template_len);
1822
1823 template = cstring_to_text_with_len(template_str,
1824 template_len);
1825
1826 value = parse_datetime(datetime, template, collid, true,
1827 &typid, &typmod, &tz,
1828 jspThrowErrors(cxt) ? NULL : &have_error);
1829
1830 if (have_error)
1831 res = jperError;
1832 else
1833 res = jperOk;
1834 }
1835 else
1836 {
1837 /*
1838 * According to SQL/JSON standard enumerate ISO formats for: date,
1839 * timetz, time, timestamptz, timestamp.
1840 *
1841 * We also support ISO 8601 for timestamps, because to_json[b]()
1842 * functions use this format.
1843 */
1844 static const char *fmt_str[] =
1845 {
1846 "yyyy-mm-dd",
1847 "HH24:MI:SSTZH:TZM",
1848 "HH24:MI:SSTZH",
1849 "HH24:MI:SS",
1850 "yyyy-mm-dd HH24:MI:SSTZH:TZM",
1851 "yyyy-mm-dd HH24:MI:SSTZH",
1852 "yyyy-mm-dd HH24:MI:SS",
1853 "yyyy-mm-dd\"T\"HH24:MI:SSTZH:TZM",
1854 "yyyy-mm-dd\"T\"HH24:MI:SSTZH",
1855 "yyyy-mm-dd\"T\"HH24:MI:SS"
1856 };
1857
1858 /* cache for format texts */
1859 static text *fmt_txt[lengthof(fmt_str)] = {0};
1860 int i;
1861
1862 /* loop until datetime format fits */
1863 for (i = 0; i < lengthof(fmt_str); i++)
1864 {
1865 bool have_error = false;
1866
1867 if (!fmt_txt[i])
1868 {
1869 MemoryContext oldcxt =
1870 MemoryContextSwitchTo(TopMemoryContext);
1871
1872 fmt_txt[i] = cstring_to_text(fmt_str[i]);
1873 MemoryContextSwitchTo(oldcxt);
1874 }
1875
1876 value = parse_datetime(datetime, fmt_txt[i], collid, true,
1877 &typid, &typmod, &tz,
1878 &have_error);
1879
1880 if (!have_error)
1881 {
1882 res = jperOk;
1883 break;
1884 }
1885 }
1886
1887 if (res == jperNotFound)
1888 RETURN_ERROR(ereport(ERROR,
1889 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
1890 errmsg("datetime format is not recognized: \"%s\"",
1891 text_to_cstring(datetime)),
1892 errhint("Use a datetime template argument to specify the input data format."))));
1893 }
1894
1895 pfree(datetime);
1896
1897 if (jperIsError(res))
1898 return res;
1899
1900 hasNext = jspGetNext(jsp, &elem);
1901
1902 if (!hasNext && !found)
1903 return res;
1904
1905 jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
1906
1907 jb->type = jbvDatetime;
1908 jb->val.datetime.value = value;
1909 jb->val.datetime.typid = typid;
1910 jb->val.datetime.typmod = typmod;
1911 jb->val.datetime.tz = tz;
1912
1913 return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
1914 }
1915
1916 /*
1917 * Implementation of .keyvalue() method.
1918 *
1919 * .keyvalue() method returns a sequence of object's key-value pairs in the
1920 * following format: '{ "key": key, "value": value, "id": id }'.
1921 *
1922 * "id" field is an object identifier which is constructed from the two parts:
1923 * base object id and its binary offset in base object's jsonb:
1924 * id = 10000000000 * base_object_id + obj_offset_in_base_object
1925 *
1926 * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1927 * (maximal offset in jsonb). Decimal multiplier is used here to improve the
1928 * readability of identifiers.
1929 *
1930 * Base object is usually a root object of the path: context item '$' or path
1931 * variable '$var', literals can't produce objects for now. But if the path
1932 * contains generated objects (.keyvalue() itself, for example), then they
1933 * become base object for the subsequent .keyvalue().
1934 *
1935 * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
1936 * of variables (see getJsonPathVariable()). Ids for generated objects
1937 * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
1938 */
1939 static JsonPathExecResult
executeKeyValueMethod(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,JsonValueList * found)1940 executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1941 JsonbValue *jb, JsonValueList *found)
1942 {
1943 JsonPathExecResult res = jperNotFound;
1944 JsonPathItem next;
1945 JsonbContainer *jbc;
1946 JsonbValue key;
1947 JsonbValue val;
1948 JsonbValue idval;
1949 JsonbValue keystr;
1950 JsonbValue valstr;
1951 JsonbValue idstr;
1952 JsonbIterator *it;
1953 JsonbIteratorToken tok;
1954 int64 id;
1955 bool hasNext;
1956
1957 if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
1958 RETURN_ERROR(ereport(ERROR,
1959 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
1960 errmsg("jsonpath item method .%s() can only be applied to an object",
1961 jspOperationName(jsp->type)))));
1962
1963 jbc = jb->val.binary.data;
1964
1965 if (!JsonContainerSize(jbc))
1966 return jperNotFound; /* no key-value pairs */
1967
1968 hasNext = jspGetNext(jsp, &next);
1969
1970 keystr.type = jbvString;
1971 keystr.val.string.val = "key";
1972 keystr.val.string.len = 3;
1973
1974 valstr.type = jbvString;
1975 valstr.val.string.val = "value";
1976 valstr.val.string.len = 5;
1977
1978 idstr.type = jbvString;
1979 idstr.val.string.val = "id";
1980 idstr.val.string.len = 2;
1981
1982 /* construct object id from its base object and offset inside that */
1983 id = jb->type != jbvBinary ? 0 :
1984 (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
1985 id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
1986
1987 idval.type = jbvNumeric;
1988 idval.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
1989 Int64GetDatum(id)));
1990
1991 it = JsonbIteratorInit(jbc);
1992
1993 while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
1994 {
1995 JsonBaseObjectInfo baseObject;
1996 JsonbValue obj;
1997 JsonbParseState *ps;
1998 JsonbValue *keyval;
1999 Jsonb *jsonb;
2000
2001 if (tok != WJB_KEY)
2002 continue;
2003
2004 res = jperOk;
2005
2006 if (!hasNext && !found)
2007 break;
2008
2009 tok = JsonbIteratorNext(&it, &val, true);
2010 Assert(tok == WJB_VALUE);
2011
2012 ps = NULL;
2013 pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
2014
2015 pushJsonbValue(&ps, WJB_KEY, &keystr);
2016 pushJsonbValue(&ps, WJB_VALUE, &key);
2017
2018 pushJsonbValue(&ps, WJB_KEY, &valstr);
2019 pushJsonbValue(&ps, WJB_VALUE, &val);
2020
2021 pushJsonbValue(&ps, WJB_KEY, &idstr);
2022 pushJsonbValue(&ps, WJB_VALUE, &idval);
2023
2024 keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2025
2026 jsonb = JsonbValueToJsonb(keyval);
2027
2028 JsonbInitBinary(&obj, jsonb);
2029
2030 baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
2031
2032 res = executeNextItem(cxt, jsp, &next, &obj, found, true);
2033
2034 cxt->baseObject = baseObject;
2035
2036 if (jperIsError(res))
2037 return res;
2038
2039 if (res == jperOk && !found)
2040 break;
2041 }
2042
2043 return res;
2044 }
2045
2046 /*
2047 * Convert boolean execution status 'res' to a boolean JSON item and execute
2048 * next jsonpath.
2049 */
2050 static JsonPathExecResult
appendBoolResult(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonValueList * found,JsonPathBool res)2051 appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
2052 JsonValueList *found, JsonPathBool res)
2053 {
2054 JsonPathItem next;
2055 JsonbValue jbv;
2056
2057 if (!jspGetNext(jsp, &next) && !found)
2058 return jperOk; /* found singleton boolean value */
2059
2060 if (res == jpbUnknown)
2061 {
2062 jbv.type = jbvNull;
2063 }
2064 else
2065 {
2066 jbv.type = jbvBool;
2067 jbv.val.boolean = res == jpbTrue;
2068 }
2069
2070 return executeNextItem(cxt, jsp, &next, &jbv, found, true);
2071 }
2072
2073 /*
2074 * Convert jsonpath's scalar or variable node to actual jsonb value.
2075 *
2076 * If node is a variable then its id returned, otherwise 0 returned.
2077 */
2078 static void
getJsonPathItem(JsonPathExecContext * cxt,JsonPathItem * item,JsonbValue * value)2079 getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
2080 JsonbValue *value)
2081 {
2082 switch (item->type)
2083 {
2084 case jpiNull:
2085 value->type = jbvNull;
2086 break;
2087 case jpiBool:
2088 value->type = jbvBool;
2089 value->val.boolean = jspGetBool(item);
2090 break;
2091 case jpiNumeric:
2092 value->type = jbvNumeric;
2093 value->val.numeric = jspGetNumeric(item);
2094 break;
2095 case jpiString:
2096 value->type = jbvString;
2097 value->val.string.val = jspGetString(item,
2098 &value->val.string.len);
2099 break;
2100 case jpiVariable:
2101 getJsonPathVariable(cxt, item, cxt->vars, value);
2102 return;
2103 default:
2104 elog(ERROR, "unexpected jsonpath item type");
2105 }
2106 }
2107
2108 /*
2109 * Get the value of variable passed to jsonpath executor
2110 */
2111 static void
getJsonPathVariable(JsonPathExecContext * cxt,JsonPathItem * variable,Jsonb * vars,JsonbValue * value)2112 getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
2113 Jsonb *vars, JsonbValue *value)
2114 {
2115 char *varName;
2116 int varNameLength;
2117 JsonbValue tmp;
2118 JsonbValue *v;
2119
2120 if (!vars)
2121 {
2122 value->type = jbvNull;
2123 return;
2124 }
2125
2126 Assert(variable->type == jpiVariable);
2127 varName = jspGetString(variable, &varNameLength);
2128 tmp.type = jbvString;
2129 tmp.val.string.val = varName;
2130 tmp.val.string.len = varNameLength;
2131
2132 v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
2133
2134 if (v)
2135 {
2136 *value = *v;
2137 pfree(v);
2138 }
2139 else
2140 {
2141 ereport(ERROR,
2142 (errcode(ERRCODE_UNDEFINED_OBJECT),
2143 errmsg("could not find jsonpath variable \"%s\"",
2144 pnstrdup(varName, varNameLength))));
2145 }
2146
2147 JsonbInitBinary(&tmp, vars);
2148 setBaseObject(cxt, &tmp, 1);
2149 }
2150
2151 /**************** Support functions for JsonPath execution *****************/
2152
2153 /*
2154 * Returns the size of an array item, or -1 if item is not an array.
2155 */
2156 static int
JsonbArraySize(JsonbValue * jb)2157 JsonbArraySize(JsonbValue *jb)
2158 {
2159 Assert(jb->type != jbvArray);
2160
2161 if (jb->type == jbvBinary)
2162 {
2163 JsonbContainer *jbc = jb->val.binary.data;
2164
2165 if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
2166 return JsonContainerSize(jbc);
2167 }
2168
2169 return -1;
2170 }
2171
2172 /* Comparison predicate callback. */
2173 static JsonPathBool
executeComparison(JsonPathItem * cmp,JsonbValue * lv,JsonbValue * rv,void * p)2174 executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
2175 {
2176 JsonPathExecContext *cxt = (JsonPathExecContext *) p;
2177
2178 return compareItems(cmp->type, lv, rv, cxt->useTz);
2179 }
2180
2181 /*
2182 * Perform per-byte comparison of two strings.
2183 */
2184 static int
binaryCompareStrings(const char * s1,int len1,const char * s2,int len2)2185 binaryCompareStrings(const char *s1, int len1,
2186 const char *s2, int len2)
2187 {
2188 int cmp;
2189
2190 cmp = memcmp(s1, s2, Min(len1, len2));
2191
2192 if (cmp != 0)
2193 return cmp;
2194
2195 if (len1 == len2)
2196 return 0;
2197
2198 return len1 < len2 ? -1 : 1;
2199 }
2200
2201 /*
2202 * Compare two strings in the current server encoding using Unicode codepoint
2203 * collation.
2204 */
2205 static int
compareStrings(const char * mbstr1,int mblen1,const char * mbstr2,int mblen2)2206 compareStrings(const char *mbstr1, int mblen1,
2207 const char *mbstr2, int mblen2)
2208 {
2209 if (GetDatabaseEncoding() == PG_SQL_ASCII ||
2210 GetDatabaseEncoding() == PG_UTF8)
2211 {
2212 /*
2213 * It's known property of UTF-8 strings that their per-byte comparison
2214 * result matches codepoints comparison result. ASCII can be
2215 * considered as special case of UTF-8.
2216 */
2217 return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2218 }
2219 else
2220 {
2221 char *utf8str1,
2222 *utf8str2;
2223 int cmp,
2224 utf8len1,
2225 utf8len2;
2226
2227 /*
2228 * We have to convert other encodings to UTF-8 first, then compare.
2229 * Input strings may be not null-terminated and pg_server_to_any() may
2230 * return them "as is". So, use strlen() only if there is real
2231 * conversion.
2232 */
2233 utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
2234 utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
2235 utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
2236 utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
2237
2238 cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
2239
2240 /*
2241 * If pg_server_to_any() did no real conversion, then we actually
2242 * compared original strings. So, we already done.
2243 */
2244 if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
2245 return cmp;
2246
2247 /* Free memory if needed */
2248 if (mbstr1 != utf8str1)
2249 pfree(utf8str1);
2250 if (mbstr2 != utf8str2)
2251 pfree(utf8str2);
2252
2253 /*
2254 * When all Unicode codepoints are equal, return result of binary
2255 * comparison. In some edge cases, same characters may have different
2256 * representations in encoding. Then our behavior could diverge from
2257 * standard. However, that allow us to do simple binary comparison
2258 * for "==" operator, which is performance critical in typical cases.
2259 * In future to implement strict standard conformance, we can do
2260 * normalization of input JSON strings.
2261 */
2262 if (cmp == 0)
2263 return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2264 else
2265 return cmp;
2266 }
2267 }
2268
2269 /*
2270 * Compare two SQL/JSON items using comparison operation 'op'.
2271 */
2272 static JsonPathBool
compareItems(int32 op,JsonbValue * jb1,JsonbValue * jb2,bool useTz)2273 compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
2274 {
2275 int cmp;
2276 bool res;
2277
2278 if (jb1->type != jb2->type)
2279 {
2280 if (jb1->type == jbvNull || jb2->type == jbvNull)
2281
2282 /*
2283 * Equality and order comparison of nulls to non-nulls returns
2284 * always false, but inequality comparison returns true.
2285 */
2286 return op == jpiNotEqual ? jpbTrue : jpbFalse;
2287
2288 /* Non-null items of different types are not comparable. */
2289 return jpbUnknown;
2290 }
2291
2292 switch (jb1->type)
2293 {
2294 case jbvNull:
2295 cmp = 0;
2296 break;
2297 case jbvBool:
2298 cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2299 jb1->val.boolean ? 1 : -1;
2300 break;
2301 case jbvNumeric:
2302 cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2303 break;
2304 case jbvString:
2305 if (op == jpiEqual)
2306 return jb1->val.string.len != jb2->val.string.len ||
2307 memcmp(jb1->val.string.val,
2308 jb2->val.string.val,
2309 jb1->val.string.len) ? jpbFalse : jpbTrue;
2310
2311 cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
2312 jb2->val.string.val, jb2->val.string.len);
2313 break;
2314 case jbvDatetime:
2315 {
2316 bool cast_error;
2317
2318 cmp = compareDatetime(jb1->val.datetime.value,
2319 jb1->val.datetime.typid,
2320 jb2->val.datetime.value,
2321 jb2->val.datetime.typid,
2322 useTz,
2323 &cast_error);
2324
2325 if (cast_error)
2326 return jpbUnknown;
2327 }
2328 break;
2329
2330 case jbvBinary:
2331 case jbvArray:
2332 case jbvObject:
2333 return jpbUnknown; /* non-scalars are not comparable */
2334
2335 default:
2336 elog(ERROR, "invalid jsonb value type %d", jb1->type);
2337 }
2338
2339 switch (op)
2340 {
2341 case jpiEqual:
2342 res = (cmp == 0);
2343 break;
2344 case jpiNotEqual:
2345 res = (cmp != 0);
2346 break;
2347 case jpiLess:
2348 res = (cmp < 0);
2349 break;
2350 case jpiGreater:
2351 res = (cmp > 0);
2352 break;
2353 case jpiLessOrEqual:
2354 res = (cmp <= 0);
2355 break;
2356 case jpiGreaterOrEqual:
2357 res = (cmp >= 0);
2358 break;
2359 default:
2360 elog(ERROR, "unrecognized jsonpath operation: %d", op);
2361 return jpbUnknown;
2362 }
2363
2364 return res ? jpbTrue : jpbFalse;
2365 }
2366
2367 /* Compare two numerics */
2368 static int
compareNumeric(Numeric a,Numeric b)2369 compareNumeric(Numeric a, Numeric b)
2370 {
2371 return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2372 NumericGetDatum(a),
2373 NumericGetDatum(b)));
2374 }
2375
2376 static JsonbValue *
copyJsonbValue(JsonbValue * src)2377 copyJsonbValue(JsonbValue *src)
2378 {
2379 JsonbValue *dst = palloc(sizeof(*dst));
2380
2381 *dst = *src;
2382
2383 return dst;
2384 }
2385
2386 /*
2387 * Execute array subscript expression and convert resulting numeric item to
2388 * the integer type with truncation.
2389 */
2390 static JsonPathExecResult
getArrayIndex(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,int32 * index)2391 getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2392 int32 *index)
2393 {
2394 JsonbValue *jbv;
2395 JsonValueList found = {0};
2396 JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2397 Datum numeric_index;
2398 bool have_error = false;
2399
2400 if (jperIsError(res))
2401 return res;
2402
2403 if (JsonValueListLength(&found) != 1 ||
2404 !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2405 RETURN_ERROR(ereport(ERROR,
2406 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2407 errmsg("jsonpath array subscript is not a single numeric value"))));
2408
2409 numeric_index = DirectFunctionCall2(numeric_trunc,
2410 NumericGetDatum(jbv->val.numeric),
2411 Int32GetDatum(0));
2412
2413 *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
2414 &have_error);
2415
2416 if (have_error)
2417 RETURN_ERROR(ereport(ERROR,
2418 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2419 errmsg("jsonpath array subscript is out of integer range"))));
2420
2421 return jperOk;
2422 }
2423
2424 /* Save base object and its id needed for the execution of .keyvalue(). */
2425 static JsonBaseObjectInfo
setBaseObject(JsonPathExecContext * cxt,JsonbValue * jbv,int32 id)2426 setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2427 {
2428 JsonBaseObjectInfo baseObject = cxt->baseObject;
2429
2430 cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2431 (JsonbContainer *) jbv->val.binary.data;
2432 cxt->baseObject.id = id;
2433
2434 return baseObject;
2435 }
2436
2437 static void
JsonValueListAppend(JsonValueList * jvl,JsonbValue * jbv)2438 JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2439 {
2440 if (jvl->singleton)
2441 {
2442 jvl->list = list_make2(jvl->singleton, jbv);
2443 jvl->singleton = NULL;
2444 }
2445 else if (!jvl->list)
2446 jvl->singleton = jbv;
2447 else
2448 jvl->list = lappend(jvl->list, jbv);
2449 }
2450
2451 static int
JsonValueListLength(const JsonValueList * jvl)2452 JsonValueListLength(const JsonValueList *jvl)
2453 {
2454 return jvl->singleton ? 1 : list_length(jvl->list);
2455 }
2456
2457 static bool
JsonValueListIsEmpty(JsonValueList * jvl)2458 JsonValueListIsEmpty(JsonValueList *jvl)
2459 {
2460 return !jvl->singleton && list_length(jvl->list) <= 0;
2461 }
2462
2463 static JsonbValue *
JsonValueListHead(JsonValueList * jvl)2464 JsonValueListHead(JsonValueList *jvl)
2465 {
2466 return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2467 }
2468
2469 static List *
JsonValueListGetList(JsonValueList * jvl)2470 JsonValueListGetList(JsonValueList *jvl)
2471 {
2472 if (jvl->singleton)
2473 return list_make1(jvl->singleton);
2474
2475 return jvl->list;
2476 }
2477
2478 static void
JsonValueListInitIterator(const JsonValueList * jvl,JsonValueListIterator * it)2479 JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2480 {
2481 if (jvl->singleton)
2482 {
2483 it->value = jvl->singleton;
2484 it->list = NIL;
2485 it->next = NULL;
2486 }
2487 else if (jvl->list != NIL)
2488 {
2489 it->value = (JsonbValue *) linitial(jvl->list);
2490 it->list = jvl->list;
2491 it->next = list_second_cell(jvl->list);
2492 }
2493 else
2494 {
2495 it->value = NULL;
2496 it->list = NIL;
2497 it->next = NULL;
2498 }
2499 }
2500
2501 /*
2502 * Get the next item from the sequence advancing iterator.
2503 */
2504 static JsonbValue *
JsonValueListNext(const JsonValueList * jvl,JsonValueListIterator * it)2505 JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2506 {
2507 JsonbValue *result = it->value;
2508
2509 if (it->next)
2510 {
2511 it->value = lfirst(it->next);
2512 it->next = lnext(it->list, it->next);
2513 }
2514 else
2515 {
2516 it->value = NULL;
2517 }
2518
2519 return result;
2520 }
2521
2522 /*
2523 * Initialize a binary JsonbValue with the given jsonb container.
2524 */
2525 static JsonbValue *
JsonbInitBinary(JsonbValue * jbv,Jsonb * jb)2526 JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2527 {
2528 jbv->type = jbvBinary;
2529 jbv->val.binary.data = &jb->root;
2530 jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2531
2532 return jbv;
2533 }
2534
2535 /*
2536 * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
2537 */
2538 static int
JsonbType(JsonbValue * jb)2539 JsonbType(JsonbValue *jb)
2540 {
2541 int type = jb->type;
2542
2543 if (jb->type == jbvBinary)
2544 {
2545 JsonbContainer *jbc = (void *) jb->val.binary.data;
2546
2547 /* Scalars should be always extracted during jsonpath execution. */
2548 Assert(!JsonContainerIsScalar(jbc));
2549
2550 if (JsonContainerIsObject(jbc))
2551 type = jbvObject;
2552 else if (JsonContainerIsArray(jbc))
2553 type = jbvArray;
2554 else
2555 elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2556 }
2557
2558 return type;
2559 }
2560
2561 /* Get scalar of given type or NULL on type mismatch */
2562 static JsonbValue *
getScalar(JsonbValue * scalar,enum jbvType type)2563 getScalar(JsonbValue *scalar, enum jbvType type)
2564 {
2565 /* Scalars should be always extracted during jsonpath execution. */
2566 Assert(scalar->type != jbvBinary ||
2567 !JsonContainerIsScalar(scalar->val.binary.data));
2568
2569 return scalar->type == type ? scalar : NULL;
2570 }
2571
2572 /* Construct a JSON array from the item list */
2573 static JsonbValue *
wrapItemsInArray(const JsonValueList * items)2574 wrapItemsInArray(const JsonValueList *items)
2575 {
2576 JsonbParseState *ps = NULL;
2577 JsonValueListIterator it;
2578 JsonbValue *jbv;
2579
2580 pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2581
2582 JsonValueListInitIterator(items, &it);
2583 while ((jbv = JsonValueListNext(items, &it)))
2584 pushJsonbValue(&ps, WJB_ELEM, jbv);
2585
2586 return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
2587 }
2588
2589 /* Check if the timezone required for casting from type1 to type2 is used */
2590 static void
checkTimezoneIsUsedForCast(bool useTz,const char * type1,const char * type2)2591 checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
2592 {
2593 if (!useTz)
2594 ereport(ERROR,
2595 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2596 errmsg("cannot convert value from %s to %s without time zone usage",
2597 type1, type2),
2598 errhint("Use *_tz() function for time zone support.")));
2599 }
2600
2601 /* Convert time datum to timetz datum */
2602 static Datum
castTimeToTimeTz(Datum time,bool useTz)2603 castTimeToTimeTz(Datum time, bool useTz)
2604 {
2605 checkTimezoneIsUsedForCast(useTz, "time", "timetz");
2606
2607 return DirectFunctionCall1(time_timetz, time);
2608 }
2609
2610 /*
2611 * Compare date to timestamp.
2612 * Note that this doesn't involve any timezone considerations.
2613 */
2614 static int
cmpDateToTimestamp(DateADT date1,Timestamp ts2,bool useTz)2615 cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
2616 {
2617 return date_cmp_timestamp_internal(date1, ts2);
2618 }
2619
2620 /*
2621 * Compare date to timestamptz.
2622 */
2623 static int
cmpDateToTimestampTz(DateADT date1,TimestampTz tstz2,bool useTz)2624 cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
2625 {
2626 checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
2627
2628 return date_cmp_timestamptz_internal(date1, tstz2);
2629 }
2630
2631 /*
2632 * Compare timestamp to timestamptz.
2633 */
2634 static int
cmpTimestampToTimestampTz(Timestamp ts1,TimestampTz tstz2,bool useTz)2635 cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
2636 {
2637 checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
2638
2639 return timestamp_cmp_timestamptz_internal(ts1, tstz2);
2640 }
2641
2642 /*
2643 * Cross-type comparison of two datetime SQL/JSON items. If items are
2644 * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
2645 * If the cast requires timezone and it is not used, then explicit error is thrown.
2646 */
2647 static int
compareDatetime(Datum val1,Oid typid1,Datum val2,Oid typid2,bool useTz,bool * cast_error)2648 compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
2649 bool useTz, bool *cast_error)
2650 {
2651 PGFunction cmpfunc;
2652
2653 *cast_error = false;
2654
2655 switch (typid1)
2656 {
2657 case DATEOID:
2658 switch (typid2)
2659 {
2660 case DATEOID:
2661 cmpfunc = date_cmp;
2662
2663 break;
2664
2665 case TIMESTAMPOID:
2666 return cmpDateToTimestamp(DatumGetDateADT(val1),
2667 DatumGetTimestamp(val2),
2668 useTz);
2669
2670 case TIMESTAMPTZOID:
2671 return cmpDateToTimestampTz(DatumGetDateADT(val1),
2672 DatumGetTimestampTz(val2),
2673 useTz);
2674
2675 case TIMEOID:
2676 case TIMETZOID:
2677 *cast_error = true; /* uncomparable types */
2678 return 0;
2679
2680 default:
2681 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2682 typid2);
2683 }
2684 break;
2685
2686 case TIMEOID:
2687 switch (typid2)
2688 {
2689 case TIMEOID:
2690 cmpfunc = time_cmp;
2691
2692 break;
2693
2694 case TIMETZOID:
2695 val1 = castTimeToTimeTz(val1, useTz);
2696 cmpfunc = timetz_cmp;
2697
2698 break;
2699
2700 case DATEOID:
2701 case TIMESTAMPOID:
2702 case TIMESTAMPTZOID:
2703 *cast_error = true; /* uncomparable types */
2704 return 0;
2705
2706 default:
2707 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2708 typid2);
2709 }
2710 break;
2711
2712 case TIMETZOID:
2713 switch (typid2)
2714 {
2715 case TIMEOID:
2716 val2 = castTimeToTimeTz(val2, useTz);
2717 cmpfunc = timetz_cmp;
2718
2719 break;
2720
2721 case TIMETZOID:
2722 cmpfunc = timetz_cmp;
2723
2724 break;
2725
2726 case DATEOID:
2727 case TIMESTAMPOID:
2728 case TIMESTAMPTZOID:
2729 *cast_error = true; /* uncomparable types */
2730 return 0;
2731
2732 default:
2733 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2734 typid2);
2735 }
2736 break;
2737
2738 case TIMESTAMPOID:
2739 switch (typid2)
2740 {
2741 case DATEOID:
2742 return -cmpDateToTimestamp(DatumGetDateADT(val2),
2743 DatumGetTimestamp(val1),
2744 useTz);
2745
2746 case TIMESTAMPOID:
2747 cmpfunc = timestamp_cmp;
2748
2749 break;
2750
2751 case TIMESTAMPTZOID:
2752 return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
2753 DatumGetTimestampTz(val2),
2754 useTz);
2755
2756 case TIMEOID:
2757 case TIMETZOID:
2758 *cast_error = true; /* uncomparable types */
2759 return 0;
2760
2761 default:
2762 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2763 typid2);
2764 }
2765 break;
2766
2767 case TIMESTAMPTZOID:
2768 switch (typid2)
2769 {
2770 case DATEOID:
2771 return -cmpDateToTimestampTz(DatumGetDateADT(val2),
2772 DatumGetTimestampTz(val1),
2773 useTz);
2774
2775 case TIMESTAMPOID:
2776 return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
2777 DatumGetTimestampTz(val1),
2778 useTz);
2779
2780 case TIMESTAMPTZOID:
2781 cmpfunc = timestamp_cmp;
2782
2783 break;
2784
2785 case TIMEOID:
2786 case TIMETZOID:
2787 *cast_error = true; /* uncomparable types */
2788 return 0;
2789
2790 default:
2791 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2792 typid2);
2793 }
2794 break;
2795
2796 default:
2797 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
2798 }
2799
2800 if (*cast_error)
2801 return 0; /* cast error */
2802
2803 return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
2804 }
2805