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

Generated by: LCOV version 1.13