LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_exec.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 751 785 95.7 %
Date: 2019-06-19 14:06:47 Functions: 45 45 100.0 %
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 evade 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, PostgreSQL Global Development Group
      53             :  *
      54             :  * IDENTIFICATION
      55             :  *  src/backend/utils/adt/jsonpath_exec.c
      56             :  *
      57             :  *-------------------------------------------------------------------------
      58             :  */
      59             : 
      60             : #include "postgres.h"
      61             : 
      62             : #include "catalog/pg_collation.h"
      63             : #include "catalog/pg_type.h"
      64             : #include "funcapi.h"
      65             : #include "lib/stringinfo.h"
      66             : #include "miscadmin.h"
      67             : #include "regex/regex.h"
      68             : #include "utils/builtins.h"
      69             : #include "utils/datum.h"
      70             : #include "utils/formatting.h"
      71             : #include "utils/float.h"
      72             : #include "utils/guc.h"
      73             : #include "utils/json.h"
      74             : #include "utils/jsonpath.h"
      75             : #include "utils/date.h"
      76             : #include "utils/timestamp.h"
      77             : #include "utils/varlena.h"
      78             : 
      79             : 
      80             : /*
      81             :  * Represents "base object" and it's "id" for .keyvalue() evaluation.
      82             :  */
      83             : typedef struct JsonBaseObjectInfo
      84             : {
      85             :     JsonbContainer *jbc;
      86             :     int         id;
      87             : } JsonBaseObjectInfo;
      88             : 
      89             : /*
      90             :  * Context of jsonpath execution.
      91             :  */
      92             : typedef struct JsonPathExecContext
      93             : {
      94             :     Jsonb      *vars;           /* variables to substitute into jsonpath */
      95             :     JsonbValue *root;           /* for $ evaluation */
      96             :     JsonbValue *current;        /* for @ evaluation */
      97             :     JsonBaseObjectInfo baseObject;  /* "base object" for .keyvalue()
      98             :                                      * evaluation */
      99             :     int         lastGeneratedObjectId;  /* "id" counter for .keyvalue()
     100             :                                          * evaluation */
     101             :     int         innermostArraySize; /* for LAST array index evaluation */
     102             :     bool        laxMode;        /* true for "lax" mode, false for "strict"
     103             :                                  * mode */
     104             :     bool        ignoreStructuralErrors; /* with "true" structural errors such
     105             :                                          * as absence of required json item or
     106             :                                          * unexpected json item type are
     107             :                                          * ignored */
     108             :     bool        throwErrors;    /* with "false" all suppressible errors are
     109             :                                  * suppressed */
     110             : } JsonPathExecContext;
     111             : 
     112             : /* Context for LIKE_REGEX execution. */
     113             : typedef struct JsonLikeRegexContext
     114             : {
     115             :     text       *regex;
     116             :     int         cflags;
     117             : } JsonLikeRegexContext;
     118             : 
     119             : /* Result of jsonpath predicate evaluation */
     120             : typedef enum JsonPathBool
     121             : {
     122             :     jpbFalse = 0,
     123             :     jpbTrue = 1,
     124             :     jpbUnknown = 2
     125             : } JsonPathBool;
     126             : 
     127             : /* Result of jsonpath expression evaluation */
     128             : typedef enum JsonPathExecResult
     129             : {
     130             :     jperOk = 0,
     131             :     jperNotFound = 1,
     132             :     jperError = 2
     133             : } JsonPathExecResult;
     134             : 
     135             : #define jperIsError(jper)           ((jper) == jperError)
     136             : 
     137             : /*
     138             :  * List of jsonb values with shortcut for single-value list.
     139             :  */
     140             : typedef struct JsonValueList
     141             : {
     142             :     JsonbValue *singleton;
     143             :     List       *list;
     144             : } JsonValueList;
     145             : 
     146             : typedef struct JsonValueListIterator
     147             : {
     148             :     JsonbValue *value;
     149             :     ListCell   *next;
     150             : } JsonValueListIterator;
     151             : 
     152             : /* strict/lax flags is decomposed into four [un]wrap/error flags */
     153             : #define jspStrictAbsenseOfErrors(cxt)   (!(cxt)->laxMode)
     154             : #define jspAutoUnwrap(cxt)              ((cxt)->laxMode)
     155             : #define jspAutoWrap(cxt)                ((cxt)->laxMode)
     156             : #define jspIgnoreStructuralErrors(cxt)  ((cxt)->ignoreStructuralErrors)
     157             : #define jspThrowErrors(cxt)             ((cxt)->throwErrors)
     158             : 
     159             : /* Convenience macro: return or throw error depending on context */
     160             : #define RETURN_ERROR(throw_error) \
     161             : do { \
     162             :     if (jspThrowErrors(cxt)) \
     163             :         throw_error; \
     164             :     else \
     165             :         return jperError; \
     166             : } while (0)
     167             : 
     168             : typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
     169             :                                                    JsonbValue *larg,
     170             :                                                    JsonbValue *rarg,
     171             :                                                    void *param);
     172             : typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
     173             : 
     174             : static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
     175             :                                           Jsonb *json, bool throwErrors, JsonValueList *result);
     176             : static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
     177             :                                       JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
     178             : static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
     179             :                                                      JsonPathItem *jsp, JsonbValue *jb,
     180             :                                                      JsonValueList *found, bool unwrap);
     181             : static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
     182             :                                                        JsonPathItem *jsp, JsonbValue *jb,
     183             :                                                        JsonValueList *found, bool unwrapElements);
     184             : static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
     185             :                                           JsonPathItem *cur, JsonPathItem *next,
     186             :                                           JsonbValue *v, JsonValueList *found, bool copy);
     187             : static JsonPathExecResult executeItemOptUnwrapResult(
     188             :                                                      JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
     189             :                                                      bool unwrap, JsonValueList *found);
     190             : static JsonPathExecResult executeItemOptUnwrapResultNoThrow(
     191             :                                                             JsonPathExecContext *cxt, JsonPathItem *jsp,
     192             :                                                             JsonbValue *jb, bool unwrap, JsonValueList *found);
     193             : static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
     194             :                                     JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
     195             : static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
     196             :                                           JsonPathItem *jsp, JsonbValue *jb);
     197             : static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
     198             :                                          JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
     199             :                                          uint32 level, uint32 first, uint32 last,
     200             :                                          bool ignoreStructuralErrors, bool unwrapNext);
     201             : static JsonPathBool executePredicate(JsonPathExecContext *cxt,
     202             :                                      JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
     203             :                                      JsonbValue *jb, bool unwrapRightArg,
     204             :                                      JsonPathPredicateCallback exec, void *param);
     205             : static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
     206             :                                                   JsonPathItem *jsp, JsonbValue *jb,
     207             :                                                   BinaryArithmFunc func, JsonValueList *found);
     208             : static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
     209             :                                                  JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
     210             :                                                  JsonValueList *found);
     211             : static JsonPathBool executeStartsWith(JsonPathItem *jsp,
     212             :                                       JsonbValue *whole, JsonbValue *initial, void *param);
     213             : static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
     214             :                                      JsonbValue *rarg, void *param);
     215             : static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
     216             :                                                    JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
     217             :                                                    JsonValueList *found);
     218             : static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
     219             :                                                 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
     220             : static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
     221             :                                            JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
     222             : static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
     223             :                             JsonbValue *value);
     224             : static void getJsonPathVariable(JsonPathExecContext *cxt,
     225             :                                 JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
     226             : static int  JsonbArraySize(JsonbValue *jb);
     227             : static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
     228             :                                       JsonbValue *rv, void *p);
     229             : static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2);
     230             : static int  compareNumeric(Numeric a, Numeric b);
     231             : static JsonbValue *copyJsonbValue(JsonbValue *src);
     232             : static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
     233             :                                         JsonPathItem *jsp, JsonbValue *jb, int32 *index);
     234             : static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
     235             :                                         JsonbValue *jbv, int32 id);
     236             : static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
     237             : static int  JsonValueListLength(const JsonValueList *jvl);
     238             : static bool JsonValueListIsEmpty(JsonValueList *jvl);
     239             : static JsonbValue *JsonValueListHead(JsonValueList *jvl);
     240             : static List *JsonValueListGetList(JsonValueList *jvl);
     241             : static void JsonValueListInitIterator(const JsonValueList *jvl,
     242             :                                       JsonValueListIterator *it);
     243             : static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
     244             :                                      JsonValueListIterator *it);
     245             : static int  JsonbType(JsonbValue *jb);
     246             : static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
     247             : static int  JsonbType(JsonbValue *jb);
     248             : static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
     249             : static JsonbValue *wrapItemsInArray(const JsonValueList *items);
     250             : 
     251             : /****************** User interface to JsonPath executor ********************/
     252             : 
     253             : /*
     254             :  * jsonb_path_exists
     255             :  *      Returns true if jsonpath returns at least one item for the specified
     256             :  *      jsonb value.  This function and jsonb_path_match() are used to
     257             :  *      implement @? and @@ operators, which in turn are intended to have an
     258             :  *      index support.  Thus, it's desirable to make it easier to achieve
     259             :  *      consistency between index scan results and sequential scan results.
     260             :  *      So, we throw as less errors as possible.  Regarding this function,
     261             :  *      such behavior also matches behavior of JSON_EXISTS() clause of
     262             :  *      SQL/JSON.  Regarding jsonb_path_match(), this function doesn't have
     263             :  *      an analogy in SQL/JSON, so we define its behavior on our own.
     264             :  */
     265             : Datum
     266       57328 : jsonb_path_exists(PG_FUNCTION_ARGS)
     267             : {
     268       57328 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     269       57328 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     270             :     JsonPathExecResult res;
     271       57328 :     Jsonb      *vars = NULL;
     272       57328 :     bool        silent = true;
     273             : 
     274       57328 :     if (PG_NARGS() == 4)
     275             :     {
     276          28 :         vars = PG_GETARG_JSONB_P(2);
     277          28 :         silent = PG_GETARG_BOOL(3);
     278             :     }
     279             : 
     280       57328 :     res = executeJsonPath(jp, vars, jb, !silent, NULL);
     281             : 
     282       57324 :     PG_FREE_IF_COPY(jb, 0);
     283       57324 :     PG_FREE_IF_COPY(jp, 1);
     284             : 
     285       57324 :     if (jperIsError(res))
     286          36 :         PG_RETURN_NULL();
     287             : 
     288       57288 :     PG_RETURN_BOOL(res == jperOk);
     289             : }
     290             : 
     291             : /*
     292             :  * jsonb_path_exists_opr
     293             :  *      Implementation of operator "jsonb @? jsonpath" (2-argument version of
     294             :  *      jsonb_path_exists()).
     295             :  */
     296             : Datum
     297       57300 : jsonb_path_exists_opr(PG_FUNCTION_ARGS)
     298             : {
     299             :     /* just call the other one -- it can handle both cases */
     300       57300 :     return jsonb_path_exists(fcinfo);
     301             : }
     302             : 
     303             : /*
     304             :  * jsonb_path_match
     305             :  *      Returns jsonpath predicate result item for the specified jsonb value.
     306             :  *      See jsonb_path_exists() comment for details regarding error handling.
     307             :  */
     308             : Datum
     309       65268 : jsonb_path_match(PG_FUNCTION_ARGS)
     310             : {
     311       65268 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     312       65268 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     313       65268 :     JsonValueList found = {0};
     314       65268 :     Jsonb      *vars = NULL;
     315       65268 :     bool        silent = true;
     316             : 
     317       65268 :     if (PG_NARGS() == 4)
     318             :     {
     319          76 :         vars = PG_GETARG_JSONB_P(2);
     320          76 :         silent = PG_GETARG_BOOL(3);
     321             :     }
     322             : 
     323       65268 :     (void) executeJsonPath(jp, vars, jb, !silent, &found);
     324             : 
     325       65264 :     PG_FREE_IF_COPY(jb, 0);
     326       65264 :     PG_FREE_IF_COPY(jp, 1);
     327             : 
     328       65264 :     if (JsonValueListLength(&found) == 1)
     329             :     {
     330       65244 :         JsonbValue *jbv = JsonValueListHead(&found);
     331             : 
     332       65244 :         if (jbv->type == jbvBool)
     333       65196 :             PG_RETURN_BOOL(jbv->val.boolean);
     334             : 
     335          48 :         if (jbv->type == jbvNull)
     336          16 :             PG_RETURN_NULL();
     337             :     }
     338             : 
     339          52 :     if (!silent)
     340          24 :         ereport(ERROR,
     341             :                 (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
     342             :                  errmsg("single boolean result is expected")));
     343             : 
     344          28 :     PG_RETURN_NULL();
     345             : }
     346             : 
     347             : /*
     348             :  * jsonb_path_match_opr
     349             :  *      Implementation of operator "jsonb @@ jsonpath" (2-argument version of
     350             :  *      jsonb_path_match()).
     351             :  */
     352             : Datum
     353       65192 : jsonb_path_match_opr(PG_FUNCTION_ARGS)
     354             : {
     355             :     /* just call the other one -- it can handle both cases */
     356       65192 :     return jsonb_path_match(fcinfo);
     357             : }
     358             : 
     359             : /*
     360             :  * jsonb_path_query
     361             :  *      Executes jsonpath for given jsonb document and returns result as
     362             :  *      rowset.
     363             :  */
     364             : Datum
     365        1620 : jsonb_path_query(PG_FUNCTION_ARGS)
     366             : {
     367             :     FuncCallContext *funcctx;
     368             :     List       *found;
     369             :     JsonbValue *v;
     370             :     ListCell   *c;
     371             : 
     372        1620 :     if (SRF_IS_FIRSTCALL())
     373             :     {
     374             :         JsonPath   *jp;
     375             :         Jsonb      *jb;
     376             :         MemoryContext oldcontext;
     377             :         Jsonb      *vars;
     378             :         bool        silent;
     379         848 :         JsonValueList found = {0};
     380             : 
     381         848 :         funcctx = SRF_FIRSTCALL_INIT();
     382         848 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     383             : 
     384         848 :         jb = PG_GETARG_JSONB_P_COPY(0);
     385         848 :         jp = PG_GETARG_JSONPATH_P_COPY(1);
     386         848 :         vars = PG_GETARG_JSONB_P_COPY(2);
     387         848 :         silent = PG_GETARG_BOOL(3);
     388             : 
     389         848 :         (void) executeJsonPath(jp, vars, jb, !silent, &found);
     390             : 
     391         688 :         funcctx->user_fctx = JsonValueListGetList(&found);
     392             : 
     393         688 :         MemoryContextSwitchTo(oldcontext);
     394             :     }
     395             : 
     396        1460 :     funcctx = SRF_PERCALL_SETUP();
     397        1460 :     found = funcctx->user_fctx;
     398             : 
     399        1460 :     c = list_head(found);
     400             : 
     401        1460 :     if (c == NULL)
     402         688 :         SRF_RETURN_DONE(funcctx);
     403             : 
     404         772 :     v = lfirst(c);
     405         772 :     funcctx->user_fctx = list_delete_first(found);
     406             : 
     407         772 :     SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
     408             : }
     409             : 
     410             : /*
     411             :  * jsonb_path_query_array
     412             :  *      Executes jsonpath for given jsonb document and returns result as
     413             :  *      jsonb array.
     414             :  */
     415             : Datum
     416          24 : jsonb_path_query_array(PG_FUNCTION_ARGS)
     417             : {
     418          24 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     419          24 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     420          24 :     JsonValueList found = {0};
     421          24 :     Jsonb      *vars = PG_GETARG_JSONB_P(2);
     422          24 :     bool        silent = PG_GETARG_BOOL(3);
     423             : 
     424          24 :     (void) executeJsonPath(jp, vars, jb, !silent, &found);
     425             : 
     426          20 :     PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
     427             : }
     428             : 
     429             : /*
     430             :  * jsonb_path_query_first
     431             :  *      Executes jsonpath for given jsonb document and returns first result
     432             :  *      item.  If there are no items, NULL returned.
     433             :  */
     434             : Datum
     435          28 : jsonb_path_query_first(PG_FUNCTION_ARGS)
     436             : {
     437          28 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     438          28 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     439          28 :     JsonValueList found = {0};
     440          28 :     Jsonb      *vars = PG_GETARG_JSONB_P(2);
     441          28 :     bool        silent = PG_GETARG_BOOL(3);
     442             : 
     443          28 :     (void) executeJsonPath(jp, vars, jb, !silent, &found);
     444             : 
     445          24 :     if (JsonValueListLength(&found) >= 1)
     446          16 :         PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
     447             :     else
     448           8 :         PG_RETURN_NULL();
     449             : }
     450             : 
     451             : /********************Execute functions for JsonPath**************************/
     452             : 
     453             : /*
     454             :  * Interface to jsonpath executor
     455             :  *
     456             :  * 'path' - jsonpath to be executed
     457             :  * 'vars' - variables to be substituted to jsonpath
     458             :  * 'json' - target document for jsonpath evaluation
     459             :  * 'throwErrors' - whether we should throw suppressible errors
     460             :  * 'result' - list to store result items into
     461             :  *
     462             :  * Returns an error if a recoverable error happens during processing, or NULL
     463             :  * on no error.
     464             :  *
     465             :  * Note, jsonb and jsonpath values should be available and untoasted during
     466             :  * work because JsonPathItem, JsonbValue and result item could have pointers
     467             :  * into input values.  If caller needs to just check if document matches
     468             :  * jsonpath, then it doesn't provide a result arg.  In this case executor
     469             :  * works till first positive result and does not check the rest if possible.
     470             :  * In other case it tries to find all the satisfied result items.
     471             :  */
     472             : static JsonPathExecResult
     473      123496 : executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
     474             :                 JsonValueList *result)
     475             : {
     476             :     JsonPathExecContext cxt;
     477             :     JsonPathExecResult res;
     478             :     JsonPathItem jsp;
     479             :     JsonbValue  jbv;
     480             : 
     481      123496 :     jspInit(&jsp, path);
     482             : 
     483      123496 :     if (!JsonbExtractScalar(&json->root, &jbv))
     484      123272 :         JsonbInitBinary(&jbv, json);
     485             : 
     486      123496 :     if (vars && !JsonContainerIsObject(&vars->root))
     487             :     {
     488           8 :         ereport(ERROR,
     489             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     490             :                  errmsg("\"vars\" argument is not an object"),
     491             :                  errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
     492             :     }
     493             : 
     494      123488 :     cxt.vars = vars;
     495      123488 :     cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
     496      123488 :     cxt.ignoreStructuralErrors = cxt.laxMode;
     497      123488 :     cxt.root = &jbv;
     498      123488 :     cxt.current = &jbv;
     499      123488 :     cxt.baseObject.jbc = NULL;
     500      123488 :     cxt.baseObject.id = 0;
     501      123488 :     cxt.lastGeneratedObjectId = vars ? 2 : 1;
     502      123488 :     cxt.innermostArraySize = -1;
     503      123488 :     cxt.throwErrors = throwErrors;
     504             : 
     505      123488 :     if (jspStrictAbsenseOfErrors(&cxt) && !result)
     506             :     {
     507             :         /*
     508             :          * In strict mode we must get a complete list of values to check that
     509             :          * there are no errors at all.
     510             :          */
     511          36 :         JsonValueList vals = {0};
     512             : 
     513          36 :         res = executeItem(&cxt, &jsp, &jbv, &vals);
     514             : 
     515          32 :         if (jperIsError(res))
     516          28 :             return res;
     517             : 
     518           4 :         return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
     519             :     }
     520             : 
     521      123452 :     res = executeItem(&cxt, &jsp, &jbv, result);
     522             : 
     523             :     Assert(!throwErrors || !jperIsError(res));
     524             : 
     525      123288 :     return res;
     526             : }
     527             : 
     528             : /*
     529             :  * Execute jsonpath with automatic unwrapping of current item in lax mode.
     530             :  */
     531             : static JsonPathExecResult
     532      360896 : executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
     533             :             JsonbValue *jb, JsonValueList *found)
     534             : {
     535      360896 :     return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
     536             : }
     537             : 
     538             : /*
     539             :  * Main jsonpath executor function: walks on jsonpath structure, finds
     540             :  * relevant parts of jsonb and evaluates expressions over them.
     541             :  * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
     542             :  */
     543             : static JsonPathExecResult
     544      362468 : executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
     545             :                            JsonbValue *jb, JsonValueList *found, bool unwrap)
     546             : {
     547             :     JsonPathItem elem;
     548      362468 :     JsonPathExecResult res = jperNotFound;
     549             :     JsonBaseObjectInfo baseObject;
     550             : 
     551      362468 :     check_stack_depth();
     552      362468 :     CHECK_FOR_INTERRUPTS();
     553             : 
     554      362468 :     switch (jsp->type)
     555             :     {
     556             :             /* all boolean item types: */
     557             :         case jpiAnd:
     558             :         case jpiOr:
     559             :         case jpiNot:
     560             :         case jpiIsUnknown:
     561             :         case jpiEqual:
     562             :         case jpiNotEqual:
     563             :         case jpiLess:
     564             :         case jpiGreater:
     565             :         case jpiLessOrEqual:
     566             :         case jpiGreaterOrEqual:
     567             :         case jpiExists:
     568             :         case jpiStartsWith:
     569             :         case jpiLikeRegex:
     570             :             {
     571       65224 :                 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
     572             : 
     573       65224 :                 res = appendBoolResult(cxt, jsp, found, st);
     574       65224 :                 break;
     575             :             }
     576             : 
     577             :         case jpiKey:
     578      107272 :             if (JsonbType(jb) == jbvObject)
     579             :             {
     580             :                 JsonbValue *v;
     581             :                 JsonbValue  key;
     582             : 
     583      107104 :                 key.type = jbvString;
     584      107104 :                 key.val.string.val = jspGetString(jsp, &key.val.string.len);
     585             : 
     586      107104 :                 v = findJsonbValueFromContainer(jb->val.binary.data,
     587             :                                                 JB_FOBJECT, &key);
     588             : 
     589      107104 :                 if (v != NULL)
     590             :                 {
     591       14552 :                     res = executeNextItem(cxt, jsp, NULL,
     592             :                                           v, found, false);
     593             : 
     594             :                     /* free value if it was not added to found list */
     595       14552 :                     if (jspHasNext(jsp) || !found)
     596       10736 :                         pfree(v);
     597             :                 }
     598       92552 :                 else if (!jspIgnoreStructuralErrors(cxt))
     599             :                 {
     600             :                     Assert(found);
     601             : 
     602          48 :                     if (!jspThrowErrors(cxt))
     603          32 :                         return jperError;
     604             : 
     605          16 :                     ereport(ERROR,
     606             :                             (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND), \
     607             :                              errmsg("JSON object does not contain key \"%s\"",
     608             :                                     pnstrdup(key.val.string.val,
     609             :                                              key.val.string.len))));
     610             :                 }
     611             :             }
     612         168 :             else if (unwrap && JsonbType(jb) == jbvArray)
     613           4 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
     614         164 :             else if (!jspIgnoreStructuralErrors(cxt))
     615             :             {
     616             :                 Assert(found);
     617          44 :                 RETURN_ERROR(ereport(ERROR,
     618             :                                      (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND),
     619             :                                       errmsg("jsonpath member accessor can only be applied to an object"))));
     620             :             }
     621      107176 :             break;
     622             : 
     623             :         case jpiRoot:
     624      131656 :             jb = cxt->root;
     625      131656 :             baseObject = setBaseObject(cxt, jb, 0);
     626      131656 :             res = executeNextItem(cxt, jsp, NULL, jb, found, true);
     627      131520 :             cxt->baseObject = baseObject;
     628      131520 :             break;
     629             : 
     630             :         case jpiCurrent:
     631       11820 :             res = executeNextItem(cxt, jsp, NULL, cxt->current,
     632             :                                   found, true);
     633       11820 :             break;
     634             : 
     635             :         case jpiAnyArray:
     636         732 :             if (JsonbType(jb) == jbvArray)
     637             :             {
     638         608 :                 bool        hasNext = jspGetNext(jsp, &elem);
     639             : 
     640         608 :                 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
     641         608 :                                                    jb, found, jspAutoUnwrap(cxt));
     642             :             }
     643         124 :             else if (jspAutoWrap(cxt))
     644         116 :                 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
     645           8 :             else if (!jspIgnoreStructuralErrors(cxt))
     646           8 :                 RETURN_ERROR(ereport(ERROR,
     647             :                                      (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
     648             :                                       errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
     649         700 :             break;
     650             : 
     651             :         case jpiIndexArray:
     652         192 :             if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
     653         116 :             {
     654         184 :                 int         innermostArraySize = cxt->innermostArraySize;
     655             :                 int         i;
     656         184 :                 int         size = JsonbArraySize(jb);
     657         184 :                 bool        singleton = size < 0;
     658         184 :                 bool        hasNext = jspGetNext(jsp, &elem);
     659             : 
     660         184 :                 if (singleton)
     661           4 :                     size = 1;
     662             : 
     663         184 :                 cxt->innermostArraySize = size; /* for LAST evaluation */
     664             : 
     665         576 :                 for (i = 0; i < jsp->content.array.nelems; i++)
     666             :                 {
     667             :                     JsonPathItem from;
     668             :                     JsonPathItem to;
     669             :                     int32       index;
     670             :                     int32       index_from;
     671             :                     int32       index_to;
     672         192 :                     bool        range = jspGetArraySubscript(jsp, &from,
     673             :                                                              &to, i);
     674             : 
     675         192 :                     res = getArrayIndex(cxt, &from, jb, &index_from);
     676             : 
     677         176 :                     if (jperIsError(res))
     678          36 :                         break;
     679             : 
     680         160 :                     if (range)
     681             :                     {
     682          20 :                         res = getArrayIndex(cxt, &to, jb, &index_to);
     683             : 
     684          16 :                         if (jperIsError(res))
     685           0 :                             break;
     686             :                     }
     687             :                     else
     688         140 :                         index_to = index_from;
     689             : 
     690         212 :                     if (!jspIgnoreStructuralErrors(cxt) &&
     691         104 :                         (index_from < 0 ||
     692          96 :                          index_from > index_to ||
     693          48 :                          index_to >= size))
     694          68 :                         RETURN_ERROR(ereport(ERROR,
     695             :                                              (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
     696             :                                               errmsg("jsonpath array subscript is out of bounds"))));
     697             : 
     698         124 :                     if (index_from < 0)
     699           8 :                         index_from = 0;
     700             : 
     701         124 :                     if (index_to >= size)
     702          12 :                         index_to = size - 1;
     703             : 
     704         124 :                     res = jperNotFound;
     705             : 
     706         240 :                     for (index = index_from; index <= index_to; index++)
     707             :                     {
     708             :                         JsonbValue *v;
     709             :                         bool        copy;
     710             : 
     711         136 :                         if (singleton)
     712             :                         {
     713           4 :                             v = jb;
     714           4 :                             copy = true;
     715             :                         }
     716             :                         else
     717             :                         {
     718         132 :                             v = getIthJsonbValueFromContainer(jb->val.binary.data,
     719             :                                                               (uint32) index);
     720             : 
     721         132 :                             if (v == NULL)
     722           0 :                                 continue;
     723             : 
     724         132 :                             copy = false;
     725             :                         }
     726             : 
     727         136 :                         if (!hasNext && !found)
     728          16 :                             return jperOk;
     729             : 
     730         120 :                         res = executeNextItem(cxt, jsp, &elem, v, found,
     731             :                                               copy);
     732             : 
     733         120 :                         if (jperIsError(res))
     734           0 :                             break;
     735             : 
     736         120 :                         if (res == jperOk && !found)
     737           4 :                             break;
     738             :                     }
     739             : 
     740         108 :                     if (jperIsError(res))
     741           0 :                         break;
     742             : 
     743         108 :                     if (res == jperOk && !found)
     744           4 :                         break;
     745             :                 }
     746             : 
     747         116 :                 cxt->innermostArraySize = innermostArraySize;
     748             :             }
     749           8 :             else if (!jspIgnoreStructuralErrors(cxt))
     750             :             {
     751           8 :                 RETURN_ERROR(ereport(ERROR,
     752             :                                      (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
     753             :                                       errmsg("jsonpath array accessor can only be applied to an array"))));
     754             :             }
     755         116 :             break;
     756             : 
     757             :         case jpiLast:
     758             :             {
     759             :                 JsonbValue  tmpjbv;
     760             :                 JsonbValue *lastjbv;
     761             :                 int         last;
     762          44 :                 bool        hasNext = jspGetNext(jsp, &elem);
     763             : 
     764          44 :                 if (cxt->innermostArraySize < 0)
     765           0 :                     elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
     766             : 
     767          44 :                 if (!hasNext && !found)
     768             :                 {
     769           4 :                     res = jperOk;
     770           4 :                     break;
     771             :                 }
     772             : 
     773          40 :                 last = cxt->innermostArraySize - 1;
     774             : 
     775          40 :                 lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
     776             : 
     777          40 :                 lastjbv->type = jbvNumeric;
     778          40 :                 lastjbv->val.numeric =
     779          40 :                     DatumGetNumeric(DirectFunctionCall1(int4_numeric,
     780             :                                                         Int32GetDatum(last)));
     781             : 
     782          40 :                 res = executeNextItem(cxt, jsp, &elem,
     783             :                                       lastjbv, found, hasNext);
     784             :             }
     785          40 :             break;
     786             : 
     787             :         case jpiAnyKey:
     788          56 :             if (JsonbType(jb) == jbvObject)
     789             :             {
     790          44 :                 bool        hasNext = jspGetNext(jsp, &elem);
     791             : 
     792          44 :                 if (jb->type != jbvBinary)
     793           0 :                     elog(ERROR, "invalid jsonb object type: %d", jb->type);
     794             : 
     795          44 :                 return executeAnyItem
     796             :                     (cxt, hasNext ? &elem : NULL,
     797             :                      jb->val.binary.data, found, 1, 1, 1,
     798          44 :                      false, jspAutoUnwrap(cxt));
     799             :             }
     800          12 :             else if (unwrap && JsonbType(jb) == jbvArray)
     801           0 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
     802          12 :             else if (!jspIgnoreStructuralErrors(cxt))
     803             :             {
     804             :                 Assert(found);
     805           8 :                 RETURN_ERROR(ereport(ERROR,
     806             :                                      (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
     807             :                                       errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
     808             :             }
     809           4 :             break;
     810             : 
     811             :         case jpiAdd:
     812         112 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     813             :                                            numeric_add_opt_error, found);
     814             : 
     815             :         case jpiSub:
     816          40 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     817             :                                            numeric_sub_opt_error, found);
     818             : 
     819             :         case jpiMul:
     820          24 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     821             :                                            numeric_mul_opt_error, found);
     822             : 
     823             :         case jpiDiv:
     824          44 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     825             :                                            numeric_div_opt_error, found);
     826             : 
     827             :         case jpiMod:
     828           8 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     829             :                                            numeric_mod_opt_error, found);
     830             : 
     831             :         case jpiPlus:
     832          40 :             return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
     833             : 
     834             :         case jpiMinus:
     835          84 :             return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
     836             :                                           found);
     837             : 
     838             :         case jpiFilter:
     839             :             {
     840             :                 JsonPathBool st;
     841             : 
     842       11304 :                 if (unwrap && JsonbType(jb) == jbvArray)
     843          84 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
     844             :                                                         false);
     845             : 
     846       11220 :                 jspGetArg(jsp, &elem);
     847       11220 :                 st = executeNestedBoolItem(cxt, &elem, jb);
     848       11216 :                 if (st != jpbTrue)
     849       10296 :                     res = jperNotFound;
     850             :                 else
     851         920 :                     res = executeNextItem(cxt, jsp, NULL,
     852             :                                           jb, found, true);
     853       11216 :                 break;
     854             :             }
     855             : 
     856             :         case jpiAny:
     857             :             {
     858         204 :                 bool        hasNext = jspGetNext(jsp, &elem);
     859             : 
     860             :                 /* first try without any intermediate steps */
     861         204 :                 if (jsp->content.anybounds.first == 0)
     862             :                 {
     863             :                     bool        savedIgnoreStructuralErrors;
     864             : 
     865         112 :                     savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
     866         112 :                     cxt->ignoreStructuralErrors = true;
     867         112 :                     res = executeNextItem(cxt, jsp, &elem,
     868             :                                           jb, found, true);
     869         112 :                     cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
     870             : 
     871         112 :                     if (res == jperOk && !found)
     872           4 :                         break;
     873             :                 }
     874             : 
     875         200 :                 if (jb->type == jbvBinary)
     876         200 :                     res = executeAnyItem
     877             :                         (cxt, hasNext ? &elem : NULL,
     878             :                          jb->val.binary.data, found,
     879             :                          1,
     880             :                          jsp->content.anybounds.first,
     881             :                          jsp->content.anybounds.last,
     882         200 :                          true, jspAutoUnwrap(cxt));
     883         200 :                 break;
     884             :             }
     885             : 
     886             :         case jpiNull:
     887             :         case jpiBool:
     888             :         case jpiNumeric:
     889             :         case jpiString:
     890             :         case jpiVariable:
     891             :             {
     892             :                 JsonbValue  vbuf;
     893             :                 JsonbValue *v;
     894       33168 :                 bool        hasNext = jspGetNext(jsp, &elem);
     895             : 
     896       33168 :                 if (!hasNext && !found)
     897             :                 {
     898           4 :                     res = jperOk;   /* skip evaluation */
     899           4 :                     break;
     900             :                 }
     901             : 
     902       33164 :                 v = hasNext ? &vbuf : palloc(sizeof(*v));
     903             : 
     904       33164 :                 baseObject = cxt->baseObject;
     905       33164 :                 getJsonPathItem(cxt, jsp, v);
     906             : 
     907       33160 :                 res = executeNextItem(cxt, jsp, &elem,
     908             :                                       v, found, hasNext);
     909       33160 :                 cxt->baseObject = baseObject;
     910             :             }
     911       33160 :             break;
     912             : 
     913             :         case jpiType:
     914             :             {
     915          92 :                 JsonbValue *jbv = palloc(sizeof(*jbv));
     916             : 
     917          92 :                 jbv->type = jbvString;
     918          92 :                 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
     919          92 :                 jbv->val.string.len = strlen(jbv->val.string.val);
     920             : 
     921          92 :                 res = executeNextItem(cxt, jsp, NULL, jbv,
     922             :                                       found, false);
     923             :             }
     924          92 :             break;
     925             : 
     926             :         case jpiSize:
     927             :             {
     928          48 :                 int         size = JsonbArraySize(jb);
     929             : 
     930          48 :                 if (size < 0)
     931             :                 {
     932          32 :                     if (!jspAutoWrap(cxt))
     933             :                     {
     934           8 :                         if (!jspIgnoreStructuralErrors(cxt))
     935           8 :                             RETURN_ERROR(ereport(ERROR,
     936             :                                                  (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
     937             :                                                   errmsg("jsonpath item method .%s() can only be applied to an array",
     938             :                                                          jspOperationName(jsp->type)))));
     939           0 :                         break;
     940             :                     }
     941             : 
     942          24 :                     size = 1;
     943             :                 }
     944             : 
     945          40 :                 jb = palloc(sizeof(*jb));
     946             : 
     947          40 :                 jb->type = jbvNumeric;
     948          40 :                 jb->val.numeric =
     949          40 :                     DatumGetNumeric(DirectFunctionCall1(int4_numeric,
     950             :                                                         Int32GetDatum(size)));
     951             : 
     952          40 :                 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
     953             :             }
     954          40 :             break;
     955             : 
     956             :         case jpiAbs:
     957          72 :             return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
     958             :                                             found);
     959             : 
     960             :         case jpiFloor:
     961          32 :             return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
     962             :                                             found);
     963             : 
     964             :         case jpiCeiling:
     965          68 :             return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
     966             :                                             found);
     967             : 
     968             :         case jpiDouble:
     969             :             {
     970             :                 JsonbValue  jbv;
     971             : 
     972          72 :                 if (unwrap && JsonbType(jb) == jbvArray)
     973          32 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
     974             :                                                         false);
     975             : 
     976          68 :                 if (jb->type == jbvNumeric)
     977             :                 {
     978           4 :                     char       *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
     979             :                                                                           NumericGetDatum(jb->val.numeric)));
     980           4 :                     bool        have_error = false;
     981             : 
     982           4 :                     (void) float8in_internal_opt_error(tmp,
     983             :                                                        NULL,
     984             :                                                        "double precision",
     985             :                                                        tmp,
     986             :                                                        &have_error);
     987             : 
     988           4 :                     if (have_error)
     989           0 :                         RETURN_ERROR(ereport(ERROR,
     990             :                                              (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
     991             :                                               errmsg("jsonpath item method .%s() can only be applied to a numeric value",
     992             :                                                      jspOperationName(jsp->type)))));
     993           4 :                     res = jperOk;
     994             :                 }
     995          64 :                 else if (jb->type == jbvString)
     996             :                 {
     997             :                     /* cast string as double */
     998             :                     double      val;
     999          32 :                     char       *tmp = pnstrdup(jb->val.string.val,
    1000          32 :                                                jb->val.string.len);
    1001          32 :                     bool        have_error = false;
    1002             : 
    1003          32 :                     val = float8in_internal_opt_error(tmp,
    1004             :                                                       NULL,
    1005             :                                                       "double precision",
    1006             :                                                       tmp,
    1007             :                                                       &have_error);
    1008             : 
    1009          32 :                     if (have_error || isinf(val))
    1010          20 :                         RETURN_ERROR(ereport(ERROR,
    1011             :                                              (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
    1012             :                                               errmsg("jsonpath item method .%s() can only be applied to a numeric value",
    1013             :                                                      jspOperationName(jsp->type)))));
    1014             : 
    1015          12 :                     jb = &jbv;
    1016          12 :                     jb->type = jbvNumeric;
    1017          12 :                     jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
    1018             :                                                                           Float8GetDatum(val)));
    1019          12 :                     res = jperOk;
    1020             :                 }
    1021             : 
    1022          48 :                 if (res == jperNotFound)
    1023          32 :                     RETURN_ERROR(ereport(ERROR,
    1024             :                                          (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
    1025             :                                           errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
    1026             :                                                  jspOperationName(jsp->type)))));
    1027             : 
    1028          16 :                 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
    1029             :             }
    1030          16 :             break;
    1031             : 
    1032             :         case jpiKeyValue:
    1033          60 :             if (unwrap && JsonbType(jb) == jbvArray)
    1034           4 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1035             : 
    1036          56 :             return executeKeyValueMethod(cxt, jsp, jb, found);
    1037             : 
    1038             :         default:
    1039           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
    1040             :     }
    1041             : 
    1042      361336 :     return res;
    1043             : }
    1044             : 
    1045             : /*
    1046             :  * Unwrap current array item and execute jsonpath for each of its elements.
    1047             :  */
    1048             : static JsonPathExecResult
    1049         740 : executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1050             :                              JsonbValue *jb, JsonValueList *found,
    1051             :                              bool unwrapElements)
    1052             : {
    1053         740 :     if (jb->type != jbvBinary)
    1054             :     {
    1055             :         Assert(jb->type != jbvArray);
    1056           0 :         elog(ERROR, "invalid jsonb array value type: %d", jb->type);
    1057             :     }
    1058             : 
    1059         740 :     return executeAnyItem
    1060             :         (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
    1061             :          false, unwrapElements);
    1062             : }
    1063             : 
    1064             : /*
    1065             :  * Execute next jsonpath item if exists.  Otherwise put "v" to the "found"
    1066             :  * list if provided.
    1067             :  */
    1068             : static JsonPathExecResult
    1069      258256 : executeNextItem(JsonPathExecContext *cxt,
    1070             :                 JsonPathItem *cur, JsonPathItem *next,
    1071             :                 JsonbValue *v, JsonValueList *found, bool copy)
    1072             : {
    1073             :     JsonPathItem elem;
    1074             :     bool        hasNext;
    1075             : 
    1076      258256 :     if (!cur)
    1077           0 :         hasNext = next != NULL;
    1078      258256 :     else if (next)
    1079       99044 :         hasNext = jspHasNext(cur);
    1080             :     else
    1081             :     {
    1082      159212 :         next = &elem;
    1083      159212 :         hasNext = jspGetNext(cur, next);
    1084             :     }
    1085             : 
    1086      258256 :     if (hasNext)
    1087      118632 :         return executeItem(cxt, next, v, found);
    1088             : 
    1089      139624 :     if (found)
    1090      106264 :         JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
    1091             : 
    1092      139624 :     return jperOk;
    1093             : }
    1094             : 
    1095             : /*
    1096             :  * Same as executeItem(), but when "unwrap == true" automatically unwraps
    1097             :  * each array item from the resulting sequence in lax mode.
    1098             :  */
    1099             : static JsonPathExecResult
    1100      118564 : executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1101             :                            JsonbValue *jb, bool unwrap,
    1102             :                            JsonValueList *found)
    1103             : {
    1104      118564 :     if (unwrap && jspAutoUnwrap(cxt))
    1105             :     {
    1106       65640 :         JsonValueList seq = {0};
    1107             :         JsonValueListIterator it;
    1108       65640 :         JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
    1109             :         JsonbValue *item;
    1110             : 
    1111       65624 :         if (jperIsError(res))
    1112          44 :             return res;
    1113             : 
    1114       65580 :         JsonValueListInitIterator(&seq, &it);
    1115      171512 :         while ((item = JsonValueListNext(&seq, &it)))
    1116             :         {
    1117             :             Assert(item->type != jbvArray);
    1118             : 
    1119       40352 :             if (JsonbType(item) == jbvArray)
    1120          36 :                 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
    1121             :             else
    1122       40316 :                 JsonValueListAppend(found, item);
    1123             :         }
    1124             : 
    1125       65580 :         return jperOk;
    1126             :     }
    1127             : 
    1128       52924 :     return executeItem(cxt, jsp, jb, found);
    1129             : }
    1130             : 
    1131             : /*
    1132             :  * Same as executeItemOptUnwrapResult(), but with error suppression.
    1133             :  */
    1134             : static JsonPathExecResult
    1135      117988 : executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
    1136             :                                   JsonPathItem *jsp,
    1137             :                                   JsonbValue *jb, bool unwrap,
    1138             :                                   JsonValueList *found)
    1139             : {
    1140             :     JsonPathExecResult res;
    1141      117988 :     bool        throwErrors = cxt->throwErrors;
    1142             : 
    1143      117988 :     cxt->throwErrors = false;
    1144      117988 :     res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
    1145      117984 :     cxt->throwErrors = throwErrors;
    1146             : 
    1147      117984 :     return res;
    1148             : }
    1149             : 
    1150             : /* Execute boolean-valued jsonpath expression. */
    1151             : static JsonPathBool
    1152      111328 : executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1153             :                 JsonbValue *jb, bool canHaveNext)
    1154             : {
    1155             :     JsonPathItem larg;
    1156             :     JsonPathItem rarg;
    1157             :     JsonPathBool res;
    1158             :     JsonPathBool res2;
    1159             : 
    1160      111328 :     if (!canHaveNext && jspHasNext(jsp))
    1161           0 :         elog(ERROR, "boolean jsonpath item cannot have next item");
    1162             : 
    1163      111328 :     switch (jsp->type)
    1164             :     {
    1165             :         case jpiAnd:
    1166       17020 :             jspGetLeftArg(jsp, &larg);
    1167       17020 :             res = executeBoolItem(cxt, &larg, jb, false);
    1168             : 
    1169       17020 :             if (res == jpbFalse)
    1170       14980 :                 return jpbFalse;
    1171             : 
    1172             :             /*
    1173             :              * SQL/JSON says that we should check second arg in case of
    1174             :              * jperError
    1175             :              */
    1176             : 
    1177        2040 :             jspGetRightArg(jsp, &rarg);
    1178        2040 :             res2 = executeBoolItem(cxt, &rarg, jb, false);
    1179             : 
    1180        2040 :             return res2 == jpbTrue ? res : res2;
    1181             : 
    1182             :         case jpiOr:
    1183        8636 :             jspGetLeftArg(jsp, &larg);
    1184        8636 :             res = executeBoolItem(cxt, &larg, jb, false);
    1185             : 
    1186        8636 :             if (res == jpbTrue)
    1187        1660 :                 return jpbTrue;
    1188             : 
    1189        6976 :             jspGetRightArg(jsp, &rarg);
    1190        6976 :             res2 = executeBoolItem(cxt, &rarg, jb, false);
    1191             : 
    1192        6976 :             return res2 == jpbFalse ? res : res2;
    1193             : 
    1194             :         case jpiNot:
    1195          72 :             jspGetArg(jsp, &larg);
    1196             : 
    1197          72 :             res = executeBoolItem(cxt, &larg, jb, false);
    1198             : 
    1199          72 :             if (res == jpbUnknown)
    1200          24 :                 return jpbUnknown;
    1201             : 
    1202          48 :             return res == jpbTrue ? jpbFalse : jpbTrue;
    1203             : 
    1204             :         case jpiIsUnknown:
    1205         140 :             jspGetArg(jsp, &larg);
    1206         140 :             res = executeBoolItem(cxt, &larg, jb, false);
    1207         140 :             return res == jpbUnknown ? jpbTrue : jpbFalse;
    1208             : 
    1209             :         case jpiEqual:
    1210             :         case jpiNotEqual:
    1211             :         case jpiLess:
    1212             :         case jpiGreater:
    1213             :         case jpiLessOrEqual:
    1214             :         case jpiGreaterOrEqual:
    1215       32484 :             jspGetLeftArg(jsp, &larg);
    1216       32484 :             jspGetRightArg(jsp, &rarg);
    1217       32484 :             return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
    1218             :                                     executeComparison, NULL);
    1219             : 
    1220             :         case jpiStartsWith:     /* 'whole STARTS WITH initial' */
    1221          56 :             jspGetLeftArg(jsp, &larg);  /* 'whole' */
    1222          56 :             jspGetRightArg(jsp, &rarg); /* 'initial' */
    1223          56 :             return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
    1224             :                                     executeStartsWith, NULL);
    1225             : 
    1226             :         case jpiLikeRegex:      /* 'expr LIKE_REGEX pattern FLAGS flags' */
    1227             :             {
    1228             :                 /*
    1229             :                  * 'expr' is a sequence-returning expression.  'pattern' is a
    1230             :                  * regex string literal.  SQL/JSON standard requires XQuery
    1231             :                  * regexes, but we use Postgres regexes here.  'flags' is a
    1232             :                  * string literal converted to integer flags at compile-time.
    1233             :                  */
    1234         128 :                 JsonLikeRegexContext lrcxt = {0};
    1235             : 
    1236         128 :                 jspInitByBuffer(&larg, jsp->base,
    1237             :                                 jsp->content.like_regex.expr);
    1238             : 
    1239         128 :                 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
    1240             :                                         executeLikeRegex, &lrcxt);
    1241             :             }
    1242             : 
    1243             :         case jpiExists:
    1244       52792 :             jspGetArg(jsp, &larg);
    1245             : 
    1246       52792 :             if (jspStrictAbsenseOfErrors(cxt))
    1247             :             {
    1248             :                 /*
    1249             :                  * In strict mode we must get a complete list of values to
    1250             :                  * check that there are no errors at all.
    1251             :                  */
    1252          32 :                 JsonValueList vals = {0};
    1253          32 :                 JsonPathExecResult res =
    1254             :                 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
    1255             :                                                   false, &vals);
    1256             : 
    1257          32 :                 if (jperIsError(res))
    1258          24 :                     return jpbUnknown;
    1259             : 
    1260           8 :                 return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
    1261             :             }
    1262             :             else
    1263             :             {
    1264       52760 :                 JsonPathExecResult res =
    1265             :                 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
    1266             :                                                   false, NULL);
    1267             : 
    1268       52760 :                 if (jperIsError(res))
    1269          16 :                     return jpbUnknown;
    1270             : 
    1271       52744 :                 return res == jperOk ? jpbTrue : jpbFalse;
    1272             :             }
    1273             : 
    1274             :         default:
    1275           0 :             elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
    1276             :             return jpbUnknown;
    1277             :     }
    1278             : }
    1279             : 
    1280             : /*
    1281             :  * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
    1282             :  * item onto the stack.
    1283             :  */
    1284             : static JsonPathBool
    1285       11220 : executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1286             :                       JsonbValue *jb)
    1287             : {
    1288             :     JsonbValue *prev;
    1289             :     JsonPathBool res;
    1290             : 
    1291       11220 :     prev = cxt->current;
    1292       11220 :     cxt->current = jb;
    1293       11220 :     res = executeBoolItem(cxt, jsp, jb, false);
    1294       11216 :     cxt->current = prev;
    1295             : 
    1296       11216 :     return res;
    1297             : }
    1298             : 
    1299             : /*
    1300             :  * Implementation of several jsonpath nodes:
    1301             :  *  - jpiAny (.** accessor),
    1302             :  *  - jpiAnyKey (.* accessor),
    1303             :  *  - jpiAnyArray ([*] accessor)
    1304             :  */
    1305             : static JsonPathExecResult
    1306        1108 : executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
    1307             :                JsonValueList *found, uint32 level, uint32 first, uint32 last,
    1308             :                bool ignoreStructuralErrors, bool unwrapNext)
    1309             : {
    1310        1108 :     JsonPathExecResult res = jperNotFound;
    1311             :     JsonbIterator *it;
    1312             :     int32       r;
    1313             :     JsonbValue  v;
    1314             : 
    1315        1108 :     check_stack_depth();
    1316             : 
    1317        1108 :     if (level > last)
    1318          20 :         return res;
    1319             : 
    1320        1088 :     it = JsonbIteratorInit(jbc);
    1321             : 
    1322             :     /*
    1323             :      * Recursively iterate over jsonb objects/arrays
    1324             :      */
    1325        6068 :     while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
    1326             :     {
    1327        4216 :         if (r == WJB_KEY)
    1328             :         {
    1329         368 :             r = JsonbIteratorNext(&it, &v, true);
    1330             :             Assert(r == WJB_VALUE);
    1331             :         }
    1332             : 
    1333        4216 :         if (r == WJB_VALUE || r == WJB_ELEM)
    1334             :         {
    1335             : 
    1336        2364 :             if (level >= first ||
    1337          16 :                 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
    1338           8 :                  v.type != jbvBinary))  /* leaves only requested */
    1339             :             {
    1340             :                 /* check expression */
    1341        2324 :                 if (jsp)
    1342             :                 {
    1343        1572 :                     if (ignoreStructuralErrors)
    1344             :                     {
    1345             :                         bool        savedIgnoreStructuralErrors;
    1346             : 
    1347         232 :                         savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
    1348         232 :                         cxt->ignoreStructuralErrors = true;
    1349         232 :                         res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
    1350         232 :                         cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
    1351             :                     }
    1352             :                     else
    1353        1340 :                         res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
    1354             : 
    1355        1548 :                     if (jperIsError(res))
    1356          44 :                         break;
    1357             : 
    1358        1504 :                     if (res == jperOk && !found)
    1359         216 :                         break;
    1360             :                 }
    1361         752 :                 else if (found)
    1362         736 :                     JsonValueListAppend(found, copyJsonbValue(&v));
    1363             :                 else
    1364          16 :                     return jperOk;
    1365             :             }
    1366             : 
    1367        2064 :             if (level < last && v.type == jbvBinary)
    1368             :             {
    1369         124 :                 res = executeAnyItem
    1370             :                     (cxt, jsp, v.val.binary.data, found,
    1371             :                      level + 1, first, last,
    1372             :                      ignoreStructuralErrors, unwrapNext);
    1373             : 
    1374         124 :                 if (jperIsError(res))
    1375           0 :                     break;
    1376             : 
    1377         124 :                 if (res == jperOk && found == NULL)
    1378          24 :                     break;
    1379             :             }
    1380             :         }
    1381             :     }
    1382             : 
    1383        1048 :     return res;
    1384             : }
    1385             : 
    1386             : /*
    1387             :  * Execute unary or binary predicate.
    1388             :  *
    1389             :  * Predicates have existence semantics, because their operands are item
    1390             :  * sequences.  Pairs of items from the left and right operand's sequences are
    1391             :  * checked.  TRUE returned only if any pair satisfying the condition is found.
    1392             :  * In strict mode, even if the desired pair has already been found, all pairs
    1393             :  * still need to be examined to check the absence of errors.  If any error
    1394             :  * occurs, UNKNOWN (analogous to SQL NULL) is returned.
    1395             :  */
    1396             : static JsonPathBool
    1397       32668 : executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
    1398             :                  JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
    1399             :                  bool unwrapRightArg, JsonPathPredicateCallback exec,
    1400             :                  void *param)
    1401             : {
    1402             :     JsonPathExecResult res;
    1403             :     JsonValueListIterator lseqit;
    1404       32668 :     JsonValueList lseq = {0};
    1405       32668 :     JsonValueList rseq = {0};
    1406             :     JsonbValue *lval;
    1407       32668 :     bool        error = false;
    1408       32668 :     bool        found = false;
    1409             : 
    1410             :     /* Left argument is always auto-unwrapped. */
    1411       32668 :     res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
    1412       32668 :     if (jperIsError(res))
    1413          12 :         return jpbUnknown;
    1414             : 
    1415       32656 :     if (rarg)
    1416             :     {
    1417             :         /* Right argument is conditionally auto-unwrapped. */
    1418       32528 :         res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
    1419             :                                                 unwrapRightArg, &rseq);
    1420       32524 :         if (jperIsError(res))
    1421          36 :             return jpbUnknown;
    1422             :     }
    1423             : 
    1424       32616 :     JsonValueListInitIterator(&lseq, &lseqit);
    1425       32616 :     while ((lval = JsonValueListNext(&lseq, &lseqit)))
    1426             :     {
    1427             :         JsonValueListIterator rseqit;
    1428             :         JsonbValue *rval;
    1429       10748 :         bool        first = true;
    1430             : 
    1431       10748 :         JsonValueListInitIterator(&rseq, &rseqit);
    1432       10748 :         if (rarg)
    1433       10620 :             rval = JsonValueListNext(&rseq, &rseqit);
    1434             :         else
    1435         128 :             rval = NULL;
    1436             : 
    1437             :         /* Loop over right arg sequence or do single pass otherwise */
    1438       27272 :         while (rarg ? (rval != NULL) : first)
    1439             :         {
    1440        7452 :             JsonPathBool res = exec(pred, lval, rval, param);
    1441             : 
    1442        7452 :             if (res == jpbUnknown)
    1443             :             {
    1444         236 :                 if (jspStrictAbsenseOfErrors(cxt))
    1445        1692 :                     return jpbUnknown;
    1446             : 
    1447         220 :                 error = true;
    1448             :             }
    1449        7216 :             else if (res == jpbTrue)
    1450             :             {
    1451        1692 :                 if (!jspStrictAbsenseOfErrors(cxt))
    1452        1660 :                     return jpbTrue;
    1453             : 
    1454          32 :                 found = true;
    1455             :             }
    1456             : 
    1457        5776 :             first = false;
    1458        5776 :             if (rarg)
    1459        5688 :                 rval = JsonValueListNext(&rseq, &rseqit);
    1460             :         }
    1461             :     }
    1462             : 
    1463       30940 :     if (found)                  /* possible only in strict mode */
    1464          12 :         return jpbTrue;
    1465             : 
    1466       30928 :     if (error)                  /* possible only in lax mode */
    1467         200 :         return jpbUnknown;
    1468             : 
    1469       30728 :     return jpbFalse;
    1470             : }
    1471             : 
    1472             : /*
    1473             :  * Execute binary arithmetic expression on singleton numeric operands.
    1474             :  * Array operands are automatically unwrapped in lax mode.
    1475             :  */
    1476             : static JsonPathExecResult
    1477         228 : executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1478             :                         JsonbValue *jb, BinaryArithmFunc func,
    1479             :                         JsonValueList *found)
    1480             : {
    1481             :     JsonPathExecResult jper;
    1482             :     JsonPathItem elem;
    1483         228 :     JsonValueList lseq = {0};
    1484         228 :     JsonValueList rseq = {0};
    1485             :     JsonbValue *lval;
    1486             :     JsonbValue *rval;
    1487             :     Numeric     res;
    1488             : 
    1489         228 :     jspGetLeftArg(jsp, &elem);
    1490             : 
    1491             :     /*
    1492             :      * XXX: By standard only operands of multiplicative expressions are
    1493             :      * unwrapped.  We extend it to other binary arithmetic expressions too.
    1494             :      */
    1495         228 :     jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
    1496         224 :     if (jperIsError(jper))
    1497           0 :         return jper;
    1498             : 
    1499         224 :     jspGetRightArg(jsp, &elem);
    1500             : 
    1501         224 :     jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
    1502         220 :     if (jperIsError(jper))
    1503           0 :         return jper;
    1504             : 
    1505         396 :     if (JsonValueListLength(&lseq) != 1 ||
    1506         176 :         !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
    1507          44 :         RETURN_ERROR(ereport(ERROR,
    1508             :                              (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
    1509             :                               errmsg("left operand of jsonpath operator %s is not a single numeric value",
    1510             :                                      jspOperationName(jsp->type)))));
    1511             : 
    1512         332 :     if (JsonValueListLength(&rseq) != 1 ||
    1513         156 :         !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
    1514          36 :         RETURN_ERROR(ereport(ERROR,
    1515             :                              (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
    1516             :                               errmsg("right operand of jsonpath operator %s is not a single numeric value",
    1517             :                                      jspOperationName(jsp->type)))));
    1518             : 
    1519         140 :     if (jspThrowErrors(cxt))
    1520             :     {
    1521          52 :         res = func(lval->val.numeric, rval->val.numeric, NULL);
    1522             :     }
    1523             :     else
    1524             :     {
    1525          88 :         bool        error = false;
    1526             : 
    1527          88 :         res = func(lval->val.numeric, rval->val.numeric, &error);
    1528             : 
    1529          88 :         if (error)
    1530           8 :             return jperError;
    1531             :     }
    1532             : 
    1533         116 :     if (!jspGetNext(jsp, &elem) && !found)
    1534           4 :         return jperOk;
    1535             : 
    1536         112 :     lval = palloc(sizeof(*lval));
    1537         112 :     lval->type = jbvNumeric;
    1538         112 :     lval->val.numeric = res;
    1539             : 
    1540         112 :     return executeNextItem(cxt, jsp, &elem, lval, found, false);
    1541             : }
    1542             : 
    1543             : /*
    1544             :  * Execute unary arithmetic expression for each numeric item in its operand's
    1545             :  * sequence.  Array operand is automatically unwrapped in lax mode.
    1546             :  */
    1547             : static JsonPathExecResult
    1548         124 : executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1549             :                        JsonbValue *jb, PGFunction func, JsonValueList *found)
    1550             : {
    1551             :     JsonPathExecResult jper;
    1552             :     JsonPathExecResult jper2;
    1553             :     JsonPathItem elem;
    1554         124 :     JsonValueList seq = {0};
    1555             :     JsonValueListIterator it;
    1556             :     JsonbValue *val;
    1557             :     bool        hasNext;
    1558             : 
    1559         124 :     jspGetArg(jsp, &elem);
    1560         124 :     jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
    1561             : 
    1562         120 :     if (jperIsError(jper))
    1563           0 :         return jper;
    1564             : 
    1565         120 :     jper = jperNotFound;
    1566             : 
    1567         120 :     hasNext = jspGetNext(jsp, &elem);
    1568             : 
    1569         120 :     JsonValueListInitIterator(&seq, &it);
    1570         336 :     while ((val = JsonValueListNext(&seq, &it)))
    1571             :     {
    1572         128 :         if ((val = getScalar(val, jbvNumeric)))
    1573             :         {
    1574         100 :             if (!found && !hasNext)
    1575           8 :                 return jperOk;
    1576             :         }
    1577             :         else
    1578             :         {
    1579          28 :             if (!found && !hasNext)
    1580           4 :                 continue;       /* skip non-numerics processing */
    1581             : 
    1582          24 :             RETURN_ERROR(ereport(ERROR,
    1583             :                                  (errcode(ERRCODE_JSON_NUMBER_NOT_FOUND),
    1584             :                                   errmsg("operand of unary jsonpath operator %s is not a numeric value",
    1585             :                                          jspOperationName(jsp->type)))));
    1586             :         }
    1587             : 
    1588          92 :         if (func)
    1589          56 :             val->val.numeric =
    1590          56 :                 DatumGetNumeric(DirectFunctionCall1(func,
    1591             :                                                     NumericGetDatum(val->val.numeric)));
    1592             : 
    1593          92 :         jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
    1594             : 
    1595          92 :         if (jperIsError(jper2))
    1596           0 :             return jper2;
    1597             : 
    1598          92 :         if (jper2 == jperOk)
    1599             :         {
    1600          92 :             if (!found)
    1601           0 :                 return jperOk;
    1602          92 :             jper = jperOk;
    1603             :         }
    1604             :     }
    1605             : 
    1606          88 :     return jper;
    1607             : }
    1608             : 
    1609             : /*
    1610             :  * STARTS_WITH predicate callback.
    1611             :  *
    1612             :  * Check if the 'whole' string starts from 'initial' string.
    1613             :  */
    1614             : static JsonPathBool
    1615         116 : executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
    1616             :                   void *param)
    1617             : {
    1618         116 :     if (!(whole = getScalar(whole, jbvString)))
    1619          32 :         return jpbUnknown;      /* error */
    1620             : 
    1621          84 :     if (!(initial = getScalar(initial, jbvString)))
    1622           0 :         return jpbUnknown;      /* error */
    1623             : 
    1624         144 :     if (whole->val.string.len >= initial->val.string.len &&
    1625         120 :         !memcmp(whole->val.string.val,
    1626          60 :                 initial->val.string.val,
    1627          60 :                 initial->val.string.len))
    1628          36 :         return jpbTrue;
    1629             : 
    1630          48 :     return jpbFalse;
    1631             : }
    1632             : 
    1633             : /*
    1634             :  * LIKE_REGEX predicate callback.
    1635             :  *
    1636             :  * Check if the string matches regex pattern.
    1637             :  */
    1638             : static JsonPathBool
    1639         128 : executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
    1640             :                  void *param)
    1641             : {
    1642         128 :     JsonLikeRegexContext *cxt = param;
    1643             : 
    1644         128 :     if (!(str = getScalar(str, jbvString)))
    1645          32 :         return jpbUnknown;
    1646             : 
    1647             :     /* Cache regex text and converted flags. */
    1648          96 :     if (!cxt->regex)
    1649             :     {
    1650          96 :         uint32      flags = jsp->content.like_regex.flags;
    1651             : 
    1652          96 :         cxt->regex =
    1653          96 :             cstring_to_text_with_len(jsp->content.like_regex.pattern,
    1654             :                                      jsp->content.like_regex.patternlen);
    1655             : 
    1656             :         /* Convert regex flags. */
    1657          96 :         cxt->cflags = REG_ADVANCED;
    1658             : 
    1659          96 :         if (flags & JSP_REGEX_ICASE)
    1660          24 :             cxt->cflags |= REG_ICASE;
    1661          96 :         if (flags & JSP_REGEX_MLINE)
    1662          24 :             cxt->cflags |= REG_NEWLINE;
    1663          96 :         if (flags & JSP_REGEX_SLINE)
    1664          24 :             cxt->cflags &= ~REG_NEWLINE;
    1665          96 :         if (flags & JSP_REGEX_WSPACE)
    1666          24 :             cxt->cflags |= REG_EXPANDED;
    1667             :     }
    1668             : 
    1669          96 :     if (RE_compile_and_execute(cxt->regex, str->val.string.val,
    1670             :                                str->val.string.len,
    1671             :                                cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
    1672          40 :         return jpbTrue;
    1673             : 
    1674          56 :     return jpbFalse;
    1675             : }
    1676             : 
    1677             : /*
    1678             :  * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
    1679             :  * user function 'func'.
    1680             :  */
    1681             : static JsonPathExecResult
    1682         172 : executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1683             :                          JsonbValue *jb, bool unwrap, PGFunction func,
    1684             :                          JsonValueList *found)
    1685             : {
    1686             :     JsonPathItem next;
    1687             :     Datum       datum;
    1688             : 
    1689         172 :     if (unwrap && JsonbType(jb) == jbvArray)
    1690           0 :         return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1691             : 
    1692         172 :     if (!(jb = getScalar(jb, jbvNumeric)))
    1693          24 :         RETURN_ERROR(ereport(ERROR,
    1694             :                              (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
    1695             :                               errmsg("jsonpath item method .%s() can only be applied to a numeric value",
    1696             :                                      jspOperationName(jsp->type)))));
    1697             : 
    1698         148 :     datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
    1699             : 
    1700         148 :     if (!jspGetNext(jsp, &next) && !found)
    1701           0 :         return jperOk;
    1702             : 
    1703         148 :     jb = palloc(sizeof(*jb));
    1704         148 :     jb->type = jbvNumeric;
    1705         148 :     jb->val.numeric = DatumGetNumeric(datum);
    1706             : 
    1707         148 :     return executeNextItem(cxt, jsp, &next, jb, found, false);
    1708             : }
    1709             : 
    1710             : /*
    1711             :  * Implementation of .keyvalue() method.
    1712             :  *
    1713             :  * .keyvalue() method returns a sequence of object's key-value pairs in the
    1714             :  * following format: '{ "key": key, "value": value, "id": id }'.
    1715             :  *
    1716             :  * "id" field is an object identifier which is constructed from the two parts:
    1717             :  * base object id and its binary offset in base object's jsonb:
    1718             :  * id = 10000000000 * base_object_id + obj_offset_in_base_object
    1719             :  *
    1720             :  * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
    1721             :  * (maximal offset in jsonb).  Decimal multiplier is used here to improve the
    1722             :  * readability of identifiers.
    1723             :  *
    1724             :  * Base object is usually a root object of the path: context item '$' or path
    1725             :  * variable '$var', literals can't produce objects for now.  But if the path
    1726             :  * contains generated objects (.keyvalue() itself, for example), then they
    1727             :  * become base object for the subsequent .keyvalue().
    1728             :  *
    1729             :  * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
    1730             :  * of variables (see getJsonPathVariable()).  Ids for generated objects
    1731             :  * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
    1732             :  */
    1733             : static JsonPathExecResult
    1734          56 : executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1735             :                       JsonbValue *jb, JsonValueList *found)
    1736             : {
    1737          56 :     JsonPathExecResult res = jperNotFound;
    1738             :     JsonPathItem next;
    1739             :     JsonbContainer *jbc;
    1740             :     JsonbValue  key;
    1741             :     JsonbValue  val;
    1742             :     JsonbValue  idval;
    1743             :     JsonbValue  keystr;
    1744             :     JsonbValue  valstr;
    1745             :     JsonbValue  idstr;
    1746             :     JsonbIterator *it;
    1747             :     JsonbIteratorToken tok;
    1748             :     int64       id;
    1749             :     bool        hasNext;
    1750             : 
    1751          56 :     if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
    1752          16 :         RETURN_ERROR(ereport(ERROR,
    1753             :                              (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
    1754             :                               errmsg("jsonpath item method .%s() can only be applied to an object",
    1755             :                                      jspOperationName(jsp->type)))));
    1756             : 
    1757          40 :     jbc = jb->val.binary.data;
    1758             : 
    1759          40 :     if (!JsonContainerSize(jbc))
    1760          12 :         return jperNotFound;    /* no key-value pairs */
    1761             : 
    1762          28 :     hasNext = jspGetNext(jsp, &next);
    1763             : 
    1764          28 :     keystr.type = jbvString;
    1765          28 :     keystr.val.string.val = "key";
    1766          28 :     keystr.val.string.len = 3;
    1767             : 
    1768          28 :     valstr.type = jbvString;
    1769          28 :     valstr.val.string.val = "value";
    1770          28 :     valstr.val.string.len = 5;
    1771             : 
    1772          28 :     idstr.type = jbvString;
    1773          28 :     idstr.val.string.val = "id";
    1774          28 :     idstr.val.string.len = 2;
    1775             : 
    1776             :     /* construct object id from its base object and offset inside that */
    1777          56 :     id = jb->type != jbvBinary ? 0 :
    1778          28 :         (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
    1779          28 :     id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
    1780             : 
    1781          28 :     idval.type = jbvNumeric;
    1782          28 :     idval.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
    1783             :                                                             Int64GetDatum(id)));
    1784             : 
    1785          28 :     it = JsonbIteratorInit(jbc);
    1786             : 
    1787         140 :     while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
    1788             :     {
    1789             :         JsonBaseObjectInfo baseObject;
    1790             :         JsonbValue  obj;
    1791             :         JsonbParseState *ps;
    1792             :         JsonbValue *keyval;
    1793             :         Jsonb      *jsonb;
    1794             : 
    1795          92 :         if (tok != WJB_KEY)
    1796          48 :             continue;
    1797             : 
    1798          44 :         res = jperOk;
    1799             : 
    1800          44 :         if (!hasNext && !found)
    1801          12 :             break;
    1802             : 
    1803          40 :         tok = JsonbIteratorNext(&it, &val, true);
    1804             :         Assert(tok == WJB_VALUE);
    1805             : 
    1806          40 :         ps = NULL;
    1807          40 :         pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
    1808             : 
    1809          40 :         pushJsonbValue(&ps, WJB_KEY, &keystr);
    1810          40 :         pushJsonbValue(&ps, WJB_VALUE, &key);
    1811             : 
    1812          40 :         pushJsonbValue(&ps, WJB_KEY, &valstr);
    1813          40 :         pushJsonbValue(&ps, WJB_VALUE, &val);
    1814             : 
    1815          40 :         pushJsonbValue(&ps, WJB_KEY, &idstr);
    1816          40 :         pushJsonbValue(&ps, WJB_VALUE, &idval);
    1817             : 
    1818          40 :         keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
    1819             : 
    1820          40 :         jsonb = JsonbValueToJsonb(keyval);
    1821             : 
    1822          40 :         JsonbInitBinary(&obj, jsonb);
    1823             : 
    1824          40 :         baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
    1825             : 
    1826          40 :         res = executeNextItem(cxt, jsp, &next, &obj, found, true);
    1827             : 
    1828          40 :         cxt->baseObject = baseObject;
    1829             : 
    1830          40 :         if (jperIsError(res))
    1831           0 :             return res;
    1832             : 
    1833          40 :         if (res == jperOk && !found)
    1834           4 :             break;
    1835             :     }
    1836             : 
    1837          28 :     return res;
    1838             : }
    1839             : 
    1840             : /*
    1841             :  * Convert boolean execution status 'res' to a boolean JSON item and execute
    1842             :  * next jsonpath.
    1843             :  */
    1844             : static JsonPathExecResult
    1845       65224 : appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1846             :                  JsonValueList *found, JsonPathBool res)
    1847             : {
    1848             :     JsonPathItem next;
    1849             :     JsonbValue  jbv;
    1850             : 
    1851       65224 :     if (!jspGetNext(jsp, &next) && !found)
    1852           4 :         return jperOk;          /* found singleton boolean value */
    1853             : 
    1854       65220 :     if (res == jpbUnknown)
    1855             :     {
    1856          20 :         jbv.type = jbvNull;
    1857             :     }
    1858             :     else
    1859             :     {
    1860       65200 :         jbv.type = jbvBool;
    1861       65200 :         jbv.val.boolean = res == jpbTrue;
    1862             :     }
    1863             : 
    1864       65220 :     return executeNextItem(cxt, jsp, &next, &jbv, found, true);
    1865             : }
    1866             : 
    1867             : /*
    1868             :  * Convert jsonpath's scalar or variable node to actual jsonb value.
    1869             :  *
    1870             :  * If node is a variable then its id returned, otherwise 0 returned.
    1871             :  */
    1872             : static void
    1873       33164 : getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
    1874             :                 JsonbValue *value)
    1875             : {
    1876       33164 :     switch (item->type)
    1877             :     {
    1878             :         case jpiNull:
    1879        5036 :             value->type = jbvNull;
    1880        5036 :             break;
    1881             :         case jpiBool:
    1882         936 :             value->type = jbvBool;
    1883         936 :             value->val.boolean = jspGetBool(item);
    1884         936 :             break;
    1885             :         case jpiNumeric:
    1886       12904 :             value->type = jbvNumeric;
    1887       12904 :             value->val.numeric = jspGetNumeric(item);
    1888       12904 :             break;
    1889             :         case jpiString:
    1890       13684 :             value->type = jbvString;
    1891       13684 :             value->val.string.val = jspGetString(item,
    1892       13684 :                                                  &value->val.string.len);
    1893       13684 :             break;
    1894             :         case jpiVariable:
    1895         604 :             getJsonPathVariable(cxt, item, cxt->vars, value);
    1896         600 :             return;
    1897             :         default:
    1898           0 :             elog(ERROR, "unexpected jsonpath item type");
    1899             :     }
    1900             : }
    1901             : 
    1902             : /*
    1903             :  * Get the value of variable passed to jsonpath executor
    1904             :  */
    1905             : static void
    1906         604 : getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
    1907             :                     Jsonb *vars, JsonbValue *value)
    1908             : {
    1909             :     char       *varName;
    1910             :     int         varNameLength;
    1911             :     JsonbValue  tmp;
    1912             :     JsonbValue *v;
    1913             : 
    1914         604 :     if (!vars)
    1915             :     {
    1916           0 :         value->type = jbvNull;
    1917           0 :         return;
    1918             :     }
    1919             : 
    1920             :     Assert(variable->type == jpiVariable);
    1921         604 :     varName = jspGetString(variable, &varNameLength);
    1922         604 :     tmp.type = jbvString;
    1923         604 :     tmp.val.string.val = varName;
    1924         604 :     tmp.val.string.len = varNameLength;
    1925             : 
    1926         604 :     v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
    1927             : 
    1928         604 :     if (v)
    1929             :     {
    1930         600 :         *value = *v;
    1931         600 :         pfree(v);
    1932             :     }
    1933             :     else
    1934             :     {
    1935           4 :         ereport(ERROR,
    1936             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    1937             :                  errmsg("could not find jsonpath variable \"%s\"",
    1938             :                         pnstrdup(varName, varNameLength))));
    1939             :     }
    1940             : 
    1941         600 :     JsonbInitBinary(&tmp, vars);
    1942         600 :     setBaseObject(cxt, &tmp, 1);
    1943             : }
    1944             : 
    1945             : /**************** Support functions for JsonPath execution *****************/
    1946             : 
    1947             : /*
    1948             :  * Returns the size of an array item, or -1 if item is not an array.
    1949             :  */
    1950             : static int
    1951         232 : JsonbArraySize(JsonbValue *jb)
    1952             : {
    1953             :     Assert(jb->type != jbvArray);
    1954             : 
    1955         232 :     if (jb->type == jbvBinary)
    1956             :     {
    1957         204 :         JsonbContainer *jbc = jb->val.binary.data;
    1958             : 
    1959         204 :         if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
    1960         196 :             return JsonContainerSize(jbc);
    1961             :     }
    1962             : 
    1963          36 :     return -1;
    1964             : }
    1965             : 
    1966             : /* Comparison predicate callback. */
    1967             : static JsonPathBool
    1968        7208 : executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
    1969             : {
    1970        7208 :     return compareItems(cmp->type, lv, rv);
    1971             : }
    1972             : 
    1973             : /*
    1974             :  * Compare two SQL/JSON items using comparison operation 'op'.
    1975             :  */
    1976             : static JsonPathBool
    1977        7208 : compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2)
    1978             : {
    1979             :     int         cmp;
    1980             :     bool        res;
    1981             : 
    1982        7208 :     if (jb1->type != jb2->type)
    1983             :     {
    1984        1980 :         if (jb1->type == jbvNull || jb2->type == jbvNull)
    1985             : 
    1986             :             /*
    1987             :              * Equality and order comparison of nulls to non-nulls returns
    1988             :              * always false, but inequality comparison returns true.
    1989             :              */
    1990        1816 :             return op == jpiNotEqual ? jpbTrue : jpbFalse;
    1991             : 
    1992             :         /* Non-null items of different types are not comparable. */
    1993         164 :         return jpbUnknown;
    1994             :     }
    1995             : 
    1996        5228 :     switch (jb1->type)
    1997             :     {
    1998             :         case jbvNull:
    1999         124 :             cmp = 0;
    2000         124 :             break;
    2001             :         case jbvBool:
    2002         844 :             cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
    2003         264 :                 jb1->val.boolean ? 1 : -1;
    2004         580 :             break;
    2005             :         case jbvNumeric:
    2006         776 :             cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
    2007         776 :             break;
    2008             :         case jbvString:
    2009        3740 :             if (op == jpiEqual)
    2010        3740 :                 return jb1->val.string.len != jb2->val.string.len ||
    2011        4504 :                     memcmp(jb1->val.string.val,
    2012        2252 :                            jb2->val.string.val,
    2013        5992 :                            jb1->val.string.len) ? jpbFalse : jpbTrue;
    2014             : 
    2015           0 :             cmp = varstr_cmp(jb1->val.string.val, jb1->val.string.len,
    2016           0 :                              jb2->val.string.val, jb2->val.string.len,
    2017             :                              DEFAULT_COLLATION_OID);
    2018           0 :             break;
    2019             : 
    2020             :         case jbvBinary:
    2021             :         case jbvArray:
    2022             :         case jbvObject:
    2023           8 :             return jpbUnknown;  /* non-scalars are not comparable */
    2024             : 
    2025             :         default:
    2026           0 :             elog(ERROR, "invalid jsonb value type %d", jb1->type);
    2027             :     }
    2028             : 
    2029        1480 :     switch (op)
    2030             :     {
    2031             :         case jpiEqual:
    2032         884 :             res = (cmp == 0);
    2033         884 :             break;
    2034             :         case jpiNotEqual:
    2035           4 :             res = (cmp != 0);
    2036           4 :             break;
    2037             :         case jpiLess:
    2038         128 :             res = (cmp < 0);
    2039         128 :             break;
    2040             :         case jpiGreater:
    2041         376 :             res = (cmp > 0);
    2042         376 :             break;
    2043             :         case jpiLessOrEqual:
    2044           8 :             res = (cmp <= 0);
    2045           8 :             break;
    2046             :         case jpiGreaterOrEqual:
    2047          80 :             res = (cmp >= 0);
    2048          80 :             break;
    2049             :         default:
    2050           0 :             elog(ERROR, "unrecognized jsonpath operation: %d", op);
    2051             :             return jpbUnknown;
    2052             :     }
    2053             : 
    2054        1480 :     return res ? jpbTrue : jpbFalse;
    2055             : }
    2056             : 
    2057             : /* Compare two numerics */
    2058             : static int
    2059         776 : compareNumeric(Numeric a, Numeric b)
    2060             : {
    2061         776 :     return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
    2062             :                                              NumericGetDatum(a),
    2063             :                                              NumericGetDatum(b)));
    2064             : }
    2065             : 
    2066             : static JsonbValue *
    2067       69552 : copyJsonbValue(JsonbValue *src)
    2068             : {
    2069       69552 :     JsonbValue *dst = palloc(sizeof(*dst));
    2070             : 
    2071       69552 :     *dst = *src;
    2072             : 
    2073       69552 :     return dst;
    2074             : }
    2075             : 
    2076             : /*
    2077             :  * Execute array subscript expression and convert resulting numeric item to
    2078             :  * the integer type with truncation.
    2079             :  */
    2080             : static JsonPathExecResult
    2081         212 : getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
    2082             :               int32 *index)
    2083             : {
    2084             :     JsonbValue *jbv;
    2085         212 :     JsonValueList found = {0};
    2086         212 :     JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
    2087             :     Datum       numeric_index;
    2088         208 :     bool        have_error = false;
    2089             : 
    2090         208 :     if (jperIsError(res))
    2091           0 :         return res;
    2092             : 
    2093         408 :     if (JsonValueListLength(&found) != 1 ||
    2094         200 :         !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
    2095          16 :         RETURN_ERROR(ereport(ERROR,
    2096             :                              (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
    2097             :                               errmsg("jsonpath array subscript is not a single numeric value"))));
    2098             : 
    2099         192 :     numeric_index = DirectFunctionCall2(numeric_trunc,
    2100             :                                         NumericGetDatum(jbv->val.numeric),
    2101             :                                         Int32GetDatum(0));
    2102             : 
    2103         192 :     *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
    2104             :                                     &have_error);
    2105             : 
    2106         192 :     if (have_error)
    2107          16 :         RETURN_ERROR(ereport(ERROR,
    2108             :                              (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
    2109             :                               errmsg("jsonpath array subscript is out of integer range"))));
    2110             : 
    2111         176 :     return jperOk;
    2112             : }
    2113             : 
    2114             : /* Save base object and its id needed for the execution of .keyvalue(). */
    2115             : static JsonBaseObjectInfo
    2116      132296 : setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
    2117             : {
    2118      132296 :     JsonBaseObjectInfo baseObject = cxt->baseObject;
    2119             : 
    2120      132296 :     cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
    2121             :         (JsonbContainer *) jbv->val.binary.data;
    2122      132296 :     cxt->baseObject.id = id;
    2123             : 
    2124      132296 :     return baseObject;
    2125             : }
    2126             : 
    2127             : static void
    2128      147316 : JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
    2129             : {
    2130      147316 :     if (jvl->singleton)
    2131             :     {
    2132         564 :         jvl->list = list_make2(jvl->singleton, jbv);
    2133         564 :         jvl->singleton = NULL;
    2134             :     }
    2135      146752 :     else if (!jvl->list)
    2136      146252 :         jvl->singleton = jbv;
    2137             :     else
    2138         500 :         jvl->list = lappend(jvl->list, jbv);
    2139      147316 : }
    2140             : 
    2141             : static int
    2142       65892 : JsonValueListLength(const JsonValueList *jvl)
    2143             : {
    2144       65892 :     return jvl->singleton ? 1 : list_length(jvl->list);
    2145             : }
    2146             : 
    2147             : static bool
    2148          12 : JsonValueListIsEmpty(JsonValueList *jvl)
    2149             : {
    2150          12 :     return !jvl->singleton && list_length(jvl->list) <= 0;
    2151             : }
    2152             : 
    2153             : static JsonbValue *
    2154       65792 : JsonValueListHead(JsonValueList *jvl)
    2155             : {
    2156       65792 :     return jvl->singleton ? jvl->singleton : linitial(jvl->list);
    2157             : }
    2158             : 
    2159             : static List *
    2160         688 : JsonValueListGetList(JsonValueList *jvl)
    2161             : {
    2162         688 :     if (jvl->singleton)
    2163         368 :         return list_make1(jvl->singleton);
    2164             : 
    2165         320 :     return jvl->list;
    2166             : }
    2167             : 
    2168             : static void
    2169      109084 : JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
    2170             : {
    2171      109084 :     if (jvl->singleton)
    2172             :     {
    2173       57580 :         it->value = jvl->singleton;
    2174       57580 :         it->next = NULL;
    2175             :     }
    2176       51504 :     else if (list_head(jvl->list) != NULL)
    2177             :     {
    2178         412 :         it->value = (JsonbValue *) linitial(jvl->list);
    2179         412 :         it->next = lnext(list_head(jvl->list));
    2180             :     }
    2181             :     else
    2182             :     {
    2183       51092 :         it->value = NULL;
    2184       51092 :         it->next = NULL;
    2185             :     }
    2186      109084 : }
    2187             : 
    2188             : /*
    2189             :  * Get the next item from the sequence advancing iterator.
    2190             :  */
    2191             : static JsonbValue *
    2192      164184 : JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
    2193             : {
    2194      164184 :     JsonbValue *result = it->value;
    2195             : 
    2196      164184 :     if (it->next)
    2197             :     {
    2198         712 :         it->value = lfirst(it->next);
    2199         712 :         it->next = lnext(it->next);
    2200             :     }
    2201             :     else
    2202             :     {
    2203      163472 :         it->value = NULL;
    2204             :     }
    2205             : 
    2206      164184 :     return result;
    2207             : }
    2208             : 
    2209             : /*
    2210             :  * Initialize a binary JsonbValue with the given jsonb container.
    2211             :  */
    2212             : static JsonbValue *
    2213      123912 : JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
    2214             : {
    2215      123912 :     jbv->type = jbvBinary;
    2216      123912 :     jbv->val.binary.data = &jb->root;
    2217      123912 :     jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
    2218             : 
    2219      123912 :     return jbv;
    2220             : }
    2221             : 
    2222             : /*
    2223             :  * Returns jbv* type of of JsonbValue. Note, it never returns jbvBinary as is.
    2224             :  */
    2225             : static int
    2226      160164 : JsonbType(JsonbValue *jb)
    2227             : {
    2228      160164 :     int         type = jb->type;
    2229             : 
    2230      160164 :     if (jb->type == jbvBinary)
    2231             :     {
    2232      116592 :         JsonbContainer *jbc = (void *) jb->val.binary.data;
    2233             : 
    2234             :         /* Scalars should be always extracted during jsonpath execution. */
    2235             :         Assert(!JsonContainerIsScalar(jbc));
    2236             : 
    2237      116592 :         if (JsonContainerIsObject(jbc))
    2238      115652 :             type = jbvObject;
    2239         940 :         else if (JsonContainerIsArray(jbc))
    2240         940 :             type = jbvArray;
    2241             :         else
    2242           0 :             elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
    2243             :     }
    2244             : 
    2245      160164 :     return type;
    2246             : }
    2247             : 
    2248             : /* Get scalar of given type or NULL on type mismatch */
    2249             : static JsonbValue *
    2250        1160 : getScalar(JsonbValue *scalar, enum jbvType type)
    2251             : {
    2252             :     /* Scalars should be always extracted during jsonpath execution. */
    2253             :     Assert(scalar->type != jbvBinary ||
    2254             :            !JsonContainerIsScalar(scalar->val.binary.data));
    2255             : 
    2256        1160 :     return scalar->type == type ? scalar : NULL;
    2257             : }
    2258             : 
    2259             : /* Construct a JSON array from the item list */
    2260             : static JsonbValue *
    2261          20 : wrapItemsInArray(const JsonValueList *items)
    2262             : {
    2263          20 :     JsonbParseState *ps = NULL;
    2264             :     JsonValueListIterator it;
    2265             :     JsonbValue *jbv;
    2266             : 
    2267          20 :     pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
    2268             : 
    2269          20 :     JsonValueListInitIterator(items, &it);
    2270          60 :     while ((jbv = JsonValueListNext(items, &it)))
    2271          20 :         pushJsonbValue(&ps, WJB_ELEM, jbv);
    2272             : 
    2273          20 :     return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
    2274             : }

Generated by: LCOV version 1.13