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

Generated by: LCOV version 2.0-1