LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_exec.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 952 1025 92.9 %
Date: 2023-12-02 14:10:25 Functions: 60 64 93.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * jsonpath_exec.c
       4             :  *   Routines for SQL/JSON path execution.
       5             :  *
       6             :  * Jsonpath is executed in the global context stored in JsonPathExecContext,
       7             :  * which is passed to almost every function involved into execution.  Entry
       8             :  * point for jsonpath execution is executeJsonPath() function, which
       9             :  * initializes execution context including initial JsonPathItem and JsonbValue,
      10             :  * flags, stack for calculation of @ in filters.
      11             :  *
      12             :  * The result of jsonpath query execution is enum JsonPathExecResult and
      13             :  * if succeeded sequence of JsonbValue, written to JsonValueList *found, which
      14             :  * is passed through the jsonpath items.  When found == NULL, we're inside
      15             :  * exists-query and we're interested only in whether result is empty.  In this
      16             :  * case execution is stopped once first result item is found, and the only
      17             :  * execution result is JsonPathExecResult.  The values of JsonPathExecResult
      18             :  * are following:
      19             :  * - jperOk         -- result sequence is not empty
      20             :  * - jperNotFound   -- result sequence is empty
      21             :  * - jperError      -- error occurred during execution
      22             :  *
      23             :  * Jsonpath is executed recursively (see executeItem()) starting form the
      24             :  * first path item (which in turn might be, for instance, an arithmetic
      25             :  * expression evaluated separately).  On each step single JsonbValue obtained
      26             :  * from previous path item is processed.  The result of processing is a
      27             :  * sequence of JsonbValue (probably empty), which is passed to the next path
      28             :  * item one by one.  When there is no next path item, then JsonbValue is added
      29             :  * to the 'found' list.  When found == NULL, then execution functions just
      30             :  * return jperOk (see executeNextItem()).
      31             :  *
      32             :  * Many of jsonpath operations require automatic unwrapping of arrays in lax
      33             :  * mode.  So, if input value is array, then corresponding operation is
      34             :  * processed not on array itself, but on all of its members one by one.
      35             :  * executeItemOptUnwrapTarget() function have 'unwrap' argument, which indicates
      36             :  * whether unwrapping of array is needed.  When unwrap == true, each of array
      37             :  * members is passed to executeItemOptUnwrapTarget() again but with unwrap == false
      38             :  * in order to avoid subsequent array unwrapping.
      39             :  *
      40             :  * All boolean expressions (predicates) are evaluated by executeBoolItem()
      41             :  * function, which returns tri-state JsonPathBool.  When error is occurred
      42             :  * during predicate execution, it returns jpbUnknown.  According to standard
      43             :  * predicates can be only inside filters.  But we support their usage as
      44             :  * jsonpath expression.  This helps us to implement @@ operator.  In this case
      45             :  * resulting JsonPathBool is transformed into jsonb bool or null.
      46             :  *
      47             :  * Arithmetic and boolean expression are evaluated recursively from expression
      48             :  * tree top down to the leaves.  Therefore, for binary arithmetic expressions
      49             :  * we calculate operands first.  Then we check that results are numeric
      50             :  * singleton lists, calculate the result and pass it to the next path item.
      51             :  *
      52             :  * Copyright (c) 2019-2023, 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 "nodes/miscnodes.h"
      68             : #include "regex/regex.h"
      69             : #include "utils/builtins.h"
      70             : #include "utils/date.h"
      71             : #include "utils/datetime.h"
      72             : #include "utils/datum.h"
      73             : #include "utils/float.h"
      74             : #include "utils/formatting.h"
      75             : #include "utils/guc.h"
      76             : #include "utils/json.h"
      77             : #include "utils/jsonpath.h"
      78             : #include "utils/timestamp.h"
      79             : #include "utils/varlena.h"
      80             : 
      81             : /*
      82             :  * Represents "base object" and it's "id" for .keyvalue() evaluation.
      83             :  */
      84             : typedef struct JsonBaseObjectInfo
      85             : {
      86             :     JsonbContainer *jbc;
      87             :     int         id;
      88             : } JsonBaseObjectInfo;
      89             : 
      90             : /*
      91             :  * Context of jsonpath execution.
      92             :  */
      93             : typedef struct JsonPathExecContext
      94             : {
      95             :     Jsonb      *vars;           /* variables to substitute into jsonpath */
      96             :     JsonbValue *root;           /* for $ evaluation */
      97             :     JsonbValue *current;        /* for @ evaluation */
      98             :     JsonBaseObjectInfo baseObject;  /* "base object" for .keyvalue()
      99             :                                      * evaluation */
     100             :     int         lastGeneratedObjectId;  /* "id" counter for .keyvalue()
     101             :                                          * evaluation */
     102             :     int         innermostArraySize; /* for LAST array index evaluation */
     103             :     bool        laxMode;        /* true for "lax" mode, false for "strict"
     104             :                                  * mode */
     105             :     bool        ignoreStructuralErrors; /* with "true" structural errors such
     106             :                                          * as absence of required json item or
     107             :                                          * unexpected json item type are
     108             :                                          * ignored */
     109             :     bool        throwErrors;    /* with "false" all suppressible errors are
     110             :                                  * suppressed */
     111             :     bool        useTz;
     112             : } JsonPathExecContext;
     113             : 
     114             : /* Context for LIKE_REGEX execution. */
     115             : typedef struct JsonLikeRegexContext
     116             : {
     117             :     text       *regex;
     118             :     int         cflags;
     119             : } JsonLikeRegexContext;
     120             : 
     121             : /* Result of jsonpath predicate evaluation */
     122             : typedef enum JsonPathBool
     123             : {
     124             :     jpbFalse = 0,
     125             :     jpbTrue = 1,
     126             :     jpbUnknown = 2
     127             : } JsonPathBool;
     128             : 
     129             : /* Result of jsonpath expression evaluation */
     130             : typedef enum JsonPathExecResult
     131             : {
     132             :     jperOk = 0,
     133             :     jperNotFound = 1,
     134             :     jperError = 2
     135             : } JsonPathExecResult;
     136             : 
     137             : #define jperIsError(jper)           ((jper) == jperError)
     138             : 
     139             : /*
     140             :  * List of jsonb values with shortcut for single-value list.
     141             :  */
     142             : typedef struct JsonValueList
     143             : {
     144             :     JsonbValue *singleton;
     145             :     List       *list;
     146             : } JsonValueList;
     147             : 
     148             : typedef struct JsonValueListIterator
     149             : {
     150             :     JsonbValue *value;
     151             :     List       *list;
     152             :     ListCell   *next;
     153             : } JsonValueListIterator;
     154             : 
     155             : /* strict/lax flags is decomposed into four [un]wrap/error flags */
     156             : #define jspStrictAbsenseOfErrors(cxt)   (!(cxt)->laxMode)
     157             : #define jspAutoUnwrap(cxt)              ((cxt)->laxMode)
     158             : #define jspAutoWrap(cxt)                ((cxt)->laxMode)
     159             : #define jspIgnoreStructuralErrors(cxt)  ((cxt)->ignoreStructuralErrors)
     160             : #define jspThrowErrors(cxt)             ((cxt)->throwErrors)
     161             : 
     162             : /* Convenience macro: return or throw error depending on context */
     163             : #define RETURN_ERROR(throw_error) \
     164             : do { \
     165             :     if (jspThrowErrors(cxt)) \
     166             :         throw_error; \
     167             :     else \
     168             :         return jperError; \
     169             : } while (0)
     170             : 
     171             : typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
     172             :                                                    JsonbValue *larg,
     173             :                                                    JsonbValue *rarg,
     174             :                                                    void *param);
     175             : typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
     176             : 
     177             : static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
     178             :                                           Jsonb *json, bool throwErrors,
     179             :                                           JsonValueList *result, bool useTz);
     180             : static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
     181             :                                       JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
     182             : static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
     183             :                                                      JsonPathItem *jsp, JsonbValue *jb,
     184             :                                                      JsonValueList *found, bool unwrap);
     185             : static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
     186             :                                                        JsonPathItem *jsp, JsonbValue *jb,
     187             :                                                        JsonValueList *found, bool unwrapElements);
     188             : static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
     189             :                                           JsonPathItem *cur, JsonPathItem *next,
     190             :                                           JsonbValue *v, JsonValueList *found, bool copy);
     191             : static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
     192             :                                                      bool unwrap, JsonValueList *found);
     193             : static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp,
     194             :                                                             JsonbValue *jb, bool unwrap, JsonValueList *found);
     195             : static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
     196             :                                     JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
     197             : static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
     198             :                                           JsonPathItem *jsp, JsonbValue *jb);
     199             : static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
     200             :                                          JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
     201             :                                          uint32 level, uint32 first, uint32 last,
     202             :                                          bool ignoreStructuralErrors, bool unwrapNext);
     203             : static JsonPathBool executePredicate(JsonPathExecContext *cxt,
     204             :                                      JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
     205             :                                      JsonbValue *jb, bool unwrapRightArg,
     206             :                                      JsonPathPredicateCallback exec, void *param);
     207             : static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
     208             :                                                   JsonPathItem *jsp, JsonbValue *jb,
     209             :                                                   BinaryArithmFunc func, JsonValueList *found);
     210             : static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
     211             :                                                  JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
     212             :                                                  JsonValueList *found);
     213             : static JsonPathBool executeStartsWith(JsonPathItem *jsp,
     214             :                                       JsonbValue *whole, JsonbValue *initial, void *param);
     215             : static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
     216             :                                      JsonbValue *rarg, void *param);
     217             : static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
     218             :                                                    JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
     219             :                                                    JsonValueList *found);
     220             : static JsonPathExecResult executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
     221             :                                                 JsonbValue *jb, JsonValueList *found);
     222             : static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
     223             :                                                 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
     224             : static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
     225             :                                            JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
     226             : static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
     227             :                             JsonbValue *value);
     228             : static void getJsonPathVariable(JsonPathExecContext *cxt,
     229             :                                 JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
     230             : static int  JsonbArraySize(JsonbValue *jb);
     231             : static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
     232             :                                       JsonbValue *rv, void *p);
     233             : static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2,
     234             :                                  bool useTz);
     235             : static int  compareNumeric(Numeric a, Numeric b);
     236             : static JsonbValue *copyJsonbValue(JsonbValue *src);
     237             : static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
     238             :                                         JsonPathItem *jsp, JsonbValue *jb, int32 *index);
     239             : static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
     240             :                                         JsonbValue *jbv, int32 id);
     241             : static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
     242             : static int  JsonValueListLength(const JsonValueList *jvl);
     243             : static bool JsonValueListIsEmpty(JsonValueList *jvl);
     244             : static JsonbValue *JsonValueListHead(JsonValueList *jvl);
     245             : static List *JsonValueListGetList(JsonValueList *jvl);
     246             : static void JsonValueListInitIterator(const JsonValueList *jvl,
     247             :                                       JsonValueListIterator *it);
     248             : static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
     249             :                                      JsonValueListIterator *it);
     250             : static int  JsonbType(JsonbValue *jb);
     251             : static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
     252             : static int  JsonbType(JsonbValue *jb);
     253             : static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
     254             : static JsonbValue *wrapItemsInArray(const JsonValueList *items);
     255             : static int  compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
     256             :                             bool useTz, bool *cast_error);
     257             : 
     258             : /****************** User interface to JsonPath executor ********************/
     259             : 
     260             : /*
     261             :  * jsonb_path_exists
     262             :  *      Returns true if jsonpath returns at least one item for the specified
     263             :  *      jsonb value.  This function and jsonb_path_match() are used to
     264             :  *      implement @? and @@ operators, which in turn are intended to have an
     265             :  *      index support.  Thus, it's desirable to make it easier to achieve
     266             :  *      consistency between index scan results and sequential scan results.
     267             :  *      So, we throw as few errors as possible.  Regarding this function,
     268             :  *      such behavior also matches behavior of JSON_EXISTS() clause of
     269             :  *      SQL/JSON.  Regarding jsonb_path_match(), this function doesn't have
     270             :  *      an analogy in SQL/JSON, so we define its behavior on our own.
     271             :  */
     272             : static Datum
     273       86010 : jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
     274             : {
     275       86010 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     276       86010 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     277             :     JsonPathExecResult res;
     278       86010 :     Jsonb      *vars = NULL;
     279       86010 :     bool        silent = true;
     280             : 
     281       86010 :     if (PG_NARGS() == 4)
     282             :     {
     283          54 :         vars = PG_GETARG_JSONB_P(2);
     284          54 :         silent = PG_GETARG_BOOL(3);
     285             :     }
     286             : 
     287       86010 :     res = executeJsonPath(jp, vars, jb, !silent, NULL, tz);
     288             : 
     289       85998 :     PG_FREE_IF_COPY(jb, 0);
     290       85998 :     PG_FREE_IF_COPY(jp, 1);
     291             : 
     292       85998 :     if (jperIsError(res))
     293          54 :         PG_RETURN_NULL();
     294             : 
     295       85944 :     PG_RETURN_BOOL(res == jperOk);
     296             : }
     297             : 
     298             : Datum
     299          54 : jsonb_path_exists(PG_FUNCTION_ARGS)
     300             : {
     301          54 :     return jsonb_path_exists_internal(fcinfo, false);
     302             : }
     303             : 
     304             : Datum
     305           0 : jsonb_path_exists_tz(PG_FUNCTION_ARGS)
     306             : {
     307           0 :     return jsonb_path_exists_internal(fcinfo, true);
     308             : }
     309             : 
     310             : /*
     311             :  * jsonb_path_exists_opr
     312             :  *      Implementation of operator "jsonb @? jsonpath" (2-argument version of
     313             :  *      jsonb_path_exists()).
     314             :  */
     315             : Datum
     316       85956 : jsonb_path_exists_opr(PG_FUNCTION_ARGS)
     317             : {
     318             :     /* just call the other one -- it can handle both cases */
     319       85956 :     return jsonb_path_exists_internal(fcinfo, false);
     320             : }
     321             : 
     322             : /*
     323             :  * jsonb_path_match
     324             :  *      Returns jsonpath predicate result item for the specified jsonb value.
     325             :  *      See jsonb_path_exists() comment for details regarding error handling.
     326             :  */
     327             : static Datum
     328       97914 : jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
     329             : {
     330       97914 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     331       97914 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     332       97914 :     JsonValueList found = {0};
     333       97914 :     Jsonb      *vars = NULL;
     334       97914 :     bool        silent = true;
     335             : 
     336       97914 :     if (PG_NARGS() == 4)
     337             :     {
     338         126 :         vars = PG_GETARG_JSONB_P(2);
     339         126 :         silent = PG_GETARG_BOOL(3);
     340             :     }
     341             : 
     342       97914 :     (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
     343             : 
     344       97902 :     PG_FREE_IF_COPY(jb, 0);
     345       97902 :     PG_FREE_IF_COPY(jp, 1);
     346             : 
     347       97902 :     if (JsonValueListLength(&found) == 1)
     348             :     {
     349       97872 :         JsonbValue *jbv = JsonValueListHead(&found);
     350             : 
     351       97872 :         if (jbv->type == jbvBool)
     352       97800 :             PG_RETURN_BOOL(jbv->val.boolean);
     353             : 
     354          72 :         if (jbv->type == jbvNull)
     355          24 :             PG_RETURN_NULL();
     356             :     }
     357             : 
     358          78 :     if (!silent)
     359          36 :         ereport(ERROR,
     360             :                 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
     361             :                  errmsg("single boolean result is expected")));
     362             : 
     363          42 :     PG_RETURN_NULL();
     364             : }
     365             : 
     366             : Datum
     367         126 : jsonb_path_match(PG_FUNCTION_ARGS)
     368             : {
     369         126 :     return jsonb_path_match_internal(fcinfo, false);
     370             : }
     371             : 
     372             : Datum
     373           0 : jsonb_path_match_tz(PG_FUNCTION_ARGS)
     374             : {
     375           0 :     return jsonb_path_match_internal(fcinfo, true);
     376             : }
     377             : 
     378             : /*
     379             :  * jsonb_path_match_opr
     380             :  *      Implementation of operator "jsonb @@ jsonpath" (2-argument version of
     381             :  *      jsonb_path_match()).
     382             :  */
     383             : Datum
     384       97788 : jsonb_path_match_opr(PG_FUNCTION_ARGS)
     385             : {
     386             :     /* just call the other one -- it can handle both cases */
     387       97788 :     return jsonb_path_match_internal(fcinfo, false);
     388             : }
     389             : 
     390             : /*
     391             :  * jsonb_path_query
     392             :  *      Executes jsonpath for given jsonb document and returns result as
     393             :  *      rowset.
     394             :  */
     395             : static Datum
     396        3648 : jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
     397             : {
     398             :     FuncCallContext *funcctx;
     399             :     List       *found;
     400             :     JsonbValue *v;
     401             :     ListCell   *c;
     402             : 
     403        3648 :     if (SRF_IS_FIRSTCALL())
     404             :     {
     405             :         JsonPath   *jp;
     406             :         Jsonb      *jb;
     407             :         MemoryContext oldcontext;
     408             :         Jsonb      *vars;
     409             :         bool        silent;
     410        1890 :         JsonValueList found = {0};
     411             : 
     412        1890 :         funcctx = SRF_FIRSTCALL_INIT();
     413        1890 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
     414             : 
     415        1890 :         jb = PG_GETARG_JSONB_P_COPY(0);
     416        1890 :         jp = PG_GETARG_JSONPATH_P_COPY(1);
     417        1890 :         vars = PG_GETARG_JSONB_P_COPY(2);
     418        1890 :         silent = PG_GETARG_BOOL(3);
     419             : 
     420        1890 :         (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
     421             : 
     422        1428 :         funcctx->user_fctx = JsonValueListGetList(&found);
     423             : 
     424        1428 :         MemoryContextSwitchTo(oldcontext);
     425             :     }
     426             : 
     427        3186 :     funcctx = SRF_PERCALL_SETUP();
     428        3186 :     found = funcctx->user_fctx;
     429             : 
     430        3186 :     c = list_head(found);
     431             : 
     432        3186 :     if (c == NULL)
     433        1428 :         SRF_RETURN_DONE(funcctx);
     434             : 
     435        1758 :     v = lfirst(c);
     436        1758 :     funcctx->user_fctx = list_delete_first(found);
     437             : 
     438        1758 :     SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
     439             : }
     440             : 
     441             : Datum
     442        3264 : jsonb_path_query(PG_FUNCTION_ARGS)
     443             : {
     444        3264 :     return jsonb_path_query_internal(fcinfo, false);
     445             : }
     446             : 
     447             : Datum
     448         384 : jsonb_path_query_tz(PG_FUNCTION_ARGS)
     449             : {
     450         384 :     return jsonb_path_query_internal(fcinfo, true);
     451             : }
     452             : 
     453             : /*
     454             :  * jsonb_path_query_array
     455             :  *      Executes jsonpath for given jsonb document and returns result as
     456             :  *      jsonb array.
     457             :  */
     458             : static Datum
     459          48 : jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
     460             : {
     461          48 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     462          48 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     463          48 :     JsonValueList found = {0};
     464          48 :     Jsonb      *vars = PG_GETARG_JSONB_P(2);
     465          48 :     bool        silent = PG_GETARG_BOOL(3);
     466             : 
     467          48 :     (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
     468             : 
     469          42 :     PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
     470             : }
     471             : 
     472             : Datum
     473          48 : jsonb_path_query_array(PG_FUNCTION_ARGS)
     474             : {
     475          48 :     return jsonb_path_query_array_internal(fcinfo, false);
     476             : }
     477             : 
     478             : Datum
     479           0 : jsonb_path_query_array_tz(PG_FUNCTION_ARGS)
     480             : {
     481           0 :     return jsonb_path_query_array_internal(fcinfo, true);
     482             : }
     483             : 
     484             : /*
     485             :  * jsonb_path_query_first
     486             :  *      Executes jsonpath for given jsonb document and returns first result
     487             :  *      item.  If there are no items, NULL returned.
     488             :  */
     489             : static Datum
     490        4374 : jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
     491             : {
     492        4374 :     Jsonb      *jb = PG_GETARG_JSONB_P(0);
     493        4374 :     JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
     494        4374 :     JsonValueList found = {0};
     495        4374 :     Jsonb      *vars = PG_GETARG_JSONB_P(2);
     496        4374 :     bool        silent = PG_GETARG_BOOL(3);
     497             : 
     498        4374 :     (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
     499             : 
     500        4362 :     if (JsonValueListLength(&found) >= 1)
     501        4350 :         PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
     502             :     else
     503          12 :         PG_RETURN_NULL();
     504             : }
     505             : 
     506             : Datum
     507        4374 : jsonb_path_query_first(PG_FUNCTION_ARGS)
     508             : {
     509        4374 :     return jsonb_path_query_first_internal(fcinfo, false);
     510             : }
     511             : 
     512             : Datum
     513           0 : jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
     514             : {
     515           0 :     return jsonb_path_query_first_internal(fcinfo, true);
     516             : }
     517             : 
     518             : /********************Execute functions for JsonPath**************************/
     519             : 
     520             : /*
     521             :  * Interface to jsonpath executor
     522             :  *
     523             :  * 'path' - jsonpath to be executed
     524             :  * 'vars' - variables to be substituted to jsonpath
     525             :  * 'json' - target document for jsonpath evaluation
     526             :  * 'throwErrors' - whether we should throw suppressible errors
     527             :  * 'result' - list to store result items into
     528             :  *
     529             :  * Returns an error if a recoverable error happens during processing, or NULL
     530             :  * on no error.
     531             :  *
     532             :  * Note, jsonb and jsonpath values should be available and untoasted during
     533             :  * work because JsonPathItem, JsonbValue and result item could have pointers
     534             :  * into input values.  If caller needs to just check if document matches
     535             :  * jsonpath, then it doesn't provide a result arg.  In this case executor
     536             :  * works till first positive result and does not check the rest if possible.
     537             :  * In other case it tries to find all the satisfied result items.
     538             :  */
     539             : static JsonPathExecResult
     540      190236 : executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
     541             :                 JsonValueList *result, bool useTz)
     542             : {
     543             :     JsonPathExecContext cxt;
     544             :     JsonPathExecResult res;
     545             :     JsonPathItem jsp;
     546             :     JsonbValue  jbv;
     547             : 
     548      190236 :     jspInit(&jsp, path);
     549             : 
     550      190236 :     if (!JsonbExtractScalar(&json->root, &jbv))
     551      189522 :         JsonbInitBinary(&jbv, json);
     552             : 
     553      190236 :     if (vars && !JsonContainerIsObject(&vars->root))
     554             :     {
     555          12 :         ereport(ERROR,
     556             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     557             :                  errmsg("\"vars\" argument is not an object"),
     558             :                  errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
     559             :     }
     560             : 
     561      190224 :     cxt.vars = vars;
     562      190224 :     cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
     563      190224 :     cxt.ignoreStructuralErrors = cxt.laxMode;
     564      190224 :     cxt.root = &jbv;
     565      190224 :     cxt.current = &jbv;
     566      190224 :     cxt.baseObject.jbc = NULL;
     567      190224 :     cxt.baseObject.id = 0;
     568      190224 :     cxt.lastGeneratedObjectId = vars ? 2 : 1;
     569      190224 :     cxt.innermostArraySize = -1;
     570      190224 :     cxt.throwErrors = throwErrors;
     571      190224 :     cxt.useTz = useTz;
     572             : 
     573      190224 :     if (jspStrictAbsenseOfErrors(&cxt) && !result)
     574             :     {
     575             :         /*
     576             :          * In strict mode we must get a complete list of values to check that
     577             :          * there are no errors at all.
     578             :          */
     579          54 :         JsonValueList vals = {0};
     580             : 
     581          54 :         res = executeItem(&cxt, &jsp, &jbv, &vals);
     582             : 
     583          48 :         if (jperIsError(res))
     584          42 :             return res;
     585             : 
     586           6 :         return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
     587             :     }
     588             : 
     589      190170 :     res = executeItem(&cxt, &jsp, &jbv, result);
     590             : 
     591             :     Assert(!throwErrors || !jperIsError(res));
     592             : 
     593      189684 :     return res;
     594             : }
     595             : 
     596             : /*
     597             :  * Execute jsonpath with automatic unwrapping of current item in lax mode.
     598             :  */
     599             : static JsonPathExecResult
     600      565902 : executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
     601             :             JsonbValue *jb, JsonValueList *found)
     602             : {
     603      565902 :     return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
     604             : }
     605             : 
     606             : /*
     607             :  * Main jsonpath executor function: walks on jsonpath structure, finds
     608             :  * relevant parts of jsonb and evaluates expressions over them.
     609             :  * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
     610             :  */
     611             : static JsonPathExecResult
     612      569886 : executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
     613             :                            JsonbValue *jb, JsonValueList *found, bool unwrap)
     614             : {
     615             :     JsonPathItem elem;
     616      569886 :     JsonPathExecResult res = jperNotFound;
     617             :     JsonBaseObjectInfo baseObject;
     618             : 
     619      569886 :     check_stack_depth();
     620      569886 :     CHECK_FOR_INTERRUPTS();
     621             : 
     622      569886 :     switch (jsp->type)
     623             :     {
     624             :             /* all boolean item types: */
     625      102162 :         case jpiAnd:
     626             :         case jpiOr:
     627             :         case jpiNot:
     628             :         case jpiIsUnknown:
     629             :         case jpiEqual:
     630             :         case jpiNotEqual:
     631             :         case jpiLess:
     632             :         case jpiGreater:
     633             :         case jpiLessOrEqual:
     634             :         case jpiGreaterOrEqual:
     635             :         case jpiExists:
     636             :         case jpiStartsWith:
     637             :         case jpiLikeRegex:
     638             :             {
     639      102162 :                 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
     640             : 
     641      102162 :                 res = appendBoolResult(cxt, jsp, found, st);
     642      102162 :                 break;
     643             :             }
     644             : 
     645      165228 :         case jpiKey:
     646      165228 :             if (JsonbType(jb) == jbvObject)
     647             :             {
     648             :                 JsonbValue *v;
     649             :                 JsonbValue  key;
     650             : 
     651      164976 :                 key.type = jbvString;
     652      164976 :                 key.val.string.val = jspGetString(jsp, &key.val.string.len);
     653             : 
     654      164976 :                 v = findJsonbValueFromContainer(jb->val.binary.data,
     655             :                                                 JB_FOBJECT, &key);
     656             : 
     657      164976 :                 if (v != NULL)
     658             :                 {
     659       26148 :                     res = executeNextItem(cxt, jsp, NULL,
     660             :                                           v, found, false);
     661             : 
     662             :                     /* free value if it was not added to found list */
     663       26148 :                     if (jspHasNext(jsp) || !found)
     664       16104 :                         pfree(v);
     665             :                 }
     666      138828 :                 else if (!jspIgnoreStructuralErrors(cxt))
     667             :                 {
     668             :                     Assert(found);
     669             : 
     670          72 :                     if (!jspThrowErrors(cxt))
     671          48 :                         return jperError;
     672             : 
     673          24 :                     ereport(ERROR,
     674             :                             (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
     675             :                              errmsg("JSON object does not contain key \"%s\"",
     676             :                                     pnstrdup(key.val.string.val,
     677             :                                              key.val.string.len))));
     678             :                 }
     679             :             }
     680         252 :             else if (unwrap && JsonbType(jb) == jbvArray)
     681           6 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
     682         246 :             else if (!jspIgnoreStructuralErrors(cxt))
     683             :             {
     684             :                 Assert(found);
     685          66 :                 RETURN_ERROR(ereport(ERROR,
     686             :                                      (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
     687             :                                       errmsg("jsonpath member accessor can only be applied to an object"))));
     688             :             }
     689      165084 :             break;
     690             : 
     691      202428 :         case jpiRoot:
     692      202428 :             jb = cxt->root;
     693      202428 :             baseObject = setBaseObject(cxt, jb, 0);
     694      202428 :             res = executeNextItem(cxt, jsp, NULL, jb, found, true);
     695      202008 :             cxt->baseObject = baseObject;
     696      202008 :             break;
     697             : 
     698       19356 :         case jpiCurrent:
     699       19356 :             res = executeNextItem(cxt, jsp, NULL, cxt->current,
     700             :                                   found, true);
     701       19356 :             break;
     702             : 
     703        1326 :         case jpiAnyArray:
     704        1326 :             if (JsonbType(jb) == jbvArray)
     705             :             {
     706        1140 :                 bool        hasNext = jspGetNext(jsp, &elem);
     707             : 
     708        1140 :                 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
     709        1140 :                                                    jb, found, jspAutoUnwrap(cxt));
     710             :             }
     711         186 :             else if (jspAutoWrap(cxt))
     712         174 :                 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
     713          12 :             else if (!jspIgnoreStructuralErrors(cxt))
     714          12 :                 RETURN_ERROR(ereport(ERROR,
     715             :                                      (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
     716             :                                       errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
     717        1188 :             break;
     718             : 
     719         288 :         case jpiIndexArray:
     720         288 :             if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
     721         174 :             {
     722         276 :                 int         innermostArraySize = cxt->innermostArraySize;
     723             :                 int         i;
     724         276 :                 int         size = JsonbArraySize(jb);
     725         276 :                 bool        singleton = size < 0;
     726         276 :                 bool        hasNext = jspGetNext(jsp, &elem);
     727             : 
     728         276 :                 if (singleton)
     729           6 :                     size = 1;
     730             : 
     731         276 :                 cxt->innermostArraySize = size; /* for LAST evaluation */
     732             : 
     733         432 :                 for (i = 0; i < jsp->content.array.nelems; i++)
     734             :                 {
     735             :                     JsonPathItem from;
     736             :                     JsonPathItem to;
     737             :                     int32       index;
     738             :                     int32       index_from;
     739             :                     int32       index_to;
     740         288 :                     bool        range = jspGetArraySubscript(jsp, &from,
     741             :                                                              &to, i);
     742             : 
     743         288 :                     res = getArrayIndex(cxt, &from, jb, &index_from);
     744             : 
     745         264 :                     if (jperIsError(res))
     746          30 :                         break;
     747             : 
     748         240 :                     if (range)
     749             :                     {
     750          30 :                         res = getArrayIndex(cxt, &to, jb, &index_to);
     751             : 
     752          24 :                         if (jperIsError(res))
     753           0 :                             break;
     754             :                     }
     755             :                     else
     756         210 :                         index_to = index_from;
     757             : 
     758         234 :                     if (!jspIgnoreStructuralErrors(cxt) &&
     759          84 :                         (index_from < 0 ||
     760          72 :                          index_from > index_to ||
     761          72 :                          index_to >= size))
     762          72 :                         RETURN_ERROR(ereport(ERROR,
     763             :                                              (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
     764             :                                               errmsg("jsonpath array subscript is out of bounds"))));
     765             : 
     766         186 :                     if (index_from < 0)
     767          12 :                         index_from = 0;
     768             : 
     769         186 :                     if (index_to >= size)
     770          18 :                         index_to = size - 1;
     771             : 
     772         186 :                     res = jperNotFound;
     773             : 
     774         360 :                     for (index = index_from; index <= index_to; index++)
     775             :                     {
     776             :                         JsonbValue *v;
     777             :                         bool        copy;
     778             : 
     779         204 :                         if (singleton)
     780             :                         {
     781           6 :                             v = jb;
     782           6 :                             copy = true;
     783             :                         }
     784             :                         else
     785             :                         {
     786         198 :                             v = getIthJsonbValueFromContainer(jb->val.binary.data,
     787             :                                                               (uint32) index);
     788             : 
     789         198 :                             if (v == NULL)
     790           0 :                                 continue;
     791             : 
     792         198 :                             copy = false;
     793             :                         }
     794             : 
     795         204 :                         if (!hasNext && !found)
     796          24 :                             return jperOk;
     797             : 
     798         180 :                         res = executeNextItem(cxt, jsp, &elem, v, found,
     799             :                                               copy);
     800             : 
     801         180 :                         if (jperIsError(res))
     802           0 :                             break;
     803             : 
     804         180 :                         if (res == jperOk && !found)
     805           6 :                             break;
     806             :                     }
     807             : 
     808         162 :                     if (jperIsError(res))
     809           0 :                         break;
     810             : 
     811         162 :                     if (res == jperOk && !found)
     812           6 :                         break;
     813             :                 }
     814             : 
     815         174 :                 cxt->innermostArraySize = innermostArraySize;
     816             :             }
     817          12 :             else if (!jspIgnoreStructuralErrors(cxt))
     818             :             {
     819          12 :                 RETURN_ERROR(ereport(ERROR,
     820             :                                      (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
     821             :                                       errmsg("jsonpath array accessor can only be applied to an array"))));
     822             :             }
     823         174 :             break;
     824             : 
     825          66 :         case jpiLast:
     826             :             {
     827             :                 JsonbValue  tmpjbv;
     828             :                 JsonbValue *lastjbv;
     829             :                 int         last;
     830          66 :                 bool        hasNext = jspGetNext(jsp, &elem);
     831             : 
     832          66 :                 if (cxt->innermostArraySize < 0)
     833           0 :                     elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
     834             : 
     835          66 :                 if (!hasNext && !found)
     836             :                 {
     837           6 :                     res = jperOk;
     838           6 :                     break;
     839             :                 }
     840             : 
     841          60 :                 last = cxt->innermostArraySize - 1;
     842             : 
     843          60 :                 lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
     844             : 
     845          60 :                 lastjbv->type = jbvNumeric;
     846          60 :                 lastjbv->val.numeric = int64_to_numeric(last);
     847             : 
     848          60 :                 res = executeNextItem(cxt, jsp, &elem,
     849             :                                       lastjbv, found, hasNext);
     850             :             }
     851          60 :             break;
     852             : 
     853          84 :         case jpiAnyKey:
     854          84 :             if (JsonbType(jb) == jbvObject)
     855             :             {
     856          66 :                 bool        hasNext = jspGetNext(jsp, &elem);
     857             : 
     858          66 :                 if (jb->type != jbvBinary)
     859           0 :                     elog(ERROR, "invalid jsonb object type: %d", jb->type);
     860             : 
     861          66 :                 return executeAnyItem
     862             :                     (cxt, hasNext ? &elem : NULL,
     863             :                      jb->val.binary.data, found, 1, 1, 1,
     864          66 :                      false, jspAutoUnwrap(cxt));
     865             :             }
     866          18 :             else if (unwrap && JsonbType(jb) == jbvArray)
     867           0 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
     868          18 :             else if (!jspIgnoreStructuralErrors(cxt))
     869             :             {
     870             :                 Assert(found);
     871          12 :                 RETURN_ERROR(ereport(ERROR,
     872             :                                      (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
     873             :                                       errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
     874             :             }
     875           6 :             break;
     876             : 
     877         168 :         case jpiAdd:
     878         168 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     879             :                                            numeric_add_opt_error, found);
     880             : 
     881          60 :         case jpiSub:
     882          60 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     883             :                                            numeric_sub_opt_error, found);
     884             : 
     885          36 :         case jpiMul:
     886          36 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     887             :                                            numeric_mul_opt_error, found);
     888             : 
     889          66 :         case jpiDiv:
     890          66 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     891             :                                            numeric_div_opt_error, found);
     892             : 
     893          12 :         case jpiMod:
     894          12 :             return executeBinaryArithmExpr(cxt, jsp, jb,
     895             :                                            numeric_mod_opt_error, found);
     896             : 
     897          60 :         case jpiPlus:
     898          60 :             return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
     899             : 
     900         126 :         case jpiMinus:
     901         126 :             return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
     902             :                                           found);
     903             : 
     904       18582 :         case jpiFilter:
     905             :             {
     906             :                 JsonPathBool st;
     907             : 
     908       18582 :                 if (unwrap && JsonbType(jb) == jbvArray)
     909         126 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
     910             :                                                         false);
     911             : 
     912       18456 :                 jspGetArg(jsp, &elem);
     913       18456 :                 st = executeNestedBoolItem(cxt, &elem, jb);
     914       18360 :                 if (st != jpbTrue)
     915       16482 :                     res = jperNotFound;
     916             :                 else
     917        1878 :                     res = executeNextItem(cxt, jsp, NULL,
     918             :                                           jb, found, true);
     919       18360 :                 break;
     920             :             }
     921             : 
     922         306 :         case jpiAny:
     923             :             {
     924         306 :                 bool        hasNext = jspGetNext(jsp, &elem);
     925             : 
     926             :                 /* first try without any intermediate steps */
     927         306 :                 if (jsp->content.anybounds.first == 0)
     928             :                 {
     929             :                     bool        savedIgnoreStructuralErrors;
     930             : 
     931         168 :                     savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
     932         168 :                     cxt->ignoreStructuralErrors = true;
     933         168 :                     res = executeNextItem(cxt, jsp, &elem,
     934             :                                           jb, found, true);
     935         168 :                     cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
     936             : 
     937         168 :                     if (res == jperOk && !found)
     938           6 :                         break;
     939             :                 }
     940             : 
     941         300 :                 if (jb->type == jbvBinary)
     942         300 :                     res = executeAnyItem
     943             :                         (cxt, hasNext ? &elem : NULL,
     944             :                          jb->val.binary.data, found,
     945             :                          1,
     946             :                          jsp->content.anybounds.first,
     947             :                          jsp->content.anybounds.last,
     948         300 :                          true, jspAutoUnwrap(cxt));
     949         300 :                 break;
     950             :             }
     951             : 
     952       55548 :         case jpiNull:
     953             :         case jpiBool:
     954             :         case jpiNumeric:
     955             :         case jpiString:
     956             :         case jpiVariable:
     957             :             {
     958             :                 JsonbValue  vbuf;
     959             :                 JsonbValue *v;
     960       55548 :                 bool        hasNext = jspGetNext(jsp, &elem);
     961             : 
     962       55548 :                 if (!hasNext && !found && jsp->type != jpiVariable)
     963             :                 {
     964             :                     /*
     965             :                      * Skip evaluation, but not for variables.  We must
     966             :                      * trigger an error for the missing variable.
     967             :                      */
     968          12 :                     res = jperOk;
     969          12 :                     break;
     970             :                 }
     971             : 
     972       55536 :                 v = hasNext ? &vbuf : palloc(sizeof(*v));
     973             : 
     974       55536 :                 baseObject = cxt->baseObject;
     975       55536 :                 getJsonPathItem(cxt, jsp, v);
     976             : 
     977       55506 :                 res = executeNextItem(cxt, jsp, &elem,
     978             :                                       v, found, hasNext);
     979       55506 :                 cxt->baseObject = baseObject;
     980             :             }
     981       55506 :             break;
     982             : 
     983         210 :         case jpiType:
     984             :             {
     985         210 :                 JsonbValue *jbv = palloc(sizeof(*jbv));
     986             : 
     987         210 :                 jbv->type = jbvString;
     988         210 :                 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
     989         210 :                 jbv->val.string.len = strlen(jbv->val.string.val);
     990             : 
     991         210 :                 res = executeNextItem(cxt, jsp, NULL, jbv,
     992             :                                       found, false);
     993             :             }
     994         210 :             break;
     995             : 
     996          72 :         case jpiSize:
     997             :             {
     998          72 :                 int         size = JsonbArraySize(jb);
     999             : 
    1000          72 :                 if (size < 0)
    1001             :                 {
    1002          48 :                     if (!jspAutoWrap(cxt))
    1003             :                     {
    1004          12 :                         if (!jspIgnoreStructuralErrors(cxt))
    1005          12 :                             RETURN_ERROR(ereport(ERROR,
    1006             :                                                  (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
    1007             :                                                   errmsg("jsonpath item method .%s() can only be applied to an array",
    1008             :                                                          jspOperationName(jsp->type)))));
    1009           0 :                         break;
    1010             :                     }
    1011             : 
    1012          36 :                     size = 1;
    1013             :                 }
    1014             : 
    1015          60 :                 jb = palloc(sizeof(*jb));
    1016             : 
    1017          60 :                 jb->type = jbvNumeric;
    1018          60 :                 jb->val.numeric = int64_to_numeric(size);
    1019             : 
    1020          60 :                 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
    1021             :             }
    1022          60 :             break;
    1023             : 
    1024         108 :         case jpiAbs:
    1025         108 :             return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
    1026             :                                             found);
    1027             : 
    1028          48 :         case jpiFloor:
    1029          48 :             return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
    1030             :                                             found);
    1031             : 
    1032         102 :         case jpiCeiling:
    1033         102 :             return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
    1034             :                                             found);
    1035             : 
    1036         114 :         case jpiDouble:
    1037             :             {
    1038             :                 JsonbValue  jbv;
    1039             : 
    1040         114 :                 if (unwrap && JsonbType(jb) == jbvArray)
    1041          42 :                     return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
    1042             :                                                         false);
    1043             : 
    1044         108 :                 if (jb->type == jbvNumeric)
    1045             :                 {
    1046          12 :                     char       *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
    1047             :                                                                           NumericGetDatum(jb->val.numeric)));
    1048             :                     double      val;
    1049          12 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
    1050             : 
    1051          12 :                     val = float8in_internal(tmp,
    1052             :                                             NULL,
    1053             :                                             "double precision",
    1054             :                                             tmp,
    1055             :                                             (Node *) &escontext);
    1056             : 
    1057          12 :                     if (escontext.error_occurred || isinf(val) || isnan(val))
    1058           6 :                         RETURN_ERROR(ereport(ERROR,
    1059             :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1060             :                                               errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
    1061             :                                                      jspOperationName(jsp->type)))));
    1062           6 :                     res = jperOk;
    1063             :                 }
    1064          96 :                 else if (jb->type == jbvString)
    1065             :                 {
    1066             :                     /* cast string as double */
    1067             :                     double      val;
    1068          48 :                     char       *tmp = pnstrdup(jb->val.string.val,
    1069          48 :                                                jb->val.string.len);
    1070          48 :                     ErrorSaveContext escontext = {T_ErrorSaveContext};
    1071             : 
    1072          48 :                     val = float8in_internal(tmp,
    1073             :                                             NULL,
    1074             :                                             "double precision",
    1075             :                                             tmp,
    1076             :                                             (Node *) &escontext);
    1077             : 
    1078          48 :                     if (escontext.error_occurred || isinf(val) || isnan(val))
    1079          42 :                         RETURN_ERROR(ereport(ERROR,
    1080             :                                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1081             :                                               errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
    1082             :                                                      jspOperationName(jsp->type)))));
    1083             : 
    1084           6 :                     jb = &jbv;
    1085           6 :                     jb->type = jbvNumeric;
    1086           6 :                     jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
    1087             :                                                                           Float8GetDatum(val)));
    1088           6 :                     res = jperOk;
    1089             :                 }
    1090             : 
    1091          60 :                 if (res == jperNotFound)
    1092          48 :                     RETURN_ERROR(ereport(ERROR,
    1093             :                                          (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1094             :                                           errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
    1095             :                                                  jspOperationName(jsp->type)))));
    1096             : 
    1097          12 :                 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
    1098             :             }
    1099          12 :             break;
    1100             : 
    1101        3240 :         case jpiDatetime:
    1102        3240 :             if (unwrap && JsonbType(jb) == jbvArray)
    1103           6 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1104             : 
    1105        3234 :             return executeDateTimeMethod(cxt, jsp, jb, found);
    1106             : 
    1107          90 :         case jpiKeyValue:
    1108          90 :             if (unwrap && JsonbType(jb) == jbvArray)
    1109           6 :                 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1110             : 
    1111          84 :             return executeKeyValueMethod(cxt, jsp, jb, found);
    1112             : 
    1113           0 :         default:
    1114           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
    1115             :     }
    1116             : 
    1117      564510 :     return res;
    1118             : }
    1119             : 
    1120             : /*
    1121             :  * Unwrap current array item and execute jsonpath for each of its elements.
    1122             :  */
    1123             : static JsonPathExecResult
    1124        1344 : executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1125             :                              JsonbValue *jb, JsonValueList *found,
    1126             :                              bool unwrapElements)
    1127             : {
    1128        1344 :     if (jb->type != jbvBinary)
    1129             :     {
    1130             :         Assert(jb->type != jbvArray);
    1131           0 :         elog(ERROR, "invalid jsonb array value type: %d", jb->type);
    1132             :     }
    1133             : 
    1134        1344 :     return executeAnyItem
    1135             :         (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
    1136             :          false, unwrapElements);
    1137             : }
    1138             : 
    1139             : /*
    1140             :  * Execute next jsonpath item if exists.  Otherwise put "v" to the "found"
    1141             :  * list if provided.
    1142             :  */
    1143             : static JsonPathExecResult
    1144      412044 : executeNextItem(JsonPathExecContext *cxt,
    1145             :                 JsonPathItem *cur, JsonPathItem *next,
    1146             :                 JsonbValue *v, JsonValueList *found, bool copy)
    1147             : {
    1148             :     JsonPathItem elem;
    1149             :     bool        hasNext;
    1150             : 
    1151      412044 :     if (!cur)
    1152           0 :         hasNext = next != NULL;
    1153      412044 :     else if (next)
    1154      161778 :         hasNext = jspHasNext(cur);
    1155             :     else
    1156             :     {
    1157      250266 :         next = &elem;
    1158      250266 :         hasNext = jspGetNext(cur, next);
    1159             :     }
    1160             : 
    1161      412044 :     if (hasNext)
    1162      185814 :         return executeItem(cxt, next, v, found);
    1163             : 
    1164      226230 :     if (found)
    1165      176190 :         JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
    1166             : 
    1167      226230 :     return jperOk;
    1168             : }
    1169             : 
    1170             : /*
    1171             :  * Same as executeItem(), but when "unwrap == true" automatically unwraps
    1172             :  * each array item from the resulting sequence in lax mode.
    1173             :  */
    1174             : static JsonPathExecResult
    1175      189546 : executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1176             :                            JsonbValue *jb, bool unwrap,
    1177             :                            JsonValueList *found)
    1178             : {
    1179      189546 :     if (unwrap && jspAutoUnwrap(cxt))
    1180             :     {
    1181      110160 :         JsonValueList seq = {0};
    1182             :         JsonValueListIterator it;
    1183      110160 :         JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
    1184             :         JsonbValue *item;
    1185             : 
    1186      110136 :         if (jperIsError(res))
    1187          66 :             return res;
    1188             : 
    1189      110070 :         JsonValueListInitIterator(&seq, &it);
    1190      182298 :         while ((item = JsonValueListNext(&seq, &it)))
    1191             :         {
    1192             :             Assert(item->type != jbvArray);
    1193             : 
    1194       72228 :             if (JsonbType(item) == jbvArray)
    1195          54 :                 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
    1196             :             else
    1197       72174 :                 JsonValueListAppend(found, item);
    1198             :         }
    1199             : 
    1200      110070 :         return jperOk;
    1201             :     }
    1202             : 
    1203       79386 :     return executeItem(cxt, jsp, jb, found);
    1204             : }
    1205             : 
    1206             : /*
    1207             :  * Same as executeItemOptUnwrapResult(), but with error suppression.
    1208             :  */
    1209             : static JsonPathExecResult
    1210      188682 : executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
    1211             :                                   JsonPathItem *jsp,
    1212             :                                   JsonbValue *jb, bool unwrap,
    1213             :                                   JsonValueList *found)
    1214             : {
    1215             :     JsonPathExecResult res;
    1216      188682 :     bool        throwErrors = cxt->throwErrors;
    1217             : 
    1218      188682 :     cxt->throwErrors = false;
    1219      188682 :     res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
    1220      188676 :     cxt->throwErrors = throwErrors;
    1221             : 
    1222      188676 :     return res;
    1223             : }
    1224             : 
    1225             : /* Execute boolean-valued jsonpath expression. */
    1226             : static JsonPathBool
    1227      172944 : executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1228             :                 JsonbValue *jb, bool canHaveNext)
    1229             : {
    1230             :     JsonPathItem larg;
    1231             :     JsonPathItem rarg;
    1232             :     JsonPathBool res;
    1233             :     JsonPathBool res2;
    1234             : 
    1235      172944 :     if (!canHaveNext && jspHasNext(jsp))
    1236           0 :         elog(ERROR, "boolean jsonpath item cannot have next item");
    1237             : 
    1238      172944 :     switch (jsp->type)
    1239             :     {
    1240       25530 :         case jpiAnd:
    1241       25530 :             jspGetLeftArg(jsp, &larg);
    1242       25530 :             res = executeBoolItem(cxt, &larg, jb, false);
    1243             : 
    1244       25530 :             if (res == jpbFalse)
    1245       22470 :                 return jpbFalse;
    1246             : 
    1247             :             /*
    1248             :              * SQL/JSON says that we should check second arg in case of
    1249             :              * jperError
    1250             :              */
    1251             : 
    1252        3060 :             jspGetRightArg(jsp, &rarg);
    1253        3060 :             res2 = executeBoolItem(cxt, &rarg, jb, false);
    1254             : 
    1255        3060 :             return res2 == jpbTrue ? res : res2;
    1256             : 
    1257       12954 :         case jpiOr:
    1258       12954 :             jspGetLeftArg(jsp, &larg);
    1259       12954 :             res = executeBoolItem(cxt, &larg, jb, false);
    1260             : 
    1261       12954 :             if (res == jpbTrue)
    1262        2490 :                 return jpbTrue;
    1263             : 
    1264       10464 :             jspGetRightArg(jsp, &rarg);
    1265       10464 :             res2 = executeBoolItem(cxt, &rarg, jb, false);
    1266             : 
    1267       10464 :             return res2 == jpbFalse ? res : res2;
    1268             : 
    1269         108 :         case jpiNot:
    1270         108 :             jspGetArg(jsp, &larg);
    1271             : 
    1272         108 :             res = executeBoolItem(cxt, &larg, jb, false);
    1273             : 
    1274         108 :             if (res == jpbUnknown)
    1275          36 :                 return jpbUnknown;
    1276             : 
    1277          72 :             return res == jpbTrue ? jpbFalse : jpbTrue;
    1278             : 
    1279         210 :         case jpiIsUnknown:
    1280         210 :             jspGetArg(jsp, &larg);
    1281         210 :             res = executeBoolItem(cxt, &larg, jb, false);
    1282         210 :             return res == jpbUnknown ? jpbTrue : jpbFalse;
    1283             : 
    1284       54474 :         case jpiEqual:
    1285             :         case jpiNotEqual:
    1286             :         case jpiLess:
    1287             :         case jpiGreater:
    1288             :         case jpiLessOrEqual:
    1289             :         case jpiGreaterOrEqual:
    1290       54474 :             jspGetLeftArg(jsp, &larg);
    1291       54474 :             jspGetRightArg(jsp, &rarg);
    1292       54474 :             return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
    1293             :                                     executeComparison, cxt);
    1294             : 
    1295          84 :         case jpiStartsWith:     /* 'whole STARTS WITH initial' */
    1296          84 :             jspGetLeftArg(jsp, &larg);  /* 'whole' */
    1297          84 :             jspGetRightArg(jsp, &rarg); /* 'initial' */
    1298          84 :             return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
    1299             :                                     executeStartsWith, NULL);
    1300             : 
    1301         396 :         case jpiLikeRegex:      /* 'expr LIKE_REGEX pattern FLAGS flags' */
    1302             :             {
    1303             :                 /*
    1304             :                  * 'expr' is a sequence-returning expression.  'pattern' is a
    1305             :                  * regex string literal.  SQL/JSON standard requires XQuery
    1306             :                  * regexes, but we use Postgres regexes here.  'flags' is a
    1307             :                  * string literal converted to integer flags at compile-time.
    1308             :                  */
    1309         396 :                 JsonLikeRegexContext lrcxt = {0};
    1310             : 
    1311         396 :                 jspInitByBuffer(&larg, jsp->base,
    1312             :                                 jsp->content.like_regex.expr);
    1313             : 
    1314         396 :                 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
    1315             :                                         executeLikeRegex, &lrcxt);
    1316             :             }
    1317             : 
    1318       79188 :         case jpiExists:
    1319       79188 :             jspGetArg(jsp, &larg);
    1320             : 
    1321       79188 :             if (jspStrictAbsenseOfErrors(cxt))
    1322             :             {
    1323             :                 /*
    1324             :                  * In strict mode we must get a complete list of values to
    1325             :                  * check that there are no errors at all.
    1326             :                  */
    1327          48 :                 JsonValueList vals = {0};
    1328             :                 JsonPathExecResult res =
    1329          48 :                     executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
    1330             :                                                       false, &vals);
    1331             : 
    1332          48 :                 if (jperIsError(res))
    1333          36 :                     return jpbUnknown;
    1334             : 
    1335          12 :                 return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
    1336             :             }
    1337             :             else
    1338             :             {
    1339             :                 JsonPathExecResult res =
    1340       79140 :                     executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
    1341             :                                                       false, NULL);
    1342             : 
    1343       79140 :                 if (jperIsError(res))
    1344          24 :                     return jpbUnknown;
    1345             : 
    1346       79116 :                 return res == jperOk ? jpbTrue : jpbFalse;
    1347             :             }
    1348             : 
    1349           0 :         default:
    1350           0 :             elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
    1351             :             return jpbUnknown;
    1352             :     }
    1353             : }
    1354             : 
    1355             : /*
    1356             :  * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
    1357             :  * item onto the stack.
    1358             :  */
    1359             : static JsonPathBool
    1360       18456 : executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1361             :                       JsonbValue *jb)
    1362             : {
    1363             :     JsonbValue *prev;
    1364             :     JsonPathBool res;
    1365             : 
    1366       18456 :     prev = cxt->current;
    1367       18456 :     cxt->current = jb;
    1368       18456 :     res = executeBoolItem(cxt, jsp, jb, false);
    1369       18360 :     cxt->current = prev;
    1370             : 
    1371       18360 :     return res;
    1372             : }
    1373             : 
    1374             : /*
    1375             :  * Implementation of several jsonpath nodes:
    1376             :  *  - jpiAny (.** accessor),
    1377             :  *  - jpiAnyKey (.* accessor),
    1378             :  *  - jpiAnyArray ([*] accessor)
    1379             :  */
    1380             : static JsonPathExecResult
    1381        1896 : executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
    1382             :                JsonValueList *found, uint32 level, uint32 first, uint32 last,
    1383             :                bool ignoreStructuralErrors, bool unwrapNext)
    1384             : {
    1385        1896 :     JsonPathExecResult res = jperNotFound;
    1386             :     JsonbIterator *it;
    1387             :     int32       r;
    1388             :     JsonbValue  v;
    1389             : 
    1390        1896 :     check_stack_depth();
    1391             : 
    1392        1896 :     if (level > last)
    1393          30 :         return res;
    1394             : 
    1395        1866 :     it = JsonbIteratorInit(jbc);
    1396             : 
    1397             :     /*
    1398             :      * Recursively iterate over jsonb objects/arrays
    1399             :      */
    1400        9630 :     while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
    1401             :     {
    1402        8340 :         if (r == WJB_KEY)
    1403             :         {
    1404         552 :             r = JsonbIteratorNext(&it, &v, true);
    1405             :             Assert(r == WJB_VALUE);
    1406             :         }
    1407             : 
    1408        8340 :         if (r == WJB_VALUE || r == WJB_ELEM)
    1409             :         {
    1410             : 
    1411        5184 :             if (level >= first ||
    1412          12 :                 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
    1413          12 :                  v.type != jbvBinary))  /* leaves only requested */
    1414             :             {
    1415             :                 /* check expression */
    1416        5124 :                 if (jsp)
    1417             :                 {
    1418        3984 :                     if (ignoreStructuralErrors)
    1419             :                     {
    1420             :                         bool        savedIgnoreStructuralErrors;
    1421             : 
    1422         348 :                         savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
    1423         348 :                         cxt->ignoreStructuralErrors = true;
    1424         348 :                         res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
    1425         348 :                         cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
    1426             :                     }
    1427             :                     else
    1428        3636 :                         res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
    1429             : 
    1430        3858 :                     if (jperIsError(res))
    1431          66 :                         break;
    1432             : 
    1433        3792 :                     if (res == jperOk && !found)
    1434         324 :                         break;
    1435             :                 }
    1436        1140 :                 else if (found)
    1437        1116 :                     JsonValueListAppend(found, copyJsonbValue(&v));
    1438             :                 else
    1439          24 :                     return jperOk;
    1440             :             }
    1441             : 
    1442        4644 :             if (level < last && v.type == jbvBinary)
    1443             :             {
    1444         186 :                 res = executeAnyItem
    1445             :                     (cxt, jsp, v.val.binary.data, found,
    1446             :                      level + 1, first, last,
    1447             :                      ignoreStructuralErrors, unwrapNext);
    1448             : 
    1449         186 :                 if (jperIsError(res))
    1450           0 :                     break;
    1451             : 
    1452         186 :                 if (res == jperOk && found == NULL)
    1453          36 :                     break;
    1454             :             }
    1455             :         }
    1456             :     }
    1457             : 
    1458        1716 :     return res;
    1459             : }
    1460             : 
    1461             : /*
    1462             :  * Execute unary or binary predicate.
    1463             :  *
    1464             :  * Predicates have existence semantics, because their operands are item
    1465             :  * sequences.  Pairs of items from the left and right operand's sequences are
    1466             :  * checked.  TRUE returned only if any pair satisfying the condition is found.
    1467             :  * In strict mode, even if the desired pair has already been found, all pairs
    1468             :  * still need to be examined to check the absence of errors.  If any error
    1469             :  * occurs, UNKNOWN (analogous to SQL NULL) is returned.
    1470             :  */
    1471             : static JsonPathBool
    1472       54954 : executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
    1473             :                  JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
    1474             :                  bool unwrapRightArg, JsonPathPredicateCallback exec,
    1475             :                  void *param)
    1476             : {
    1477             :     JsonPathExecResult res;
    1478             :     JsonValueListIterator lseqit;
    1479       54954 :     JsonValueList lseq = {0};
    1480       54954 :     JsonValueList rseq = {0};
    1481             :     JsonbValue *lval;
    1482       54954 :     bool        error = false;
    1483       54954 :     bool        found = false;
    1484             : 
    1485             :     /* Left argument is always auto-unwrapped. */
    1486       54954 :     res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
    1487       54954 :     if (jperIsError(res))
    1488          18 :         return jpbUnknown;
    1489             : 
    1490       54936 :     if (rarg)
    1491             :     {
    1492             :         /* Right argument is conditionally auto-unwrapped. */
    1493       54540 :         res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
    1494             :                                                 unwrapRightArg, &rseq);
    1495       54534 :         if (jperIsError(res))
    1496          54 :             return jpbUnknown;
    1497             :     }
    1498             : 
    1499       54876 :     JsonValueListInitIterator(&lseq, &lseqit);
    1500       72042 :     while ((lval = JsonValueListNext(&lseq, &lseqit)))
    1501             :     {
    1502             :         JsonValueListIterator rseqit;
    1503             :         JsonbValue *rval;
    1504       22074 :         bool        first = true;
    1505             : 
    1506       22074 :         JsonValueListInitIterator(&rseq, &rseqit);
    1507       22074 :         if (rarg)
    1508       21678 :             rval = JsonValueListNext(&rseq, &rseqit);
    1509             :         else
    1510         396 :             rval = NULL;
    1511             : 
    1512             :         /* Loop over right arg sequence or do single pass otherwise */
    1513       34296 :         while (rarg ? (rval != NULL) : first)
    1514             :         {
    1515       17130 :             JsonPathBool res = exec(pred, lval, rval, param);
    1516             : 
    1517       17040 :             if (res == jpbUnknown)
    1518             :             {
    1519         678 :                 if (jspStrictAbsenseOfErrors(cxt))
    1520        4818 :                     return jpbUnknown;
    1521             : 
    1522         654 :                 error = true;
    1523             :             }
    1524       16362 :             else if (res == jpbTrue)
    1525             :             {
    1526        4842 :                 if (!jspStrictAbsenseOfErrors(cxt))
    1527        4794 :                     return jpbTrue;
    1528             : 
    1529          48 :                 found = true;
    1530             :             }
    1531             : 
    1532       12222 :             first = false;
    1533       12222 :             if (rarg)
    1534       11928 :                 rval = JsonValueListNext(&rseq, &rseqit);
    1535             :         }
    1536             :     }
    1537             : 
    1538       49968 :     if (found)                  /* possible only in strict mode */
    1539          18 :         return jpbTrue;
    1540             : 
    1541       49950 :     if (error)                  /* possible only in lax mode */
    1542         624 :         return jpbUnknown;
    1543             : 
    1544       49326 :     return jpbFalse;
    1545             : }
    1546             : 
    1547             : /*
    1548             :  * Execute binary arithmetic expression on singleton numeric operands.
    1549             :  * Array operands are automatically unwrapped in lax mode.
    1550             :  */
    1551             : static JsonPathExecResult
    1552         342 : executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1553             :                         JsonbValue *jb, BinaryArithmFunc func,
    1554             :                         JsonValueList *found)
    1555             : {
    1556             :     JsonPathExecResult jper;
    1557             :     JsonPathItem elem;
    1558         342 :     JsonValueList lseq = {0};
    1559         342 :     JsonValueList rseq = {0};
    1560             :     JsonbValue *lval;
    1561             :     JsonbValue *rval;
    1562             :     Numeric     res;
    1563             : 
    1564         342 :     jspGetLeftArg(jsp, &elem);
    1565             : 
    1566             :     /*
    1567             :      * XXX: By standard only operands of multiplicative expressions are
    1568             :      * unwrapped.  We extend it to other binary arithmetic expressions too.
    1569             :      */
    1570         342 :     jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
    1571         336 :     if (jperIsError(jper))
    1572           0 :         return jper;
    1573             : 
    1574         336 :     jspGetRightArg(jsp, &elem);
    1575             : 
    1576         336 :     jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
    1577         330 :     if (jperIsError(jper))
    1578           0 :         return jper;
    1579             : 
    1580         594 :     if (JsonValueListLength(&lseq) != 1 ||
    1581         264 :         !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
    1582          66 :         RETURN_ERROR(ereport(ERROR,
    1583             :                              (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
    1584             :                               errmsg("left operand of jsonpath operator %s is not a single numeric value",
    1585             :                                      jspOperationName(jsp->type)))));
    1586             : 
    1587         498 :     if (JsonValueListLength(&rseq) != 1 ||
    1588         234 :         !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
    1589          54 :         RETURN_ERROR(ereport(ERROR,
    1590             :                              (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
    1591             :                               errmsg("right operand of jsonpath operator %s is not a single numeric value",
    1592             :                                      jspOperationName(jsp->type)))));
    1593             : 
    1594         210 :     if (jspThrowErrors(cxt))
    1595             :     {
    1596          78 :         res = func(lval->val.numeric, rval->val.numeric, NULL);
    1597             :     }
    1598             :     else
    1599             :     {
    1600         132 :         bool        error = false;
    1601             : 
    1602         132 :         res = func(lval->val.numeric, rval->val.numeric, &error);
    1603             : 
    1604         132 :         if (error)
    1605          12 :             return jperError;
    1606             :     }
    1607             : 
    1608         174 :     if (!jspGetNext(jsp, &elem) && !found)
    1609           6 :         return jperOk;
    1610             : 
    1611         168 :     lval = palloc(sizeof(*lval));
    1612         168 :     lval->type = jbvNumeric;
    1613         168 :     lval->val.numeric = res;
    1614             : 
    1615         168 :     return executeNextItem(cxt, jsp, &elem, lval, found, false);
    1616             : }
    1617             : 
    1618             : /*
    1619             :  * Execute unary arithmetic expression for each numeric item in its operand's
    1620             :  * sequence.  Array operand is automatically unwrapped in lax mode.
    1621             :  */
    1622             : static JsonPathExecResult
    1623         186 : executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1624             :                        JsonbValue *jb, PGFunction func, JsonValueList *found)
    1625             : {
    1626             :     JsonPathExecResult jper;
    1627             :     JsonPathExecResult jper2;
    1628             :     JsonPathItem elem;
    1629         186 :     JsonValueList seq = {0};
    1630             :     JsonValueListIterator it;
    1631             :     JsonbValue *val;
    1632             :     bool        hasNext;
    1633             : 
    1634         186 :     jspGetArg(jsp, &elem);
    1635         186 :     jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
    1636             : 
    1637         180 :     if (jperIsError(jper))
    1638           0 :         return jper;
    1639             : 
    1640         180 :     jper = jperNotFound;
    1641             : 
    1642         180 :     hasNext = jspGetNext(jsp, &elem);
    1643             : 
    1644         180 :     JsonValueListInitIterator(&seq, &it);
    1645         324 :     while ((val = JsonValueListNext(&seq, &it)))
    1646             :     {
    1647         192 :         if ((val = getScalar(val, jbvNumeric)))
    1648             :         {
    1649         150 :             if (!found && !hasNext)
    1650          12 :                 return jperOk;
    1651             :         }
    1652             :         else
    1653             :         {
    1654          42 :             if (!found && !hasNext)
    1655           6 :                 continue;       /* skip non-numerics processing */
    1656             : 
    1657          36 :             RETURN_ERROR(ereport(ERROR,
    1658             :                                  (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
    1659             :                                   errmsg("operand of unary jsonpath operator %s is not a numeric value",
    1660             :                                          jspOperationName(jsp->type)))));
    1661             :         }
    1662             : 
    1663         138 :         if (func)
    1664          84 :             val->val.numeric =
    1665          84 :                 DatumGetNumeric(DirectFunctionCall1(func,
    1666             :                                                     NumericGetDatum(val->val.numeric)));
    1667             : 
    1668         138 :         jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
    1669             : 
    1670         138 :         if (jperIsError(jper2))
    1671           0 :             return jper2;
    1672             : 
    1673         138 :         if (jper2 == jperOk)
    1674             :         {
    1675         138 :             if (!found)
    1676           0 :                 return jperOk;
    1677         138 :             jper = jperOk;
    1678             :         }
    1679             :     }
    1680             : 
    1681         132 :     return jper;
    1682             : }
    1683             : 
    1684             : /*
    1685             :  * STARTS_WITH predicate callback.
    1686             :  *
    1687             :  * Check if the 'whole' string starts from 'initial' string.
    1688             :  */
    1689             : static JsonPathBool
    1690         174 : executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
    1691             :                   void *param)
    1692             : {
    1693         174 :     if (!(whole = getScalar(whole, jbvString)))
    1694          48 :         return jpbUnknown;      /* error */
    1695             : 
    1696         126 :     if (!(initial = getScalar(initial, jbvString)))
    1697           0 :         return jpbUnknown;      /* error */
    1698             : 
    1699         126 :     if (whole->val.string.len >= initial->val.string.len &&
    1700          90 :         !memcmp(whole->val.string.val,
    1701          90 :                 initial->val.string.val,
    1702          90 :                 initial->val.string.len))
    1703          54 :         return jpbTrue;
    1704             : 
    1705          72 :     return jpbFalse;
    1706             : }
    1707             : 
    1708             : /*
    1709             :  * LIKE_REGEX predicate callback.
    1710             :  *
    1711             :  * Check if the string matches regex pattern.
    1712             :  */
    1713             : static JsonPathBool
    1714         396 : executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
    1715             :                  void *param)
    1716             : {
    1717         396 :     JsonLikeRegexContext *cxt = param;
    1718             : 
    1719         396 :     if (!(str = getScalar(str, jbvString)))
    1720         120 :         return jpbUnknown;
    1721             : 
    1722             :     /* Cache regex text and converted flags. */
    1723         276 :     if (!cxt->regex)
    1724             :     {
    1725         276 :         cxt->regex =
    1726         276 :             cstring_to_text_with_len(jsp->content.like_regex.pattern,
    1727             :                                      jsp->content.like_regex.patternlen);
    1728         276 :         (void) jspConvertRegexFlags(jsp->content.like_regex.flags,
    1729             :                                     &(cxt->cflags), NULL);
    1730             :     }
    1731             : 
    1732         276 :     if (RE_compile_and_execute(cxt->regex, str->val.string.val,
    1733             :                                str->val.string.len,
    1734             :                                cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
    1735         102 :         return jpbTrue;
    1736             : 
    1737         174 :     return jpbFalse;
    1738             : }
    1739             : 
    1740             : /*
    1741             :  * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
    1742             :  * user function 'func'.
    1743             :  */
    1744             : static JsonPathExecResult
    1745         258 : executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1746             :                          JsonbValue *jb, bool unwrap, PGFunction func,
    1747             :                          JsonValueList *found)
    1748             : {
    1749             :     JsonPathItem next;
    1750             :     Datum       datum;
    1751             : 
    1752         258 :     if (unwrap && JsonbType(jb) == jbvArray)
    1753           0 :         return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
    1754             : 
    1755         258 :     if (!(jb = getScalar(jb, jbvNumeric)))
    1756          36 :         RETURN_ERROR(ereport(ERROR,
    1757             :                              (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
    1758             :                               errmsg("jsonpath item method .%s() can only be applied to a numeric value",
    1759             :                                      jspOperationName(jsp->type)))));
    1760             : 
    1761         222 :     datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
    1762             : 
    1763         222 :     if (!jspGetNext(jsp, &next) && !found)
    1764           0 :         return jperOk;
    1765             : 
    1766         222 :     jb = palloc(sizeof(*jb));
    1767         222 :     jb->type = jbvNumeric;
    1768         222 :     jb->val.numeric = DatumGetNumeric(datum);
    1769             : 
    1770         222 :     return executeNextItem(cxt, jsp, &next, jb, found, false);
    1771             : }
    1772             : 
    1773             : /*
    1774             :  * Implementation of the .datetime() method.
    1775             :  *
    1776             :  * Converts a string into a date/time value. The actual type is determined at run time.
    1777             :  * If an argument is provided, this argument is used as a template string.
    1778             :  * Otherwise, the first fitting ISO format is selected.
    1779             :  */
    1780             : static JsonPathExecResult
    1781        3234 : executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1782             :                       JsonbValue *jb, JsonValueList *found)
    1783             : {
    1784             :     JsonbValue  jbvbuf;
    1785             :     Datum       value;
    1786             :     text       *datetime;
    1787             :     Oid         collid;
    1788             :     Oid         typid;
    1789        3234 :     int32       typmod = -1;
    1790        3234 :     int         tz = 0;
    1791             :     bool        hasNext;
    1792        3234 :     JsonPathExecResult res = jperNotFound;
    1793             :     JsonPathItem elem;
    1794             : 
    1795        3234 :     if (!(jb = getScalar(jb, jbvString)))
    1796          30 :         RETURN_ERROR(ereport(ERROR,
    1797             :                              (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    1798             :                               errmsg("jsonpath item method .%s() can only be applied to a string",
    1799             :                                      jspOperationName(jsp->type)))));
    1800             : 
    1801        3204 :     datetime = cstring_to_text_with_len(jb->val.string.val,
    1802             :                                         jb->val.string.len);
    1803             : 
    1804             :     /*
    1805             :      * At some point we might wish to have callers supply the collation to
    1806             :      * use, but right now it's unclear that they'd be able to do better than
    1807             :      * DEFAULT_COLLATION_OID anyway.
    1808             :      */
    1809        3204 :     collid = DEFAULT_COLLATION_OID;
    1810             : 
    1811        3204 :     if (jsp->content.arg)
    1812             :     {
    1813             :         text       *template;
    1814             :         char       *template_str;
    1815             :         int         template_len;
    1816        1650 :         ErrorSaveContext escontext = {T_ErrorSaveContext};
    1817             : 
    1818        1650 :         jspGetArg(jsp, &elem);
    1819             : 
    1820        1650 :         if (elem.type != jpiString)
    1821           0 :             elog(ERROR, "invalid jsonpath item type for .datetime() argument");
    1822             : 
    1823        1650 :         template_str = jspGetString(&elem, &template_len);
    1824             : 
    1825        1650 :         template = cstring_to_text_with_len(template_str,
    1826             :                                             template_len);
    1827             : 
    1828        1650 :         value = parse_datetime(datetime, template, collid, true,
    1829             :                                &typid, &typmod, &tz,
    1830        1650 :                                jspThrowErrors(cxt) ? NULL : (Node *) &escontext);
    1831             : 
    1832        1590 :         if (escontext.error_occurred)
    1833           0 :             res = jperError;
    1834             :         else
    1835        1590 :             res = jperOk;
    1836             :     }
    1837             :     else
    1838             :     {
    1839             :         /*
    1840             :          * According to SQL/JSON standard enumerate ISO formats for: date,
    1841             :          * timetz, time, timestamptz, timestamp.
    1842             :          *
    1843             :          * We also support ISO 8601 format (with "T") for timestamps, because
    1844             :          * to_json[b]() functions use this format.
    1845             :          */
    1846             :         static const char *fmt_str[] =
    1847             :         {
    1848             :             "yyyy-mm-dd",     /* date */
    1849             :             "HH24:MI:SS.USTZH:TZM", /* timetz */
    1850             :             "HH24:MI:SS.USTZH",
    1851             :             "HH24:MI:SSTZH:TZM",
    1852             :             "HH24:MI:SSTZH",
    1853             :             "HH24:MI:SS.US",  /* time without tz */
    1854             :             "HH24:MI:SS",
    1855             :             "yyyy-mm-dd HH24:MI:SS.USTZH:TZM",    /* timestamptz */
    1856             :             "yyyy-mm-dd HH24:MI:SS.USTZH",
    1857             :             "yyyy-mm-dd HH24:MI:SSTZH:TZM",
    1858             :             "yyyy-mm-dd HH24:MI:SSTZH",
    1859             :             "yyyy-mm-dd\"T\"HH24:MI:SS.USTZH:TZM",
    1860             :             "yyyy-mm-dd\"T\"HH24:MI:SS.USTZH",
    1861             :             "yyyy-mm-dd\"T\"HH24:MI:SSTZH:TZM",
    1862             :             "yyyy-mm-dd\"T\"HH24:MI:SSTZH",
    1863             :             "yyyy-mm-dd HH24:MI:SS.US", /* timestamp without tz */
    1864             :             "yyyy-mm-dd HH24:MI:SS",
    1865             :             "yyyy-mm-dd\"T\"HH24:MI:SS.US",
    1866             :             "yyyy-mm-dd\"T\"HH24:MI:SS"
    1867             :         };
    1868             : 
    1869             :         /* cache for format texts */
    1870             :         static text *fmt_txt[lengthof(fmt_str)] = {0};
    1871             :         int         i;
    1872             : 
    1873             :         /* loop until datetime format fits */
    1874       13590 :         for (i = 0; i < lengthof(fmt_str); i++)
    1875             :         {
    1876       13572 :             ErrorSaveContext escontext = {T_ErrorSaveContext};
    1877             : 
    1878       13572 :             if (!fmt_txt[i])
    1879             :             {
    1880             :                 MemoryContext oldcxt =
    1881         114 :                     MemoryContextSwitchTo(TopMemoryContext);
    1882             : 
    1883         114 :                 fmt_txt[i] = cstring_to_text(fmt_str[i]);
    1884         114 :                 MemoryContextSwitchTo(oldcxt);
    1885             :             }
    1886             : 
    1887       13572 :             value = parse_datetime(datetime, fmt_txt[i], collid, true,
    1888             :                                    &typid, &typmod, &tz,
    1889             :                                    (Node *) &escontext);
    1890             : 
    1891       13572 :             if (!escontext.error_occurred)
    1892             :             {
    1893        1536 :                 res = jperOk;
    1894        1536 :                 break;
    1895             :             }
    1896             :         }
    1897             : 
    1898        1554 :         if (res == jperNotFound)
    1899          18 :             RETURN_ERROR(ereport(ERROR,
    1900             :                                  (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
    1901             :                                   errmsg("datetime format is not recognized: \"%s\"",
    1902             :                                          text_to_cstring(datetime)),
    1903             :                                   errhint("Use a datetime template argument to specify the input data format."))));
    1904             :     }
    1905             : 
    1906        3126 :     pfree(datetime);
    1907             : 
    1908        3126 :     if (jperIsError(res))
    1909           0 :         return res;
    1910             : 
    1911        3126 :     hasNext = jspGetNext(jsp, &elem);
    1912             : 
    1913        3126 :     if (!hasNext && !found)
    1914           6 :         return res;
    1915             : 
    1916        3120 :     jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
    1917             : 
    1918        3120 :     jb->type = jbvDatetime;
    1919        3120 :     jb->val.datetime.value = value;
    1920        3120 :     jb->val.datetime.typid = typid;
    1921        3120 :     jb->val.datetime.typmod = typmod;
    1922        3120 :     jb->val.datetime.tz = tz;
    1923             : 
    1924        3120 :     return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
    1925             : }
    1926             : 
    1927             : /*
    1928             :  * Implementation of .keyvalue() method.
    1929             :  *
    1930             :  * .keyvalue() method returns a sequence of object's key-value pairs in the
    1931             :  * following format: '{ "key": key, "value": value, "id": id }'.
    1932             :  *
    1933             :  * "id" field is an object identifier which is constructed from the two parts:
    1934             :  * base object id and its binary offset in base object's jsonb:
    1935             :  * id = 10000000000 * base_object_id + obj_offset_in_base_object
    1936             :  *
    1937             :  * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
    1938             :  * (maximal offset in jsonb).  Decimal multiplier is used here to improve the
    1939             :  * readability of identifiers.
    1940             :  *
    1941             :  * Base object is usually a root object of the path: context item '$' or path
    1942             :  * variable '$var', literals can't produce objects for now.  But if the path
    1943             :  * contains generated objects (.keyvalue() itself, for example), then they
    1944             :  * become base object for the subsequent .keyvalue().
    1945             :  *
    1946             :  * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
    1947             :  * of variables (see getJsonPathVariable()).  Ids for generated objects
    1948             :  * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
    1949             :  */
    1950             : static JsonPathExecResult
    1951          84 : executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
    1952             :                       JsonbValue *jb, JsonValueList *found)
    1953             : {
    1954          84 :     JsonPathExecResult res = jperNotFound;
    1955             :     JsonPathItem next;
    1956             :     JsonbContainer *jbc;
    1957             :     JsonbValue  key;
    1958             :     JsonbValue  val;
    1959             :     JsonbValue  idval;
    1960             :     JsonbValue  keystr;
    1961             :     JsonbValue  valstr;
    1962             :     JsonbValue  idstr;
    1963             :     JsonbIterator *it;
    1964             :     JsonbIteratorToken tok;
    1965             :     int64       id;
    1966             :     bool        hasNext;
    1967             : 
    1968          84 :     if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
    1969          24 :         RETURN_ERROR(ereport(ERROR,
    1970             :                              (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
    1971             :                               errmsg("jsonpath item method .%s() can only be applied to an object",
    1972             :                                      jspOperationName(jsp->type)))));
    1973             : 
    1974          60 :     jbc = jb->val.binary.data;
    1975             : 
    1976          60 :     if (!JsonContainerSize(jbc))
    1977          18 :         return jperNotFound;    /* no key-value pairs */
    1978             : 
    1979          42 :     hasNext = jspGetNext(jsp, &next);
    1980             : 
    1981          42 :     keystr.type = jbvString;
    1982          42 :     keystr.val.string.val = "key";
    1983          42 :     keystr.val.string.len = 3;
    1984             : 
    1985          42 :     valstr.type = jbvString;
    1986          42 :     valstr.val.string.val = "value";
    1987          42 :     valstr.val.string.len = 5;
    1988             : 
    1989          42 :     idstr.type = jbvString;
    1990          42 :     idstr.val.string.val = "id";
    1991          42 :     idstr.val.string.len = 2;
    1992             : 
    1993             :     /* construct object id from its base object and offset inside that */
    1994          42 :     id = jb->type != jbvBinary ? 0 :
    1995          42 :         (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
    1996          42 :     id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
    1997             : 
    1998          42 :     idval.type = jbvNumeric;
    1999          42 :     idval.val.numeric = int64_to_numeric(id);
    2000             : 
    2001          42 :     it = JsonbIteratorInit(jbc);
    2002             : 
    2003         168 :     while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
    2004             :     {
    2005             :         JsonBaseObjectInfo baseObject;
    2006             :         JsonbValue  obj;
    2007             :         JsonbParseState *ps;
    2008             :         JsonbValue *keyval;
    2009             :         Jsonb      *jsonb;
    2010             : 
    2011         138 :         if (tok != WJB_KEY)
    2012          72 :             continue;
    2013             : 
    2014          66 :         res = jperOk;
    2015             : 
    2016          66 :         if (!hasNext && !found)
    2017          12 :             break;
    2018             : 
    2019          60 :         tok = JsonbIteratorNext(&it, &val, true);
    2020             :         Assert(tok == WJB_VALUE);
    2021             : 
    2022          60 :         ps = NULL;
    2023          60 :         pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
    2024             : 
    2025          60 :         pushJsonbValue(&ps, WJB_KEY, &keystr);
    2026          60 :         pushJsonbValue(&ps, WJB_VALUE, &key);
    2027             : 
    2028          60 :         pushJsonbValue(&ps, WJB_KEY, &valstr);
    2029          60 :         pushJsonbValue(&ps, WJB_VALUE, &val);
    2030             : 
    2031          60 :         pushJsonbValue(&ps, WJB_KEY, &idstr);
    2032          60 :         pushJsonbValue(&ps, WJB_VALUE, &idval);
    2033             : 
    2034          60 :         keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
    2035             : 
    2036          60 :         jsonb = JsonbValueToJsonb(keyval);
    2037             : 
    2038          60 :         JsonbInitBinary(&obj, jsonb);
    2039             : 
    2040          60 :         baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
    2041             : 
    2042          60 :         res = executeNextItem(cxt, jsp, &next, &obj, found, true);
    2043             : 
    2044          60 :         cxt->baseObject = baseObject;
    2045             : 
    2046          60 :         if (jperIsError(res))
    2047           0 :             return res;
    2048             : 
    2049          60 :         if (res == jperOk && !found)
    2050           6 :             break;
    2051             :     }
    2052             : 
    2053          42 :     return res;
    2054             : }
    2055             : 
    2056             : /*
    2057             :  * Convert boolean execution status 'res' to a boolean JSON item and execute
    2058             :  * next jsonpath.
    2059             :  */
    2060             : static JsonPathExecResult
    2061      102162 : appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
    2062             :                  JsonValueList *found, JsonPathBool res)
    2063             : {
    2064             :     JsonPathItem next;
    2065             :     JsonbValue  jbv;
    2066             : 
    2067      102162 :     if (!jspGetNext(jsp, &next) && !found)
    2068           6 :         return jperOk;          /* found singleton boolean value */
    2069             : 
    2070      102156 :     if (res == jpbUnknown)
    2071             :     {
    2072          30 :         jbv.type = jbvNull;
    2073             :     }
    2074             :     else
    2075             :     {
    2076      102126 :         jbv.type = jbvBool;
    2077      102126 :         jbv.val.boolean = res == jpbTrue;
    2078             :     }
    2079             : 
    2080      102156 :     return executeNextItem(cxt, jsp, &next, &jbv, found, true);
    2081             : }
    2082             : 
    2083             : /*
    2084             :  * Convert jsonpath's scalar or variable node to actual jsonb value.
    2085             :  *
    2086             :  * If node is a variable then its id returned, otherwise 0 returned.
    2087             :  */
    2088             : static void
    2089       55536 : getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
    2090             :                 JsonbValue *value)
    2091             : {
    2092       55536 :     switch (item->type)
    2093             :     {
    2094        7554 :         case jpiNull:
    2095        7554 :             value->type = jbvNull;
    2096        7554 :             break;
    2097        1422 :         case jpiBool:
    2098        1422 :             value->type = jbvBool;
    2099        1422 :             value->val.boolean = jspGetBool(item);
    2100        1422 :             break;
    2101       19356 :         case jpiNumeric:
    2102       19356 :             value->type = jbvNumeric;
    2103       19356 :             value->val.numeric = jspGetNumeric(item);
    2104       19356 :             break;
    2105       21954 :         case jpiString:
    2106       21954 :             value->type = jbvString;
    2107       43908 :             value->val.string.val = jspGetString(item,
    2108       21954 :                                                  &value->val.string.len);
    2109       21954 :             break;
    2110        5250 :         case jpiVariable:
    2111        5250 :             getJsonPathVariable(cxt, item, cxt->vars, value);
    2112        5220 :             return;
    2113           0 :         default:
    2114           0 :             elog(ERROR, "unexpected jsonpath item type");
    2115             :     }
    2116             : }
    2117             : 
    2118             : /*
    2119             :  * Get the value of variable passed to jsonpath executor
    2120             :  */
    2121             : static void
    2122        5250 : getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
    2123             :                     Jsonb *vars, JsonbValue *value)
    2124             : {
    2125             :     char       *varName;
    2126             :     int         varNameLength;
    2127             :     JsonbValue  tmp;
    2128             :     JsonbValue *v;
    2129             : 
    2130        5250 :     if (!vars)
    2131             :     {
    2132           0 :         value->type = jbvNull;
    2133           0 :         return;
    2134             :     }
    2135             : 
    2136             :     Assert(variable->type == jpiVariable);
    2137        5250 :     varName = jspGetString(variable, &varNameLength);
    2138        5250 :     tmp.type = jbvString;
    2139        5250 :     tmp.val.string.val = varName;
    2140        5250 :     tmp.val.string.len = varNameLength;
    2141             : 
    2142        5250 :     v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
    2143             : 
    2144        5250 :     if (v)
    2145             :     {
    2146        5220 :         *value = *v;
    2147        5220 :         pfree(v);
    2148             :     }
    2149             :     else
    2150             :     {
    2151          30 :         ereport(ERROR,
    2152             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
    2153             :                  errmsg("could not find jsonpath variable \"%s\"",
    2154             :                         pnstrdup(varName, varNameLength))));
    2155             :     }
    2156             : 
    2157        5220 :     JsonbInitBinary(&tmp, vars);
    2158        5220 :     setBaseObject(cxt, &tmp, 1);
    2159             : }
    2160             : 
    2161             : /**************** Support functions for JsonPath execution *****************/
    2162             : 
    2163             : /*
    2164             :  * Returns the size of an array item, or -1 if item is not an array.
    2165             :  */
    2166             : static int
    2167         348 : JsonbArraySize(JsonbValue *jb)
    2168             : {
    2169             :     Assert(jb->type != jbvArray);
    2170             : 
    2171         348 :     if (jb->type == jbvBinary)
    2172             :     {
    2173         306 :         JsonbContainer *jbc = jb->val.binary.data;
    2174             : 
    2175         306 :         if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
    2176         294 :             return JsonContainerSize(jbc);
    2177             :     }
    2178             : 
    2179          54 :     return -1;
    2180             : }
    2181             : 
    2182             : /* Comparison predicate callback. */
    2183             : static JsonPathBool
    2184       16560 : executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
    2185             : {
    2186       16560 :     JsonPathExecContext *cxt = (JsonPathExecContext *) p;
    2187             : 
    2188       16560 :     return compareItems(cmp->type, lv, rv, cxt->useTz);
    2189             : }
    2190             : 
    2191             : /*
    2192             :  * Perform per-byte comparison of two strings.
    2193             :  */
    2194             : static int
    2195        3456 : binaryCompareStrings(const char *s1, int len1,
    2196             :                      const char *s2, int len2)
    2197             : {
    2198             :     int         cmp;
    2199             : 
    2200        3456 :     cmp = memcmp(s1, s2, Min(len1, len2));
    2201             : 
    2202        3456 :     if (cmp != 0)
    2203        1968 :         return cmp;
    2204             : 
    2205        1488 :     if (len1 == len2)
    2206         288 :         return 0;
    2207             : 
    2208        1200 :     return len1 < len2 ? -1 : 1;
    2209             : }
    2210             : 
    2211             : /*
    2212             :  * Compare two strings in the current server encoding using Unicode codepoint
    2213             :  * collation.
    2214             :  */
    2215             : static int
    2216        3456 : compareStrings(const char *mbstr1, int mblen1,
    2217             :                const char *mbstr2, int mblen2)
    2218             : {
    2219        4608 :     if (GetDatabaseEncoding() == PG_SQL_ASCII ||
    2220        1152 :         GetDatabaseEncoding() == PG_UTF8)
    2221             :     {
    2222             :         /*
    2223             :          * It's known property of UTF-8 strings that their per-byte comparison
    2224             :          * result matches codepoints comparison result.  ASCII can be
    2225             :          * considered as special case of UTF-8.
    2226             :          */
    2227        3456 :         return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
    2228             :     }
    2229             :     else
    2230             :     {
    2231             :         char       *utf8str1,
    2232             :                    *utf8str2;
    2233             :         int         cmp,
    2234             :                     utf8len1,
    2235             :                     utf8len2;
    2236             : 
    2237             :         /*
    2238             :          * We have to convert other encodings to UTF-8 first, then compare.
    2239             :          * Input strings may be not null-terminated and pg_server_to_any() may
    2240             :          * return them "as is".  So, use strlen() only if there is real
    2241             :          * conversion.
    2242             :          */
    2243           0 :         utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
    2244           0 :         utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
    2245           0 :         utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
    2246           0 :         utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
    2247             : 
    2248           0 :         cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
    2249             : 
    2250             :         /*
    2251             :          * If pg_server_to_any() did no real conversion, then we actually
    2252             :          * compared original strings.  So, we already done.
    2253             :          */
    2254           0 :         if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
    2255           0 :             return cmp;
    2256             : 
    2257             :         /* Free memory if needed */
    2258           0 :         if (mbstr1 != utf8str1)
    2259           0 :             pfree(utf8str1);
    2260           0 :         if (mbstr2 != utf8str2)
    2261           0 :             pfree(utf8str2);
    2262             : 
    2263             :         /*
    2264             :          * When all Unicode codepoints are equal, return result of binary
    2265             :          * comparison.  In some edge cases, same characters may have different
    2266             :          * representations in encoding.  Then our behavior could diverge from
    2267             :          * standard.  However, that allow us to do simple binary comparison
    2268             :          * for "==" operator, which is performance critical in typical cases.
    2269             :          * In future to implement strict standard conformance, we can do
    2270             :          * normalization of input JSON strings.
    2271             :          */
    2272           0 :         if (cmp == 0)
    2273           0 :             return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
    2274             :         else
    2275           0 :             return cmp;
    2276             :     }
    2277             : }
    2278             : 
    2279             : /*
    2280             :  * Compare two SQL/JSON items using comparison operation 'op'.
    2281             :  */
    2282             : static JsonPathBool
    2283       16560 : compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
    2284             : {
    2285             :     int         cmp;
    2286             :     bool        res;
    2287             : 
    2288       16560 :     if (jb1->type != jb2->type)
    2289             :     {
    2290        2970 :         if (jb1->type == jbvNull || jb2->type == jbvNull)
    2291             : 
    2292             :             /*
    2293             :              * Equality and order comparison of nulls to non-nulls returns
    2294             :              * always false, but inequality comparison returns true.
    2295             :              */
    2296        2724 :             return op == jpiNotEqual ? jpbTrue : jpbFalse;
    2297             : 
    2298             :         /* Non-null items of different types are not comparable. */
    2299         246 :         return jpbUnknown;
    2300             :     }
    2301             : 
    2302       13590 :     switch (jb1->type)
    2303             :     {
    2304         186 :         case jbvNull:
    2305         186 :             cmp = 0;
    2306         186 :             break;
    2307         870 :         case jbvBool:
    2308        1266 :             cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
    2309         396 :                 jb1->val.boolean ? 1 : -1;
    2310         870 :             break;
    2311        1164 :         case jbvNumeric:
    2312        1164 :             cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
    2313        1164 :             break;
    2314        9930 :         case jbvString:
    2315        9930 :             if (op == jpiEqual)
    2316        6474 :                 return jb1->val.string.len != jb2->val.string.len ||
    2317        3582 :                     memcmp(jb1->val.string.val,
    2318        3582 :                            jb2->val.string.val,
    2319        6474 :                            jb1->val.string.len) ? jpbFalse : jpbTrue;
    2320             : 
    2321        3456 :             cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
    2322        3456 :                                  jb2->val.string.val, jb2->val.string.len);
    2323        3456 :             break;
    2324        1428 :         case jbvDatetime:
    2325             :             {
    2326             :                 bool        cast_error;
    2327             : 
    2328        1428 :                 cmp = compareDatetime(jb1->val.datetime.value,
    2329             :                                       jb1->val.datetime.typid,
    2330             :                                       jb2->val.datetime.value,
    2331             :                                       jb2->val.datetime.typid,
    2332             :                                       useTz,
    2333             :                                       &cast_error);
    2334             : 
    2335        1338 :                 if (cast_error)
    2336         252 :                     return jpbUnknown;
    2337             :             }
    2338        1086 :             break;
    2339             : 
    2340          12 :         case jbvBinary:
    2341             :         case jbvArray:
    2342             :         case jbvObject:
    2343          12 :             return jpbUnknown;  /* non-scalars are not comparable */
    2344             : 
    2345           0 :         default:
    2346           0 :             elog(ERROR, "invalid jsonb value type %d", jb1->type);
    2347             :     }
    2348             : 
    2349        6762 :     switch (op)
    2350             :     {
    2351        1686 :         case jpiEqual:
    2352        1686 :             res = (cmp == 0);
    2353        1686 :             break;
    2354           6 :         case jpiNotEqual:
    2355           6 :             res = (cmp != 0);
    2356           6 :             break;
    2357        1416 :         case jpiLess:
    2358        1416 :             res = (cmp < 0);
    2359        1416 :             break;
    2360        1434 :         case jpiGreater:
    2361        1434 :             res = (cmp > 0);
    2362        1434 :             break;
    2363         876 :         case jpiLessOrEqual:
    2364         876 :             res = (cmp <= 0);
    2365         876 :             break;
    2366        1344 :         case jpiGreaterOrEqual:
    2367        1344 :             res = (cmp >= 0);
    2368        1344 :             break;
    2369           0 :         default:
    2370           0 :             elog(ERROR, "unrecognized jsonpath operation: %d", op);
    2371             :             return jpbUnknown;
    2372             :     }
    2373             : 
    2374        6762 :     return res ? jpbTrue : jpbFalse;
    2375             : }
    2376             : 
    2377             : /* Compare two numerics */
    2378             : static int
    2379        1164 : compareNumeric(Numeric a, Numeric b)
    2380             : {
    2381        1164 :     return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
    2382             :                                              NumericGetDatum(a),
    2383             :                                              NumericGetDatum(b)));
    2384             : }
    2385             : 
    2386             : static JsonbValue *
    2387      110778 : copyJsonbValue(JsonbValue *src)
    2388             : {
    2389      110778 :     JsonbValue *dst = palloc(sizeof(*dst));
    2390             : 
    2391      110778 :     *dst = *src;
    2392             : 
    2393      110778 :     return dst;
    2394             : }
    2395             : 
    2396             : /*
    2397             :  * Execute array subscript expression and convert resulting numeric item to
    2398             :  * the integer type with truncation.
    2399             :  */
    2400             : static JsonPathExecResult
    2401         318 : getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
    2402             :               int32 *index)
    2403             : {
    2404             :     JsonbValue *jbv;
    2405         318 :     JsonValueList found = {0};
    2406         318 :     JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
    2407             :     Datum       numeric_index;
    2408         312 :     bool        have_error = false;
    2409             : 
    2410         312 :     if (jperIsError(res))
    2411           0 :         return res;
    2412             : 
    2413         612 :     if (JsonValueListLength(&found) != 1 ||
    2414         300 :         !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
    2415          24 :         RETURN_ERROR(ereport(ERROR,
    2416             :                              (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
    2417             :                               errmsg("jsonpath array subscript is not a single numeric value"))));
    2418             : 
    2419         288 :     numeric_index = DirectFunctionCall2(numeric_trunc,
    2420             :                                         NumericGetDatum(jbv->val.numeric),
    2421             :                                         Int32GetDatum(0));
    2422             : 
    2423         288 :     *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
    2424             :                                     &have_error);
    2425             : 
    2426         288 :     if (have_error)
    2427          24 :         RETURN_ERROR(ereport(ERROR,
    2428             :                              (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
    2429             :                               errmsg("jsonpath array subscript is out of integer range"))));
    2430             : 
    2431         264 :     return jperOk;
    2432             : }
    2433             : 
    2434             : /* Save base object and its id needed for the execution of .keyvalue(). */
    2435             : static JsonBaseObjectInfo
    2436      207708 : setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
    2437             : {
    2438      207708 :     JsonBaseObjectInfo baseObject = cxt->baseObject;
    2439             : 
    2440      207708 :     cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
    2441             :         (JsonbContainer *) jbv->val.binary.data;
    2442      207708 :     cxt->baseObject.id = id;
    2443             : 
    2444      207708 :     return baseObject;
    2445             : }
    2446             : 
    2447             : static void
    2448      249480 : JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
    2449             : {
    2450      249480 :     if (jvl->singleton)
    2451             :     {
    2452         984 :         jvl->list = list_make2(jvl->singleton, jbv);
    2453         984 :         jvl->singleton = NULL;
    2454             :     }
    2455      248496 :     else if (!jvl->list)
    2456      247596 :         jvl->singleton = jbv;
    2457             :     else
    2458         900 :         jvl->list = lappend(jvl->list, jbv);
    2459      249480 : }
    2460             : 
    2461             : static int
    2462      103170 : JsonValueListLength(const JsonValueList *jvl)
    2463             : {
    2464      103170 :     return jvl->singleton ? 1 : list_length(jvl->list);
    2465             : }
    2466             : 
    2467             : static bool
    2468          18 : JsonValueListIsEmpty(JsonValueList *jvl)
    2469             : {
    2470          18 :     return !jvl->singleton && (jvl->list == NIL);
    2471             : }
    2472             : 
    2473             : static JsonbValue *
    2474      103020 : JsonValueListHead(JsonValueList *jvl)
    2475             : {
    2476      103020 :     return jvl->singleton ? jvl->singleton : linitial(jvl->list);
    2477             : }
    2478             : 
    2479             : static List *
    2480        1428 : JsonValueListGetList(JsonValueList *jvl)
    2481             : {
    2482        1428 :     if (jvl->singleton)
    2483         846 :         return list_make1(jvl->singleton);
    2484             : 
    2485         582 :     return jvl->list;
    2486             : }
    2487             : 
    2488             : static void
    2489      187242 : JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
    2490             : {
    2491      187242 :     if (jvl->singleton)
    2492             :     {
    2493      109782 :         it->value = jvl->singleton;
    2494      109782 :         it->list = NIL;
    2495      109782 :         it->next = NULL;
    2496             :     }
    2497       77460 :     else if (jvl->list != NIL)
    2498             :     {
    2499         618 :         it->value = (JsonbValue *) linitial(jvl->list);
    2500         618 :         it->list = jvl->list;
    2501         618 :         it->next = list_second_cell(jvl->list);
    2502             :     }
    2503             :     else
    2504             :     {
    2505       76842 :         it->value = NULL;
    2506       76842 :         it->list = NIL;
    2507       76842 :         it->next = NULL;
    2508             :     }
    2509      187242 : }
    2510             : 
    2511             : /*
    2512             :  * Get the next item from the sequence advancing iterator.
    2513             :  */
    2514             : static JsonbValue *
    2515      288354 : JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
    2516             : {
    2517      288354 :     JsonbValue *result = it->value;
    2518             : 
    2519      288354 :     if (it->next)
    2520             :     {
    2521        1068 :         it->value = lfirst(it->next);
    2522        1068 :         it->next = lnext(it->list, it->next);
    2523             :     }
    2524             :     else
    2525             :     {
    2526      287286 :         it->value = NULL;
    2527             :     }
    2528             : 
    2529      288354 :     return result;
    2530             : }
    2531             : 
    2532             : /*
    2533             :  * Initialize a binary JsonbValue with the given jsonb container.
    2534             :  */
    2535             : static JsonbValue *
    2536      194802 : JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
    2537             : {
    2538      194802 :     jbv->type = jbvBinary;
    2539      194802 :     jbv->val.binary.data = &jb->root;
    2540      194802 :     jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
    2541             : 
    2542      194802 :     return jbv;
    2543             : }
    2544             : 
    2545             : /*
    2546             :  * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
    2547             :  */
    2548             : static int
    2549      261360 : JsonbType(JsonbValue *jb)
    2550             : {
    2551      261360 :     int         type = jb->type;
    2552             : 
    2553      261360 :     if (jb->type == jbvBinary)
    2554             :     {
    2555      179448 :         JsonbContainer *jbc = (void *) jb->val.binary.data;
    2556             : 
    2557             :         /* Scalars should be always extracted during jsonpath execution. */
    2558             :         Assert(!JsonContainerIsScalar(jbc));
    2559             : 
    2560      179448 :         if (JsonContainerIsObject(jbc))
    2561      177804 :             type = jbvObject;
    2562        1644 :         else if (JsonContainerIsArray(jbc))
    2563        1644 :             type = jbvArray;
    2564             :         else
    2565           0 :             elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
    2566             :     }
    2567             : 
    2568      261360 :     return type;
    2569             : }
    2570             : 
    2571             : /* Get scalar of given type or NULL on type mismatch */
    2572             : static JsonbValue *
    2573        5178 : getScalar(JsonbValue *scalar, enum jbvType type)
    2574             : {
    2575             :     /* Scalars should be always extracted during jsonpath execution. */
    2576             :     Assert(scalar->type != jbvBinary ||
    2577             :            !JsonContainerIsScalar(scalar->val.binary.data));
    2578             : 
    2579        5178 :     return scalar->type == type ? scalar : NULL;
    2580             : }
    2581             : 
    2582             : /* Construct a JSON array from the item list */
    2583             : static JsonbValue *
    2584          42 : wrapItemsInArray(const JsonValueList *items)
    2585             : {
    2586          42 :     JsonbParseState *ps = NULL;
    2587             :     JsonValueListIterator it;
    2588             :     JsonbValue *jbv;
    2589             : 
    2590          42 :     pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
    2591             : 
    2592          42 :     JsonValueListInitIterator(items, &it);
    2593          84 :     while ((jbv = JsonValueListNext(items, &it)))
    2594          42 :         pushJsonbValue(&ps, WJB_ELEM, jbv);
    2595             : 
    2596          42 :     return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
    2597             : }
    2598             : 
    2599             : /* Check if the timezone required for casting from type1 to type2 is used */
    2600             : static void
    2601         378 : checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
    2602             : {
    2603         378 :     if (!useTz)
    2604          90 :         ereport(ERROR,
    2605             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2606             :                  errmsg("cannot convert value from %s to %s without time zone usage",
    2607             :                         type1, type2),
    2608             :                  errhint("Use *_tz() function for time zone support.")));
    2609         288 : }
    2610             : 
    2611             : /* Convert time datum to timetz datum */
    2612             : static Datum
    2613         144 : castTimeToTimeTz(Datum time, bool useTz)
    2614             : {
    2615         144 :     checkTimezoneIsUsedForCast(useTz, "time", "timetz");
    2616             : 
    2617         108 :     return DirectFunctionCall1(time_timetz, time);
    2618             : }
    2619             : 
    2620             : /*
    2621             :  * Compare date to timestamp.
    2622             :  * Note that this doesn't involve any timezone considerations.
    2623             :  */
    2624             : static int
    2625         114 : cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
    2626             : {
    2627         114 :     return date_cmp_timestamp_internal(date1, ts2);
    2628             : }
    2629             : 
    2630             : /*
    2631             :  * Compare date to timestamptz.
    2632             :  */
    2633             : static int
    2634          90 : cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
    2635             : {
    2636          90 :     checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
    2637             : 
    2638          72 :     return date_cmp_timestamptz_internal(date1, tstz2);
    2639             : }
    2640             : 
    2641             : /*
    2642             :  * Compare timestamp to timestamptz.
    2643             :  */
    2644             : static int
    2645         144 : cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
    2646             : {
    2647         144 :     checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
    2648             : 
    2649         108 :     return timestamp_cmp_timestamptz_internal(ts1, tstz2);
    2650             : }
    2651             : 
    2652             : /*
    2653             :  * Cross-type comparison of two datetime SQL/JSON items.  If items are
    2654             :  * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
    2655             :  * If the cast requires timezone and it is not used, then explicit error is thrown.
    2656             :  */
    2657             : static int
    2658        1428 : compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
    2659             :                 bool useTz, bool *cast_error)
    2660             : {
    2661             :     PGFunction  cmpfunc;
    2662             : 
    2663        1428 :     *cast_error = false;
    2664             : 
    2665        1428 :     switch (typid1)
    2666             :     {
    2667         222 :         case DATEOID:
    2668             :             switch (typid2)
    2669             :             {
    2670         108 :                 case DATEOID:
    2671         108 :                     cmpfunc = date_cmp;
    2672             : 
    2673         108 :                     break;
    2674             : 
    2675          42 :                 case TIMESTAMPOID:
    2676          42 :                     return cmpDateToTimestamp(DatumGetDateADT(val1),
    2677             :                                               DatumGetTimestamp(val2),
    2678             :                                               useTz);
    2679             : 
    2680          36 :                 case TIMESTAMPTZOID:
    2681          36 :                     return cmpDateToTimestampTz(DatumGetDateADT(val1),
    2682             :                                                 DatumGetTimestampTz(val2),
    2683             :                                                 useTz);
    2684             : 
    2685          36 :                 case TIMEOID:
    2686             :                 case TIMETZOID:
    2687          36 :                     *cast_error = true; /* uncomparable types */
    2688          36 :                     return 0;
    2689             : 
    2690           0 :                 default:
    2691           0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    2692             :                          typid2);
    2693             :             }
    2694         108 :             break;
    2695             : 
    2696         252 :         case TIMEOID:
    2697             :             switch (typid2)
    2698             :             {
    2699         108 :                 case TIMEOID:
    2700         108 :                     cmpfunc = time_cmp;
    2701             : 
    2702         108 :                     break;
    2703             : 
    2704          72 :                 case TIMETZOID:
    2705          72 :                     val1 = castTimeToTimeTz(val1, useTz);
    2706          54 :                     cmpfunc = timetz_cmp;
    2707             : 
    2708          54 :                     break;
    2709             : 
    2710          72 :                 case DATEOID:
    2711             :                 case TIMESTAMPOID:
    2712             :                 case TIMESTAMPTZOID:
    2713          72 :                     *cast_error = true; /* uncomparable types */
    2714          72 :                     return 0;
    2715             : 
    2716           0 :                 default:
    2717           0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    2718             :                          typid2);
    2719             :             }
    2720         162 :             break;
    2721             : 
    2722         324 :         case TIMETZOID:
    2723             :             switch (typid2)
    2724             :             {
    2725          72 :                 case TIMEOID:
    2726          72 :                     val2 = castTimeToTimeTz(val2, useTz);
    2727          54 :                     cmpfunc = timetz_cmp;
    2728             : 
    2729          54 :                     break;
    2730             : 
    2731         180 :                 case TIMETZOID:
    2732         180 :                     cmpfunc = timetz_cmp;
    2733             : 
    2734         180 :                     break;
    2735             : 
    2736          72 :                 case DATEOID:
    2737             :                 case TIMESTAMPOID:
    2738             :                 case TIMESTAMPTZOID:
    2739          72 :                     *cast_error = true; /* uncomparable types */
    2740          72 :                     return 0;
    2741             : 
    2742           0 :                 default:
    2743           0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    2744             :                          typid2);
    2745             :             }
    2746         234 :             break;
    2747             : 
    2748         288 :         case TIMESTAMPOID:
    2749             :             switch (typid2)
    2750             :             {
    2751          72 :                 case DATEOID:
    2752          72 :                     return -cmpDateToTimestamp(DatumGetDateADT(val2),
    2753             :                                                DatumGetTimestamp(val1),
    2754             :                                                useTz);
    2755             : 
    2756         108 :                 case TIMESTAMPOID:
    2757         108 :                     cmpfunc = timestamp_cmp;
    2758             : 
    2759         108 :                     break;
    2760             : 
    2761          72 :                 case TIMESTAMPTZOID:
    2762          72 :                     return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
    2763             :                                                      DatumGetTimestampTz(val2),
    2764             :                                                      useTz);
    2765             : 
    2766          36 :                 case TIMEOID:
    2767             :                 case TIMETZOID:
    2768          36 :                     *cast_error = true; /* uncomparable types */
    2769          36 :                     return 0;
    2770             : 
    2771           0 :                 default:
    2772           0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    2773             :                          typid2);
    2774             :             }
    2775         108 :             break;
    2776             : 
    2777         342 :         case TIMESTAMPTZOID:
    2778             :             switch (typid2)
    2779             :             {
    2780          54 :                 case DATEOID:
    2781          54 :                     return -cmpDateToTimestampTz(DatumGetDateADT(val2),
    2782             :                                                  DatumGetTimestampTz(val1),
    2783             :                                                  useTz);
    2784             : 
    2785          72 :                 case TIMESTAMPOID:
    2786          72 :                     return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
    2787             :                                                       DatumGetTimestampTz(val1),
    2788             :                                                       useTz);
    2789             : 
    2790         180 :                 case TIMESTAMPTZOID:
    2791         180 :                     cmpfunc = timestamp_cmp;
    2792             : 
    2793         180 :                     break;
    2794             : 
    2795          36 :                 case TIMEOID:
    2796             :                 case TIMETZOID:
    2797          36 :                     *cast_error = true; /* uncomparable types */
    2798          36 :                     return 0;
    2799             : 
    2800           0 :                 default:
    2801           0 :                     elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
    2802             :                          typid2);
    2803             :             }
    2804         180 :             break;
    2805             : 
    2806           0 :         default:
    2807           0 :             elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
    2808             :     }
    2809             : 
    2810         792 :     if (*cast_error)
    2811           0 :         return 0;               /* cast error */
    2812             : 
    2813         792 :     return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
    2814             : }

Generated by: LCOV version 1.14