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

Generated by: LCOV version 2.0-1