LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_exec.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19beta1 Lines: 91.9 % 1758 1615
Test Date: 2026-06-11 00:16:48 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         5412 : jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
     557              : {
     558              :     FuncCallContext *funcctx;
     559              :     JsonValueListIterator *iter;
     560              :     JsonbValue *v;
     561              : 
     562         5412 :     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         3056 :         funcctx = SRF_FIRSTCALL_INIT();
     572         3056 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     573              : 
     574         3056 :         jb = PG_GETARG_JSONB_P_COPY(0);
     575         3056 :         jp = PG_GETARG_JSONPATH_P_COPY(1);
     576         3056 :         vars = PG_GETARG_JSONB_P_COPY(2);
     577         3056 :         silent = PG_GETARG_BOOL(3);
     578              : 
     579         3056 :         found = palloc_object(JsonValueList);
     580         3056 :         JsonValueListInit(found);
     581              : 
     582         3056 :         (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
     583              :                                countVariablesFromJsonb,
     584         3056 :                                jb, !silent, found, tz);
     585              : 
     586         2020 :         iter = palloc_object(JsonValueListIterator);
     587         2020 :         JsonValueListInitIterator(found, iter);
     588              : 
     589         2020 :         funcctx->user_fctx = iter;
     590              : 
     591         2020 :         MemoryContextSwitchTo(oldcontext);
     592              :     }
     593              : 
     594         4376 :     funcctx = SRF_PERCALL_SETUP();
     595         4376 :     iter = funcctx->user_fctx;
     596              : 
     597         4376 :     v = JsonValueListNext(iter);
     598              : 
     599         4376 :     if (v == NULL)
     600         2020 :         SRF_RETURN_DONE(funcctx);
     601              : 
     602         2356 :     SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
     603              : }
     604              : 
     605              : Datum
     606         4408 : jsonb_path_query(PG_FUNCTION_ARGS)
     607              : {
     608         4408 :     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       133034 : 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       133034 :     jspInit(&jsp, path);
     726              : 
     727       133034 :     if (!JsonbExtractScalar(&json->root, &jbv))
     728       128964 :         JsonbInitBinary(&jbv, json);
     729              : 
     730       133034 :     cxt.vars = vars;
     731       133034 :     cxt.getVar = getVar;
     732       133034 :     cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
     733       133034 :     cxt.ignoreStructuralErrors = cxt.laxMode;
     734       133034 :     cxt.root = &jbv;
     735       133034 :     cxt.current = &jbv;
     736       133034 :     cxt.baseObject.jbc = NULL;
     737       133034 :     cxt.baseObject.id = 0;
     738              :     /* 1 + number of base objects in vars */
     739       133034 :     cxt.lastGeneratedObjectId = 1 + countVars(vars);
     740       133026 :     cxt.innermostArraySize = -1;
     741       133026 :     cxt.throwErrors = throwErrors;
     742       133026 :     cxt.useTz = useTz;
     743              : 
     744       133026 :     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       132861 :     res = executeItem(&cxt, &jsp, &jbv, result);
     767              : 
     768              :     Assert(!throwErrors || !jperIsError(res));
     769              : 
     770       131785 :     return res;
     771              : }
     772              : 
     773              : /*
     774              :  * Execute jsonpath with automatic unwrapping of current item in lax mode.
     775              :  */
     776              : static JsonPathExecResult
     777       398419 : executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
     778              :             JsonbValue *jb, JsonValueList *found)
     779              : {
     780       398419 :     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       404123 : executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
     790              :                            JsonbValue *jb, JsonValueList *found, bool unwrap)
     791              : {
     792              :     JsonPathItem elem;
     793       404123 :     JsonPathExecResult res = jperNotFound;
     794              :     JsonBaseObjectInfo baseObject;
     795              : 
     796       404123 :     check_stack_depth();
     797       404123 :     CHECK_FOR_INTERRUPTS();
     798              : 
     799       404123 :     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       141075 :         case jpiRoot:
    1096       141075 :             jb = cxt->root;
    1097       141075 :             baseObject = setBaseObject(cxt, jb, 0);
    1098       141075 :             res = executeNextItem(cxt, jsp, NULL, jb, found);
    1099       140051 :             cxt->baseObject = baseObject;
    1100       140051 :             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          526 :         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          526 :                 if (unwrap && JsonbType(jb) == jbvArray)
    1695           40 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1696              : 
    1697          486 :                 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       394705 :     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       294240 : executeNextItem(JsonPathExecContext *cxt,
    1733              :                 JsonPathItem *cur, JsonPathItem *next,
    1734              :                 JsonbValue *v, JsonValueList *found)
    1735              : {
    1736              :     JsonPathItem elem;
    1737              :     bool        hasNext;
    1738              : 
    1739       294240 :     if (!cur)
    1740            0 :         hasNext = next != NULL;
    1741       294240 :     else if (next)
    1742       115365 :         hasNext = jspHasNext(cur);
    1743              :     else
    1744              :     {
    1745       178875 :         next = &elem;
    1746       178875 :         hasNext = jspGetNext(cur, next);
    1747              :     }
    1748              : 
    1749       294240 :     if (hasNext)
    1750       131705 :         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          486 : executeStringInternalMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    2906              :                             JsonbValue *jb, JsonValueList *found)
    2907              : {
    2908              :     JsonbValue  jbv;
    2909              :     bool        hasNext;
    2910          486 :     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          486 :     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          486 :     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          286 :     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          286 :     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           24 :         case jpiStrSplitPart:
    3018              :             {
    3019              :                 char       *from_str;
    3020              :                 int32       n;
    3021           24 :                 ErrorSaveContext escontext = {T_ErrorSaveContext};
    3022              : 
    3023           24 :                 jspGetLeftArg(jsp, &elem);
    3024           24 :                 if (elem.type != jpiString)
    3025            0 :                     elog(ERROR, "invalid jsonpath item type for .split_part()");
    3026              : 
    3027           24 :                 from_str = jspGetString(&elem, NULL);
    3028              : 
    3029           24 :                 jspGetRightArg(jsp, &elem);
    3030           24 :                 if (elem.type != jpiNumeric)
    3031            0 :                     elog(ERROR, "invalid jsonpath item type for .split_part()");
    3032              : 
    3033           24 :                 n = numeric_int4_safe(jspGetNumeric(&elem),
    3034              :                                       (Node *) &escontext);
    3035           24 :                 if (escontext.error_occurred)
    3036           12 :                     RETURN_ERROR(ereport(ERROR,
    3037              :                                          errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3038              :                                          errmsg("field position of jsonpath item method .%s() is out of range for type integer",
    3039              :                                                 jspOperationName(jsp->type))));
    3040              : 
    3041           16 :                 if (n == 0)
    3042            8 :                     RETURN_ERROR(ereport(ERROR,
    3043              :                                          errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3044              :                                          errmsg("field position of jsonpath item method .%s() must not be zero",
    3045              :                                                 jspOperationName(jsp->type))));
    3046              : 
    3047            8 :                 resStr = TextDatumGetCString(DirectFunctionCall3Coll(split_part,
    3048              :                                                                      DEFAULT_COLLATION_OID,
    3049              :                                                                      str,
    3050              :                                                                      CStringGetTextDatum(from_str),
    3051              :                                                                      Int32GetDatum(n)));
    3052            8 :                 break;
    3053              :             }
    3054            0 :         default:
    3055            0 :             elog(ERROR, "unsupported jsonpath item type: %d", jsp->type);
    3056              :     }
    3057              : 
    3058          270 :     if (resStr)
    3059          270 :         res = jperOk;
    3060              : 
    3061          270 :     hasNext = jspGetNext(jsp, &elem);
    3062              : 
    3063          270 :     if (!hasNext && !found)
    3064            0 :         return res;
    3065              : 
    3066          270 :     jbv.type = jbvString;
    3067          270 :     jbv.val.string.val = resStr;
    3068          270 :     jbv.val.string.len = strlen(resStr);
    3069              : 
    3070          270 :     return executeNextItem(cxt, jsp, &elem, &jbv, found);
    3071              : }
    3072              : 
    3073              : /*
    3074              :  * Implementation of .keyvalue() method.
    3075              :  *
    3076              :  * .keyvalue() method returns a sequence of object's key-value pairs in the
    3077              :  * following format: '{ "key": key, "value": value, "id": id }'.
    3078              :  *
    3079              :  * "id" field is an object identifier which is constructed from the two parts:
    3080              :  * base object id and its binary offset in base object's jsonb:
    3081              :  * id = 10000000000 * base_object_id + obj_offset_in_base_object
    3082              :  *
    3083              :  * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
    3084              :  * (maximal offset in jsonb).  Decimal multiplier is used here to improve the
    3085              :  * readability of identifiers.
    3086              :  *
    3087              :  * Base object is usually a root object of the path: context item '$' or path
    3088              :  * variable '$var', literals can't produce objects for now.  But if the path
    3089              :  * contains generated objects (.keyvalue() itself, for example), then they
    3090              :  * become base object for the subsequent .keyvalue().
    3091              :  *
    3092              :  * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
    3093              :  * of variables (see getJsonPathVariable()).  Ids for generated objects
    3094              :  * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
    3095              :  */
    3096              : static JsonPathExecResult
    3097           58 : executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    3098              :                       JsonbValue *jb, JsonValueList *found)
    3099              : {
    3100           58 :     JsonPathExecResult res = jperNotFound;
    3101              :     JsonPathItem next;
    3102              :     JsonbContainer *jbc;
    3103              :     JsonbValue  key;
    3104              :     JsonbValue  val;
    3105              :     JsonbValue  idval;
    3106              :     JsonbValue  keystr;
    3107              :     JsonbValue  valstr;
    3108              :     JsonbValue  idstr;
    3109              :     JsonbIterator *it;
    3110              :     JsonbIteratorToken tok;
    3111              :     int64       id;
    3112              :     bool        hasNext;
    3113              : 
    3114           58 :     if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
    3115           16 :         RETURN_ERROR(ereport(ERROR,
    3116              :                              (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
    3117              :                               errmsg("jsonpath item method .%s() can only be applied to an object",
    3118              :                                      jspOperationName(jsp->type)))));
    3119              : 
    3120           42 :     jbc = jb->val.binary.data;
    3121              : 
    3122           42 :     if (!JsonContainerSize(jbc))
    3123           12 :         return jperNotFound;    /* no key-value pairs */
    3124              : 
    3125           30 :     hasNext = jspGetNext(jsp, &next);
    3126              : 
    3127           30 :     keystr.type = jbvString;
    3128           30 :     keystr.val.string.val = "key";
    3129           30 :     keystr.val.string.len = 3;
    3130              : 
    3131           30 :     valstr.type = jbvString;
    3132           30 :     valstr.val.string.val = "value";
    3133           30 :     valstr.val.string.len = 5;
    3134              : 
    3135           30 :     idstr.type = jbvString;
    3136           30 :     idstr.val.string.val = "id";
    3137           30 :     idstr.val.string.len = 2;
    3138              : 
    3139              :     /* construct object id from its base object and offset inside that */
    3140           30 :     id = jb->type != jbvBinary ? 0 :
    3141           30 :         (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
    3142           30 :     id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
    3143              : 
    3144           30 :     idval.type = jbvNumeric;
    3145           30 :     idval.val.numeric = int64_to_numeric(id);
    3146              : 
    3147           30 :     it = JsonbIteratorInit(jbc);
    3148              : 
    3149          116 :     while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
    3150              :     {
    3151              :         JsonBaseObjectInfo baseObject;
    3152              :         JsonbValue  obj;
    3153              :         JsonbInState ps;
    3154              :         Jsonb      *jsonb;
    3155              : 
    3156           96 :         if (tok != WJB_KEY)
    3157           50 :             continue;
    3158              : 
    3159           46 :         res = jperOk;
    3160              : 
    3161           46 :         if (!hasNext && !found)
    3162           10 :             break;
    3163              : 
    3164           41 :         tok = JsonbIteratorNext(&it, &val, true);
    3165              :         Assert(tok == WJB_VALUE);
    3166              : 
    3167           41 :         memset(&ps, 0, sizeof(ps));
    3168              : 
    3169           41 :         pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
    3170              : 
    3171           41 :         pushJsonbValue(&ps, WJB_KEY, &keystr);
    3172           41 :         pushJsonbValue(&ps, WJB_VALUE, &key);
    3173              : 
    3174           41 :         pushJsonbValue(&ps, WJB_KEY, &valstr);
    3175           41 :         pushJsonbValue(&ps, WJB_VALUE, &val);
    3176              : 
    3177           41 :         pushJsonbValue(&ps, WJB_KEY, &idstr);
    3178           41 :         pushJsonbValue(&ps, WJB_VALUE, &idval);
    3179              : 
    3180           41 :         pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
    3181              : 
    3182           41 :         jsonb = JsonbValueToJsonb(ps.result);
    3183              : 
    3184           41 :         JsonbInitBinary(&obj, jsonb);
    3185              : 
    3186           41 :         baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
    3187              : 
    3188           41 :         res = executeNextItem(cxt, jsp, &next, &obj, found);
    3189              : 
    3190           41 :         cxt->baseObject = baseObject;
    3191              : 
    3192           41 :         if (jperIsError(res))
    3193            0 :             return res;
    3194              : 
    3195           41 :         if (res == jperOk && !found)
    3196            5 :             break;
    3197              :     }
    3198              : 
    3199           30 :     return res;
    3200              : }
    3201              : 
    3202              : /*
    3203              :  * Convert boolean execution status 'res' to a boolean JSON item and execute
    3204              :  * next jsonpath.
    3205              :  */
    3206              : static JsonPathExecResult
    3207        68139 : appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
    3208              :                  JsonValueList *found, JsonPathBool res)
    3209              : {
    3210              :     JsonPathItem next;
    3211              :     JsonbValue  jbv;
    3212              : 
    3213        68139 :     if (!jspGetNext(jsp, &next) && !found)
    3214           13 :         return jperOk;          /* found singleton boolean value */
    3215              : 
    3216        68126 :     if (res == jpbUnknown)
    3217              :     {
    3218           23 :         jbv.type = jbvNull;
    3219              :     }
    3220              :     else
    3221              :     {
    3222        68103 :         jbv.type = jbvBool;
    3223        68103 :         jbv.val.boolean = res == jpbTrue;
    3224              :     }
    3225              : 
    3226        68126 :     return executeNextItem(cxt, jsp, &next, &jbv, found);
    3227              : }
    3228              : 
    3229              : /*
    3230              :  * Convert jsonpath's scalar or variable node to actual jsonb value.
    3231              :  *
    3232              :  * If node is a variable then its id returned, otherwise 0 returned.
    3233              :  */
    3234              : static void
    3235        40705 : getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
    3236              :                 JsonbValue *value)
    3237              : {
    3238        40705 :     switch (item->type)
    3239              :     {
    3240         5036 :         case jpiNull:
    3241         5036 :             value->type = jbvNull;
    3242         5036 :             break;
    3243          950 :         case jpiBool:
    3244          950 :             value->type = jbvBool;
    3245          950 :             value->val.boolean = jspGetBool(item);
    3246          950 :             break;
    3247        13328 :         case jpiNumeric:
    3248        13328 :             value->type = jbvNumeric;
    3249        13328 :             value->val.numeric = jspGetNumeric(item);
    3250        13328 :             break;
    3251        16115 :         case jpiString:
    3252        16115 :             value->type = jbvString;
    3253        32230 :             value->val.string.val = jspGetString(item,
    3254        16115 :                                                  &value->val.string.len);
    3255        16115 :             break;
    3256         5276 :         case jpiVariable:
    3257         5276 :             getJsonPathVariable(cxt, item, value);
    3258         5244 :             return;
    3259            0 :         default:
    3260            0 :             elog(ERROR, "unexpected jsonpath item type");
    3261              :     }
    3262              : }
    3263              : 
    3264              : /*
    3265              :  * Returns the computed value of a JSON path variable with given name.
    3266              :  */
    3267              : static JsonbValue *
    3268         1740 : GetJsonPathVar(void *cxt, char *varName, int varNameLen,
    3269              :                JsonbValue *baseObject, int *baseObjectId)
    3270              : {
    3271         1740 :     JsonPathVariable *var = NULL;
    3272         1740 :     List       *vars = cxt;
    3273              :     ListCell   *lc;
    3274              :     JsonbValue *result;
    3275         1740 :     int         id = 1;
    3276              : 
    3277         2504 :     foreach(lc, vars)
    3278              :     {
    3279         2492 :         JsonPathVariable *curvar = lfirst(lc);
    3280              : 
    3281         2492 :         if (curvar->namelen == varNameLen &&
    3282         2484 :             strncmp(curvar->name, varName, varNameLen) == 0)
    3283              :         {
    3284         1728 :             var = curvar;
    3285         1728 :             break;
    3286              :         }
    3287              : 
    3288          764 :         id++;
    3289              :     }
    3290              : 
    3291         1740 :     if (var == NULL)
    3292              :     {
    3293           12 :         *baseObjectId = -1;
    3294           12 :         return NULL;
    3295              :     }
    3296              : 
    3297         1728 :     result = palloc_object(JsonbValue);
    3298         1728 :     if (var->isnull)
    3299              :     {
    3300            0 :         *baseObjectId = 0;
    3301            0 :         result->type = jbvNull;
    3302              :     }
    3303              :     else
    3304         1728 :         JsonItemFromDatum(var->value, var->typid, var->typmod, result);
    3305              : 
    3306         1728 :     *baseObject = *result;
    3307         1728 :     *baseObjectId = id;
    3308              : 
    3309         1728 :     return result;
    3310              : }
    3311              : 
    3312              : static int
    3313         4204 : CountJsonPathVars(void *cxt)
    3314              : {
    3315         4204 :     List       *vars = (List *) cxt;
    3316              : 
    3317         4204 :     return list_length(vars);
    3318              : }
    3319              : 
    3320              : 
    3321              : /*
    3322              :  * Initialize JsonbValue to pass to jsonpath executor from given
    3323              :  * datum value of the specified type.
    3324              :  */
    3325              : static void
    3326         1728 : JsonItemFromDatum(Datum val, Oid typid, int32 typmod, JsonbValue *res)
    3327              : {
    3328         1728 :     switch (typid)
    3329              :     {
    3330            0 :         case BOOLOID:
    3331            0 :             res->type = jbvBool;
    3332            0 :             res->val.boolean = DatumGetBool(val);
    3333            0 :             break;
    3334            0 :         case NUMERICOID:
    3335            0 :             JsonbValueInitNumericDatum(res, val);
    3336            0 :             break;
    3337            0 :         case INT2OID:
    3338            0 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(int2_numeric, val));
    3339            0 :             break;
    3340         1660 :         case INT4OID:
    3341         1660 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(int4_numeric, val));
    3342         1660 :             break;
    3343            0 :         case INT8OID:
    3344            0 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(int8_numeric, val));
    3345            0 :             break;
    3346            0 :         case FLOAT4OID:
    3347            0 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(float4_numeric, val));
    3348            0 :             break;
    3349            0 :         case FLOAT8OID:
    3350            0 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(float8_numeric, val));
    3351            0 :             break;
    3352            8 :         case TEXTOID:
    3353              :         case VARCHAROID:
    3354            8 :             res->type = jbvString;
    3355            8 :             res->val.string.val = VARDATA_ANY(DatumGetPointer(val));
    3356            8 :             res->val.string.len = VARSIZE_ANY_EXHDR(DatumGetPointer(val));
    3357            8 :             break;
    3358           48 :         case DATEOID:
    3359              :         case TIMEOID:
    3360              :         case TIMETZOID:
    3361              :         case TIMESTAMPOID:
    3362              :         case TIMESTAMPTZOID:
    3363           48 :             res->type = jbvDatetime;
    3364           48 :             res->val.datetime.value = val;
    3365           48 :             res->val.datetime.typid = typid;
    3366           48 :             res->val.datetime.typmod = typmod;
    3367           48 :             res->val.datetime.tz = 0;
    3368           48 :             break;
    3369           12 :         case JSONBOID:
    3370              :             {
    3371           12 :                 JsonbValue *jbv = res;
    3372           12 :                 Jsonb      *jb = DatumGetJsonbP(val);
    3373              : 
    3374           12 :                 if (JsonContainerIsScalar(&jb->root))
    3375              :                 {
    3376              :                     bool        result PG_USED_FOR_ASSERTS_ONLY;
    3377              : 
    3378           12 :                     result = JsonbExtractScalar(&jb->root, jbv);
    3379              :                     Assert(result);
    3380              :                 }
    3381              :                 else
    3382            0 :                     JsonbInitBinary(jbv, jb);
    3383           12 :                 break;
    3384              :             }
    3385            0 :         case JSONOID:
    3386              :             {
    3387            0 :                 text       *txt = DatumGetTextP(val);
    3388            0 :                 char       *str = text_to_cstring(txt);
    3389              :                 Jsonb      *jb;
    3390              : 
    3391            0 :                 jb = DatumGetJsonbP(DirectFunctionCall1(jsonb_in,
    3392              :                                                         CStringGetDatum(str)));
    3393            0 :                 pfree(str);
    3394              : 
    3395            0 :                 JsonItemFromDatum(JsonbPGetDatum(jb), JSONBOID, -1, res);
    3396            0 :                 break;
    3397              :             }
    3398            0 :         default:
    3399            0 :             ereport(ERROR,
    3400              :                     errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3401              :                     errmsg("could not convert value of type %s to jsonpath",
    3402              :                            format_type_be(typid)));
    3403              :     }
    3404         1728 : }
    3405              : 
    3406              : /* Initialize numeric value from the given datum */
    3407              : static void
    3408         1660 : JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num)
    3409              : {
    3410         1660 :     jbv->type = jbvNumeric;
    3411         1660 :     jbv->val.numeric = DatumGetNumeric(num);
    3412         1660 : }
    3413              : 
    3414              : /*
    3415              :  * Get the value of variable passed to jsonpath executor
    3416              :  */
    3417              : static void
    3418         5276 : getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
    3419              :                     JsonbValue *value)
    3420              : {
    3421              :     char       *varName;
    3422              :     int         varNameLength;
    3423              :     JsonbValue  baseObject;
    3424              :     int         baseObjectId;
    3425              :     JsonbValue *v;
    3426              : 
    3427              :     Assert(variable->type == jpiVariable);
    3428         5276 :     varName = jspGetString(variable, &varNameLength);
    3429              : 
    3430        10552 :     if (cxt->vars == NULL ||
    3431         5276 :         (v = cxt->getVar(cxt->vars, varName, varNameLength,
    3432              :                          &baseObject, &baseObjectId)) == NULL)
    3433           32 :         ereport(ERROR,
    3434              :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    3435              :                  errmsg("could not find jsonpath variable \"%s\"",
    3436              :                         pnstrdup(varName, varNameLength))));
    3437              : 
    3438         5244 :     if (baseObjectId > 0)
    3439              :     {
    3440         5244 :         *value = *v;
    3441         5244 :         setBaseObject(cxt, &baseObject, baseObjectId);
    3442              :     }
    3443         5244 : }
    3444              : 
    3445              : /*
    3446              :  * Definition of JsonPathGetVarCallback for when JsonPathExecContext.vars
    3447              :  * is specified as a jsonb value.
    3448              :  */
    3449              : static JsonbValue *
    3450         3536 : getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, int varNameLength,
    3451              :                              JsonbValue *baseObject, int *baseObjectId)
    3452              : {
    3453         3536 :     Jsonb      *vars = varsJsonb;
    3454              :     JsonbValue  tmp;
    3455              :     JsonbValue *result;
    3456              : 
    3457         3536 :     tmp.type = jbvString;
    3458         3536 :     tmp.val.string.val = varName;
    3459         3536 :     tmp.val.string.len = varNameLength;
    3460              : 
    3461         3536 :     result = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
    3462              : 
    3463         3536 :     if (result == NULL)
    3464              :     {
    3465           20 :         *baseObjectId = -1;
    3466           20 :         return NULL;
    3467              :     }
    3468              : 
    3469         3516 :     *baseObjectId = 1;
    3470         3516 :     JsonbInitBinary(baseObject, vars);
    3471              : 
    3472         3516 :     return result;
    3473              : }
    3474              : 
    3475              : /*
    3476              :  * Definition of JsonPathCountVarsCallback for when JsonPathExecContext.vars
    3477              :  * is specified as a jsonb value.
    3478              :  */
    3479              : static int
    3480       128830 : countVariablesFromJsonb(void *varsJsonb)
    3481              : {
    3482       128830 :     Jsonb      *vars = varsJsonb;
    3483              : 
    3484       128830 :     if (vars && !JsonContainerIsObject(&vars->root))
    3485              :     {
    3486            8 :         ereport(ERROR,
    3487              :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3488              :                 errmsg("\"vars\" argument is not an object"),
    3489              :                 errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object."));
    3490              :     }
    3491              : 
    3492              :     /* count of base objects */
    3493       128822 :     return vars != NULL ? 1 : 0;
    3494              : }
    3495              : 
    3496              : /**************** Support functions for JsonPath execution *****************/
    3497              : 
    3498              : /*
    3499              :  * Returns the size of an array item, or -1 if item is not an array.
    3500              :  */
    3501              : static int
    3502          381 : JsonbArraySize(JsonbValue *jb)
    3503              : {
    3504              :     Assert(jb->type != jbvArray);
    3505              : 
    3506          381 :     if (jb->type == jbvBinary)
    3507              :     {
    3508          353 :         JsonbContainer *jbc = jb->val.binary.data;
    3509              : 
    3510          353 :         if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
    3511          345 :             return JsonContainerSize(jbc);
    3512              :     }
    3513              : 
    3514           36 :     return -1;
    3515              : }
    3516              : 
    3517              : /* Comparison predicate callback. */
    3518              : static JsonPathBool
    3519        14593 : executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
    3520              : {
    3521        14593 :     JsonPathExecContext *cxt = (JsonPathExecContext *) p;
    3522              : 
    3523        14593 :     return compareItems(cmp->type, lv, rv, cxt->useTz);
    3524              : }
    3525              : 
    3526              : /*
    3527              :  * Perform per-byte comparison of two strings.
    3528              :  */
    3529              : static int
    3530         2304 : binaryCompareStrings(const char *s1, int len1,
    3531              :                      const char *s2, int len2)
    3532              : {
    3533              :     int         cmp;
    3534              : 
    3535         2304 :     cmp = memcmp(s1, s2, Min(len1, len2));
    3536              : 
    3537         2304 :     if (cmp != 0)
    3538         1312 :         return cmp;
    3539              : 
    3540          992 :     if (len1 == len2)
    3541          192 :         return 0;
    3542              : 
    3543          800 :     return len1 < len2 ? -1 : 1;
    3544              : }
    3545              : 
    3546              : /*
    3547              :  * Compare two strings in the current server encoding using Unicode codepoint
    3548              :  * collation.
    3549              :  */
    3550              : static int
    3551         2304 : compareStrings(const char *mbstr1, int mblen1,
    3552              :                const char *mbstr2, int mblen2)
    3553              : {
    3554         4608 :     if (GetDatabaseEncoding() == PG_SQL_ASCII ||
    3555         2304 :         GetDatabaseEncoding() == PG_UTF8)
    3556              :     {
    3557              :         /*
    3558              :          * It's known property of UTF-8 strings that their per-byte comparison
    3559              :          * result matches codepoints comparison result.  ASCII can be
    3560              :          * considered as special case of UTF-8.
    3561              :          */
    3562         2304 :         return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
    3563              :     }
    3564              :     else
    3565              :     {
    3566              :         char       *utf8str1,
    3567              :                    *utf8str2;
    3568              :         int         cmp,
    3569              :                     utf8len1,
    3570              :                     utf8len2;
    3571              : 
    3572              :         /*
    3573              :          * We have to convert other encodings to UTF-8 first, then compare.
    3574              :          * Input strings may be not null-terminated and pg_server_to_any() may
    3575              :          * return them "as is".  So, use strlen() only if there is real
    3576              :          * conversion.
    3577              :          */
    3578            0 :         utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
    3579            0 :         utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
    3580            0 :         utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
    3581            0 :         utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
    3582              : 
    3583            0 :         cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
    3584              : 
    3585              :         /*
    3586              :          * If pg_server_to_any() did no real conversion, then we actually
    3587              :          * compared original strings.  So, we already done.
    3588              :          */
    3589            0 :         if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
    3590            0 :             return cmp;
    3591              : 
    3592              :         /* Free memory if needed */
    3593            0 :         if (mbstr1 != utf8str1)
    3594            0 :             pfree(utf8str1);
    3595            0 :         if (mbstr2 != utf8str2)
    3596            0 :             pfree(utf8str2);
    3597              : 
    3598              :         /*
    3599              :          * When all Unicode codepoints are equal, return result of binary
    3600              :          * comparison.  In some edge cases, same characters may have different
    3601              :          * representations in encoding.  Then our behavior could diverge from
    3602              :          * standard.  However, that allow us to do simple binary comparison
    3603              :          * for "==" operator, which is performance critical in typical cases.
    3604              :          * In future to implement strict standard conformance, we can do
    3605              :          * normalization of input JSON strings.
    3606              :          */
    3607            0 :         if (cmp == 0)
    3608            0 :             return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
    3609              :         else
    3610            0 :             return cmp;
    3611              :     }
    3612              : }
    3613              : 
    3614              : /*
    3615              :  * Compare two SQL/JSON items using comparison operation 'op'.
    3616              :  */
    3617              : static JsonPathBool
    3618        14593 : compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
    3619              : {
    3620              :     int         cmp;
    3621              :     bool        res;
    3622              : 
    3623        14593 :     if (jb1->type != jb2->type)
    3624              :     {
    3625         2096 :         if (jb1->type == jbvNull || jb2->type == jbvNull)
    3626              : 
    3627              :             /*
    3628              :              * Equality and order comparison of nulls to non-nulls returns
    3629              :              * always false, but inequality comparison returns true.
    3630              :              */
    3631         1918 :             return op == jpiNotEqual ? jpbTrue : jpbFalse;
    3632              : 
    3633              :         /* Non-null items of different types are not comparable. */
    3634          178 :         return jpbUnknown;
    3635              :     }
    3636              : 
    3637        12497 :     switch (jb1->type)
    3638              :     {
    3639          124 :         case jbvNull:
    3640          124 :             cmp = 0;
    3641          124 :             break;
    3642          580 :         case jbvBool:
    3643          844 :             cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
    3644          264 :                 jb1->val.boolean ? 1 : -1;
    3645          580 :             break;
    3646         2749 :         case jbvNumeric:
    3647         2749 :             cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
    3648         2749 :             break;
    3649         6620 :         case jbvString:
    3650         6620 :             if (op == jpiEqual)
    3651         4316 :                 return jb1->val.string.len != jb2->val.string.len ||
    3652         2388 :                     memcmp(jb1->val.string.val,
    3653         2388 :                            jb2->val.string.val,
    3654         4316 :                            jb1->val.string.len) ? jpbFalse : jpbTrue;
    3655              : 
    3656         2304 :             cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
    3657         2304 :                                  jb2->val.string.val, jb2->val.string.len);
    3658         2304 :             break;
    3659         2416 :         case jbvDatetime:
    3660              :             {
    3661              :                 bool        cast_error;
    3662              : 
    3663         2416 :                 cmp = compareDatetime(jb1->val.datetime.value,
    3664              :                                       jb1->val.datetime.typid,
    3665              :                                       jb2->val.datetime.value,
    3666              :                                       jb2->val.datetime.typid,
    3667              :                                       useTz,
    3668              :                                       &cast_error);
    3669              : 
    3670         2356 :                 if (cast_error)
    3671          204 :                     return jpbUnknown;
    3672              :             }
    3673         2152 :             break;
    3674              : 
    3675            8 :         case jbvBinary:
    3676              :         case jbvArray:
    3677              :         case jbvObject:
    3678            8 :             return jpbUnknown;  /* non-scalars are not comparable */
    3679              : 
    3680            0 :         default:
    3681            0 :             elog(ERROR, "invalid jsonb value type %d", jb1->type);
    3682              :     }
    3683              : 
    3684         7909 :     switch (op)
    3685              :     {
    3686         1818 :         case jpiEqual:
    3687         1818 :             res = (cmp == 0);
    3688         1818 :             break;
    3689            4 :         case jpiNotEqual:
    3690            4 :             res = (cmp != 0);
    3691            4 :             break;
    3692         1418 :         case jpiLess:
    3693         1418 :             res = (cmp < 0);
    3694         1418 :             break;
    3695         1105 :         case jpiGreater:
    3696         1105 :             res = (cmp > 0);
    3697         1105 :             break;
    3698         1361 :         case jpiLessOrEqual:
    3699         1361 :             res = (cmp <= 0);
    3700         1361 :             break;
    3701         2203 :         case jpiGreaterOrEqual:
    3702         2203 :             res = (cmp >= 0);
    3703         2203 :             break;
    3704            0 :         default:
    3705            0 :             elog(ERROR, "unrecognized jsonpath operation: %d", op);
    3706              :             return jpbUnknown;
    3707              :     }
    3708              : 
    3709         7909 :     return res ? jpbTrue : jpbFalse;
    3710              : }
    3711              : 
    3712              : /* Compare two numerics */
    3713              : static int
    3714         2749 : compareNumeric(Numeric a, Numeric b)
    3715              : {
    3716         2749 :     return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
    3717              :                                              NumericGetDatum(a),
    3718              :                                              NumericGetDatum(b)));
    3719              : }
    3720              : 
    3721              : static JsonbValue *
    3722         1248 : copyJsonbValue(JsonbValue *src)
    3723              : {
    3724         1248 :     JsonbValue *dst = palloc_object(JsonbValue);
    3725              : 
    3726         1248 :     *dst = *src;
    3727              : 
    3728         1248 :     return dst;
    3729              : }
    3730              : 
    3731              : /*
    3732              :  * Execute array subscript expression and convert resulting numeric item to
    3733              :  * the integer type with truncation.
    3734              :  */
    3735              : static JsonPathExecResult
    3736          362 : getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
    3737              :               int32 *index)
    3738              : {
    3739              :     JsonbValue *jbv;
    3740              :     JsonValueList found;
    3741              :     JsonPathExecResult res;
    3742              :     Datum       numeric_index;
    3743          362 :     ErrorSaveContext escontext = {T_ErrorSaveContext};
    3744              : 
    3745          362 :     JsonValueListInit(&found);
    3746              : 
    3747          362 :     res = executeItem(cxt, jsp, jb, &found);
    3748              : 
    3749          358 :     if (jperIsError(res))
    3750              :     {
    3751            0 :         JsonValueListClear(&found);
    3752            0 :         return res;
    3753              :     }
    3754              : 
    3755          708 :     if (!JsonValueListIsSingleton(&found) ||
    3756          350 :         !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
    3757              :     {
    3758           16 :         JsonValueListClear(&found);
    3759           16 :         RETURN_ERROR(ereport(ERROR,
    3760              :                              (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
    3761              :                               errmsg("jsonpath array subscript is not a single numeric value"))));
    3762              :     }
    3763              : 
    3764          342 :     numeric_index = DirectFunctionCall2(numeric_trunc,
    3765              :                                         NumericGetDatum(jbv->val.numeric),
    3766              :                                         Int32GetDatum(0));
    3767              : 
    3768          342 :     *index = numeric_int4_safe(DatumGetNumeric(numeric_index),
    3769              :                                (Node *) &escontext);
    3770              : 
    3771          342 :     JsonValueListClear(&found);
    3772              : 
    3773          342 :     if (escontext.error_occurred)
    3774           18 :         RETURN_ERROR(ereport(ERROR,
    3775              :                              (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
    3776              :                               errmsg("jsonpath array subscript is out of integer range"))));
    3777              : 
    3778          324 :     return jperOk;
    3779              : }
    3780              : 
    3781              : /* Save base object and its id needed for the execution of .keyvalue(). */
    3782              : static JsonBaseObjectInfo
    3783       146360 : setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
    3784              : {
    3785       146360 :     JsonBaseObjectInfo baseObject = cxt->baseObject;
    3786              : 
    3787       146360 :     cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
    3788              :         (JsonbContainer *) jbv->val.binary.data;
    3789       146360 :     cxt->baseObject.id = id;
    3790              : 
    3791       146360 :     return baseObject;
    3792              : }
    3793              : 
    3794              : /*
    3795              :  * JsonValueList support functions
    3796              :  */
    3797              : 
    3798              : static void
    3799       236221 : JsonValueListInit(JsonValueList *jvl)
    3800              : {
    3801       236221 :     jvl->nitems = 0;
    3802       236221 :     jvl->maxitems = BASE_JVL_ITEMS;
    3803       236221 :     jvl->next = NULL;
    3804       236221 :     jvl->last = jvl;
    3805       236221 : }
    3806              : 
    3807              : static void
    3808       161497 : JsonValueListClear(JsonValueList *jvl)
    3809              : {
    3810              :     JsonValueList *nxt;
    3811              : 
    3812              :     /* Release any extra chunks */
    3813       161953 :     for (JsonValueList *chunk = jvl->next; chunk != NULL; chunk = nxt)
    3814              :     {
    3815          456 :         nxt = chunk->next;
    3816          456 :         pfree(chunk);
    3817              :     }
    3818              :     /* ... and reset to empty */
    3819       161497 :     jvl->nitems = 0;
    3820              :     Assert(jvl->maxitems == BASE_JVL_ITEMS);
    3821       161497 :     jvl->next = NULL;
    3822       161497 :     jvl->last = jvl;
    3823       161497 : }
    3824              : 
    3825              : static void
    3826       185436 : JsonValueListAppend(JsonValueList *jvl, const JsonbValue *jbv)
    3827              : {
    3828       185436 :     JsonValueList *last = jvl->last;
    3829              : 
    3830       185436 :     if (last->nitems < last->maxitems)
    3831              :     {
    3832              :         /* there's still room in the last existing chunk */
    3833       184542 :         last->items[last->nitems] = *jbv;
    3834       184542 :         last->nitems++;
    3835              :     }
    3836              :     else
    3837              :     {
    3838              :         /* need a new last chunk */
    3839              :         JsonValueList *nxt;
    3840              :         int         nxtsize;
    3841              : 
    3842          894 :         nxtsize = last->maxitems * 2;    /* double the size with each chunk */
    3843          894 :         nxtsize = Max(nxtsize, MIN_EXTRA_JVL_ITEMS);    /* but at least this */
    3844          894 :         nxt = palloc(offsetof(JsonValueList, items) +
    3845          894 :                      nxtsize * sizeof(JsonbValue));
    3846          894 :         nxt->nitems = 1;
    3847          894 :         nxt->maxitems = nxtsize;
    3848          894 :         nxt->next = NULL;
    3849          894 :         nxt->items[0] = *jbv;
    3850          894 :         last->next = nxt;
    3851          894 :         jvl->last = nxt;
    3852              :     }
    3853       185436 : }
    3854              : 
    3855              : static bool
    3856         7518 : JsonValueListIsEmpty(const JsonValueList *jvl)
    3857              : {
    3858              :     /* We need not examine extra chunks for this */
    3859         7518 :     return (jvl->nitems == 0);
    3860              : }
    3861              : 
    3862              : static bool
    3863        66476 : JsonValueListIsSingleton(const JsonValueList *jvl)
    3864              : {
    3865              : #if BASE_JVL_ITEMS > 1
    3866              :     /* We need not examine extra chunks in this case */
    3867        66476 :     return (jvl->nitems == 1);
    3868              : #else
    3869              :     return (jvl->nitems == 1 && jvl->next == NULL);
    3870              : #endif
    3871              : }
    3872              : 
    3873              : static bool
    3874         2732 : JsonValueListHasMultipleItems(const JsonValueList *jvl)
    3875              : {
    3876              : #if BASE_JVL_ITEMS > 1
    3877              :     /* We need not examine extra chunks in this case */
    3878         2732 :     return (jvl->nitems > 1);
    3879              : #else
    3880              :     return (jvl->nitems == 1 && jvl->next != NULL);
    3881              : #endif
    3882              : }
    3883              : 
    3884              : static JsonbValue *
    3885        71706 : JsonValueListHead(JsonValueList *jvl)
    3886              : {
    3887              :     Assert(jvl->nitems > 0);
    3888        71706 :     return &jvl->items[0];
    3889              : }
    3890              : 
    3891              : /*
    3892              :  * JsonValueListIterator functions
    3893              :  */
    3894              : 
    3895              : static void
    3896       140842 : JsonValueListInitIterator(JsonValueList *jvl, JsonValueListIterator *it)
    3897              : {
    3898       140842 :     it->chunk = jvl;
    3899       140842 :     it->nextitem = 0;
    3900       140842 : }
    3901              : 
    3902              : /*
    3903              :  * Get the next item from the sequence advancing iterator.
    3904              :  * Returns NULL if no more items.
    3905              :  */
    3906              : static JsonbValue *
    3907       222853 : JsonValueListNext(JsonValueListIterator *it)
    3908              : {
    3909       222853 :     if (it->chunk == NULL)
    3910          300 :         return NULL;
    3911       222553 :     if (it->nextitem >= it->chunk->nitems)
    3912              :     {
    3913       131899 :         it->chunk = it->chunk->next;
    3914       131899 :         if (it->chunk == NULL)
    3915       131130 :             return NULL;
    3916          769 :         it->nextitem = 0;
    3917              :         Assert(it->chunk->nitems > 0);
    3918              :     }
    3919        91423 :     return &it->chunk->items[it->nextitem++];
    3920              : }
    3921              : 
    3922              : /*
    3923              :  * Initialize a binary JsonbValue with the given jsonb container.
    3924              :  */
    3925              : static JsonbValue *
    3926       132521 : JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
    3927              : {
    3928       132521 :     jbv->type = jbvBinary;
    3929       132521 :     jbv->val.binary.data = &jb->root;
    3930       132521 :     jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
    3931              : 
    3932       132521 :     return jbv;
    3933              : }
    3934              : 
    3935              : /*
    3936              :  * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
    3937              :  */
    3938              : static int
    3939       191185 : JsonbType(JsonbValue *jb)
    3940              : {
    3941       191185 :     int         type = jb->type;
    3942              : 
    3943       191185 :     if (jb->type == jbvBinary)
    3944              :     {
    3945       122443 :         JsonbContainer *jbc = jb->val.binary.data;
    3946              : 
    3947              :         /* Scalars should be always extracted during jsonpath execution. */
    3948              :         Assert(!JsonContainerIsScalar(jbc));
    3949              : 
    3950       122443 :         if (JsonContainerIsObject(jbc))
    3951       119886 :             type = jbvObject;
    3952         2557 :         else if (JsonContainerIsArray(jbc))
    3953         2557 :             type = jbvArray;
    3954              :         else
    3955            0 :             elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
    3956              :     }
    3957              : 
    3958       191185 :     return type;
    3959              : }
    3960              : 
    3961              : /* Get scalar of given type or NULL on type mismatch */
    3962              : static JsonbValue *
    3963         8067 : getScalar(JsonbValue *scalar, enum jbvType type)
    3964              : {
    3965              :     /* Scalars should be always extracted during jsonpath execution. */
    3966              :     Assert(scalar->type != jbvBinary ||
    3967              :            !JsonContainerIsScalar(scalar->val.binary.data));
    3968              : 
    3969         8067 :     return scalar->type == type ? scalar : NULL;
    3970              : }
    3971              : 
    3972              : /* Construct a JSON array from the item list */
    3973              : static JsonbValue *
    3974          330 : wrapItemsInArray(JsonValueList *items)
    3975              : {
    3976          330 :     JsonbInState ps = {0};
    3977              :     JsonValueListIterator it;
    3978              :     JsonbValue *jbv;
    3979              : 
    3980          330 :     pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
    3981              : 
    3982          330 :     JsonValueListInitIterator(items, &it);
    3983          910 :     while ((jbv = JsonValueListNext(&it)))
    3984          580 :         pushJsonbValue(&ps, WJB_ELEM, jbv);
    3985              : 
    3986          330 :     pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
    3987              : 
    3988          330 :     return ps.result;
    3989              : }
    3990              : 
    3991              : /* Check if the timezone required for casting from type1 to type2 is used */
    3992              : static void
    3993          900 : checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
    3994              : {
    3995          900 :     if (!useTz)
    3996          192 :         ereport(ERROR,
    3997              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3998              :                  errmsg("cannot convert value from %s to %s without time zone usage",
    3999              :                         type1, type2),
    4000              :                  errhint("Use *_tz() function for time zone support.")));
    4001          708 : }
    4002              : 
    4003              : /* Convert time datum to timetz datum */
    4004              : static Datum
    4005          168 : castTimeToTimeTz(Datum time, bool useTz)
    4006              : {
    4007          168 :     checkTimezoneIsUsedForCast(useTz, "time", "timetz");
    4008              : 
    4009          144 :     return DirectFunctionCall1(time_timetz, time);
    4010              : }
    4011              : 
    4012              : /*
    4013              :  * Compare date to timestamp.
    4014              :  * Note that this doesn't involve any timezone considerations.
    4015              :  */
    4016              : static int
    4017          124 : cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
    4018              : {
    4019          124 :     return date_cmp_timestamp_internal(date1, ts2);
    4020              : }
    4021              : 
    4022              : /*
    4023              :  * Compare date to timestamptz.
    4024              :  */
    4025              : static int
    4026          108 : cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
    4027              : {
    4028          108 :     checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
    4029              : 
    4030           96 :     return date_cmp_timestamptz_internal(date1, tstz2);
    4031              : }
    4032              : 
    4033              : /*
    4034              :  * Compare timestamp to timestamptz.
    4035              :  */
    4036              : static int
    4037          168 : cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
    4038              : {
    4039          168 :     checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
    4040              : 
    4041          144 :     return timestamp_cmp_timestamptz_internal(ts1, tstz2);
    4042              : }
    4043              : 
    4044              : /*
    4045              :  * Cross-type comparison of two datetime SQL/JSON items.  If items are
    4046              :  * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
    4047              :  * If the cast requires timezone and it is not used, then explicit error is thrown.
    4048              :  */
    4049              : static int
    4050         2416 : compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
    4051              :                 bool useTz, bool *cast_error)
    4052              : {
    4053              :     PGFunction  cmpfunc;
    4054              : 
    4055         2416 :     *cast_error = false;
    4056              : 
    4057         2416 :     switch (typid1)
    4058              :     {
    4059          376 :         case DATEOID:
    4060          376 :             switch (typid2)
    4061              :             {
    4062          252 :                 case DATEOID:
    4063          252 :                     cmpfunc = date_cmp;
    4064              : 
    4065          252 :                     break;
    4066              : 
    4067           52 :                 case TIMESTAMPOID:
    4068           52 :                     return cmpDateToTimestamp(DatumGetDateADT(val1),
    4069              :                                               DatumGetTimestamp(val2),
    4070              :                                               useTz);
    4071              : 
    4072           48 :                 case TIMESTAMPTZOID:
    4073           48 :                     return cmpDateToTimestampTz(DatumGetDateADT(val1),
    4074              :                                                 DatumGetTimestampTz(val2),
    4075              :                                                 useTz);
    4076              : 
    4077           24 :                 case TIMEOID:
    4078              :                 case TIMETZOID:
    4079           24 :                     *cast_error = true; /* uncomparable types */
    4080           24 :                     return 0;
    4081              : 
    4082            0 :                 default:
    4083            0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    4084              :                          typid2);
    4085              :             }
    4086          252 :             break;
    4087              : 
    4088          416 :         case TIMEOID:
    4089          416 :             switch (typid2)
    4090              :             {
    4091          284 :                 case TIMEOID:
    4092          284 :                     cmpfunc = time_cmp;
    4093              : 
    4094          284 :                     break;
    4095              : 
    4096           84 :                 case TIMETZOID:
    4097           84 :                     val1 = castTimeToTimeTz(val1, useTz);
    4098           72 :                     cmpfunc = timetz_cmp;
    4099              : 
    4100           72 :                     break;
    4101              : 
    4102           48 :                 case DATEOID:
    4103              :                 case TIMESTAMPOID:
    4104              :                 case TIMESTAMPTZOID:
    4105           48 :                     *cast_error = true; /* uncomparable types */
    4106           48 :                     return 0;
    4107              : 
    4108            0 :                 default:
    4109            0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    4110              :                          typid2);
    4111              :             }
    4112          356 :             break;
    4113              : 
    4114          536 :         case TIMETZOID:
    4115          536 :             switch (typid2)
    4116              :             {
    4117           84 :                 case TIMEOID:
    4118           84 :                     val2 = castTimeToTimeTz(val2, useTz);
    4119           72 :                     cmpfunc = timetz_cmp;
    4120              : 
    4121           72 :                     break;
    4122              : 
    4123          404 :                 case TIMETZOID:
    4124          404 :                     cmpfunc = timetz_cmp;
    4125              : 
    4126          404 :                     break;
    4127              : 
    4128           48 :                 case DATEOID:
    4129              :                 case TIMESTAMPOID:
    4130              :                 case TIMESTAMPTZOID:
    4131           48 :                     *cast_error = true; /* uncomparable types */
    4132           48 :                     return 0;
    4133              : 
    4134            0 :                 default:
    4135            0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    4136              :                          typid2);
    4137              :             }
    4138          476 :             break;
    4139              : 
    4140          476 :         case TIMESTAMPOID:
    4141          476 :             switch (typid2)
    4142              :             {
    4143           72 :                 case DATEOID:
    4144           72 :                     return -cmpDateToTimestamp(DatumGetDateADT(val2),
    4145              :                                                DatumGetTimestamp(val1),
    4146              :                                                useTz);
    4147              : 
    4148          284 :                 case TIMESTAMPOID:
    4149          284 :                     cmpfunc = timestamp_cmp;
    4150              : 
    4151          284 :                     break;
    4152              : 
    4153           84 :                 case TIMESTAMPTZOID:
    4154           84 :                     return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
    4155              :                                                      DatumGetTimestampTz(val2),
    4156              :                                                      useTz);
    4157              : 
    4158           36 :                 case TIMEOID:
    4159              :                 case TIMETZOID:
    4160           36 :                     *cast_error = true; /* uncomparable types */
    4161           36 :                     return 0;
    4162              : 
    4163            0 :                 default:
    4164            0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    4165              :                          typid2);
    4166              :             }
    4167          284 :             break;
    4168              : 
    4169          612 :         case TIMESTAMPTZOID:
    4170          612 :             switch (typid2)
    4171              :             {
    4172           60 :                 case DATEOID:
    4173           60 :                     return -cmpDateToTimestampTz(DatumGetDateADT(val2),
    4174              :                                                  DatumGetTimestampTz(val1),
    4175              :                                                  useTz);
    4176              : 
    4177           84 :                 case TIMESTAMPOID:
    4178           84 :                     return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
    4179              :                                                       DatumGetTimestampTz(val1),
    4180              :                                                       useTz);
    4181              : 
    4182          420 :                 case TIMESTAMPTZOID:
    4183          420 :                     cmpfunc = timestamp_cmp;
    4184              : 
    4185          420 :                     break;
    4186              : 
    4187           48 :                 case TIMEOID:
    4188              :                 case TIMETZOID:
    4189           48 :                     *cast_error = true; /* uncomparable types */
    4190           48 :                     return 0;
    4191              : 
    4192            0 :                 default:
    4193            0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    4194              :                          typid2);
    4195              :             }
    4196          420 :             break;
    4197              : 
    4198            0 :         default:
    4199            0 :             elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
    4200              :     }
    4201              : 
    4202         1788 :     if (*cast_error)
    4203            0 :         return 0;               /* cast error */
    4204              : 
    4205         1788 :     return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
    4206              : }
    4207              : 
    4208              : /*
    4209              :  * Executor-callable JSON_EXISTS implementation
    4210              :  *
    4211              :  * Returns NULL instead of throwing errors if 'error' is not NULL, setting
    4212              :  * *error to true.
    4213              :  */
    4214              : bool
    4215          388 : JsonPathExists(Datum jb, JsonPath *jp, bool *error, List *vars)
    4216              : {
    4217              :     JsonPathExecResult res;
    4218              : 
    4219          388 :     res = executeJsonPath(jp, vars,
    4220              :                           GetJsonPathVar, CountJsonPathVars,
    4221              :                           DatumGetJsonbP(jb), !error, NULL, true);
    4222              : 
    4223              :     Assert(error || !jperIsError(res));
    4224              : 
    4225          384 :     if (error && jperIsError(res))
    4226          104 :         *error = true;
    4227              : 
    4228          384 :     return res == jperOk;
    4229              : }
    4230              : 
    4231              : /*
    4232              :  * Executor-callable JSON_QUERY implementation
    4233              :  *
    4234              :  * Returns NULL instead of throwing errors if 'error' is not NULL, setting
    4235              :  * *error to true.  *empty is set to true if no match is found.
    4236              :  */
    4237              : Datum
    4238         1640 : JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
    4239              :               bool *error, List *vars,
    4240              :               const char *column_name)
    4241              : {
    4242              :     bool        wrap;
    4243              :     JsonValueList found;
    4244              :     JsonPathExecResult res;
    4245              : 
    4246         1640 :     JsonValueListInit(&found);
    4247              : 
    4248         1640 :     res = executeJsonPath(jp, vars,
    4249              :                           GetJsonPathVar, CountJsonPathVars,
    4250              :                           DatumGetJsonbP(jb), !error, &found, true);
    4251              :     Assert(error || !jperIsError(res));
    4252         1628 :     if (error && jperIsError(res))
    4253              :     {
    4254           20 :         *error = true;
    4255           20 :         *empty = false;
    4256           20 :         return (Datum) 0;
    4257              :     }
    4258              : 
    4259              :     /*
    4260              :      * Determine whether to wrap the result in a JSON array or not.
    4261              :      *
    4262              :      * If the returned JsonValueList is empty, no wrapping is necessary.
    4263              :      *
    4264              :      * If the wrapper mode is JSW_NONE or JSW_UNSPEC, wrapping is explicitly
    4265              :      * disabled. This enforces a WITHOUT WRAPPER clause, which is also the
    4266              :      * default when no WRAPPER clause is specified.
    4267              :      *
    4268              :      * If the mode is JSW_UNCONDITIONAL, wrapping is enforced regardless of
    4269              :      * the number of SQL/JSON items, enforcing a WITH WRAPPER or WITH
    4270              :      * UNCONDITIONAL WRAPPER clause.
    4271              :      *
    4272              :      * For JSW_CONDITIONAL, wrapping occurs only if there is more than one
    4273              :      * SQL/JSON item in the list, enforcing a WITH CONDITIONAL WRAPPER clause.
    4274              :      */
    4275         1608 :     if (JsonValueListIsEmpty(&found))
    4276          140 :         wrap = false;
    4277         1468 :     else if (wrapper == JSW_NONE || wrapper == JSW_UNSPEC)
    4278         1140 :         wrap = false;
    4279          328 :     else if (wrapper == JSW_UNCONDITIONAL)
    4280          212 :         wrap = true;
    4281          116 :     else if (wrapper == JSW_CONDITIONAL)
    4282          116 :         wrap = JsonValueListHasMultipleItems(&found);
    4283              :     else
    4284              :     {
    4285            0 :         elog(ERROR, "unrecognized json wrapper %d", (int) wrapper);
    4286              :         wrap = false;
    4287              :     }
    4288              : 
    4289         1608 :     if (wrap)
    4290          252 :         return JsonbPGetDatum(JsonbValueToJsonb(wrapItemsInArray(&found)));
    4291              : 
    4292              :     /* No wrapping means at most one item is expected. */
    4293         1356 :     if (JsonValueListHasMultipleItems(&found))
    4294              :     {
    4295           40 :         if (error)
    4296              :         {
    4297           32 :             *error = true;
    4298           32 :             return (Datum) 0;
    4299              :         }
    4300              : 
    4301            8 :         if (column_name)
    4302            4 :             ereport(ERROR,
    4303              :                     (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
    4304              :                      errmsg("JSON path expression for column \"%s\" must return single item when no wrapper is requested",
    4305              :                             column_name),
    4306              :                      errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
    4307              :         else
    4308            4 :             ereport(ERROR,
    4309              :                     (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
    4310              :                      errmsg("JSON path expression in JSON_QUERY must return single item when no wrapper is requested"),
    4311              :                      errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
    4312              :     }
    4313              : 
    4314         1316 :     if (!JsonValueListIsEmpty(&found))
    4315         1176 :         return JsonbPGetDatum(JsonbValueToJsonb(JsonValueListHead(&found)));
    4316              : 
    4317          140 :     *empty = true;
    4318          140 :     return PointerGetDatum(NULL);
    4319              : }
    4320              : 
    4321              : /*
    4322              :  * Executor-callable JSON_VALUE implementation
    4323              :  *
    4324              :  * Returns NULL instead of throwing errors if 'error' is not NULL, setting
    4325              :  * *error to true.  *empty is set to true if no match is found.
    4326              :  */
    4327              : JsonbValue *
    4328         1508 : JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars,
    4329              :               const char *column_name)
    4330              : {
    4331              :     JsonbValue *res;
    4332              :     JsonValueList found;
    4333              :     JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY;
    4334              : 
    4335         1508 :     JsonValueListInit(&found);
    4336              : 
    4337         1508 :     jper = executeJsonPath(jp, vars, GetJsonPathVar, CountJsonPathVars,
    4338              :                            DatumGetJsonbP(jb),
    4339              :                            !error, &found, true);
    4340              : 
    4341              :     Assert(error || !jperIsError(jper));
    4342              : 
    4343         1500 :     if (error && jperIsError(jper))
    4344              :     {
    4345           12 :         *error = true;
    4346           12 :         *empty = false;
    4347           12 :         return NULL;
    4348              :     }
    4349              : 
    4350         1488 :     *empty = JsonValueListIsEmpty(&found);
    4351              : 
    4352         1488 :     if (*empty)
    4353          228 :         return NULL;
    4354              : 
    4355              :     /* JSON_VALUE expects to get only singletons. */
    4356         1260 :     if (JsonValueListHasMultipleItems(&found))
    4357              :     {
    4358           12 :         if (error)
    4359              :         {
    4360            8 :             *error = true;
    4361            8 :             return NULL;
    4362              :         }
    4363              : 
    4364            4 :         if (column_name)
    4365            0 :             ereport(ERROR,
    4366              :                     (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
    4367              :                      errmsg("JSON path expression for column \"%s\" must return single scalar item",
    4368              :                             column_name)));
    4369              :         else
    4370            4 :             ereport(ERROR,
    4371              :                     (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
    4372              :                      errmsg("JSON path expression in JSON_VALUE must return single scalar item")));
    4373              :     }
    4374              : 
    4375         1248 :     res = copyJsonbValue(JsonValueListHead(&found));
    4376         1248 :     if (res->type == jbvBinary && JsonContainerIsScalar(res->val.binary.data))
    4377            0 :         JsonbExtractScalar(res->val.binary.data, res);
    4378              : 
    4379              :     /* JSON_VALUE expects to get only scalars. */
    4380         1248 :     if (!IsAJsonbScalar(res))
    4381              :     {
    4382           64 :         if (error)
    4383              :         {
    4384           56 :             *error = true;
    4385           56 :             return NULL;
    4386              :         }
    4387              : 
    4388            8 :         if (column_name)
    4389            0 :             ereport(ERROR,
    4390              :                     (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
    4391              :                      errmsg("JSON path expression for column \"%s\" must return single scalar item",
    4392              :                             column_name)));
    4393              :         else
    4394            8 :             ereport(ERROR,
    4395              :                     (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
    4396              :                      errmsg("JSON path expression in JSON_VALUE must return single scalar item")));
    4397              :     }
    4398              : 
    4399         1184 :     if (res->type == jbvNull)
    4400           48 :         return NULL;
    4401              : 
    4402         1136 :     return res;
    4403              : }
    4404              : 
    4405              : /************************ JSON_TABLE functions ***************************/
    4406              : 
    4407              : /*
    4408              :  * Sanity-checks and returns the opaque JsonTableExecContext from the
    4409              :  * given executor state struct.
    4410              :  */
    4411              : static inline JsonTableExecContext *
    4412         4860 : GetJsonTableExecContext(TableFuncScanState *state, const char *fname)
    4413              : {
    4414              :     JsonTableExecContext *result;
    4415              : 
    4416         4860 :     if (!IsA(state, TableFuncScanState))
    4417            0 :         elog(ERROR, "%s called with invalid TableFuncScanState", fname);
    4418         4860 :     result = (JsonTableExecContext *) state->opaque;
    4419         4860 :     if (result->magic != JSON_TABLE_EXEC_CONTEXT_MAGIC)
    4420            0 :         elog(ERROR, "%s called with invalid TableFuncScanState", fname);
    4421              : 
    4422         4860 :     return result;
    4423              : }
    4424              : 
    4425              : /*
    4426              :  * JsonTableInitOpaque
    4427              :  *      Fill in TableFuncScanState->opaque for processing JSON_TABLE
    4428              :  *
    4429              :  * This initializes the PASSING arguments and the JsonTablePlanState for
    4430              :  * JsonTablePlan given in TableFunc.
    4431              :  */
    4432              : static void
    4433          352 : JsonTableInitOpaque(TableFuncScanState *state, int natts)
    4434              : {
    4435              :     JsonTableExecContext *cxt;
    4436          352 :     PlanState  *ps = &state->ss.ps;
    4437          352 :     TableFuncScan *tfs = castNode(TableFuncScan, ps->plan);
    4438          352 :     TableFunc  *tf = tfs->tablefunc;
    4439          352 :     JsonTablePlan *rootplan = (JsonTablePlan *) tf->plan;
    4440          352 :     JsonExpr   *je = castNode(JsonExpr, tf->docexpr);
    4441          352 :     List       *args = NIL;
    4442              : 
    4443          352 :     cxt = palloc0_object(JsonTableExecContext);
    4444          352 :     cxt->magic = JSON_TABLE_EXEC_CONTEXT_MAGIC;
    4445              : 
    4446              :     /*
    4447              :      * Evaluate JSON_TABLE() PASSING arguments to be passed to the jsonpath
    4448              :      * executor via JsonPathVariables.
    4449              :      */
    4450          352 :     if (state->passingvalexprs)
    4451              :     {
    4452              :         ListCell   *exprlc;
    4453              :         ListCell   *namelc;
    4454              : 
    4455              :         Assert(list_length(state->passingvalexprs) ==
    4456              :                list_length(je->passing_names));
    4457          248 :         forboth(exprlc, state->passingvalexprs,
    4458              :                 namelc, je->passing_names)
    4459              :         {
    4460          164 :             ExprState  *state = lfirst_node(ExprState, exprlc);
    4461          164 :             String     *name = lfirst_node(String, namelc);
    4462          164 :             JsonPathVariable *var = palloc_object(JsonPathVariable);
    4463              : 
    4464          164 :             var->name = pstrdup(name->sval);
    4465          164 :             var->namelen = strlen(var->name);
    4466          164 :             var->typid = exprType((Node *) state->expr);
    4467          164 :             var->typmod = exprTypmod((Node *) state->expr);
    4468              : 
    4469              :             /*
    4470              :              * Evaluate the expression and save the value to be returned by
    4471              :              * GetJsonPathVar().
    4472              :              */
    4473          164 :             var->value = ExecEvalExpr(state, ps->ps_ExprContext,
    4474              :                                       &var->isnull);
    4475              : 
    4476          164 :             args = lappend(args, var);
    4477              :         }
    4478              :     }
    4479              : 
    4480          352 :     cxt->colplanstates = palloc_array(JsonTablePlanState *, list_length(tf->colvalexprs));
    4481              : 
    4482              :     /*
    4483              :      * Initialize plan for the root path and, recursively, also any child
    4484              :      * plans that compute the NESTED paths.
    4485              :      */
    4486          352 :     cxt->rootplanstate = JsonTableInitPlan(cxt, rootplan, NULL, args,
    4487              :                                            CurrentMemoryContext);
    4488              : 
    4489          352 :     state->opaque = cxt;
    4490          352 : }
    4491              : 
    4492              : /*
    4493              :  * JsonTableDestroyOpaque
    4494              :  *      Resets state->opaque
    4495              :  */
    4496              : static void
    4497          352 : JsonTableDestroyOpaque(TableFuncScanState *state)
    4498              : {
    4499              :     JsonTableExecContext *cxt =
    4500          352 :         GetJsonTableExecContext(state, "JsonTableDestroyOpaque");
    4501              : 
    4502              :     /* not valid anymore */
    4503          352 :     cxt->magic = 0;
    4504              : 
    4505          352 :     state->opaque = NULL;
    4506          352 : }
    4507              : 
    4508              : /*
    4509              :  * JsonTableInitPlan
    4510              :  *      Initialize information for evaluating jsonpath in the given
    4511              :  *      JsonTablePlan and, recursively, in any child plans
    4512              :  */
    4513              : static JsonTablePlanState *
    4514          688 : JsonTableInitPlan(JsonTableExecContext *cxt, JsonTablePlan *plan,
    4515              :                   JsonTablePlanState *parentstate,
    4516              :                   List *args, MemoryContext mcxt)
    4517              : {
    4518          688 :     JsonTablePlanState *planstate = palloc0_object(JsonTablePlanState);
    4519              : 
    4520          688 :     planstate->plan = plan;
    4521          688 :     planstate->parent = parentstate;
    4522          688 :     JsonValueListInit(&planstate->found);
    4523              : 
    4524          688 :     if (IsA(plan, JsonTablePathScan))
    4525              :     {
    4526          612 :         JsonTablePathScan *scan = (JsonTablePathScan *) plan;
    4527              :         int         i;
    4528              : 
    4529          612 :         planstate->path = DatumGetJsonPathP(scan->path->value->constvalue);
    4530          612 :         planstate->args = args;
    4531          612 :         planstate->mcxt = AllocSetContextCreate(mcxt, "JsonTableExecContext",
    4532              :                                                 ALLOCSET_DEFAULT_SIZES);
    4533              : 
    4534              :         /* No row pattern evaluated yet. */
    4535          612 :         planstate->current.value = PointerGetDatum(NULL);
    4536          612 :         planstate->current.isnull = true;
    4537              : 
    4538         1556 :         for (i = scan->colMin; i >= 0 && i <= scan->colMax; i++)
    4539          944 :             cxt->colplanstates[i] = planstate;
    4540              : 
    4541          612 :         planstate->nested = scan->child ?
    4542          612 :             JsonTableInitPlan(cxt, scan->child, planstate, args, mcxt) : NULL;
    4543              :     }
    4544           76 :     else if (IsA(plan, JsonTableSiblingJoin))
    4545              :     {
    4546           76 :         JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;
    4547              : 
    4548           76 :         planstate->left = JsonTableInitPlan(cxt, join->lplan, parentstate,
    4549              :                                             args, mcxt);
    4550           76 :         planstate->right = JsonTableInitPlan(cxt, join->rplan, parentstate,
    4551              :                                              args, mcxt);
    4552              :     }
    4553              : 
    4554          688 :     return planstate;
    4555              : }
    4556              : 
    4557              : /*
    4558              :  * JsonTableSetDocument
    4559              :  *      Install the input document and evaluate the row pattern
    4560              :  */
    4561              : static void
    4562          348 : JsonTableSetDocument(TableFuncScanState *state, Datum value)
    4563              : {
    4564              :     JsonTableExecContext *cxt =
    4565          348 :         GetJsonTableExecContext(state, "JsonTableSetDocument");
    4566              : 
    4567          348 :     JsonTableResetRowPattern(cxt->rootplanstate, value);
    4568          344 : }
    4569              : 
    4570              : /*
    4571              :  * Evaluate a JsonTablePlan's jsonpath to get a new row pattern from
    4572              :  * the given context item
    4573              :  */
    4574              : static void
    4575          668 : JsonTableResetRowPattern(JsonTablePlanState *planstate, Datum item)
    4576              : {
    4577          668 :     JsonTablePathScan *scan = castNode(JsonTablePathScan, planstate->plan);
    4578              :     MemoryContext oldcxt;
    4579              :     JsonPathExecResult res;
    4580          668 :     Jsonb      *js = (Jsonb *) DatumGetJsonbP(item);
    4581              : 
    4582          668 :     JsonValueListClear(&planstate->found);
    4583              : 
    4584          668 :     MemoryContextResetOnly(planstate->mcxt);
    4585              : 
    4586          668 :     oldcxt = MemoryContextSwitchTo(planstate->mcxt);
    4587              : 
    4588          668 :     res = executeJsonPath(planstate->path, planstate->args,
    4589              :                           GetJsonPathVar, CountJsonPathVars,
    4590          668 :                           js, scan->errorOnError,
    4591              :                           &planstate->found,
    4592              :                           true);
    4593              : 
    4594          664 :     MemoryContextSwitchTo(oldcxt);
    4595              : 
    4596          664 :     if (jperIsError(res))
    4597              :     {
    4598              :         Assert(!scan->errorOnError);
    4599           12 :         JsonValueListClear(&planstate->found);
    4600              :     }
    4601              : 
    4602              :     /* Reset plan iterator to the beginning of the item list */
    4603          664 :     JsonValueListInitIterator(&planstate->found, &planstate->iter);
    4604          664 :     planstate->current.value = PointerGetDatum(NULL);
    4605          664 :     planstate->current.isnull = true;
    4606          664 :     planstate->ordinal = 0;
    4607          664 : }
    4608              : 
    4609              : /*
    4610              :  * Fetch next row from a JsonTablePlan.
    4611              :  *
    4612              :  * Returns false if the plan has run out of rows, true otherwise.
    4613              :  */
    4614              : static bool
    4615         2800 : JsonTablePlanNextRow(JsonTablePlanState *planstate)
    4616              : {
    4617         2800 :     if (IsA(planstate->plan, JsonTablePathScan))
    4618         2188 :         return JsonTablePlanScanNextRow(planstate);
    4619          612 :     else if (IsA(planstate->plan, JsonTableSiblingJoin))
    4620          612 :         return JsonTablePlanJoinNextRow(planstate);
    4621              :     else
    4622            0 :         elog(ERROR, "invalid JsonTablePlan %d", (int) planstate->plan->type);
    4623              : 
    4624              :     Assert(false);
    4625              :     /* Appease compiler */
    4626              :     return false;
    4627              : }
    4628              : 
    4629              : /*
    4630              :  * Fetch next row from a JsonTablePlan's path evaluation result and from
    4631              :  * any child nested path(s).
    4632              :  *
    4633              :  * Returns true if any of the paths (this or the nested) has more rows to
    4634              :  * return.
    4635              :  *
    4636              :  * By fetching the nested path(s)'s rows based on the parent row at each
    4637              :  * level, this essentially joins the rows of different levels.  If a nested
    4638              :  * path at a given level has no matching rows, the columns of that level will
    4639              :  * compute to NULL, making it an OUTER join.
    4640              :  */
    4641              : static bool
    4642         2188 : JsonTablePlanScanNextRow(JsonTablePlanState *planstate)
    4643              : {
    4644              :     JsonbValue *jbv;
    4645              :     MemoryContext oldcxt;
    4646              : 
    4647              :     /*
    4648              :      * If planstate already has an active row and there is a nested plan,
    4649              :      * check if it has an active row to join with the former.
    4650              :      */
    4651         2188 :     if (!planstate->current.isnull)
    4652              :     {
    4653         1224 :         if (planstate->nested && JsonTablePlanNextRow(planstate->nested))
    4654          324 :             return true;
    4655              :     }
    4656              : 
    4657              :     /* Fetch new row from the list of found values to set as active. */
    4658         1864 :     jbv = JsonValueListNext(&planstate->iter);
    4659              : 
    4660              :     /* End of list? */
    4661         1864 :     if (jbv == NULL)
    4662              :     {
    4663          904 :         planstate->current.value = PointerGetDatum(NULL);
    4664          904 :         planstate->current.isnull = true;
    4665          904 :         return false;
    4666              :     }
    4667              : 
    4668              :     /*
    4669              :      * Set current row item for subsequent JsonTableGetValue() calls for
    4670              :      * evaluating individual columns.
    4671              :      */
    4672          960 :     oldcxt = MemoryContextSwitchTo(planstate->mcxt);
    4673          960 :     planstate->current.value = JsonbPGetDatum(JsonbValueToJsonb(jbv));
    4674          960 :     planstate->current.isnull = false;
    4675          960 :     MemoryContextSwitchTo(oldcxt);
    4676              : 
    4677              :     /* Next row! */
    4678          960 :     planstate->ordinal++;
    4679              : 
    4680              :     /* Process nested plan(s), if any. */
    4681          960 :     if (planstate->nested)
    4682              :     {
    4683              :         /* Re-evaluate the nested path using the above parent row. */
    4684          232 :         JsonTableResetNestedPlan(planstate->nested);
    4685              : 
    4686              :         /*
    4687              :          * Now fetch the nested plan's current row to be joined against the
    4688              :          * parent row.  Any further nested plans' paths will be re-evaluated
    4689              :          * recursively, level at a time, after setting each nested plan's
    4690              :          * current row.
    4691              :          */
    4692          232 :         (void) JsonTablePlanNextRow(planstate->nested);
    4693              :     }
    4694              : 
    4695              :     /* There are more rows. */
    4696          960 :     return true;
    4697              : }
    4698              : 
    4699              : /*
    4700              :  * Re-evaluate the row pattern of a nested plan using the new parent row
    4701              :  * pattern.
    4702              :  */
    4703              : static void
    4704          408 : JsonTableResetNestedPlan(JsonTablePlanState *planstate)
    4705              : {
    4706              :     /* This better be a child plan. */
    4707              :     Assert(planstate->parent != NULL);
    4708          408 :     if (IsA(planstate->plan, JsonTablePathScan))
    4709              :     {
    4710          320 :         JsonTablePlanState *parent = planstate->parent;
    4711              : 
    4712          320 :         if (!parent->current.isnull)
    4713          320 :             JsonTableResetRowPattern(planstate, parent->current.value);
    4714              : 
    4715              :         /*
    4716              :          * If this plan itself has a child nested plan, it will be reset when
    4717              :          * the caller calls JsonTablePlanNextRow() on this plan.
    4718              :          */
    4719              :     }
    4720           88 :     else if (IsA(planstate->plan, JsonTableSiblingJoin))
    4721              :     {
    4722           88 :         JsonTableResetNestedPlan(planstate->left);
    4723           88 :         JsonTableResetNestedPlan(planstate->right);
    4724              :     }
    4725          408 : }
    4726              : 
    4727              : /*
    4728              :  * Fetch the next row from a JsonTableSiblingJoin.
    4729              :  *
    4730              :  * This is essentially a UNION between the rows from left and right siblings.
    4731              :  */
    4732              : static bool
    4733          612 : JsonTablePlanJoinNextRow(JsonTablePlanState *planstate)
    4734              : {
    4735              : 
    4736              :     /* Fetch row from left sibling. */
    4737          612 :     if (!JsonTablePlanNextRow(planstate->left))
    4738              :     {
    4739              :         /*
    4740              :          * Left sibling ran out of rows, so start fetching from the right
    4741              :          * sibling.
    4742              :          */
    4743          364 :         if (!JsonTablePlanNextRow(planstate->right))
    4744              :         {
    4745              :             /* Right sibling ran out of rows too, so there are no more rows. */
    4746          216 :             return false;
    4747              :         }
    4748              :     }
    4749              : 
    4750          396 :     return true;
    4751              : }
    4752              : 
    4753              : /*
    4754              :  * JsonTableFetchRow
    4755              :  *      Prepare the next "current" row for upcoming GetValue calls.
    4756              :  *
    4757              :  * Returns false if no more rows can be returned.
    4758              :  */
    4759              : static bool
    4760         1036 : JsonTableFetchRow(TableFuncScanState *state)
    4761              : {
    4762              :     JsonTableExecContext *cxt =
    4763         1036 :         GetJsonTableExecContext(state, "JsonTableFetchRow");
    4764              : 
    4765         1036 :     return JsonTablePlanNextRow(cxt->rootplanstate);
    4766              : }
    4767              : 
    4768              : /*
    4769              :  * JsonTableGetValue
    4770              :  *      Return the value for column number 'colnum' for the current row.
    4771              :  *
    4772              :  * This leaks memory, so be sure to reset often the context in which it's
    4773              :  * called.
    4774              :  */
    4775              : static Datum
    4776         3124 : JsonTableGetValue(TableFuncScanState *state, int colnum,
    4777              :                   Oid typid, int32 typmod, bool *isnull)
    4778              : {
    4779              :     JsonTableExecContext *cxt =
    4780         3124 :         GetJsonTableExecContext(state, "JsonTableGetValue");
    4781         3124 :     ExprContext *econtext = state->ss.ps.ps_ExprContext;
    4782         3124 :     ExprState  *estate = list_nth(state->colvalexprs, colnum);
    4783         3124 :     JsonTablePlanState *planstate = cxt->colplanstates[colnum];
    4784         3124 :     JsonTablePlanRowSource *current = &planstate->current;
    4785              :     Datum       result;
    4786              : 
    4787              :     /* Row pattern value is NULL */
    4788         3124 :     if (current->isnull)
    4789              :     {
    4790          596 :         result = (Datum) 0;
    4791          596 :         *isnull = true;
    4792              :     }
    4793              :     /* Evaluate JsonExpr. */
    4794         2528 :     else if (estate)
    4795              :     {
    4796         2228 :         Datum       saved_caseValue = econtext->caseValue_datum;
    4797         2228 :         bool        saved_caseIsNull = econtext->caseValue_isNull;
    4798              : 
    4799              :         /* Pass the row pattern value via CaseTestExpr. */
    4800         2228 :         econtext->caseValue_datum = current->value;
    4801         2228 :         econtext->caseValue_isNull = false;
    4802              : 
    4803         2228 :         result = ExecEvalExpr(estate, econtext, isnull);
    4804              : 
    4805         2168 :         econtext->caseValue_datum = saved_caseValue;
    4806         2168 :         econtext->caseValue_isNull = saved_caseIsNull;
    4807              :     }
    4808              :     /* ORDINAL column */
    4809              :     else
    4810              :     {
    4811          300 :         result = Int32GetDatum(planstate->ordinal);
    4812          300 :         *isnull = false;
    4813              :     }
    4814              : 
    4815         3064 :     return result;
    4816              : }
        

Generated by: LCOV version 2.0-1