LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_exec.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15beta1 Lines: 1241 1349 92.0 %
Date: 2022-05-18 02:09:37 Functions: 81 85 95.3 %
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-2022, 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 "executor/execExpr.h"
      65             : #include "funcapi.h"
      66             : #include "lib/stringinfo.h"
      67             : #include "miscadmin.h"
      68             : #include "nodes/nodeFuncs.h"
      69             : #include "regex/regex.h"
      70             : #include "utils/builtins.h"
      71             : #include "utils/date.h"
      72             : #include "utils/datetime.h"
      73             : #include "utils/datum.h"
      74             : #include "utils/float.h"
      75             : #include "utils/formatting.h"
      76             : #include "utils/guc.h"
      77             : #include "utils/json.h"
      78             : #include "utils/jsonpath.h"
      79             : #include "utils/lsyscache.h"
      80             : #include "utils/memutils.h"
      81             : #include "utils/timestamp.h"
      82             : #include "utils/varlena.h"
      83             : 
      84             : /*
      85             :  * Represents "base object" and it's "id" for .keyvalue() evaluation.
      86             :  */
      87             : typedef struct JsonBaseObjectInfo
      88             : {
      89             :     JsonbContainer *jbc;
      90             :     int         id;
      91             : } JsonBaseObjectInfo;
      92             : 
      93             : typedef int (*JsonPathVarCallback) (void *vars, char *varName, int varNameLen,
      94             :                                     JsonbValue *val, JsonbValue *baseObject);
      95             : 
      96             : /*
      97             :  * Context of jsonpath execution.
      98             :  */
      99             : typedef struct JsonPathExecContext
     100             : {
     101             :     void       *vars;           /* variables to substitute into jsonpath */
     102             :     JsonPathVarCallback getVar;
     103             :     JsonbValue *root;           /* for $ evaluation */
     104             :     JsonbValue *current;        /* for @ evaluation */
     105             :     JsonBaseObjectInfo baseObject;  /* "base object" for .keyvalue()
     106             :                                      * evaluation */
     107             :     int         lastGeneratedObjectId;  /* "id" counter for .keyvalue()
     108             :                                          * evaluation */
     109             :     int         innermostArraySize; /* for LAST array index evaluation */
     110             :     bool        laxMode;        /* true for "lax" mode, false for "strict"
     111             :                                  * mode */
     112             :     bool        ignoreStructuralErrors; /* with "true" structural errors such
     113             :                                          * as absence of required json item or
     114             :                                          * unexpected json item type are
     115             :                                          * ignored */
     116             :     bool        throwErrors;    /* with "false" all suppressible errors are
     117             :                                  * suppressed */
     118             :     bool        useTz;
     119             : } JsonPathExecContext;
     120             : 
     121             : /* Context for LIKE_REGEX execution. */
     122             : typedef struct JsonLikeRegexContext
     123             : {
     124             :     text       *regex;
     125             :     int         cflags;
     126             : } JsonLikeRegexContext;
     127             : 
     128             : /* Result of jsonpath predicate evaluation */
     129             : typedef enum JsonPathBool
     130             : {
     131             :     jpbFalse = 0,
     132             :     jpbTrue = 1,
     133             :     jpbUnknown = 2
     134             : } JsonPathBool;
     135             : 
     136             : /* Result of jsonpath expression evaluation */
     137             : typedef enum JsonPathExecResult
     138             : {
     139             :     jperOk = 0,
     140             :     jperNotFound = 1,
     141             :     jperError = 2
     142             : } JsonPathExecResult;
     143             : 
     144             : #define jperIsError(jper)           ((jper) == jperError)
     145             : 
     146             : /*
     147             :  * List of jsonb values with shortcut for single-value list.
     148             :  */
     149             : typedef struct JsonValueList
     150             : {
     151             :     JsonbValue *singleton;
     152             :     List       *list;
     153             : } JsonValueList;
     154             : 
     155             : typedef struct JsonValueListIterator
     156             : {
     157             :     JsonbValue *value;
     158             :     List       *list;
     159             :     ListCell   *next;
     160             : } JsonValueListIterator;
     161             : 
     162             : /* Structures for JSON_TABLE execution  */
     163             : typedef struct JsonTableScanState JsonTableScanState;
     164             : typedef struct JsonTableJoinState JsonTableJoinState;
     165             : 
     166             : struct JsonTableScanState
     167             : {
     168             :     JsonTableScanState *parent;
     169             :     JsonTableJoinState *nested;
     170             :     MemoryContext mcxt;
     171             :     JsonPath   *path;
     172             :     List       *args;
     173             :     JsonValueList found;
     174             :     JsonValueListIterator iter;
     175             :     Datum       current;
     176             :     int         ordinal;
     177             :     bool        currentIsNull;
     178             :     bool        outerJoin;
     179             :     bool        errorOnError;
     180             :     bool        advanceNested;
     181             :     bool        reset;
     182             : };
     183             : 
     184             : struct JsonTableJoinState
     185             : {
     186             :     union
     187             :     {
     188             :         struct
     189             :         {
     190             :             JsonTableJoinState *left;
     191             :             JsonTableJoinState *right;
     192             :             bool        cross;
     193             :             bool        advanceRight;
     194             :         }           join;
     195             :         JsonTableScanState scan;
     196             :     }           u;
     197             :     bool        is_join;
     198             : };
     199             : 
     200             : /* random number to identify JsonTableContext */
     201             : #define JSON_TABLE_CONTEXT_MAGIC    418352867
     202             : 
     203             : typedef struct JsonTableContext
     204             : {
     205             :     int         magic;
     206             :     struct
     207             :     {
     208             :         ExprState  *expr;
     209             :         JsonTableScanState *scan;
     210             :     }          *colexprs;
     211             :     JsonTableScanState root;
     212             :     bool        empty;
     213             : } JsonTableContext;
     214             : 
     215             : /* strict/lax flags is decomposed into four [un]wrap/error flags */
     216             : #define jspStrictAbsenseOfErrors(cxt)   (!(cxt)->laxMode)
     217             : #define jspAutoUnwrap(cxt)              ((cxt)->laxMode)
     218             : #define jspAutoWrap(cxt)                ((cxt)->laxMode)
     219             : #define jspIgnoreStructuralErrors(cxt)  ((cxt)->ignoreStructuralErrors)
     220             : #define jspThrowErrors(cxt)             ((cxt)->throwErrors)
     221             : 
     222             : /* Convenience macro: return or throw error depending on context */
     223             : #define RETURN_ERROR(throw_error) \
     224             : do { \
     225             :     if (jspThrowErrors(cxt)) \
     226             :         throw_error; \
     227             :     else \
     228             :         return jperError; \
     229             : } while (0)
     230             : 
     231             : typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
     232             :                                                    JsonbValue *larg,
     233             :                                                    JsonbValue *rarg,
     234             :                                                    void *param);
     235             : typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
     236             : 
     237             : static JsonPathExecResult executeJsonPath(JsonPath *path, void *vars,
     238             :                                           JsonPathVarCallback getVar,
     239             :                                           Jsonb *json, bool throwErrors,
     240             :                                           JsonValueList *result, bool useTz);
     241             : static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
     242             :                                       JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
     243             : static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
     244             :                                                      JsonPathItem *jsp, JsonbValue *jb,
     245             :                                                      JsonValueList *found, bool unwrap);
     246             : static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
     247             :                                                        JsonPathItem *jsp, JsonbValue *jb,
     248             :                                                        JsonValueList *found, bool unwrapElements);
     249             : static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
     250             :                                           JsonPathItem *cur, JsonPathItem *next,
     251             :                                           JsonbValue *v, JsonValueList *found, bool copy);
     252             : static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
     253             :                                                      bool unwrap, JsonValueList *found);
     254             : static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp,
     255             :                                                             JsonbValue *jb, bool unwrap, JsonValueList *found);
     256             : static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
     257             :                                     JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
     258             : static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
     259             :                                           JsonPathItem *jsp, JsonbValue *jb);
     260             : static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
     261             :                                          JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
     262             :                                          uint32 level, uint32 first, uint32 last,
     263             :                                          bool ignoreStructuralErrors, bool unwrapNext);
     264             : static JsonPathBool executePredicate(JsonPathExecContext *cxt,
     265             :                                      JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
     266             :                                      JsonbValue *jb, bool unwrapRightArg,
     267             :                                      JsonPathPredicateCallback exec, void *param);
     268             : static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
     269             :                                                   JsonPathItem *jsp, JsonbValue *jb,
     270             :                                                   BinaryArithmFunc func, JsonValueList *found);
     271             : static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
     272             :                                                  JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
     273             :                                                  JsonValueList *found);
     274             : static JsonPathBool executeStartsWith(JsonPathItem *jsp,
     275             :                                       JsonbValue *whole, JsonbValue *initial, void *param);
     276             : static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
     277             :                                      JsonbValue *rarg, void *param);
     278             : static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
     279             :                                                    JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
     280             :                                                    JsonValueList *found);
     281             : static JsonPathExecResult executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
     282             :                                                 JsonbValue *jb, JsonValueList *found);
     283             : static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
     284             :                                                 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
     285             : static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
     286             :                                            JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
     287             : static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
     288             :                             JsonbValue *value);
     289             : static void getJsonPathVariable(JsonPathExecContext *cxt,
     290             :                                 JsonPathItem *variable, JsonbValue *value);
     291             : static int  getJsonPathVariableFromJsonb(void *varsJsonb, char *varName,
     292             :                                          int varNameLen, JsonbValue *val,
     293             :                                          JsonbValue *baseObject);
     294             : static int  JsonbArraySize(JsonbValue *jb);
     295             : static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
     296             :                                       JsonbValue *rv, void *p);
     297             : static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2,
     298             :                                  bool useTz);
     299             : static int  compareNumeric(Numeric a, Numeric b);
     300             : static JsonbValue *copyJsonbValue(JsonbValue *src);
     301             : static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
     302             :                                         JsonPathItem *jsp, JsonbValue *jb, int32 *index);
     303             : static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
     304             :                                         JsonbValue *jbv, int32 id);
     305             : static void JsonValueListClear(JsonValueList *jvl);
     306             : static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
     307             : static int  JsonValueListLength(const JsonValueList *jvl);
     308             : static bool JsonValueListIsEmpty(JsonValueList *jvl);
     309             : static JsonbValue *JsonValueListHead(JsonValueList *jvl);
     310             : static List *JsonValueListGetList(JsonValueList *jvl);
     311             : static void JsonValueListInitIterator(const JsonValueList *jvl,
     312             :                                       JsonValueListIterator *it);
     313             : static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
     314             :                                      JsonValueListIterator *it);
     315             : static int  JsonbType(JsonbValue *jb);
     316             : static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
     317             : static int  JsonbType(JsonbValue *jb);
     318             : static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
     319             : static JsonbValue *wrapItemsInArray(const JsonValueList *items);
     320             : static int  compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
     321             :                             bool useTz, bool *have_error);
     322             : 
     323             : 
     324             : static JsonTableJoinState *JsonTableInitPlanState(JsonTableContext *cxt,
     325             :                                                   Node *plan, JsonTableScanState *parent);
     326             : static bool JsonTableNextRow(JsonTableScanState *scan);
     327             : 
     328             : 
     329             : /****************** User interface to JsonPath executor ********************/
     330             : 
     331             : /*
     332             :  * jsonb_path_exists
     333             :  *      Returns true if jsonpath returns at least one item for the specified
     334             :  *      jsonb value.  This function and jsonb_path_match() are used to
     335             :  *      implement @? and @@ operators, which in turn are intended to have an
     336             :  *      index support.  Thus, it's desirable to make it easier to achieve
     337             :  *      consistency between index scan results and sequential scan results.
     338             :  *      So, we throw as few errors as possible.  Regarding this function,
     339             :  *      such behavior also matches behavior of JSON_EXISTS() clause of
     340             :  *      SQL/JSON.  Regarding jsonb_path_match(), this function doesn't have
     341             :  *      an analogy in SQL/JSON, so we define its behavior on our own.
     342             :  */
     343             : static Datum
     344       85998 : jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
     345             : {
     346       85998 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     347       85998 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     348             :     JsonPathExecResult res;
     349       85998 :     Jsonb      *vars = NULL;
     350       85998 :     bool        silent = true;
     351             : 
     352       85998 :     if (PG_NARGS() == 4)
     353             :     {
     354          42 :         vars = PG_GETARG_JSONB_P(2);
     355          42 :         silent = PG_GETARG_BOOL(3);
     356             :     }
     357             : 
     358       85998 :     res = executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
     359       85998 :                           jb, !silent, NULL, tz);
     360             : 
     361       85992 :     PG_FREE_IF_COPY(jb, 0);
     362       85992 :     PG_FREE_IF_COPY(jp, 1);
     363             : 
     364       85992 :     if (jperIsError(res))
     365          54 :         PG_RETURN_NULL();
     366             : 
     367       85938 :     PG_RETURN_BOOL(res == jperOk);
     368             : }
     369             : 
     370             : Datum
     371          42 : jsonb_path_exists(PG_FUNCTION_ARGS)
     372             : {
     373          42 :     return jsonb_path_exists_internal(fcinfo, false);
     374             : }
     375             : 
     376             : Datum
     377           0 : jsonb_path_exists_tz(PG_FUNCTION_ARGS)
     378             : {
     379           0 :     return jsonb_path_exists_internal(fcinfo, true);
     380             : }
     381             : 
     382             : /*
     383             :  * jsonb_path_exists_opr
     384             :  *      Implementation of operator "jsonb @? jsonpath" (2-argument version of
     385             :  *      jsonb_path_exists()).
     386             :  */
     387             : Datum
     388       85956 : jsonb_path_exists_opr(PG_FUNCTION_ARGS)
     389             : {
     390             :     /* just call the other one -- it can handle both cases */
     391       85956 :     return jsonb_path_exists_internal(fcinfo, false);
     392             : }
     393             : 
     394             : /*
     395             :  * jsonb_path_match
     396             :  *      Returns jsonpath predicate result item for the specified jsonb value.
     397             :  *      See jsonb_path_exists() comment for details regarding error handling.
     398             :  */
     399             : static Datum
     400       97902 : jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
     401             : {
     402       97902 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     403       97902 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     404       97902 :     JsonValueList found = {0};
     405       97902 :     Jsonb      *vars = NULL;
     406       97902 :     bool        silent = true;
     407             : 
     408       97902 :     if (PG_NARGS() == 4)
     409             :     {
     410         114 :         vars = PG_GETARG_JSONB_P(2);
     411         114 :         silent = PG_GETARG_BOOL(3);
     412             :     }
     413             : 
     414       97902 :     (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
     415       97902 :                            jb, !silent, &found, tz);
     416             : 
     417       97896 :     PG_FREE_IF_COPY(jb, 0);
     418       97896 :     PG_FREE_IF_COPY(jp, 1);
     419             : 
     420       97896 :     if (JsonValueListLength(&found) == 1)
     421             :     {
     422       97866 :         JsonbValue *jbv = JsonValueListHead(&found);
     423             : 
     424       97866 :         if (jbv->type == jbvBool)
     425       97794 :             PG_RETURN_BOOL(jbv->val.boolean);
     426             : 
     427          72 :         if (jbv->type == jbvNull)
     428          24 :             PG_RETURN_NULL();
     429             :     }
     430             : 
     431          78 :     if (!silent)
     432          36 :         ereport(ERROR,
     433             :                 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
     434             :                  errmsg("single boolean result is expected")));
     435             : 
     436          42 :     PG_RETURN_NULL();
     437             : }
     438             : 
     439             : Datum
     440         114 : jsonb_path_match(PG_FUNCTION_ARGS)
     441             : {
     442         114 :     return jsonb_path_match_internal(fcinfo, false);
     443             : }
     444             : 
     445             : Datum
     446           0 : jsonb_path_match_tz(PG_FUNCTION_ARGS)
     447             : {
     448           0 :     return jsonb_path_match_internal(fcinfo, true);
     449             : }
     450             : 
     451             : /*
     452             :  * jsonb_path_match_opr
     453             :  *      Implementation of operator "jsonb @@ jsonpath" (2-argument version of
     454             :  *      jsonb_path_match()).
     455             :  */
     456             : Datum
     457       97788 : jsonb_path_match_opr(PG_FUNCTION_ARGS)
     458             : {
     459             :     /* just call the other one -- it can handle both cases */
     460       97788 :     return jsonb_path_match_internal(fcinfo, false);
     461             : }
     462             : 
     463             : /*
     464             :  * jsonb_path_query
     465             :  *      Executes jsonpath for given jsonb document and returns result as
     466             :  *      rowset.
     467             :  */
     468             : static Datum
     469        3600 : jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
     470             : {
     471             :     FuncCallContext *funcctx;
     472             :     List       *found;
     473             :     JsonbValue *v;
     474             :     ListCell   *c;
     475             : 
     476        3600 :     if (SRF_IS_FIRSTCALL())
     477             :     {
     478             :         JsonPath   *jp;
     479             :         Jsonb      *jb;
     480             :         MemoryContext oldcontext;
     481             :         Jsonb      *vars;
     482             :         bool        silent;
     483        1860 :         JsonValueList found = {0};
     484             : 
     485        1860 :         funcctx = SRF_FIRSTCALL_INIT();
     486        1860 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     487             : 
     488        1860 :         jb = PG_GETARG_JSONB_P_COPY(0);
     489        1860 :         jp = PG_GETARG_JSONPATH_P_COPY(1);
     490        1860 :         vars = PG_GETARG_JSONB_P_COPY(2);
     491        1860 :         silent = PG_GETARG_BOOL(3);
     492             : 
     493        1860 :         (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
     494        1860 :                                jb, !silent, &found, tz);
     495             : 
     496        1410 :         funcctx->user_fctx = JsonValueListGetList(&found);
     497             : 
     498        1410 :         MemoryContextSwitchTo(oldcontext);
     499             :     }
     500             : 
     501        3150 :     funcctx = SRF_PERCALL_SETUP();
     502        3150 :     found = funcctx->user_fctx;
     503             : 
     504        3150 :     c = list_head(found);
     505             : 
     506        3150 :     if (c == NULL)
     507        1410 :         SRF_RETURN_DONE(funcctx);
     508             : 
     509        1740 :     v = lfirst(c);
     510        1740 :     funcctx->user_fctx = list_delete_first(found);
     511             : 
     512        1740 :     SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
     513             : }
     514             : 
     515             : Datum
     516        3216 : jsonb_path_query(PG_FUNCTION_ARGS)
     517             : {
     518        3216 :     return jsonb_path_query_internal(fcinfo, false);
     519             : }
     520             : 
     521             : Datum
     522         384 : jsonb_path_query_tz(PG_FUNCTION_ARGS)
     523             : {
     524         384 :     return jsonb_path_query_internal(fcinfo, true);
     525             : }
     526             : 
     527             : /*
     528             :  * jsonb_path_query_array
     529             :  *      Executes jsonpath for given jsonb document and returns result as
     530             :  *      jsonb array.
     531             :  */
     532             : static Datum
     533          48 : jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
     534             : {
     535          48 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     536          48 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     537          48 :     JsonValueList found = {0};
     538          48 :     Jsonb      *vars = PG_GETARG_JSONB_P(2);
     539          48 :     bool        silent = PG_GETARG_BOOL(3);
     540             : 
     541          48 :     (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
     542          48 :                            jb, !silent, &found, tz);
     543             : 
     544          42 :     PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
     545             : }
     546             : 
     547             : Datum
     548          48 : jsonb_path_query_array(PG_FUNCTION_ARGS)
     549             : {
     550          48 :     return jsonb_path_query_array_internal(fcinfo, false);
     551             : }
     552             : 
     553             : Datum
     554           0 : jsonb_path_query_array_tz(PG_FUNCTION_ARGS)
     555             : {
     556           0 :     return jsonb_path_query_array_internal(fcinfo, true);
     557             : }
     558             : 
     559             : /*
     560             :  * jsonb_path_query_first
     561             :  *      Executes jsonpath for given jsonb document and returns first result
     562             :  *      item.  If there are no items, NULL returned.
     563             :  */
     564             : static Datum
     565        4362 : jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
     566             : {
     567        4362 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     568        4362 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     569        4362 :     JsonValueList found = {0};
     570        4362 :     Jsonb      *vars = PG_GETARG_JSONB_P(2);
     571        4362 :     bool        silent = PG_GETARG_BOOL(3);
     572             : 
     573        4362 :     (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
     574        4362 :                            jb, !silent, &found, tz);
     575             : 
     576        4356 :     if (JsonValueListLength(&found) >= 1)
     577        4344 :         PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
     578             :     else
     579          12 :         PG_RETURN_NULL();
     580             : }
     581             : 
     582             : Datum
     583        4362 : jsonb_path_query_first(PG_FUNCTION_ARGS)
     584             : {
     585        4362 :     return jsonb_path_query_first_internal(fcinfo, false);
     586             : }
     587             : 
     588             : Datum
     589           0 : jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
     590             : {
     591           0 :     return jsonb_path_query_first_internal(fcinfo, true);
     592             : }
     593             : 
     594             : /********************Execute functions for JsonPath**************************/
     595             : 
     596             : /*
     597             :  * Interface to jsonpath executor
     598             :  *
     599             :  * 'path' - jsonpath to be executed
     600             :  * 'vars' - variables to be substituted to jsonpath
     601             :  * 'json' - target document for jsonpath evaluation
     602             :  * 'throwErrors' - whether we should throw suppressible errors
     603             :  * 'result' - list to store result items into
     604             :  *
     605             :  * Returns an error if a recoverable error happens during processing, or NULL
     606             :  * on no error.
     607             :  *
     608             :  * Note, jsonb and jsonpath values should be available and untoasted during
     609             :  * work because JsonPathItem, JsonbValue and result item could have pointers
     610             :  * into input values.  If caller needs to just check if document matches
     611             :  * jsonpath, then it doesn't provide a result arg.  In this case executor
     612             :  * works till first positive result and does not check the rest if possible.
     613             :  * In other case it tries to find all the satisfied result items.
     614             :  */
     615             : static JsonPathExecResult
     616      797304 : executeJsonPath(JsonPath *path, void *vars, JsonPathVarCallback getVar,
     617             :                 Jsonb *json, bool throwErrors, JsonValueList *result,
     618             :                 bool useTz)
     619             : {
     620             :     JsonPathExecContext cxt;
     621             :     JsonPathExecResult res;
     622             :     JsonPathItem jsp;
     623             :     JsonbValue  jbv;
     624             : 
     625      797304 :     jspInit(&jsp, path);
     626             : 
     627      797304 :     if (!JsonbExtractScalar(&json->root, &jbv))
     628      192684 :         JsonbInitBinary(&jbv, json);
     629             : 
     630      797304 :     cxt.vars = vars;
     631      797304 :     cxt.getVar = getVar;
     632      797304 :     cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
     633      797304 :     cxt.ignoreStructuralErrors = cxt.laxMode;
     634      797304 :     cxt.root = &jbv;
     635      797304 :     cxt.current = &jbv;
     636      797304 :     cxt.baseObject.jbc = NULL;
     637      797304 :     cxt.baseObject.id = 0;
     638             :     /* 1 + number of base objects in vars */
     639      797304 :     cxt.lastGeneratedObjectId = 1 + getVar(vars, NULL, 0, NULL, NULL);
     640      797292 :     cxt.innermostArraySize = -1;
     641      797292 :     cxt.throwErrors = throwErrors;
     642      797292 :     cxt.useTz = useTz;
     643             : 
     644      797292 :     if (jspStrictAbsenseOfErrors(&cxt) && !result)
     645             :     {
     646             :         /*
     647             :          * In strict mode we must get a complete list of values to check that
     648             :          * there are no errors at all.
     649             :          */
     650         234 :         JsonValueList vals = {0};
     651             : 
     652         234 :         res = executeItem(&cxt, &jsp, &jbv, &vals);
     653             : 
     654         222 :         if (jperIsError(res))
     655         204 :             return res;
     656             : 
     657          18 :         return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
     658             :     }
     659             : 
     660      797058 :     res = executeItem(&cxt, &jsp, &jbv, result);
     661             : 
     662             :     Assert(!throwErrors || !jperIsError(res));
     663             : 
     664      796584 :     return res;
     665             : }
     666             : 
     667             : /*
     668             :  * Execute jsonpath with automatic unwrapping of current item in lax mode.
     669             :  */
     670             : static JsonPathExecResult
     671     1181268 : executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
     672             :             JsonbValue *jb, JsonValueList *found)
     673             : {
     674     1181268 :     return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
     675             : }
     676             : 
     677             : /*
     678             :  * Main jsonpath executor function: walks on jsonpath structure, finds
     679             :  * relevant parts of jsonb and evaluates expressions over them.
     680             :  * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
     681             :  */
     682             : static JsonPathExecResult
     683     1186860 : executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
     684             :                            JsonbValue *jb, JsonValueList *found, bool unwrap)
     685             : {
     686             :     JsonPathItem elem;
     687     1186860 :     JsonPathExecResult res = jperNotFound;
     688             :     JsonBaseObjectInfo baseObject;
     689             : 
     690     1186860 :     check_stack_depth();
     691     1186860 :     CHECK_FOR_INTERRUPTS();
     692             : 
     693     1186860 :     switch (jsp->type)
     694             :     {
     695             :             /* all boolean item types: */
     696      102174 :         case jpiAnd:
     697             :         case jpiOr:
     698             :         case jpiNot:
     699             :         case jpiIsUnknown:
     700             :         case jpiEqual:
     701             :         case jpiNotEqual:
     702             :         case jpiLess:
     703             :         case jpiGreater:
     704             :         case jpiLessOrEqual:
     705             :         case jpiGreaterOrEqual:
     706             :         case jpiExists:
     707             :         case jpiStartsWith:
     708             :         case jpiLikeRegex:
     709             :             {
     710      102174 :                 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
     711             : 
     712      102174 :                 res = appendBoolResult(cxt, jsp, found, st);
     713      102174 :                 break;
     714             :             }
     715             : 
     716      167286 :         case jpiKey:
     717      167286 :             if (JsonbType(jb) == jbvObject)
     718             :             {
     719             :                 JsonbValue *v;
     720             :                 JsonbValue  key;
     721             : 
     722      166422 :                 key.type = jbvString;
     723      166422 :                 key.val.string.val = jspGetString(jsp, &key.val.string.len);
     724             : 
     725      166422 :                 v = findJsonbValueFromContainer(jb->val.binary.data,
     726             :                                                 JB_FOBJECT, &key);
     727             : 
     728      166422 :                 if (v != NULL)
     729             :                 {
     730       27444 :                     res = executeNextItem(cxt, jsp, NULL,
     731             :                                           v, found, false);
     732             : 
     733             :                     /* free value if it was not added to found list */
     734       27444 :                     if (jspHasNext(jsp) || !found)
     735       16650 :                         pfree(v);
     736             :                 }
     737      138978 :                 else if (!jspIgnoreStructuralErrors(cxt))
     738             :                 {
     739             :                     Assert(found);
     740             : 
     741          84 :                     if (!jspThrowErrors(cxt))
     742          60 :                         return jperError;
     743             : 
     744          24 :                     ereport(ERROR,
     745             :                             (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
     746             :                              errmsg("JSON object does not contain key \"%s\"",
     747             :                                     pnstrdup(key.val.string.val,
     748             :                                              key.val.string.len))));
     749             :                 }
     750             :             }
     751         864 :             else if (unwrap && JsonbType(jb) == jbvArray)
     752          24 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
     753         840 :             else if (!jspIgnoreStructuralErrors(cxt))
     754             :             {
     755             :                 Assert(found);
     756         246 :                 RETURN_ERROR(ereport(ERROR,
     757             :                                      (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
     758             :                                       errmsg("jsonpath member accessor can only be applied to an object"))));
     759             :             }
     760      166932 :             break;
     761             : 
     762      809484 :         case jpiRoot:
     763      809484 :             jb = cxt->root;
     764      809484 :             baseObject = setBaseObject(cxt, jb, 0);
     765      809484 :             res = executeNextItem(cxt, jsp, NULL, jb, found, true);
     766      809046 :             cxt->baseObject = baseObject;
     767      809046 :             break;
     768             : 
     769       21540 :         case jpiCurrent:
     770       21540 :             res = executeNextItem(cxt, jsp, NULL, cxt->current,
     771             :                                   found, true);
     772       21540 :             break;
     773             : 
     774        3072 :         case jpiAnyArray:
     775        3072 :             if (JsonbType(jb) == jbvArray)
     776             :             {
     777        2634 :                 bool        hasNext = jspGetNext(jsp, &elem);
     778             : 
     779        2634 :                 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
     780        2634 :                                                    jb, found, jspAutoUnwrap(cxt));
     781             :             }
     782         438 :             else if (jspAutoWrap(cxt))
     783         186 :                 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
     784         252 :             else if (!jspIgnoreStructuralErrors(cxt))
     785         252 :                 RETURN_ERROR(ereport(ERROR,
     786             :                                      (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
     787             :                                       errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
     788        2694 :             break;
     789             : 
     790         336 :         case jpiIndexArray:
     791         336 :             if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
     792         198 :             {
     793         318 :                 int         innermostArraySize = cxt->innermostArraySize;
     794             :                 int         i;
     795         318 :                 int         size = JsonbArraySize(jb);
     796         318 :                 bool        singleton = size < 0;
     797         318 :                 bool        hasNext = jspGetNext(jsp, &elem);
     798             : 
     799         318 :                 if (singleton)
     800          42 :                     size = 1;
     801             : 
     802         318 :                 cxt->innermostArraySize = size; /* for LAST evaluation */
     803             : 
     804         498 :                 for (i = 0; i < jsp->content.array.nelems; i++)
     805             :                 {
     806             :                     JsonPathItem from;
     807             :                     JsonPathItem to;
     808             :                     int32       index;
     809             :                     int32       index_from;
     810             :                     int32       index_to;
     811         330 :                     bool        range = jspGetArraySubscript(jsp, &from,
     812             :                                                              &to, i);
     813             : 
     814         330 :                     res = getArrayIndex(cxt, &from, jb, &index_from);
     815             : 
     816         306 :                     if (jperIsError(res))
     817          30 :                         break;
     818             : 
     819         282 :                     if (range)
     820             :                     {
     821          30 :                         res = getArrayIndex(cxt, &to, jb, &index_to);
     822             : 
     823          24 :                         if (jperIsError(res))
     824           0 :                             break;
     825             :                     }
     826             :                     else
     827         252 :                         index_to = index_from;
     828             : 
     829         276 :                     if (!jspIgnoreStructuralErrors(cxt) &&
     830          84 :                         (index_from < 0 ||
     831          72 :                          index_from > index_to ||
     832          72 :                          index_to >= size))
     833          90 :                         RETURN_ERROR(ereport(ERROR,
     834             :                                              (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
     835             :                                               errmsg("jsonpath array subscript is out of bounds"))));
     836             : 
     837         228 :                     if (index_from < 0)
     838          12 :                         index_from = 0;
     839             : 
     840         228 :                     if (index_to >= size)
     841          36 :                         index_to = size - 1;
     842             : 
     843         228 :                     res = jperNotFound;
     844             : 
     845         408 :                     for (index = index_from; index <= index_to; index++)
     846             :                     {
     847             :                         JsonbValue *v;
     848             :                         bool        copy;
     849             : 
     850         228 :                         if (singleton)
     851             :                         {
     852          24 :                             v = jb;
     853          24 :                             copy = true;
     854             :                         }
     855             :                         else
     856             :                         {
     857         204 :                             v = getIthJsonbValueFromContainer(jb->val.binary.data,
     858             :                                                               (uint32) index);
     859             : 
     860         204 :                             if (v == NULL)
     861           0 :                                 continue;
     862             : 
     863         204 :                             copy = false;
     864             :                         }
     865             : 
     866         228 :                         if (!hasNext && !found)
     867          42 :                             return jperOk;
     868             : 
     869         186 :                         res = executeNextItem(cxt, jsp, &elem, v, found,
     870             :                                               copy);
     871             : 
     872         186 :                         if (jperIsError(res))
     873           0 :                             break;
     874             : 
     875         186 :                         if (res == jperOk && !found)
     876           6 :                             break;
     877             :                     }
     878             : 
     879         186 :                     if (jperIsError(res))
     880           0 :                         break;
     881             : 
     882         186 :                     if (res == jperOk && !found)
     883           6 :                         break;
     884             :                 }
     885             : 
     886         198 :                 cxt->innermostArraySize = innermostArraySize;
     887             :             }
     888          18 :             else if (!jspIgnoreStructuralErrors(cxt))
     889             :             {
     890          18 :                 RETURN_ERROR(ereport(ERROR,
     891             :                                      (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
     892             :                                       errmsg("jsonpath array accessor can only be applied to an array"))));
     893             :             }
     894         198 :             break;
     895             : 
     896          66 :         case jpiLast:
     897             :             {
     898             :                 JsonbValue  tmpjbv;
     899             :                 JsonbValue *lastjbv;
     900             :                 int         last;
     901          66 :                 bool        hasNext = jspGetNext(jsp, &elem);
     902             : 
     903          66 :                 if (cxt->innermostArraySize < 0)
     904           0 :                     elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
     905             : 
     906          66 :                 if (!hasNext && !found)
     907             :                 {
     908           6 :                     res = jperOk;
     909           6 :                     break;
     910             :                 }
     911             : 
     912          60 :                 last = cxt->innermostArraySize - 1;
     913             : 
     914          60 :                 lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
     915             : 
     916          60 :                 lastjbv->type = jbvNumeric;
     917          60 :                 lastjbv->val.numeric = int64_to_numeric(last);
     918             : 
     919          60 :                 res = executeNextItem(cxt, jsp, &elem,
     920             :                                       lastjbv, found, hasNext);
     921             :             }
     922          60 :             break;
     923             : 
     924         126 :         case jpiAnyKey:
     925         126 :             if (JsonbType(jb) == jbvObject)
     926             :             {
     927         108 :                 bool        hasNext = jspGetNext(jsp, &elem);
     928             : 
     929         108 :                 if (jb->type != jbvBinary)
     930           0 :                     elog(ERROR, "invalid jsonb object type: %d", jb->type);
     931             : 
     932         108 :                 return executeAnyItem
     933             :                     (cxt, hasNext ? &elem : NULL,
     934             :                      jb->val.binary.data, found, 1, 1, 1,
     935         108 :                      false, jspAutoUnwrap(cxt));
     936             :             }
     937          18 :             else if (unwrap && JsonbType(jb) == jbvArray)
     938           0 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
     939          18 :             else if (!jspIgnoreStructuralErrors(cxt))
     940             :             {
     941             :                 Assert(found);
     942          12 :                 RETURN_ERROR(ereport(ERROR,
     943             :                                      (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
     944             :                                       errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
     945             :             }
     946           6 :             break;
     947             : 
     948         168 :         case jpiAdd:
     949         168 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     950             :                                            numeric_add_opt_error, found);
     951             : 
     952          60 :         case jpiSub:
     953          60 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     954             :                                            numeric_sub_opt_error, found);
     955             : 
     956          36 :         case jpiMul:
     957          36 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     958             :                                            numeric_mul_opt_error, found);
     959             : 
     960          66 :         case jpiDiv:
     961          66 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     962             :                                            numeric_div_opt_error, found);
     963             : 
     964          12 :         case jpiMod:
     965          12 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     966             :                                            numeric_mod_opt_error, found);
     967             : 
     968          60 :         case jpiPlus:
     969          60 :             return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
     970             : 
     971         126 :         case jpiMinus:
     972         126 :             return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
     973             :                                           found);
     974             : 
     975       20178 :         case jpiFilter:
     976             :             {
     977             :                 JsonPathBool st;
     978             : 
     979       20178 :                 if (unwrap && JsonbType(jb) == jbvArray)
     980         126 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
     981             :                                                         false);
     982             : 
     983       20052 :                 jspGetArg(jsp, &elem);
     984       20052 :                 st = executeNestedBoolItem(cxt, &elem, jb);
     985       19950 :                 if (st != jpbTrue)
     986       17424 :                     res = jperNotFound;
     987             :                 else
     988        2526 :                     res = executeNextItem(cxt, jsp, NULL,
     989             :                                           jb, found, true);
     990       19950 :                 break;
     991             :             }
     992             : 
     993         306 :         case jpiAny:
     994             :             {
     995         306 :                 bool        hasNext = jspGetNext(jsp, &elem);
     996             : 
     997             :                 /* first try without any intermediate steps */
     998         306 :                 if (jsp->content.anybounds.first == 0)
     999             :                 {
    1000             :                     bool        savedIgnoreStructuralErrors;
    1001             : 
    1002         168 :                     savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
    1003         168 :                     cxt->ignoreStructuralErrors = true;
    1004         168 :                     res = executeNextItem(cxt, jsp, &elem,
    1005             :                                           jb, found, true);
    1006         168 :                     cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
    1007             : 
    1008         168 :                     if (res == jperOk && !found)
    1009           6 :                         break;
    1010             :                 }
    1011             : 
    1012         300 :                 if (jb->type == jbvBinary)
    1013         300 :                     res = executeAnyItem
    1014             :                         (cxt, hasNext ? &elem : NULL,
    1015             :                          jb->val.binary.data, found,
    1016             :                          1,
    1017             :                          jsp->content.anybounds.first,
    1018             :                          jsp->content.anybounds.last,
    1019         300 :                          true, jspAutoUnwrap(cxt));
    1020         300 :                 break;
    1021             :             }
    1022             : 
    1023       57798 :         case jpiNull:
    1024             :         case jpiBool:
    1025             :         case jpiNumeric:
    1026             :         case jpiString:
    1027             :         case jpiVariable:
    1028             :             {
    1029             :                 JsonbValue  vbuf;
    1030             :                 JsonbValue *v;
    1031       57798 :                 bool        hasNext = jspGetNext(jsp, &elem);
    1032             : 
    1033       57798 :                 if (!hasNext && !found)
    1034             :                 {
    1035           6 :                     res = jperOk;   /* skip evaluation */
    1036           6 :                     break;
    1037             :                 }
    1038             : 
    1039       57792 :                 v = hasNext ? &vbuf : palloc(sizeof(*v));
    1040             : 
    1041       57792 :                 baseObject = cxt->baseObject;
    1042       57792 :                 getJsonPathItem(cxt, jsp, v);
    1043             : 
    1044       57780 :                 res = executeNextItem(cxt, jsp, &elem,
    1045             :                                       v, found, hasNext);
    1046       57780 :                 cxt->baseObject = baseObject;
    1047             :             }
    1048       57780 :             break;
    1049             : 
    1050         210 :         case jpiType:
    1051             :             {
    1052         210 :                 JsonbValue *jbv = palloc(sizeof(*jbv));
    1053             : 
    1054         210 :                 jbv->type = jbvString;
    1055         210 :                 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
    1056         210 :                 jbv->val.string.len = strlen(jbv->val.string.val);
    1057             : 
    1058         210 :                 res = executeNextItem(cxt, jsp, NULL, jbv,
    1059             :                                       found, false);
    1060             :             }
    1061         210 :             break;
    1062             : 
    1063          72 :         case jpiSize:
    1064             :             {
    1065          72 :                 int         size = JsonbArraySize(jb);
    1066             : 
    1067          72 :                 if (size < 0)
    1068             :                 {
    1069          48 :                     if (!jspAutoWrap(cxt))
    1070             :                     {
    1071          12 :                         if (!jspIgnoreStructuralErrors(cxt))
    1072          12 :                             RETURN_ERROR(ereport(ERROR,
    1073             :                                                  (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
    1074             :                                                   errmsg("jsonpath item method .%s() can only be applied to an array",
    1075             :                                                          jspOperationName(jsp->type)))));
    1076           0 :                         break;
    1077             :                     }
    1078             : 
    1079          36 :                     size = 1;
    1080             :                 }
    1081             : 
    1082          60 :                 jb = palloc(sizeof(*jb));
    1083             : 
    1084          60 :                 jb->type = jbvNumeric;
    1085          60 :                 jb->val.numeric = int64_to_numeric(size);
    1086             : 
    1087          60 :                 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
    1088             :             }
    1089          60 :             break;
    1090             : 
    1091         108 :         case jpiAbs:
    1092         108 :             return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
    1093             :                                             found);
    1094             : 
    1095          48 :         case jpiFloor:
    1096          48 :             return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
    1097             :                                             found);
    1098             : 
    1099         102 :         case jpiCeiling:
    1100         102 :             return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
    1101             :                                             found);
    1102             : 
    1103         114 :         case jpiDouble:
    1104             :             {
    1105             :                 JsonbValue  jbv;
    1106             : 
    1107         114 :                 if (unwrap && JsonbType(jb) == jbvArray)
    1108          42 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
    1109             :                                                         false);
    1110             : 
    1111         108 :                 if (jb->type == jbvNumeric)
    1112             :                 {
    1113          12 :                     char       *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
    1114             :                                                                           NumericGetDatum(jb->val.numeric)));
    1115             :                     double      val;
    1116          12 :                     bool        have_error = false;
    1117             : 
    1118          12 :                     val = float8in_internal_opt_error(tmp,
    1119             :                                                       NULL,
    1120             :                                                       "double precision",
    1121             :                                                       tmp,
    1122             :                                                       &have_error);
    1123             : 
    1124          12 :                     if (have_error || isinf(val) || isnan(val))
    1125           6 :                         RETURN_ERROR(ereport(ERROR,
    1126             :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1127             :                                               errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
    1128             :                                                      jspOperationName(jsp->type)))));
    1129           6 :                     res = jperOk;
    1130             :                 }
    1131          96 :                 else if (jb->type == jbvString)
    1132             :                 {
    1133             :                     /* cast string as double */
    1134             :                     double      val;
    1135          48 :                     char       *tmp = pnstrdup(jb->val.string.val,
    1136          48 :                                                jb->val.string.len);
    1137          48 :                     bool        have_error = false;
    1138             : 
    1139          48 :                     val = float8in_internal_opt_error(tmp,
    1140             :                                                       NULL,
    1141             :                                                       "double precision",
    1142             :                                                       tmp,
    1143             :                                                       &have_error);
    1144             : 
    1145          48 :                     if (have_error || isinf(val) || isnan(val))
    1146          42 :                         RETURN_ERROR(ereport(ERROR,
    1147             :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1148             :                                               errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
    1149             :                                                      jspOperationName(jsp->type)))));
    1150             : 
    1151           6 :                     jb = &jbv;
    1152           6 :                     jb->type = jbvNumeric;
    1153           6 :                     jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
    1154             :                                                                           Float8GetDatum(val)));
    1155           6 :                     res = jperOk;
    1156             :                 }
    1157             : 
    1158          60 :                 if (res == jperNotFound)
    1159          48 :                     RETURN_ERROR(ereport(ERROR,
    1160             :                                          (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1161             :                                           errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
    1162             :                                                  jspOperationName(jsp->type)))));
    1163             : 
    1164          12 :                 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
    1165             :             }
    1166          12 :             break;
    1167             : 
    1168        3222 :         case jpiDatetime:
    1169        3222 :             if (unwrap && JsonbType(jb) == jbvArray)
    1170           6 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1171             : 
    1172        3216 :             return executeDateTimeMethod(cxt, jsp, jb, found);
    1173             : 
    1174          90 :         case jpiKeyValue:
    1175          90 :             if (unwrap && JsonbType(jb) == jbvArray)
    1176           6 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1177             : 
    1178          84 :             return executeKeyValueMethod(cxt, jsp, jb, found);
    1179             : 
    1180           0 :         default:
    1181           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
    1182             :     }
    1183             : 
    1184     1180980 :     return res;
    1185             : }
    1186             : 
    1187             : /*
    1188             :  * Unwrap current array item and execute jsonpath for each of its elements.
    1189             :  */
    1190             : static JsonPathExecResult
    1191        2856 : executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1192             :                              JsonbValue *jb, JsonValueList *found,
    1193             :                              bool unwrapElements)
    1194             : {
    1195        2856 :     if (jb->type != jbvBinary)
    1196             :     {
    1197             :         Assert(jb->type != jbvArray);
    1198           0 :         elog(ERROR, "invalid jsonb array value type: %d", jb->type);
    1199             :     }
    1200             : 
    1201        2856 :     return executeAnyItem
    1202             :         (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
    1203             :          false, unwrapElements);
    1204             : }
    1205             : 
    1206             : /*
    1207             :  * Execute next jsonpath item if exists.  Otherwise put "v" to the "found"
    1208             :  * list if provided.
    1209             :  */
    1210             : static JsonPathExecResult
    1211     1025508 : executeNextItem(JsonPathExecContext *cxt,
    1212             :                 JsonPathItem *cur, JsonPathItem *next,
    1213             :                 JsonbValue *v, JsonValueList *found, bool copy)
    1214             : {
    1215             :     JsonPathItem elem;
    1216             :     bool        hasNext;
    1217             : 
    1218     1025508 :     if (!cur)
    1219           0 :         hasNext = next != NULL;
    1220     1025508 :     else if (next)
    1221      164046 :         hasNext = jspHasNext(cur);
    1222             :     else
    1223             :     {
    1224      861462 :         next = &elem;
    1225      861462 :         hasNext = jspGetNext(cur, next);
    1226             :     }
    1227             : 
    1228     1025508 :     if (hasNext)
    1229      189678 :         return executeItem(cxt, next, v, found);
    1230             : 
    1231      835830 :     if (found)
    1232      785694 :         JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
    1233             : 
    1234      835830 :     return jperOk;
    1235             : }
    1236             : 
    1237             : /*
    1238             :  * Same as executeItem(), but when "unwrap == true" automatically unwraps
    1239             :  * each array item from the resulting sequence in lax mode.
    1240             :  */
    1241             : static JsonPathExecResult
    1242      193938 : executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1243             :                            JsonbValue *jb, bool unwrap,
    1244             :                            JsonValueList *found)
    1245             : {
    1246      193938 :     if (unwrap && jspAutoUnwrap(cxt))
    1247             :     {
    1248      113364 :         JsonValueList seq = {0};
    1249             :         JsonValueListIterator it;
    1250      113364 :         JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
    1251             :         JsonbValue *item;
    1252             : 
    1253      113334 :         if (jperIsError(res))
    1254          66 :             return res;
    1255             : 
    1256      113268 :         JsonValueListInitIterator(&seq, &it);
    1257      188688 :         while ((item = JsonValueListNext(&seq, &it)))
    1258             :         {
    1259             :             Assert(item->type != jbvArray);
    1260             : 
    1261       75420 :             if (JsonbType(item) == jbvArray)
    1262          54 :                 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
    1263             :             else
    1264       75366 :                 JsonValueListAppend(found, item);
    1265             :         }
    1266             : 
    1267      113268 :         return jperOk;
    1268             :     }
    1269             : 
    1270       80574 :     return executeItem(cxt, jsp, jb, found);
    1271             : }
    1272             : 
    1273             : /*
    1274             :  * Same as executeItemOptUnwrapResult(), but with error suppression.
    1275             :  */
    1276             : static JsonPathExecResult
    1277      193074 : executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
    1278             :                                   JsonPathItem *jsp,
    1279             :                                   JsonbValue *jb, bool unwrap,
    1280             :                                   JsonValueList *found)
    1281             : {
    1282             :     JsonPathExecResult res;
    1283      193074 :     bool        throwErrors = cxt->throwErrors;
    1284             : 
    1285      193074 :     cxt->throwErrors = false;
    1286      193074 :     res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
    1287      193062 :     cxt->throwErrors = throwErrors;
    1288             : 
    1289      193062 :     return res;
    1290             : }
    1291             : 
    1292             : /* Execute boolean-valued jsonpath expression. */
    1293             : static JsonPathBool
    1294      176058 : executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1295             :                 JsonbValue *jb, bool canHaveNext)
    1296             : {
    1297             :     JsonPathItem larg;
    1298             :     JsonPathItem rarg;
    1299             :     JsonPathBool res;
    1300             :     JsonPathBool res2;
    1301             : 
    1302      176058 :     if (!canHaveNext && jspHasNext(jsp))
    1303           0 :         elog(ERROR, "boolean jsonpath item cannot have next item");
    1304             : 
    1305      176058 :     switch (jsp->type)
    1306             :     {
    1307       26448 :         case jpiAnd:
    1308       26448 :             jspGetLeftArg(jsp, &larg);
    1309       26448 :             res = executeBoolItem(cxt, &larg, jb, false);
    1310             : 
    1311       26448 :             if (res == jpbFalse)
    1312       22800 :                 return jpbFalse;
    1313             : 
    1314             :             /*
    1315             :              * SQL/JSON says that we should check second arg in case of
    1316             :              * jperError
    1317             :              */
    1318             : 
    1319        3648 :             jspGetRightArg(jsp, &rarg);
    1320        3648 :             res2 = executeBoolItem(cxt, &rarg, jb, false);
    1321             : 
    1322        3648 :             return res2 == jpbTrue ? res : res2;
    1323             : 
    1324       12954 :         case jpiOr:
    1325       12954 :             jspGetLeftArg(jsp, &larg);
    1326       12954 :             res = executeBoolItem(cxt, &larg, jb, false);
    1327             : 
    1328       12954 :             if (res == jpbTrue)
    1329        2490 :                 return jpbTrue;
    1330             : 
    1331       10464 :             jspGetRightArg(jsp, &rarg);
    1332       10464 :             res2 = executeBoolItem(cxt, &rarg, jb, false);
    1333             : 
    1334       10464 :             return res2 == jpbFalse ? res : res2;
    1335             : 
    1336         108 :         case jpiNot:
    1337         108 :             jspGetArg(jsp, &larg);
    1338             : 
    1339         108 :             res = executeBoolItem(cxt, &larg, jb, false);
    1340             : 
    1341         108 :             if (res == jpbUnknown)
    1342          36 :                 return jpbUnknown;
    1343             : 
    1344          72 :             return res == jpbTrue ? jpbFalse : jpbTrue;
    1345             : 
    1346         210 :         case jpiIsUnknown:
    1347         210 :             jspGetArg(jsp, &larg);
    1348         210 :             res = executeBoolItem(cxt, &larg, jb, false);
    1349         210 :             return res == jpbUnknown ? jpbTrue : jpbFalse;
    1350             : 
    1351       56670 :         case jpiEqual:
    1352             :         case jpiNotEqual:
    1353             :         case jpiLess:
    1354             :         case jpiGreater:
    1355             :         case jpiLessOrEqual:
    1356             :         case jpiGreaterOrEqual:
    1357       56670 :             jspGetLeftArg(jsp, &larg);
    1358       56670 :             jspGetRightArg(jsp, &rarg);
    1359       56670 :             return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
    1360             :                                     executeComparison, cxt);
    1361             : 
    1362          84 :         case jpiStartsWith:     /* 'whole STARTS WITH initial' */
    1363          84 :             jspGetLeftArg(jsp, &larg);  /* 'whole' */
    1364          84 :             jspGetRightArg(jsp, &rarg); /* 'initial' */
    1365          84 :             return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
    1366             :                                     executeStartsWith, NULL);
    1367             : 
    1368         396 :         case jpiLikeRegex:      /* 'expr LIKE_REGEX pattern FLAGS flags' */
    1369             :             {
    1370             :                 /*
    1371             :                  * 'expr' is a sequence-returning expression.  'pattern' is a
    1372             :                  * regex string literal.  SQL/JSON standard requires XQuery
    1373             :                  * regexes, but we use Postgres regexes here.  'flags' is a
    1374             :                  * string literal converted to integer flags at compile-time.
    1375             :                  */
    1376         396 :                 JsonLikeRegexContext lrcxt = {0};
    1377             : 
    1378         396 :                 jspInitByBuffer(&larg, jsp->base,
    1379             :                                 jsp->content.like_regex.expr);
    1380             : 
    1381         396 :                 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
    1382             :                                         executeLikeRegex, &lrcxt);
    1383             :             }
    1384             : 
    1385       79188 :         case jpiExists:
    1386       79188 :             jspGetArg(jsp, &larg);
    1387             : 
    1388       79188 :             if (jspStrictAbsenseOfErrors(cxt))
    1389             :             {
    1390             :                 /*
    1391             :                  * In strict mode we must get a complete list of values to
    1392             :                  * check that there are no errors at all.
    1393             :                  */
    1394          48 :                 JsonValueList vals = {0};
    1395             :                 JsonPathExecResult res =
    1396          48 :                 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
    1397             :                                                   false, &vals);
    1398             : 
    1399          48 :                 if (jperIsError(res))
    1400          36 :                     return jpbUnknown;
    1401             : 
    1402          12 :                 return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
    1403             :             }
    1404             :             else
    1405             :             {
    1406             :                 JsonPathExecResult res =
    1407       79140 :                 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
    1408             :                                                   false, NULL);
    1409             : 
    1410       79140 :                 if (jperIsError(res))
    1411          24 :                     return jpbUnknown;
    1412             : 
    1413       79116 :                 return res == jperOk ? jpbTrue : jpbFalse;
    1414             :             }
    1415             : 
    1416           0 :         default:
    1417           0 :             elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
    1418             :             return jpbUnknown;
    1419             :     }
    1420             : }
    1421             : 
    1422             : /*
    1423             :  * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
    1424             :  * item onto the stack.
    1425             :  */
    1426             : static JsonPathBool
    1427       20052 : executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1428             :                       JsonbValue *jb)
    1429             : {
    1430             :     JsonbValue *prev;
    1431             :     JsonPathBool res;
    1432             : 
    1433       20052 :     prev = cxt->current;
    1434       20052 :     cxt->current = jb;
    1435       20052 :     res = executeBoolItem(cxt, jsp, jb, false);
    1436       19950 :     cxt->current = prev;
    1437             : 
    1438       19950 :     return res;
    1439             : }
    1440             : 
    1441             : /*
    1442             :  * Implementation of several jsonpath nodes:
    1443             :  *  - jpiAny (.** accessor),
    1444             :  *  - jpiAnyKey (.* accessor),
    1445             :  *  - jpiAnyArray ([*] accessor)
    1446             :  */
    1447             : static JsonPathExecResult
    1448        3450 : executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
    1449             :                JsonValueList *found, uint32 level, uint32 first, uint32 last,
    1450             :                bool ignoreStructuralErrors, bool unwrapNext)
    1451             : {
    1452        3450 :     JsonPathExecResult res = jperNotFound;
    1453             :     JsonbIterator *it;
    1454             :     int32       r;
    1455             :     JsonbValue  v;
    1456             : 
    1457        3450 :     check_stack_depth();
    1458             : 
    1459        3450 :     if (level > last)
    1460          30 :         return res;
    1461             : 
    1462        3420 :     it = JsonbIteratorInit(jbc);
    1463             : 
    1464             :     /*
    1465             :      * Recursively iterate over jsonb objects/arrays
    1466             :      */
    1467       18210 :     while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
    1468             :     {
    1469       15384 :         if (r == WJB_KEY)
    1470             :         {
    1471         630 :             r = JsonbIteratorNext(&it, &v, true);
    1472             :             Assert(r == WJB_VALUE);
    1473             :         }
    1474             : 
    1475       15384 :         if (r == WJB_VALUE || r == WJB_ELEM)
    1476             :         {
    1477             : 
    1478        9138 :             if (level >= first ||
    1479          12 :                 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
    1480          12 :                  v.type != jbvBinary))  /* leaves only requested */
    1481             :             {
    1482             :                 /* check expression */
    1483        9078 :                 if (jsp)
    1484             :                 {
    1485        5592 :                     if (ignoreStructuralErrors)
    1486             :                     {
    1487             :                         bool        savedIgnoreStructuralErrors;
    1488             : 
    1489         348 :                         savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
    1490         348 :                         cxt->ignoreStructuralErrors = true;
    1491         348 :                         res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
    1492         348 :                         cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
    1493             :                     }
    1494             :                     else
    1495        5244 :                         res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
    1496             : 
    1497        5466 :                     if (jperIsError(res))
    1498          66 :                         break;
    1499             : 
    1500        5400 :                     if (res == jperOk && !found)
    1501         342 :                         break;
    1502             :                 }
    1503        3486 :                 else if (found)
    1504        3462 :                     JsonValueListAppend(found, copyJsonbValue(&v));
    1505             :                 else
    1506          24 :                     return jperOk;
    1507             :             }
    1508             : 
    1509        8580 :             if (level < last && v.type == jbvBinary)
    1510             :             {
    1511         186 :                 res = executeAnyItem
    1512             :                     (cxt, jsp, v.val.binary.data, found,
    1513             :                      level + 1, first, last,
    1514             :                      ignoreStructuralErrors, unwrapNext);
    1515             : 
    1516         186 :                 if (jperIsError(res))
    1517           0 :                     break;
    1518             : 
    1519         186 :                 if (res == jperOk && found == NULL)
    1520          36 :                     break;
    1521             :             }
    1522             :         }
    1523             :     }
    1524             : 
    1525        3270 :     return res;
    1526             : }
    1527             : 
    1528             : /*
    1529             :  * Execute unary or binary predicate.
    1530             :  *
    1531             :  * Predicates have existence semantics, because their operands are item
    1532             :  * sequences.  Pairs of items from the left and right operand's sequences are
    1533             :  * checked.  TRUE returned only if any pair satisfying the condition is found.
    1534             :  * In strict mode, even if the desired pair has already been found, all pairs
    1535             :  * still need to be examined to check the absence of errors.  If any error
    1536             :  * occurs, UNKNOWN (analogous to SQL NULL) is returned.
    1537             :  */
    1538             : static JsonPathBool
    1539       57150 : executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
    1540             :                  JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
    1541             :                  bool unwrapRightArg, JsonPathPredicateCallback exec,
    1542             :                  void *param)
    1543             : {
    1544             :     JsonPathExecResult res;
    1545             :     JsonValueListIterator lseqit;
    1546       57150 :     JsonValueList lseq = {0};
    1547       57150 :     JsonValueList rseq = {0};
    1548             :     JsonbValue *lval;
    1549       57150 :     bool        error = false;
    1550       57150 :     bool        found = false;
    1551             : 
    1552             :     /* Left argument is always auto-unwrapped. */
    1553       57150 :     res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
    1554       57150 :     if (jperIsError(res))
    1555          18 :         return jpbUnknown;
    1556             : 
    1557       57132 :     if (rarg)
    1558             :     {
    1559             :         /* Right argument is conditionally auto-unwrapped. */
    1560       56736 :         res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
    1561             :                                                 unwrapRightArg, &rseq);
    1562       56724 :         if (jperIsError(res))
    1563          54 :             return jpbUnknown;
    1564             :     }
    1565             : 
    1566       57066 :     JsonValueListInitIterator(&lseq, &lseqit);
    1567       76176 :     while ((lval = JsonValueListNext(&lseq, &lseqit)))
    1568             :     {
    1569             :         JsonValueListIterator rseqit;
    1570             :         JsonbValue *rval;
    1571       24834 :         bool        first = true;
    1572             : 
    1573       24834 :         JsonValueListInitIterator(&rseq, &rseqit);
    1574       24834 :         if (rarg)
    1575       24438 :             rval = JsonValueListNext(&rseq, &rseqit);
    1576             :         else
    1577         396 :             rval = NULL;
    1578             : 
    1579             :         /* Loop over right arg sequence or do single pass otherwise */
    1580       39000 :         while (rarg ? (rval != NULL) : first)
    1581             :         {
    1582       19890 :             JsonPathBool res = exec(pred, lval, rval, param);
    1583             : 
    1584       19800 :             if (res == jpbUnknown)
    1585             :             {
    1586         690 :                 if (jspStrictAbsenseOfErrors(cxt))
    1587        5634 :                     return jpbUnknown;
    1588             : 
    1589         666 :                 error = true;
    1590             :             }
    1591       19110 :             else if (res == jpbTrue)
    1592             :             {
    1593        6150 :                 if (!jspStrictAbsenseOfErrors(cxt))
    1594        5610 :                     return jpbTrue;
    1595             : 
    1596         540 :                 found = true;
    1597             :             }
    1598             : 
    1599       14166 :             first = false;
    1600       14166 :             if (rarg)
    1601       13872 :                 rval = JsonValueListNext(&rseq, &rseqit);
    1602             :         }
    1603             :     }
    1604             : 
    1605       51342 :     if (found)                  /* possible only in strict mode */
    1606         438 :         return jpbTrue;
    1607             : 
    1608       50904 :     if (error)                  /* possible only in lax mode */
    1609         636 :         return jpbUnknown;
    1610             : 
    1611       50268 :     return jpbFalse;
    1612             : }
    1613             : 
    1614             : /*
    1615             :  * Execute binary arithmetic expression on singleton numeric operands.
    1616             :  * Array operands are automatically unwrapped in lax mode.
    1617             :  */
    1618             : static JsonPathExecResult
    1619         342 : executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1620             :                         JsonbValue *jb, BinaryArithmFunc func,
    1621             :                         JsonValueList *found)
    1622             : {
    1623             :     JsonPathExecResult jper;
    1624             :     JsonPathItem elem;
    1625         342 :     JsonValueList lseq = {0};
    1626         342 :     JsonValueList rseq = {0};
    1627             :     JsonbValue *lval;
    1628             :     JsonbValue *rval;
    1629             :     Numeric     res;
    1630             : 
    1631         342 :     jspGetLeftArg(jsp, &elem);
    1632             : 
    1633             :     /*
    1634             :      * XXX: By standard only operands of multiplicative expressions are
    1635             :      * unwrapped.  We extend it to other binary arithmetic expressions too.
    1636             :      */
    1637         342 :     jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
    1638         336 :     if (jperIsError(jper))
    1639           0 :         return jper;
    1640             : 
    1641         336 :     jspGetRightArg(jsp, &elem);
    1642             : 
    1643         336 :     jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
    1644         330 :     if (jperIsError(jper))
    1645           0 :         return jper;
    1646             : 
    1647         594 :     if (JsonValueListLength(&lseq) != 1 ||
    1648         264 :         !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
    1649          66 :         RETURN_ERROR(ereport(ERROR,
    1650             :                              (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
    1651             :                               errmsg("left operand of jsonpath operator %s is not a single numeric value",
    1652             :                                      jspOperationName(jsp->type)))));
    1653             : 
    1654         498 :     if (JsonValueListLength(&rseq) != 1 ||
    1655         234 :         !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
    1656          54 :         RETURN_ERROR(ereport(ERROR,
    1657             :                              (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
    1658             :                               errmsg("right operand of jsonpath operator %s is not a single numeric value",
    1659             :                                      jspOperationName(jsp->type)))));
    1660             : 
    1661         210 :     if (jspThrowErrors(cxt))
    1662             :     {
    1663          78 :         res = func(lval->val.numeric, rval->val.numeric, NULL);
    1664             :     }
    1665             :     else
    1666             :     {
    1667         132 :         bool        error = false;
    1668             : 
    1669         132 :         res = func(lval->val.numeric, rval->val.numeric, &error);
    1670             : 
    1671         132 :         if (error)
    1672          12 :             return jperError;
    1673             :     }
    1674             : 
    1675         174 :     if (!jspGetNext(jsp, &elem) && !found)
    1676           6 :         return jperOk;
    1677             : 
    1678         168 :     lval = palloc(sizeof(*lval));
    1679         168 :     lval->type = jbvNumeric;
    1680         168 :     lval->val.numeric = res;
    1681             : 
    1682         168 :     return executeNextItem(cxt, jsp, &elem, lval, found, false);
    1683             : }
    1684             : 
    1685             : /*
    1686             :  * Execute unary arithmetic expression for each numeric item in its operand's
    1687             :  * sequence.  Array operand is automatically unwrapped in lax mode.
    1688             :  */
    1689             : static JsonPathExecResult
    1690         186 : executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1691             :                        JsonbValue *jb, PGFunction func, JsonValueList *found)
    1692             : {
    1693             :     JsonPathExecResult jper;
    1694             :     JsonPathExecResult jper2;
    1695             :     JsonPathItem elem;
    1696         186 :     JsonValueList seq = {0};
    1697             :     JsonValueListIterator it;
    1698             :     JsonbValue *val;
    1699             :     bool        hasNext;
    1700             : 
    1701         186 :     jspGetArg(jsp, &elem);
    1702         186 :     jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
    1703             : 
    1704         180 :     if (jperIsError(jper))
    1705           0 :         return jper;
    1706             : 
    1707         180 :     jper = jperNotFound;
    1708             : 
    1709         180 :     hasNext = jspGetNext(jsp, &elem);
    1710             : 
    1711         180 :     JsonValueListInitIterator(&seq, &it);
    1712         324 :     while ((val = JsonValueListNext(&seq, &it)))
    1713             :     {
    1714         192 :         if ((val = getScalar(val, jbvNumeric)))
    1715             :         {
    1716         150 :             if (!found && !hasNext)
    1717          12 :                 return jperOk;
    1718             :         }
    1719             :         else
    1720             :         {
    1721          42 :             if (!found && !hasNext)
    1722           6 :                 continue;       /* skip non-numerics processing */
    1723             : 
    1724          36 :             RETURN_ERROR(ereport(ERROR,
    1725             :                                  (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
    1726             :                                   errmsg("operand of unary jsonpath operator %s is not a numeric value",
    1727             :                                          jspOperationName(jsp->type)))));
    1728             :         }
    1729             : 
    1730         138 :         if (func)
    1731          84 :             val->val.numeric =
    1732          84 :                 DatumGetNumeric(DirectFunctionCall1(func,
    1733             :                                                     NumericGetDatum(val->val.numeric)));
    1734             : 
    1735         138 :         jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
    1736             : 
    1737         138 :         if (jperIsError(jper2))
    1738           0 :             return jper2;
    1739             : 
    1740         138 :         if (jper2 == jperOk)
    1741             :         {
    1742         138 :             if (!found)
    1743           0 :                 return jperOk;
    1744         138 :             jper = jperOk;
    1745             :         }
    1746             :     }
    1747             : 
    1748         132 :     return jper;
    1749             : }
    1750             : 
    1751             : /*
    1752             :  * STARTS_WITH predicate callback.
    1753             :  *
    1754             :  * Check if the 'whole' string starts from 'initial' string.
    1755             :  */
    1756             : static JsonPathBool
    1757         174 : executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
    1758             :                   void *param)
    1759             : {
    1760         174 :     if (!(whole = getScalar(whole, jbvString)))
    1761          48 :         return jpbUnknown;      /* error */
    1762             : 
    1763         126 :     if (!(initial = getScalar(initial, jbvString)))
    1764           0 :         return jpbUnknown;      /* error */
    1765             : 
    1766         126 :     if (whole->val.string.len >= initial->val.string.len &&
    1767          90 :         !memcmp(whole->val.string.val,
    1768          90 :                 initial->val.string.val,
    1769          90 :                 initial->val.string.len))
    1770          54 :         return jpbTrue;
    1771             : 
    1772          72 :     return jpbFalse;
    1773             : }
    1774             : 
    1775             : /*
    1776             :  * LIKE_REGEX predicate callback.
    1777             :  *
    1778             :  * Check if the string matches regex pattern.
    1779             :  */
    1780             : static JsonPathBool
    1781         396 : executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
    1782             :                  void *param)
    1783             : {
    1784         396 :     JsonLikeRegexContext *cxt = param;
    1785             : 
    1786         396 :     if (!(str = getScalar(str, jbvString)))
    1787         120 :         return jpbUnknown;
    1788             : 
    1789             :     /* Cache regex text and converted flags. */
    1790         276 :     if (!cxt->regex)
    1791             :     {
    1792         276 :         cxt->regex =
    1793         276 :             cstring_to_text_with_len(jsp->content.like_regex.pattern,
    1794             :                                      jsp->content.like_regex.patternlen);
    1795         276 :         cxt->cflags = jspConvertRegexFlags(jsp->content.like_regex.flags);
    1796             :     }
    1797             : 
    1798         276 :     if (RE_compile_and_execute(cxt->regex, str->val.string.val,
    1799             :                                str->val.string.len,
    1800             :                                cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
    1801         102 :         return jpbTrue;
    1802             : 
    1803         174 :     return jpbFalse;
    1804             : }
    1805             : 
    1806             : /*
    1807             :  * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
    1808             :  * user function 'func'.
    1809             :  */
    1810             : static JsonPathExecResult
    1811         258 : executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1812             :                          JsonbValue *jb, bool unwrap, PGFunction func,
    1813             :                          JsonValueList *found)
    1814             : {
    1815             :     JsonPathItem next;
    1816             :     Datum       datum;
    1817             : 
    1818         258 :     if (unwrap && JsonbType(jb) == jbvArray)
    1819           0 :         return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1820             : 
    1821         258 :     if (!(jb = getScalar(jb, jbvNumeric)))
    1822          36 :         RETURN_ERROR(ereport(ERROR,
    1823             :                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1824             :                               errmsg("jsonpath item method .%s() can only be applied to a numeric value",
    1825             :                                      jspOperationName(jsp->type)))));
    1826             : 
    1827         222 :     datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
    1828             : 
    1829         222 :     if (!jspGetNext(jsp, &next) && !found)
    1830           0 :         return jperOk;
    1831             : 
    1832         222 :     jb = palloc(sizeof(*jb));
    1833         222 :     jb->type = jbvNumeric;
    1834         222 :     jb->val.numeric = DatumGetNumeric(datum);
    1835             : 
    1836         222 :     return executeNextItem(cxt, jsp, &next, jb, found, false);
    1837             : }
    1838             : 
    1839             : /*
    1840             :  * Implementation of the .datetime() method.
    1841             :  *
    1842             :  * Converts a string into a date/time value. The actual type is determined at run time.
    1843             :  * If an argument is provided, this argument is used as a template string.
    1844             :  * Otherwise, the first fitting ISO format is selected.
    1845             :  */
    1846             : static JsonPathExecResult
    1847        3216 : executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1848             :                       JsonbValue *jb, JsonValueList *found)
    1849             : {
    1850             :     JsonbValue  jbvbuf;
    1851             :     Datum       value;
    1852             :     text       *datetime;
    1853             :     Oid         collid;
    1854             :     Oid         typid;
    1855        3216 :     int32       typmod = -1;
    1856        3216 :     int         tz = 0;
    1857             :     bool        hasNext;
    1858        3216 :     JsonPathExecResult res = jperNotFound;
    1859             :     JsonPathItem elem;
    1860             : 
    1861        3216 :     if (!(jb = getScalar(jb, jbvString)))
    1862          30 :         RETURN_ERROR(ereport(ERROR,
    1863             :                              (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    1864             :                               errmsg("jsonpath item method .%s() can only be applied to a string",
    1865             :                                      jspOperationName(jsp->type)))));
    1866             : 
    1867        3186 :     datetime = cstring_to_text_with_len(jb->val.string.val,
    1868             :                                         jb->val.string.len);
    1869             : 
    1870             :     /*
    1871             :      * At some point we might wish to have callers supply the collation to
    1872             :      * use, but right now it's unclear that they'd be able to do better than
    1873             :      * DEFAULT_COLLATION_OID anyway.
    1874             :      */
    1875        3186 :     collid = DEFAULT_COLLATION_OID;
    1876             : 
    1877        3186 :     if (jsp->content.arg)
    1878             :     {
    1879             :         text       *template;
    1880             :         char       *template_str;
    1881             :         int         template_len;
    1882        1650 :         bool        have_error = false;
    1883             : 
    1884        1650 :         jspGetArg(jsp, &elem);
    1885             : 
    1886        1650 :         if (elem.type != jpiString)
    1887           0 :             elog(ERROR, "invalid jsonpath item type for .datetime() argument");
    1888             : 
    1889        1650 :         template_str = jspGetString(&elem, &template_len);
    1890             : 
    1891        1650 :         template = cstring_to_text_with_len(template_str,
    1892             :                                             template_len);
    1893             : 
    1894        1650 :         value = parse_datetime(datetime, template, collid, true,
    1895             :                                &typid, &typmod, &tz,
    1896        1650 :                                jspThrowErrors(cxt) ? NULL : &have_error);
    1897             : 
    1898        1590 :         if (have_error)
    1899           0 :             res = jperError;
    1900             :         else
    1901        1590 :             res = jperOk;
    1902             :     }
    1903             :     else
    1904             :     {
    1905             :         /*
    1906             :          * According to SQL/JSON standard enumerate ISO formats for: date,
    1907             :          * timetz, time, timestamptz, timestamp.
    1908             :          *
    1909             :          * We also support ISO 8601 for timestamps, because to_json[b]()
    1910             :          * functions use this format.
    1911             :          */
    1912             :         static const char *fmt_str[] =
    1913             :         {
    1914             :             "yyyy-mm-dd",
    1915             :             "HH24:MI:SSTZH:TZM",
    1916             :             "HH24:MI:SSTZH",
    1917             :             "HH24:MI:SS",
    1918             :             "yyyy-mm-dd HH24:MI:SSTZH:TZM",
    1919             :             "yyyy-mm-dd HH24:MI:SSTZH",
    1920             :             "yyyy-mm-dd HH24:MI:SS",
    1921             :             "yyyy-mm-dd\"T\"HH24:MI:SSTZH:TZM",
    1922             :             "yyyy-mm-dd\"T\"HH24:MI:SSTZH",
    1923             :             "yyyy-mm-dd\"T\"HH24:MI:SS"
    1924             :         };
    1925             : 
    1926             :         /* cache for format texts */
    1927             :         static text *fmt_txt[lengthof(fmt_str)] = {0};
    1928             :         int         i;
    1929             : 
    1930             :         /* loop until datetime format fits */
    1931        6828 :         for (i = 0; i < lengthof(fmt_str); i++)
    1932             :         {
    1933        6816 :             bool        have_error = false;
    1934             : 
    1935        6816 :             if (!fmt_txt[i])
    1936             :             {
    1937             :                 MemoryContext oldcxt =
    1938          60 :                 MemoryContextSwitchTo(TopMemoryContext);
    1939             : 
    1940          60 :                 fmt_txt[i] = cstring_to_text(fmt_str[i]);
    1941          60 :                 MemoryContextSwitchTo(oldcxt);
    1942             :             }
    1943             : 
    1944        6816 :             value = parse_datetime(datetime, fmt_txt[i], collid, true,
    1945             :                                    &typid, &typmod, &tz,
    1946             :                                    &have_error);
    1947             : 
    1948        6816 :             if (!have_error)
    1949             :             {
    1950        1524 :                 res = jperOk;
    1951        1524 :                 break;
    1952             :             }
    1953             :         }
    1954             : 
    1955        1536 :         if (res == jperNotFound)
    1956          12 :             RETURN_ERROR(ereport(ERROR,
    1957             :                                  (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    1958             :                                   errmsg("datetime format is not recognized: \"%s\"",
    1959             :                                          text_to_cstring(datetime)),
    1960             :                                   errhint("Use a datetime template argument to specify the input data format."))));
    1961             :     }
    1962             : 
    1963        3114 :     pfree(datetime);
    1964             : 
    1965        3114 :     if (jperIsError(res))
    1966           0 :         return res;
    1967             : 
    1968        3114 :     hasNext = jspGetNext(jsp, &elem);
    1969             : 
    1970        3114 :     if (!hasNext && !found)
    1971           6 :         return res;
    1972             : 
    1973        3108 :     jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
    1974             : 
    1975        3108 :     jb->type = jbvDatetime;
    1976        3108 :     jb->val.datetime.value = value;
    1977        3108 :     jb->val.datetime.typid = typid;
    1978        3108 :     jb->val.datetime.typmod = typmod;
    1979        3108 :     jb->val.datetime.tz = tz;
    1980             : 
    1981        3108 :     return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
    1982             : }
    1983             : 
    1984             : /*
    1985             :  * Implementation of .keyvalue() method.
    1986             :  *
    1987             :  * .keyvalue() method returns a sequence of object's key-value pairs in the
    1988             :  * following format: '{ "key": key, "value": value, "id": id }'.
    1989             :  *
    1990             :  * "id" field is an object identifier which is constructed from the two parts:
    1991             :  * base object id and its binary offset in base object's jsonb:
    1992             :  * id = 10000000000 * base_object_id + obj_offset_in_base_object
    1993             :  *
    1994             :  * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
    1995             :  * (maximal offset in jsonb).  Decimal multiplier is used here to improve the
    1996             :  * readability of identifiers.
    1997             :  *
    1998             :  * Base object is usually a root object of the path: context item '$' or path
    1999             :  * variable '$var', literals can't produce objects for now.  But if the path
    2000             :  * contains generated objects (.keyvalue() itself, for example), then they
    2001             :  * become base object for the subsequent .keyvalue().
    2002             :  *
    2003             :  * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
    2004             :  * of variables (see getJsonPathVariable()).  Ids for generated objects
    2005             :  * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
    2006             :  */
    2007             : static JsonPathExecResult
    2008          84 : executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    2009             :                       JsonbValue *jb, JsonValueList *found)
    2010             : {
    2011          84 :     JsonPathExecResult res = jperNotFound;
    2012             :     JsonPathItem next;
    2013             :     JsonbContainer *jbc;
    2014             :     JsonbValue  key;
    2015             :     JsonbValue  val;
    2016             :     JsonbValue  idval;
    2017             :     JsonbValue  keystr;
    2018             :     JsonbValue  valstr;
    2019             :     JsonbValue  idstr;
    2020             :     JsonbIterator *it;
    2021             :     JsonbIteratorToken tok;
    2022             :     int64       id;
    2023             :     bool        hasNext;
    2024             : 
    2025          84 :     if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
    2026          24 :         RETURN_ERROR(ereport(ERROR,
    2027             :                              (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
    2028             :                               errmsg("jsonpath item method .%s() can only be applied to an object",
    2029             :                                      jspOperationName(jsp->type)))));
    2030             : 
    2031          60 :     jbc = jb->val.binary.data;
    2032             : 
    2033          60 :     if (!JsonContainerSize(jbc))
    2034          18 :         return jperNotFound;    /* no key-value pairs */
    2035             : 
    2036          42 :     hasNext = jspGetNext(jsp, &next);
    2037             : 
    2038          42 :     keystr.type = jbvString;
    2039          42 :     keystr.val.string.val = "key";
    2040          42 :     keystr.val.string.len = 3;
    2041             : 
    2042          42 :     valstr.type = jbvString;
    2043          42 :     valstr.val.string.val = "value";
    2044          42 :     valstr.val.string.len = 5;
    2045             : 
    2046          42 :     idstr.type = jbvString;
    2047          42 :     idstr.val.string.val = "id";
    2048          42 :     idstr.val.string.len = 2;
    2049             : 
    2050             :     /* construct object id from its base object and offset inside that */
    2051          42 :     id = jb->type != jbvBinary ? 0 :
    2052          42 :         (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
    2053          42 :     id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
    2054             : 
    2055          42 :     idval.type = jbvNumeric;
    2056          42 :     idval.val.numeric = int64_to_numeric(id);
    2057             : 
    2058          42 :     it = JsonbIteratorInit(jbc);
    2059             : 
    2060         168 :     while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
    2061             :     {
    2062             :         JsonBaseObjectInfo baseObject;
    2063             :         JsonbValue  obj;
    2064             :         JsonbParseState *ps;
    2065             :         JsonbValue *keyval;
    2066             :         Jsonb      *jsonb;
    2067             : 
    2068         138 :         if (tok != WJB_KEY)
    2069          72 :             continue;
    2070             : 
    2071          66 :         res = jperOk;
    2072             : 
    2073          66 :         if (!hasNext && !found)
    2074          12 :             break;
    2075             : 
    2076          60 :         tok = JsonbIteratorNext(&it, &val, true);
    2077             :         Assert(tok == WJB_VALUE);
    2078             : 
    2079          60 :         ps = NULL;
    2080          60 :         pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
    2081             : 
    2082          60 :         pushJsonbValue(&ps, WJB_KEY, &keystr);
    2083          60 :         pushJsonbValue(&ps, WJB_VALUE, &key);
    2084             : 
    2085          60 :         pushJsonbValue(&ps, WJB_KEY, &valstr);
    2086          60 :         pushJsonbValue(&ps, WJB_VALUE, &val);
    2087             : 
    2088          60 :         pushJsonbValue(&ps, WJB_KEY, &idstr);
    2089          60 :         pushJsonbValue(&ps, WJB_VALUE, &idval);
    2090             : 
    2091          60 :         keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
    2092             : 
    2093          60 :         jsonb = JsonbValueToJsonb(keyval);
    2094             : 
    2095          60 :         JsonbInitBinary(&obj, jsonb);
    2096             : 
    2097          60 :         baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
    2098             : 
    2099          60 :         res = executeNextItem(cxt, jsp, &next, &obj, found, true);
    2100             : 
    2101          60 :         cxt->baseObject = baseObject;
    2102             : 
    2103          60 :         if (jperIsError(res))
    2104           0 :             return res;
    2105             : 
    2106          60 :         if (res == jperOk && !found)
    2107           6 :             break;
    2108             :     }
    2109             : 
    2110          42 :     return res;
    2111             : }
    2112             : 
    2113             : /*
    2114             :  * Convert boolean execution status 'res' to a boolean JSON item and execute
    2115             :  * next jsonpath.
    2116             :  */
    2117             : static JsonPathExecResult
    2118      102174 : appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
    2119             :                  JsonValueList *found, JsonPathBool res)
    2120             : {
    2121             :     JsonPathItem next;
    2122             :     JsonbValue  jbv;
    2123             : 
    2124      102174 :     if (!jspGetNext(jsp, &next) && !found)
    2125          18 :         return jperOk;          /* found singleton boolean value */
    2126             : 
    2127      102156 :     if (res == jpbUnknown)
    2128             :     {
    2129          30 :         jbv.type = jbvNull;
    2130             :     }
    2131             :     else
    2132             :     {
    2133      102126 :         jbv.type = jbvBool;
    2134      102126 :         jbv.val.boolean = res == jpbTrue;
    2135             :     }
    2136             : 
    2137      102156 :     return executeNextItem(cxt, jsp, &next, &jbv, found, true);
    2138             : }
    2139             : 
    2140             : /*
    2141             :  * Convert jsonpath's scalar or variable node to actual jsonb value.
    2142             :  *
    2143             :  * If node is a variable then its id returned, otherwise 0 returned.
    2144             :  */
    2145             : static void
    2146       57792 : getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
    2147             :                 JsonbValue *value)
    2148             : {
    2149       57792 :     switch (item->type)
    2150             :     {
    2151        7554 :         case jpiNull:
    2152        7554 :             value->type = jbvNull;
    2153        7554 :             break;
    2154        1404 :         case jpiBool:
    2155        1404 :             value->type = jbvBool;
    2156        1404 :             value->val.boolean = jspGetBool(item);
    2157        1404 :             break;
    2158       19410 :         case jpiNumeric:
    2159       19410 :             value->type = jbvNumeric;
    2160       19410 :             value->val.numeric = jspGetNumeric(item);
    2161       19410 :             break;
    2162       21954 :         case jpiString:
    2163       21954 :             value->type = jbvString;
    2164       43908 :             value->val.string.val = jspGetString(item,
    2165       21954 :                                                  &value->val.string.len);
    2166       21954 :             break;
    2167        7470 :         case jpiVariable:
    2168        7470 :             getJsonPathVariable(cxt, item, value);
    2169        7458 :             return;
    2170           0 :         default:
    2171           0 :             elog(ERROR, "unexpected jsonpath item type");
    2172             :     }
    2173             : }
    2174             : 
    2175             : /*
    2176             :  * Get the value of variable passed to jsonpath executor
    2177             :  */
    2178             : static void
    2179        7470 : getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
    2180             :                     JsonbValue *value)
    2181             : {
    2182             :     char       *varName;
    2183             :     int         varNameLength;
    2184             :     JsonbValue  baseObject;
    2185             :     int         baseObjectId;
    2186             : 
    2187             :     Assert(variable->type == jpiVariable);
    2188        7470 :     varName = jspGetString(variable, &varNameLength);
    2189             : 
    2190       14934 :     if (!cxt->vars ||
    2191        7464 :         (baseObjectId = cxt->getVar(cxt->vars, varName, varNameLength, value,
    2192             :                                     &baseObject)) < 0)
    2193          12 :         ereport(ERROR,
    2194             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    2195             :                  errmsg("could not find jsonpath variable \"%s\"",
    2196             :                         pnstrdup(varName, varNameLength))));
    2197             : 
    2198        7458 :     if (baseObjectId > 0)
    2199        7458 :         setBaseObject(cxt, &baseObject, baseObjectId);
    2200        7458 : }
    2201             : 
    2202             : static int
    2203      195396 : getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, int varNameLength,
    2204             :                              JsonbValue *value, JsonbValue *baseObject)
    2205             : {
    2206      195396 :     Jsonb      *vars = varsJsonb;
    2207             :     JsonbValue  tmp;
    2208             :     JsonbValue *v;
    2209             : 
    2210      195396 :     if (!varName)
    2211             :     {
    2212      190170 :         if (vars && !JsonContainerIsObject(&vars->root))
    2213             :         {
    2214          12 :             ereport(ERROR,
    2215             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    2216             :                      errmsg("\"vars\" argument is not an object"),
    2217             :                      errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
    2218             :         }
    2219             : 
    2220      190158 :         return vars ? 1 : 0;    /* count of base objects */
    2221             :     }
    2222             : 
    2223        5226 :     tmp.type = jbvString;
    2224        5226 :     tmp.val.string.val = varName;
    2225        5226 :     tmp.val.string.len = varNameLength;
    2226             : 
    2227        5226 :     v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
    2228             : 
    2229        5226 :     if (!v)
    2230           6 :         return -1;
    2231             : 
    2232        5220 :     *value = *v;
    2233        5220 :     pfree(v);
    2234             : 
    2235        5220 :     JsonbInitBinary(baseObject, vars);
    2236        5220 :     return 1;
    2237             : }
    2238             : 
    2239             : /**************** Support functions for JsonPath execution *****************/
    2240             : 
    2241             : /*
    2242             :  * Returns the size of an array item, or -1 if item is not an array.
    2243             :  */
    2244             : static int
    2245         390 : JsonbArraySize(JsonbValue *jb)
    2246             : {
    2247             :     Assert(jb->type != jbvArray);
    2248             : 
    2249         390 :     if (jb->type == jbvBinary)
    2250             :     {
    2251         312 :         JsonbContainer *jbc = jb->val.binary.data;
    2252             : 
    2253         312 :         if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
    2254         300 :             return JsonContainerSize(jbc);
    2255             :     }
    2256             : 
    2257          90 :     return -1;
    2258             : }
    2259             : 
    2260             : /* Comparison predicate callback. */
    2261             : static JsonPathBool
    2262       19320 : executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
    2263             : {
    2264       19320 :     JsonPathExecContext *cxt = (JsonPathExecContext *) p;
    2265             : 
    2266       19320 :     return compareItems(cmp->type, lv, rv, cxt->useTz);
    2267             : }
    2268             : 
    2269             : /*
    2270             :  * Perform per-byte comparison of two strings.
    2271             :  */
    2272             : static int
    2273        3456 : binaryCompareStrings(const char *s1, int len1,
    2274             :                      const char *s2, int len2)
    2275             : {
    2276             :     int         cmp;
    2277             : 
    2278        3456 :     cmp = memcmp(s1, s2, Min(len1, len2));
    2279             : 
    2280        3456 :     if (cmp != 0)
    2281        1968 :         return cmp;
    2282             : 
    2283        1488 :     if (len1 == len2)
    2284         288 :         return 0;
    2285             : 
    2286        1200 :     return len1 < len2 ? -1 : 1;
    2287             : }
    2288             : 
    2289             : /*
    2290             :  * Compare two strings in the current server encoding using Unicode codepoint
    2291             :  * collation.
    2292             :  */
    2293             : static int
    2294        3456 : compareStrings(const char *mbstr1, int mblen1,
    2295             :                const char *mbstr2, int mblen2)
    2296             : {
    2297        6912 :     if (GetDatabaseEncoding() == PG_SQL_ASCII ||
    2298        3456 :         GetDatabaseEncoding() == PG_UTF8)
    2299             :     {
    2300             :         /*
    2301             :          * It's known property of UTF-8 strings that their per-byte comparison
    2302             :          * result matches codepoints comparison result.  ASCII can be
    2303             :          * considered as special case of UTF-8.
    2304             :          */
    2305        3456 :         return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
    2306             :     }
    2307             :     else
    2308             :     {
    2309             :         char       *utf8str1,
    2310             :                    *utf8str2;
    2311             :         int         cmp,
    2312             :                     utf8len1,
    2313             :                     utf8len2;
    2314             : 
    2315             :         /*
    2316             :          * We have to convert other encodings to UTF-8 first, then compare.
    2317             :          * Input strings may be not null-terminated and pg_server_to_any() may
    2318             :          * return them "as is".  So, use strlen() only if there is real
    2319             :          * conversion.
    2320             :          */
    2321           0 :         utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
    2322           0 :         utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
    2323           0 :         utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
    2324           0 :         utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
    2325             : 
    2326           0 :         cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
    2327             : 
    2328             :         /*
    2329             :          * If pg_server_to_any() did no real conversion, then we actually
    2330             :          * compared original strings.  So, we already done.
    2331             :          */
    2332           0 :         if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
    2333           0 :             return cmp;
    2334             : 
    2335             :         /* Free memory if needed */
    2336           0 :         if (mbstr1 != utf8str1)
    2337           0 :             pfree(utf8str1);
    2338           0 :         if (mbstr2 != utf8str2)
    2339           0 :             pfree(utf8str2);
    2340             : 
    2341             :         /*
    2342             :          * When all Unicode codepoints are equal, return result of binary
    2343             :          * comparison.  In some edge cases, same characters may have different
    2344             :          * representations in encoding.  Then our behavior could diverge from
    2345             :          * standard.  However, that allow us to do simple binary comparison
    2346             :          * for "==" operator, which is performance critical in typical cases.
    2347             :          * In future to implement strict standard conformance, we can do
    2348             :          * normalization of input JSON strings.
    2349             :          */
    2350           0 :         if (cmp == 0)
    2351           0 :             return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
    2352             :         else
    2353           0 :             return cmp;
    2354             :     }
    2355             : }
    2356             : 
    2357             : /*
    2358             :  * Compare two SQL/JSON items using comparison operation 'op'.
    2359             :  */
    2360             : static JsonPathBool
    2361       19320 : compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
    2362             : {
    2363             :     int         cmp;
    2364             :     bool        res;
    2365             : 
    2366       19320 :     if (jb1->type != jb2->type)
    2367             :     {
    2368        3132 :         if (jb1->type == jbvNull || jb2->type == jbvNull)
    2369             : 
    2370             :             /*
    2371             :              * Equality and order comparison of nulls to non-nulls returns
    2372             :              * always false, but inequality comparison returns true.
    2373             :              */
    2374        2874 :             return op == jpiNotEqual ? jpbTrue : jpbFalse;
    2375             : 
    2376             :         /* Non-null items of different types are not comparable. */
    2377         258 :         return jpbUnknown;
    2378             :     }
    2379             : 
    2380       16188 :     switch (jb1->type)
    2381             :     {
    2382         186 :         case jbvNull:
    2383         186 :             cmp = 0;
    2384         186 :             break;
    2385         870 :         case jbvBool:
    2386        1266 :             cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
    2387         396 :                 jb1->val.boolean ? 1 : -1;
    2388         870 :             break;
    2389        3762 :         case jbvNumeric:
    2390        3762 :             cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
    2391        3762 :             break;
    2392        9930 :         case jbvString:
    2393        9930 :             if (op == jpiEqual)
    2394        6474 :                 return jb1->val.string.len != jb2->val.string.len ||
    2395        3582 :                     memcmp(jb1->val.string.val,
    2396        3582 :                            jb2->val.string.val,
    2397        6474 :                            jb1->val.string.len) ? jpbFalse : jpbTrue;
    2398             : 
    2399        3456 :             cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
    2400        3456 :                                  jb2->val.string.val, jb2->val.string.len);
    2401        3456 :             break;
    2402        1428 :         case jbvDatetime:
    2403             :             {
    2404             :                 bool        cast_error;
    2405             : 
    2406        1428 :                 cmp = compareDatetime(jb1->val.datetime.value,
    2407             :                                       jb1->val.datetime.typid,
    2408             :                                       jb2->val.datetime.value,
    2409             :                                       jb2->val.datetime.typid,
    2410             :                                       useTz,
    2411             :                                       &cast_error);
    2412             : 
    2413        1338 :                 if (cast_error)
    2414         252 :                     return jpbUnknown;
    2415             :             }
    2416        1086 :             break;
    2417             : 
    2418          12 :         case jbvBinary:
    2419             :         case jbvArray:
    2420             :         case jbvObject:
    2421          12 :             return jpbUnknown;  /* non-scalars are not comparable */
    2422             : 
    2423           0 :         default:
    2424           0 :             elog(ERROR, "invalid jsonb value type %d", jb1->type);
    2425             :     }
    2426             : 
    2427        9360 :     switch (op)
    2428             :     {
    2429        1686 :         case jpiEqual:
    2430        1686 :             res = (cmp == 0);
    2431        1686 :             break;
    2432           6 :         case jpiNotEqual:
    2433           6 :             res = (cmp != 0);
    2434           6 :             break;
    2435        2244 :         case jpiLess:
    2436        2244 :             res = (cmp < 0);
    2437        2244 :             break;
    2438        1506 :         case jpiGreater:
    2439        1506 :             res = (cmp > 0);
    2440        1506 :             break;
    2441        1446 :         case jpiLessOrEqual:
    2442        1446 :             res = (cmp <= 0);
    2443        1446 :             break;
    2444        2472 :         case jpiGreaterOrEqual:
    2445        2472 :             res = (cmp >= 0);
    2446        2472 :             break;
    2447           0 :         default:
    2448           0 :             elog(ERROR, "unrecognized jsonpath operation: %d", op);
    2449             :             return jpbUnknown;
    2450             :     }
    2451             : 
    2452        9360 :     return res ? jpbTrue : jpbFalse;
    2453             : }
    2454             : 
    2455             : /* Compare two numerics */
    2456             : static int
    2457        3762 : compareNumeric(Numeric a, Numeric b)
    2458             : {
    2459        3762 :     return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
    2460             :                                              NumericGetDatum(a),
    2461             :                                              NumericGetDatum(b)));
    2462             : }
    2463             : 
    2464             : static JsonbValue *
    2465      719610 : copyJsonbValue(JsonbValue *src)
    2466             : {
    2467      719610 :     JsonbValue *dst = palloc(sizeof(*dst));
    2468             : 
    2469      719610 :     *dst = *src;
    2470             : 
    2471      719610 :     return dst;
    2472             : }
    2473             : 
    2474             : /*
    2475             :  * Execute array subscript expression and convert resulting numeric item to
    2476             :  * the integer type with truncation.
    2477             :  */
    2478             : static JsonPathExecResult
    2479         360 : getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
    2480             :               int32 *index)
    2481             : {
    2482             :     JsonbValue *jbv;
    2483         360 :     JsonValueList found = {0};
    2484         360 :     JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
    2485             :     Datum       numeric_index;
    2486         354 :     bool        have_error = false;
    2487             : 
    2488         354 :     if (jperIsError(res))
    2489           0 :         return res;
    2490             : 
    2491         696 :     if (JsonValueListLength(&found) != 1 ||
    2492         342 :         !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
    2493          24 :         RETURN_ERROR(ereport(ERROR,
    2494             :                              (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
    2495             :                               errmsg("jsonpath array subscript is not a single numeric value"))));
    2496             : 
    2497         330 :     numeric_index = DirectFunctionCall2(numeric_trunc,
    2498             :                                         NumericGetDatum(jbv->val.numeric),
    2499             :                                         Int32GetDatum(0));
    2500             : 
    2501         330 :     *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
    2502             :                                     &have_error);
    2503             : 
    2504         330 :     if (have_error)
    2505          24 :         RETURN_ERROR(ereport(ERROR,
    2506             :                              (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
    2507             :                               errmsg("jsonpath array subscript is out of integer range"))));
    2508             : 
    2509         306 :     return jperOk;
    2510             : }
    2511             : 
    2512             : /* Save base object and its id needed for the execution of .keyvalue(). */
    2513             : static JsonBaseObjectInfo
    2514      817002 : setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
    2515             : {
    2516      817002 :     JsonBaseObjectInfo baseObject = cxt->baseObject;
    2517             : 
    2518      817002 :     cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
    2519             :         (JsonbContainer *) jbv->val.binary.data;
    2520      817002 :     cxt->baseObject.id = id;
    2521             : 
    2522      817002 :     return baseObject;
    2523             : }
    2524             : 
    2525             : static void
    2526        1224 : JsonValueListClear(JsonValueList *jvl)
    2527             : {
    2528        1224 :     jvl->singleton = NULL;
    2529        1224 :     jvl->list = NULL;
    2530        1224 : }
    2531             : 
    2532             : static void
    2533      864522 : JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
    2534             : {
    2535      864522 :     if (jvl->singleton)
    2536             :     {
    2537        1866 :         jvl->list = list_make2(jvl->singleton, jbv);
    2538        1866 :         jvl->singleton = NULL;
    2539             :     }
    2540      862656 :     else if (!jvl->list)
    2541      860814 :         jvl->singleton = jbv;
    2542             :     else
    2543        1842 :         jvl->list = lappend(jvl->list, jbv);
    2544      864522 : }
    2545             : 
    2546             : static int
    2547      708708 : JsonValueListLength(const JsonValueList *jvl)
    2548             : {
    2549      708708 :     return jvl->singleton ? 1 : list_length(jvl->list);
    2550             : }
    2551             : 
    2552             : static bool
    2553          30 : JsonValueListIsEmpty(JsonValueList *jvl)
    2554             : {
    2555          30 :     return !jvl->singleton && list_length(jvl->list) <= 0;
    2556             : }
    2557             : 
    2558             : static JsonbValue *
    2559      708030 : JsonValueListHead(JsonValueList *jvl)
    2560             : {
    2561      708030 :     return jvl->singleton ? jvl->singleton : linitial(jvl->list);
    2562             : }
    2563             : 
    2564             : static List *
    2565        1410 : JsonValueListGetList(JsonValueList *jvl)
    2566             : {
    2567        1410 :     if (jvl->singleton)
    2568         828 :         return list_make1(jvl->singleton);
    2569             : 
    2570         582 :     return jvl->list;
    2571             : }
    2572             : 
    2573             : static void
    2574      197082 : JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
    2575             : {
    2576      197082 :     if (jvl->singleton)
    2577             :     {
    2578      118164 :         it->value = jvl->singleton;
    2579      118164 :         it->list = NIL;
    2580      118164 :         it->next = NULL;
    2581             :     }
    2582       78918 :     else if (jvl->list != NIL)
    2583             :     {
    2584        1536 :         it->value = (JsonbValue *) linitial(jvl->list);
    2585        1536 :         it->list = jvl->list;
    2586        1536 :         it->next = list_second_cell(jvl->list);
    2587             :     }
    2588             :     else
    2589             :     {
    2590       77382 :         it->value = NULL;
    2591       77382 :         it->list = NIL;
    2592       77382 :         it->next = NULL;
    2593             :     }
    2594      197082 : }
    2595             : 
    2596             : /*
    2597             :  * Get the next item from the sequence advancing iterator.
    2598             :  */
    2599             : static JsonbValue *
    2600      307572 : JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
    2601             : {
    2602      307572 :     JsonbValue *result = it->value;
    2603             : 
    2604      307572 :     if (it->next)
    2605             :     {
    2606        2952 :         it->value = lfirst(it->next);
    2607        2952 :         it->next = lnext(it->list, it->next);
    2608             :     }
    2609             :     else
    2610             :     {
    2611      304620 :         it->value = NULL;
    2612             :     }
    2613             : 
    2614      307572 :     return result;
    2615             : }
    2616             : 
    2617             : /*
    2618             :  * Initialize a binary JsonbValue with the given jsonb container.
    2619             :  */
    2620             : static JsonbValue *
    2621      197964 : JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
    2622             : {
    2623      197964 :     jbv->type = jbvBinary;
    2624      197964 :     jbv->val.binary.data = &jb->root;
    2625      197964 :     jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
    2626             : 
    2627      197964 :     return jbv;
    2628             : }
    2629             : 
    2630             : /*
    2631             :  * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
    2632             :  */
    2633             : static int
    2634      269850 : JsonbType(JsonbValue *jb)
    2635             : {
    2636      269850 :     int         type = jb->type;
    2637             : 
    2638      269850 :     if (jb->type == jbvBinary)
    2639             :     {
    2640      182484 :         JsonbContainer *jbc = (void *) jb->val.binary.data;
    2641             : 
    2642             :         /* Scalars should be always extracted during jsonpath execution. */
    2643             :         Assert(!JsonContainerIsScalar(jbc));
    2644             : 
    2645      182484 :         if (JsonContainerIsObject(jbc))
    2646      179298 :             type = jbvObject;
    2647        3186 :         else if (JsonContainerIsArray(jbc))
    2648        3186 :             type = jbvArray;
    2649             :         else
    2650           0 :             elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
    2651             :     }
    2652             : 
    2653      269850 :     return type;
    2654             : }
    2655             : 
    2656             : /* Get scalar of given type or NULL on type mismatch */
    2657             : static JsonbValue *
    2658        5202 : getScalar(JsonbValue *scalar, enum jbvType type)
    2659             : {
    2660             :     /* Scalars should be always extracted during jsonpath execution. */
    2661             :     Assert(scalar->type != jbvBinary ||
    2662             :            !JsonContainerIsScalar(scalar->val.binary.data));
    2663             : 
    2664        5202 :     return scalar->type == type ? scalar : NULL;
    2665             : }
    2666             : 
    2667             : /* Construct a JSON array from the item list */
    2668             : static JsonbValue *
    2669         480 : wrapItemsInArray(const JsonValueList *items)
    2670             : {
    2671         480 :     JsonbParseState *ps = NULL;
    2672             :     JsonValueListIterator it;
    2673             :     JsonbValue *jbv;
    2674             : 
    2675         480 :     pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
    2676             : 
    2677         480 :     JsonValueListInitIterator(items, &it);
    2678        1170 :     while ((jbv = JsonValueListNext(items, &it)))
    2679         690 :         pushJsonbValue(&ps, WJB_ELEM, jbv);
    2680             : 
    2681         480 :     return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
    2682             : }
    2683             : 
    2684             : /* Check if the timezone required for casting from type1 to type2 is used */
    2685             : static void
    2686         378 : checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
    2687             : {
    2688         378 :     if (!useTz)
    2689          90 :         ereport(ERROR,
    2690             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2691             :                  errmsg("cannot convert value from %s to %s without time zone usage",
    2692             :                         type1, type2),
    2693             :                  errhint("Use *_tz() function for time zone support.")));
    2694         288 : }
    2695             : 
    2696             : /* Convert time datum to timetz datum */
    2697             : static Datum
    2698         144 : castTimeToTimeTz(Datum time, bool useTz)
    2699             : {
    2700         144 :     checkTimezoneIsUsedForCast(useTz, "time", "timetz");
    2701             : 
    2702         108 :     return DirectFunctionCall1(time_timetz, time);
    2703             : }
    2704             : 
    2705             : /*
    2706             :  * Compare date to timestamp.
    2707             :  * Note that this doesn't involve any timezone considerations.
    2708             :  */
    2709             : static int
    2710         114 : cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
    2711             : {
    2712         114 :     return date_cmp_timestamp_internal(date1, ts2);
    2713             : }
    2714             : 
    2715             : /*
    2716             :  * Compare date to timestamptz.
    2717             :  */
    2718             : static int
    2719          90 : cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
    2720             : {
    2721          90 :     checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
    2722             : 
    2723          72 :     return date_cmp_timestamptz_internal(date1, tstz2);
    2724             : }
    2725             : 
    2726             : /*
    2727             :  * Compare timestamp to timestamptz.
    2728             :  */
    2729             : static int
    2730         144 : cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
    2731             : {
    2732         144 :     checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
    2733             : 
    2734         108 :     return timestamp_cmp_timestamptz_internal(ts1, tstz2);
    2735             : }
    2736             : 
    2737             : /*
    2738             :  * Cross-type comparison of two datetime SQL/JSON items.  If items are
    2739             :  * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
    2740             :  * If the cast requires timezone and it is not used, then explicit error is thrown.
    2741             :  */
    2742             : static int
    2743        1428 : compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
    2744             :                 bool useTz, bool *cast_error)
    2745             : {
    2746             :     PGFunction  cmpfunc;
    2747             : 
    2748        1428 :     *cast_error = false;
    2749             : 
    2750        1428 :     switch (typid1)
    2751             :     {
    2752         222 :         case DATEOID:
    2753             :             switch (typid2)
    2754             :             {
    2755         108 :                 case DATEOID:
    2756         108 :                     cmpfunc = date_cmp;
    2757             : 
    2758         108 :                     break;
    2759             : 
    2760          42 :                 case TIMESTAMPOID:
    2761          42 :                     return cmpDateToTimestamp(DatumGetDateADT(val1),
    2762             :                                               DatumGetTimestamp(val2),
    2763             :                                               useTz);
    2764             : 
    2765          36 :                 case TIMESTAMPTZOID:
    2766          36 :                     return cmpDateToTimestampTz(DatumGetDateADT(val1),
    2767             :                                                 DatumGetTimestampTz(val2),
    2768             :                                                 useTz);
    2769             : 
    2770          36 :                 case TIMEOID:
    2771             :                 case TIMETZOID:
    2772          36 :                     *cast_error = true; /* uncomparable types */
    2773          36 :                     return 0;
    2774             : 
    2775           0 :                 default:
    2776           0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    2777             :                          typid2);
    2778             :             }
    2779         108 :             break;
    2780             : 
    2781         252 :         case TIMEOID:
    2782             :             switch (typid2)
    2783             :             {
    2784         108 :                 case TIMEOID:
    2785         108 :                     cmpfunc = time_cmp;
    2786             : 
    2787         108 :                     break;
    2788             : 
    2789          72 :                 case TIMETZOID:
    2790          72 :                     val1 = castTimeToTimeTz(val1, useTz);
    2791          54 :                     cmpfunc = timetz_cmp;
    2792             : 
    2793          54 :                     break;
    2794             : 
    2795          72 :                 case DATEOID:
    2796             :                 case TIMESTAMPOID:
    2797             :                 case TIMESTAMPTZOID:
    2798          72 :                     *cast_error = true; /* uncomparable types */
    2799          72 :                     return 0;
    2800             : 
    2801           0 :                 default:
    2802           0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    2803             :                          typid2);
    2804             :             }
    2805         162 :             break;
    2806             : 
    2807         324 :         case TIMETZOID:
    2808             :             switch (typid2)
    2809             :             {
    2810          72 :                 case TIMEOID:
    2811          72 :                     val2 = castTimeToTimeTz(val2, useTz);
    2812          54 :                     cmpfunc = timetz_cmp;
    2813             : 
    2814          54 :                     break;
    2815             : 
    2816         180 :                 case TIMETZOID:
    2817         180 :                     cmpfunc = timetz_cmp;
    2818             : 
    2819         180 :                     break;
    2820             : 
    2821          72 :                 case DATEOID:
    2822             :                 case TIMESTAMPOID:
    2823             :                 case TIMESTAMPTZOID:
    2824          72 :                     *cast_error = true; /* uncomparable types */
    2825          72 :                     return 0;
    2826             : 
    2827           0 :                 default:
    2828           0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    2829             :                          typid2);
    2830             :             }
    2831         234 :             break;
    2832             : 
    2833         288 :         case TIMESTAMPOID:
    2834             :             switch (typid2)
    2835             :             {
    2836          72 :                 case DATEOID:
    2837          72 :                     return -cmpDateToTimestamp(DatumGetDateADT(val2),
    2838             :                                                DatumGetTimestamp(val1),
    2839             :                                                useTz);
    2840             : 
    2841         108 :                 case TIMESTAMPOID:
    2842         108 :                     cmpfunc = timestamp_cmp;
    2843             : 
    2844         108 :                     break;
    2845             : 
    2846          72 :                 case TIMESTAMPTZOID:
    2847          72 :                     return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
    2848             :                                                      DatumGetTimestampTz(val2),
    2849             :                                                      useTz);
    2850             : 
    2851          36 :                 case TIMEOID:
    2852             :                 case TIMETZOID:
    2853          36 :                     *cast_error = true; /* uncomparable types */
    2854          36 :                     return 0;
    2855             : 
    2856           0 :                 default:
    2857           0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    2858             :                          typid2);
    2859             :             }
    2860         108 :             break;
    2861             : 
    2862         342 :         case TIMESTAMPTZOID:
    2863             :             switch (typid2)
    2864             :             {
    2865          54 :                 case DATEOID:
    2866          54 :                     return -cmpDateToTimestampTz(DatumGetDateADT(val2),
    2867             :                                                  DatumGetTimestampTz(val1),
    2868             :                                                  useTz);
    2869             : 
    2870          72 :                 case TIMESTAMPOID:
    2871          72 :                     return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
    2872             :                                                       DatumGetTimestampTz(val1),
    2873             :                                                       useTz);
    2874             : 
    2875         180 :                 case TIMESTAMPTZOID:
    2876         180 :                     cmpfunc = timestamp_cmp;
    2877             : 
    2878         180 :                     break;
    2879             : 
    2880          36 :                 case TIMEOID:
    2881             :                 case TIMETZOID:
    2882          36 :                     *cast_error = true; /* uncomparable types */
    2883          36 :                     return 0;
    2884             : 
    2885           0 :                 default:
    2886           0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    2887             :                          typid2);
    2888             :             }
    2889         180 :             break;
    2890             : 
    2891           0 :         default:
    2892           0 :             elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
    2893             :     }
    2894             : 
    2895         792 :     if (*cast_error)
    2896           0 :         return 0;               /* cast error */
    2897             : 
    2898         792 :     return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
    2899             : }
    2900             : 
    2901             : /********************Interface to pgsql's executor***************************/
    2902             : 
    2903             : bool
    2904         546 : JsonPathExists(Datum jb, JsonPath *jp, List *vars, bool *error)
    2905             : {
    2906         546 :     JsonPathExecResult res = executeJsonPath(jp, vars, EvalJsonPathVar,
    2907         546 :                                              DatumGetJsonbP(jb), !error, NULL,
    2908             :                                              true);
    2909             : 
    2910             :     Assert(error || !jperIsError(res));
    2911             : 
    2912         540 :     if (error && jperIsError(res))
    2913         162 :         *error = true;
    2914             : 
    2915         540 :     return res == jperOk;
    2916             : }
    2917             : 
    2918             : Datum
    2919        2508 : JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
    2920             :               bool *error, List *vars)
    2921             : {
    2922             :     JsonbValue *first;
    2923             :     bool        wrap;
    2924        2508 :     JsonValueList found = {0};
    2925             :     JsonPathExecResult res PG_USED_FOR_ASSERTS_ONLY;
    2926             :     int         count;
    2927             : 
    2928        2508 :     res = executeJsonPath(jp, vars, EvalJsonPathVar, DatumGetJsonbP(jb), !error,
    2929             :                           &found, true);
    2930             : 
    2931             :     Assert(error || !jperIsError(res));
    2932             : 
    2933        2502 :     if (error && jperIsError(res))
    2934             :     {
    2935          30 :         *error = true;
    2936          30 :         *empty = false;
    2937          30 :         return (Datum) 0;
    2938             :     }
    2939             : 
    2940        2472 :     count = JsonValueListLength(&found);
    2941             : 
    2942        2472 :     first = count ? JsonValueListHead(&found) : NULL;
    2943             : 
    2944        2472 :     if (!first)
    2945         174 :         wrap = false;
    2946        2298 :     else if (wrapper == JSW_NONE)
    2947        1836 :         wrap = false;
    2948         462 :     else if (wrapper == JSW_UNCONDITIONAL)
    2949         288 :         wrap = true;
    2950         174 :     else if (wrapper == JSW_CONDITIONAL)
    2951         174 :         wrap = count > 1 ||
    2952         198 :             IsAJsonbScalar(first) ||
    2953          24 :             (first->type == jbvBinary &&
    2954          24 :              JsonContainerIsScalar(first->val.binary.data));
    2955             :     else
    2956             :     {
    2957           0 :         elog(ERROR, "unrecognized json wrapper %d", wrapper);
    2958             :         wrap = false;
    2959             :     }
    2960             : 
    2961        2472 :     if (wrap)
    2962         438 :         return JsonbPGetDatum(JsonbValueToJsonb(wrapItemsInArray(&found)));
    2963             : 
    2964        2034 :     if (count > 1)
    2965             :     {
    2966          48 :         if (error)
    2967             :         {
    2968          42 :             *error = true;
    2969          42 :             return (Datum) 0;
    2970             :         }
    2971             : 
    2972           6 :         ereport(ERROR,
    2973             :                 (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
    2974             :                  errmsg("JSON path expression in JSON_QUERY should return "
    2975             :                         "singleton item without wrapper"),
    2976             :                  errhint("use WITH WRAPPER clause to wrap SQL/JSON item "
    2977             :                          "sequence into array")));
    2978             :     }
    2979             : 
    2980        1986 :     if (first)
    2981        1812 :         return JsonbPGetDatum(JsonbValueToJsonb(first));
    2982             : 
    2983         174 :     *empty = true;
    2984         174 :     return PointerGetDatum(NULL);
    2985             : }
    2986             : 
    2987             : JsonbValue *
    2988      603066 : JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars)
    2989             : {
    2990             :     JsonbValue *res;
    2991      603066 :     JsonValueList found = {0};
    2992             :     JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY;
    2993             :     int         count;
    2994             : 
    2995      603066 :     jper = executeJsonPath(jp, vars, EvalJsonPathVar, DatumGetJsonbP(jb), !error,
    2996             :                            &found, true);
    2997             : 
    2998             :     Assert(error || !jperIsError(jper));
    2999             : 
    3000      603054 :     if (error && jperIsError(jper))
    3001             :     {
    3002          18 :         *error = true;
    3003          18 :         *empty = false;
    3004          18 :         return NULL;
    3005             :     }
    3006             : 
    3007      603036 :     count = JsonValueListLength(&found);
    3008             : 
    3009      603036 :     *empty = !count;
    3010             : 
    3011      603036 :     if (*empty)
    3012         336 :         return NULL;
    3013             : 
    3014      602700 :     if (count > 1)
    3015             :     {
    3016          18 :         if (error)
    3017             :         {
    3018          12 :             *error = true;
    3019          12 :             return NULL;
    3020             :         }
    3021             : 
    3022           6 :         ereport(ERROR,
    3023             :                 (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
    3024             :                  errmsg("JSON path expression in JSON_VALUE should return "
    3025             :                         "singleton scalar item")));
    3026             :     }
    3027             : 
    3028      602682 :     res = JsonValueListHead(&found);
    3029             : 
    3030      602682 :     if (res->type == jbvBinary &&
    3031          96 :         JsonContainerIsScalar(res->val.binary.data))
    3032           0 :         JsonbExtractScalar(res->val.binary.data, res);
    3033             : 
    3034      602682 :     if (!IsAJsonbScalar(res))
    3035             :     {
    3036          96 :         if (error)
    3037             :         {
    3038          84 :             *error = true;
    3039          84 :             return NULL;
    3040             :         }
    3041             : 
    3042          12 :         ereport(ERROR,
    3043             :                 (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
    3044             :                  errmsg("JSON path expression in JSON_VALUE should return "
    3045             :                         "singleton scalar item")));
    3046             :     }
    3047             : 
    3048      602586 :     if (res->type == jbvNull)
    3049         156 :         return NULL;
    3050             : 
    3051      602430 :     return res;
    3052             : }
    3053             : 
    3054             : static void
    3055        2166 : JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num)
    3056             : {
    3057        2166 :     jbv->type = jbvNumeric;
    3058        2166 :     jbv->val.numeric = DatumGetNumeric(num);
    3059        2166 : }
    3060             : 
    3061             : void
    3062        2238 : JsonItemFromDatum(Datum val, Oid typid, int32 typmod, JsonbValue *res)
    3063             : {
    3064        2238 :     switch (typid)
    3065             :     {
    3066           0 :         case BOOLOID:
    3067           0 :             res->type = jbvBool;
    3068           0 :             res->val.boolean = DatumGetBool(val);
    3069           0 :             break;
    3070           0 :         case NUMERICOID:
    3071           0 :             JsonbValueInitNumericDatum(res, val);
    3072           0 :             break;
    3073           0 :         case INT2OID:
    3074           0 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(int2_numeric, val));
    3075           0 :             break;
    3076        2166 :         case INT4OID:
    3077        2166 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(int4_numeric, val));
    3078        2166 :             break;
    3079           0 :         case INT8OID:
    3080           0 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(int8_numeric, val));
    3081           0 :             break;
    3082           0 :         case FLOAT4OID:
    3083           0 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(float4_numeric, val));
    3084           0 :             break;
    3085           0 :         case FLOAT8OID:
    3086           0 :             JsonbValueInitNumericDatum(res, DirectFunctionCall1(float8_numeric, val));
    3087           0 :             break;
    3088          12 :         case TEXTOID:
    3089             :         case VARCHAROID:
    3090          12 :             res->type = jbvString;
    3091          12 :             res->val.string.val = VARDATA_ANY(val);
    3092          12 :             res->val.string.len = VARSIZE_ANY_EXHDR(val);
    3093          12 :             break;
    3094          48 :         case DATEOID:
    3095             :         case TIMEOID:
    3096             :         case TIMETZOID:
    3097             :         case TIMESTAMPOID:
    3098             :         case TIMESTAMPTZOID:
    3099          48 :             res->type = jbvDatetime;
    3100          48 :             res->val.datetime.value = val;
    3101          48 :             res->val.datetime.typid = typid;
    3102          48 :             res->val.datetime.typmod = typmod;
    3103          48 :             res->val.datetime.tz = 0;
    3104          48 :             break;
    3105          12 :         case JSONBOID:
    3106             :             {
    3107          12 :                 JsonbValue *jbv = res;
    3108          12 :                 Jsonb      *jb = DatumGetJsonbP(val);
    3109             : 
    3110          12 :                 if (JsonContainerIsScalar(&jb->root))
    3111             :                 {
    3112             :                     bool        res PG_USED_FOR_ASSERTS_ONLY;
    3113             : 
    3114          12 :                     res = JsonbExtractScalar(&jb->root, jbv);
    3115             :                     Assert(res);
    3116             :                 }
    3117             :                 else
    3118           0 :                     JsonbInitBinary(jbv, jb);
    3119          12 :                 break;
    3120             :             }
    3121           0 :         case JSONOID:
    3122             :             {
    3123           0 :                 text       *txt = DatumGetTextP(val);
    3124           0 :                 char       *str = text_to_cstring(txt);
    3125             :                 Jsonb      *jb =
    3126           0 :                 DatumGetJsonbP(DirectFunctionCall1(jsonb_in,
    3127             :                                                    CStringGetDatum(str)));
    3128             : 
    3129           0 :                 pfree(str);
    3130             : 
    3131           0 :                 JsonItemFromDatum(JsonbPGetDatum(jb), JSONBOID, -1, res);
    3132           0 :                 break;
    3133             :             }
    3134           0 :         default:
    3135           0 :             ereport(ERROR,
    3136             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3137             :                      errmsg("only bool, numeric, and text types could be "
    3138             :                             "casted to supported jsonpath types.")));
    3139             :     }
    3140        2238 : }
    3141             : 
    3142             : /************************ JSON_TABLE functions ***************************/
    3143             : 
    3144             : /*
    3145             :  * Returns private data from executor state. Ensure validity by check with
    3146             :  * MAGIC number.
    3147             :  */
    3148             : static inline JsonTableContext *
    3149        8106 : GetJsonTableContext(TableFuncScanState *state, const char *fname)
    3150             : {
    3151             :     JsonTableContext *result;
    3152             : 
    3153        8106 :     if (!IsA(state, TableFuncScanState))
    3154           0 :         elog(ERROR, "%s called with invalid TableFuncScanState", fname);
    3155        8106 :     result = (JsonTableContext *) state->opaque;
    3156        8106 :     if (result->magic != JSON_TABLE_CONTEXT_MAGIC)
    3157           0 :         elog(ERROR, "%s called with invalid TableFuncScanState", fname);
    3158             : 
    3159        8106 :     return result;
    3160             : }
    3161             : 
    3162             : /* Recursively initialize JSON_TABLE scan state */
    3163             : static void
    3164         516 : JsonTableInitScanState(JsonTableContext *cxt, JsonTableScanState *scan,
    3165             :                        JsonTableParent *node, JsonTableScanState *parent,
    3166             :                        List *args, MemoryContext mcxt)
    3167             : {
    3168             :     int         i;
    3169             : 
    3170         516 :     scan->parent = parent;
    3171         516 :     scan->outerJoin = node->outerJoin;
    3172         516 :     scan->errorOnError = node->errorOnError;
    3173         516 :     scan->path = DatumGetJsonPathP(node->path->constvalue);
    3174         516 :     scan->args = args;
    3175         516 :     scan->mcxt = AllocSetContextCreate(mcxt, "JsonTableContext",
    3176             :                                        ALLOCSET_DEFAULT_SIZES);
    3177        1032 :     scan->nested = node->child ?
    3178         516 :         JsonTableInitPlanState(cxt, node->child, scan) : NULL;
    3179         516 :     scan->current = PointerGetDatum(NULL);
    3180         516 :     scan->currentIsNull = true;
    3181             : 
    3182        1710 :     for (i = node->colMin; i <= node->colMax; i++)
    3183        1194 :         cxt->colexprs[i].scan = scan;
    3184         516 : }
    3185             : 
    3186             : /* Recursively initialize JSON_TABLE scan state */
    3187             : static JsonTableJoinState *
    3188         324 : JsonTableInitPlanState(JsonTableContext *cxt, Node *plan,
    3189             :                        JsonTableScanState *parent)
    3190             : {
    3191         324 :     JsonTableJoinState *state = palloc0(sizeof(*state));
    3192             : 
    3193         324 :     if (IsA(plan, JsonTableSibling))
    3194             :     {
    3195          78 :         JsonTableSibling *join = castNode(JsonTableSibling, plan);
    3196             : 
    3197          78 :         state->is_join = true;
    3198          78 :         state->u.join.cross = join->cross;
    3199          78 :         state->u.join.left = JsonTableInitPlanState(cxt, join->larg, parent);
    3200          78 :         state->u.join.right = JsonTableInitPlanState(cxt, join->rarg, parent);
    3201             :     }
    3202             :     else
    3203             :     {
    3204         246 :         JsonTableParent *node = castNode(JsonTableParent, plan);
    3205             : 
    3206         246 :         state->is_join = false;
    3207             : 
    3208         246 :         JsonTableInitScanState(cxt, &state->u.scan, node, parent,
    3209             :                                parent->args, parent->mcxt);
    3210             :     }
    3211             : 
    3212         324 :     return state;
    3213             : }
    3214             : 
    3215             : /*
    3216             :  * JsonTableInitOpaque
    3217             :  *      Fill in TableFuncScanState->opaque for JsonTable processor
    3218             :  */
    3219             : static void
    3220         270 : JsonTableInitOpaque(TableFuncScanState *state, int natts)
    3221             : {
    3222             :     JsonTableContext *cxt;
    3223         270 :     PlanState  *ps = &state->ss.ps;
    3224         270 :     TableFuncScan *tfs = castNode(TableFuncScan, ps->plan);
    3225         270 :     TableFunc  *tf = tfs->tablefunc;
    3226         270 :     JsonExpr   *ci = castNode(JsonExpr, tf->docexpr);
    3227         270 :     JsonTableParent *root = castNode(JsonTableParent, tf->plan);
    3228         270 :     List       *args = NIL;
    3229             :     ListCell   *lc;
    3230             :     int         i;
    3231             : 
    3232         270 :     cxt = palloc0(sizeof(JsonTableContext));
    3233         270 :     cxt->magic = JSON_TABLE_CONTEXT_MAGIC;
    3234             : 
    3235         270 :     if (ci->passing_values)
    3236             :     {
    3237             :         ListCell   *exprlc;
    3238             :         ListCell   *namelc;
    3239             : 
    3240         228 :         forboth(exprlc, ci->passing_values,
    3241             :                 namelc, ci->passing_names)
    3242             :         {
    3243         150 :             Expr       *expr = (Expr *) lfirst(exprlc);
    3244         150 :             String     *name = lfirst_node(String, namelc);
    3245         150 :             JsonPathVariableEvalContext *var = palloc(sizeof(*var));
    3246             : 
    3247         150 :             var->name = pstrdup(name->sval);
    3248         150 :             var->typid = exprType((Node *) expr);
    3249         150 :             var->typmod = exprTypmod((Node *) expr);
    3250         150 :             var->estate = ExecInitExpr(expr, ps);
    3251         150 :             var->econtext = ps->ps_ExprContext;
    3252         150 :             var->mcxt = CurrentMemoryContext;
    3253         150 :             var->evaluated = false;
    3254         150 :             var->value = (Datum) 0;
    3255         150 :             var->isnull = true;
    3256             : 
    3257         150 :             args = lappend(args, var);
    3258             :         }
    3259             :     }
    3260             : 
    3261         540 :     cxt->colexprs = palloc(sizeof(*cxt->colexprs) *
    3262         270 :                            list_length(tf->colvalexprs));
    3263             : 
    3264         270 :     JsonTableInitScanState(cxt, &cxt->root, root, NULL, args,
    3265             :                            CurrentMemoryContext);
    3266             : 
    3267         270 :     i = 0;
    3268             : 
    3269        1464 :     foreach(lc, tf->colvalexprs)
    3270             :     {
    3271        1194 :         Expr       *expr = lfirst(lc);
    3272             : 
    3273        2388 :         cxt->colexprs[i].expr =
    3274        1194 :             ExecInitExprWithCaseValue(expr, ps,
    3275        1194 :                                       &cxt->colexprs[i].scan->current,
    3276        1194 :                                       &cxt->colexprs[i].scan->currentIsNull);
    3277             : 
    3278        1194 :         i++;
    3279             :     }
    3280             : 
    3281         270 :     state->opaque = cxt;
    3282         270 : }
    3283             : 
    3284             : /* Reset scan iterator to the beginning of the item list */
    3285             : static void
    3286        1254 : JsonTableRescan(JsonTableScanState *scan)
    3287             : {
    3288        1254 :     JsonValueListInitIterator(&scan->found, &scan->iter);
    3289        1254 :     scan->current = PointerGetDatum(NULL);
    3290        1254 :     scan->currentIsNull = true;
    3291        1254 :     scan->advanceNested = false;
    3292        1254 :     scan->ordinal = 0;
    3293        1254 : }
    3294             : 
    3295             : /* Reset context item of a scan, execute JSON path and reset a scan */
    3296             : static void
    3297        1014 : JsonTableResetContextItem(JsonTableScanState *scan, Datum item)
    3298             : {
    3299             :     MemoryContext oldcxt;
    3300             :     JsonPathExecResult res;
    3301        1014 :     Jsonb      *js = (Jsonb *) DatumGetJsonbP(item);
    3302             : 
    3303        1014 :     JsonValueListClear(&scan->found);
    3304             : 
    3305        1014 :     MemoryContextResetOnly(scan->mcxt);
    3306             : 
    3307        1014 :     oldcxt = MemoryContextSwitchTo(scan->mcxt);
    3308             : 
    3309        1014 :     res = executeJsonPath(scan->path, scan->args, EvalJsonPathVar, js,
    3310        1014 :                           scan->errorOnError, &scan->found, false /* FIXME */ );
    3311             : 
    3312        1014 :     MemoryContextSwitchTo(oldcxt);
    3313             : 
    3314        1014 :     if (jperIsError(res))
    3315             :     {
    3316             :         Assert(!scan->errorOnError);
    3317         210 :         JsonValueListClear(&scan->found);    /* EMPTY ON ERROR case */
    3318             :     }
    3319             : 
    3320        1014 :     JsonTableRescan(scan);
    3321        1014 : }
    3322             : 
    3323             : /*
    3324             :  * JsonTableSetDocument
    3325             :  *      Install the input document
    3326             :  */
    3327             : static void
    3328         264 : JsonTableSetDocument(TableFuncScanState *state, Datum value)
    3329             : {
    3330         264 :     JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableSetDocument");
    3331             : 
    3332         264 :     JsonTableResetContextItem(&cxt->root, value);
    3333         264 : }
    3334             : 
    3335             : /* Recursively reset scan and its child nodes */
    3336             : static void
    3337         240 : JsonTableRescanRecursive(JsonTableJoinState *state)
    3338             : {
    3339         240 :     if (state->is_join)
    3340             :     {
    3341           0 :         JsonTableRescanRecursive(state->u.join.left);
    3342           0 :         JsonTableRescanRecursive(state->u.join.right);
    3343           0 :         state->u.join.advanceRight = false;
    3344             :     }
    3345             :     else
    3346             :     {
    3347         240 :         JsonTableRescan(&state->u.scan);
    3348         240 :         if (state->u.scan.nested)
    3349          36 :             JsonTableRescanRecursive(state->u.scan.nested);
    3350             :     }
    3351         240 : }
    3352             : 
    3353             : /*
    3354             :  * Fetch next row from a cross/union joined scan.
    3355             :  *
    3356             :  * Returns false at the end of a scan, true otherwise.
    3357             :  */
    3358             : static bool
    3359        3054 : JsonTableNextJoinRow(JsonTableJoinState *state)
    3360             : {
    3361        3054 :     if (!state->is_join)
    3362        2112 :         return JsonTableNextRow(&state->u.scan);
    3363             : 
    3364         942 :     if (state->u.join.advanceRight)
    3365             :     {
    3366             :         /* fetch next inner row */
    3367         456 :         if (JsonTableNextJoinRow(state->u.join.right))
    3368         300 :             return true;
    3369             : 
    3370             :         /* inner rows are exhausted */
    3371         156 :         if (state->u.join.cross)
    3372         108 :             state->u.join.advanceRight = false; /* next outer row */
    3373             :         else
    3374          48 :             return false;       /* end of scan */
    3375             :     }
    3376             : 
    3377         690 :     while (!state->u.join.advanceRight)
    3378             :     {
    3379             :         /* fetch next outer row */
    3380         690 :         bool        left = JsonTableNextJoinRow(state->u.join.left);
    3381             : 
    3382         690 :         if (state->u.join.cross)
    3383             :         {
    3384         318 :             if (!left)
    3385         114 :                 return false;   /* end of scan */
    3386             : 
    3387         204 :             JsonTableRescanRecursive(state->u.join.right);
    3388             : 
    3389         204 :             if (!JsonTableNextJoinRow(state->u.join.right))
    3390          96 :                 continue;       /* next outer row */
    3391             : 
    3392         108 :             state->u.join.advanceRight = true;   /* next inner row */
    3393             :         }
    3394         372 :         else if (!left)
    3395             :         {
    3396         144 :             if (!JsonTableNextJoinRow(state->u.join.right))
    3397          96 :                 return false;   /* end of scan */
    3398             : 
    3399          48 :             state->u.join.advanceRight = true;   /* next inner row */
    3400             :         }
    3401             : 
    3402         384 :         break;
    3403             :     }
    3404             : 
    3405         384 :     return true;
    3406             : }
    3407             : 
    3408             : /* Recursively set 'reset' flag of scan and its child nodes */
    3409             : static void
    3410        1080 : JsonTableJoinReset(JsonTableJoinState *state)
    3411             : {
    3412        1080 :     if (state->is_join)
    3413             :     {
    3414         258 :         JsonTableJoinReset(state->u.join.left);
    3415         258 :         JsonTableJoinReset(state->u.join.right);
    3416         258 :         state->u.join.advanceRight = false;
    3417             :     }
    3418             :     else
    3419             :     {
    3420         822 :         state->u.scan.reset = true;
    3421         822 :         state->u.scan.advanceNested = false;
    3422             : 
    3423         822 :         if (state->u.scan.nested)
    3424          36 :             JsonTableJoinReset(state->u.scan.nested);
    3425             :     }
    3426        1080 : }
    3427             : 
    3428             : /*
    3429             :  * Fetch next row from a simple scan with outer/inner joined nested subscans.
    3430             :  *
    3431             :  * Returns false at the end of a scan, true otherwise.
    3432             :  */
    3433             : static bool
    3434        3588 : JsonTableNextRow(JsonTableScanState *scan)
    3435             : {
    3436             :     /* reset context item if requested */
    3437        3588 :     if (scan->reset)
    3438             :     {
    3439             :         Assert(!scan->parent->currentIsNull);
    3440         750 :         JsonTableResetContextItem(scan, scan->parent->current);
    3441         750 :         scan->reset = false;
    3442             :     }
    3443             : 
    3444        3588 :     if (scan->advanceNested)
    3445             :     {
    3446             :         /* fetch next nested row */
    3447        1032 :         scan->advanceNested = JsonTableNextJoinRow(scan->nested);
    3448             : 
    3449        1032 :         if (scan->advanceNested)
    3450         768 :             return true;
    3451             :     }
    3452             : 
    3453             :     for (;;)
    3454          84 :     {
    3455             :         /* fetch next row */
    3456        2904 :         JsonbValue *jbv = JsonValueListNext(&scan->found, &scan->iter);
    3457             :         MemoryContext oldcxt;
    3458             : 
    3459        2904 :         if (!jbv)
    3460             :         {
    3461        1110 :             scan->current = PointerGetDatum(NULL);
    3462        1110 :             scan->currentIsNull = true;
    3463        1110 :             return false;       /* end of scan */
    3464             :         }
    3465             : 
    3466             :         /* set current row item */
    3467        1794 :         oldcxt = MemoryContextSwitchTo(scan->mcxt);
    3468        1794 :         scan->current = JsonbPGetDatum(JsonbValueToJsonb(jbv));
    3469        1794 :         scan->currentIsNull = false;
    3470        1794 :         MemoryContextSwitchTo(oldcxt);
    3471             : 
    3472        1794 :         scan->ordinal++;
    3473             : 
    3474        1794 :         if (!scan->nested)
    3475        1266 :             break;
    3476             : 
    3477         528 :         JsonTableJoinReset(scan->nested);
    3478             : 
    3479         528 :         scan->advanceNested = JsonTableNextJoinRow(scan->nested);
    3480             : 
    3481         528 :         if (scan->advanceNested || scan->outerJoin)
    3482             :             break;
    3483             :     }
    3484             : 
    3485        1710 :     return true;
    3486             : }
    3487             : 
    3488             : /*
    3489             :  * JsonTableFetchRow
    3490             :  *      Prepare the next "current" tuple for upcoming GetValue calls.
    3491             :  *      Returns FALSE if the row-filter expression returned no more rows.
    3492             :  */
    3493             : static bool
    3494        1476 : JsonTableFetchRow(TableFuncScanState *state)
    3495             : {
    3496        1476 :     JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableFetchRow");
    3497             : 
    3498        1476 :     if (cxt->empty)
    3499           0 :         return false;
    3500             : 
    3501        1476 :     return JsonTableNextRow(&cxt->root);
    3502             : }
    3503             : 
    3504             : /*
    3505             :  * JsonTableGetValue
    3506             :  *      Return the value for column number 'colnum' for the current row.
    3507             :  *
    3508             :  * This leaks memory, so be sure to reset often the context in which it's
    3509             :  * called.
    3510             :  */
    3511             : static Datum
    3512        6096 : JsonTableGetValue(TableFuncScanState *state, int colnum,
    3513             :                   Oid typid, int32 typmod, bool *isnull)
    3514             : {
    3515        6096 :     JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableGetValue");
    3516        6096 :     ExprContext *econtext = state->ss.ps.ps_ExprContext;
    3517        6096 :     ExprState  *estate = cxt->colexprs[colnum].expr;
    3518        6096 :     JsonTableScanState *scan = cxt->colexprs[colnum].scan;
    3519             :     Datum       result;
    3520             : 
    3521        6096 :     if (scan->currentIsNull) /* NULL from outer/union join */
    3522             :     {
    3523         636 :         result = (Datum) 0;
    3524         636 :         *isnull = true;
    3525             :     }
    3526        5460 :     else if (estate)            /* regular column */
    3527             :     {
    3528        4548 :         result = ExecEvalExpr(estate, econtext, isnull);
    3529             :     }
    3530             :     else
    3531             :     {
    3532         912 :         result = Int32GetDatum(scan->ordinal);   /* ordinality column */
    3533         912 :         *isnull = false;
    3534             :     }
    3535             : 
    3536        6066 :     return result;
    3537             : }
    3538             : 
    3539             : /*
    3540             :  * JsonTableDestroyOpaque
    3541             :  */
    3542             : static void
    3543         270 : JsonTableDestroyOpaque(TableFuncScanState *state)
    3544             : {
    3545         270 :     JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableDestroyOpaque");
    3546             : 
    3547             :     /* not valid anymore */
    3548         270 :     cxt->magic = 0;
    3549             : 
    3550         270 :     state->opaque = NULL;
    3551         270 : }
    3552             : 
    3553             : const TableFuncRoutine JsonbTableRoutine =
    3554             : {
    3555             :     JsonTableInitOpaque,
    3556             :     JsonTableSetDocument,
    3557             :     NULL,
    3558             :     NULL,
    3559             :     NULL,
    3560             :     JsonTableFetchRow,
    3561             :     JsonTableGetValue,
    3562             :     JsonTableDestroyOpaque
    3563             : };

Generated by: LCOV version 1.14