LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_exec.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 91.8 % 1753 1610
Test Date: 2026-04-07 14:16:30 Functions: 95.5 % 88 84
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-2026, 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 "miscadmin.h"
      66              : #include "nodes/miscnodes.h"
      67              : #include "nodes/nodeFuncs.h"
      68              : #include "regex/regex.h"
      69              : #include "utils/builtins.h"
      70              : #include "utils/date.h"
      71              : #include "utils/datetime.h"
      72              : #include "utils/float.h"
      73              : #include "utils/formatting.h"
      74              : #include "utils/json.h"
      75              : #include "utils/jsonpath.h"
      76              : #include "utils/memutils.h"
      77              : #include "utils/timestamp.h"
      78              : 
      79              : /*
      80              :  * Represents "base object" and its "id" for .keyvalue() evaluation.
      81              :  */
      82              : typedef struct JsonBaseObjectInfo
      83              : {
      84              :     JsonbContainer *jbc;
      85              :     int         id;
      86              : } JsonBaseObjectInfo;
      87              : 
      88              : /* Callbacks for executeJsonPath() */
      89              : typedef JsonbValue *(*JsonPathGetVarCallback) (void *vars, char *varName, int varNameLen,
      90              :                                                JsonbValue *baseObject, int *baseObjectId);
      91              : typedef int (*JsonPathCountVarsCallback) (void *vars);
      92              : 
      93              : /*
      94              :  * Context of jsonpath execution.
      95              :  */
      96              : typedef struct JsonPathExecContext
      97              : {
      98              :     void       *vars;           /* variables to substitute into jsonpath */
      99              :     JsonPathGetVarCallback getVar;  /* callback to extract a given variable
     100              :                                      * from 'vars' */
     101              :     JsonbValue *root;           /* for $ evaluation */
     102              :     JsonbValue *current;        /* for @ evaluation */
     103              :     JsonBaseObjectInfo baseObject;  /* "base object" for .keyvalue()
     104              :                                      * evaluation */
     105              :     int         lastGeneratedObjectId;  /* "id" counter for .keyvalue()
     106              :                                          * evaluation */
     107              :     int         innermostArraySize; /* for LAST array index evaluation */
     108              :     bool        laxMode;        /* true for "lax" mode, false for "strict"
     109              :                                  * mode */
     110              :     bool        ignoreStructuralErrors; /* with "true" structural errors such
     111              :                                          * as absence of required json item or
     112              :                                          * unexpected json item type are
     113              :                                          * ignored */
     114              :     bool        throwErrors;    /* with "false" all suppressible errors are
     115              :                                  * suppressed */
     116              :     bool        useTz;
     117              : } JsonPathExecContext;
     118              : 
     119              : /* Context for LIKE_REGEX execution. */
     120              : typedef struct JsonLikeRegexContext
     121              : {
     122              :     text       *regex;
     123              :     int         cflags;
     124              : } JsonLikeRegexContext;
     125              : 
     126              : /* Result of jsonpath predicate evaluation */
     127              : typedef enum JsonPathBool
     128              : {
     129              :     jpbFalse = 0,
     130              :     jpbTrue = 1,
     131              :     jpbUnknown = 2
     132              : } JsonPathBool;
     133              : 
     134              : /* Result of jsonpath expression evaluation */
     135              : typedef enum JsonPathExecResult
     136              : {
     137              :     jperOk = 0,
     138              :     jperNotFound = 1,
     139              :     jperError = 2
     140              : } JsonPathExecResult;
     141              : 
     142              : #define jperIsError(jper)           ((jper) == jperError)
     143              : 
     144              : /*
     145              :  * List (or really array) of JsonbValues.  This is the output representation
     146              :  * of jsonpath evaluation.
     147              :  *
     148              :  * The initial or "base" chunk of a list is typically a local variable in
     149              :  * a calling function.  If we need more entries than will fit in the base
     150              :  * chunk, we palloc more chunks.  For notational simplicity, those are also
     151              :  * treated as being of type JsonValueList, although they will have items[]
     152              :  * arrays that are larger than BASE_JVL_ITEMS.
     153              :  *
     154              :  * Callers *must* initialize the base chunk with JsonValueListInit().
     155              :  * Typically they should free any extra chunks when done, using
     156              :  * JsonValueListClear(), although some top-level functions skip that
     157              :  * on the assumption that the caller's context will be reset soon.
     158              :  *
     159              :  * Note that most types of JsonbValue include pointers to external data, which
     160              :  * will not be managed by the JsonValueList functions.  We expect that such
     161              :  * data is part of the input to the jsonpath operation, and the caller will
     162              :  * see to it that it holds still for the duration of the operation.
     163              :  *
     164              :  * Most lists are short, though some can be quite long.  So we set
     165              :  * BASE_JVL_ITEMS small to conserve stack space, but grow the extra
     166              :  * chunks aggressively.
     167              :  */
     168              : #define BASE_JVL_ITEMS 2        /* number of items a base chunk holds */
     169              : #define MIN_EXTRA_JVL_ITEMS 16  /* min number of items an extra chunk holds */
     170              : 
     171              : typedef struct JsonValueList
     172              : {
     173              :     int         nitems;         /* number of items stored in this chunk */
     174              :     int         maxitems;       /* allocated length of items[] */
     175              :     struct JsonValueList *next; /* => next chunk, if any */
     176              :     struct JsonValueList *last; /* => last chunk (only valid in base chunk) */
     177              :     JsonbValue  items[BASE_JVL_ITEMS];
     178              : } JsonValueList;
     179              : 
     180              : /* State data for iterating through a JsonValueList */
     181              : typedef struct JsonValueListIterator
     182              : {
     183              :     JsonValueList *chunk;       /* current chunk of list */
     184              :     int         nextitem;       /* index of next value to return in chunk */
     185              : } JsonValueListIterator;
     186              : 
     187              : /* Structures for JSON_TABLE execution  */
     188              : 
     189              : /*
     190              :  * Struct holding the result of jsonpath evaluation, to be used as source row
     191              :  * for JsonTableGetValue() which in turn computes the values of individual
     192              :  * JSON_TABLE columns.
     193              :  */
     194              : typedef struct JsonTablePlanRowSource
     195              : {
     196              :     Datum       value;
     197              :     bool        isnull;
     198              : } JsonTablePlanRowSource;
     199              : 
     200              : /*
     201              :  * State of evaluation of row pattern derived by applying jsonpath given in
     202              :  * a JsonTablePlan to an input document given in the parent TableFunc.
     203              :  */
     204              : typedef struct JsonTablePlanState
     205              : {
     206              :     /* Original plan */
     207              :     JsonTablePlan *plan;
     208              : 
     209              :     /* The following fields are only valid for JsonTablePathScan plans */
     210              : 
     211              :     /* jsonpath to evaluate against the input doc to get the row pattern */
     212              :     JsonPath   *path;
     213              : 
     214              :     /*
     215              :      * Memory context to use when evaluating the row pattern from the jsonpath
     216              :      */
     217              :     MemoryContext mcxt;
     218              : 
     219              :     /* PASSING arguments passed to jsonpath executor */
     220              :     List       *args;
     221              : 
     222              :     /* List and iterator of jsonpath result values */
     223              :     JsonValueList found;
     224              :     JsonValueListIterator iter;
     225              : 
     226              :     /* Currently selected row for JsonTableGetValue() to use */
     227              :     JsonTablePlanRowSource current;
     228              : 
     229              :     /* Counter for ORDINAL columns */
     230              :     int         ordinal;
     231              : 
     232              :     /* Nested plan, if any */
     233              :     struct JsonTablePlanState *nested;
     234              : 
     235              :     /* Left sibling, if any */
     236              :     struct JsonTablePlanState *left;
     237              : 
     238              :     /* Right sibling, if any */
     239              :     struct JsonTablePlanState *right;
     240              : 
     241              :     /* Parent plan, if this is a nested plan */
     242              :     struct JsonTablePlanState *parent;
     243              : } JsonTablePlanState;
     244              : 
     245              : /* Random number to identify JsonTableExecContext for sanity checking */
     246              : #define JSON_TABLE_EXEC_CONTEXT_MAGIC       418352867
     247              : 
     248              : typedef struct JsonTableExecContext
     249              : {
     250              :     int         magic;
     251              : 
     252              :     /* State of the plan providing a row evaluated from "root" jsonpath */
     253              :     JsonTablePlanState *rootplanstate;
     254              : 
     255              :     /*
     256              :      * Per-column JsonTablePlanStates for all columns including the nested
     257              :      * ones.
     258              :      */
     259              :     JsonTablePlanState **colplanstates;
     260              : } JsonTableExecContext;
     261              : 
     262              : /* strict/lax flags is decomposed into four [un]wrap/error flags */
     263              : #define jspStrictAbsenceOfErrors(cxt)   (!(cxt)->laxMode)
     264              : #define jspAutoUnwrap(cxt)              ((cxt)->laxMode)
     265              : #define jspAutoWrap(cxt)                ((cxt)->laxMode)
     266              : #define jspIgnoreStructuralErrors(cxt)  ((cxt)->ignoreStructuralErrors)
     267              : #define jspThrowErrors(cxt)             ((cxt)->throwErrors)
     268              : 
     269              : /* Convenience macro: return or throw error depending on context */
     270              : #define RETURN_ERROR(throw_error) \
     271              : do { \
     272              :     if (jspThrowErrors(cxt)) \
     273              :         throw_error; \
     274              :     else \
     275              :         return jperError; \
     276              : } while (0)
     277              : 
     278              : typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
     279              :                                                    JsonbValue *larg,
     280              :                                                    JsonbValue *rarg,
     281              :                                                    void *param);
     282              : typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2,
     283              :                                      Node *escontext);
     284              : 
     285              : static JsonPathExecResult executeJsonPath(JsonPath *path, void *vars,
     286              :                                           JsonPathGetVarCallback getVar,
     287              :                                           JsonPathCountVarsCallback countVars,
     288              :                                           Jsonb *json, bool throwErrors,
     289              :                                           JsonValueList *result, bool useTz);
     290              : static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
     291              :                                       JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
     292              : static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
     293              :                                                      JsonPathItem *jsp, JsonbValue *jb,
     294              :                                                      JsonValueList *found, bool unwrap);
     295              : static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
     296              :                                                        JsonPathItem *jsp, JsonbValue *jb,
     297              :                                                        JsonValueList *found, bool unwrapElements);
     298              : static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
     299              :                                           JsonPathItem *cur, JsonPathItem *next,
     300              :                                           JsonbValue *v, JsonValueList *found);
     301              : static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
     302              :                                                      bool unwrap, JsonValueList *found);
     303              : static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp,
     304              :                                                             JsonbValue *jb, bool unwrap, JsonValueList *found);
     305              : static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
     306              :                                     JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
     307              : static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
     308              :                                           JsonPathItem *jsp, JsonbValue *jb);
     309              : static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
     310              :                                          JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
     311              :                                          uint32 level, uint32 first, uint32 last,
     312              :                                          bool ignoreStructuralErrors, bool unwrapNext);
     313              : static JsonPathBool executePredicate(JsonPathExecContext *cxt,
     314              :                                      JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
     315              :                                      JsonbValue *jb, bool unwrapRightArg,
     316              :                                      JsonPathPredicateCallback exec, void *param);
     317              : static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
     318              :                                                   JsonPathItem *jsp, JsonbValue *jb,
     319              :                                                   BinaryArithmFunc func, JsonValueList *found);
     320              : static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
     321              :                                                  JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
     322              :                                                  JsonValueList *found);
     323              : static JsonPathBool executeStartsWith(JsonPathItem *jsp,
     324              :                                       JsonbValue *whole, JsonbValue *initial, void *param);
     325              : static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
     326              :                                      JsonbValue *rarg, void *param);
     327              : static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
     328              :                                                    JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
     329              :                                                    JsonValueList *found);
     330              : static JsonPathExecResult executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
     331              :                                                 JsonbValue *jb, JsonValueList *found);
     332              : static JsonPathExecResult executeStringInternalMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
     333              :                                                       JsonbValue *jb, JsonValueList *found);
     334              : static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
     335              :                                                 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
     336              : static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
     337              :                                            JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
     338              : static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
     339              :                             JsonbValue *value);
     340              : static JsonbValue *GetJsonPathVar(void *cxt, char *varName, int varNameLen,
     341              :                                   JsonbValue *baseObject, int *baseObjectId);
     342              : static int  CountJsonPathVars(void *cxt);
     343              : static void JsonItemFromDatum(Datum val, Oid typid, int32 typmod,
     344              :                               JsonbValue *res);
     345              : static void JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num);
     346              : static void getJsonPathVariable(JsonPathExecContext *cxt,
     347              :                                 JsonPathItem *variable, JsonbValue *value);
     348              : static int  countVariablesFromJsonb(void *varsJsonb);
     349              : static JsonbValue *getJsonPathVariableFromJsonb(void *varsJsonb, char *varName,
     350              :                                                 int varNameLength,
     351              :                                                 JsonbValue *baseObject,
     352              :                                                 int *baseObjectId);
     353              : static int  JsonbArraySize(JsonbValue *jb);
     354              : static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
     355              :                                       JsonbValue *rv, void *p);
     356              : static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2,
     357              :                                  bool useTz);
     358              : static int  compareNumeric(Numeric a, Numeric b);
     359              : static JsonbValue *copyJsonbValue(JsonbValue *src);
     360              : static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
     361              :                                         JsonPathItem *jsp, JsonbValue *jb, int32 *index);
     362              : static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
     363              :                                         JsonbValue *jbv, int32 id);
     364              : static void JsonValueListInit(JsonValueList *jvl);
     365              : static void JsonValueListClear(JsonValueList *jvl);
     366              : static void JsonValueListAppend(JsonValueList *jvl, const JsonbValue *jbv);
     367              : static bool JsonValueListIsEmpty(const JsonValueList *jvl);
     368              : static bool JsonValueListIsSingleton(const JsonValueList *jvl);
     369              : static bool JsonValueListHasMultipleItems(const JsonValueList *jvl);
     370              : static JsonbValue *JsonValueListHead(JsonValueList *jvl);
     371              : static void JsonValueListInitIterator(JsonValueList *jvl,
     372              :                                       JsonValueListIterator *it);
     373              : static JsonbValue *JsonValueListNext(JsonValueListIterator *it);
     374              : static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
     375              : static int  JsonbType(JsonbValue *jb);
     376              : static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
     377              : static JsonbValue *wrapItemsInArray(JsonValueList *items);
     378              : static int  compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
     379              :                             bool useTz, bool *cast_error);
     380              : static void checkTimezoneIsUsedForCast(bool useTz, const char *type1,
     381              :                                        const char *type2);
     382              : 
     383              : static void JsonTableInitOpaque(TableFuncScanState *state, int natts);
     384              : static JsonTablePlanState *JsonTableInitPlan(JsonTableExecContext *cxt,
     385              :                                              JsonTablePlan *plan,
     386              :                                              JsonTablePlanState *parentstate,
     387              :                                              List *args,
     388              :                                              MemoryContext mcxt);
     389              : static void JsonTableSetDocument(TableFuncScanState *state, Datum value);
     390              : static void JsonTableResetRowPattern(JsonTablePlanState *planstate, Datum item);
     391              : static bool JsonTableFetchRow(TableFuncScanState *state);
     392              : static Datum JsonTableGetValue(TableFuncScanState *state, int colnum,
     393              :                                Oid typid, int32 typmod, bool *isnull);
     394              : static void JsonTableDestroyOpaque(TableFuncScanState *state);
     395              : static bool JsonTablePlanScanNextRow(JsonTablePlanState *planstate);
     396              : static void JsonTableResetNestedPlan(JsonTablePlanState *planstate);
     397              : static bool JsonTablePlanJoinNextRow(JsonTablePlanState *planstate);
     398              : static bool JsonTablePlanNextRow(JsonTablePlanState *planstate);
     399              : 
     400              : const TableFuncRoutine JsonbTableRoutine =
     401              : {
     402              :     .InitOpaque = JsonTableInitOpaque,
     403              :     .SetDocument = JsonTableSetDocument,
     404              :     .SetNamespace = NULL,
     405              :     .SetRowFilter = NULL,
     406              :     .SetColumnFilter = NULL,
     407              :     .FetchRow = JsonTableFetchRow,
     408              :     .GetValue = JsonTableGetValue,
     409              :     .DestroyOpaque = JsonTableDestroyOpaque
     410              : };
     411              : 
     412              : /****************** User interface to JsonPath executor ********************/
     413              : 
     414              : /*
     415              :  * jsonb_path_exists
     416              :  *      Returns true if jsonpath returns at least one item for the specified
     417              :  *      jsonb value.  This function and jsonb_path_match() are used to
     418              :  *      implement @? and @@ operators, which in turn are intended to have an
     419              :  *      index support.  Thus, it's desirable to make it easier to achieve
     420              :  *      consistency between index scan results and sequential scan results.
     421              :  *      So, we throw as few errors as possible.  Regarding this function,
     422              :  *      such behavior also matches behavior of JSON_EXISTS() clause of
     423              :  *      SQL/JSON.  Regarding jsonb_path_match(), this function doesn't have
     424              :  *      an analogy in SQL/JSON, so we define its behavior on our own.
     425              :  */
     426              : static Datum
     427        57470 : jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
     428              : {
     429        57470 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     430        57470 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     431              :     JsonPathExecResult res;
     432        57470 :     Jsonb      *vars = NULL;
     433        57470 :     bool        silent = true;
     434              : 
     435        57470 :     if (PG_NARGS() == 4)
     436              :     {
     437           43 :         vars = PG_GETARG_JSONB_P(2);
     438           43 :         silent = PG_GETARG_BOOL(3);
     439              :     }
     440              : 
     441        57470 :     res = executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
     442              :                           countVariablesFromJsonb,
     443        57470 :                           jb, !silent, NULL, tz);
     444              : 
     445        57462 :     PG_FREE_IF_COPY(jb, 0);
     446        57462 :     PG_FREE_IF_COPY(jp, 1);
     447              : 
     448        57462 :     if (jperIsError(res))
     449           50 :         PG_RETURN_NULL();
     450              : 
     451        57412 :     PG_RETURN_BOOL(res == jperOk);
     452              : }
     453              : 
     454              : Datum
     455           43 : jsonb_path_exists(PG_FUNCTION_ARGS)
     456              : {
     457           43 :     return jsonb_path_exists_internal(fcinfo, false);
     458              : }
     459              : 
     460              : Datum
     461            0 : jsonb_path_exists_tz(PG_FUNCTION_ARGS)
     462              : {
     463            0 :     return jsonb_path_exists_internal(fcinfo, true);
     464              : }
     465              : 
     466              : /*
     467              :  * jsonb_path_exists_opr
     468              :  *      Implementation of operator "jsonb @? jsonpath" (2-argument version of
     469              :  *      jsonb_path_exists()).
     470              :  */
     471              : Datum
     472        57427 : jsonb_path_exists_opr(PG_FUNCTION_ARGS)
     473              : {
     474              :     /* just call the other one -- it can handle both cases */
     475        57427 :     return jsonb_path_exists_internal(fcinfo, false);
     476              : }
     477              : 
     478              : /*
     479              :  * jsonb_path_match
     480              :  *      Returns jsonpath predicate result item for the specified jsonb value.
     481              :  *      See jsonb_path_exists() comment for details regarding error handling.
     482              :  */
     483              : static Datum
     484        65299 : jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
     485              : {
     486        65299 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     487        65299 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     488        65299 :     Jsonb      *vars = NULL;
     489        65299 :     bool        silent = true;
     490              :     JsonValueList found;
     491              : 
     492        65299 :     if (PG_NARGS() == 4)
     493              :     {
     494           97 :         vars = PG_GETARG_JSONB_P(2);
     495           97 :         silent = PG_GETARG_BOOL(3);
     496              :     }
     497              : 
     498        65299 :     JsonValueListInit(&found);
     499              : 
     500        65299 :     (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
     501              :                            countVariablesFromJsonb,
     502        65299 :                            jb, !silent, &found, tz);
     503              : 
     504        65291 :     PG_FREE_IF_COPY(jb, 0);
     505        65291 :     PG_FREE_IF_COPY(jp, 1);
     506              : 
     507        65291 :     if (JsonValueListIsSingleton(&found))
     508              :     {
     509        65268 :         JsonbValue *jbv = JsonValueListHead(&found);
     510              : 
     511        65268 :         if (jbv->type == jbvBool)
     512        65212 :             PG_RETURN_BOOL(jbv->val.boolean);
     513              : 
     514           56 :         if (jbv->type == jbvNull)
     515           20 :             PG_RETURN_NULL();
     516              :     }
     517              : 
     518           59 :     if (!silent)
     519           24 :         ereport(ERROR,
     520              :                 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
     521              :                  errmsg("single boolean result is expected")));
     522              : 
     523           35 :     PG_RETURN_NULL();
     524              : }
     525              : 
     526              : Datum
     527           97 : jsonb_path_match(PG_FUNCTION_ARGS)
     528              : {
     529           97 :     return jsonb_path_match_internal(fcinfo, false);
     530              : }
     531              : 
     532              : Datum
     533            0 : jsonb_path_match_tz(PG_FUNCTION_ARGS)
     534              : {
     535            0 :     return jsonb_path_match_internal(fcinfo, true);
     536              : }
     537              : 
     538              : /*
     539              :  * jsonb_path_match_opr
     540              :  *      Implementation of operator "jsonb @@ jsonpath" (2-argument version of
     541              :  *      jsonb_path_match()).
     542              :  */
     543              : Datum
     544        65202 : jsonb_path_match_opr(PG_FUNCTION_ARGS)
     545              : {
     546              :     /* just call the other one -- it can handle both cases */
     547        65202 :     return jsonb_path_match_internal(fcinfo, false);
     548              : }
     549              : 
     550              : /*
     551              :  * jsonb_path_query
     552              :  *      Executes jsonpath for given jsonb document and returns result as
     553              :  *      rowset.
     554              :  */
     555              : static Datum
     556         5396 : jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
     557              : {
     558              :     FuncCallContext *funcctx;
     559              :     JsonValueListIterator *iter;
     560              :     JsonbValue *v;
     561              : 
     562         5396 :     if (SRF_IS_FIRSTCALL())
     563              :     {
     564              :         JsonPath   *jp;
     565              :         Jsonb      *jb;
     566              :         Jsonb      *vars;
     567              :         bool        silent;
     568              :         MemoryContext oldcontext;
     569              :         JsonValueList *found;
     570              : 
     571         3040 :         funcctx = SRF_FIRSTCALL_INIT();
     572         3040 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     573              : 
     574         3040 :         jb = PG_GETARG_JSONB_P_COPY(0);
     575         3040 :         jp = PG_GETARG_JSONPATH_P_COPY(1);
     576         3040 :         vars = PG_GETARG_JSONB_P_COPY(2);
     577         3040 :         silent = PG_GETARG_BOOL(3);
     578              : 
     579         3040 :         found = palloc_object(JsonValueList);
     580         3040 :         JsonValueListInit(found);
     581              : 
     582         3040 :         (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
     583              :                                countVariablesFromJsonb,
     584         3040 :                                jb, !silent, found, tz);
     585              : 
     586         2012 :         iter = palloc_object(JsonValueListIterator);
     587         2012 :         JsonValueListInitIterator(found, iter);
     588              : 
     589         2012 :         funcctx->user_fctx = iter;
     590              : 
     591         2012 :         MemoryContextSwitchTo(oldcontext);
     592              :     }
     593              : 
     594         4368 :     funcctx = SRF_PERCALL_SETUP();
     595         4368 :     iter = funcctx->user_fctx;
     596              : 
     597         4368 :     v = JsonValueListNext(iter);
     598              : 
     599         4368 :     if (v == NULL)
     600         2012 :         SRF_RETURN_DONE(funcctx);
     601              : 
     602         2356 :     SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
     603              : }
     604              : 
     605              : Datum
     606         4392 : jsonb_path_query(PG_FUNCTION_ARGS)
     607              : {
     608         4392 :     return jsonb_path_query_internal(fcinfo, false);
     609              : }
     610              : 
     611              : Datum
     612         1004 : jsonb_path_query_tz(PG_FUNCTION_ARGS)
     613              : {
     614         1004 :     return jsonb_path_query_internal(fcinfo, true);
     615              : }
     616              : 
     617              : /*
     618              :  * jsonb_path_query_array
     619              :  *      Executes jsonpath for given jsonb document and returns result as
     620              :  *      jsonb array.
     621              :  */
     622              : static Datum
     623           82 : jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
     624              : {
     625           82 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     626           82 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     627           82 :     Jsonb      *vars = PG_GETARG_JSONB_P(2);
     628           82 :     bool        silent = PG_GETARG_BOOL(3);
     629              :     JsonValueList found;
     630              : 
     631           82 :     JsonValueListInit(&found);
     632              : 
     633           82 :     (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
     634              :                            countVariablesFromJsonb,
     635           82 :                            jb, !silent, &found, tz);
     636              : 
     637           78 :     PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
     638              : }
     639              : 
     640              : Datum
     641           82 : jsonb_path_query_array(PG_FUNCTION_ARGS)
     642              : {
     643           82 :     return jsonb_path_query_array_internal(fcinfo, false);
     644              : }
     645              : 
     646              : Datum
     647            0 : jsonb_path_query_array_tz(PG_FUNCTION_ARGS)
     648              : {
     649            0 :     return jsonb_path_query_array_internal(fcinfo, true);
     650              : }
     651              : 
     652              : /*
     653              :  * jsonb_path_query_first
     654              :  *      Executes jsonpath for given jsonb document and returns first result
     655              :  *      item.  If there are no items, NULL returned.
     656              :  */
     657              : static Datum
     658         2923 : jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
     659              : {
     660         2923 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     661         2923 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     662         2923 :     Jsonb      *vars = PG_GETARG_JSONB_P(2);
     663         2923 :     bool        silent = PG_GETARG_BOOL(3);
     664              :     JsonValueList found;
     665              : 
     666         2923 :     JsonValueListInit(&found);
     667              : 
     668         2923 :     (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
     669              :                            countVariablesFromJsonb,
     670         2923 :                            jb, !silent, &found, tz);
     671              : 
     672         2915 :     if (!JsonValueListIsEmpty(&found))
     673         2905 :         PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
     674              :     else
     675           10 :         PG_RETURN_NULL();
     676              : }
     677              : 
     678              : Datum
     679         2923 : jsonb_path_query_first(PG_FUNCTION_ARGS)
     680              : {
     681         2923 :     return jsonb_path_query_first_internal(fcinfo, false);
     682              : }
     683              : 
     684              : Datum
     685            0 : jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
     686              : {
     687            0 :     return jsonb_path_query_first_internal(fcinfo, true);
     688              : }
     689              : 
     690              : /********************Execute functions for JsonPath**************************/
     691              : 
     692              : /*
     693              :  * Interface to jsonpath executor
     694              :  *
     695              :  * 'path' - jsonpath to be executed
     696              :  * 'vars' - variables to be substituted to jsonpath
     697              :  * 'getVar' - callback used by getJsonPathVariable() to extract variables from
     698              :  *      'vars'
     699              :  * 'countVars' - callback to count the number of jsonpath variables in 'vars'
     700              :  * 'json' - target document for jsonpath evaluation
     701              :  * 'throwErrors' - whether we should throw suppressible errors
     702              :  * 'result' - list to store result items into
     703              :  *
     704              :  * Returns an error if a recoverable error happens during processing, or NULL
     705              :  * on no error.
     706              :  *
     707              :  * Note, jsonb and jsonpath values should be available and untoasted during
     708              :  * work because JsonPathItem, JsonbValue and result item could have pointers
     709              :  * into input values.  If caller needs to just check if document matches
     710              :  * jsonpath, then it doesn't provide a result arg.  In this case executor
     711              :  * works till first positive result and does not check the rest if possible.
     712              :  * In other case it tries to find all the satisfied result items.
     713              :  */
     714              : static JsonPathExecResult
     715       133018 : executeJsonPath(JsonPath *path, void *vars, JsonPathGetVarCallback getVar,
     716              :                 JsonPathCountVarsCallback countVars,
     717              :                 Jsonb *json, bool throwErrors, JsonValueList *result,
     718              :                 bool useTz)
     719              : {
     720              :     JsonPathExecContext cxt;
     721              :     JsonPathExecResult res;
     722              :     JsonPathItem jsp;
     723              :     JsonbValue  jbv;
     724              : 
     725       133018 :     jspInit(&jsp, path);
     726              : 
     727       133018 :     if (!JsonbExtractScalar(&json->root, &jbv))
     728       128964 :         JsonbInitBinary(&jbv, json);
     729              : 
     730       133018 :     cxt.vars = vars;
     731       133018 :     cxt.getVar = getVar;
     732       133018 :     cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
     733       133018 :     cxt.ignoreStructuralErrors = cxt.laxMode;
     734       133018 :     cxt.root = &jbv;
     735       133018 :     cxt.current = &jbv;
     736       133018 :     cxt.baseObject.jbc = NULL;
     737       133018 :     cxt.baseObject.id = 0;
     738              :     /* 1 + number of base objects in vars */
     739       133018 :     cxt.lastGeneratedObjectId = 1 + countVars(vars);
     740       133010 :     cxt.innermostArraySize = -1;
     741       133010 :     cxt.throwErrors = throwErrors;
     742       133010 :     cxt.useTz = useTz;
     743              : 
     744       133010 :     if (jspStrictAbsenceOfErrors(&cxt) && !result)
     745              :     {
     746              :         /*
     747              :          * In strict mode we must get a complete list of values to check that
     748              :          * there are no errors at all.
     749              :          */
     750              :         JsonValueList vals;
     751              :         bool        isempty;
     752              : 
     753          165 :         JsonValueListInit(&vals);
     754              : 
     755          165 :         res = executeItem(&cxt, &jsp, &jbv, &vals);
     756              : 
     757          157 :         isempty = JsonValueListIsEmpty(&vals);
     758          157 :         JsonValueListClear(&vals);
     759              : 
     760          157 :         if (jperIsError(res))
     761          144 :             return res;
     762              : 
     763           13 :         return isempty ? jperNotFound : jperOk;
     764              :     }
     765              : 
     766       132845 :     res = executeItem(&cxt, &jsp, &jbv, result);
     767              : 
     768              :     Assert(!throwErrors || !jperIsError(res));
     769              : 
     770       131777 :     return res;
     771              : }
     772              : 
     773              : /*
     774              :  * Execute jsonpath with automatic unwrapping of current item in lax mode.
     775              :  */
     776              : static JsonPathExecResult
     777       398387 : executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
     778              :             JsonbValue *jb, JsonValueList *found)
     779              : {
     780       398387 :     return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
     781              : }
     782              : 
     783              : /*
     784              :  * Main jsonpath executor function: walks on jsonpath structure, finds
     785              :  * relevant parts of jsonb and evaluates expressions over them.
     786              :  * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
     787              :  */
     788              : static JsonPathExecResult
     789       404091 : executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
     790              :                            JsonbValue *jb, JsonValueList *found, bool unwrap)
     791              : {
     792              :     JsonPathItem elem;
     793       404091 :     JsonPathExecResult res = jperNotFound;
     794              :     JsonBaseObjectInfo baseObject;
     795              : 
     796       404091 :     check_stack_depth();
     797       404091 :     CHECK_FOR_INTERRUPTS();
     798              : 
     799       404091 :     switch (jsp->type)
     800              :     {
     801        40715 :         case jpiNull:
     802              :         case jpiBool:
     803              :         case jpiNumeric:
     804              :         case jpiString:
     805              :         case jpiVariable:
     806              :             {
     807              :                 JsonbValue  v;
     808        40715 :                 bool        hasNext = jspGetNext(jsp, &elem);
     809              : 
     810        40715 :                 if (!hasNext && !found && jsp->type != jpiVariable)
     811              :                 {
     812              :                     /*
     813              :                      * Skip evaluation, but not for variables.  We must
     814              :                      * trigger an error for the missing variable.
     815              :                      */
     816           10 :                     res = jperOk;
     817           10 :                     break;
     818              :                 }
     819              : 
     820        40705 :                 baseObject = cxt->baseObject;
     821        40705 :                 getJsonPathItem(cxt, jsp, &v);
     822              : 
     823        40673 :                 res = executeNextItem(cxt, jsp, &elem,
     824              :                                       &v, found);
     825        40673 :                 cxt->baseObject = baseObject;
     826              :             }
     827        40673 :             break;
     828              : 
     829              :             /* all boolean item types: */
     830        68139 :         case jpiAnd:
     831              :         case jpiOr:
     832              :         case jpiNot:
     833              :         case jpiIsUnknown:
     834              :         case jpiEqual:
     835              :         case jpiNotEqual:
     836              :         case jpiLess:
     837              :         case jpiGreater:
     838              :         case jpiLessOrEqual:
     839              :         case jpiGreaterOrEqual:
     840              :         case jpiExists:
     841              :         case jpiStartsWith:
     842              :         case jpiLikeRegex:
     843              :             {
     844        68139 :                 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
     845              : 
     846        68139 :                 res = appendBoolResult(cxt, jsp, found, st);
     847        68139 :                 break;
     848              :             }
     849              : 
     850          242 :         case jpiAdd:
     851          242 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     852              :                                            numeric_add_safe, found);
     853              : 
     854          110 :         case jpiSub:
     855          110 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     856              :                                            numeric_sub_safe, found);
     857              : 
     858           40 :         case jpiMul:
     859           40 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     860              :                                            numeric_mul_safe, found);
     861              : 
     862           44 :         case jpiDiv:
     863           44 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     864              :                                            numeric_div_safe, found);
     865              : 
     866            8 :         case jpiMod:
     867            8 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     868              :                                            numeric_mod_safe, found);
     869              : 
     870           48 :         case jpiPlus:
     871           48 :             return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
     872              : 
     873          100 :         case jpiMinus:
     874          100 :             return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
     875              :                                           found);
     876              : 
     877         2079 :         case jpiAnyArray:
     878         2079 :             if (JsonbType(jb) == jbvArray)
     879              :             {
     880         1875 :                 bool        hasNext = jspGetNext(jsp, &elem);
     881              : 
     882         1875 :                 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
     883         1875 :                                                    jb, found, jspAutoUnwrap(cxt));
     884              :             }
     885          204 :             else if (jspAutoWrap(cxt))
     886          172 :                 res = executeNextItem(cxt, jsp, NULL, jb, found);
     887           32 :             else if (!jspIgnoreStructuralErrors(cxt))
     888           32 :                 RETURN_ERROR(ereport(ERROR,
     889              :                                      (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
     890              :                                       errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
     891         1887 :             break;
     892              : 
     893          194 :         case jpiAnyKey:
     894          194 :             if (JsonbType(jb) == jbvObject)
     895              :             {
     896           97 :                 bool        hasNext = jspGetNext(jsp, &elem);
     897              : 
     898           97 :                 if (jb->type != jbvBinary)
     899            0 :                     elog(ERROR, "invalid jsonb object type: %d", jb->type);
     900              : 
     901           97 :                 return executeAnyItem
     902              :                     (cxt, hasNext ? &elem : NULL,
     903              :                      jb->val.binary.data, found, 1, 1, 1,
     904           97 :                      false, jspAutoUnwrap(cxt));
     905              :             }
     906           97 :             else if (unwrap && JsonbType(jb) == jbvArray)
     907           18 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
     908           79 :             else if (!jspIgnoreStructuralErrors(cxt))
     909              :             {
     910              :                 Assert(found);
     911           21 :                 RETURN_ERROR(ereport(ERROR,
     912              :                                      (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
     913              :                                       errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
     914              :             }
     915           58 :             break;
     916              : 
     917          341 :         case jpiIndexArray:
     918          341 :             if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
     919          259 :             {
     920          333 :                 int         innermostArraySize = cxt->innermostArraySize;
     921              :                 int         i;
     922          333 :                 int         size = JsonbArraySize(jb);
     923          333 :                 bool        singleton = size < 0;
     924          333 :                 bool        hasNext = jspGetNext(jsp, &elem);
     925              : 
     926          333 :                 if (singleton)
     927            4 :                     size = 1;
     928              : 
     929          333 :                 cxt->innermostArraySize = size; /* for LAST evaluation */
     930              : 
     931          577 :                 for (i = 0; i < jsp->content.array.nelems; i++)
     932              :                 {
     933              :                     JsonPathItem from;
     934              :                     JsonPathItem to;
     935              :                     int32       index;
     936              :                     int32       index_from;
     937              :                     int32       index_to;
     938          341 :                     bool        range = jspGetArraySubscript(jsp, &from,
     939              :                                                              &to, i);
     940              : 
     941          341 :                     res = getArrayIndex(cxt, &from, jb, &index_from);
     942              : 
     943          325 :                     if (jperIsError(res))
     944           23 :                         break;
     945              : 
     946          307 :                     if (range)
     947              :                     {
     948           21 :                         res = getArrayIndex(cxt, &to, jb, &index_to);
     949              : 
     950           17 :                         if (jperIsError(res))
     951            0 :                             break;
     952              :                     }
     953              :                     else
     954          286 :                         index_to = index_from;
     955              : 
     956          303 :                     if (!jspIgnoreStructuralErrors(cxt) &&
     957           64 :                         (index_from < 0 ||
     958           56 :                          index_from > index_to ||
     959           56 :                          index_to >= size))
     960           54 :                         RETURN_ERROR(ereport(ERROR,
     961              :                                              (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
     962              :                                               errmsg("jsonpath array subscript is out of bounds"))));
     963              : 
     964          269 :                     if (index_from < 0)
     965            8 :                         index_from = 0;
     966              : 
     967          269 :                     if (index_to >= size)
     968           22 :                         index_to = size - 1;
     969              : 
     970          269 :                     res = jperNotFound;
     971              : 
     972          516 :                     for (index = index_from; index <= index_to; index++)
     973              :                     {
     974              :                         JsonbValue *v;
     975              : 
     976          272 :                         if (singleton)
     977              :                         {
     978            4 :                             v = jb;
     979              :                         }
     980              :                         else
     981              :                         {
     982          268 :                             v = getIthJsonbValueFromContainer(jb->val.binary.data,
     983              :                                                               (uint32) index);
     984              : 
     985          268 :                             if (v == NULL)
     986            0 :                                 continue;
     987              :                         }
     988              : 
     989          272 :                         if (!hasNext && !found)
     990           20 :                             return jperOk;
     991              : 
     992          252 :                         res = executeNextItem(cxt, jsp, &elem, v, found);
     993              : 
     994          252 :                         if (jperIsError(res))
     995            0 :                             break;
     996              : 
     997          252 :                         if (res == jperOk && !found)
     998            5 :                             break;
     999              :                     }
    1000              : 
    1001          249 :                     if (jperIsError(res))
    1002            0 :                         break;
    1003              : 
    1004          249 :                     if (res == jperOk && !found)
    1005            5 :                         break;
    1006              :                 }
    1007              : 
    1008          259 :                 cxt->innermostArraySize = innermostArraySize;
    1009              :             }
    1010            8 :             else if (!jspIgnoreStructuralErrors(cxt))
    1011              :             {
    1012            8 :                 RETURN_ERROR(ereport(ERROR,
    1013              :                                      (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
    1014              :                                       errmsg("jsonpath array accessor can only be applied to an array"))));
    1015              :             }
    1016          259 :             break;
    1017              : 
    1018          229 :         case jpiAny:
    1019              :             {
    1020          229 :                 bool        hasNext = jspGetNext(jsp, &elem);
    1021              : 
    1022              :                 /* first try without any intermediate steps */
    1023          229 :                 if (jsp->content.anybounds.first == 0)
    1024              :                 {
    1025              :                     bool        savedIgnoreStructuralErrors;
    1026              : 
    1027          127 :                     savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
    1028          127 :                     cxt->ignoreStructuralErrors = true;
    1029          127 :                     res = executeNextItem(cxt, jsp, &elem,
    1030              :                                           jb, found);
    1031          127 :                     cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
    1032              : 
    1033          127 :                     if (res == jperOk && !found)
    1034            5 :                         break;
    1035              :                 }
    1036              : 
    1037          224 :                 if (jb->type == jbvBinary)
    1038          224 :                     res = executeAnyItem
    1039              :                         (cxt, hasNext ? &elem : NULL,
    1040              :                          jb->val.binary.data, found,
    1041              :                          1,
    1042              :                          jsp->content.anybounds.first,
    1043              :                          jsp->content.anybounds.last,
    1044          224 :                          true, jspAutoUnwrap(cxt));
    1045          224 :                 break;
    1046              :             }
    1047              : 
    1048       111725 :         case jpiKey:
    1049       111725 :             if (JsonbType(jb) == jbvObject)
    1050              :             {
    1051              :                 JsonbValue *v;
    1052              :                 JsonbValue  key;
    1053              : 
    1054       111093 :                 key.type = jbvString;
    1055       111093 :                 key.val.string.val = jspGetString(jsp, &key.val.string.len);
    1056              : 
    1057       111093 :                 v = findJsonbValueFromContainer(jb->val.binary.data,
    1058              :                                                 JB_FOBJECT, &key);
    1059              : 
    1060       111093 :                 if (v != NULL)
    1061              :                 {
    1062        18400 :                     res = executeNextItem(cxt, jsp, NULL,
    1063              :                                           v, found);
    1064        18400 :                     pfree(v);
    1065              :                 }
    1066        92693 :                 else if (!jspIgnoreStructuralErrors(cxt))
    1067              :                 {
    1068              :                     Assert(found);
    1069              : 
    1070           59 :                     if (!jspThrowErrors(cxt))
    1071           43 :                         return jperError;
    1072              : 
    1073           16 :                     ereport(ERROR,
    1074              :                             (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
    1075              :                              errmsg("JSON object does not contain key \"%s\"",
    1076              :                                     pnstrdup(key.val.string.val,
    1077              :                                              key.val.string.len))));
    1078              :                 }
    1079              :             }
    1080          632 :             else if (unwrap && JsonbType(jb) == jbvArray)
    1081           24 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1082          608 :             else if (!jspIgnoreStructuralErrors(cxt))
    1083              :             {
    1084              :                 Assert(found);
    1085          175 :                 RETURN_ERROR(ereport(ERROR,
    1086              :                                      (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
    1087              :                                       errmsg("jsonpath member accessor can only be applied to an object"))));
    1088              :             }
    1089       111467 :             break;
    1090              : 
    1091        16149 :         case jpiCurrent:
    1092        16149 :             res = executeNextItem(cxt, jsp, NULL, cxt->current, found);
    1093        16149 :             break;
    1094              : 
    1095       141059 :         case jpiRoot:
    1096       141059 :             jb = cxt->root;
    1097       141059 :             baseObject = setBaseObject(cxt, jb, 0);
    1098       141059 :             res = executeNextItem(cxt, jsp, NULL, jb, found);
    1099       140043 :             cxt->baseObject = baseObject;
    1100       140043 :             break;
    1101              : 
    1102        15214 :         case jpiFilter:
    1103              :             {
    1104              :                 JsonPathBool st;
    1105              : 
    1106        15214 :                 if (unwrap && JsonbType(jb) == jbvArray)
    1107           88 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
    1108              :                                                         false);
    1109              : 
    1110        15126 :                 jspGetArg(jsp, &elem);
    1111        15126 :                 st = executeNestedBoolItem(cxt, &elem, jb);
    1112        15062 :                 if (st != jpbTrue)
    1113        12660 :                     res = jperNotFound;
    1114              :                 else
    1115         2402 :                     res = executeNextItem(cxt, jsp, NULL,
    1116              :                                           jb, found);
    1117        15062 :                 break;
    1118              :             }
    1119              : 
    1120          236 :         case jpiType:
    1121              :             {
    1122              :                 JsonbValue  jbv;
    1123              : 
    1124          236 :                 jbv.type = jbvString;
    1125          236 :                 jbv.val.string.val = pstrdup(JsonbTypeName(jb));
    1126          236 :                 jbv.val.string.len = strlen(jbv.val.string.val);
    1127              : 
    1128          236 :                 res = executeNextItem(cxt, jsp, NULL, &jbv, found);
    1129              :             }
    1130          236 :             break;
    1131              : 
    1132           48 :         case jpiSize:
    1133              :             {
    1134           48 :                 int         size = JsonbArraySize(jb);
    1135              :                 JsonbValue  jbv;
    1136              : 
    1137           48 :                 if (size < 0)
    1138              :                 {
    1139           32 :                     if (!jspAutoWrap(cxt))
    1140              :                     {
    1141            8 :                         if (!jspIgnoreStructuralErrors(cxt))
    1142            8 :                             RETURN_ERROR(ereport(ERROR,
    1143              :                                                  (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
    1144              :                                                   errmsg("jsonpath item method .%s() can only be applied to an array",
    1145              :                                                          jspOperationName(jsp->type)))));
    1146            0 :                         break;
    1147              :                     }
    1148              : 
    1149           24 :                     size = 1;
    1150              :                 }
    1151              : 
    1152           40 :                 jbv.type = jbvNumeric;
    1153           40 :                 jbv.val.numeric = int64_to_numeric(size);
    1154              : 
    1155           40 :                 res = executeNextItem(cxt, jsp, NULL, &jbv, found);
    1156              :             }
    1157           40 :             break;
    1158              : 
    1159           72 :         case jpiAbs:
    1160           72 :             return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
    1161              :                                             found);
    1162              : 
    1163           32 :         case jpiFloor:
    1164           32 :             return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
    1165              :                                             found);
    1166              : 
    1167           68 :         case jpiCeiling:
    1168           68 :             return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
    1169              :                                             found);
    1170              : 
    1171           76 :         case jpiDouble:
    1172              :             {
    1173              :                 JsonbValue  jbv;
    1174              : 
    1175           76 :                 if (unwrap && JsonbType(jb) == jbvArray)
    1176           28 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
    1177              :                                                         false);
    1178              : 
    1179           72 :                 if (jb->type == jbvNumeric)
    1180              :                 {
    1181            8 :                     char       *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
    1182              :                                                                           NumericGetDatum(jb->val.numeric)));
    1183              :                     double      val;
    1184            8 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
    1185              : 
    1186            8 :                     val = float8in_internal(tmp,
    1187              :                                             NULL,
    1188              :                                             "double precision",
    1189              :                                             tmp,
    1190              :                                             (Node *) &escontext);
    1191              : 
    1192            8 :                     if (escontext.error_occurred)
    1193            4 :                         RETURN_ERROR(ereport(ERROR,
    1194              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1195              :                                               errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
    1196              :                                                      tmp, jspOperationName(jsp->type), "double precision"))));
    1197            4 :                     if (isinf(val) || isnan(val))
    1198            0 :                         RETURN_ERROR(ereport(ERROR,
    1199              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1200              :                                               errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
    1201              :                                                      jspOperationName(jsp->type)))));
    1202            4 :                     res = jperOk;
    1203              :                 }
    1204           64 :                 else if (jb->type == jbvString)
    1205              :                 {
    1206              :                     /* cast string as double */
    1207              :                     double      val;
    1208           32 :                     char       *tmp = pnstrdup(jb->val.string.val,
    1209           32 :                                                jb->val.string.len);
    1210           32 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
    1211              : 
    1212           32 :                     val = float8in_internal(tmp,
    1213              :                                             NULL,
    1214              :                                             "double precision",
    1215              :                                             tmp,
    1216              :                                             (Node *) &escontext);
    1217              : 
    1218           32 :                     if (escontext.error_occurred)
    1219           12 :                         RETURN_ERROR(ereport(ERROR,
    1220              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1221              :                                               errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
    1222              :                                                      tmp, jspOperationName(jsp->type), "double precision"))));
    1223           28 :                     if (isinf(val) || isnan(val))
    1224           24 :                         RETURN_ERROR(ereport(ERROR,
    1225              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1226              :                                               errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
    1227              :                                                      jspOperationName(jsp->type)))));
    1228              : 
    1229            4 :                     jb = &jbv;
    1230            4 :                     jb->type = jbvNumeric;
    1231            4 :                     jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
    1232              :                                                                           Float8GetDatum(val)));
    1233            4 :                     res = jperOk;
    1234              :                 }
    1235              : 
    1236           40 :                 if (res == jperNotFound)
    1237           32 :                     RETURN_ERROR(ereport(ERROR,
    1238              :                                          (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1239              :                                           errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
    1240              :                                                  jspOperationName(jsp->type)))));
    1241              : 
    1242            8 :                 res = executeNextItem(cxt, jsp, NULL, jb, found);
    1243              :             }
    1244            8 :             break;
    1245              : 
    1246         5686 :         case jpiDatetime:
    1247              :         case jpiDate:
    1248              :         case jpiTime:
    1249              :         case jpiTimeTz:
    1250              :         case jpiTimestamp:
    1251              :         case jpiTimestampTz:
    1252         5686 :             if (unwrap && JsonbType(jb) == jbvArray)
    1253           24 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1254              : 
    1255         5662 :             return executeDateTimeMethod(cxt, jsp, jb, found);
    1256              : 
    1257           62 :         case jpiKeyValue:
    1258           62 :             if (unwrap && JsonbType(jb) == jbvArray)
    1259            4 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1260              : 
    1261           58 :             return executeKeyValueMethod(cxt, jsp, jb, found);
    1262              : 
    1263           44 :         case jpiLast:
    1264              :             {
    1265              :                 JsonbValue  jbv;
    1266              :                 int         last;
    1267           44 :                 bool        hasNext = jspGetNext(jsp, &elem);
    1268              : 
    1269           44 :                 if (cxt->innermostArraySize < 0)
    1270            0 :                     elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
    1271              : 
    1272           44 :                 if (!hasNext && !found)
    1273              :                 {
    1274            4 :                     res = jperOk;
    1275            4 :                     break;
    1276              :                 }
    1277              : 
    1278           40 :                 last = cxt->innermostArraySize - 1;
    1279              : 
    1280           40 :                 jbv.type = jbvNumeric;
    1281           40 :                 jbv.val.numeric = int64_to_numeric(last);
    1282              : 
    1283           40 :                 res = executeNextItem(cxt, jsp, &elem,
    1284              :                                       &jbv, found);
    1285              :             }
    1286           40 :             break;
    1287              : 
    1288          120 :         case jpiBigint:
    1289              :             {
    1290              :                 JsonbValue  jbv;
    1291              :                 Datum       datum;
    1292              : 
    1293          120 :                 if (unwrap && JsonbType(jb) == jbvArray)
    1294           28 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
    1295              :                                                         false);
    1296              : 
    1297          116 :                 if (jb->type == jbvNumeric)
    1298              :                 {
    1299           32 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
    1300              :                     int64       val;
    1301              : 
    1302           32 :                     val = numeric_int8_safe(jb->val.numeric,
    1303              :                                             (Node *) &escontext);
    1304           32 :                     if (escontext.error_occurred)
    1305            8 :                         RETURN_ERROR(ereport(ERROR,
    1306              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1307              :                                               errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
    1308              :                                                      DatumGetCString(DirectFunctionCall1(numeric_out,
    1309              :                                                                                          NumericGetDatum(jb->val.numeric))),
    1310              :                                                      jspOperationName(jsp->type),
    1311              :                                                      "bigint"))));
    1312              : 
    1313           24 :                     datum = Int64GetDatum(val);
    1314           24 :                     res = jperOk;
    1315              :                 }
    1316           84 :                 else if (jb->type == jbvString)
    1317              :                 {
    1318              :                     /* cast string as bigint */
    1319           52 :                     char       *tmp = pnstrdup(jb->val.string.val,
    1320           52 :                                                jb->val.string.len);
    1321           52 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
    1322              :                     bool        noerr;
    1323              : 
    1324           52 :                     noerr = DirectInputFunctionCallSafe(int8in, tmp,
    1325              :                                                         InvalidOid, -1,
    1326              :                                                         (Node *) &escontext,
    1327              :                                                         &datum);
    1328              : 
    1329           52 :                     if (!noerr || escontext.error_occurred)
    1330           36 :                         RETURN_ERROR(ereport(ERROR,
    1331              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1332              :                                               errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
    1333              :                                                      tmp, jspOperationName(jsp->type), "bigint"))));
    1334           16 :                     res = jperOk;
    1335              :                 }
    1336              : 
    1337           72 :                 if (res == jperNotFound)
    1338           32 :                     RETURN_ERROR(ereport(ERROR,
    1339              :                                          (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1340              :                                           errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
    1341              :                                                  jspOperationName(jsp->type)))));
    1342              : 
    1343           40 :                 jbv.type = jbvNumeric;
    1344           40 :                 jbv.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
    1345              :                                                                       datum));
    1346              : 
    1347           40 :                 res = executeNextItem(cxt, jsp, NULL, &jbv, found);
    1348              :             }
    1349           40 :             break;
    1350              : 
    1351          171 :         case jpiBoolean:
    1352              :             {
    1353              :                 JsonbValue  jbv;
    1354              :                 bool        bval;
    1355              : 
    1356          171 :                 if (unwrap && JsonbType(jb) == jbvArray)
    1357           24 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
    1358              :                                                         false);
    1359              : 
    1360          167 :                 if (jb->type == jbvBool)
    1361              :                 {
    1362           17 :                     bval = jb->val.boolean;
    1363              : 
    1364           17 :                     res = jperOk;
    1365              :                 }
    1366          150 :                 else if (jb->type == jbvNumeric)
    1367              :                 {
    1368              :                     int         ival;
    1369              :                     Datum       datum;
    1370              :                     bool        noerr;
    1371           33 :                     char       *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
    1372              :                                                                           NumericGetDatum(jb->val.numeric)));
    1373           33 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
    1374              : 
    1375           33 :                     noerr = DirectInputFunctionCallSafe(int4in, tmp,
    1376              :                                                         InvalidOid, -1,
    1377              :                                                         (Node *) &escontext,
    1378              :                                                         &datum);
    1379              : 
    1380           33 :                     if (!noerr || escontext.error_occurred)
    1381            8 :                         RETURN_ERROR(ereport(ERROR,
    1382              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1383              :                                               errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
    1384              :                                                      tmp, jspOperationName(jsp->type), "boolean"))));
    1385              : 
    1386           25 :                     ival = DatumGetInt32(datum);
    1387           25 :                     if (ival == 0)
    1388            4 :                         bval = false;
    1389              :                     else
    1390           21 :                         bval = true;
    1391              : 
    1392           25 :                     res = jperOk;
    1393              :                 }
    1394          117 :                 else if (jb->type == jbvString)
    1395              :                 {
    1396              :                     /* cast string as boolean */
    1397           93 :                     char       *tmp = pnstrdup(jb->val.string.val,
    1398           93 :                                                jb->val.string.len);
    1399              : 
    1400           93 :                     if (!parse_bool(tmp, &bval))
    1401           36 :                         RETURN_ERROR(ereport(ERROR,
    1402              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1403              :                                               errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
    1404              :                                                      tmp, jspOperationName(jsp->type), "boolean"))));
    1405              : 
    1406           57 :                     res = jperOk;
    1407              :                 }
    1408              : 
    1409          123 :                 if (res == jperNotFound)
    1410           24 :                     RETURN_ERROR(ereport(ERROR,
    1411              :                                          (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1412              :                                           errmsg("jsonpath item method .%s() can only be applied to a boolean, string, or numeric value",
    1413              :                                                  jspOperationName(jsp->type)))));
    1414              : 
    1415           99 :                 jbv.type = jbvBool;
    1416           99 :                 jbv.val.boolean = bval;
    1417              : 
    1418           99 :                 res = executeNextItem(cxt, jsp, NULL, &jbv, found);
    1419              :             }
    1420           99 :             break;
    1421              : 
    1422          284 :         case jpiDecimal:
    1423              :         case jpiNumber:
    1424              :             {
    1425              :                 JsonbValue  jbv;
    1426              :                 Numeric     num;
    1427          284 :                 char       *numstr = NULL;
    1428              : 
    1429          284 :                 if (unwrap && JsonbType(jb) == jbvArray)
    1430           56 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
    1431              :                                                         false);
    1432              : 
    1433          276 :                 if (jb->type == jbvNumeric)
    1434              :                 {
    1435          116 :                     num = jb->val.numeric;
    1436          116 :                     if (numeric_is_nan(num) || numeric_is_inf(num))
    1437            0 :                         RETURN_ERROR(ereport(ERROR,
    1438              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1439              :                                               errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
    1440              :                                                      jspOperationName(jsp->type)))));
    1441              : 
    1442          116 :                     if (jsp->type == jpiDecimal)
    1443           92 :                         numstr = DatumGetCString(DirectFunctionCall1(numeric_out,
    1444              :                                                                      NumericGetDatum(num)));
    1445          116 :                     res = jperOk;
    1446              :                 }
    1447          160 :                 else if (jb->type == jbvString)
    1448              :                 {
    1449              :                     /* cast string as number */
    1450              :                     Datum       datum;
    1451              :                     bool        noerr;
    1452           96 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
    1453              : 
    1454           96 :                     numstr = pnstrdup(jb->val.string.val, jb->val.string.len);
    1455              : 
    1456           96 :                     noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
    1457              :                                                         InvalidOid, -1,
    1458              :                                                         (Node *) &escontext,
    1459              :                                                         &datum);
    1460              : 
    1461           96 :                     if (!noerr || escontext.error_occurred)
    1462           24 :                         RETURN_ERROR(ereport(ERROR,
    1463              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1464              :                                               errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
    1465              :                                                      numstr, jspOperationName(jsp->type), "numeric"))));
    1466              : 
    1467           88 :                     num = DatumGetNumeric(datum);
    1468           88 :                     if (numeric_is_nan(num) || numeric_is_inf(num))
    1469           48 :                         RETURN_ERROR(ereport(ERROR,
    1470              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1471              :                                               errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
    1472              :                                                      jspOperationName(jsp->type)))));
    1473              : 
    1474           40 :                     res = jperOk;
    1475              :                 }
    1476              : 
    1477          220 :                 if (res == jperNotFound)
    1478           64 :                     RETURN_ERROR(ereport(ERROR,
    1479              :                                          (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1480              :                                           errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
    1481              :                                                  jspOperationName(jsp->type)))));
    1482              : 
    1483              :                 /*
    1484              :                  * If we have arguments, then they must be the precision and
    1485              :                  * optional scale used in .decimal().  Convert them to the
    1486              :                  * typmod equivalent and then truncate the numeric value per
    1487              :                  * this typmod details.
    1488              :                  */
    1489          156 :                 if (jsp->type == jpiDecimal && jsp->content.args.left)
    1490              :                 {
    1491              :                     Datum       numdatum;
    1492              :                     Datum       dtypmod;
    1493              :                     int32       precision;
    1494           68 :                     int32       scale = 0;
    1495              :                     bool        noerr;
    1496              :                     ArrayType  *arrtypmod;
    1497              :                     Datum       datums[2];
    1498              :                     char        pstr[12];   /* sign, 10 digits and '\0' */
    1499              :                     char        sstr[12];   /* sign, 10 digits and '\0' */
    1500           68 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
    1501              : 
    1502           68 :                     jspGetLeftArg(jsp, &elem);
    1503           68 :                     if (elem.type != jpiNumeric)
    1504            0 :                         elog(ERROR, "invalid jsonpath item type for .decimal() precision");
    1505              : 
    1506           68 :                     precision = numeric_int4_safe(jspGetNumeric(&elem),
    1507              :                                                   (Node *) &escontext);
    1508           68 :                     if (escontext.error_occurred)
    1509            4 :                         RETURN_ERROR(ereport(ERROR,
    1510              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1511              :                                               errmsg("precision of jsonpath item method .%s() is out of range for type integer",
    1512              :                                                      jspOperationName(jsp->type)))));
    1513              : 
    1514           64 :                     if (jsp->content.args.right)
    1515              :                     {
    1516           64 :                         jspGetRightArg(jsp, &elem);
    1517           64 :                         if (elem.type != jpiNumeric)
    1518            0 :                             elog(ERROR, "invalid jsonpath item type for .decimal() scale");
    1519              : 
    1520           64 :                         scale = numeric_int4_safe(jspGetNumeric(&elem),
    1521              :                                                   (Node *) &escontext);
    1522           64 :                         if (escontext.error_occurred)
    1523            4 :                             RETURN_ERROR(ereport(ERROR,
    1524              :                                                  (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1525              :                                                   errmsg("scale of jsonpath item method .%s() is out of range for type integer",
    1526              :                                                          jspOperationName(jsp->type)))));
    1527              :                     }
    1528              : 
    1529              :                     /*
    1530              :                      * numerictypmodin() takes the precision and scale in the
    1531              :                      * form of CString arrays.
    1532              :                      */
    1533           60 :                     pg_ltoa(precision, pstr);
    1534           60 :                     datums[0] = CStringGetDatum(pstr);
    1535           60 :                     pg_ltoa(scale, sstr);
    1536           60 :                     datums[1] = CStringGetDatum(sstr);
    1537           60 :                     arrtypmod = construct_array_builtin(datums, 2, CSTRINGOID);
    1538              : 
    1539           60 :                     dtypmod = DirectFunctionCall1(numerictypmodin,
    1540              :                                                   PointerGetDatum(arrtypmod));
    1541              : 
    1542              :                     /* Convert numstr to Numeric with typmod */
    1543              :                     Assert(numstr != NULL);
    1544           40 :                     noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
    1545              :                                                         InvalidOid, DatumGetInt32(dtypmod),
    1546              :                                                         (Node *) &escontext,
    1547              :                                                         &numdatum);
    1548              : 
    1549           40 :                     if (!noerr || escontext.error_occurred)
    1550            8 :                         RETURN_ERROR(ereport(ERROR,
    1551              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1552              :                                               errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
    1553              :                                                      numstr, jspOperationName(jsp->type), "numeric"))));
    1554              : 
    1555           32 :                     num = DatumGetNumeric(numdatum);
    1556           32 :                     pfree(arrtypmod);
    1557              :                 }
    1558              : 
    1559          120 :                 jbv.type = jbvNumeric;
    1560          120 :                 jbv.val.numeric = num;
    1561              : 
    1562          120 :                 res = executeNextItem(cxt, jsp, NULL, &jbv, found);
    1563              :             }
    1564          120 :             break;
    1565              : 
    1566          112 :         case jpiInteger:
    1567              :             {
    1568              :                 JsonbValue  jbv;
    1569              :                 Datum       datum;
    1570              : 
    1571          112 :                 if (unwrap && JsonbType(jb) == jbvArray)
    1572           28 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
    1573              :                                                         false);
    1574              : 
    1575          108 :                 if (jb->type == jbvNumeric)
    1576              :                 {
    1577              :                     int32       val;
    1578           28 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
    1579              : 
    1580           28 :                     val = numeric_int4_safe(jb->val.numeric,
    1581              :                                             (Node *) &escontext);
    1582           28 :                     if (escontext.error_occurred)
    1583            8 :                         RETURN_ERROR(ereport(ERROR,
    1584              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1585              :                                               errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
    1586              :                                                      DatumGetCString(DirectFunctionCall1(numeric_out,
    1587              :                                                                                          NumericGetDatum(jb->val.numeric))),
    1588              :                                                      jspOperationName(jsp->type), "integer"))));
    1589              : 
    1590           20 :                     datum = Int32GetDatum(val);
    1591           20 :                     res = jperOk;
    1592              :                 }
    1593           80 :                 else if (jb->type == jbvString)
    1594              :                 {
    1595              :                     /* cast string as integer */
    1596           48 :                     char       *tmp = pnstrdup(jb->val.string.val,
    1597           48 :                                                jb->val.string.len);
    1598           48 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
    1599              :                     bool        noerr;
    1600              : 
    1601           48 :                     noerr = DirectInputFunctionCallSafe(int4in, tmp,
    1602              :                                                         InvalidOid, -1,
    1603              :                                                         (Node *) &escontext,
    1604              :                                                         &datum);
    1605              : 
    1606           48 :                     if (!noerr || escontext.error_occurred)
    1607           36 :                         RETURN_ERROR(ereport(ERROR,
    1608              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1609              :                                               errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
    1610              :                                                      tmp, jspOperationName(jsp->type), "integer"))));
    1611           12 :                     res = jperOk;
    1612              :                 }
    1613              : 
    1614           64 :                 if (res == jperNotFound)
    1615           32 :                     RETURN_ERROR(ereport(ERROR,
    1616              :                                          (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1617              :                                           errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
    1618              :                                                  jspOperationName(jsp->type)))));
    1619              : 
    1620           32 :                 jbv.type = jbvNumeric;
    1621           32 :                 jbv.val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
    1622              :                                                                       datum));
    1623              : 
    1624           32 :                 res = executeNextItem(cxt, jsp, NULL, &jbv, found);
    1625              :             }
    1626           32 :             break;
    1627              : 
    1628          134 :         case jpiStringFunc:
    1629              :             {
    1630              :                 JsonbValue  jbv;
    1631          134 :                 char       *tmp = NULL;
    1632              : 
    1633          134 :                 if (unwrap && JsonbType(jb) == jbvArray)
    1634           20 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1635              : 
    1636          126 :                 switch (JsonbType(jb))
    1637              :                 {
    1638           18 :                     case jbvString:
    1639              : 
    1640              :                         /*
    1641              :                          * Value is not necessarily null-terminated, so we do
    1642              :                          * pnstrdup() here.
    1643              :                          */
    1644           18 :                         tmp = pnstrdup(jb->val.string.val,
    1645           18 :                                        jb->val.string.len);
    1646           18 :                         break;
    1647           26 :                     case jbvNumeric:
    1648           26 :                         tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
    1649              :                                                                   NumericGetDatum(jb->val.numeric)));
    1650           26 :                         break;
    1651           18 :                     case jbvBool:
    1652           18 :                         tmp = (jb->val.boolean) ? "true" : "false";
    1653           18 :                         break;
    1654           40 :                     case jbvDatetime:
    1655              :                         {
    1656              :                             char        buf[MAXDATELEN + 1];
    1657              : 
    1658           40 :                             JsonEncodeDateTime(buf,
    1659              :                                                jb->val.datetime.value,
    1660              :                                                jb->val.datetime.typid,
    1661           40 :                                                &jb->val.datetime.tz);
    1662           40 :                             tmp = pstrdup(buf);
    1663              :                         }
    1664           40 :                         break;
    1665           24 :                     case jbvNull:
    1666              :                     case jbvArray:
    1667              :                     case jbvObject:
    1668              :                     case jbvBinary:
    1669           24 :                         RETURN_ERROR(ereport(ERROR,
    1670              :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1671              :                                               errmsg("jsonpath item method .%s() can only be applied to a boolean, string, numeric, or datetime value",
    1672              :                                                      jspOperationName(jsp->type)))));
    1673              :                         break;
    1674              :                 }
    1675              : 
    1676              :                 Assert(tmp != NULL);    /* We must have set tmp above */
    1677          102 :                 jbv.val.string.val = tmp;
    1678          102 :                 jbv.val.string.len = strlen(jbv.val.string.val);
    1679          102 :                 jbv.type = jbvString;
    1680              : 
    1681          102 :                 res = executeNextItem(cxt, jsp, NULL, &jbv, found);
    1682              :             }
    1683          102 :             break;
    1684              : 
    1685          510 :         case jpiStrReplace:
    1686              :         case jpiStrLower:
    1687              :         case jpiStrUpper:
    1688              :         case jpiStrLtrim:
    1689              :         case jpiStrRtrim:
    1690              :         case jpiStrBtrim:
    1691              :         case jpiStrInitcap:
    1692              :         case jpiStrSplitPart:
    1693              :             {
    1694          510 :                 if (unwrap && JsonbType(jb) == jbvArray)
    1695           40 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1696              : 
    1697          470 :                 return executeStringInternalMethod(cxt, jsp, jb, found);
    1698              :             }
    1699              :             break;
    1700              : 
    1701            0 :         default:
    1702            0 :             elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
    1703              :     }
    1704              : 
    1705       394697 :     return res;
    1706              : }
    1707              : 
    1708              : /*
    1709              :  * Unwrap current array item and execute jsonpath for each of its elements.
    1710              :  */
    1711              : static JsonPathExecResult
    1712         2141 : executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1713              :                              JsonbValue *jb, JsonValueList *found,
    1714              :                              bool unwrapElements)
    1715              : {
    1716         2141 :     if (jb->type != jbvBinary)
    1717              :     {
    1718              :         Assert(jb->type != jbvArray);
    1719            0 :         elog(ERROR, "invalid jsonb array value type: %d", jb->type);
    1720              :     }
    1721              : 
    1722         2141 :     return executeAnyItem
    1723              :         (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
    1724              :          false, unwrapElements);
    1725              : }
    1726              : 
    1727              : /*
    1728              :  * Execute next jsonpath item if exists.  Otherwise put "v" to the "found"
    1729              :  * list if provided.
    1730              :  */
    1731              : static JsonPathExecResult
    1732       294224 : executeNextItem(JsonPathExecContext *cxt,
    1733              :                 JsonPathItem *cur, JsonPathItem *next,
    1734              :                 JsonbValue *v, JsonValueList *found)
    1735              : {
    1736              :     JsonPathItem elem;
    1737              :     bool        hasNext;
    1738              : 
    1739       294224 :     if (!cur)
    1740            0 :         hasNext = next != NULL;
    1741       294224 :     else if (next)
    1742       115365 :         hasNext = jspHasNext(cur);
    1743              :     else
    1744              :     {
    1745       178859 :         next = &elem;
    1746       178859 :         hasNext = jspGetNext(cur, next);
    1747              :     }
    1748              : 
    1749       294224 :     if (hasNext)
    1750       131689 :         return executeItem(cxt, next, v, found);
    1751              : 
    1752       162535 :     if (found)
    1753       129064 :         JsonValueListAppend(found, v);
    1754              : 
    1755       162535 :     return jperOk;
    1756              : }
    1757              : 
    1758              : /*
    1759              :  * Same as executeItem(), but when "unwrap == true" automatically unwraps
    1760              :  * each array item from the resulting sequence in lax mode.
    1761              :  */
    1762              : static JsonPathExecResult
    1763       133326 : executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1764              :                            JsonbValue *jb, bool unwrap,
    1765              :                            JsonValueList *found)
    1766              : {
    1767       133326 :     if (unwrap && jspAutoUnwrap(cxt))
    1768              :     {
    1769              :         JsonValueList seq;
    1770              :         JsonValueListIterator it;
    1771              :         JsonPathExecResult res;
    1772              :         JsonbValue *item;
    1773              : 
    1774        79654 :         JsonValueListInit(&seq);
    1775              : 
    1776        79654 :         res = executeItem(cxt, jsp, jb, &seq);
    1777              : 
    1778        79638 :         if (jperIsError(res))
    1779              :         {
    1780           47 :             JsonValueListClear(&seq);
    1781           47 :             return res;
    1782              :         }
    1783              : 
    1784        79591 :         JsonValueListInitIterator(&seq, &it);
    1785       133986 :         while ((item = JsonValueListNext(&it)))
    1786              :         {
    1787              :             Assert(item->type != jbvArray);
    1788              : 
    1789        54395 :             if (JsonbType(item) == jbvArray)
    1790           36 :                 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
    1791              :             else
    1792        54359 :                 JsonValueListAppend(found, item);
    1793              :         }
    1794              : 
    1795        79591 :         JsonValueListClear(&seq);
    1796              : 
    1797        79591 :         return jperOk;
    1798              :     }
    1799              : 
    1800        53672 :     return executeItem(cxt, jsp, jb, found);
    1801              : }
    1802              : 
    1803              : /*
    1804              :  * Same as executeItemOptUnwrapResult(), but with error suppression.
    1805              :  */
    1806              : static JsonPathExecResult
    1807       132294 : executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
    1808              :                                   JsonPathItem *jsp,
    1809              :                                   JsonbValue *jb, bool unwrap,
    1810              :                                   JsonValueList *found)
    1811              : {
    1812              :     JsonPathExecResult res;
    1813       132294 :     bool        throwErrors = cxt->throwErrors;
    1814              : 
    1815       132294 :     cxt->throwErrors = false;
    1816       132294 :     res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
    1817       132290 :     cxt->throwErrors = throwErrors;
    1818              : 
    1819       132290 :     return res;
    1820              : }
    1821              : 
    1822              : /* Execute boolean-valued jsonpath expression. */
    1823              : static JsonPathBool
    1824       119187 : executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1825              :                 JsonbValue *jb, bool canHaveNext)
    1826              : {
    1827              :     JsonPathItem larg;
    1828              :     JsonPathItem rarg;
    1829              :     JsonPathBool res;
    1830              :     JsonPathBool res2;
    1831              : 
    1832              :     /* since this function recurses, it could be driven to stack overflow */
    1833       119187 :     check_stack_depth();
    1834              : 
    1835       119187 :     if (!canHaveNext && jspHasNext(jsp))
    1836            0 :         elog(ERROR, "boolean jsonpath item cannot have next item");
    1837              : 
    1838       119187 :     switch (jsp->type)
    1839              :     {
    1840        17654 :         case jpiAnd:
    1841        17654 :             jspGetLeftArg(jsp, &larg);
    1842        17654 :             res = executeBoolItem(cxt, &larg, jb, false);
    1843              : 
    1844        17654 :             if (res == jpbFalse)
    1845        15212 :                 return jpbFalse;
    1846              : 
    1847              :             /*
    1848              :              * SQL/JSON says that we should check second arg in case of
    1849              :              * jperError
    1850              :              */
    1851              : 
    1852         2442 :             jspGetRightArg(jsp, &rarg);
    1853         2442 :             res2 = executeBoolItem(cxt, &rarg, jb, false);
    1854              : 
    1855         2442 :             return res2 == jpbTrue ? res : res2;
    1856              : 
    1857         8636 :         case jpiOr:
    1858         8636 :             jspGetLeftArg(jsp, &larg);
    1859         8636 :             res = executeBoolItem(cxt, &larg, jb, false);
    1860              : 
    1861         8636 :             if (res == jpbTrue)
    1862         1660 :                 return jpbTrue;
    1863              : 
    1864         6976 :             jspGetRightArg(jsp, &rarg);
    1865         6976 :             res2 = executeBoolItem(cxt, &rarg, jb, false);
    1866              : 
    1867         6976 :             return res2 == jpbFalse ? res : res2;
    1868              : 
    1869           72 :         case jpiNot:
    1870           72 :             jspGetArg(jsp, &larg);
    1871              : 
    1872           72 :             res = executeBoolItem(cxt, &larg, jb, false);
    1873              : 
    1874           72 :             if (res == jpbUnknown)
    1875           24 :                 return jpbUnknown;
    1876              : 
    1877           48 :             return res == jpbTrue ? jpbFalse : jpbTrue;
    1878              : 
    1879          142 :         case jpiIsUnknown:
    1880          142 :             jspGetArg(jsp, &larg);
    1881          142 :             res = executeBoolItem(cxt, &larg, jb, false);
    1882          142 :             return res == jpbUnknown ? jpbTrue : jpbFalse;
    1883              : 
    1884        39555 :         case jpiEqual:
    1885              :         case jpiNotEqual:
    1886              :         case jpiLess:
    1887              :         case jpiGreater:
    1888              :         case jpiLessOrEqual:
    1889              :         case jpiGreaterOrEqual:
    1890        39555 :             jspGetLeftArg(jsp, &larg);
    1891        39555 :             jspGetRightArg(jsp, &rarg);
    1892        39555 :             return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
    1893              :                                     executeComparison, cxt);
    1894              : 
    1895           68 :         case jpiStartsWith:     /* 'whole STARTS WITH initial' */
    1896           68 :             jspGetLeftArg(jsp, &larg);  /* 'whole' */
    1897           68 :             jspGetRightArg(jsp, &rarg); /* 'initial' */
    1898           68 :             return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
    1899              :                                     executeStartsWith, NULL);
    1900              : 
    1901          264 :         case jpiLikeRegex:      /* 'expr LIKE_REGEX pattern FLAGS flags' */
    1902              :             {
    1903              :                 /*
    1904              :                  * 'expr' is a sequence-returning expression.  'pattern' is a
    1905              :                  * regex string literal.  SQL/JSON standard requires XQuery
    1906              :                  * regexes, but we use Postgres regexes here.  'flags' is a
    1907              :                  * string literal converted to integer flags at compile-time.
    1908              :                  */
    1909          264 :                 JsonLikeRegexContext lrcxt = {0};
    1910              : 
    1911          264 :                 jspInitByBuffer(&larg, jsp->base,
    1912              :                                 jsp->content.like_regex.expr);
    1913              : 
    1914          264 :                 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
    1915              :                                         executeLikeRegex, &lrcxt);
    1916              :             }
    1917              : 
    1918        52796 :         case jpiExists:
    1919        52796 :             jspGetArg(jsp, &larg);
    1920              : 
    1921        52796 :             if (jspStrictAbsenceOfErrors(cxt))
    1922              :             {
    1923              :                 /*
    1924              :                  * In strict mode we must get a complete list of values to
    1925              :                  * check that there are no errors at all.
    1926              :                  */
    1927              :                 JsonValueList vals;
    1928              :                 JsonPathExecResult res;
    1929              :                 bool        isempty;
    1930              : 
    1931           34 :                 JsonValueListInit(&vals);
    1932              : 
    1933           34 :                 res = executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
    1934              :                                                         false, &vals);
    1935              : 
    1936           34 :                 isempty = JsonValueListIsEmpty(&vals);
    1937           34 :                 JsonValueListClear(&vals);
    1938              : 
    1939           34 :                 if (jperIsError(res))
    1940           26 :                     return jpbUnknown;
    1941              : 
    1942            8 :                 return isempty ? jpbFalse : jpbTrue;
    1943              :             }
    1944              :             else
    1945              :             {
    1946              :                 JsonPathExecResult res =
    1947        52762 :                     executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
    1948              :                                                       false, NULL);
    1949              : 
    1950        52762 :                 if (jperIsError(res))
    1951           16 :                     return jpbUnknown;
    1952              : 
    1953        52746 :                 return res == jperOk ? jpbTrue : jpbFalse;
    1954              :             }
    1955              : 
    1956            0 :         default:
    1957            0 :             elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
    1958              :             return jpbUnknown;
    1959              :     }
    1960              : }
    1961              : 
    1962              : /*
    1963              :  * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
    1964              :  * item onto the stack.
    1965              :  */
    1966              : static JsonPathBool
    1967        15126 : executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1968              :                       JsonbValue *jb)
    1969              : {
    1970              :     JsonbValue *prev;
    1971              :     JsonPathBool res;
    1972              : 
    1973        15126 :     prev = cxt->current;
    1974        15126 :     cxt->current = jb;
    1975        15126 :     res = executeBoolItem(cxt, jsp, jb, false);
    1976        15062 :     cxt->current = prev;
    1977              : 
    1978        15062 :     return res;
    1979              : }
    1980              : 
    1981              : /*
    1982              :  * Implementation of several jsonpath nodes:
    1983              :  *  - jpiAny (.** accessor),
    1984              :  *  - jpiAnyKey (.* accessor),
    1985              :  *  - jpiAnyArray ([*] accessor)
    1986              :  */
    1987              : static JsonPathExecResult
    1988         2593 : executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
    1989              :                JsonValueList *found, uint32 level, uint32 first, uint32 last,
    1990              :                bool ignoreStructuralErrors, bool unwrapNext)
    1991              : {
    1992         2593 :     JsonPathExecResult res = jperNotFound;
    1993              :     JsonbIterator *it;
    1994              :     int32       r;
    1995              :     JsonbValue  v;
    1996              : 
    1997         2593 :     check_stack_depth();
    1998              : 
    1999         2593 :     if (level > last)
    2000           22 :         return res;
    2001              : 
    2002         2571 :     it = JsonbIteratorInit(jbc);
    2003              : 
    2004              :     /*
    2005              :      * Recursively iterate over jsonb objects/arrays
    2006              :      */
    2007        14408 :     while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
    2008              :     {
    2009        12385 :         if (r == WJB_KEY)
    2010              :         {
    2011          477 :             r = JsonbIteratorNext(&it, &v, true);
    2012              :             Assert(r == WJB_VALUE);
    2013              :         }
    2014              : 
    2015        12385 :         if (r == WJB_VALUE || r == WJB_ELEM)
    2016              :         {
    2017              : 
    2018         7791 :             if (level >= first ||
    2019            8 :                 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
    2020            8 :                  v.type != jbvBinary))  /* leaves only requested */
    2021              :             {
    2022              :                 /* check expression */
    2023         7747 :                 if (jsp)
    2024              :                 {
    2025         5704 :                     if (ignoreStructuralErrors)
    2026              :                     {
    2027              :                         bool        savedIgnoreStructuralErrors;
    2028              : 
    2029          255 :                         savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
    2030          255 :                         cxt->ignoreStructuralErrors = true;
    2031          255 :                         res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
    2032          255 :                         cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
    2033              :                     }
    2034              :                     else
    2035         5449 :                         res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
    2036              : 
    2037         5528 :                     if (jperIsError(res))
    2038           49 :                         break;
    2039              : 
    2040         5479 :                     if (res == jperOk && !found)
    2041          263 :                         break;
    2042              :                 }
    2043         2043 :                 else if (found)
    2044         2013 :                     JsonValueListAppend(found, &v);
    2045              :                 else
    2046           30 :                     return jperOk;
    2047              :             }
    2048              : 
    2049         7273 :             if (level < last && v.type == jbvBinary)
    2050              :             {
    2051          131 :                 res = executeAnyItem
    2052              :                     (cxt, jsp, v.val.binary.data, found,
    2053              :                      level + 1, first, last,
    2054              :                      ignoreStructuralErrors, unwrapNext);
    2055              : 
    2056          131 :                 if (jperIsError(res))
    2057            0 :                     break;
    2058              : 
    2059          131 :                 if (res == jperOk && found == NULL)
    2060           30 :                     break;
    2061              :             }
    2062              :         }
    2063              :     }
    2064              : 
    2065         2365 :     return res;
    2066              : }
    2067              : 
    2068              : /*
    2069              :  * Execute unary or binary predicate.
    2070              :  *
    2071              :  * Predicates have existence semantics, because their operands are item
    2072              :  * sequences.  Pairs of items from the left and right operand's sequences are
    2073              :  * checked.  TRUE returned only if any pair satisfying the condition is found.
    2074              :  * In strict mode, even if the desired pair has already been found, all pairs
    2075              :  * still need to be examined to check the absence of errors.  If any error
    2076              :  * occurs, UNKNOWN (analogous to SQL NULL) is returned.
    2077              :  */
    2078              : static JsonPathBool
    2079        39887 : executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
    2080              :                  JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
    2081              :                  bool unwrapRightArg, JsonPathPredicateCallback exec,
    2082              :                  void *param)
    2083              : {
    2084              :     JsonPathExecResult res;
    2085              :     JsonValueListIterator lseqit;
    2086              :     JsonValueList lseq;
    2087              :     JsonValueList rseq;
    2088              :     JsonbValue *lval;
    2089        39887 :     bool        error = false;
    2090        39887 :     bool        found = false;
    2091              : 
    2092        39887 :     JsonValueListInit(&lseq);
    2093        39887 :     JsonValueListInit(&rseq);
    2094              : 
    2095              :     /* Left argument is always auto-unwrapped. */
    2096        39887 :     res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
    2097        39887 :     if (jperIsError(res))
    2098              :     {
    2099           12 :         error = true;
    2100           12 :         goto exit;
    2101              :     }
    2102              : 
    2103        39875 :     if (rarg)
    2104              :     {
    2105              :         /* Right argument is conditionally auto-unwrapped. */
    2106        39611 :         res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
    2107              :                                                 unwrapRightArg, &rseq);
    2108        39607 :         if (jperIsError(res))
    2109              :         {
    2110           39 :             error = true;
    2111           39 :             goto exit;
    2112              :         }
    2113              :     }
    2114              : 
    2115        39832 :     JsonValueListInitIterator(&lseq, &lseqit);
    2116        53383 :     while ((lval = JsonValueListNext(&lseqit)))
    2117              :     {
    2118              :         JsonValueListIterator rseqit;
    2119              :         JsonbValue *rval;
    2120        18261 :         bool        first = true;
    2121              : 
    2122        18261 :         JsonValueListInitIterator(&rseq, &rseqit);
    2123        18261 :         if (rarg)
    2124        17997 :             rval = JsonValueListNext(&rseqit);
    2125              :         else
    2126          264 :             rval = NULL;
    2127              : 
    2128              :         /* Loop over right arg sequence or do single pass otherwise */
    2129        28536 :         while (rarg ? (rval != NULL) : first)
    2130              :         {
    2131        14985 :             JsonPathBool res = exec(pred, lval, rval, param);
    2132              : 
    2133        14925 :             if (res == jpbUnknown)
    2134              :             {
    2135          502 :                 error = true;
    2136          502 :                 if (jspStrictAbsenceOfErrors(cxt))
    2137              :                 {
    2138           17 :                     found = false;  /* return unknown, not success */
    2139         4650 :                     goto exit;
    2140              :                 }
    2141              :             }
    2142        14423 :             else if (res == jpbTrue)
    2143              :             {
    2144         4843 :                 found = true;
    2145         4843 :                 if (!jspStrictAbsenceOfErrors(cxt))
    2146         4633 :                     goto exit;
    2147              :             }
    2148              : 
    2149        10275 :             first = false;
    2150        10275 :             if (rarg)
    2151        10079 :                 rval = JsonValueListNext(&rseqit);
    2152              :         }
    2153              :     }
    2154              : 
    2155        35122 : exit:
    2156        39823 :     JsonValueListClear(&lseq);
    2157        39823 :     JsonValueListClear(&rseq);
    2158              : 
    2159        39823 :     if (found)                  /* possible only in strict mode */
    2160         4775 :         return jpbTrue;
    2161              : 
    2162        35048 :     if (error)                  /* possible only in lax mode */
    2163          531 :         return jpbUnknown;
    2164              : 
    2165        34517 :     return jpbFalse;
    2166              : }
    2167              : 
    2168              : /*
    2169              :  * Execute binary arithmetic expression on singleton numeric operands.
    2170              :  * Array operands are automatically unwrapped in lax mode.
    2171              :  */
    2172              : static JsonPathExecResult
    2173          444 : executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
    2174              :                         JsonbValue *jb, BinaryArithmFunc func,
    2175              :                         JsonValueList *found)
    2176              : {
    2177              :     JsonPathExecResult jper;
    2178              :     JsonPathItem elem;
    2179              :     JsonValueList lseq;
    2180              :     JsonValueList rseq;
    2181              :     JsonbValue *lval;
    2182              :     JsonbValue *rval;
    2183              :     JsonbValue  resval;
    2184              :     Numeric     res;
    2185              : 
    2186          444 :     JsonValueListInit(&lseq);
    2187          444 :     JsonValueListInit(&rseq);
    2188              : 
    2189          444 :     jspGetLeftArg(jsp, &elem);
    2190              : 
    2191              :     /*
    2192              :      * XXX: By standard only operands of multiplicative expressions are
    2193              :      * unwrapped.  We extend it to other binary arithmetic expressions too.
    2194              :      */
    2195          444 :     jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
    2196          440 :     if (jperIsError(jper))
    2197              :     {
    2198            0 :         JsonValueListClear(&lseq);
    2199            0 :         JsonValueListClear(&rseq);
    2200            0 :         return jper;
    2201              :     }
    2202              : 
    2203          440 :     jspGetRightArg(jsp, &elem);
    2204              : 
    2205          440 :     jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
    2206          436 :     if (jperIsError(jper))
    2207              :     {
    2208            0 :         JsonValueListClear(&lseq);
    2209            0 :         JsonValueListClear(&rseq);
    2210            0 :         return jper;
    2211              :     }
    2212              : 
    2213          827 :     if (!JsonValueListIsSingleton(&lseq) ||
    2214          391 :         !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
    2215              :     {
    2216           45 :         JsonValueListClear(&lseq);
    2217           45 :         JsonValueListClear(&rseq);
    2218           45 :         RETURN_ERROR(ereport(ERROR,
    2219              :                              (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
    2220              :                               errmsg("left operand of jsonpath operator %s is not a single numeric value",
    2221              :                                      jspOperationName(jsp->type)))));
    2222              :     }
    2223              : 
    2224          759 :     if (!JsonValueListIsSingleton(&rseq) ||
    2225          368 :         !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
    2226              :     {
    2227           39 :         JsonValueListClear(&lseq);
    2228           39 :         JsonValueListClear(&rseq);
    2229           39 :         RETURN_ERROR(ereport(ERROR,
    2230              :                              (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
    2231              :                               errmsg("right operand of jsonpath operator %s is not a single numeric value",
    2232              :                                      jspOperationName(jsp->type)))));
    2233              :     }
    2234              : 
    2235          352 :     if (jspThrowErrors(cxt))
    2236              :     {
    2237           68 :         res = func(lval->val.numeric, rval->val.numeric, NULL);
    2238              :     }
    2239              :     else
    2240              :     {
    2241          284 :         ErrorSaveContext escontext = {T_ErrorSaveContext};
    2242              : 
    2243          284 :         res = func(lval->val.numeric, rval->val.numeric, (Node *) &escontext);
    2244              : 
    2245          284 :         if (escontext.error_occurred)
    2246              :         {
    2247            8 :             JsonValueListClear(&lseq);
    2248            8 :             JsonValueListClear(&rseq);
    2249            8 :             return jperError;
    2250              :         }
    2251              :     }
    2252              : 
    2253          328 :     JsonValueListClear(&lseq);
    2254          328 :     JsonValueListClear(&rseq);
    2255              : 
    2256          328 :     if (!jspGetNext(jsp, &elem) && !found)
    2257            5 :         return jperOk;
    2258              : 
    2259          323 :     resval.type = jbvNumeric;
    2260          323 :     resval.val.numeric = res;
    2261              : 
    2262          323 :     return executeNextItem(cxt, jsp, &elem, &resval, found);
    2263              : }
    2264              : 
    2265              : /*
    2266              :  * Execute unary arithmetic expression for each numeric item in its operand's
    2267              :  * sequence.  Array operand is automatically unwrapped in lax mode.
    2268              :  */
    2269              : static JsonPathExecResult
    2270          148 : executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
    2271              :                        JsonbValue *jb, PGFunction func, JsonValueList *found)
    2272              : {
    2273              :     JsonPathExecResult jper;
    2274              :     JsonPathExecResult jper2;
    2275              :     JsonPathItem elem;
    2276              :     JsonValueList seq;
    2277              :     JsonValueListIterator it;
    2278              :     JsonbValue *val;
    2279              :     bool        hasNext;
    2280              : 
    2281          148 :     JsonValueListInit(&seq);
    2282              : 
    2283          148 :     jspGetArg(jsp, &elem);
    2284          148 :     jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
    2285              : 
    2286          144 :     if (jperIsError(jper))
    2287            0 :         goto exit;
    2288              : 
    2289          144 :     jper = jperNotFound;
    2290              : 
    2291          144 :     hasNext = jspGetNext(jsp, &elem);
    2292              : 
    2293          144 :     JsonValueListInitIterator(&seq, &it);
    2294          258 :     while ((val = JsonValueListNext(&it)))
    2295              :     {
    2296          150 :         if ((val = getScalar(val, jbvNumeric)))
    2297              :         {
    2298          119 :             if (!found && !hasNext)
    2299              :             {
    2300           10 :                 jper = jperOk;
    2301           10 :                 goto exit;
    2302              :             }
    2303              :         }
    2304              :         else
    2305              :         {
    2306           31 :             if (!found && !hasNext)
    2307            5 :                 continue;       /* skip non-numerics processing */
    2308              : 
    2309           26 :             JsonValueListClear(&seq);
    2310           26 :             RETURN_ERROR(ereport(ERROR,
    2311              :                                  (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
    2312              :                                   errmsg("operand of unary jsonpath operator %s is not a numeric value",
    2313              :                                          jspOperationName(jsp->type)))));
    2314              :         }
    2315              : 
    2316          109 :         if (func)
    2317           66 :             val->val.numeric =
    2318           66 :                 DatumGetNumeric(DirectFunctionCall1(func,
    2319              :                                                     NumericGetDatum(val->val.numeric)));
    2320              : 
    2321          109 :         jper2 = executeNextItem(cxt, jsp, &elem, val, found);
    2322              : 
    2323          109 :         if (jperIsError(jper2))
    2324              :         {
    2325            0 :             jper = jper2;
    2326            0 :             goto exit;
    2327              :         }
    2328              : 
    2329          109 :         if (jper2 == jperOk)
    2330              :         {
    2331          109 :             jper = jperOk;
    2332          109 :             if (!found)
    2333            0 :                 goto exit;
    2334              :         }
    2335              :     }
    2336              : 
    2337          108 : exit:
    2338          118 :     JsonValueListClear(&seq);
    2339              : 
    2340          118 :     return jper;
    2341              : }
    2342              : 
    2343              : /*
    2344              :  * STARTS_WITH predicate callback.
    2345              :  *
    2346              :  * Check if the 'whole' string starts from 'initial' string.
    2347              :  */
    2348              : static JsonPathBool
    2349          128 : executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
    2350              :                   void *param)
    2351              : {
    2352          128 :     if (!(whole = getScalar(whole, jbvString)))
    2353           32 :         return jpbUnknown;      /* error */
    2354              : 
    2355           96 :     if (!(initial = getScalar(initial, jbvString)))
    2356            0 :         return jpbUnknown;      /* error */
    2357              : 
    2358           96 :     if (whole->val.string.len >= initial->val.string.len &&
    2359           72 :         !memcmp(whole->val.string.val,
    2360           72 :                 initial->val.string.val,
    2361           72 :                 initial->val.string.len))
    2362           48 :         return jpbTrue;
    2363              : 
    2364           48 :     return jpbFalse;
    2365              : }
    2366              : 
    2367              : /*
    2368              :  * LIKE_REGEX predicate callback.
    2369              :  *
    2370              :  * Check if the string matches regex pattern.
    2371              :  */
    2372              : static JsonPathBool
    2373          264 : executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
    2374              :                  void *param)
    2375              : {
    2376          264 :     JsonLikeRegexContext *cxt = param;
    2377              : 
    2378          264 :     if (!(str = getScalar(str, jbvString)))
    2379           80 :         return jpbUnknown;
    2380              : 
    2381              :     /* Cache regex text and converted flags. */
    2382          184 :     if (!cxt->regex)
    2383              :     {
    2384          184 :         cxt->regex =
    2385          184 :             cstring_to_text_with_len(jsp->content.like_regex.pattern,
    2386              :                                      jsp->content.like_regex.patternlen);
    2387          184 :         (void) jspConvertRegexFlags(jsp->content.like_regex.flags,
    2388              :                                     &(cxt->cflags), NULL);
    2389              :     }
    2390              : 
    2391          184 :     if (RE_compile_and_execute(cxt->regex, str->val.string.val,
    2392              :                                str->val.string.len,
    2393              :                                cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
    2394           68 :         return jpbTrue;
    2395              : 
    2396          116 :     return jpbFalse;
    2397              : }
    2398              : 
    2399              : /*
    2400              :  * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
    2401              :  * user function 'func'.
    2402              :  */
    2403              : static JsonPathExecResult
    2404          172 : executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    2405              :                          JsonbValue *jb, bool unwrap, PGFunction func,
    2406              :                          JsonValueList *found)
    2407              : {
    2408              :     JsonPathItem next;
    2409              :     Datum       datum;
    2410              :     JsonbValue  jbv;
    2411              : 
    2412          172 :     if (unwrap && JsonbType(jb) == jbvArray)
    2413            0 :         return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    2414              : 
    2415          172 :     if (!(jb = getScalar(jb, jbvNumeric)))
    2416           24 :         RETURN_ERROR(ereport(ERROR,
    2417              :                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    2418              :                               errmsg("jsonpath item method .%s() can only be applied to a numeric value",
    2419              :                                      jspOperationName(jsp->type)))));
    2420              : 
    2421          148 :     datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
    2422              : 
    2423          148 :     if (!jspGetNext(jsp, &next) && !found)
    2424            0 :         return jperOk;
    2425              : 
    2426          148 :     jbv.type = jbvNumeric;
    2427          148 :     jbv.val.numeric = DatumGetNumeric(datum);
    2428              : 
    2429          148 :     return executeNextItem(cxt, jsp, &next, &jbv, found);
    2430              : }
    2431              : 
    2432              : /*
    2433              :  * Implementation of the .datetime() and related methods.
    2434              :  *
    2435              :  * Converts a string into a date/time value. The actual type is determined at
    2436              :  * run time.
    2437              :  * If an argument is provided, this argument is used as a template string.
    2438              :  * Otherwise, the first fitting ISO format is selected.
    2439              :  *
    2440              :  * .date(), .time(), .time_tz(), .timestamp(), .timestamp_tz() methods don't
    2441              :  * have a format, so ISO format is used.  However, except for .date(), they all
    2442              :  * take an optional time precision.
    2443              :  */
    2444              : static JsonPathExecResult
    2445         5662 : executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    2446              :                       JsonbValue *jb, JsonValueList *found)
    2447              : {
    2448              :     JsonbValue  jbv;
    2449              :     Datum       value;
    2450              :     text       *datetime;
    2451              :     Oid         collid;
    2452              :     Oid         typid;
    2453         5662 :     int32       typmod = -1;
    2454         5662 :     int         tz = 0;
    2455              :     bool        hasNext;
    2456         5662 :     JsonPathExecResult res = jperNotFound;
    2457              :     JsonPathItem elem;
    2458         5662 :     int32       time_precision = -1;
    2459              : 
    2460         5662 :     if (!(jb = getScalar(jb, jbvString)))
    2461          120 :         RETURN_ERROR(ereport(ERROR,
    2462              :                              (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    2463              :                               errmsg("jsonpath item method .%s() can only be applied to a string",
    2464              :                                      jspOperationName(jsp->type)))));
    2465              : 
    2466         5542 :     datetime = cstring_to_text_with_len(jb->val.string.val,
    2467              :                                         jb->val.string.len);
    2468              : 
    2469              :     /*
    2470              :      * At some point we might wish to have callers supply the collation to
    2471              :      * use, but right now it's unclear that they'd be able to do better than
    2472              :      * DEFAULT_COLLATION_OID anyway.
    2473              :      */
    2474         5542 :     collid = DEFAULT_COLLATION_OID;
    2475              : 
    2476              :     /*
    2477              :      * .datetime(template) has an argument, the rest of the methods don't have
    2478              :      * an argument.  So we handle that separately.
    2479              :      */
    2480         5542 :     if (jsp->type == jpiDatetime && jsp->content.arg)
    2481         1061 :     {
    2482              :         text       *template;
    2483              :         char       *template_str;
    2484              :         int         template_len;
    2485         1101 :         ErrorSaveContext escontext = {T_ErrorSaveContext};
    2486              : 
    2487         1101 :         jspGetArg(jsp, &elem);
    2488              : 
    2489         1101 :         if (elem.type != jpiString)
    2490            0 :             elog(ERROR, "invalid jsonpath item type for .datetime() argument");
    2491              : 
    2492         1101 :         template_str = jspGetString(&elem, &template_len);
    2493              : 
    2494         1101 :         template = cstring_to_text_with_len(template_str,
    2495              :                                             template_len);
    2496              : 
    2497         1101 :         value = parse_datetime(datetime, template, collid, true,
    2498              :                                &typid, &typmod, &tz,
    2499         1101 :                                jspThrowErrors(cxt) ? NULL : (Node *) &escontext);
    2500              : 
    2501         1061 :         if (escontext.error_occurred)
    2502            0 :             res = jperError;
    2503              :         else
    2504         1061 :             res = jperOk;
    2505              :     }
    2506              :     else
    2507              :     {
    2508              :         /*
    2509              :          * According to SQL/JSON standard enumerate ISO formats for: date,
    2510              :          * timetz, time, timestamptz, timestamp.
    2511              :          *
    2512              :          * We also support ISO 8601 format (with "T") for timestamps, because
    2513              :          * to_json[b]() functions use this format.
    2514              :          */
    2515              :         static const char *fmt_str[] =
    2516              :         {
    2517              :             "yyyy-mm-dd",     /* date */
    2518              :             "HH24:MI:SS.USTZ",    /* timetz */
    2519              :             "HH24:MI:SSTZ",
    2520              :             "HH24:MI:SS.US",  /* time without tz */
    2521              :             "HH24:MI:SS",
    2522              :             "yyyy-mm-dd HH24:MI:SS.USTZ", /* timestamptz */
    2523              :             "yyyy-mm-dd HH24:MI:SSTZ",
    2524              :             "yyyy-mm-dd\"T\"HH24:MI:SS.USTZ",
    2525              :             "yyyy-mm-dd\"T\"HH24:MI:SSTZ",
    2526              :             "yyyy-mm-dd HH24:MI:SS.US", /* timestamp without tz */
    2527              :             "yyyy-mm-dd HH24:MI:SS",
    2528              :             "yyyy-mm-dd\"T\"HH24:MI:SS.US",
    2529              :             "yyyy-mm-dd\"T\"HH24:MI:SS"
    2530              :         };
    2531              : 
    2532              :         /* cache for format texts */
    2533              :         static text *fmt_txt[lengthof(fmt_str)] = {0};
    2534              :         int         i;
    2535              : 
    2536              :         /*
    2537              :          * Check for optional precision for methods other than .datetime() and
    2538              :          * .date()
    2539              :          */
    2540         4441 :         if (jsp->type != jpiDatetime && jsp->type != jpiDate &&
    2541         2468 :             jsp->content.arg)
    2542              :         {
    2543          520 :             ErrorSaveContext escontext = {T_ErrorSaveContext};
    2544              : 
    2545          520 :             jspGetArg(jsp, &elem);
    2546              : 
    2547          520 :             if (elem.type != jpiNumeric)
    2548            0 :                 elog(ERROR, "invalid jsonpath item type for %s argument",
    2549              :                      jspOperationName(jsp->type));
    2550              : 
    2551          520 :             time_precision = numeric_int4_safe(jspGetNumeric(&elem),
    2552              :                                                (Node *) &escontext);
    2553          520 :             if (escontext.error_occurred)
    2554           16 :                 RETURN_ERROR(ereport(ERROR,
    2555              :                                      (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    2556              :                                       errmsg("time precision of jsonpath item method .%s() is out of range for type integer",
    2557              :                                              jspOperationName(jsp->type)))));
    2558              :         }
    2559              : 
    2560              :         /* loop until datetime format fits */
    2561        25175 :         for (i = 0; i < lengthof(fmt_str); i++)
    2562              :         {
    2563        25143 :             ErrorSaveContext escontext = {T_ErrorSaveContext};
    2564              : 
    2565        25143 :             if (!fmt_txt[i])
    2566              :             {
    2567              :                 MemoryContext oldcxt =
    2568           52 :                     MemoryContextSwitchTo(TopMemoryContext);
    2569              : 
    2570           52 :                 fmt_txt[i] = cstring_to_text(fmt_str[i]);
    2571           52 :                 MemoryContextSwitchTo(oldcxt);
    2572              :             }
    2573              : 
    2574        25143 :             value = parse_datetime(datetime, fmt_txt[i], collid, true,
    2575              :                                    &typid, &typmod, &tz,
    2576              :                                    (Node *) &escontext);
    2577              : 
    2578        25143 :             if (!escontext.error_occurred)
    2579              :             {
    2580         4393 :                 res = jperOk;
    2581         4393 :                 break;
    2582              :             }
    2583              :         }
    2584              : 
    2585         4425 :         if (res == jperNotFound)
    2586              :         {
    2587           32 :             if (jsp->type == jpiDatetime)
    2588           12 :                 RETURN_ERROR(ereport(ERROR,
    2589              :                                      (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    2590              :                                       errmsg("%s format is not recognized: \"%s\"",
    2591              :                                              "datetime", text_to_cstring(datetime)),
    2592              :                                       errhint("Use a datetime template argument to specify the input data format."))));
    2593              :             else
    2594           20 :                 RETURN_ERROR(ereport(ERROR,
    2595              :                                      (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    2596              :                                       errmsg("%s format is not recognized: \"%s\"",
    2597              :                                              jspOperationName(jsp->type), text_to_cstring(datetime)))));
    2598              : 
    2599              :         }
    2600              :     }
    2601              : 
    2602              :     /*
    2603              :      * parse_datetime() processes the entire input string per the template or
    2604              :      * ISO format and returns the Datum in best fitted datetime type.  So, if
    2605              :      * this call is for a specific datatype, then we do the conversion here.
    2606              :      * Throw an error for incompatible types.
    2607              :      */
    2608         5454 :     switch (jsp->type)
    2609              :     {
    2610         2597 :         case jpiDatetime:       /* Nothing to do for DATETIME */
    2611         2597 :             break;
    2612          421 :         case jpiDate:
    2613              :             {
    2614              :                 /* Convert result type to date */
    2615          421 :                 switch (typid)
    2616              :                 {
    2617          317 :                     case DATEOID:   /* Nothing to do for DATE */
    2618          317 :                         break;
    2619            8 :                     case TIMEOID:
    2620              :                     case TIMETZOID:
    2621            8 :                         RETURN_ERROR(ereport(ERROR,
    2622              :                                              (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    2623              :                                               errmsg("%s format is not recognized: \"%s\"",
    2624              :                                                      "date", text_to_cstring(datetime)))));
    2625              :                         break;
    2626           52 :                     case TIMESTAMPOID:
    2627           52 :                         value = DirectFunctionCall1(timestamp_date,
    2628              :                                                     value);
    2629           52 :                         break;
    2630           44 :                     case TIMESTAMPTZOID:
    2631           44 :                         checkTimezoneIsUsedForCast(cxt->useTz,
    2632              :                                                    "timestamptz", "date");
    2633           28 :                         value = DirectFunctionCall1(timestamptz_date,
    2634              :                                                     value);
    2635           28 :                         break;
    2636            0 :                     default:
    2637            0 :                         elog(ERROR, "type with oid %u not supported", typid);
    2638              :                 }
    2639              : 
    2640          397 :                 typid = DATEOID;
    2641              :             }
    2642          397 :             break;
    2643          541 :         case jpiTime:
    2644              :             {
    2645              :                 /* Convert result type to time without time zone */
    2646          541 :                 switch (typid)
    2647              :                 {
    2648            4 :                     case DATEOID:
    2649            4 :                         RETURN_ERROR(ereport(ERROR,
    2650              :                                              (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    2651              :                                               errmsg("%s format is not recognized: \"%s\"",
    2652              :                                                      "time", text_to_cstring(datetime)))));
    2653              :                         break;
    2654          405 :                     case TIMEOID:   /* Nothing to do for TIME */
    2655          405 :                         break;
    2656           72 :                     case TIMETZOID:
    2657           72 :                         checkTimezoneIsUsedForCast(cxt->useTz,
    2658              :                                                    "timetz", "time");
    2659           52 :                         value = DirectFunctionCall1(timetz_time,
    2660              :                                                     value);
    2661           52 :                         break;
    2662           20 :                     case TIMESTAMPOID:
    2663           20 :                         value = DirectFunctionCall1(timestamp_time,
    2664              :                                                     value);
    2665           20 :                         break;
    2666           40 :                     case TIMESTAMPTZOID:
    2667           40 :                         checkTimezoneIsUsedForCast(cxt->useTz,
    2668              :                                                    "timestamptz", "time");
    2669           28 :                         value = DirectFunctionCall1(timestamptz_time,
    2670              :                                                     value);
    2671           28 :                         break;
    2672            0 :                     default:
    2673            0 :                         elog(ERROR, "type with oid %u not supported", typid);
    2674              :                 }
    2675              : 
    2676              :                 /* Force the user-given time precision, if any */
    2677          505 :                 if (time_precision != -1)
    2678              :                 {
    2679              :                     TimeADT     result;
    2680              : 
    2681              :                     /* Get a warning when precision is reduced */
    2682          108 :                     time_precision = anytime_typmod_check(false,
    2683              :                                                           time_precision);
    2684          108 :                     result = DatumGetTimeADT(value);
    2685          108 :                     AdjustTimeForTypmod(&result, time_precision);
    2686          108 :                     value = TimeADTGetDatum(result);
    2687              : 
    2688              :                     /* Update the typmod value with the user-given precision */
    2689          108 :                     typmod = time_precision;
    2690              :                 }
    2691              : 
    2692          505 :                 typid = TIMEOID;
    2693              :             }
    2694          505 :             break;
    2695          641 :         case jpiTimeTz:
    2696              :             {
    2697              :                 /* Convert result type to time with time zone */
    2698          641 :                 switch (typid)
    2699              :                 {
    2700            8 :                     case DATEOID:
    2701              :                     case TIMESTAMPOID:
    2702            8 :                         RETURN_ERROR(ereport(ERROR,
    2703              :                                              (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    2704              :                                               errmsg("%s format is not recognized: \"%s\"",
    2705              :                                                      "time_tz", text_to_cstring(datetime)))));
    2706              :                         break;
    2707           76 :                     case TIMEOID:
    2708           76 :                         checkTimezoneIsUsedForCast(cxt->useTz,
    2709              :                                                    "time", "timetz");
    2710           56 :                         value = DirectFunctionCall1(time_timetz,
    2711              :                                                     value);
    2712           56 :                         break;
    2713          529 :                     case TIMETZOID: /* Nothing to do for TIMETZ */
    2714          529 :                         break;
    2715           28 :                     case TIMESTAMPTZOID:
    2716           28 :                         value = DirectFunctionCall1(timestamptz_timetz,
    2717              :                                                     value);
    2718           28 :                         break;
    2719            0 :                     default:
    2720            0 :                         elog(ERROR, "type with oid %u not supported", typid);
    2721              :                 }
    2722              : 
    2723              :                 /* Force the user-given time precision, if any */
    2724          613 :                 if (time_precision != -1)
    2725              :                 {
    2726              :                     TimeTzADT  *result;
    2727              : 
    2728              :                     /* Get a warning when precision is reduced */
    2729          132 :                     time_precision = anytime_typmod_check(true,
    2730              :                                                           time_precision);
    2731          132 :                     result = DatumGetTimeTzADTP(value);
    2732          132 :                     AdjustTimeForTypmod(&result->time, time_precision);
    2733          132 :                     value = TimeTzADTPGetDatum(result);
    2734              : 
    2735              :                     /* Update the typmod value with the user-given precision */
    2736          132 :                     typmod = time_precision;
    2737              :                 }
    2738              : 
    2739          613 :                 typid = TIMETZOID;
    2740              :             }
    2741          613 :             break;
    2742          549 :         case jpiTimestamp:
    2743              :             {
    2744              :                 /* Convert result type to timestamp without time zone */
    2745          549 :                 switch (typid)
    2746              :                 {
    2747           36 :                     case DATEOID:
    2748           36 :                         value = DirectFunctionCall1(date_timestamp,
    2749              :                                                     value);
    2750           36 :                         break;
    2751            8 :                     case TIMEOID:
    2752              :                     case TIMETZOID:
    2753            8 :                         RETURN_ERROR(ereport(ERROR,
    2754              :                                              (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    2755              :                                               errmsg("%s format is not recognized: \"%s\"",
    2756              :                                                      "timestamp", text_to_cstring(datetime)))));
    2757              :                         break;
    2758          409 :                     case TIMESTAMPOID:  /* Nothing to do for TIMESTAMP */
    2759          409 :                         break;
    2760           96 :                     case TIMESTAMPTZOID:
    2761           96 :                         checkTimezoneIsUsedForCast(cxt->useTz,
    2762              :                                                    "timestamptz", "timestamp");
    2763           64 :                         value = DirectFunctionCall1(timestamptz_timestamp,
    2764              :                                                     value);
    2765           64 :                         break;
    2766            0 :                     default:
    2767            0 :                         elog(ERROR, "type with oid %u not supported", typid);
    2768              :                 }
    2769              : 
    2770              :                 /* Force the user-given time precision, if any */
    2771          509 :                 if (time_precision != -1)
    2772              :                 {
    2773              :                     Timestamp   result;
    2774          108 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
    2775              : 
    2776              :                     /* Get a warning when precision is reduced */
    2777          108 :                     time_precision = anytimestamp_typmod_check(false,
    2778              :                                                                time_precision);
    2779          108 :                     result = DatumGetTimestamp(value);
    2780          108 :                     AdjustTimestampForTypmod(&result, time_precision,
    2781              :                                              (Node *) &escontext);
    2782          108 :                     if (escontext.error_occurred)   /* should not happen */
    2783            0 :                         RETURN_ERROR(ereport(ERROR,
    2784              :                                              (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    2785              :                                               errmsg("time precision of jsonpath item method .%s() is invalid",
    2786              :                                                      jspOperationName(jsp->type)))));
    2787          108 :                     value = TimestampGetDatum(result);
    2788              : 
    2789              :                     /* Update the typmod value with the user-given precision */
    2790          108 :                     typmod = time_precision;
    2791              :                 }
    2792              : 
    2793          509 :                 typid = TIMESTAMPOID;
    2794              :             }
    2795          509 :             break;
    2796          705 :         case jpiTimestampTz:
    2797              :             {
    2798              :                 struct pg_tm tm;
    2799              :                 fsec_t      fsec;
    2800              : 
    2801              :                 /* Convert result type to timestamp with time zone */
    2802          705 :                 switch (typid)
    2803              :                 {
    2804           40 :                     case DATEOID:
    2805           40 :                         checkTimezoneIsUsedForCast(cxt->useTz,
    2806              :                                                    "date", "timestamptz");
    2807              : 
    2808              :                         /*
    2809              :                          * Get the timezone value explicitly since JsonbValue
    2810              :                          * keeps that separate.
    2811              :                          */
    2812           36 :                         j2date(DatumGetDateADT(value) + POSTGRES_EPOCH_JDATE,
    2813              :                                &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
    2814           36 :                         tm.tm_hour = 0;
    2815           36 :                         tm.tm_min = 0;
    2816           36 :                         tm.tm_sec = 0;
    2817           36 :                         tz = DetermineTimeZoneOffset(&tm, session_timezone);
    2818              : 
    2819           36 :                         value = DirectFunctionCall1(date_timestamptz,
    2820              :                                                     value);
    2821           36 :                         break;
    2822            8 :                     case TIMEOID:
    2823              :                     case TIMETZOID:
    2824            8 :                         RETURN_ERROR(ereport(ERROR,
    2825              :                                              (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    2826              :                                               errmsg("%s format is not recognized: \"%s\"",
    2827              :                                                      "timestamp_tz", text_to_cstring(datetime)))));
    2828              :                         break;
    2829           88 :                     case TIMESTAMPOID:
    2830           88 :                         checkTimezoneIsUsedForCast(cxt->useTz,
    2831              :                                                    "timestamp", "timestamptz");
    2832              : 
    2833              :                         /*
    2834              :                          * Get the timezone value explicitly since JsonbValue
    2835              :                          * keeps that separate.
    2836              :                          */
    2837           60 :                         if (timestamp2tm(DatumGetTimestamp(value), NULL, &tm,
    2838              :                                          &fsec, NULL, NULL) == 0)
    2839           60 :                             tz = DetermineTimeZoneOffset(&tm,
    2840              :                                                          session_timezone);
    2841              : 
    2842           60 :                         value = DirectFunctionCall1(timestamp_timestamptz,
    2843              :                                                     value);
    2844           60 :                         break;
    2845          569 :                     case TIMESTAMPTZOID:    /* Nothing to do for TIMESTAMPTZ */
    2846          569 :                         break;
    2847            0 :                     default:
    2848            0 :                         elog(ERROR, "type with oid %u not supported", typid);
    2849              :                 }
    2850              : 
    2851              :                 /* Force the user-given time precision, if any */
    2852          665 :                 if (time_precision != -1)
    2853              :                 {
    2854              :                     Timestamp   result;
    2855          140 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
    2856              : 
    2857              :                     /* Get a warning when precision is reduced */
    2858          140 :                     time_precision = anytimestamp_typmod_check(true,
    2859              :                                                                time_precision);
    2860          140 :                     result = DatumGetTimestampTz(value);
    2861          140 :                     AdjustTimestampForTypmod(&result, time_precision,
    2862              :                                              (Node *) &escontext);
    2863          140 :                     if (escontext.error_occurred)   /* should not happen */
    2864            0 :                         RETURN_ERROR(ereport(ERROR,
    2865              :                                              (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    2866              :                                               errmsg("time precision of jsonpath item method .%s() is invalid",
    2867              :                                                      jspOperationName(jsp->type)))));
    2868          140 :                     value = TimestampTzGetDatum(result);
    2869              : 
    2870              :                     /* Update the typmod value with the user-given precision */
    2871          140 :                     typmod = time_precision;
    2872              :                 }
    2873              : 
    2874          665 :                 typid = TIMESTAMPTZOID;
    2875              :             }
    2876          665 :             break;
    2877            0 :         default:
    2878            0 :             elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
    2879              :     }
    2880              : 
    2881         5286 :     pfree(datetime);
    2882              : 
    2883         5286 :     if (jperIsError(res))
    2884            0 :         return res;
    2885              : 
    2886         5286 :     hasNext = jspGetNext(jsp, &elem);
    2887              : 
    2888         5286 :     if (!hasNext && !found)
    2889           30 :         return res;
    2890              : 
    2891         5256 :     jbv.type = jbvDatetime;
    2892         5256 :     jbv.val.datetime.value = value;
    2893         5256 :     jbv.val.datetime.typid = typid;
    2894         5256 :     jbv.val.datetime.typmod = typmod;
    2895         5256 :     jbv.val.datetime.tz = tz;
    2896              : 
    2897         5256 :     return executeNextItem(cxt, jsp, &elem, &jbv, found);
    2898              : }
    2899              : 
    2900              : /*
    2901              :  * Implementation of .upper(), .lower() et al. string methods,
    2902              :  * that forward their actual implementation to internal functions.
    2903              :  */
    2904              : static JsonPathExecResult
    2905          470 : executeStringInternalMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    2906              :                             JsonbValue *jb, JsonValueList *found)
    2907              : {
    2908              :     JsonbValue  jbv;
    2909              :     bool        hasNext;
    2910          470 :     JsonPathExecResult res = jperNotFound;
    2911              :     JsonPathItem elem;
    2912              :     Datum       str;            /* Datum representation for the current string
    2913              :                                  * value. The first argument to internal
    2914              :                                  * functions */
    2915          470 :     char       *resStr = NULL;
    2916              : 
    2917              :     Assert(jsp->type == jpiStrReplace ||
    2918              :            jsp->type == jpiStrLower ||
    2919              :            jsp->type == jpiStrUpper ||
    2920              :            jsp->type == jpiStrLtrim ||
    2921              :            jsp->type == jpiStrRtrim ||
    2922              :            jsp->type == jpiStrBtrim ||
    2923              :            jsp->type == jpiStrInitcap ||
    2924              :            jsp->type == jpiStrSplitPart);
    2925              : 
    2926          470 :     if (!(jb = getScalar(jb, jbvString)))
    2927          200 :         RETURN_ERROR(ereport(ERROR,
    2928              :                              (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    2929              :                               errmsg("jsonpath item method .%s() can only be applied to a string",
    2930              :                                      jspOperationName(jsp->type)))));
    2931              : 
    2932          270 :     str = PointerGetDatum(cstring_to_text_with_len(jb->val.string.val, jb->val.string.len));
    2933              : 
    2934              :     /* Dispatch to the appropriate internal string function */
    2935          270 :     switch (jsp->type)
    2936              :     {
    2937           40 :         case jpiStrReplace:
    2938              :             {
    2939              :                 char       *from_str,
    2940              :                            *to_str;
    2941              : 
    2942           40 :                 jspGetLeftArg(jsp, &elem);
    2943           40 :                 if (elem.type != jpiString)
    2944            0 :                     elog(ERROR, "invalid jsonpath item type for .replace() from");
    2945              : 
    2946           40 :                 from_str = jspGetString(&elem, NULL);
    2947              : 
    2948           40 :                 jspGetRightArg(jsp, &elem);
    2949           40 :                 if (elem.type != jpiString)
    2950            0 :                     elog(ERROR, "invalid jsonpath item type for .replace() to");
    2951              : 
    2952           40 :                 to_str = jspGetString(&elem, NULL);
    2953              : 
    2954           40 :                 resStr = TextDatumGetCString(DirectFunctionCall3Coll(replace_text,
    2955              :                                                                      DEFAULT_COLLATION_OID,
    2956              :                                                                      str,
    2957              :                                                                      CStringGetTextDatum(from_str),
    2958              :                                                                      CStringGetTextDatum(to_str)));
    2959           40 :                 break;
    2960              :             }
    2961           66 :         case jpiStrLower:
    2962           66 :             resStr = TextDatumGetCString(DirectFunctionCall1Coll(lower, DEFAULT_COLLATION_OID, str));
    2963           66 :             break;
    2964           62 :         case jpiStrUpper:
    2965           62 :             resStr = TextDatumGetCString(DirectFunctionCall1Coll(upper, DEFAULT_COLLATION_OID, str));
    2966           62 :             break;
    2967           78 :         case jpiStrLtrim:
    2968              :         case jpiStrRtrim:
    2969              :         case jpiStrBtrim:
    2970              :             {
    2971           78 :                 PGFunction  func1 = NULL;
    2972           78 :                 PGFunction  func2 = NULL;
    2973              : 
    2974           78 :                 switch (jsp->type)
    2975              :                 {
    2976           50 :                     case jpiStrLtrim:
    2977           50 :                         func1 = ltrim1;
    2978           50 :                         func2 = ltrim;
    2979           50 :                         break;
    2980           12 :                     case jpiStrRtrim:
    2981           12 :                         func1 = rtrim1;
    2982           12 :                         func2 = rtrim;
    2983           12 :                         break;
    2984           16 :                     case jpiStrBtrim:
    2985           16 :                         func1 = btrim1;
    2986           16 :                         func2 = btrim;
    2987           16 :                         break;
    2988            0 :                     default:
    2989            0 :                         break;
    2990              :                 }
    2991              : 
    2992           78 :                 if (jsp->content.arg)
    2993              :                 {
    2994              :                     char       *characters_str;
    2995              : 
    2996           24 :                     jspGetArg(jsp, &elem);
    2997           24 :                     if (elem.type != jpiString)
    2998            0 :                         elog(ERROR, "invalid jsonpath item type for .%s() argument",
    2999              :                              jspOperationName(jsp->type));
    3000              : 
    3001           24 :                     characters_str = jspGetString(&elem, NULL);
    3002           24 :                     resStr = TextDatumGetCString(DirectFunctionCall2Coll(func2,
    3003              :                                                                          DEFAULT_COLLATION_OID, str,
    3004              :                                                                          CStringGetTextDatum(characters_str)));
    3005              :                 }
    3006              :                 else
    3007              :                 {
    3008           54 :                     resStr = TextDatumGetCString(DirectFunctionCall1Coll(func1,
    3009              :                                                                          DEFAULT_COLLATION_OID, str));
    3010              :                 }
    3011           78 :                 break;
    3012              :             }
    3013              : 
    3014           16 :         case jpiStrInitcap:
    3015           16 :             resStr = TextDatumGetCString(DirectFunctionCall1Coll(initcap, DEFAULT_COLLATION_OID, str));
    3016           16 :             break;
    3017            8 :         case jpiStrSplitPart:
    3018              :             {
    3019              :                 char       *from_str;
    3020              :                 Numeric     n;
    3021              : 
    3022            8 :                 jspGetLeftArg(jsp, &elem);
    3023            8 :                 if (elem.type != jpiString)
    3024            0 :                     elog(ERROR, "invalid jsonpath item type for .split_part()");
    3025              : 
    3026            8 :                 from_str = jspGetString(&elem, NULL);
    3027              : 
    3028            8 :                 jspGetRightArg(jsp, &elem);
    3029            8 :                 if (elem.type != jpiNumeric)
    3030            0 :                     elog(ERROR, "invalid jsonpath item type for .split_part()");
    3031              : 
    3032            8 :                 n = jspGetNumeric(&elem);
    3033              : 
    3034            8 :                 resStr = TextDatumGetCString(DirectFunctionCall3Coll(split_part,
    3035              :                                                                      DEFAULT_COLLATION_OID,
    3036              :                                                                      str,
    3037              :                                                                      CStringGetTextDatum(from_str),
    3038              :                                                                      DirectFunctionCall1(numeric_int4, NumericGetDatum(n))));
    3039            8 :                 break;
    3040              :             }
    3041            0 :         default:
    3042            0 :             elog(ERROR, "unsupported jsonpath item type: %d", jsp->type);
    3043              :     }
    3044              : 
    3045          270 :     if (resStr)
    3046          270 :         res = jperOk;
    3047              : 
    3048          270 :     hasNext = jspGetNext(jsp, &elem);
    3049              : 
    3050          270 :     if (!hasNext && !found)
    3051            0 :         return res;
    3052              : 
    3053          270 :     jbv.type = jbvString;
    3054          270 :     jbv.val.string.val = resStr;
    3055          270 :     jbv.val.string.len = strlen(resStr);
    3056              : 
    3057          270 :     return executeNextItem(cxt, jsp, &elem, &jbv, found);
    3058              : }
    3059              : 
    3060              : /*
    3061              :  * Implementation of .keyvalue() method.
    3062              :  *
    3063              :  * .keyvalue() method returns a sequence of object's key-value pairs in the
    3064              :  * following format: '{ "key": key, "value": value, "id": id }'.
    3065              :  *
    3066              :  * "id" field is an object identifier which is constructed from the two parts:
    3067              :  * base object id and its binary offset in base object's jsonb:
    3068              :  * id = 10000000000 * base_object_id + obj_offset_in_base_object
    3069              :  *
    3070              :  * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
    3071              :  * (maximal offset in jsonb).  Decimal multiplier is used here to improve the
    3072              :  * readability of identifiers.
    3073              :  *
    3074              :  * Base object is usually a root object of the path: context item '$' or path
    3075              :  * variable '$var', literals can't produce objects for now.  But if the path
    3076              :  * contains generated objects (.keyvalue() itself, for example), then they
    3077              :  * become base object for the subsequent .keyvalue().
    3078              :  *
    3079              :  * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
    3080              :  * of variables (see getJsonPathVariable()).  Ids for generated objects
    3081              :  * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
    3082              :  */
    3083              : static JsonPathExecResult
    3084           58 : executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    3085              :                       JsonbValue *jb, JsonValueList *found)
    3086              : {
    3087           58 :     JsonPathExecResult res = jperNotFound;
    3088              :     JsonPathItem next;
    3089              :     JsonbContainer *jbc;
    3090              :     JsonbValue  key;
    3091              :     JsonbValue  val;
    3092              :     JsonbValue  idval;
    3093              :     JsonbValue  keystr;
    3094              :     JsonbValue  valstr;
    3095              :     JsonbValue  idstr;
    3096              :     JsonbIterator *it;
    3097              :     JsonbIteratorToken tok;
    3098              :     int64       id;
    3099              :     bool        hasNext;
    3100              : 
    3101           58 :     if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
    3102           16 :         RETURN_ERROR(ereport(ERROR,
    3103              :                              (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
    3104              :                               errmsg("jsonpath item method .%s() can only be applied to an object",
    3105              :                                      jspOperationName(jsp->type)))));
    3106              : 
    3107           42 :     jbc = jb->val.binary.data;
    3108              : 
    3109           42 :     if (!JsonContainerSize(jbc))
    3110           12 :         return jperNotFound;    /* no key-value pairs */
    3111              : 
    3112           30 :     hasNext = jspGetNext(jsp, &next);
    3113              : 
    3114           30 :     keystr.type = jbvString;
    3115           30 :     keystr.val.string.val = "key";
    3116           30 :     keystr.val.string.len = 3;
    3117              : 
    3118           30 :     valstr.type = jbvString;
    3119           30 :     valstr.val.string.val = "value";
    3120           30 :     valstr.val.string.len = 5;
    3121              : 
    3122           30 :     idstr.type = jbvString;
    3123           30 :     idstr.val.string.val = "id";
    3124           30 :     idstr.val.string.len = 2;
    3125              : 
    3126              :     /* construct object id from its base object and offset inside that */
    3127           30 :     id = jb->type != jbvBinary ? 0 :
    3128           30 :         (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
    3129           30 :     id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
    3130              : 
    3131           30 :     idval.type = jbvNumeric;
    3132           30 :     idval.val.numeric = int64_to_numeric(id);
    3133              : 
    3134           30 :     it = JsonbIteratorInit(jbc);
    3135              : 
    3136          116 :     while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
    3137              :     {
    3138              :         JsonBaseObjectInfo baseObject;
    3139              :         JsonbValue  obj;
    3140              :         JsonbInState ps;
    3141              :         Jsonb      *jsonb;
    3142              : 
    3143           96 :         if (tok != WJB_KEY)
    3144           50 :             continue;
    3145              : 
    3146           46 :         res = jperOk;
    3147              : 
    3148           46 :         if (!hasNext && !found)
    3149           10 :             break;
    3150              : 
    3151           41 :         tok = JsonbIteratorNext(&it, &val, true);
    3152              :         Assert(tok == WJB_VALUE);
    3153              : 
    3154           41 :         memset(&ps, 0, sizeof(ps));
    3155              : 
    3156           41 :         pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
    3157              : 
    3158           41 :         pushJsonbValue(&ps, WJB_KEY, &keystr);
    3159           41 :         pushJsonbValue(&ps, WJB_VALUE, &key);
    3160              : 
    3161           41 :         pushJsonbValue(&ps, WJB_KEY, &valstr);
    3162           41 :         pushJsonbValue(&ps, WJB_VALUE, &val);
    3163              : 
    3164           41 :         pushJsonbValue(&ps, WJB_KEY, &idstr);
    3165           41 :         pushJsonbValue(&ps, WJB_VALUE, &idval);
    3166              : 
    3167           41 :         pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
    3168              : 
    3169           41 :         jsonb = JsonbValueToJsonb(ps.result);
    3170              : 
    3171           41 :         JsonbInitBinary(&obj, jsonb);
    3172              : 
    3173           41 :         baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
    3174              : 
    3175           41 :         res = executeNextItem(cxt, jsp, &next, &obj, found);
    3176              : 
    3177           41 :         cxt->baseObject = baseObject;
    3178              : 
    3179           41 :         if (jperIsError(res))
    3180            0 :             return res;
    3181              : 
    3182           41 :         if (res == jperOk && !found)
    3183            5 :             break;
    3184              :     }
    3185              : 
    3186           30 :     return res;
    3187              : }
    3188              : 
    3189              : /*
    3190              :  * Convert boolean execution status 'res' to a boolean JSON item and execute
    3191              :  * next jsonpath.
    3192              :  */
    3193              : static JsonPathExecResult
    3194        68139 : appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
    3195              :                  JsonValueList *found, JsonPathBool res)
    3196              : {
    3197              :     JsonPathItem next;
    3198              :     JsonbValue  jbv;
    3199              : 
    3200        68139 :     if (!jspGetNext(jsp, &next) && !found)
    3201           13 :         return jperOk;          /* found singleton boolean value */
    3202              : 
    3203        68126 :     if (res == jpbUnknown)
    3204              :     {
    3205           23 :         jbv.type = jbvNull;
    3206              :     }
    3207              :     else
    3208              :     {
    3209        68103 :         jbv.type = jbvBool;
    3210        68103 :         jbv.val.boolean = res == jpbTrue;
    3211              :     }
    3212              : 
    3213        68126 :     return executeNextItem(cxt, jsp, &next, &jbv, found);
    3214              : }
    3215              : 
    3216              : /*
    3217              :  * Convert jsonpath's scalar or variable node to actual jsonb value.
    3218              :  *
    3219              :  * If node is a variable then its id returned, otherwise 0 returned.
    3220              :  */
    3221              : static void
    3222        40705 : getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
    3223              :                 JsonbValue *value)
    3224              : {
    3225        40705 :     switch (item->type)
    3226              :     {
    3227         5036 :         case jpiNull:
    3228         5036 :             value->type = jbvNull;
    3229         5036 :             break;
    3230          950 :         case jpiBool:
    3231          950 :             value->type = jbvBool;
    3232          950 :             value->val.boolean = jspGetBool(item);
    3233          950 :             break;
    3234        13328 :         case jpiNumeric:
    3235        13328 :             value->type = jbvNumeric;
    3236        13328 :             value->val.numeric = jspGetNumeric(item);
    3237        13328 :             break;
    3238        16115 :         case jpiString:
    3239        16115 :             value->type = jbvString;
    3240        32230 :             value->val.string.val = jspGetString(item,
    3241        16115 :                                                  &value->val.string.len);
    3242        16115 :             break;
    3243         5276 :         case jpiVariable:
    3244         5276 :             getJsonPathVariable(cxt, item, value);
    3245         5244 :             return;
    3246            0 :         default:
    3247            0 :             elog(ERROR, "unexpected jsonpath item type");
    3248              :     }
    3249              : }
    3250              : 
    3251              : /*
    3252              :  * Returns the computed value of a JSON path variable with given name.
    3253              :  */
    3254              : static JsonbValue *
    3255         1740 : GetJsonPathVar(void *cxt, char *varName, int varNameLen,
    3256              :                JsonbValue *baseObject, int *baseObjectId)
    3257              : {
    3258         1740 :     JsonPathVariable *var = NULL;
    3259         1740 :     List       *vars = cxt;
    3260              :     ListCell   *lc;
    3261              :     JsonbValue *result;
    3262         1740 :     int         id = 1;
    3263              : 
    3264         2504 :     foreach(lc, vars)
    3265              :     {
    3266         2492 :         JsonPathVariable *curvar = lfirst(lc);
    3267              : 
    3268         2492 :         if (curvar->namelen == varNameLen &&
    3269         2484 :             strncmp(curvar->name, varName, varNameLen) == 0)
    3270              :         {
    3271         1728 :             var = curvar;
    3272         1728 :             break;
    3273              :         }
    3274              : 
    3275          764 :         id++;
    3276              :     }
    3277              : 
    3278         1740 :     if (var == NULL)
    3279              :     {
    3280           12 :         *baseObjectId = -1;
    3281           12 :         return NULL;
    3282              :     }
    3283              : 
    3284         1728 :     result = palloc_object(JsonbValue);
    3285         1728 :     if (var->isnull)
    3286              :     {
    3287            0 :         *baseObjectId = 0;
    3288            0 :         result->type = jbvNull;
    3289              :     }
    3290              :     else
    3291         1728 :         JsonItemFromDatum(var->value, var->typid, var->typmod, result);
    3292              : 
    3293         1728 :     *baseObject = *result;
    3294         1728 :     *baseObjectId = id;
    3295              : 
    3296         1728 :     return result;
    3297              : }
    3298              : 
    3299              : static int
    3300         4204 : CountJsonPathVars(void *cxt)
    3301              : {
    3302         4204 :     List       *vars = (List *) cxt;
    3303              : 
    3304         4204 :     return list_length(vars);
    3305              : }
    3306              : 
    3307              : 
    3308              : /*
    3309              :  * Initialize JsonbValue to pass to jsonpath executor from given
    3310              :  * datum value of the specified type.
    3311              :  */
    3312              : static void
    3313         1728 : JsonItemFromDatum(Datum val, Oid typid, int32 typmod, JsonbValue *res)
    3314              : {
    3315         1728 :     switch (typid)
    3316              :     {
    3317            0 :         case BOOLOID:
    3318            0 :             res->type = jbvBool;
    3319            0 :             res->val.boolean = DatumGetBool(val);
    3320            0 :             break;
    3321            0 :         case NUMERICOID:
    3322            0 :             JsonbValueInitNumericDatum(res, val);
    3323            0 :             break;
    3324            0 :         case INT2OID:
    3325            0 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(int2_numeric, val));
    3326            0 :             break;
    3327         1660 :         case INT4OID:
    3328         1660 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(int4_numeric, val));
    3329         1660 :             break;
    3330            0 :         case INT8OID:
    3331            0 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(int8_numeric, val));
    3332            0 :             break;
    3333            0 :         case FLOAT4OID:
    3334            0 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(float4_numeric, val));
    3335            0 :             break;
    3336            0 :         case FLOAT8OID:
    3337            0 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(float8_numeric, val));
    3338            0 :             break;
    3339            8 :         case TEXTOID:
    3340              :         case VARCHAROID:
    3341            8 :             res->type = jbvString;
    3342            8 :             res->val.string.val = VARDATA_ANY(DatumGetPointer(val));
    3343            8 :             res->val.string.len = VARSIZE_ANY_EXHDR(DatumGetPointer(val));
    3344            8 :             break;
    3345           48 :         case DATEOID:
    3346              :         case TIMEOID:
    3347              :         case TIMETZOID:
    3348              :         case TIMESTAMPOID:
    3349              :         case TIMESTAMPTZOID:
    3350           48 :             res->type = jbvDatetime;
    3351           48 :             res->val.datetime.value = val;
    3352           48 :             res->val.datetime.typid = typid;
    3353           48 :             res->val.datetime.typmod = typmod;
    3354           48 :             res->val.datetime.tz = 0;
    3355           48 :             break;
    3356           12 :         case JSONBOID:
    3357              :             {
    3358           12 :                 JsonbValue *jbv = res;
    3359           12 :                 Jsonb      *jb = DatumGetJsonbP(val);
    3360              : 
    3361           12 :                 if (JsonContainerIsScalar(&jb->root))
    3362              :                 {
    3363              :                     bool        result PG_USED_FOR_ASSERTS_ONLY;
    3364              : 
    3365           12 :                     result = JsonbExtractScalar(&jb->root, jbv);
    3366              :                     Assert(result);
    3367              :                 }
    3368              :                 else
    3369            0 :                     JsonbInitBinary(jbv, jb);
    3370           12 :                 break;
    3371              :             }
    3372            0 :         case JSONOID:
    3373              :             {
    3374            0 :                 text       *txt = DatumGetTextP(val);
    3375            0 :                 char       *str = text_to_cstring(txt);
    3376              :                 Jsonb      *jb;
    3377              : 
    3378            0 :                 jb = DatumGetJsonbP(DirectFunctionCall1(jsonb_in,
    3379              :                                                         CStringGetDatum(str)));
    3380            0 :                 pfree(str);
    3381              : 
    3382            0 :                 JsonItemFromDatum(JsonbPGetDatum(jb), JSONBOID, -1, res);
    3383            0 :                 break;
    3384              :             }
    3385            0 :         default:
    3386            0 :             ereport(ERROR,
    3387              :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3388              :                     errmsg("could not convert value of type %s to jsonpath",
    3389              :                            format_type_be(typid)));
    3390              :     }
    3391         1728 : }
    3392              : 
    3393              : /* Initialize numeric value from the given datum */
    3394              : static void
    3395         1660 : JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num)
    3396              : {
    3397         1660 :     jbv->type = jbvNumeric;
    3398         1660 :     jbv->val.numeric = DatumGetNumeric(num);
    3399         1660 : }
    3400              : 
    3401              : /*
    3402              :  * Get the value of variable passed to jsonpath executor
    3403              :  */
    3404              : static void
    3405         5276 : getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
    3406              :                     JsonbValue *value)
    3407              : {
    3408              :     char       *varName;
    3409              :     int         varNameLength;
    3410              :     JsonbValue  baseObject;
    3411              :     int         baseObjectId;
    3412              :     JsonbValue *v;
    3413              : 
    3414              :     Assert(variable->type == jpiVariable);
    3415         5276 :     varName = jspGetString(variable, &varNameLength);
    3416              : 
    3417        10552 :     if (cxt->vars == NULL ||
    3418         5276 :         (v = cxt->getVar(cxt->vars, varName, varNameLength,
    3419              :                          &baseObject, &baseObjectId)) == NULL)
    3420           32 :         ereport(ERROR,
    3421              :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    3422              :                  errmsg("could not find jsonpath variable \"%s\"",
    3423              :                         pnstrdup(varName, varNameLength))));
    3424              : 
    3425         5244 :     if (baseObjectId > 0)
    3426              :     {
    3427         5244 :         *value = *v;
    3428         5244 :         setBaseObject(cxt, &baseObject, baseObjectId);
    3429              :     }
    3430         5244 : }
    3431              : 
    3432              : /*
    3433              :  * Definition of JsonPathGetVarCallback for when JsonPathExecContext.vars
    3434              :  * is specified as a jsonb value.
    3435              :  */
    3436              : static JsonbValue *
    3437         3536 : getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, int varNameLength,
    3438              :                              JsonbValue *baseObject, int *baseObjectId)
    3439              : {
    3440         3536 :     Jsonb      *vars = varsJsonb;
    3441              :     JsonbValue  tmp;
    3442              :     JsonbValue *result;
    3443              : 
    3444         3536 :     tmp.type = jbvString;
    3445         3536 :     tmp.val.string.val = varName;
    3446         3536 :     tmp.val.string.len = varNameLength;
    3447              : 
    3448         3536 :     result = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
    3449              : 
    3450         3536 :     if (result == NULL)
    3451              :     {
    3452           20 :         *baseObjectId = -1;
    3453           20 :         return NULL;
    3454              :     }
    3455              : 
    3456         3516 :     *baseObjectId = 1;
    3457         3516 :     JsonbInitBinary(baseObject, vars);
    3458              : 
    3459         3516 :     return result;
    3460              : }
    3461              : 
    3462              : /*
    3463              :  * Definition of JsonPathCountVarsCallback for when JsonPathExecContext.vars
    3464              :  * is specified as a jsonb value.
    3465              :  */
    3466              : static int
    3467       128814 : countVariablesFromJsonb(void *varsJsonb)
    3468              : {
    3469       128814 :     Jsonb      *vars = varsJsonb;
    3470              : 
    3471       128814 :     if (vars && !JsonContainerIsObject(&vars->root))
    3472              :     {
    3473            8 :         ereport(ERROR,
    3474              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3475              :                 errmsg("\"vars\" argument is not an object"),
    3476              :                 errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object."));
    3477              :     }
    3478              : 
    3479              :     /* count of base objects */
    3480       128806 :     return vars != NULL ? 1 : 0;
    3481              : }
    3482              : 
    3483              : /**************** Support functions for JsonPath execution *****************/
    3484              : 
    3485              : /*
    3486              :  * Returns the size of an array item, or -1 if item is not an array.
    3487              :  */
    3488              : static int
    3489          381 : JsonbArraySize(JsonbValue *jb)
    3490              : {
    3491              :     Assert(jb->type != jbvArray);
    3492              : 
    3493          381 :     if (jb->type == jbvBinary)
    3494              :     {
    3495          353 :         JsonbContainer *jbc = jb->val.binary.data;
    3496              : 
    3497          353 :         if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
    3498          345 :             return JsonContainerSize(jbc);
    3499              :     }
    3500              : 
    3501           36 :     return -1;
    3502              : }
    3503              : 
    3504              : /* Comparison predicate callback. */
    3505              : static JsonPathBool
    3506        14593 : executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
    3507              : {
    3508        14593 :     JsonPathExecContext *cxt = (JsonPathExecContext *) p;
    3509              : 
    3510        14593 :     return compareItems(cmp->type, lv, rv, cxt->useTz);
    3511              : }
    3512              : 
    3513              : /*
    3514              :  * Perform per-byte comparison of two strings.
    3515              :  */
    3516              : static int
    3517         2304 : binaryCompareStrings(const char *s1, int len1,
    3518              :                      const char *s2, int len2)
    3519              : {
    3520              :     int         cmp;
    3521              : 
    3522         2304 :     cmp = memcmp(s1, s2, Min(len1, len2));
    3523              : 
    3524         2304 :     if (cmp != 0)
    3525         1312 :         return cmp;
    3526              : 
    3527          992 :     if (len1 == len2)
    3528          192 :         return 0;
    3529              : 
    3530          800 :     return len1 < len2 ? -1 : 1;
    3531              : }
    3532              : 
    3533              : /*
    3534              :  * Compare two strings in the current server encoding using Unicode codepoint
    3535              :  * collation.
    3536              :  */
    3537              : static int
    3538         2304 : compareStrings(const char *mbstr1, int mblen1,
    3539              :                const char *mbstr2, int mblen2)
    3540              : {
    3541         4608 :     if (GetDatabaseEncoding() == PG_SQL_ASCII ||
    3542         2304 :         GetDatabaseEncoding() == PG_UTF8)
    3543              :     {
    3544              :         /*
    3545              :          * It's known property of UTF-8 strings that their per-byte comparison
    3546              :          * result matches codepoints comparison result.  ASCII can be
    3547              :          * considered as special case of UTF-8.
    3548              :          */
    3549         2304 :         return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
    3550              :     }
    3551              :     else
    3552              :     {
    3553              :         char       *utf8str1,
    3554              :                    *utf8str2;
    3555              :         int         cmp,
    3556              :                     utf8len1,
    3557              :                     utf8len2;
    3558              : 
    3559              :         /*
    3560              :          * We have to convert other encodings to UTF-8 first, then compare.
    3561              :          * Input strings may be not null-terminated and pg_server_to_any() may
    3562              :          * return them "as is".  So, use strlen() only if there is real
    3563              :          * conversion.
    3564              :          */
    3565            0 :         utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
    3566            0 :         utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
    3567            0 :         utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
    3568            0 :         utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
    3569              : 
    3570            0 :         cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
    3571              : 
    3572              :         /*
    3573              :          * If pg_server_to_any() did no real conversion, then we actually
    3574              :          * compared original strings.  So, we already done.
    3575              :          */
    3576            0 :         if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
    3577            0 :             return cmp;
    3578              : 
    3579              :         /* Free memory if needed */
    3580            0 :         if (mbstr1 != utf8str1)
    3581            0 :             pfree(utf8str1);
    3582            0 :         if (mbstr2 != utf8str2)
    3583            0 :             pfree(utf8str2);
    3584              : 
    3585              :         /*
    3586              :          * When all Unicode codepoints are equal, return result of binary
    3587              :          * comparison.  In some edge cases, same characters may have different
    3588              :          * representations in encoding.  Then our behavior could diverge from
    3589              :          * standard.  However, that allow us to do simple binary comparison
    3590              :          * for "==" operator, which is performance critical in typical cases.
    3591              :          * In future to implement strict standard conformance, we can do
    3592              :          * normalization of input JSON strings.
    3593              :          */
    3594            0 :         if (cmp == 0)
    3595            0 :             return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
    3596              :         else
    3597            0 :             return cmp;
    3598              :     }
    3599              : }
    3600              : 
    3601              : /*
    3602              :  * Compare two SQL/JSON items using comparison operation 'op'.
    3603              :  */
    3604              : static JsonPathBool
    3605        14593 : compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
    3606              : {
    3607              :     int         cmp;
    3608              :     bool        res;
    3609              : 
    3610        14593 :     if (jb1->type != jb2->type)
    3611              :     {
    3612         2096 :         if (jb1->type == jbvNull || jb2->type == jbvNull)
    3613              : 
    3614              :             /*
    3615              :              * Equality and order comparison of nulls to non-nulls returns
    3616              :              * always false, but inequality comparison returns true.
    3617              :              */
    3618         1918 :             return op == jpiNotEqual ? jpbTrue : jpbFalse;
    3619              : 
    3620              :         /* Non-null items of different types are not comparable. */
    3621          178 :         return jpbUnknown;
    3622              :     }
    3623              : 
    3624        12497 :     switch (jb1->type)
    3625              :     {
    3626          124 :         case jbvNull:
    3627          124 :             cmp = 0;
    3628          124 :             break;
    3629          580 :         case jbvBool:
    3630          844 :             cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
    3631          264 :                 jb1->val.boolean ? 1 : -1;
    3632          580 :             break;
    3633         2749 :         case jbvNumeric:
    3634         2749 :             cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
    3635         2749 :             break;
    3636         6620 :         case jbvString:
    3637         6620 :             if (op == jpiEqual)
    3638         4316 :                 return jb1->val.string.len != jb2->val.string.len ||
    3639         2388 :                     memcmp(jb1->val.string.val,
    3640         2388 :                            jb2->val.string.val,
    3641         4316 :                            jb1->val.string.len) ? jpbFalse : jpbTrue;
    3642              : 
    3643         2304 :             cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
    3644         2304 :                                  jb2->val.string.val, jb2->val.string.len);
    3645         2304 :             break;
    3646         2416 :         case jbvDatetime:
    3647              :             {
    3648              :                 bool        cast_error;
    3649              : 
    3650         2416 :                 cmp = compareDatetime(jb1->val.datetime.value,
    3651              :                                       jb1->val.datetime.typid,
    3652              :                                       jb2->val.datetime.value,
    3653              :                                       jb2->val.datetime.typid,
    3654              :                                       useTz,
    3655              :                                       &cast_error);
    3656              : 
    3657         2356 :                 if (cast_error)
    3658          204 :                     return jpbUnknown;
    3659              :             }
    3660         2152 :             break;
    3661              : 
    3662            8 :         case jbvBinary:
    3663              :         case jbvArray:
    3664              :         case jbvObject:
    3665            8 :             return jpbUnknown;  /* non-scalars are not comparable */
    3666              : 
    3667            0 :         default:
    3668            0 :             elog(ERROR, "invalid jsonb value type %d", jb1->type);
    3669              :     }
    3670              : 
    3671         7909 :     switch (op)
    3672              :     {
    3673         1818 :         case jpiEqual:
    3674         1818 :             res = (cmp == 0);
    3675         1818 :             break;
    3676            4 :         case jpiNotEqual:
    3677            4 :             res = (cmp != 0);
    3678            4 :             break;
    3679         1418 :         case jpiLess:
    3680         1418 :             res = (cmp < 0);
    3681         1418 :             break;
    3682         1105 :         case jpiGreater:
    3683         1105 :             res = (cmp > 0);
    3684         1105 :             break;
    3685         1361 :         case jpiLessOrEqual:
    3686         1361 :             res = (cmp <= 0);
    3687         1361 :             break;
    3688         2203 :         case jpiGreaterOrEqual:
    3689         2203 :             res = (cmp >= 0);
    3690         2203 :             break;
    3691            0 :         default:
    3692            0 :             elog(ERROR, "unrecognized jsonpath operation: %d", op);
    3693              :             return jpbUnknown;
    3694              :     }
    3695              : 
    3696         7909 :     return res ? jpbTrue : jpbFalse;
    3697              : }
    3698              : 
    3699              : /* Compare two numerics */
    3700              : static int
    3701         2749 : compareNumeric(Numeric a, Numeric b)
    3702              : {
    3703         2749 :     return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
    3704              :                                              NumericGetDatum(a),
    3705              :                                              NumericGetDatum(b)));
    3706              : }
    3707              : 
    3708              : static JsonbValue *
    3709         1248 : copyJsonbValue(JsonbValue *src)
    3710              : {
    3711         1248 :     JsonbValue *dst = palloc_object(JsonbValue);
    3712              : 
    3713         1248 :     *dst = *src;
    3714              : 
    3715         1248 :     return dst;
    3716              : }
    3717              : 
    3718              : /*
    3719              :  * Execute array subscript expression and convert resulting numeric item to
    3720              :  * the integer type with truncation.
    3721              :  */
    3722              : static JsonPathExecResult
    3723          362 : getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
    3724              :               int32 *index)
    3725              : {
    3726              :     JsonbValue *jbv;
    3727              :     JsonValueList found;
    3728              :     JsonPathExecResult res;
    3729              :     Datum       numeric_index;
    3730          362 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
    3731              : 
    3732          362 :     JsonValueListInit(&found);
    3733              : 
    3734          362 :     res = executeItem(cxt, jsp, jb, &found);
    3735              : 
    3736          358 :     if (jperIsError(res))
    3737              :     {
    3738            0 :         JsonValueListClear(&found);
    3739            0 :         return res;
    3740              :     }
    3741              : 
    3742          708 :     if (!JsonValueListIsSingleton(&found) ||
    3743          350 :         !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
    3744              :     {
    3745           16 :         JsonValueListClear(&found);
    3746           16 :         RETURN_ERROR(ereport(ERROR,
    3747              :                              (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
    3748              :                               errmsg("jsonpath array subscript is not a single numeric value"))));
    3749              :     }
    3750              : 
    3751          342 :     numeric_index = DirectFunctionCall2(numeric_trunc,
    3752              :                                         NumericGetDatum(jbv->val.numeric),
    3753              :                                         Int32GetDatum(0));
    3754              : 
    3755          342 :     *index = numeric_int4_safe(DatumGetNumeric(numeric_index),
    3756              :                                (Node *) &escontext);
    3757              : 
    3758          342 :     JsonValueListClear(&found);
    3759              : 
    3760          342 :     if (escontext.error_occurred)
    3761           18 :         RETURN_ERROR(ereport(ERROR,
    3762              :                              (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
    3763              :                               errmsg("jsonpath array subscript is out of integer range"))));
    3764              : 
    3765          324 :     return jperOk;
    3766              : }
    3767              : 
    3768              : /* Save base object and its id needed for the execution of .keyvalue(). */
    3769              : static JsonBaseObjectInfo
    3770       146344 : setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
    3771              : {
    3772       146344 :     JsonBaseObjectInfo baseObject = cxt->baseObject;
    3773              : 
    3774       146344 :     cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
    3775              :         (JsonbContainer *) jbv->val.binary.data;
    3776       146344 :     cxt->baseObject.id = id;
    3777              : 
    3778       146344 :     return baseObject;
    3779              : }
    3780              : 
    3781              : /*
    3782              :  * JsonValueList support functions
    3783              :  */
    3784              : 
    3785              : static void
    3786       236205 : JsonValueListInit(JsonValueList *jvl)
    3787              : {
    3788       236205 :     jvl->nitems = 0;
    3789       236205 :     jvl->maxitems = BASE_JVL_ITEMS;
    3790       236205 :     jvl->next = NULL;
    3791       236205 :     jvl->last = jvl;
    3792       236205 : }
    3793              : 
    3794              : static void
    3795       161497 : JsonValueListClear(JsonValueList *jvl)
    3796              : {
    3797              :     JsonValueList *nxt;
    3798              : 
    3799              :     /* Release any extra chunks */
    3800       161953 :     for (JsonValueList *chunk = jvl->next; chunk != NULL; chunk = nxt)
    3801              :     {
    3802          456 :         nxt = chunk->next;
    3803          456 :         pfree(chunk);
    3804              :     }
    3805              :     /* ... and reset to empty */
    3806       161497 :     jvl->nitems = 0;
    3807              :     Assert(jvl->maxitems == BASE_JVL_ITEMS);
    3808       161497 :     jvl->next = NULL;
    3809       161497 :     jvl->last = jvl;
    3810       161497 : }
    3811              : 
    3812              : static void
    3813       185436 : JsonValueListAppend(JsonValueList *jvl, const JsonbValue *jbv)
    3814              : {
    3815       185436 :     JsonValueList *last = jvl->last;
    3816              : 
    3817       185436 :     if (last->nitems < last->maxitems)
    3818              :     {
    3819              :         /* there's still room in the last existing chunk */
    3820       184542 :         last->items[last->nitems] = *jbv;
    3821       184542 :         last->nitems++;
    3822              :     }
    3823              :     else
    3824              :     {
    3825              :         /* need a new last chunk */
    3826              :         JsonValueList *nxt;
    3827              :         int         nxtsize;
    3828              : 
    3829          894 :         nxtsize = last->maxitems * 2;    /* double the size with each chunk */
    3830          894 :         nxtsize = Max(nxtsize, MIN_EXTRA_JVL_ITEMS);    /* but at least this */
    3831          894 :         nxt = palloc(offsetof(JsonValueList, items) +
    3832          894 :                      nxtsize * sizeof(JsonbValue));
    3833          894 :         nxt->nitems = 1;
    3834          894 :         nxt->maxitems = nxtsize;
    3835          894 :         nxt->next = NULL;
    3836          894 :         nxt->items[0] = *jbv;
    3837          894 :         last->next = nxt;
    3838          894 :         jvl->last = nxt;
    3839              :     }
    3840       185436 : }
    3841              : 
    3842              : static bool
    3843         7518 : JsonValueListIsEmpty(const JsonValueList *jvl)
    3844              : {
    3845              :     /* We need not examine extra chunks for this */
    3846         7518 :     return (jvl->nitems == 0);
    3847              : }
    3848              : 
    3849              : static bool
    3850        66476 : JsonValueListIsSingleton(const JsonValueList *jvl)
    3851              : {
    3852              : #if BASE_JVL_ITEMS > 1
    3853              :     /* We need not examine extra chunks in this case */
    3854        66476 :     return (jvl->nitems == 1);
    3855              : #else
    3856              :     return (jvl->nitems == 1 && jvl->next == NULL);
    3857              : #endif
    3858              : }
    3859              : 
    3860              : static bool
    3861         2732 : JsonValueListHasMultipleItems(const JsonValueList *jvl)
    3862              : {
    3863              : #if BASE_JVL_ITEMS > 1
    3864              :     /* We need not examine extra chunks in this case */
    3865         2732 :     return (jvl->nitems > 1);
    3866              : #else
    3867              :     return (jvl->nitems == 1 && jvl->next != NULL);
    3868              : #endif
    3869              : }
    3870              : 
    3871              : static JsonbValue *
    3872        71706 : JsonValueListHead(JsonValueList *jvl)
    3873              : {
    3874              :     Assert(jvl->nitems > 0);
    3875        71706 :     return &jvl->items[0];
    3876              : }
    3877              : 
    3878              : /*
    3879              :  * JsonValueListIterator functions
    3880              :  */
    3881              : 
    3882              : static void
    3883       140834 : JsonValueListInitIterator(JsonValueList *jvl, JsonValueListIterator *it)
    3884              : {
    3885       140834 :     it->chunk = jvl;
    3886       140834 :     it->nextitem = 0;
    3887       140834 : }
    3888              : 
    3889              : /*
    3890              :  * Get the next item from the sequence advancing iterator.
    3891              :  * Returns NULL if no more items.
    3892              :  */
    3893              : static JsonbValue *
    3894       222845 : JsonValueListNext(JsonValueListIterator *it)
    3895              : {
    3896       222845 :     if (it->chunk == NULL)
    3897          300 :         return NULL;
    3898       222545 :     if (it->nextitem >= it->chunk->nitems)
    3899              :     {
    3900       131891 :         it->chunk = it->chunk->next;
    3901       131891 :         if (it->chunk == NULL)
    3902       131122 :             return NULL;
    3903          769 :         it->nextitem = 0;
    3904              :         Assert(it->chunk->nitems > 0);
    3905              :     }
    3906        91423 :     return &it->chunk->items[it->nextitem++];
    3907              : }
    3908              : 
    3909              : /*
    3910              :  * Initialize a binary JsonbValue with the given jsonb container.
    3911              :  */
    3912              : static JsonbValue *
    3913       132521 : JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
    3914              : {
    3915       132521 :     jbv->type = jbvBinary;
    3916       132521 :     jbv->val.binary.data = &jb->root;
    3917       132521 :     jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
    3918              : 
    3919       132521 :     return jbv;
    3920              : }
    3921              : 
    3922              : /*
    3923              :  * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
    3924              :  */
    3925              : static int
    3926       191169 : JsonbType(JsonbValue *jb)
    3927              : {
    3928       191169 :     int         type = jb->type;
    3929              : 
    3930       191169 :     if (jb->type == jbvBinary)
    3931              :     {
    3932       122443 :         JsonbContainer *jbc = jb->val.binary.data;
    3933              : 
    3934              :         /* Scalars should be always extracted during jsonpath execution. */
    3935              :         Assert(!JsonContainerIsScalar(jbc));
    3936              : 
    3937       122443 :         if (JsonContainerIsObject(jbc))
    3938       119886 :             type = jbvObject;
    3939         2557 :         else if (JsonContainerIsArray(jbc))
    3940         2557 :             type = jbvArray;
    3941              :         else
    3942            0 :             elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
    3943              :     }
    3944              : 
    3945       191169 :     return type;
    3946              : }
    3947              : 
    3948              : /* Get scalar of given type or NULL on type mismatch */
    3949              : static JsonbValue *
    3950         8051 : getScalar(JsonbValue *scalar, enum jbvType type)
    3951              : {
    3952              :     /* Scalars should be always extracted during jsonpath execution. */
    3953              :     Assert(scalar->type != jbvBinary ||
    3954              :            !JsonContainerIsScalar(scalar->val.binary.data));
    3955              : 
    3956         8051 :     return scalar->type == type ? scalar : NULL;
    3957              : }
    3958              : 
    3959              : /* Construct a JSON array from the item list */
    3960              : static JsonbValue *
    3961          330 : wrapItemsInArray(JsonValueList *items)
    3962              : {
    3963          330 :     JsonbInState ps = {0};
    3964              :     JsonValueListIterator it;
    3965              :     JsonbValue *jbv;
    3966              : 
    3967          330 :     pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
    3968              : 
    3969          330 :     JsonValueListInitIterator(items, &it);
    3970          910 :     while ((jbv = JsonValueListNext(&it)))
    3971          580 :         pushJsonbValue(&ps, WJB_ELEM, jbv);
    3972              : 
    3973          330 :     pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
    3974              : 
    3975          330 :     return ps.result;
    3976              : }
    3977              : 
    3978              : /* Check if the timezone required for casting from type1 to type2 is used */
    3979              : static void
    3980          900 : checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
    3981              : {
    3982          900 :     if (!useTz)
    3983          192 :         ereport(ERROR,
    3984              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3985              :                  errmsg("cannot convert value from %s to %s without time zone usage",
    3986              :                         type1, type2),
    3987              :                  errhint("Use *_tz() function for time zone support.")));
    3988          708 : }
    3989              : 
    3990              : /* Convert time datum to timetz datum */
    3991              : static Datum
    3992          168 : castTimeToTimeTz(Datum time, bool useTz)
    3993              : {
    3994          168 :     checkTimezoneIsUsedForCast(useTz, "time", "timetz");
    3995              : 
    3996          144 :     return DirectFunctionCall1(time_timetz, time);
    3997              : }
    3998              : 
    3999              : /*
    4000              :  * Compare date to timestamp.
    4001              :  * Note that this doesn't involve any timezone considerations.
    4002              :  */
    4003              : static int
    4004          124 : cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
    4005              : {
    4006          124 :     return date_cmp_timestamp_internal(date1, ts2);
    4007              : }
    4008              : 
    4009              : /*
    4010              :  * Compare date to timestamptz.
    4011              :  */
    4012              : static int
    4013          108 : cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
    4014              : {
    4015          108 :     checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
    4016              : 
    4017           96 :     return date_cmp_timestamptz_internal(date1, tstz2);
    4018              : }
    4019              : 
    4020              : /*
    4021              :  * Compare timestamp to timestamptz.
    4022              :  */
    4023              : static int
    4024          168 : cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
    4025              : {
    4026          168 :     checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
    4027              : 
    4028          144 :     return timestamp_cmp_timestamptz_internal(ts1, tstz2);
    4029              : }
    4030              : 
    4031              : /*
    4032              :  * Cross-type comparison of two datetime SQL/JSON items.  If items are
    4033              :  * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
    4034              :  * If the cast requires timezone and it is not used, then explicit error is thrown.
    4035              :  */
    4036              : static int
    4037         2416 : compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
    4038              :                 bool useTz, bool *cast_error)
    4039              : {
    4040              :     PGFunction  cmpfunc;
    4041              : 
    4042         2416 :     *cast_error = false;
    4043              : 
    4044         2416 :     switch (typid1)
    4045              :     {
    4046          376 :         case DATEOID:
    4047          376 :             switch (typid2)
    4048              :             {
    4049          252 :                 case DATEOID:
    4050          252 :                     cmpfunc = date_cmp;
    4051              : 
    4052          252 :                     break;
    4053              : 
    4054           52 :                 case TIMESTAMPOID:
    4055           52 :                     return cmpDateToTimestamp(DatumGetDateADT(val1),
    4056              :                                               DatumGetTimestamp(val2),
    4057              :                                               useTz);
    4058              : 
    4059           48 :                 case TIMESTAMPTZOID:
    4060           48 :                     return cmpDateToTimestampTz(DatumGetDateADT(val1),
    4061              :                                                 DatumGetTimestampTz(val2),
    4062              :                                                 useTz);
    4063              : 
    4064           24 :                 case TIMEOID:
    4065              :                 case TIMETZOID:
    4066           24 :                     *cast_error = true; /* uncomparable types */
    4067           24 :                     return 0;
    4068              : 
    4069            0 :                 default:
    4070            0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    4071              :                          typid2);
    4072              :             }
    4073          252 :             break;
    4074              : 
    4075          416 :         case TIMEOID:
    4076          416 :             switch (typid2)
    4077              :             {
    4078          284 :                 case TIMEOID:
    4079          284 :                     cmpfunc = time_cmp;
    4080              : 
    4081          284 :                     break;
    4082              : 
    4083           84 :                 case TIMETZOID:
    4084           84 :                     val1 = castTimeToTimeTz(val1, useTz);
    4085           72 :                     cmpfunc = timetz_cmp;
    4086              : 
    4087           72 :                     break;
    4088              : 
    4089           48 :                 case DATEOID:
    4090              :                 case TIMESTAMPOID:
    4091              :                 case TIMESTAMPTZOID:
    4092           48 :                     *cast_error = true; /* uncomparable types */
    4093           48 :                     return 0;
    4094              : 
    4095            0 :                 default:
    4096            0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    4097              :                          typid2);
    4098              :             }
    4099          356 :             break;
    4100              : 
    4101          536 :         case TIMETZOID:
    4102          536 :             switch (typid2)
    4103              :             {
    4104           84 :                 case TIMEOID:
    4105           84 :                     val2 = castTimeToTimeTz(val2, useTz);
    4106           72 :                     cmpfunc = timetz_cmp;
    4107              : 
    4108           72 :                     break;
    4109              : 
    4110          404 :                 case TIMETZOID:
    4111          404 :                     cmpfunc = timetz_cmp;
    4112              : 
    4113          404 :                     break;
    4114              : 
    4115           48 :                 case DATEOID:
    4116              :                 case TIMESTAMPOID:
    4117              :                 case TIMESTAMPTZOID:
    4118           48 :                     *cast_error = true; /* uncomparable types */
    4119           48 :                     return 0;
    4120              : 
    4121            0 :                 default:
    4122            0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    4123              :                          typid2);
    4124              :             }
    4125          476 :             break;
    4126              : 
    4127          476 :         case TIMESTAMPOID:
    4128          476 :             switch (typid2)
    4129              :             {
    4130           72 :                 case DATEOID:
    4131           72 :                     return -cmpDateToTimestamp(DatumGetDateADT(val2),
    4132              :                                                DatumGetTimestamp(val1),
    4133              :                                                useTz);
    4134              : 
    4135          284 :                 case TIMESTAMPOID:
    4136          284 :                     cmpfunc = timestamp_cmp;
    4137              : 
    4138          284 :                     break;
    4139              : 
    4140           84 :                 case TIMESTAMPTZOID:
    4141           84 :                     return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
    4142              :                                                      DatumGetTimestampTz(val2),
    4143              :                                                      useTz);
    4144              : 
    4145           36 :                 case TIMEOID:
    4146              :                 case TIMETZOID:
    4147           36 :                     *cast_error = true; /* uncomparable types */
    4148           36 :                     return 0;
    4149              : 
    4150            0 :                 default:
    4151            0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    4152              :                          typid2);
    4153              :             }
    4154          284 :             break;
    4155              : 
    4156          612 :         case TIMESTAMPTZOID:
    4157          612 :             switch (typid2)
    4158              :             {
    4159           60 :                 case DATEOID:
    4160           60 :                     return -cmpDateToTimestampTz(DatumGetDateADT(val2),
    4161              :                                                  DatumGetTimestampTz(val1),
    4162              :                                                  useTz);
    4163              : 
    4164           84 :                 case TIMESTAMPOID:
    4165           84 :                     return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
    4166              :                                                       DatumGetTimestampTz(val1),
    4167              :                                                       useTz);
    4168              : 
    4169          420 :                 case TIMESTAMPTZOID:
    4170          420 :                     cmpfunc = timestamp_cmp;
    4171              : 
    4172          420 :                     break;
    4173              : 
    4174           48 :                 case TIMEOID:
    4175              :                 case TIMETZOID:
    4176           48 :                     *cast_error = true; /* uncomparable types */
    4177           48 :                     return 0;
    4178              : 
    4179            0 :                 default:
    4180            0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    4181              :                          typid2);
    4182              :             }
    4183          420 :             break;
    4184              : 
    4185            0 :         default:
    4186            0 :             elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
    4187              :     }
    4188              : 
    4189         1788 :     if (*cast_error)
    4190            0 :         return 0;               /* cast error */
    4191              : 
    4192         1788 :     return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
    4193              : }
    4194              : 
    4195              : /*
    4196              :  * Executor-callable JSON_EXISTS implementation
    4197              :  *
    4198              :  * Returns NULL instead of throwing errors if 'error' is not NULL, setting
    4199              :  * *error to true.
    4200              :  */
    4201              : bool
    4202          388 : JsonPathExists(Datum jb, JsonPath *jp, bool *error, List *vars)
    4203              : {
    4204              :     JsonPathExecResult res;
    4205              : 
    4206          388 :     res = executeJsonPath(jp, vars,
    4207              :                           GetJsonPathVar, CountJsonPathVars,
    4208              :                           DatumGetJsonbP(jb), !error, NULL, true);
    4209              : 
    4210              :     Assert(error || !jperIsError(res));
    4211              : 
    4212          384 :     if (error && jperIsError(res))
    4213          104 :         *error = true;
    4214              : 
    4215          384 :     return res == jperOk;
    4216              : }
    4217              : 
    4218              : /*
    4219              :  * Executor-callable JSON_QUERY implementation
    4220              :  *
    4221              :  * Returns NULL instead of throwing errors if 'error' is not NULL, setting
    4222              :  * *error to true.  *empty is set to true if no match is found.
    4223              :  */
    4224              : Datum
    4225         1640 : JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
    4226              :               bool *error, List *vars,
    4227              :               const char *column_name)
    4228              : {
    4229              :     bool        wrap;
    4230              :     JsonValueList found;
    4231              :     JsonPathExecResult res;
    4232              : 
    4233         1640 :     JsonValueListInit(&found);
    4234              : 
    4235         1640 :     res = executeJsonPath(jp, vars,
    4236              :                           GetJsonPathVar, CountJsonPathVars,
    4237              :                           DatumGetJsonbP(jb), !error, &found, true);
    4238              :     Assert(error || !jperIsError(res));
    4239         1628 :     if (error && jperIsError(res))
    4240              :     {
    4241           20 :         *error = true;
    4242           20 :         *empty = false;
    4243           20 :         return (Datum) 0;
    4244              :     }
    4245              : 
    4246              :     /*
    4247              :      * Determine whether to wrap the result in a JSON array or not.
    4248              :      *
    4249              :      * If the returned JsonValueList is empty, no wrapping is necessary.
    4250              :      *
    4251              :      * If the wrapper mode is JSW_NONE or JSW_UNSPEC, wrapping is explicitly
    4252              :      * disabled. This enforces a WITHOUT WRAPPER clause, which is also the
    4253              :      * default when no WRAPPER clause is specified.
    4254              :      *
    4255              :      * If the mode is JSW_UNCONDITIONAL, wrapping is enforced regardless of
    4256              :      * the number of SQL/JSON items, enforcing a WITH WRAPPER or WITH
    4257              :      * UNCONDITIONAL WRAPPER clause.
    4258              :      *
    4259              :      * For JSW_CONDITIONAL, wrapping occurs only if there is more than one
    4260              :      * SQL/JSON item in the list, enforcing a WITH CONDITIONAL WRAPPER clause.
    4261              :      */
    4262         1608 :     if (JsonValueListIsEmpty(&found))
    4263          140 :         wrap = false;
    4264         1468 :     else if (wrapper == JSW_NONE || wrapper == JSW_UNSPEC)
    4265         1140 :         wrap = false;
    4266          328 :     else if (wrapper == JSW_UNCONDITIONAL)
    4267          212 :         wrap = true;
    4268          116 :     else if (wrapper == JSW_CONDITIONAL)
    4269          116 :         wrap = JsonValueListHasMultipleItems(&found);
    4270              :     else
    4271              :     {
    4272            0 :         elog(ERROR, "unrecognized json wrapper %d", (int) wrapper);
    4273              :         wrap = false;
    4274              :     }
    4275              : 
    4276         1608 :     if (wrap)
    4277          252 :         return JsonbPGetDatum(JsonbValueToJsonb(wrapItemsInArray(&found)));
    4278              : 
    4279              :     /* No wrapping means at most one item is expected. */
    4280         1356 :     if (JsonValueListHasMultipleItems(&found))
    4281              :     {
    4282           40 :         if (error)
    4283              :         {
    4284           32 :             *error = true;
    4285           32 :             return (Datum) 0;
    4286              :         }
    4287              : 
    4288            8 :         if (column_name)
    4289            4 :             ereport(ERROR,
    4290              :                     (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
    4291              :                      errmsg("JSON path expression for column \"%s\" must return single item when no wrapper is requested",
    4292              :                             column_name),
    4293              :                      errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
    4294              :         else
    4295            4 :             ereport(ERROR,
    4296              :                     (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
    4297              :                      errmsg("JSON path expression in JSON_QUERY must return single item when no wrapper is requested"),
    4298              :                      errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
    4299              :     }
    4300              : 
    4301         1316 :     if (!JsonValueListIsEmpty(&found))
    4302         1176 :         return JsonbPGetDatum(JsonbValueToJsonb(JsonValueListHead(&found)));
    4303              : 
    4304          140 :     *empty = true;
    4305          140 :     return PointerGetDatum(NULL);
    4306              : }
    4307              : 
    4308              : /*
    4309              :  * Executor-callable JSON_VALUE implementation
    4310              :  *
    4311              :  * Returns NULL instead of throwing errors if 'error' is not NULL, setting
    4312              :  * *error to true.  *empty is set to true if no match is found.
    4313              :  */
    4314              : JsonbValue *
    4315         1508 : JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars,
    4316              :               const char *column_name)
    4317              : {
    4318              :     JsonbValue *res;
    4319              :     JsonValueList found;
    4320              :     JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY;
    4321              : 
    4322         1508 :     JsonValueListInit(&found);
    4323              : 
    4324         1508 :     jper = executeJsonPath(jp, vars, GetJsonPathVar, CountJsonPathVars,
    4325              :                            DatumGetJsonbP(jb),
    4326              :                            !error, &found, true);
    4327              : 
    4328              :     Assert(error || !jperIsError(jper));
    4329              : 
    4330         1500 :     if (error && jperIsError(jper))
    4331              :     {
    4332           12 :         *error = true;
    4333           12 :         *empty = false;
    4334           12 :         return NULL;
    4335              :     }
    4336              : 
    4337         1488 :     *empty = JsonValueListIsEmpty(&found);
    4338              : 
    4339         1488 :     if (*empty)
    4340          228 :         return NULL;
    4341              : 
    4342              :     /* JSON_VALUE expects to get only singletons. */
    4343         1260 :     if (JsonValueListHasMultipleItems(&found))
    4344              :     {
    4345           12 :         if (error)
    4346              :         {
    4347            8 :             *error = true;
    4348            8 :             return NULL;
    4349              :         }
    4350              : 
    4351            4 :         if (column_name)
    4352            0 :             ereport(ERROR,
    4353              :                     (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
    4354              :                      errmsg("JSON path expression for column \"%s\" must return single scalar item",
    4355              :                             column_name)));
    4356              :         else
    4357            4 :             ereport(ERROR,
    4358              :                     (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
    4359              :                      errmsg("JSON path expression in JSON_VALUE must return single scalar item")));
    4360              :     }
    4361              : 
    4362         1248 :     res = copyJsonbValue(JsonValueListHead(&found));
    4363         1248 :     if (res->type == jbvBinary && JsonContainerIsScalar(res->val.binary.data))
    4364            0 :         JsonbExtractScalar(res->val.binary.data, res);
    4365              : 
    4366              :     /* JSON_VALUE expects to get only scalars. */
    4367         1248 :     if (!IsAJsonbScalar(res))
    4368              :     {
    4369           64 :         if (error)
    4370              :         {
    4371           56 :             *error = true;
    4372           56 :             return NULL;
    4373              :         }
    4374              : 
    4375            8 :         if (column_name)
    4376            0 :             ereport(ERROR,
    4377              :                     (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
    4378              :                      errmsg("JSON path expression for column \"%s\" must return single scalar item",
    4379              :                             column_name)));
    4380              :         else
    4381            8 :             ereport(ERROR,
    4382              :                     (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
    4383              :                      errmsg("JSON path expression in JSON_VALUE must return single scalar item")));
    4384              :     }
    4385              : 
    4386         1184 :     if (res->type == jbvNull)
    4387           48 :         return NULL;
    4388              : 
    4389         1136 :     return res;
    4390              : }
    4391              : 
    4392              : /************************ JSON_TABLE functions ***************************/
    4393              : 
    4394              : /*
    4395              :  * Sanity-checks and returns the opaque JsonTableExecContext from the
    4396              :  * given executor state struct.
    4397              :  */
    4398              : static inline JsonTableExecContext *
    4399         4860 : GetJsonTableExecContext(TableFuncScanState *state, const char *fname)
    4400              : {
    4401              :     JsonTableExecContext *result;
    4402              : 
    4403         4860 :     if (!IsA(state, TableFuncScanState))
    4404            0 :         elog(ERROR, "%s called with invalid TableFuncScanState", fname);
    4405         4860 :     result = (JsonTableExecContext *) state->opaque;
    4406         4860 :     if (result->magic != JSON_TABLE_EXEC_CONTEXT_MAGIC)
    4407            0 :         elog(ERROR, "%s called with invalid TableFuncScanState", fname);
    4408              : 
    4409         4860 :     return result;
    4410              : }
    4411              : 
    4412              : /*
    4413              :  * JsonTableInitOpaque
    4414              :  *      Fill in TableFuncScanState->opaque for processing JSON_TABLE
    4415              :  *
    4416              :  * This initializes the PASSING arguments and the JsonTablePlanState for
    4417              :  * JsonTablePlan given in TableFunc.
    4418              :  */
    4419              : static void
    4420          352 : JsonTableInitOpaque(TableFuncScanState *state, int natts)
    4421              : {
    4422              :     JsonTableExecContext *cxt;
    4423          352 :     PlanState  *ps = &state->ss.ps;
    4424          352 :     TableFuncScan *tfs = castNode(TableFuncScan, ps->plan);
    4425          352 :     TableFunc  *tf = tfs->tablefunc;
    4426          352 :     JsonTablePlan *rootplan = (JsonTablePlan *) tf->plan;
    4427          352 :     JsonExpr   *je = castNode(JsonExpr, tf->docexpr);
    4428          352 :     List       *args = NIL;
    4429              : 
    4430          352 :     cxt = palloc0_object(JsonTableExecContext);
    4431          352 :     cxt->magic = JSON_TABLE_EXEC_CONTEXT_MAGIC;
    4432              : 
    4433              :     /*
    4434              :      * Evaluate JSON_TABLE() PASSING arguments to be passed to the jsonpath
    4435              :      * executor via JsonPathVariables.
    4436              :      */
    4437          352 :     if (state->passingvalexprs)
    4438              :     {
    4439              :         ListCell   *exprlc;
    4440              :         ListCell   *namelc;
    4441              : 
    4442              :         Assert(list_length(state->passingvalexprs) ==
    4443              :                list_length(je->passing_names));
    4444          248 :         forboth(exprlc, state->passingvalexprs,
    4445              :                 namelc, je->passing_names)
    4446              :         {
    4447          164 :             ExprState  *state = lfirst_node(ExprState, exprlc);
    4448          164 :             String     *name = lfirst_node(String, namelc);
    4449          164 :             JsonPathVariable *var = palloc_object(JsonPathVariable);
    4450              : 
    4451          164 :             var->name = pstrdup(name->sval);
    4452          164 :             var->namelen = strlen(var->name);
    4453          164 :             var->typid = exprType((Node *) state->expr);
    4454          164 :             var->typmod = exprTypmod((Node *) state->expr);
    4455              : 
    4456              :             /*
    4457              :              * Evaluate the expression and save the value to be returned by
    4458              :              * GetJsonPathVar().
    4459              :              */
    4460          164 :             var->value = ExecEvalExpr(state, ps->ps_ExprContext,
    4461              :                                       &var->isnull);
    4462              : 
    4463          164 :             args = lappend(args, var);
    4464              :         }
    4465              :     }
    4466              : 
    4467          352 :     cxt->colplanstates = palloc_array(JsonTablePlanState *, list_length(tf->colvalexprs));
    4468              : 
    4469              :     /*
    4470              :      * Initialize plan for the root path and, recursively, also any child
    4471              :      * plans that compute the NESTED paths.
    4472              :      */
    4473          352 :     cxt->rootplanstate = JsonTableInitPlan(cxt, rootplan, NULL, args,
    4474              :                                            CurrentMemoryContext);
    4475              : 
    4476          352 :     state->opaque = cxt;
    4477          352 : }
    4478              : 
    4479              : /*
    4480              :  * JsonTableDestroyOpaque
    4481              :  *      Resets state->opaque
    4482              :  */
    4483              : static void
    4484          352 : JsonTableDestroyOpaque(TableFuncScanState *state)
    4485              : {
    4486              :     JsonTableExecContext *cxt =
    4487          352 :         GetJsonTableExecContext(state, "JsonTableDestroyOpaque");
    4488              : 
    4489              :     /* not valid anymore */
    4490          352 :     cxt->magic = 0;
    4491              : 
    4492          352 :     state->opaque = NULL;
    4493          352 : }
    4494              : 
    4495              : /*
    4496              :  * JsonTableInitPlan
    4497              :  *      Initialize information for evaluating jsonpath in the given
    4498              :  *      JsonTablePlan and, recursively, in any child plans
    4499              :  */
    4500              : static JsonTablePlanState *
    4501          688 : JsonTableInitPlan(JsonTableExecContext *cxt, JsonTablePlan *plan,
    4502              :                   JsonTablePlanState *parentstate,
    4503              :                   List *args, MemoryContext mcxt)
    4504              : {
    4505          688 :     JsonTablePlanState *planstate = palloc0_object(JsonTablePlanState);
    4506              : 
    4507          688 :     planstate->plan = plan;
    4508          688 :     planstate->parent = parentstate;
    4509          688 :     JsonValueListInit(&planstate->found);
    4510              : 
    4511          688 :     if (IsA(plan, JsonTablePathScan))
    4512              :     {
    4513          612 :         JsonTablePathScan *scan = (JsonTablePathScan *) plan;
    4514              :         int         i;
    4515              : 
    4516          612 :         planstate->path = DatumGetJsonPathP(scan->path->value->constvalue);
    4517          612 :         planstate->args = args;
    4518          612 :         planstate->mcxt = AllocSetContextCreate(mcxt, "JsonTableExecContext",
    4519              :                                                 ALLOCSET_DEFAULT_SIZES);
    4520              : 
    4521              :         /* No row pattern evaluated yet. */
    4522          612 :         planstate->current.value = PointerGetDatum(NULL);
    4523          612 :         planstate->current.isnull = true;
    4524              : 
    4525         1556 :         for (i = scan->colMin; i >= 0 && i <= scan->colMax; i++)
    4526          944 :             cxt->colplanstates[i] = planstate;
    4527              : 
    4528          612 :         planstate->nested = scan->child ?
    4529          612 :             JsonTableInitPlan(cxt, scan->child, planstate, args, mcxt) : NULL;
    4530              :     }
    4531           76 :     else if (IsA(plan, JsonTableSiblingJoin))
    4532              :     {
    4533           76 :         JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;
    4534              : 
    4535           76 :         planstate->left = JsonTableInitPlan(cxt, join->lplan, parentstate,
    4536              :                                             args, mcxt);
    4537           76 :         planstate->right = JsonTableInitPlan(cxt, join->rplan, parentstate,
    4538              :                                              args, mcxt);
    4539              :     }
    4540              : 
    4541          688 :     return planstate;
    4542              : }
    4543              : 
    4544              : /*
    4545              :  * JsonTableSetDocument
    4546              :  *      Install the input document and evaluate the row pattern
    4547              :  */
    4548              : static void
    4549          348 : JsonTableSetDocument(TableFuncScanState *state, Datum value)
    4550              : {
    4551              :     JsonTableExecContext *cxt =
    4552          348 :         GetJsonTableExecContext(state, "JsonTableSetDocument");
    4553              : 
    4554          348 :     JsonTableResetRowPattern(cxt->rootplanstate, value);
    4555          344 : }
    4556              : 
    4557              : /*
    4558              :  * Evaluate a JsonTablePlan's jsonpath to get a new row pattern from
    4559              :  * the given context item
    4560              :  */
    4561              : static void
    4562          668 : JsonTableResetRowPattern(JsonTablePlanState *planstate, Datum item)
    4563              : {
    4564          668 :     JsonTablePathScan *scan = castNode(JsonTablePathScan, planstate->plan);
    4565              :     MemoryContext oldcxt;
    4566              :     JsonPathExecResult res;
    4567          668 :     Jsonb      *js = (Jsonb *) DatumGetJsonbP(item);
    4568              : 
    4569          668 :     JsonValueListClear(&planstate->found);
    4570              : 
    4571          668 :     MemoryContextResetOnly(planstate->mcxt);
    4572              : 
    4573          668 :     oldcxt = MemoryContextSwitchTo(planstate->mcxt);
    4574              : 
    4575          668 :     res = executeJsonPath(planstate->path, planstate->args,
    4576              :                           GetJsonPathVar, CountJsonPathVars,
    4577          668 :                           js, scan->errorOnError,
    4578              :                           &planstate->found,
    4579              :                           true);
    4580              : 
    4581          664 :     MemoryContextSwitchTo(oldcxt);
    4582              : 
    4583          664 :     if (jperIsError(res))
    4584              :     {
    4585              :         Assert(!scan->errorOnError);
    4586           12 :         JsonValueListClear(&planstate->found);
    4587              :     }
    4588              : 
    4589              :     /* Reset plan iterator to the beginning of the item list */
    4590          664 :     JsonValueListInitIterator(&planstate->found, &planstate->iter);
    4591          664 :     planstate->current.value = PointerGetDatum(NULL);
    4592          664 :     planstate->current.isnull = true;
    4593          664 :     planstate->ordinal = 0;
    4594          664 : }
    4595              : 
    4596              : /*
    4597              :  * Fetch next row from a JsonTablePlan.
    4598              :  *
    4599              :  * Returns false if the plan has run out of rows, true otherwise.
    4600              :  */
    4601              : static bool
    4602         2800 : JsonTablePlanNextRow(JsonTablePlanState *planstate)
    4603              : {
    4604         2800 :     if (IsA(planstate->plan, JsonTablePathScan))
    4605         2188 :         return JsonTablePlanScanNextRow(planstate);
    4606          612 :     else if (IsA(planstate->plan, JsonTableSiblingJoin))
    4607          612 :         return JsonTablePlanJoinNextRow(planstate);
    4608              :     else
    4609            0 :         elog(ERROR, "invalid JsonTablePlan %d", (int) planstate->plan->type);
    4610              : 
    4611              :     Assert(false);
    4612              :     /* Appease compiler */
    4613              :     return false;
    4614              : }
    4615              : 
    4616              : /*
    4617              :  * Fetch next row from a JsonTablePlan's path evaluation result and from
    4618              :  * any child nested path(s).
    4619              :  *
    4620              :  * Returns true if any of the paths (this or the nested) has more rows to
    4621              :  * return.
    4622              :  *
    4623              :  * By fetching the nested path(s)'s rows based on the parent row at each
    4624              :  * level, this essentially joins the rows of different levels.  If a nested
    4625              :  * path at a given level has no matching rows, the columns of that level will
    4626              :  * compute to NULL, making it an OUTER join.
    4627              :  */
    4628              : static bool
    4629         2188 : JsonTablePlanScanNextRow(JsonTablePlanState *planstate)
    4630              : {
    4631              :     JsonbValue *jbv;
    4632              :     MemoryContext oldcxt;
    4633              : 
    4634              :     /*
    4635              :      * If planstate already has an active row and there is a nested plan,
    4636              :      * check if it has an active row to join with the former.
    4637              :      */
    4638         2188 :     if (!planstate->current.isnull)
    4639              :     {
    4640         1224 :         if (planstate->nested && JsonTablePlanNextRow(planstate->nested))
    4641          324 :             return true;
    4642              :     }
    4643              : 
    4644              :     /* Fetch new row from the list of found values to set as active. */
    4645         1864 :     jbv = JsonValueListNext(&planstate->iter);
    4646              : 
    4647              :     /* End of list? */
    4648         1864 :     if (jbv == NULL)
    4649              :     {
    4650          904 :         planstate->current.value = PointerGetDatum(NULL);
    4651          904 :         planstate->current.isnull = true;
    4652          904 :         return false;
    4653              :     }
    4654              : 
    4655              :     /*
    4656              :      * Set current row item for subsequent JsonTableGetValue() calls for
    4657              :      * evaluating individual columns.
    4658              :      */
    4659          960 :     oldcxt = MemoryContextSwitchTo(planstate->mcxt);
    4660          960 :     planstate->current.value = JsonbPGetDatum(JsonbValueToJsonb(jbv));
    4661          960 :     planstate->current.isnull = false;
    4662          960 :     MemoryContextSwitchTo(oldcxt);
    4663              : 
    4664              :     /* Next row! */
    4665          960 :     planstate->ordinal++;
    4666              : 
    4667              :     /* Process nested plan(s), if any. */
    4668          960 :     if (planstate->nested)
    4669              :     {
    4670              :         /* Re-evaluate the nested path using the above parent row. */
    4671          232 :         JsonTableResetNestedPlan(planstate->nested);
    4672              : 
    4673              :         /*
    4674              :          * Now fetch the nested plan's current row to be joined against the
    4675              :          * parent row.  Any further nested plans' paths will be re-evaluated
    4676              :          * recursively, level at a time, after setting each nested plan's
    4677              :          * current row.
    4678              :          */
    4679          232 :         (void) JsonTablePlanNextRow(planstate->nested);
    4680              :     }
    4681              : 
    4682              :     /* There are more rows. */
    4683          960 :     return true;
    4684              : }
    4685              : 
    4686              : /*
    4687              :  * Re-evaluate the row pattern of a nested plan using the new parent row
    4688              :  * pattern.
    4689              :  */
    4690              : static void
    4691          408 : JsonTableResetNestedPlan(JsonTablePlanState *planstate)
    4692              : {
    4693              :     /* This better be a child plan. */
    4694              :     Assert(planstate->parent != NULL);
    4695          408 :     if (IsA(planstate->plan, JsonTablePathScan))
    4696              :     {
    4697          320 :         JsonTablePlanState *parent = planstate->parent;
    4698              : 
    4699          320 :         if (!parent->current.isnull)
    4700          320 :             JsonTableResetRowPattern(planstate, parent->current.value);
    4701              : 
    4702              :         /*
    4703              :          * If this plan itself has a child nested plan, it will be reset when
    4704              :          * the caller calls JsonTablePlanNextRow() on this plan.
    4705              :          */
    4706              :     }
    4707           88 :     else if (IsA(planstate->plan, JsonTableSiblingJoin))
    4708              :     {
    4709           88 :         JsonTableResetNestedPlan(planstate->left);
    4710           88 :         JsonTableResetNestedPlan(planstate->right);
    4711              :     }
    4712          408 : }
    4713              : 
    4714              : /*
    4715              :  * Fetch the next row from a JsonTableSiblingJoin.
    4716              :  *
    4717              :  * This is essentially a UNION between the rows from left and right siblings.
    4718              :  */
    4719              : static bool
    4720          612 : JsonTablePlanJoinNextRow(JsonTablePlanState *planstate)
    4721              : {
    4722              : 
    4723              :     /* Fetch row from left sibling. */
    4724          612 :     if (!JsonTablePlanNextRow(planstate->left))
    4725              :     {
    4726              :         /*
    4727              :          * Left sibling ran out of rows, so start fetching from the right
    4728              :          * sibling.
    4729              :          */
    4730          364 :         if (!JsonTablePlanNextRow(planstate->right))
    4731              :         {
    4732              :             /* Right sibling ran out of row, so there are more rows. */
    4733          216 :             return false;
    4734              :         }
    4735              :     }
    4736              : 
    4737          396 :     return true;
    4738              : }
    4739              : 
    4740              : /*
    4741              :  * JsonTableFetchRow
    4742              :  *      Prepare the next "current" row for upcoming GetValue calls.
    4743              :  *
    4744              :  * Returns false if no more rows can be returned.
    4745              :  */
    4746              : static bool
    4747         1036 : JsonTableFetchRow(TableFuncScanState *state)
    4748              : {
    4749              :     JsonTableExecContext *cxt =
    4750         1036 :         GetJsonTableExecContext(state, "JsonTableFetchRow");
    4751              : 
    4752         1036 :     return JsonTablePlanNextRow(cxt->rootplanstate);
    4753              : }
    4754              : 
    4755              : /*
    4756              :  * JsonTableGetValue
    4757              :  *      Return the value for column number 'colnum' for the current row.
    4758              :  *
    4759              :  * This leaks memory, so be sure to reset often the context in which it's
    4760              :  * called.
    4761              :  */
    4762              : static Datum
    4763         3124 : JsonTableGetValue(TableFuncScanState *state, int colnum,
    4764              :                   Oid typid, int32 typmod, bool *isnull)
    4765              : {
    4766              :     JsonTableExecContext *cxt =
    4767         3124 :         GetJsonTableExecContext(state, "JsonTableGetValue");
    4768         3124 :     ExprContext *econtext = state->ss.ps.ps_ExprContext;
    4769         3124 :     ExprState  *estate = list_nth(state->colvalexprs, colnum);
    4770         3124 :     JsonTablePlanState *planstate = cxt->colplanstates[colnum];
    4771         3124 :     JsonTablePlanRowSource *current = &planstate->current;
    4772              :     Datum       result;
    4773              : 
    4774              :     /* Row pattern value is NULL */
    4775         3124 :     if (current->isnull)
    4776              :     {
    4777          596 :         result = (Datum) 0;
    4778          596 :         *isnull = true;
    4779              :     }
    4780              :     /* Evaluate JsonExpr. */
    4781         2528 :     else if (estate)
    4782              :     {
    4783         2228 :         Datum       saved_caseValue = econtext->caseValue_datum;
    4784         2228 :         bool        saved_caseIsNull = econtext->caseValue_isNull;
    4785              : 
    4786              :         /* Pass the row pattern value via CaseTestExpr. */
    4787         2228 :         econtext->caseValue_datum = current->value;
    4788         2228 :         econtext->caseValue_isNull = false;
    4789              : 
    4790         2228 :         result = ExecEvalExpr(estate, econtext, isnull);
    4791              : 
    4792         2168 :         econtext->caseValue_datum = saved_caseValue;
    4793         2168 :         econtext->caseValue_isNull = saved_caseIsNull;
    4794              :     }
    4795              :     /* ORDINAL column */
    4796              :     else
    4797              :     {
    4798          300 :         result = Int32GetDatum(planstate->ordinal);
    4799          300 :         *isnull = false;
    4800              :     }
    4801              : 
    4802         3064 :     return result;
    4803              : }
        

Generated by: LCOV version 2.0-1