LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_exec.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 952 1025 92.9 %
Date: 2023-05-31 03:12:18 Functions: 60 64 93.8 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14