]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/jsonpath_exec.c
Implement jsonpath .datetime() method
[postgresql] / src / backend / utils / adt / jsonpath_exec.c
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 evade 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, 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/datetime.h"
70 #include "utils/datum.h"
71 #include "utils/formatting.h"
72 #include "utils/float.h"
73 #include "utils/guc.h"
74 #include "utils/json.h"
75 #include "utils/jsonpath.h"
76 #include "utils/date.h"
77 #include "utils/timestamp.h"
78 #include "utils/varlena.h"
79
80
81 /*
82  * Represents "base object" and it's "id" for .keyvalue() evaluation.
83  */
84 typedef struct JsonBaseObjectInfo
85 {
86         JsonbContainer *jbc;
87         int                     id;
88 } JsonBaseObjectInfo;
89
90 /*
91  * Context of jsonpath execution.
92  */
93 typedef struct JsonPathExecContext
94 {
95         Jsonb      *vars;                       /* variables to substitute into jsonpath */
96         JsonbValue *root;                       /* for $ evaluation */
97         JsonbValue *current;            /* for @ evaluation */
98         JsonBaseObjectInfo baseObject;  /* "base object" for .keyvalue()
99                                                                          * evaluation */
100         int                     lastGeneratedObjectId;  /* "id" counter for .keyvalue()
101                                                                                  * evaluation */
102         int                     innermostArraySize; /* for LAST array index evaluation */
103         bool            laxMode;                /* true for "lax" mode, false for "strict"
104                                                                  * mode */
105         bool            ignoreStructuralErrors; /* with "true" structural errors such
106                                                                                  * as absence of required json item or
107                                                                                  * unexpected json item type are
108                                                                                  * ignored */
109         bool            throwErrors;    /* with "false" all suppressible errors are
110                                                                  * suppressed */
111         bool            useTz;
112 } JsonPathExecContext;
113
114 /* Context for LIKE_REGEX execution. */
115 typedef struct JsonLikeRegexContext
116 {
117         text       *regex;
118         int                     cflags;
119 } JsonLikeRegexContext;
120
121 /* Result of jsonpath predicate evaluation */
122 typedef enum JsonPathBool
123 {
124         jpbFalse = 0,
125         jpbTrue = 1,
126         jpbUnknown = 2
127 } JsonPathBool;
128
129 /* Result of jsonpath expression evaluation */
130 typedef enum JsonPathExecResult
131 {
132         jperOk = 0,
133         jperNotFound = 1,
134         jperError = 2
135 } JsonPathExecResult;
136
137 #define jperIsError(jper)                       ((jper) == jperError)
138
139 /*
140  * List of jsonb values with shortcut for single-value list.
141  */
142 typedef struct JsonValueList
143 {
144         JsonbValue *singleton;
145         List       *list;
146 } JsonValueList;
147
148 typedef struct JsonValueListIterator
149 {
150         JsonbValue *value;
151         List       *list;
152         ListCell   *next;
153 } JsonValueListIterator;
154
155 /* strict/lax flags is decomposed into four [un]wrap/error flags */
156 #define jspStrictAbsenseOfErrors(cxt)   (!(cxt)->laxMode)
157 #define jspAutoUnwrap(cxt)                              ((cxt)->laxMode)
158 #define jspAutoWrap(cxt)                                ((cxt)->laxMode)
159 #define jspIgnoreStructuralErrors(cxt)  ((cxt)->ignoreStructuralErrors)
160 #define jspThrowErrors(cxt)                             ((cxt)->throwErrors)
161
162 /* Convenience macro: return or throw error depending on context */
163 #define RETURN_ERROR(throw_error) \
164 do { \
165         if (jspThrowErrors(cxt)) \
166                 throw_error; \
167         else \
168                 return jperError; \
169 } while (0)
170
171 typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
172                                                                                                    JsonbValue *larg,
173                                                                                                    JsonbValue *rarg,
174                                                                                                    void *param);
175 typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
176
177 static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
178                                                                                   Jsonb *json, bool throwErrors,
179                                                                                   JsonValueList *result, bool useTz);
180 static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
181                                                                           JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
182 static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
183                                                                                                          JsonPathItem *jsp, JsonbValue *jb,
184                                                                                                          JsonValueList *found, bool unwrap);
185 static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
186                                                                                                            JsonPathItem *jsp, JsonbValue *jb,
187                                                                                                            JsonValueList *found, bool unwrapElements);
188 static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
189                                                                                   JsonPathItem *cur, JsonPathItem *next,
190                                                                                   JsonbValue *v, JsonValueList *found, bool copy);
191 static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
192                                                                                                          bool unwrap, JsonValueList *found);
193 static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp,
194                                                                                                                         JsonbValue *jb, bool unwrap, JsonValueList *found);
195 static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
196                                                                         JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
197 static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
198                                                                                   JsonPathItem *jsp, JsonbValue *jb);
199 static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
200                                                                                  JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
201                                                                                  uint32 level, uint32 first, uint32 last,
202                                                                                  bool ignoreStructuralErrors, bool unwrapNext);
203 static JsonPathBool executePredicate(JsonPathExecContext *cxt,
204                                                                          JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
205                                                                          JsonbValue *jb, bool unwrapRightArg,
206                                                                          JsonPathPredicateCallback exec, void *param);
207 static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
208                                                                                                   JsonPathItem *jsp, JsonbValue *jb,
209                                                                                                   BinaryArithmFunc func, JsonValueList *found);
210 static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
211                                                                                                  JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
212                                                                                                  JsonValueList *found);
213 static JsonPathBool executeStartsWith(JsonPathItem *jsp,
214                                                                           JsonbValue *whole, JsonbValue *initial, void *param);
215 static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
216                                                                          JsonbValue *rarg, void *param);
217 static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
218                                                                                                    JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
219                                                                                                    JsonValueList *found);
220 static JsonPathExecResult executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
221                                                                                                 JsonbValue *jb, JsonValueList *found);
222 static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
223                                                                                                 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
224 static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
225                                                                                    JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
226 static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
227                                                         JsonbValue *value);
228 static void getJsonPathVariable(JsonPathExecContext *cxt,
229                                                                 JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
230 static int      JsonbArraySize(JsonbValue *jb);
231 static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
232                                                                           JsonbValue *rv, void *p);
233 static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2,
234                                                                  bool useTz);
235 static int      compareNumeric(Numeric a, Numeric b);
236 static JsonbValue *copyJsonbValue(JsonbValue *src);
237 static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
238                                                                                 JsonPathItem *jsp, JsonbValue *jb, int32 *index);
239 static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
240                                                                                 JsonbValue *jbv, int32 id);
241 static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
242 static int      JsonValueListLength(const JsonValueList *jvl);
243 static bool JsonValueListIsEmpty(JsonValueList *jvl);
244 static JsonbValue *JsonValueListHead(JsonValueList *jvl);
245 static List *JsonValueListGetList(JsonValueList *jvl);
246 static void JsonValueListInitIterator(const JsonValueList *jvl,
247                                                                           JsonValueListIterator *it);
248 static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
249                                                                          JsonValueListIterator *it);
250 static int      JsonbType(JsonbValue *jb);
251 static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
252 static int      JsonbType(JsonbValue *jb);
253 static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
254 static JsonbValue *wrapItemsInArray(const JsonValueList *items);
255 static int      compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
256                                                         bool useTz, bool *have_error);
257
258 /****************** User interface to JsonPath executor ********************/
259
260 /*
261  * jsonb_path_exists
262  *              Returns true if jsonpath returns at least one item for the specified
263  *              jsonb value.  This function and jsonb_path_match() are used to
264  *              implement @? and @@ operators, which in turn are intended to have an
265  *              index support.  Thus, it's desirable to make it easier to achieve
266  *              consistency between index scan results and sequential scan results.
267  *              So, we throw as less errors as possible.  Regarding this function,
268  *              such behavior also matches behavior of JSON_EXISTS() clause of
269  *              SQL/JSON.  Regarding jsonb_path_match(), this function doesn't have
270  *              an analogy in SQL/JSON, so we define its behavior on our own.
271  */
272 static Datum
273 jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
274 {
275         Jsonb      *jb = PG_GETARG_JSONB_P(0);
276         JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
277         JsonPathExecResult res;
278         Jsonb      *vars = NULL;
279         bool            silent = true;
280
281         if (PG_NARGS() == 4)
282         {
283                 vars = PG_GETARG_JSONB_P(2);
284                 silent = PG_GETARG_BOOL(3);
285         }
286
287         res = executeJsonPath(jp, vars, jb, !silent, NULL, tz);
288
289         PG_FREE_IF_COPY(jb, 0);
290         PG_FREE_IF_COPY(jp, 1);
291
292         if (jperIsError(res))
293                 PG_RETURN_NULL();
294
295         PG_RETURN_BOOL(res == jperOk);
296 }
297
298 Datum
299 jsonb_path_exists(PG_FUNCTION_ARGS)
300 {
301         return jsonb_path_exists_internal(fcinfo, false);
302 }
303
304 Datum
305 jsonb_path_exists_tz(PG_FUNCTION_ARGS)
306 {
307         return jsonb_path_exists_internal(fcinfo, true);
308 }
309
310 /*
311  * jsonb_path_exists_opr
312  *              Implementation of operator "jsonb @? jsonpath" (2-argument version of
313  *              jsonb_path_exists()).
314  */
315 Datum
316 jsonb_path_exists_opr(PG_FUNCTION_ARGS)
317 {
318         /* just call the other one -- it can handle both cases */
319         return jsonb_path_exists_internal(fcinfo, false);
320 }
321
322 /*
323  * jsonb_path_match
324  *              Returns jsonpath predicate result item for the specified jsonb value.
325  *              See jsonb_path_exists() comment for details regarding error handling.
326  */
327 static Datum
328 jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
329 {
330         Jsonb      *jb = PG_GETARG_JSONB_P(0);
331         JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
332         JsonValueList found = {0};
333         Jsonb      *vars = NULL;
334         bool            silent = true;
335
336         if (PG_NARGS() == 4)
337         {
338                 vars = PG_GETARG_JSONB_P(2);
339                 silent = PG_GETARG_BOOL(3);
340         }
341
342         (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
343
344         PG_FREE_IF_COPY(jb, 0);
345         PG_FREE_IF_COPY(jp, 1);
346
347         if (JsonValueListLength(&found) == 1)
348         {
349                 JsonbValue *jbv = JsonValueListHead(&found);
350
351                 if (jbv->type == jbvBool)
352                         PG_RETURN_BOOL(jbv->val.boolean);
353
354                 if (jbv->type == jbvNull)
355                         PG_RETURN_NULL();
356         }
357
358         if (!silent)
359                 ereport(ERROR,
360                                 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
361                                  errmsg("single boolean result is expected")));
362
363         PG_RETURN_NULL();
364 }
365
366 Datum
367 jsonb_path_match(PG_FUNCTION_ARGS)
368 {
369         return jsonb_path_match_internal(fcinfo, false);
370 }
371
372 Datum
373 jsonb_path_match_tz(PG_FUNCTION_ARGS)
374 {
375         return jsonb_path_match_internal(fcinfo, true);
376 }
377
378 /*
379  * jsonb_path_match_opr
380  *              Implementation of operator "jsonb @@ jsonpath" (2-argument version of
381  *              jsonb_path_match()).
382  */
383 Datum
384 jsonb_path_match_opr(PG_FUNCTION_ARGS)
385 {
386         /* just call the other one -- it can handle both cases */
387         return jsonb_path_match_internal(fcinfo, false);
388 }
389
390 /*
391  * jsonb_path_query
392  *              Executes jsonpath for given jsonb document and returns result as
393  *              rowset.
394  */
395 static Datum
396 jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
397 {
398         FuncCallContext *funcctx;
399         List       *found;
400         JsonbValue *v;
401         ListCell   *c;
402
403         if (SRF_IS_FIRSTCALL())
404         {
405                 JsonPath   *jp;
406                 Jsonb      *jb;
407                 MemoryContext oldcontext;
408                 Jsonb      *vars;
409                 bool            silent;
410                 JsonValueList found = {0};
411
412                 funcctx = SRF_FIRSTCALL_INIT();
413                 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
414
415                 jb = PG_GETARG_JSONB_P_COPY(0);
416                 jp = PG_GETARG_JSONPATH_P_COPY(1);
417                 vars = PG_GETARG_JSONB_P_COPY(2);
418                 silent = PG_GETARG_BOOL(3);
419
420                 (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
421
422                 funcctx->user_fctx = JsonValueListGetList(&found);
423
424                 MemoryContextSwitchTo(oldcontext);
425         }
426
427         funcctx = SRF_PERCALL_SETUP();
428         found = funcctx->user_fctx;
429
430         c = list_head(found);
431
432         if (c == NULL)
433                 SRF_RETURN_DONE(funcctx);
434
435         v = lfirst(c);
436         funcctx->user_fctx = list_delete_first(found);
437
438         SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
439 }
440
441 Datum
442 jsonb_path_query(PG_FUNCTION_ARGS)
443 {
444         return jsonb_path_query_internal(fcinfo, false);
445 }
446
447 Datum
448 jsonb_path_query_tz(PG_FUNCTION_ARGS)
449 {
450         return jsonb_path_query_internal(fcinfo, true);
451 }
452
453 /*
454  * jsonb_path_query_array
455  *              Executes jsonpath for given jsonb document and returns result as
456  *              jsonb array.
457  */
458 static Datum
459 jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
460 {
461         Jsonb      *jb = PG_GETARG_JSONB_P(0);
462         JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
463         JsonValueList found = {0};
464         Jsonb      *vars = PG_GETARG_JSONB_P(2);
465         bool            silent = PG_GETARG_BOOL(3);
466
467         (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
468
469         PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
470 }
471
472 Datum
473 jsonb_path_query_array(PG_FUNCTION_ARGS)
474 {
475         return jsonb_path_query_array_internal(fcinfo, false);
476 }
477
478 Datum
479 jsonb_path_query_array_tz(PG_FUNCTION_ARGS)
480 {
481         return jsonb_path_query_array_internal(fcinfo, true);
482 }
483
484 /*
485  * jsonb_path_query_first
486  *              Executes jsonpath for given jsonb document and returns first result
487  *              item.  If there are no items, NULL returned.
488  */
489 static Datum
490 jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
491 {
492         Jsonb      *jb = PG_GETARG_JSONB_P(0);
493         JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
494         JsonValueList found = {0};
495         Jsonb      *vars = PG_GETARG_JSONB_P(2);
496         bool            silent = PG_GETARG_BOOL(3);
497
498         (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
499
500         if (JsonValueListLength(&found) >= 1)
501                 PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
502         else
503                 PG_RETURN_NULL();
504 }
505
506 Datum
507 jsonb_path_query_first(PG_FUNCTION_ARGS)
508 {
509         return jsonb_path_query_first_internal(fcinfo, false);
510 }
511
512 Datum
513 jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
514 {
515         return jsonb_path_query_first_internal(fcinfo, true);
516 }
517
518 /********************Execute functions for JsonPath**************************/
519
520 /*
521  * Interface to jsonpath executor
522  *
523  * 'path' - jsonpath to be executed
524  * 'vars' - variables to be substituted to jsonpath
525  * 'json' - target document for jsonpath evaluation
526  * 'throwErrors' - whether we should throw suppressible errors
527  * 'result' - list to store result items into
528  *
529  * Returns an error if a recoverable error happens during processing, or NULL
530  * on no error.
531  *
532  * Note, jsonb and jsonpath values should be available and untoasted during
533  * work because JsonPathItem, JsonbValue and result item could have pointers
534  * into input values.  If caller needs to just check if document matches
535  * jsonpath, then it doesn't provide a result arg.  In this case executor
536  * works till first positive result and does not check the rest if possible.
537  * In other case it tries to find all the satisfied result items.
538  */
539 static JsonPathExecResult
540 executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
541                                 JsonValueList *result, bool useTz)
542 {
543         JsonPathExecContext cxt;
544         JsonPathExecResult res;
545         JsonPathItem jsp;
546         JsonbValue      jbv;
547
548         jspInit(&jsp, path);
549
550         if (!JsonbExtractScalar(&json->root, &jbv))
551                 JsonbInitBinary(&jbv, json);
552
553         if (vars && !JsonContainerIsObject(&vars->root))
554         {
555                 ereport(ERROR,
556                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
557                                  errmsg("\"vars\" argument is not an object"),
558                                  errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
559         }
560
561         cxt.vars = vars;
562         cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
563         cxt.ignoreStructuralErrors = cxt.laxMode;
564         cxt.root = &jbv;
565         cxt.current = &jbv;
566         cxt.baseObject.jbc = NULL;
567         cxt.baseObject.id = 0;
568         cxt.lastGeneratedObjectId = vars ? 2 : 1;
569         cxt.innermostArraySize = -1;
570         cxt.throwErrors = throwErrors;
571         cxt.useTz = useTz;
572
573         if (jspStrictAbsenseOfErrors(&cxt) && !result)
574         {
575                 /*
576                  * In strict mode we must get a complete list of values to check that
577                  * there are no errors at all.
578                  */
579                 JsonValueList vals = {0};
580
581                 res = executeItem(&cxt, &jsp, &jbv, &vals);
582
583                 if (jperIsError(res))
584                         return res;
585
586                 return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
587         }
588
589         res = executeItem(&cxt, &jsp, &jbv, result);
590
591         Assert(!throwErrors || !jperIsError(res));
592
593         return res;
594 }
595
596 /*
597  * Execute jsonpath with automatic unwrapping of current item in lax mode.
598  */
599 static JsonPathExecResult
600 executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
601                         JsonbValue *jb, JsonValueList *found)
602 {
603         return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
604 }
605
606 /*
607  * Main jsonpath executor function: walks on jsonpath structure, finds
608  * relevant parts of jsonb and evaluates expressions over them.
609  * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
610  */
611 static JsonPathExecResult
612 executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
613                                                    JsonbValue *jb, JsonValueList *found, bool unwrap)
614 {
615         JsonPathItem elem;
616         JsonPathExecResult res = jperNotFound;
617         JsonBaseObjectInfo baseObject;
618
619         check_stack_depth();
620         CHECK_FOR_INTERRUPTS();
621
622         switch (jsp->type)
623         {
624                         /* all boolean item types: */
625                 case jpiAnd:
626                 case jpiOr:
627                 case jpiNot:
628                 case jpiIsUnknown:
629                 case jpiEqual:
630                 case jpiNotEqual:
631                 case jpiLess:
632                 case jpiGreater:
633                 case jpiLessOrEqual:
634                 case jpiGreaterOrEqual:
635                 case jpiExists:
636                 case jpiStartsWith:
637                 case jpiLikeRegex:
638                         {
639                                 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
640
641                                 res = appendBoolResult(cxt, jsp, found, st);
642                                 break;
643                         }
644
645                 case jpiKey:
646                         if (JsonbType(jb) == jbvObject)
647                         {
648                                 JsonbValue *v;
649                                 JsonbValue      key;
650
651                                 key.type = jbvString;
652                                 key.val.string.val = jspGetString(jsp, &key.val.string.len);
653
654                                 v = findJsonbValueFromContainer(jb->val.binary.data,
655                                                                                                 JB_FOBJECT, &key);
656
657                                 if (v != NULL)
658                                 {
659                                         res = executeNextItem(cxt, jsp, NULL,
660                                                                                   v, found, false);
661
662                                         /* free value if it was not added to found list */
663                                         if (jspHasNext(jsp) || !found)
664                                                 pfree(v);
665                                 }
666                                 else if (!jspIgnoreStructuralErrors(cxt))
667                                 {
668                                         Assert(found);
669
670                                         if (!jspThrowErrors(cxt))
671                                                 return jperError;
672
673                                         ereport(ERROR,
674                                                         (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
675                                                          errmsg("JSON object does not contain key \"%s\"",
676                                                                         pnstrdup(key.val.string.val,
677                                                                                          key.val.string.len))));
678                                 }
679                         }
680                         else if (unwrap && JsonbType(jb) == jbvArray)
681                                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
682                         else if (!jspIgnoreStructuralErrors(cxt))
683                         {
684                                 Assert(found);
685                                 RETURN_ERROR(ereport(ERROR,
686                                                                          (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
687                                                                           errmsg("jsonpath member accessor can only be applied to an object"))));
688                         }
689                         break;
690
691                 case jpiRoot:
692                         jb = cxt->root;
693                         baseObject = setBaseObject(cxt, jb, 0);
694                         res = executeNextItem(cxt, jsp, NULL, jb, found, true);
695                         cxt->baseObject = baseObject;
696                         break;
697
698                 case jpiCurrent:
699                         res = executeNextItem(cxt, jsp, NULL, cxt->current,
700                                                                   found, true);
701                         break;
702
703                 case jpiAnyArray:
704                         if (JsonbType(jb) == jbvArray)
705                         {
706                                 bool            hasNext = jspGetNext(jsp, &elem);
707
708                                 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
709                                                                                                    jb, found, jspAutoUnwrap(cxt));
710                         }
711                         else if (jspAutoWrap(cxt))
712                                 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
713                         else if (!jspIgnoreStructuralErrors(cxt))
714                                 RETURN_ERROR(ereport(ERROR,
715                                                                          (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
716                                                                           errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
717                         break;
718
719                 case jpiIndexArray:
720                         if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
721                         {
722                                 int                     innermostArraySize = cxt->innermostArraySize;
723                                 int                     i;
724                                 int                     size = JsonbArraySize(jb);
725                                 bool            singleton = size < 0;
726                                 bool            hasNext = jspGetNext(jsp, &elem);
727
728                                 if (singleton)
729                                         size = 1;
730
731                                 cxt->innermostArraySize = size; /* for LAST evaluation */
732
733                                 for (i = 0; i < jsp->content.array.nelems; i++)
734                                 {
735                                         JsonPathItem from;
736                                         JsonPathItem to;
737                                         int32           index;
738                                         int32           index_from;
739                                         int32           index_to;
740                                         bool            range = jspGetArraySubscript(jsp, &from,
741                                                                                                                          &to, i);
742
743                                         res = getArrayIndex(cxt, &from, jb, &index_from);
744
745                                         if (jperIsError(res))
746                                                 break;
747
748                                         if (range)
749                                         {
750                                                 res = getArrayIndex(cxt, &to, jb, &index_to);
751
752                                                 if (jperIsError(res))
753                                                         break;
754                                         }
755                                         else
756                                                 index_to = index_from;
757
758                                         if (!jspIgnoreStructuralErrors(cxt) &&
759                                                 (index_from < 0 ||
760                                                  index_from > index_to ||
761                                                  index_to >= size))
762                                                 RETURN_ERROR(ereport(ERROR,
763                                                                                          (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
764                                                                                           errmsg("jsonpath array subscript is out of bounds"))));
765
766                                         if (index_from < 0)
767                                                 index_from = 0;
768
769                                         if (index_to >= size)
770                                                 index_to = size - 1;
771
772                                         res = jperNotFound;
773
774                                         for (index = index_from; index <= index_to; index++)
775                                         {
776                                                 JsonbValue *v;
777                                                 bool            copy;
778
779                                                 if (singleton)
780                                                 {
781                                                         v = jb;
782                                                         copy = true;
783                                                 }
784                                                 else
785                                                 {
786                                                         v = getIthJsonbValueFromContainer(jb->val.binary.data,
787                                                                                                                           (uint32) index);
788
789                                                         if (v == NULL)
790                                                                 continue;
791
792                                                         copy = false;
793                                                 }
794
795                                                 if (!hasNext && !found)
796                                                         return jperOk;
797
798                                                 res = executeNextItem(cxt, jsp, &elem, v, found,
799                                                                                           copy);
800
801                                                 if (jperIsError(res))
802                                                         break;
803
804                                                 if (res == jperOk && !found)
805                                                         break;
806                                         }
807
808                                         if (jperIsError(res))
809                                                 break;
810
811                                         if (res == jperOk && !found)
812                                                 break;
813                                 }
814
815                                 cxt->innermostArraySize = innermostArraySize;
816                         }
817                         else if (!jspIgnoreStructuralErrors(cxt))
818                         {
819                                 RETURN_ERROR(ereport(ERROR,
820                                                                          (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
821                                                                           errmsg("jsonpath array accessor can only be applied to an array"))));
822                         }
823                         break;
824
825                 case jpiLast:
826                         {
827                                 JsonbValue      tmpjbv;
828                                 JsonbValue *lastjbv;
829                                 int                     last;
830                                 bool            hasNext = jspGetNext(jsp, &elem);
831
832                                 if (cxt->innermostArraySize < 0)
833                                         elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
834
835                                 if (!hasNext && !found)
836                                 {
837                                         res = jperOk;
838                                         break;
839                                 }
840
841                                 last = cxt->innermostArraySize - 1;
842
843                                 lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
844
845                                 lastjbv->type = jbvNumeric;
846                                 lastjbv->val.numeric =
847                                         DatumGetNumeric(DirectFunctionCall1(int4_numeric,
848                                                                                                                 Int32GetDatum(last)));
849
850                                 res = executeNextItem(cxt, jsp, &elem,
851                                                                           lastjbv, found, hasNext);
852                         }
853                         break;
854
855                 case jpiAnyKey:
856                         if (JsonbType(jb) == jbvObject)
857                         {
858                                 bool            hasNext = jspGetNext(jsp, &elem);
859
860                                 if (jb->type != jbvBinary)
861                                         elog(ERROR, "invalid jsonb object type: %d", jb->type);
862
863                                 return executeAnyItem
864                                         (cxt, hasNext ? &elem : NULL,
865                                          jb->val.binary.data, found, 1, 1, 1,
866                                          false, jspAutoUnwrap(cxt));
867                         }
868                         else if (unwrap && JsonbType(jb) == jbvArray)
869                                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
870                         else if (!jspIgnoreStructuralErrors(cxt))
871                         {
872                                 Assert(found);
873                                 RETURN_ERROR(ereport(ERROR,
874                                                                          (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
875                                                                           errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
876                         }
877                         break;
878
879                 case jpiAdd:
880                         return executeBinaryArithmExpr(cxt, jsp, jb,
881                                                                                    numeric_add_opt_error, found);
882
883                 case jpiSub:
884                         return executeBinaryArithmExpr(cxt, jsp, jb,
885                                                                                    numeric_sub_opt_error, found);
886
887                 case jpiMul:
888                         return executeBinaryArithmExpr(cxt, jsp, jb,
889                                                                                    numeric_mul_opt_error, found);
890
891                 case jpiDiv:
892                         return executeBinaryArithmExpr(cxt, jsp, jb,
893                                                                                    numeric_div_opt_error, found);
894
895                 case jpiMod:
896                         return executeBinaryArithmExpr(cxt, jsp, jb,
897                                                                                    numeric_mod_opt_error, found);
898
899                 case jpiPlus:
900                         return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
901
902                 case jpiMinus:
903                         return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
904                                                                                   found);
905
906                 case jpiFilter:
907                         {
908                                 JsonPathBool st;
909
910                                 if (unwrap && JsonbType(jb) == jbvArray)
911                                         return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
912                                                                                                                 false);
913
914                                 jspGetArg(jsp, &elem);
915                                 st = executeNestedBoolItem(cxt, &elem, jb);
916                                 if (st != jpbTrue)
917                                         res = jperNotFound;
918                                 else
919                                         res = executeNextItem(cxt, jsp, NULL,
920                                                                                   jb, found, true);
921                                 break;
922                         }
923
924                 case jpiAny:
925                         {
926                                 bool            hasNext = jspGetNext(jsp, &elem);
927
928                                 /* first try without any intermediate steps */
929                                 if (jsp->content.anybounds.first == 0)
930                                 {
931                                         bool            savedIgnoreStructuralErrors;
932
933                                         savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
934                                         cxt->ignoreStructuralErrors = true;
935                                         res = executeNextItem(cxt, jsp, &elem,
936                                                                                   jb, found, true);
937                                         cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
938
939                                         if (res == jperOk && !found)
940                                                 break;
941                                 }
942
943                                 if (jb->type == jbvBinary)
944                                         res = executeAnyItem
945                                                 (cxt, hasNext ? &elem : NULL,
946                                                  jb->val.binary.data, found,
947                                                  1,
948                                                  jsp->content.anybounds.first,
949                                                  jsp->content.anybounds.last,
950                                                  true, jspAutoUnwrap(cxt));
951                                 break;
952                         }
953
954                 case jpiNull:
955                 case jpiBool:
956                 case jpiNumeric:
957                 case jpiString:
958                 case jpiVariable:
959                         {
960                                 JsonbValue      vbuf;
961                                 JsonbValue *v;
962                                 bool            hasNext = jspGetNext(jsp, &elem);
963
964                                 if (!hasNext && !found)
965                                 {
966                                         res = jperOk;   /* skip evaluation */
967                                         break;
968                                 }
969
970                                 v = hasNext ? &vbuf : palloc(sizeof(*v));
971
972                                 baseObject = cxt->baseObject;
973                                 getJsonPathItem(cxt, jsp, v);
974
975                                 res = executeNextItem(cxt, jsp, &elem,
976                                                                           v, found, hasNext);
977                                 cxt->baseObject = baseObject;
978                         }
979                         break;
980
981                 case jpiType:
982                         {
983                                 JsonbValue *jbv = palloc(sizeof(*jbv));
984
985                                 jbv->type = jbvString;
986                                 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
987                                 jbv->val.string.len = strlen(jbv->val.string.val);
988
989                                 res = executeNextItem(cxt, jsp, NULL, jbv,
990                                                                           found, false);
991                         }
992                         break;
993
994                 case jpiSize:
995                         {
996                                 int                     size = JsonbArraySize(jb);
997
998                                 if (size < 0)
999                                 {
1000                                         if (!jspAutoWrap(cxt))
1001                                         {
1002                                                 if (!jspIgnoreStructuralErrors(cxt))
1003                                                         RETURN_ERROR(ereport(ERROR,
1004                                                                                                  (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
1005                                                                                                   errmsg("jsonpath item method .%s() can only be applied to an array",
1006                                                                                                                  jspOperationName(jsp->type)))));
1007                                                 break;
1008                                         }
1009
1010                                         size = 1;
1011                                 }
1012
1013                                 jb = palloc(sizeof(*jb));
1014
1015                                 jb->type = jbvNumeric;
1016                                 jb->val.numeric =
1017                                         DatumGetNumeric(DirectFunctionCall1(int4_numeric,
1018                                                                                                                 Int32GetDatum(size)));
1019
1020                                 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
1021                         }
1022                         break;
1023
1024                 case jpiAbs:
1025                         return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
1026                                                                                         found);
1027
1028                 case jpiFloor:
1029                         return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
1030                                                                                         found);
1031
1032                 case jpiCeiling:
1033                         return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
1034                                                                                         found);
1035
1036                 case jpiDouble:
1037                         {
1038                                 JsonbValue      jbv;
1039
1040                                 if (unwrap && JsonbType(jb) == jbvArray)
1041                                         return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1042                                                                                                                 false);
1043
1044                                 if (jb->type == jbvNumeric)
1045                                 {
1046                                         char       *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1047                                                                                                                                                   NumericGetDatum(jb->val.numeric)));
1048                                         bool            have_error = false;
1049
1050                                         (void) float8in_internal_opt_error(tmp,
1051                                                                                                            NULL,
1052                                                                                                            "double precision",
1053                                                                                                            tmp,
1054                                                                                                            &have_error);
1055
1056                                         if (have_error)
1057                                                 RETURN_ERROR(ereport(ERROR,
1058                                                                                          (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1059                                                                                           errmsg("jsonpath item method .%s() can only be applied to a numeric value",
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))
1078                                                 RETURN_ERROR(ereport(ERROR,
1079                                                                                          (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1080                                                                                           errmsg("jsonpath item method .%s() can only be applied to a numeric value",
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1779 executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1780                                           JsonbValue *jb, JsonValueList *found)
1781 {
1782         JsonbValue      jbvbuf;
1783         Datum           value;
1784         text       *datetime;
1785         Oid                     typid;
1786         int32           typmod = -1;
1787         int                     tz = 0;
1788         bool            hasNext;
1789         JsonPathExecResult res = jperNotFound;
1790         JsonPathItem elem;
1791
1792         if (!(jb = getScalar(jb, jbvString)))
1793                 RETURN_ERROR(ereport(ERROR,
1794                                                          (errcode(ERRCODE_INVALID_ARGUMENT_FOR_JSON_DATETIME_FUNCTION),
1795                                                           errmsg("jsonpath item method .%s() can only be applied to a string",
1796                                                                          jspOperationName(jsp->type)))));
1797
1798         datetime = cstring_to_text_with_len(jb->val.string.val,
1799                                                                                 jb->val.string.len);
1800
1801         if (jsp->content.arg)
1802         {
1803                 text       *template;
1804                 char       *template_str;
1805                 int                     template_len;
1806                 bool            have_error = false;
1807
1808                 jspGetArg(jsp, &elem);
1809
1810                 if (elem.type != jpiString)
1811                         elog(ERROR, "invalid jsonpath item type for .datetime() argument");
1812
1813                 template_str = jspGetString(&elem, &template_len);
1814
1815                 template = cstring_to_text_with_len(template_str,
1816                                                                                         template_len);
1817
1818                 value = parse_datetime(datetime, template, true,
1819                                                            &typid, &typmod, &tz,
1820                                                            jspThrowErrors(cxt) ? NULL : &have_error);
1821
1822                 if (have_error)
1823                         res = jperError;
1824                 else
1825                         res = jperOk;
1826         }
1827         else
1828         {
1829                 /*
1830                  * According to SQL/JSON standard enumerate ISO formats for: date,
1831                  * timetz, time, timestamptz, timestamp.
1832                  */
1833                 static const char *fmt_str[] =
1834                 {
1835                         "yyyy-mm-dd",
1836                         "HH24:MI:SS TZH:TZM",
1837                         "HH24:MI:SS TZH",
1838                         "HH24:MI:SS",
1839                         "yyyy-mm-dd HH24:MI:SS TZH:TZM",
1840                         "yyyy-mm-dd HH24:MI:SS TZH",
1841                         "yyyy-mm-dd HH24:MI:SS"
1842                 };
1843
1844                 /* cache for format texts */
1845                 static text *fmt_txt[lengthof(fmt_str)] = {0};
1846                 int                     i;
1847
1848                 /* loop until datetime format fits */
1849                 for (i = 0; i < lengthof(fmt_str); i++)
1850                 {
1851                         bool            have_error = false;
1852
1853                         if (!fmt_txt[i])
1854                         {
1855                                 MemoryContext oldcxt =
1856                                 MemoryContextSwitchTo(TopMemoryContext);
1857
1858                                 fmt_txt[i] = cstring_to_text(fmt_str[i]);
1859                                 MemoryContextSwitchTo(oldcxt);
1860                         }
1861
1862                         value = parse_datetime(datetime, fmt_txt[i], true,
1863                                                                    &typid, &typmod, &tz,
1864                                                                    &have_error);
1865
1866                         if (!have_error)
1867                         {
1868                                 res = jperOk;
1869                                 break;
1870                         }
1871                 }
1872
1873                 if (res == jperNotFound)
1874                         RETURN_ERROR(ereport(ERROR,
1875                                                                  (errcode(ERRCODE_INVALID_ARGUMENT_FOR_JSON_DATETIME_FUNCTION),
1876                                                                   errmsg("datetime format is not unrecognized"),
1877                                                                   errhint("use datetime template argument for explicit format specification"))));
1878         }
1879
1880         pfree(datetime);
1881
1882         if (jperIsError(res))
1883                 return res;
1884
1885         hasNext = jspGetNext(jsp, &elem);
1886
1887         if (!hasNext && !found)
1888                 return res;
1889
1890         jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
1891
1892         jb->type = jbvDatetime;
1893         jb->val.datetime.value = value;
1894         jb->val.datetime.typid = typid;
1895         jb->val.datetime.typmod = typmod;
1896         jb->val.datetime.tz = tz;
1897
1898         return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
1899 }
1900
1901 /*
1902  * Implementation of .keyvalue() method.
1903  *
1904  * .keyvalue() method returns a sequence of object's key-value pairs in the
1905  * following format: '{ "key": key, "value": value, "id": id }'.
1906  *
1907  * "id" field is an object identifier which is constructed from the two parts:
1908  * base object id and its binary offset in base object's jsonb:
1909  * id = 10000000000 * base_object_id + obj_offset_in_base_object
1910  *
1911  * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1912  * (maximal offset in jsonb).  Decimal multiplier is used here to improve the
1913  * readability of identifiers.
1914  *
1915  * Base object is usually a root object of the path: context item '$' or path
1916  * variable '$var', literals can't produce objects for now.  But if the path
1917  * contains generated objects (.keyvalue() itself, for example), then they
1918  * become base object for the subsequent .keyvalue().
1919  *
1920  * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
1921  * of variables (see getJsonPathVariable()).  Ids for generated objects
1922  * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
1923  */
1924 static JsonPathExecResult
1925 executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1926                                           JsonbValue *jb, JsonValueList *found)
1927 {
1928         JsonPathExecResult res = jperNotFound;
1929         JsonPathItem next;
1930         JsonbContainer *jbc;
1931         JsonbValue      key;
1932         JsonbValue      val;
1933         JsonbValue      idval;
1934         JsonbValue      keystr;
1935         JsonbValue      valstr;
1936         JsonbValue      idstr;
1937         JsonbIterator *it;
1938         JsonbIteratorToken tok;
1939         int64           id;
1940         bool            hasNext;
1941
1942         if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
1943                 RETURN_ERROR(ereport(ERROR,
1944                                                          (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
1945                                                           errmsg("jsonpath item method .%s() can only be applied to an object",
1946                                                                          jspOperationName(jsp->type)))));
1947
1948         jbc = jb->val.binary.data;
1949
1950         if (!JsonContainerSize(jbc))
1951                 return jperNotFound;    /* no key-value pairs */
1952
1953         hasNext = jspGetNext(jsp, &next);
1954
1955         keystr.type = jbvString;
1956         keystr.val.string.val = "key";
1957         keystr.val.string.len = 3;
1958
1959         valstr.type = jbvString;
1960         valstr.val.string.val = "value";
1961         valstr.val.string.len = 5;
1962
1963         idstr.type = jbvString;
1964         idstr.val.string.val = "id";
1965         idstr.val.string.len = 2;
1966
1967         /* construct object id from its base object and offset inside that */
1968         id = jb->type != jbvBinary ? 0 :
1969                 (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
1970         id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
1971
1972         idval.type = jbvNumeric;
1973         idval.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
1974                                                                                                                         Int64GetDatum(id)));
1975
1976         it = JsonbIteratorInit(jbc);
1977
1978         while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
1979         {
1980                 JsonBaseObjectInfo baseObject;
1981                 JsonbValue      obj;
1982                 JsonbParseState *ps;
1983                 JsonbValue *keyval;
1984                 Jsonb      *jsonb;
1985
1986                 if (tok != WJB_KEY)
1987                         continue;
1988
1989                 res = jperOk;
1990
1991                 if (!hasNext && !found)
1992                         break;
1993
1994                 tok = JsonbIteratorNext(&it, &val, true);
1995                 Assert(tok == WJB_VALUE);
1996
1997                 ps = NULL;
1998                 pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
1999
2000                 pushJsonbValue(&ps, WJB_KEY, &keystr);
2001                 pushJsonbValue(&ps, WJB_VALUE, &key);
2002
2003                 pushJsonbValue(&ps, WJB_KEY, &valstr);
2004                 pushJsonbValue(&ps, WJB_VALUE, &val);
2005
2006                 pushJsonbValue(&ps, WJB_KEY, &idstr);
2007                 pushJsonbValue(&ps, WJB_VALUE, &idval);
2008
2009                 keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2010
2011                 jsonb = JsonbValueToJsonb(keyval);
2012
2013                 JsonbInitBinary(&obj, jsonb);
2014
2015                 baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
2016
2017                 res = executeNextItem(cxt, jsp, &next, &obj, found, true);
2018
2019                 cxt->baseObject = baseObject;
2020
2021                 if (jperIsError(res))
2022                         return res;
2023
2024                 if (res == jperOk && !found)
2025                         break;
2026         }
2027
2028         return res;
2029 }
2030
2031 /*
2032  * Convert boolean execution status 'res' to a boolean JSON item and execute
2033  * next jsonpath.
2034  */
2035 static JsonPathExecResult
2036 appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
2037                                  JsonValueList *found, JsonPathBool res)
2038 {
2039         JsonPathItem next;
2040         JsonbValue      jbv;
2041
2042         if (!jspGetNext(jsp, &next) && !found)
2043                 return jperOk;                  /* found singleton boolean value */
2044
2045         if (res == jpbUnknown)
2046         {
2047                 jbv.type = jbvNull;
2048         }
2049         else
2050         {
2051                 jbv.type = jbvBool;
2052                 jbv.val.boolean = res == jpbTrue;
2053         }
2054
2055         return executeNextItem(cxt, jsp, &next, &jbv, found, true);
2056 }
2057
2058 /*
2059  * Convert jsonpath's scalar or variable node to actual jsonb value.
2060  *
2061  * If node is a variable then its id returned, otherwise 0 returned.
2062  */
2063 static void
2064 getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
2065                                 JsonbValue *value)
2066 {
2067         switch (item->type)
2068         {
2069                 case jpiNull:
2070                         value->type = jbvNull;
2071                         break;
2072                 case jpiBool:
2073                         value->type = jbvBool;
2074                         value->val.boolean = jspGetBool(item);
2075                         break;
2076                 case jpiNumeric:
2077                         value->type = jbvNumeric;
2078                         value->val.numeric = jspGetNumeric(item);
2079                         break;
2080                 case jpiString:
2081                         value->type = jbvString;
2082                         value->val.string.val = jspGetString(item,
2083                                                                                                  &value->val.string.len);
2084                         break;
2085                 case jpiVariable:
2086                         getJsonPathVariable(cxt, item, cxt->vars, value);
2087                         return;
2088                 default:
2089                         elog(ERROR, "unexpected jsonpath item type");
2090         }
2091 }
2092
2093 /*
2094  * Get the value of variable passed to jsonpath executor
2095  */
2096 static void
2097 getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
2098                                         Jsonb *vars, JsonbValue *value)
2099 {
2100         char       *varName;
2101         int                     varNameLength;
2102         JsonbValue      tmp;
2103         JsonbValue *v;
2104
2105         if (!vars)
2106         {
2107                 value->type = jbvNull;
2108                 return;
2109         }
2110
2111         Assert(variable->type == jpiVariable);
2112         varName = jspGetString(variable, &varNameLength);
2113         tmp.type = jbvString;
2114         tmp.val.string.val = varName;
2115         tmp.val.string.len = varNameLength;
2116
2117         v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
2118
2119         if (v)
2120         {
2121                 *value = *v;
2122                 pfree(v);
2123         }
2124         else
2125         {
2126                 ereport(ERROR,
2127                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
2128                                  errmsg("could not find jsonpath variable \"%s\"",
2129                                                 pnstrdup(varName, varNameLength))));
2130         }
2131
2132         JsonbInitBinary(&tmp, vars);
2133         setBaseObject(cxt, &tmp, 1);
2134 }
2135
2136 /**************** Support functions for JsonPath execution *****************/
2137
2138 /*
2139  * Returns the size of an array item, or -1 if item is not an array.
2140  */
2141 static int
2142 JsonbArraySize(JsonbValue *jb)
2143 {
2144         Assert(jb->type != jbvArray);
2145
2146         if (jb->type == jbvBinary)
2147         {
2148                 JsonbContainer *jbc = jb->val.binary.data;
2149
2150                 if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
2151                         return JsonContainerSize(jbc);
2152         }
2153
2154         return -1;
2155 }
2156
2157 /* Comparison predicate callback. */
2158 static JsonPathBool
2159 executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
2160 {
2161         JsonPathExecContext *cxt = (JsonPathExecContext *) p;
2162
2163         return compareItems(cmp->type, lv, rv, cxt->useTz);
2164 }
2165
2166 /*
2167  * Perform per-byte comparison of two strings.
2168  */
2169 static int
2170 binaryCompareStrings(const char *s1, int len1,
2171                                          const char *s2, int len2)
2172 {
2173         int                     cmp;
2174
2175         cmp = memcmp(s1, s2, Min(len1, len2));
2176
2177         if (cmp != 0)
2178                 return cmp;
2179
2180         if (len1 == len2)
2181                 return 0;
2182
2183         return len1 < len2 ? -1 : 1;
2184 }
2185
2186 /*
2187  * Compare two strings in the current server encoding using Unicode codepoint
2188  * collation.
2189  */
2190 static int
2191 compareStrings(const char *mbstr1, int mblen1,
2192                            const char *mbstr2, int mblen2)
2193 {
2194         if (GetDatabaseEncoding() == PG_SQL_ASCII ||
2195                 GetDatabaseEncoding() == PG_UTF8)
2196         {
2197                 /*
2198                  * It's known property of UTF-8 strings that their per-byte comparison
2199                  * result matches codepoints comparison result.  ASCII can be
2200                  * considered as special case of UTF-8.
2201                  */
2202                 return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2203         }
2204         else
2205         {
2206                 char       *utf8str1,
2207                                    *utf8str2;
2208                 int                     cmp,
2209                                         utf8len1,
2210                                         utf8len2;
2211
2212                 /*
2213                  * We have to convert other encodings to UTF-8 first, then compare.
2214                  * Input strings may be not null-terminated and pg_server_to_any() may
2215                  * return them "as is".  So, use strlen() only if there is real
2216                  * conversion.
2217                  */
2218                 utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
2219                 utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
2220                 utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
2221                 utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
2222
2223                 cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
2224
2225                 /*
2226                  * If pg_server_to_any() did no real conversion, then we actually
2227                  * compared original strings.  So, we already done.
2228                  */
2229                 if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
2230                         return cmp;
2231
2232                 /* Free memory if needed */
2233                 if (mbstr1 != utf8str1)
2234                         pfree(utf8str1);
2235                 if (mbstr2 != utf8str2)
2236                         pfree(utf8str2);
2237
2238                 /*
2239                  * When all Unicode codepoints are equal, return result of binary
2240                  * comparison.  In some edge cases, same characters may have different
2241                  * representations in encoding.  Then our behavior could diverge from
2242                  * standard.  However, that allow us to do simple binary comparison
2243                  * for "==" operator, which is performance critical in typical cases.
2244                  * In future to implement strict standard conformance, we can do
2245                  * normalization of input JSON strings.
2246                  */
2247                 if (cmp == 0)
2248                         return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2249                 else
2250                         return cmp;
2251         }
2252 }
2253
2254 /*
2255  * Compare two SQL/JSON items using comparison operation 'op'.
2256  */
2257 static JsonPathBool
2258 compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
2259 {
2260         int                     cmp;
2261         bool            res;
2262
2263         if (jb1->type != jb2->type)
2264         {
2265                 if (jb1->type == jbvNull || jb2->type == jbvNull)
2266
2267                         /*
2268                          * Equality and order comparison of nulls to non-nulls returns
2269                          * always false, but inequality comparison returns true.
2270                          */
2271                         return op == jpiNotEqual ? jpbTrue : jpbFalse;
2272
2273                 /* Non-null items of different types are not comparable. */
2274                 return jpbUnknown;
2275         }
2276
2277         switch (jb1->type)
2278         {
2279                 case jbvNull:
2280                         cmp = 0;
2281                         break;
2282                 case jbvBool:
2283                         cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2284                                 jb1->val.boolean ? 1 : -1;
2285                         break;
2286                 case jbvNumeric:
2287                         cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2288                         break;
2289                 case jbvString:
2290                         if (op == jpiEqual)
2291                                 return jb1->val.string.len != jb2->val.string.len ||
2292                                         memcmp(jb1->val.string.val,
2293                                                    jb2->val.string.val,
2294                                                    jb1->val.string.len) ? jpbFalse : jpbTrue;
2295
2296                         cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
2297                                                                  jb2->val.string.val, jb2->val.string.len);
2298                         break;
2299                 case jbvDatetime:
2300                         {
2301                                 bool            have_error = false;
2302
2303                                 cmp = compareDatetime(jb1->val.datetime.value,
2304                                                                           jb1->val.datetime.typid,
2305                                                                           jb2->val.datetime.value,
2306                                                                           jb2->val.datetime.typid,
2307                                                                           useTz,
2308                                                                           &have_error);
2309
2310                                 if (have_error)
2311                                         return jpbUnknown;
2312                         }
2313                         break;
2314
2315                 case jbvBinary:
2316                 case jbvArray:
2317                 case jbvObject:
2318                         return jpbUnknown;      /* non-scalars are not comparable */
2319
2320                 default:
2321                         elog(ERROR, "invalid jsonb value type %d", jb1->type);
2322         }
2323
2324         switch (op)
2325         {
2326                 case jpiEqual:
2327                         res = (cmp == 0);
2328                         break;
2329                 case jpiNotEqual:
2330                         res = (cmp != 0);
2331                         break;
2332                 case jpiLess:
2333                         res = (cmp < 0);
2334                         break;
2335                 case jpiGreater:
2336                         res = (cmp > 0);
2337                         break;
2338                 case jpiLessOrEqual:
2339                         res = (cmp <= 0);
2340                         break;
2341                 case jpiGreaterOrEqual:
2342                         res = (cmp >= 0);
2343                         break;
2344                 default:
2345                         elog(ERROR, "unrecognized jsonpath operation: %d", op);
2346                         return jpbUnknown;
2347         }
2348
2349         return res ? jpbTrue : jpbFalse;
2350 }
2351
2352 /* Compare two numerics */
2353 static int
2354 compareNumeric(Numeric a, Numeric b)
2355 {
2356         return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2357                                                                                          NumericGetDatum(a),
2358                                                                                          NumericGetDatum(b)));
2359 }
2360
2361 static JsonbValue *
2362 copyJsonbValue(JsonbValue *src)
2363 {
2364         JsonbValue *dst = palloc(sizeof(*dst));
2365
2366         *dst = *src;
2367
2368         return dst;
2369 }
2370
2371 /*
2372  * Execute array subscript expression and convert resulting numeric item to
2373  * the integer type with truncation.
2374  */
2375 static JsonPathExecResult
2376 getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2377                           int32 *index)
2378 {
2379         JsonbValue *jbv;
2380         JsonValueList found = {0};
2381         JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2382         Datum           numeric_index;
2383         bool            have_error = false;
2384
2385         if (jperIsError(res))
2386                 return res;
2387
2388         if (JsonValueListLength(&found) != 1 ||
2389                 !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2390                 RETURN_ERROR(ereport(ERROR,
2391                                                          (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2392                                                           errmsg("jsonpath array subscript is not a single numeric value"))));
2393
2394         numeric_index = DirectFunctionCall2(numeric_trunc,
2395                                                                                 NumericGetDatum(jbv->val.numeric),
2396                                                                                 Int32GetDatum(0));
2397
2398         *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
2399                                                                         &have_error);
2400
2401         if (have_error)
2402                 RETURN_ERROR(ereport(ERROR,
2403                                                          (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2404                                                           errmsg("jsonpath array subscript is out of integer range"))));
2405
2406         return jperOk;
2407 }
2408
2409 /* Save base object and its id needed for the execution of .keyvalue(). */
2410 static JsonBaseObjectInfo
2411 setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2412 {
2413         JsonBaseObjectInfo baseObject = cxt->baseObject;
2414
2415         cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2416                 (JsonbContainer *) jbv->val.binary.data;
2417         cxt->baseObject.id = id;
2418
2419         return baseObject;
2420 }
2421
2422 static void
2423 JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2424 {
2425         if (jvl->singleton)
2426         {
2427                 jvl->list = list_make2(jvl->singleton, jbv);
2428                 jvl->singleton = NULL;
2429         }
2430         else if (!jvl->list)
2431                 jvl->singleton = jbv;
2432         else
2433                 jvl->list = lappend(jvl->list, jbv);
2434 }
2435
2436 static int
2437 JsonValueListLength(const JsonValueList *jvl)
2438 {
2439         return jvl->singleton ? 1 : list_length(jvl->list);
2440 }
2441
2442 static bool
2443 JsonValueListIsEmpty(JsonValueList *jvl)
2444 {
2445         return !jvl->singleton && list_length(jvl->list) <= 0;
2446 }
2447
2448 static JsonbValue *
2449 JsonValueListHead(JsonValueList *jvl)
2450 {
2451         return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2452 }
2453
2454 static List *
2455 JsonValueListGetList(JsonValueList *jvl)
2456 {
2457         if (jvl->singleton)
2458                 return list_make1(jvl->singleton);
2459
2460         return jvl->list;
2461 }
2462
2463 static void
2464 JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2465 {
2466         if (jvl->singleton)
2467         {
2468                 it->value = jvl->singleton;
2469                 it->list = NIL;
2470                 it->next = NULL;
2471         }
2472         else if (jvl->list != NIL)
2473         {
2474                 it->value = (JsonbValue *) linitial(jvl->list);
2475                 it->list = jvl->list;
2476                 it->next = list_second_cell(jvl->list);
2477         }
2478         else
2479         {
2480                 it->value = NULL;
2481                 it->list = NIL;
2482                 it->next = NULL;
2483         }
2484 }
2485
2486 /*
2487  * Get the next item from the sequence advancing iterator.
2488  */
2489 static JsonbValue *
2490 JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2491 {
2492         JsonbValue *result = it->value;
2493
2494         if (it->next)
2495         {
2496                 it->value = lfirst(it->next);
2497                 it->next = lnext(it->list, it->next);
2498         }
2499         else
2500         {
2501                 it->value = NULL;
2502         }
2503
2504         return result;
2505 }
2506
2507 /*
2508  * Initialize a binary JsonbValue with the given jsonb container.
2509  */
2510 static JsonbValue *
2511 JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2512 {
2513         jbv->type = jbvBinary;
2514         jbv->val.binary.data = &jb->root;
2515         jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2516
2517         return jbv;
2518 }
2519
2520 /*
2521  * Returns jbv* type of of JsonbValue. Note, it never returns jbvBinary as is.
2522  */
2523 static int
2524 JsonbType(JsonbValue *jb)
2525 {
2526         int                     type = jb->type;
2527
2528         if (jb->type == jbvBinary)
2529         {
2530                 JsonbContainer *jbc = (void *) jb->val.binary.data;
2531
2532                 /* Scalars should be always extracted during jsonpath execution. */
2533                 Assert(!JsonContainerIsScalar(jbc));
2534
2535                 if (JsonContainerIsObject(jbc))
2536                         type = jbvObject;
2537                 else if (JsonContainerIsArray(jbc))
2538                         type = jbvArray;
2539                 else
2540                         elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2541         }
2542
2543         return type;
2544 }
2545
2546 /* Get scalar of given type or NULL on type mismatch */
2547 static JsonbValue *
2548 getScalar(JsonbValue *scalar, enum jbvType type)
2549 {
2550         /* Scalars should be always extracted during jsonpath execution. */
2551         Assert(scalar->type != jbvBinary ||
2552                    !JsonContainerIsScalar(scalar->val.binary.data));
2553
2554         return scalar->type == type ? scalar : NULL;
2555 }
2556
2557 /* Construct a JSON array from the item list */
2558 static JsonbValue *
2559 wrapItemsInArray(const JsonValueList *items)
2560 {
2561         JsonbParseState *ps = NULL;
2562         JsonValueListIterator it;
2563         JsonbValue *jbv;
2564
2565         pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2566
2567         JsonValueListInitIterator(items, &it);
2568         while ((jbv = JsonValueListNext(items, &it)))
2569                 pushJsonbValue(&ps, WJB_ELEM, jbv);
2570
2571         return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
2572 }
2573
2574 /*
2575  * Cross-type comparison of two datetime SQL/JSON items.  If items are
2576  * uncomparable, 'error' flag is set.
2577  */
2578 static int
2579 compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
2580                                 bool useTz, bool *have_error)
2581 {
2582         PGFunction cmpfunc = NULL;
2583
2584         switch (typid1)
2585         {
2586                 case DATEOID:
2587                         switch (typid2)
2588                         {
2589                                 case DATEOID:
2590                                         cmpfunc = date_cmp;
2591
2592                                         break;
2593
2594                                 case TIMESTAMPOID:
2595                                         val1 = date2timestamp_opt_error(val1, have_error);
2596                                         if (have_error && *have_error)
2597                                                 return 0;
2598                                         cmpfunc = timestamp_cmp;
2599
2600                                         break;
2601
2602                                 case TIMESTAMPTZOID:
2603                                         if (!useTz)
2604                                                 ereport(ERROR,
2605                                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2606                                                                  errmsg("cannot convert value from %s to %s without timezone usage",
2607                                                                                 "date", "timestamptz"),
2608                                                                  errhint("use *_tz() function for timezone support")));
2609                                         val1 = date2timestamptz_opt_error(val1, have_error);
2610                                         if (have_error && *have_error)
2611                                                 return 0;
2612                                         cmpfunc = timestamp_cmp;
2613
2614                                         break;
2615
2616                                 case TIMEOID:
2617                                 case TIMETZOID:
2618                                         *have_error = true;
2619                                         return 0;
2620                         }
2621                         break;
2622
2623                 case TIMEOID:
2624                         switch (typid2)
2625                         {
2626                                 case TIMEOID:
2627                                         cmpfunc = time_cmp;
2628
2629                                         break;
2630
2631                                 case TIMETZOID:
2632                                         if (!useTz)
2633                                                 ereport(ERROR,
2634                                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2635                                                                  errmsg("cannot convert value from %s to %s without timezone usage",
2636                                                                                 "time", "timetz"),
2637                                                                  errhint("use *_tz() function for timezone support")));
2638                                         val1 = DirectFunctionCall1(time_timetz, val1);
2639                                         cmpfunc = timetz_cmp;
2640
2641                                         break;
2642
2643                                 case DATEOID:
2644                                 case TIMESTAMPOID:
2645                                 case TIMESTAMPTZOID:
2646                                         *have_error = true;
2647                                         return 0;
2648                         }
2649                         break;
2650
2651                 case TIMETZOID:
2652                         switch (typid2)
2653                         {
2654                                 case TIMEOID:
2655                                         if (!useTz)
2656                                                 ereport(ERROR,
2657                                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2658                                                                  errmsg("cannot convert value from %s to %s without timezone usage",
2659                                                                                 "time", "timetz"),
2660                                                                  errhint("use *_tz() function for timezone support")));
2661                                         val2 = DirectFunctionCall1(time_timetz, val2);
2662                                         cmpfunc = timetz_cmp;
2663
2664                                         break;
2665
2666                                 case TIMETZOID:
2667                                         cmpfunc = timetz_cmp;
2668
2669                                         break;
2670
2671                                 case DATEOID:
2672                                 case TIMESTAMPOID:
2673                                 case TIMESTAMPTZOID:
2674                                         *have_error = true;
2675                                         return 0;
2676                         }
2677                         break;
2678
2679                 case TIMESTAMPOID:
2680                         switch (typid2)
2681                         {
2682                                 case DATEOID:
2683                                         val2 = date2timestamp_opt_error(val2, have_error);
2684                                         if (have_error && *have_error)
2685                                                 return 0;
2686                                         cmpfunc = timestamp_cmp;
2687
2688                                         break;
2689
2690                                 case TIMESTAMPOID:
2691                                         cmpfunc = timestamp_cmp;
2692
2693                                         break;
2694
2695                                 case TIMESTAMPTZOID:
2696                                         if (!useTz)
2697                                                 ereport(ERROR,
2698                                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2699                                                                  errmsg("cannot convert value from %s to %s without timezone usage",
2700                                                                                 "timestamp", "timestamptz"),
2701                                                                  errhint("use *_tz() function for timezone support")));
2702                                         val1 = timestamp2timestamptz_opt_error(val1, have_error);
2703                                         if (have_error && *have_error)
2704                                                 return 0;
2705                                         cmpfunc = timestamp_cmp;
2706
2707                                         break;
2708
2709                                 case TIMEOID:
2710                                 case TIMETZOID:
2711                                         *have_error = true;
2712                                         return 0;
2713                         }
2714                         break;
2715
2716                 case TIMESTAMPTZOID:
2717                         switch (typid2)
2718                         {
2719                                 case DATEOID:
2720                                         if (!useTz)
2721                                                 ereport(ERROR,
2722                                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2723                                                                  errmsg("cannot convert value from %s to %s without timezone usage",
2724                                                                                 "date", "timestamptz"),
2725                                                                  errhint("use *_tz() function for timezone support")));
2726                                         val2 = date2timestamptz_opt_error(val2, have_error);
2727                                         if (have_error && *have_error)
2728                                                 return 0;
2729                                         cmpfunc = timestamp_cmp;
2730
2731                                         break;
2732
2733                                 case TIMESTAMPOID:
2734                                         if (!useTz)
2735                                                 ereport(ERROR,
2736                                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2737                                                                  errmsg("cannot convert value from %s to %s without timezone usage",
2738                                                                                 "timestamp", "timestamptz"),
2739                                                                  errhint("use *_tz() function for timezone support")));
2740                                         val2 = timestamp2timestamptz_opt_error(val2, have_error);
2741                                         if (have_error && *have_error)
2742                                                 return 0;
2743                                         cmpfunc = timestamp_cmp;
2744
2745                                         break;
2746
2747                                 case TIMESTAMPTZOID:
2748                                         cmpfunc = timestamp_cmp;
2749
2750                                         break;
2751
2752                                 case TIMEOID:
2753                                 case TIMETZOID:
2754                                         *have_error = true;
2755                                         return 0;
2756                         }
2757                         break;
2758
2759                 default:
2760                         elog(ERROR, "unrecognized SQL/JSON datetime type oid: %d",
2761                                  typid1);
2762         }
2763
2764         if (*have_error)
2765                 return 0;
2766
2767         if (!cmpfunc)
2768                 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %d",
2769                          typid2);
2770
2771         *have_error = false;
2772
2773         return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
2774 }