LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 91.5 % 754 690
Test Date: 2026-04-07 14:16:30 Functions: 91.7 % 24 22
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * jsonpath.c
       4              :  *   Input/output and supporting routines for jsonpath
       5              :  *
       6              :  * jsonpath expression is a chain of path items.  First path item is $, $var,
       7              :  * literal or arithmetic expression.  Subsequent path items are accessors
       8              :  * (.key, .*, [subscripts], [*]), filters (? (predicate)) and methods (.type(),
       9              :  * .size() etc).
      10              :  *
      11              :  * For instance, structure of path items for simple expression:
      12              :  *
      13              :  *      $.a[*].type()
      14              :  *
      15              :  * is pretty evident:
      16              :  *
      17              :  *      $ => .a => [*] => .type()
      18              :  *
      19              :  * Some path items such as arithmetic operations, predicates or array
      20              :  * subscripts may comprise subtrees.  For instance, more complex expression
      21              :  *
      22              :  *      ($.a + $[1 to 5, 7] ? (@ > 3).double()).type()
      23              :  *
      24              :  * have following structure of path items:
      25              :  *
      26              :  *            +  =>  .type()
      27              :  *        ___/ \___
      28              :  *       /         \
      29              :  *      $ => .a  $  =>  []  => ?  =>  .double()
      30              :  *                        _||_      |
      31              :  *                       /    \     >
      32              :  *                      to    to   / \
      33              :  *                     / \    /   @   3
      34              :  *                    1   5  7
      35              :  *
      36              :  * Binary encoding of jsonpath constitutes a sequence of 4-bytes aligned
      37              :  * variable-length path items connected by links.  Every item has a header
      38              :  * consisting of item type (enum JsonPathItemType) and offset of next item
      39              :  * (zero means no next item).  After the header, item may have payload
      40              :  * depending on item type.  For instance, payload of '.key' accessor item is
      41              :  * length of key name and key name itself.  Payload of '>' arithmetic operator
      42              :  * item is offsets of right and left operands.
      43              :  *
      44              :  * So, binary representation of sample expression above is:
      45              :  * (bottom arrows are next links, top lines are argument links)
      46              :  *
      47              :  *                                _____
      48              :  *       _____                ___/____ \                __
      49              :  *    _ /_    \         _____/__/____ \ \      __    _ /_ \
      50              :  *   / /  \    \       /    /  /     \ \ \    /  \  / /  \ \
      51              :  * +(LR)  $ .a  $  [](* to *, * to *) 1 5 7 ?(A)  >(LR)   @ 3 .double() .type()
      52              :  * |      |  ^  |  ^|                        ^|                   ^        ^
      53              :  * |      |__|  |__||________________________||___________________|        |
      54              :  * |_______________________________________________________________________|
      55              :  *
      56              :  * Copyright (c) 2019-2026, PostgreSQL Global Development Group
      57              :  *
      58              :  * IDENTIFICATION
      59              :  *  src/backend/utils/adt/jsonpath.c
      60              :  *
      61              :  *-------------------------------------------------------------------------
      62              :  */
      63              : 
      64              : #include "postgres.h"
      65              : 
      66              : #include "catalog/pg_type.h"
      67              : #include "lib/stringinfo.h"
      68              : #include "libpq/pqformat.h"
      69              : #include "miscadmin.h"
      70              : #include "nodes/miscnodes.h"
      71              : #include "nodes/nodeFuncs.h"
      72              : #include "utils/fmgrprotos.h"
      73              : #include "utils/formatting.h"
      74              : #include "utils/json.h"
      75              : #include "utils/jsonpath.h"
      76              : 
      77              : 
      78              : static Datum jsonPathFromCstring(char *in, int len, struct Node *escontext);
      79              : static char *jsonPathToCstring(StringInfo out, JsonPath *in,
      80              :                                int estimated_len);
      81              : static bool flattenJsonPathParseItem(StringInfo buf, int *result,
      82              :                                      struct Node *escontext,
      83              :                                      JsonPathParseItem *item,
      84              :                                      int nestingLevel, bool insideArraySubscript);
      85              : static void alignStringInfoInt(StringInfo buf);
      86              : static int32 reserveSpaceForItemPointer(StringInfo buf);
      87              : static void printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
      88              :                               bool printBracketes);
      89              : static int  operationPriority(JsonPathItemType op);
      90              : 
      91              : 
      92              : /**************************** INPUT/OUTPUT ********************************/
      93              : 
      94              : /*
      95              :  * jsonpath type input function
      96              :  */
      97              : Datum
      98         7706 : jsonpath_in(PG_FUNCTION_ARGS)
      99              : {
     100         7706 :     char       *in = PG_GETARG_CSTRING(0);
     101         7706 :     int         len = strlen(in);
     102              : 
     103         7706 :     return jsonPathFromCstring(in, len, fcinfo->context);
     104              : }
     105              : 
     106              : /*
     107              :  * jsonpath type recv function
     108              :  *
     109              :  * The type is sent as text in binary mode, so this is almost the same
     110              :  * as the input function, but it's prefixed with a version number so we
     111              :  * can change the binary format sent in future if necessary. For now,
     112              :  * only version 1 is supported.
     113              :  */
     114              : Datum
     115            0 : jsonpath_recv(PG_FUNCTION_ARGS)
     116              : {
     117            0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     118            0 :     int         version = pq_getmsgint(buf, 1);
     119              :     char       *str;
     120              :     int         nbytes;
     121              : 
     122            0 :     if (version == JSONPATH_VERSION)
     123            0 :         str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     124              :     else
     125            0 :         elog(ERROR, "unsupported jsonpath version number: %d", version);
     126              : 
     127            0 :     return jsonPathFromCstring(str, nbytes, NULL);
     128              : }
     129              : 
     130              : /*
     131              :  * jsonpath type output function
     132              :  */
     133              : Datum
     134         1336 : jsonpath_out(PG_FUNCTION_ARGS)
     135              : {
     136         1336 :     JsonPath   *in = PG_GETARG_JSONPATH_P(0);
     137              : 
     138         1336 :     PG_RETURN_CSTRING(jsonPathToCstring(NULL, in, VARSIZE(in)));
     139              : }
     140              : 
     141              : /*
     142              :  * jsonpath type send function
     143              :  *
     144              :  * Just send jsonpath as a version number, then a string of text
     145              :  */
     146              : Datum
     147            0 : jsonpath_send(PG_FUNCTION_ARGS)
     148              : {
     149            0 :     JsonPath   *in = PG_GETARG_JSONPATH_P(0);
     150              :     StringInfoData buf;
     151              :     StringInfoData jtext;
     152            0 :     int         version = JSONPATH_VERSION;
     153              : 
     154            0 :     initStringInfo(&jtext);
     155            0 :     (void) jsonPathToCstring(&jtext, in, VARSIZE(in));
     156              : 
     157            0 :     pq_begintypsend(&buf);
     158            0 :     pq_sendint8(&buf, version);
     159            0 :     pq_sendtext(&buf, jtext.data, jtext.len);
     160            0 :     pfree(jtext.data);
     161              : 
     162            0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     163              : }
     164              : 
     165              : /*
     166              :  * Converts C-string to a jsonpath value.
     167              :  *
     168              :  * Uses jsonpath parser to turn string into an AST, then
     169              :  * flattenJsonPathParseItem() does second pass turning AST into binary
     170              :  * representation of jsonpath.
     171              :  */
     172              : static Datum
     173         7706 : jsonPathFromCstring(char *in, int len, struct Node *escontext)
     174              : {
     175         7706 :     JsonPathParseResult *jsonpath = parsejsonpath(in, len, escontext);
     176              :     JsonPath   *res;
     177              :     StringInfoData buf;
     178              : 
     179         7398 :     if (SOFT_ERROR_OCCURRED(escontext))
     180           28 :         return (Datum) 0;
     181              : 
     182         7370 :     if (!jsonpath)
     183            4 :         ereturn(escontext, (Datum) 0,
     184              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     185              :                  errmsg("invalid input syntax for type %s: \"%s\"", "jsonpath",
     186              :                         in)));
     187              : 
     188         7366 :     initStringInfo(&buf);
     189         7366 :     enlargeStringInfo(&buf, 4 * len /* estimation */ );
     190              : 
     191         7366 :     appendStringInfoSpaces(&buf, JSONPATH_HDRSZ);
     192              : 
     193         7366 :     if (!flattenJsonPathParseItem(&buf, NULL, escontext,
     194              :                                   jsonpath->expr, 0, false))
     195            8 :         return (Datum) 0;
     196              : 
     197         7346 :     res = (JsonPath *) buf.data;
     198         7346 :     SET_VARSIZE(res, buf.len);
     199         7346 :     res->header = JSONPATH_VERSION;
     200         7346 :     if (jsonpath->lax)
     201         6878 :         res->header |= JSONPATH_LAX;
     202              : 
     203         7346 :     PG_RETURN_JSONPATH_P(res);
     204              : }
     205              : 
     206              : /*
     207              :  * Converts jsonpath value to a C-string.
     208              :  *
     209              :  * If 'out' argument is non-null, the resulting C-string is stored inside the
     210              :  * StringBuffer.  The resulting string is always returned.
     211              :  */
     212              : static char *
     213         1336 : jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
     214              : {
     215              :     StringInfoData buf;
     216              :     JsonPathItem v;
     217              : 
     218         1336 :     if (!out)
     219              :     {
     220         1336 :         out = &buf;
     221         1336 :         initStringInfo(out);
     222              :     }
     223         1336 :     enlargeStringInfo(out, estimated_len);
     224              : 
     225         1336 :     if (!(in->header & JSONPATH_LAX))
     226           12 :         appendStringInfoString(out, "strict ");
     227              : 
     228         1336 :     jspInit(&v, in);
     229         1336 :     printJsonPathItem(out, &v, false, true);
     230              : 
     231         1336 :     return out->data;
     232              : }
     233              : 
     234              : /*
     235              :  * Recursive function converting given jsonpath parse item and all its
     236              :  * children into a binary representation.
     237              :  */
     238              : static bool
     239        24744 : flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
     240              :                          JsonPathParseItem *item, int nestingLevel,
     241              :                          bool insideArraySubscript)
     242              : {
     243              :     /* position from beginning of jsonpath data */
     244        24744 :     int32       pos = buf->len - JSONPATH_HDRSZ;
     245              :     int32       chld;
     246              :     int32       next;
     247        24744 :     int         argNestingLevel = 0;
     248              : 
     249        24744 :     check_stack_depth();
     250        24744 :     CHECK_FOR_INTERRUPTS();
     251              : 
     252        24744 :     appendStringInfoChar(buf, (char) (item->type));
     253              : 
     254              :     /*
     255              :      * We align buffer to int32 because a series of int32 values often goes
     256              :      * after the header, and we want to read them directly by dereferencing
     257              :      * int32 pointer (see jspInitByBuffer()).
     258              :      */
     259        24744 :     alignStringInfoInt(buf);
     260              : 
     261              :     /*
     262              :      * Reserve space for next item pointer.  Actual value will be recorded
     263              :      * later, after next and children items processing.
     264              :      */
     265        24744 :     next = reserveSpaceForItemPointer(buf);
     266              : 
     267        24744 :     switch (item->type)
     268              :     {
     269         4238 :         case jpiString:
     270              :         case jpiVariable:
     271              :         case jpiKey:
     272         4238 :             appendBinaryStringInfo(buf, &item->value.string.len,
     273              :                                    sizeof(item->value.string.len));
     274         4238 :             appendBinaryStringInfo(buf, item->value.string.val,
     275         4238 :                                    item->value.string.len);
     276         4238 :             appendStringInfoChar(buf, '\0');
     277         4238 :             break;
     278         1708 :         case jpiNumeric:
     279         1708 :             appendBinaryStringInfo(buf, item->value.numeric,
     280         1708 :                                    VARSIZE(item->value.numeric));
     281         1708 :             break;
     282          120 :         case jpiBool:
     283          120 :             appendBinaryStringInfo(buf, &item->value.boolean,
     284              :                                    sizeof(item->value.boolean));
     285          120 :             break;
     286         2536 :         case jpiAnd:
     287              :         case jpiOr:
     288              :         case jpiEqual:
     289              :         case jpiNotEqual:
     290              :         case jpiLess:
     291              :         case jpiGreater:
     292              :         case jpiLessOrEqual:
     293              :         case jpiGreaterOrEqual:
     294              :         case jpiAdd:
     295              :         case jpiSub:
     296              :         case jpiMul:
     297              :         case jpiDiv:
     298              :         case jpiMod:
     299              :         case jpiStartsWith:
     300              :         case jpiDecimal:
     301              :         case jpiStrReplace:
     302              :         case jpiStrSplitPart:
     303              :             {
     304              :                 /*
     305              :                  * First, reserve place for left/right arg's positions, then
     306              :                  * record both args and sets actual position in reserved
     307              :                  * places.
     308              :                  */
     309         2536 :                 int32       left = reserveSpaceForItemPointer(buf);
     310         2536 :                 int32       right = reserveSpaceForItemPointer(buf);
     311              : 
     312         2536 :                 if (!item->value.args.left)
     313          112 :                     chld = pos;
     314         2424 :                 else if (!flattenJsonPathParseItem(buf, &chld, escontext,
     315              :                                                    item->value.args.left,
     316              :                                                    nestingLevel + argNestingLevel,
     317              :                                                    insideArraySubscript))
     318            8 :                     return false;
     319         2520 :                 *(int32 *) (buf->data + left) = chld - pos;
     320              : 
     321         2520 :                 if (!item->value.args.right)
     322          112 :                     chld = pos;
     323         2408 :                 else if (!flattenJsonPathParseItem(buf, &chld, escontext,
     324              :                                                    item->value.args.right,
     325              :                                                    nestingLevel + argNestingLevel,
     326              :                                                    insideArraySubscript))
     327            0 :                     return false;
     328         2520 :                 *(int32 *) (buf->data + right) = chld - pos;
     329              :             }
     330         2520 :             break;
     331           80 :         case jpiLikeRegex:
     332              :             {
     333              :                 int32       offs;
     334              : 
     335           80 :                 appendBinaryStringInfo(buf,
     336           80 :                                        &item->value.like_regex.flags,
     337              :                                        sizeof(item->value.like_regex.flags));
     338           80 :                 offs = reserveSpaceForItemPointer(buf);
     339           80 :                 appendBinaryStringInfo(buf,
     340           80 :                                        &item->value.like_regex.patternlen,
     341              :                                        sizeof(item->value.like_regex.patternlen));
     342           80 :                 appendBinaryStringInfo(buf, item->value.like_regex.pattern,
     343           80 :                                        item->value.like_regex.patternlen);
     344           80 :                 appendStringInfoChar(buf, '\0');
     345              : 
     346           80 :                 if (!flattenJsonPathParseItem(buf, &chld, escontext,
     347              :                                               item->value.like_regex.expr,
     348              :                                               nestingLevel,
     349              :                                               insideArraySubscript))
     350            0 :                     return false;
     351           80 :                 *(int32 *) (buf->data + offs) = chld - pos;
     352              :             }
     353           80 :             break;
     354         1520 :         case jpiFilter:
     355         1520 :             argNestingLevel++;
     356              :             pg_fallthrough;
     357         3560 :         case jpiIsUnknown:
     358              :         case jpiNot:
     359              :         case jpiPlus:
     360              :         case jpiMinus:
     361              :         case jpiExists:
     362              :         case jpiDatetime:
     363              :         case jpiTime:
     364              :         case jpiTimeTz:
     365              :         case jpiTimestamp:
     366              :         case jpiTimestampTz:
     367              :         case jpiStrLtrim:
     368              :         case jpiStrRtrim:
     369              :         case jpiStrBtrim:
     370              :             {
     371         3560 :                 int32       arg = reserveSpaceForItemPointer(buf);
     372              : 
     373         3560 :                 if (!item->value.arg)
     374         1128 :                     chld = pos;
     375         2432 :                 else if (!flattenJsonPathParseItem(buf, &chld, escontext,
     376              :                                                    item->value.arg,
     377              :                                                    nestingLevel + argNestingLevel,
     378              :                                                    insideArraySubscript))
     379            0 :                     return false;
     380         3556 :                 *(int32 *) (buf->data + arg) = chld - pos;
     381              :             }
     382         3556 :             break;
     383           76 :         case jpiNull:
     384           76 :             break;
     385         7130 :         case jpiRoot:
     386         7130 :             break;
     387         1476 :         case jpiAnyArray:
     388              :         case jpiAnyKey:
     389         1476 :             break;
     390         1712 :         case jpiCurrent:
     391         1712 :             if (nestingLevel <= 0)
     392           12 :                 ereturn(escontext, false,
     393              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     394              :                          errmsg("@ is not allowed in root expressions")));
     395         1700 :             break;
     396           60 :         case jpiLast:
     397           60 :             if (!insideArraySubscript)
     398            8 :                 ereturn(escontext, false,
     399              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     400              :                          errmsg("LAST is allowed only in array subscripts")));
     401           52 :             break;
     402          340 :         case jpiIndexArray:
     403              :             {
     404          340 :                 int32       nelems = item->value.array.nelems;
     405              :                 int         offset;
     406              :                 int         i;
     407              : 
     408          340 :                 appendBinaryStringInfo(buf, &nelems, sizeof(nelems));
     409              : 
     410          340 :                 offset = buf->len;
     411              : 
     412          340 :                 appendStringInfoSpaces(buf, sizeof(int32) * 2 * nelems);
     413              : 
     414          712 :                 for (i = 0; i < nelems; i++)
     415              :                 {
     416              :                     int32      *ppos;
     417              :                     int32       topos;
     418              :                     int32       frompos;
     419              : 
     420          372 :                     if (!flattenJsonPathParseItem(buf, &frompos, escontext,
     421          372 :                                                   item->value.array.elems[i].from,
     422              :                                                   nestingLevel, true))
     423            0 :                         return false;
     424          372 :                     frompos -= pos;
     425              : 
     426          372 :                     if (item->value.array.elems[i].to)
     427              :                     {
     428           32 :                         if (!flattenJsonPathParseItem(buf, &topos, escontext,
     429           32 :                                                       item->value.array.elems[i].to,
     430              :                                                       nestingLevel, true))
     431            0 :                             return false;
     432           32 :                         topos -= pos;
     433              :                     }
     434              :                     else
     435          340 :                         topos = 0;
     436              : 
     437          372 :                     ppos = (int32 *) &buf->data[offset + i * 2 * sizeof(int32)];
     438              : 
     439          372 :                     ppos[0] = frompos;
     440          372 :                     ppos[1] = topos;
     441              :                 }
     442              :             }
     443          340 :             break;
     444          236 :         case jpiAny:
     445          236 :             appendBinaryStringInfo(buf,
     446          236 :                                    &item->value.anybounds.first,
     447              :                                    sizeof(item->value.anybounds.first));
     448          236 :             appendBinaryStringInfo(buf,
     449          236 :                                    &item->value.anybounds.last,
     450              :                                    sizeof(item->value.anybounds.last));
     451          236 :             break;
     452         1472 :         case jpiType:
     453              :         case jpiSize:
     454              :         case jpiAbs:
     455              :         case jpiFloor:
     456              :         case jpiCeiling:
     457              :         case jpiDouble:
     458              :         case jpiKeyValue:
     459              :         case jpiBigint:
     460              :         case jpiBoolean:
     461              :         case jpiDate:
     462              :         case jpiInteger:
     463              :         case jpiNumber:
     464              :         case jpiStringFunc:
     465              :         case jpiStrLower:
     466              :         case jpiStrUpper:
     467              :         case jpiStrInitcap:
     468         1472 :             break;
     469            0 :         default:
     470            0 :             elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
     471              :     }
     472              : 
     473        24704 :     if (item->next)
     474              :     {
     475         9630 :         if (!flattenJsonPathParseItem(buf, &chld, escontext,
     476              :                                       item->next, nestingLevel,
     477              :                                       insideArraySubscript))
     478            0 :             return false;
     479         9626 :         chld -= pos;
     480         9626 :         *(int32 *) (buf->data + next) = chld;
     481              :     }
     482              : 
     483        24700 :     if (result)
     484        17354 :         *result = pos;
     485        24700 :     return true;
     486              : }
     487              : 
     488              : /*
     489              :  * Align StringInfo to int by adding zero padding bytes
     490              :  */
     491              : static void
     492        24744 : alignStringInfoInt(StringInfo buf)
     493              : {
     494        24744 :     switch (INTALIGN(buf->len) - buf->len)
     495              :     {
     496        22556 :         case 3:
     497        22556 :             appendStringInfoCharMacro(buf, 0);
     498              :             pg_fallthrough;
     499              :         case 2:
     500        22828 :             appendStringInfoCharMacro(buf, 0);
     501              :             pg_fallthrough;
     502              :         case 1:
     503        24444 :             appendStringInfoCharMacro(buf, 0);
     504              :             pg_fallthrough;
     505              :         default:
     506        24744 :             break;
     507              :     }
     508        24744 : }
     509              : 
     510              : /*
     511              :  * Reserve space for int32 JsonPathItem pointer.  Now zero pointer is written,
     512              :  * actual value will be recorded at '(int32 *) &buf->data[pos]' later.
     513              :  */
     514              : static int32
     515        33456 : reserveSpaceForItemPointer(StringInfo buf)
     516              : {
     517        33456 :     int32       pos = buf->len;
     518        33456 :     int32       ptr = 0;
     519              : 
     520        33456 :     appendBinaryStringInfo(buf, &ptr, sizeof(ptr));
     521              : 
     522        33456 :     return pos;
     523              : }
     524              : 
     525              : /*
     526              :  * Prints text representation of given jsonpath item and all its children.
     527              :  */
     528              : static void
     529         4636 : printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
     530              :                   bool printBracketes)
     531              : {
     532              :     JsonPathItem elem;
     533              :     int         i;
     534              :     int32       len;
     535              :     char       *str;
     536              : 
     537         4636 :     check_stack_depth();
     538         4636 :     CHECK_FOR_INTERRUPTS();
     539              : 
     540         4636 :     switch (v->type)
     541              :     {
     542           28 :         case jpiNull:
     543           28 :             appendStringInfoString(buf, "null");
     544           28 :             break;
     545           88 :         case jpiString:
     546           88 :             str = jspGetString(v, &len);
     547           88 :             escape_json_with_len(buf, str, len);
     548           88 :             break;
     549          656 :         case jpiNumeric:
     550          656 :             if (jspHasNext(v))
     551           56 :                 appendStringInfoChar(buf, '(');
     552          656 :             appendStringInfoString(buf,
     553          656 :                                    DatumGetCString(DirectFunctionCall1(numeric_out,
     554              :                                                                        NumericGetDatum(jspGetNumeric(v)))));
     555          656 :             if (jspHasNext(v))
     556           56 :                 appendStringInfoChar(buf, ')');
     557          656 :             break;
     558            8 :         case jpiBool:
     559            8 :             if (jspGetBool(v))
     560            4 :                 appendStringInfoString(buf, "true");
     561              :             else
     562            4 :                 appendStringInfoString(buf, "false");
     563            8 :             break;
     564          524 :         case jpiAnd:
     565              :         case jpiOr:
     566              :         case jpiEqual:
     567              :         case jpiNotEqual:
     568              :         case jpiLess:
     569              :         case jpiGreater:
     570              :         case jpiLessOrEqual:
     571              :         case jpiGreaterOrEqual:
     572              :         case jpiAdd:
     573              :         case jpiSub:
     574              :         case jpiMul:
     575              :         case jpiDiv:
     576              :         case jpiMod:
     577              :         case jpiStartsWith:
     578          524 :             if (printBracketes)
     579           76 :                 appendStringInfoChar(buf, '(');
     580          524 :             jspGetLeftArg(v, &elem);
     581          524 :             printJsonPathItem(buf, &elem, false,
     582          524 :                               operationPriority(elem.type) <=
     583          524 :                               operationPriority(v->type));
     584          524 :             appendStringInfoChar(buf, ' ');
     585          524 :             appendStringInfoString(buf, jspOperationName(v->type));
     586          524 :             appendStringInfoChar(buf, ' ');
     587          524 :             jspGetRightArg(v, &elem);
     588          524 :             printJsonPathItem(buf, &elem, false,
     589          524 :                               operationPriority(elem.type) <=
     590          524 :                               operationPriority(v->type));
     591          524 :             if (printBracketes)
     592           76 :                 appendStringInfoChar(buf, ')');
     593          524 :             break;
     594            8 :         case jpiNot:
     595            8 :             appendStringInfoString(buf, "!(");
     596            8 :             jspGetArg(v, &elem);
     597            8 :             printJsonPathItem(buf, &elem, false, false);
     598            8 :             appendStringInfoChar(buf, ')');
     599            8 :             break;
     600            4 :         case jpiIsUnknown:
     601            4 :             appendStringInfoChar(buf, '(');
     602            4 :             jspGetArg(v, &elem);
     603            4 :             printJsonPathItem(buf, &elem, false, false);
     604            4 :             appendStringInfoString(buf, ") is unknown");
     605            4 :             break;
     606           32 :         case jpiPlus:
     607              :         case jpiMinus:
     608           32 :             if (printBracketes)
     609           12 :                 appendStringInfoChar(buf, '(');
     610           32 :             appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
     611           32 :             jspGetArg(v, &elem);
     612           32 :             printJsonPathItem(buf, &elem, false,
     613           32 :                               operationPriority(elem.type) <=
     614           32 :                               operationPriority(v->type));
     615           32 :             if (printBracketes)
     616           12 :                 appendStringInfoChar(buf, ')');
     617           32 :             break;
     618          144 :         case jpiAnyArray:
     619          144 :             appendStringInfoString(buf, "[*]");
     620          144 :             break;
     621            8 :         case jpiAnyKey:
     622            8 :             if (inKey)
     623            8 :                 appendStringInfoChar(buf, '.');
     624            8 :             appendStringInfoChar(buf, '*');
     625            8 :             break;
     626           64 :         case jpiIndexArray:
     627           64 :             appendStringInfoChar(buf, '[');
     628          140 :             for (i = 0; i < v->content.array.nelems; i++)
     629              :             {
     630              :                 JsonPathItem from;
     631              :                 JsonPathItem to;
     632           76 :                 bool        range = jspGetArraySubscript(v, &from, &to, i);
     633              : 
     634           76 :                 if (i)
     635           12 :                     appendStringInfoChar(buf, ',');
     636              : 
     637           76 :                 printJsonPathItem(buf, &from, false, false);
     638              : 
     639           76 :                 if (range)
     640              :                 {
     641            8 :                     appendStringInfoString(buf, " to ");
     642            8 :                     printJsonPathItem(buf, &to, false, false);
     643              :                 }
     644              :             }
     645           64 :             appendStringInfoChar(buf, ']');
     646           64 :             break;
     647           32 :         case jpiAny:
     648           32 :             if (inKey)
     649           32 :                 appendStringInfoChar(buf, '.');
     650              : 
     651           32 :             if (v->content.anybounds.first == 0 &&
     652            8 :                 v->content.anybounds.last == PG_UINT32_MAX)
     653            4 :                 appendStringInfoString(buf, "**");
     654           28 :             else if (v->content.anybounds.first == v->content.anybounds.last)
     655              :             {
     656           12 :                 if (v->content.anybounds.first == PG_UINT32_MAX)
     657            4 :                     appendStringInfoString(buf, "**{last}");
     658              :                 else
     659            8 :                     appendStringInfo(buf, "**{%u}",
     660              :                                      v->content.anybounds.first);
     661              :             }
     662           16 :             else if (v->content.anybounds.first == PG_UINT32_MAX)
     663            4 :                 appendStringInfo(buf, "**{last to %u}",
     664              :                                  v->content.anybounds.last);
     665           12 :             else if (v->content.anybounds.last == PG_UINT32_MAX)
     666            4 :                 appendStringInfo(buf, "**{%u to last}",
     667              :                                  v->content.anybounds.first);
     668              :             else
     669            8 :                 appendStringInfo(buf, "**{%u to %u}",
     670              :                                  v->content.anybounds.first,
     671              :                                  v->content.anybounds.last);
     672           32 :             break;
     673          884 :         case jpiKey:
     674          884 :             if (inKey)
     675          884 :                 appendStringInfoChar(buf, '.');
     676          884 :             str = jspGetString(v, &len);
     677          884 :             escape_json_with_len(buf, str, len);
     678          884 :             break;
     679          400 :         case jpiCurrent:
     680              :             Assert(!inKey);
     681          400 :             appendStringInfoChar(buf, '@');
     682          400 :             break;
     683         1124 :         case jpiRoot:
     684              :             Assert(!inKey);
     685         1124 :             appendStringInfoChar(buf, '$');
     686         1124 :             break;
     687           48 :         case jpiVariable:
     688           48 :             appendStringInfoChar(buf, '$');
     689           48 :             str = jspGetString(v, &len);
     690           48 :             escape_json_with_len(buf, str, len);
     691           48 :             break;
     692          352 :         case jpiFilter:
     693          352 :             appendStringInfoString(buf, "?(");
     694          352 :             jspGetArg(v, &elem);
     695          352 :             printJsonPathItem(buf, &elem, false, false);
     696          352 :             appendStringInfoChar(buf, ')');
     697          352 :             break;
     698           16 :         case jpiExists:
     699           16 :             appendStringInfoString(buf, "exists (");
     700           16 :             jspGetArg(v, &elem);
     701           16 :             printJsonPathItem(buf, &elem, false, false);
     702           16 :             appendStringInfoChar(buf, ')');
     703           16 :             break;
     704           20 :         case jpiType:
     705           20 :             appendStringInfoString(buf, ".type()");
     706           20 :             break;
     707            4 :         case jpiSize:
     708            4 :             appendStringInfoString(buf, ".size()");
     709            4 :             break;
     710            4 :         case jpiAbs:
     711            4 :             appendStringInfoString(buf, ".abs()");
     712            4 :             break;
     713            4 :         case jpiFloor:
     714            4 :             appendStringInfoString(buf, ".floor()");
     715            4 :             break;
     716            4 :         case jpiCeiling:
     717            4 :             appendStringInfoString(buf, ".ceiling()");
     718            4 :             break;
     719            4 :         case jpiDouble:
     720            4 :             appendStringInfoString(buf, ".double()");
     721            4 :             break;
     722            8 :         case jpiDatetime:
     723            8 :             appendStringInfoString(buf, ".datetime(");
     724            8 :             if (v->content.arg)
     725              :             {
     726            4 :                 jspGetArg(v, &elem);
     727            4 :                 printJsonPathItem(buf, &elem, false, false);
     728              :             }
     729            8 :             appendStringInfoChar(buf, ')');
     730            8 :             break;
     731            4 :         case jpiKeyValue:
     732            4 :             appendStringInfoString(buf, ".keyvalue()");
     733            4 :             break;
     734            8 :         case jpiLast:
     735            8 :             appendStringInfoString(buf, "last");
     736            8 :             break;
     737           32 :         case jpiLikeRegex:
     738           32 :             if (printBracketes)
     739            0 :                 appendStringInfoChar(buf, '(');
     740              : 
     741           32 :             jspInitByBuffer(&elem, v->base, v->content.like_regex.expr);
     742           32 :             printJsonPathItem(buf, &elem, false,
     743           32 :                               operationPriority(elem.type) <=
     744           32 :                               operationPriority(v->type));
     745              : 
     746           32 :             appendStringInfoString(buf, " like_regex ");
     747              : 
     748           32 :             escape_json_with_len(buf,
     749           32 :                                  v->content.like_regex.pattern,
     750              :                                  v->content.like_regex.patternlen);
     751              : 
     752           32 :             if (v->content.like_regex.flags)
     753              :             {
     754           24 :                 appendStringInfoString(buf, " flag \"");
     755              : 
     756           24 :                 if (v->content.like_regex.flags & JSP_REGEX_ICASE)
     757           20 :                     appendStringInfoChar(buf, 'i');
     758           24 :                 if (v->content.like_regex.flags & JSP_REGEX_DOTALL)
     759           12 :                     appendStringInfoChar(buf, 's');
     760           24 :                 if (v->content.like_regex.flags & JSP_REGEX_MLINE)
     761            8 :                     appendStringInfoChar(buf, 'm');
     762           24 :                 if (v->content.like_regex.flags & JSP_REGEX_WSPACE)
     763            4 :                     appendStringInfoChar(buf, 'x');
     764           24 :                 if (v->content.like_regex.flags & JSP_REGEX_QUOTE)
     765           12 :                     appendStringInfoChar(buf, 'q');
     766              : 
     767           24 :                 appendStringInfoChar(buf, '"');
     768              :             }
     769              : 
     770           32 :             if (printBracketes)
     771            0 :                 appendStringInfoChar(buf, ')');
     772           32 :             break;
     773            4 :         case jpiBigint:
     774            4 :             appendStringInfoString(buf, ".bigint()");
     775            4 :             break;
     776            4 :         case jpiBoolean:
     777            4 :             appendStringInfoString(buf, ".boolean()");
     778            4 :             break;
     779            4 :         case jpiDate:
     780            4 :             appendStringInfoString(buf, ".date()");
     781            4 :             break;
     782            8 :         case jpiDecimal:
     783            8 :             appendStringInfoString(buf, ".decimal(");
     784            8 :             if (v->content.args.left)
     785              :             {
     786            4 :                 jspGetLeftArg(v, &elem);
     787            4 :                 printJsonPathItem(buf, &elem, false, false);
     788              :             }
     789            8 :             if (v->content.args.right)
     790              :             {
     791            4 :                 appendStringInfoChar(buf, ',');
     792            4 :                 jspGetRightArg(v, &elem);
     793            4 :                 printJsonPathItem(buf, &elem, false, false);
     794              :             }
     795            8 :             appendStringInfoChar(buf, ')');
     796            8 :             break;
     797            4 :         case jpiInteger:
     798            4 :             appendStringInfoString(buf, ".integer()");
     799            4 :             break;
     800            4 :         case jpiNumber:
     801            4 :             appendStringInfoString(buf, ".number()");
     802            4 :             break;
     803            4 :         case jpiStringFunc:
     804            4 :             appendStringInfoString(buf, ".string()");
     805            4 :             break;
     806            8 :         case jpiTime:
     807            8 :             appendStringInfoString(buf, ".time(");
     808            8 :             if (v->content.arg)
     809              :             {
     810            4 :                 jspGetArg(v, &elem);
     811            4 :                 printJsonPathItem(buf, &elem, false, false);
     812              :             }
     813            8 :             appendStringInfoChar(buf, ')');
     814            8 :             break;
     815            8 :         case jpiTimeTz:
     816            8 :             appendStringInfoString(buf, ".time_tz(");
     817            8 :             if (v->content.arg)
     818              :             {
     819            4 :                 jspGetArg(v, &elem);
     820            4 :                 printJsonPathItem(buf, &elem, false, false);
     821              :             }
     822            8 :             appendStringInfoChar(buf, ')');
     823            8 :             break;
     824            8 :         case jpiTimestamp:
     825            8 :             appendStringInfoString(buf, ".timestamp(");
     826            8 :             if (v->content.arg)
     827              :             {
     828            4 :                 jspGetArg(v, &elem);
     829            4 :                 printJsonPathItem(buf, &elem, false, false);
     830              :             }
     831            8 :             appendStringInfoChar(buf, ')');
     832            8 :             break;
     833            8 :         case jpiTimestampTz:
     834            8 :             appendStringInfoString(buf, ".timestamp_tz(");
     835            8 :             if (v->content.arg)
     836              :             {
     837            4 :                 jspGetArg(v, &elem);
     838            4 :                 printJsonPathItem(buf, &elem, false, false);
     839              :             }
     840            8 :             appendStringInfoChar(buf, ')');
     841            8 :             break;
     842            8 :         case jpiStrReplace:
     843            8 :             appendStringInfoString(buf, ".replace(");
     844            8 :             jspGetLeftArg(v, &elem);
     845            8 :             printJsonPathItem(buf, &elem, false, false);
     846            8 :             appendStringInfoChar(buf, ',');
     847            8 :             jspGetRightArg(v, &elem);
     848            8 :             printJsonPathItem(buf, &elem, false, false);
     849            8 :             appendStringInfoChar(buf, ')');
     850            8 :             break;
     851           12 :         case jpiStrLower:
     852           12 :             appendStringInfoString(buf, ".lower()");
     853           12 :             break;
     854            8 :         case jpiStrUpper:
     855            8 :             appendStringInfoString(buf, ".upper()");
     856            8 :             break;
     857            4 :         case jpiStrSplitPart:
     858            4 :             appendStringInfoString(buf, ".split_part(");
     859            4 :             jspGetLeftArg(v, &elem);
     860            4 :             printJsonPathItem(buf, &elem, false, false);
     861            4 :             appendStringInfoChar(buf, ',');
     862            4 :             jspGetRightArg(v, &elem);
     863            4 :             printJsonPathItem(buf, &elem, false, false);
     864            4 :             appendStringInfoChar(buf, ')');
     865            4 :             break;
     866            8 :         case jpiStrLtrim:
     867            8 :             appendStringInfoString(buf, ".ltrim(");
     868            8 :             if (v->content.arg)
     869              :             {
     870            4 :                 jspGetArg(v, &elem);
     871            4 :                 printJsonPathItem(buf, &elem, false, false);
     872              :             }
     873            8 :             appendStringInfoChar(buf, ')');
     874            8 :             break;
     875            8 :         case jpiStrRtrim:
     876            8 :             appendStringInfoString(buf, ".rtrim(");
     877            8 :             if (v->content.arg)
     878              :             {
     879            4 :                 jspGetArg(v, &elem);
     880            4 :                 printJsonPathItem(buf, &elem, false, false);
     881              :             }
     882            8 :             appendStringInfoChar(buf, ')');
     883            8 :             break;
     884            8 :         case jpiStrBtrim:
     885            8 :             appendStringInfoString(buf, ".btrim(");
     886            8 :             if (v->content.arg)
     887              :             {
     888            4 :                 jspGetArg(v, &elem);
     889            4 :                 printJsonPathItem(buf, &elem, false, false);
     890              :             }
     891            8 :             appendStringInfoChar(buf, ')');
     892            8 :             break;
     893            4 :         case jpiStrInitcap:
     894            4 :             appendStringInfoString(buf, ".initcap()");
     895            4 :             break;
     896            0 :         default:
     897            0 :             elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
     898              :     }
     899              : 
     900         4636 :     if (jspGetNext(v, &elem))
     901         1660 :         printJsonPathItem(buf, &elem, true, true);
     902         4636 : }
     903              : 
     904              : const char *
     905         1160 : jspOperationName(JsonPathItemType type)
     906              : {
     907         1160 :     switch (type)
     908              :     {
     909           20 :         case jpiAnd:
     910           20 :             return "&&";
     911           36 :         case jpiOr:
     912           36 :             return "||";
     913          112 :         case jpiEqual:
     914          112 :             return "==";
     915            4 :         case jpiNotEqual:
     916            4 :             return "!=";
     917          200 :         case jpiLess:
     918          200 :             return "<";
     919           28 :         case jpiGreater:
     920           28 :             return ">";
     921            4 :         case jpiLessOrEqual:
     922            4 :             return "<=";
     923           28 :         case jpiGreaterOrEqual:
     924           28 :             return ">=";
     925           56 :         case jpiAdd:
     926              :         case jpiPlus:
     927           56 :             return "+";
     928           24 :         case jpiSub:
     929              :         case jpiMinus:
     930           24 :             return "-";
     931           16 :         case jpiMul:
     932           16 :             return "*";
     933            4 :         case jpiDiv:
     934            4 :             return "/";
     935            4 :         case jpiMod:
     936            4 :             return "%";
     937            0 :         case jpiType:
     938            0 :             return "type";
     939            4 :         case jpiSize:
     940            4 :             return "size";
     941            4 :         case jpiAbs:
     942            4 :             return "abs";
     943            4 :         case jpiFloor:
     944            4 :             return "floor";
     945            4 :         case jpiCeiling:
     946            4 :             return "ceiling";
     947           40 :         case jpiDouble:
     948           40 :             return "double";
     949           20 :         case jpiDatetime:
     950           20 :             return "datetime";
     951           12 :         case jpiKeyValue:
     952           12 :             return "keyvalue";
     953            8 :         case jpiStartsWith:
     954            8 :             return "starts with";
     955            0 :         case jpiLikeRegex:
     956            0 :             return "like_regex";
     957           52 :         case jpiBigint:
     958           52 :             return "bigint";
     959           48 :         case jpiBoolean:
     960           48 :             return "boolean";
     961           24 :         case jpiDate:
     962           24 :             return "date";
     963           52 :         case jpiDecimal:
     964           52 :             return "decimal";
     965           52 :         case jpiInteger:
     966           52 :             return "integer";
     967           36 :         case jpiNumber:
     968           36 :             return "number";
     969           12 :         case jpiStringFunc:
     970           12 :             return "string";
     971           28 :         case jpiTime:
     972           28 :             return "time";
     973           28 :         case jpiTimeTz:
     974           28 :             return "time_tz";
     975           28 :         case jpiTimestamp:
     976           28 :             return "timestamp";
     977           28 :         case jpiTimestampTz:
     978           28 :             return "timestamp_tz";
     979           12 :         case jpiStrReplace:
     980           12 :             return "replace";
     981           32 :         case jpiStrLower:
     982           32 :             return "lower";
     983           32 :         case jpiStrUpper:
     984           32 :             return "upper";
     985           32 :         case jpiStrLtrim:
     986           32 :             return "ltrim";
     987            0 :         case jpiStrRtrim:
     988            0 :             return "rtrim";
     989            0 :         case jpiStrBtrim:
     990            0 :             return "btrim";
     991           32 :         case jpiStrInitcap:
     992           32 :             return "initcap";
     993            0 :         case jpiStrSplitPart:
     994            0 :             return "split_part";
     995            0 :         default:
     996            0 :             elog(ERROR, "unrecognized jsonpath item type: %d", type);
     997              :             return NULL;
     998              :     }
     999              : }
    1000              : 
    1001              : static int
    1002         2224 : operationPriority(JsonPathItemType op)
    1003              : {
    1004         2224 :     switch (op)
    1005              :     {
    1006           76 :         case jpiOr:
    1007           76 :             return 0;
    1008           52 :         case jpiAnd:
    1009           52 :             return 1;
    1010          852 :         case jpiEqual:
    1011              :         case jpiNotEqual:
    1012              :         case jpiLess:
    1013              :         case jpiGreater:
    1014              :         case jpiLessOrEqual:
    1015              :         case jpiGreaterOrEqual:
    1016              :         case jpiStartsWith:
    1017          852 :             return 2;
    1018          168 :         case jpiAdd:
    1019              :         case jpiSub:
    1020          168 :             return 3;
    1021           44 :         case jpiMul:
    1022              :         case jpiDiv:
    1023              :         case jpiMod:
    1024           44 :             return 4;
    1025           56 :         case jpiPlus:
    1026              :         case jpiMinus:
    1027           56 :             return 5;
    1028          976 :         default:
    1029          976 :             return 6;
    1030              :     }
    1031              : }
    1032              : 
    1033              : /******************* Support functions for JsonPath *************************/
    1034              : 
    1035              : /*
    1036              :  * Support macros to read stored values
    1037              :  */
    1038              : 
    1039              : #define read_byte(v, b, p) do {         \
    1040              :     (v) = *(uint8*)((b) + (p));         \
    1041              :     (p) += 1;                           \
    1042              : } while(0)                              \
    1043              : 
    1044              : #define read_int32(v, b, p) do {        \
    1045              :     (v) = *(uint32*)((b) + (p));        \
    1046              :     (p) += sizeof(int32);               \
    1047              : } while(0)                              \
    1048              : 
    1049              : #define read_int32_n(v, b, p, n) do {   \
    1050              :     (v) = (void *)((b) + (p));          \
    1051              :     (p) += sizeof(int32) * (n);         \
    1052              : } while(0)                              \
    1053              : 
    1054              : /*
    1055              :  * Read root node and fill root node representation
    1056              :  */
    1057              : void
    1058       135025 : jspInit(JsonPathItem *v, JsonPath *js)
    1059              : {
    1060              :     Assert((js->header & ~JSONPATH_LAX) == JSONPATH_VERSION);
    1061       135025 :     jspInitByBuffer(v, js->data, 0);
    1062       135025 : }
    1063              : 
    1064              : /*
    1065              :  * Read node from buffer and fill its representation
    1066              :  */
    1067              : void
    1068       460848 : jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
    1069              : {
    1070       460848 :     v->base = base + pos;
    1071              : 
    1072       460848 :     read_byte(v->type, base, pos);
    1073       460848 :     pos = INTALIGN((uintptr_t) (base + pos)) - (uintptr_t) base;
    1074       460848 :     read_int32(v->nextPos, base, pos);
    1075              : 
    1076       460848 :     switch (v->type)
    1077              :     {
    1078       169332 :         case jpiNull:
    1079              :         case jpiRoot:
    1080              :         case jpiCurrent:
    1081              :         case jpiAnyArray:
    1082              :         case jpiAnyKey:
    1083              :         case jpiType:
    1084              :         case jpiSize:
    1085              :         case jpiAbs:
    1086              :         case jpiFloor:
    1087              :         case jpiCeiling:
    1088              :         case jpiDouble:
    1089              :         case jpiKeyValue:
    1090              :         case jpiLast:
    1091              :         case jpiBigint:
    1092              :         case jpiBoolean:
    1093              :         case jpiDate:
    1094              :         case jpiInteger:
    1095              :         case jpiNumber:
    1096              :         case jpiStringFunc:
    1097              :         case jpiStrLower:
    1098              :         case jpiStrUpper:
    1099              :         case jpiStrInitcap:
    1100       169332 :             break;
    1101       135911 :         case jpiString:
    1102              :         case jpiKey:
    1103              :         case jpiVariable:
    1104       135911 :             read_int32(v->content.value.datalen, base, pos);
    1105              :             pg_fallthrough;
    1106       151663 :         case jpiNumeric:
    1107              :         case jpiBool:
    1108       151663 :             v->content.value.data = base + pos;
    1109       151663 :             break;
    1110        67734 :         case jpiAnd:
    1111              :         case jpiOr:
    1112              :         case jpiEqual:
    1113              :         case jpiNotEqual:
    1114              :         case jpiLess:
    1115              :         case jpiGreater:
    1116              :         case jpiLessOrEqual:
    1117              :         case jpiGreaterOrEqual:
    1118              :         case jpiAdd:
    1119              :         case jpiSub:
    1120              :         case jpiMul:
    1121              :         case jpiDiv:
    1122              :         case jpiMod:
    1123              :         case jpiStartsWith:
    1124              :         case jpiDecimal:
    1125              :         case jpiStrReplace:
    1126              :         case jpiStrSplitPart:
    1127        67734 :             read_int32(v->content.args.left, base, pos);
    1128        67734 :             read_int32(v->content.args.right, base, pos);
    1129        67734 :             break;
    1130        71141 :         case jpiNot:
    1131              :         case jpiIsUnknown:
    1132              :         case jpiExists:
    1133              :         case jpiPlus:
    1134              :         case jpiMinus:
    1135              :         case jpiFilter:
    1136              :         case jpiDatetime:
    1137              :         case jpiTime:
    1138              :         case jpiTimeTz:
    1139              :         case jpiTimestamp:
    1140              :         case jpiTimestampTz:
    1141              :         case jpiStrLtrim:
    1142              :         case jpiStrRtrim:
    1143              :         case jpiStrBtrim:
    1144        71141 :             read_int32(v->content.arg, base, pos);
    1145        71141 :             break;
    1146          421 :         case jpiIndexArray:
    1147          421 :             read_int32(v->content.array.nelems, base, pos);
    1148          421 :             read_int32_n(v->content.array.elems, base, pos,
    1149              :                          v->content.array.nelems * 2);
    1150          421 :             break;
    1151          261 :         case jpiAny:
    1152          261 :             read_int32(v->content.anybounds.first, base, pos);
    1153          261 :             read_int32(v->content.anybounds.last, base, pos);
    1154          261 :             break;
    1155          296 :         case jpiLikeRegex:
    1156          296 :             read_int32(v->content.like_regex.flags, base, pos);
    1157          296 :             read_int32(v->content.like_regex.expr, base, pos);
    1158          296 :             read_int32(v->content.like_regex.patternlen, base, pos);
    1159          296 :             v->content.like_regex.pattern = base + pos;
    1160          296 :             break;
    1161            0 :         default:
    1162            0 :             elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
    1163              :     }
    1164       460848 : }
    1165              : 
    1166              : void
    1167        70919 : jspGetArg(JsonPathItem *v, JsonPathItem *a)
    1168              : {
    1169              :     Assert(v->type == jpiNot ||
    1170              :            v->type == jpiIsUnknown ||
    1171              :            v->type == jpiPlus ||
    1172              :            v->type == jpiMinus ||
    1173              :            v->type == jpiFilter ||
    1174              :            v->type == jpiExists ||
    1175              :            v->type == jpiDatetime ||
    1176              :            v->type == jpiTime ||
    1177              :            v->type == jpiTimeTz ||
    1178              :            v->type == jpiTimestamp ||
    1179              :            v->type == jpiTimestampTz ||
    1180              :            v->type == jpiStrLtrim ||
    1181              :            v->type == jpiStrRtrim ||
    1182              :            v->type == jpiStrBtrim);
    1183              : 
    1184        70919 :     jspInitByBuffer(a, v->base, v->content.arg);
    1185        70919 : }
    1186              : 
    1187              : bool
    1188       303695 : jspGetNext(JsonPathItem *v, JsonPathItem *a)
    1189              : {
    1190       303695 :     if (jspHasNext(v))
    1191              :     {
    1192              :         Assert(v->type == jpiNull ||
    1193              :                v->type == jpiString ||
    1194              :                v->type == jpiNumeric ||
    1195              :                v->type == jpiBool ||
    1196              :                v->type == jpiAnd ||
    1197              :                v->type == jpiOr ||
    1198              :                v->type == jpiNot ||
    1199              :                v->type == jpiIsUnknown ||
    1200              :                v->type == jpiEqual ||
    1201              :                v->type == jpiNotEqual ||
    1202              :                v->type == jpiLess ||
    1203              :                v->type == jpiGreater ||
    1204              :                v->type == jpiLessOrEqual ||
    1205              :                v->type == jpiGreaterOrEqual ||
    1206              :                v->type == jpiAdd ||
    1207              :                v->type == jpiSub ||
    1208              :                v->type == jpiMul ||
    1209              :                v->type == jpiDiv ||
    1210              :                v->type == jpiMod ||
    1211              :                v->type == jpiPlus ||
    1212              :                v->type == jpiMinus ||
    1213              :                v->type == jpiAnyArray ||
    1214              :                v->type == jpiAnyKey ||
    1215              :                v->type == jpiIndexArray ||
    1216              :                v->type == jpiAny ||
    1217              :                v->type == jpiKey ||
    1218              :                v->type == jpiCurrent ||
    1219              :                v->type == jpiRoot ||
    1220              :                v->type == jpiVariable ||
    1221              :                v->type == jpiFilter ||
    1222              :                v->type == jpiExists ||
    1223              :                v->type == jpiType ||
    1224              :                v->type == jpiSize ||
    1225              :                v->type == jpiAbs ||
    1226              :                v->type == jpiFloor ||
    1227              :                v->type == jpiCeiling ||
    1228              :                v->type == jpiDouble ||
    1229              :                v->type == jpiDatetime ||
    1230              :                v->type == jpiKeyValue ||
    1231              :                v->type == jpiLast ||
    1232              :                v->type == jpiStartsWith ||
    1233              :                v->type == jpiLikeRegex ||
    1234              :                v->type == jpiBigint ||
    1235              :                v->type == jpiBoolean ||
    1236              :                v->type == jpiDate ||
    1237              :                v->type == jpiDecimal ||
    1238              :                v->type == jpiInteger ||
    1239              :                v->type == jpiNumber ||
    1240              :                v->type == jpiStringFunc ||
    1241              :                v->type == jpiTime ||
    1242              :                v->type == jpiTimeTz ||
    1243              :                v->type == jpiTimestamp ||
    1244              :                v->type == jpiTimestampTz ||
    1245              :                v->type == jpiStrReplace ||
    1246              :                v->type == jpiStrLower ||
    1247              :                v->type == jpiStrUpper ||
    1248              :                v->type == jpiStrLtrim ||
    1249              :                v->type == jpiStrRtrim ||
    1250              :                v->type == jpiStrBtrim ||
    1251              :                v->type == jpiStrInitcap ||
    1252              :                v->type == jpiStrSplitPart);
    1253              : 
    1254       135814 :         if (a)
    1255       135814 :             jspInitByBuffer(a, v->base, v->nextPos);
    1256       135814 :         return true;
    1257              :     }
    1258              : 
    1259       167881 :     return false;
    1260              : }
    1261              : 
    1262              : void
    1263        67598 : jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
    1264              : {
    1265              :     Assert(v->type == jpiAnd ||
    1266              :            v->type == jpiOr ||
    1267              :            v->type == jpiEqual ||
    1268              :            v->type == jpiNotEqual ||
    1269              :            v->type == jpiLess ||
    1270              :            v->type == jpiGreater ||
    1271              :            v->type == jpiLessOrEqual ||
    1272              :            v->type == jpiGreaterOrEqual ||
    1273              :            v->type == jpiAdd ||
    1274              :            v->type == jpiSub ||
    1275              :            v->type == jpiMul ||
    1276              :            v->type == jpiDiv ||
    1277              :            v->type == jpiMod ||
    1278              :            v->type == jpiStartsWith ||
    1279              :            v->type == jpiDecimal ||
    1280              :            v->type == jpiStrReplace ||
    1281              :            v->type == jpiStrSplitPart);
    1282              : 
    1283        67598 :     jspInitByBuffer(a, v->base, v->content.args.left);
    1284        67598 : }
    1285              : 
    1286              : void
    1287        50718 : jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
    1288              : {
    1289              :     Assert(v->type == jpiAnd ||
    1290              :            v->type == jpiOr ||
    1291              :            v->type == jpiEqual ||
    1292              :            v->type == jpiNotEqual ||
    1293              :            v->type == jpiLess ||
    1294              :            v->type == jpiGreater ||
    1295              :            v->type == jpiLessOrEqual ||
    1296              :            v->type == jpiGreaterOrEqual ||
    1297              :            v->type == jpiAdd ||
    1298              :            v->type == jpiSub ||
    1299              :            v->type == jpiMul ||
    1300              :            v->type == jpiDiv ||
    1301              :            v->type == jpiMod ||
    1302              :            v->type == jpiStartsWith ||
    1303              :            v->type == jpiDecimal ||
    1304              :            v->type == jpiStrReplace ||
    1305              :            v->type == jpiStrSplitPart);
    1306              : 
    1307        50718 :     jspInitByBuffer(a, v->base, v->content.args.right);
    1308        50718 : }
    1309              : 
    1310              : bool
    1311          958 : jspGetBool(JsonPathItem *v)
    1312              : {
    1313              :     Assert(v->type == jpiBool);
    1314              : 
    1315          958 :     return (bool) *v->content.value.data;
    1316              : }
    1317              : 
    1318              : Numeric
    1319        14644 : jspGetNumeric(JsonPathItem *v)
    1320              : {
    1321              :     Assert(v->type == jpiNumeric);
    1322              : 
    1323        14644 :     return (Numeric) v->content.value.data;
    1324              : }
    1325              : 
    1326              : char *
    1327       135298 : jspGetString(JsonPathItem *v, int32 *len)
    1328              : {
    1329              :     Assert(v->type == jpiKey ||
    1330              :            v->type == jpiString ||
    1331              :            v->type == jpiVariable);
    1332              : 
    1333       135298 :     if (len)
    1334       135142 :         *len = v->content.value.datalen;
    1335       135298 :     return v->content.value.data;
    1336              : }
    1337              : 
    1338              : bool
    1339          445 : jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to,
    1340              :                      int i)
    1341              : {
    1342              :     Assert(v->type == jpiIndexArray);
    1343              : 
    1344          445 :     jspInitByBuffer(from, v->base, v->content.array.elems[i].from);
    1345              : 
    1346          445 :     if (!v->content.array.elems[i].to)
    1347          412 :         return false;
    1348              : 
    1349           33 :     jspInitByBuffer(to, v->base, v->content.array.elems[i].to);
    1350              : 
    1351           33 :     return true;
    1352              : }
    1353              : 
    1354              : /* SQL/JSON datatype status: */
    1355              : enum JsonPathDatatypeStatus
    1356              : {
    1357              :     jpdsNonDateTime,            /* null, bool, numeric, string, array, object */
    1358              :     jpdsUnknownDateTime,        /* unknown datetime type */
    1359              :     jpdsDateTimeZoned,          /* timetz, timestamptz */
    1360              :     jpdsDateTimeNonZoned,       /* time, timestamp, date */
    1361              : };
    1362              : 
    1363              : /* Context for jspIsMutableWalker() */
    1364              : struct JsonPathMutableContext
    1365              : {
    1366              :     List       *varnames;       /* list of variable names */
    1367              :     List       *varexprs;       /* list of variable expressions */
    1368              :     enum JsonPathDatatypeStatus current;    /* status of @ item */
    1369              :     bool        lax;            /* jsonpath is lax or strict */
    1370              :     bool        mutable;        /* resulting mutability status */
    1371              : };
    1372              : 
    1373              : static enum JsonPathDatatypeStatus jspIsMutableWalker(JsonPathItem *jpi,
    1374              :                                                       struct JsonPathMutableContext *cxt);
    1375              : 
    1376              : /*
    1377              :  * Function to check whether jsonpath expression is mutable to be used in the
    1378              :  * planner function contain_mutable_functions().
    1379              :  */
    1380              : bool
    1381          188 : jspIsMutable(JsonPath *path, List *varnames, List *varexprs)
    1382              : {
    1383              :     struct JsonPathMutableContext cxt;
    1384              :     JsonPathItem jpi;
    1385              : 
    1386          188 :     cxt.varnames = varnames;
    1387          188 :     cxt.varexprs = varexprs;
    1388          188 :     cxt.current = jpdsNonDateTime;
    1389          188 :     cxt.lax = (path->header & JSONPATH_LAX) != 0;
    1390          188 :     cxt.mutable = false;
    1391              : 
    1392          188 :     jspInit(&jpi, path);
    1393          188 :     (void) jspIsMutableWalker(&jpi, &cxt);
    1394              : 
    1395          188 :     return cxt.mutable;
    1396              : }
    1397              : 
    1398              : /*
    1399              :  * Recursive walker for jspIsMutable()
    1400              :  */
    1401              : static enum JsonPathDatatypeStatus
    1402          556 : jspIsMutableWalker(JsonPathItem *jpi, struct JsonPathMutableContext *cxt)
    1403              : {
    1404              :     JsonPathItem next;
    1405          556 :     enum JsonPathDatatypeStatus status = jpdsNonDateTime;
    1406              : 
    1407          956 :     while (!cxt->mutable)
    1408              :     {
    1409              :         JsonPathItem arg;
    1410              :         enum JsonPathDatatypeStatus leftStatus;
    1411              :         enum JsonPathDatatypeStatus rightStatus;
    1412              : 
    1413          892 :         switch (jpi->type)
    1414              :         {
    1415          224 :             case jpiRoot:
    1416              :                 Assert(status == jpdsNonDateTime);
    1417          224 :                 break;
    1418              : 
    1419           96 :             case jpiCurrent:
    1420              :                 Assert(status == jpdsNonDateTime);
    1421           96 :                 status = cxt->current;
    1422           96 :                 break;
    1423              : 
    1424           96 :             case jpiFilter:
    1425              :                 {
    1426           96 :                     enum JsonPathDatatypeStatus prevStatus = cxt->current;
    1427              : 
    1428           96 :                     cxt->current = status;
    1429           96 :                     jspGetArg(jpi, &arg);
    1430           96 :                     jspIsMutableWalker(&arg, cxt);
    1431              : 
    1432           96 :                     cxt->current = prevStatus;
    1433           96 :                     break;
    1434              :                 }
    1435              : 
    1436           36 :             case jpiVariable:
    1437              :                 {
    1438              :                     int32       len;
    1439           36 :                     const char *name = jspGetString(jpi, &len);
    1440              :                     ListCell   *lc1;
    1441              :                     ListCell   *lc2;
    1442              : 
    1443              :                     Assert(status == jpdsNonDateTime);
    1444              : 
    1445           40 :                     forboth(lc1, cxt->varnames, lc2, cxt->varexprs)
    1446              :                     {
    1447           36 :                         String     *varname = lfirst_node(String, lc1);
    1448           36 :                         Node       *varexpr = lfirst(lc2);
    1449              : 
    1450           36 :                         if (strncmp(varname->sval, name, len))
    1451            4 :                             continue;
    1452              : 
    1453           32 :                         switch (exprType(varexpr))
    1454              :                         {
    1455           20 :                             case DATEOID:
    1456              :                             case TIMEOID:
    1457              :                             case TIMESTAMPOID:
    1458           20 :                                 status = jpdsDateTimeNonZoned;
    1459           20 :                                 break;
    1460              : 
    1461            8 :                             case TIMETZOID:
    1462              :                             case TIMESTAMPTZOID:
    1463            8 :                                 status = jpdsDateTimeZoned;
    1464            8 :                                 break;
    1465              : 
    1466            4 :                             default:
    1467            4 :                                 status = jpdsNonDateTime;
    1468            4 :                                 break;
    1469              :                         }
    1470              : 
    1471           32 :                         break;
    1472              :                     }
    1473           36 :                     break;
    1474              :                 }
    1475              : 
    1476          120 :             case jpiEqual:
    1477              :             case jpiNotEqual:
    1478              :             case jpiLess:
    1479              :             case jpiGreater:
    1480              :             case jpiLessOrEqual:
    1481              :             case jpiGreaterOrEqual:
    1482              :                 Assert(status == jpdsNonDateTime);
    1483          120 :                 jspGetLeftArg(jpi, &arg);
    1484          120 :                 leftStatus = jspIsMutableWalker(&arg, cxt);
    1485              : 
    1486          120 :                 jspGetRightArg(jpi, &arg);
    1487          120 :                 rightStatus = jspIsMutableWalker(&arg, cxt);
    1488              : 
    1489              :                 /*
    1490              :                  * Comparison of datetime type with different timezone status
    1491              :                  * is mutable.
    1492              :                  */
    1493          120 :                 if (leftStatus != jpdsNonDateTime &&
    1494           48 :                     rightStatus != jpdsNonDateTime &&
    1495           24 :                     (leftStatus == jpdsUnknownDateTime ||
    1496           24 :                      rightStatus == jpdsUnknownDateTime ||
    1497              :                      leftStatus != rightStatus))
    1498           28 :                     cxt->mutable = true;
    1499          120 :                 break;
    1500              : 
    1501            0 :             case jpiNot:
    1502              :             case jpiIsUnknown:
    1503              :             case jpiExists:
    1504              :             case jpiPlus:
    1505              :             case jpiMinus:
    1506              :                 Assert(status == jpdsNonDateTime);
    1507            0 :                 jspGetArg(jpi, &arg);
    1508            0 :                 jspIsMutableWalker(&arg, cxt);
    1509            0 :                 break;
    1510              : 
    1511            0 :             case jpiAnd:
    1512              :             case jpiOr:
    1513              :             case jpiAdd:
    1514              :             case jpiSub:
    1515              :             case jpiMul:
    1516              :             case jpiDiv:
    1517              :             case jpiMod:
    1518              :             case jpiStartsWith:
    1519              :                 Assert(status == jpdsNonDateTime);
    1520            0 :                 jspGetLeftArg(jpi, &arg);
    1521            0 :                 jspIsMutableWalker(&arg, cxt);
    1522            0 :                 jspGetRightArg(jpi, &arg);
    1523            0 :                 jspIsMutableWalker(&arg, cxt);
    1524            0 :                 break;
    1525              : 
    1526           16 :             case jpiIndexArray:
    1527           44 :                 for (int i = 0; i < jpi->content.array.nelems; i++)
    1528              :                 {
    1529              :                     JsonPathItem from;
    1530              :                     JsonPathItem to;
    1531              : 
    1532           28 :                     if (jspGetArraySubscript(jpi, &from, &to, i))
    1533            4 :                         jspIsMutableWalker(&to, cxt);
    1534              : 
    1535           28 :                     jspIsMutableWalker(&from, cxt);
    1536              :                 }
    1537              :                 pg_fallthrough;
    1538              : 
    1539              :             case jpiAnyArray:
    1540           16 :                 if (!cxt->lax)
    1541            0 :                     status = jpdsNonDateTime;
    1542           16 :                 break;
    1543              : 
    1544            0 :             case jpiAny:
    1545            0 :                 if (jpi->content.anybounds.first > 0)
    1546            0 :                     status = jpdsNonDateTime;
    1547            0 :                 break;
    1548              : 
    1549           84 :             case jpiDatetime:
    1550           84 :                 if (jpi->content.arg)
    1551              :                 {
    1552              :                     char       *template;
    1553              : 
    1554           44 :                     jspGetArg(jpi, &arg);
    1555           44 :                     if (arg.type != jpiString)
    1556              :                     {
    1557            0 :                         status = jpdsNonDateTime;
    1558            0 :                         break;  /* there will be runtime error */
    1559              :                     }
    1560              : 
    1561           44 :                     template = jspGetString(&arg, NULL);
    1562           44 :                     if (datetime_format_has_tz(template))
    1563           24 :                         status = jpdsDateTimeZoned;
    1564              :                     else
    1565           20 :                         status = jpdsDateTimeNonZoned;
    1566              :                 }
    1567              :                 else
    1568              :                 {
    1569           40 :                     status = jpdsUnknownDateTime;
    1570              :                 }
    1571           84 :                 break;
    1572              : 
    1573            0 :             case jpiLikeRegex:
    1574              :                 Assert(status == jpdsNonDateTime);
    1575            0 :                 jspInitByBuffer(&arg, jpi->base, jpi->content.like_regex.expr);
    1576            0 :                 jspIsMutableWalker(&arg, cxt);
    1577            0 :                 break;
    1578              : 
    1579              :                 /* literals */
    1580           16 :             case jpiNull:
    1581              :             case jpiString:
    1582              :             case jpiNumeric:
    1583              :             case jpiBool:
    1584           16 :                 break;
    1585              :                 /* accessors */
    1586          124 :             case jpiKey:
    1587              :             case jpiAnyKey:
    1588              :                 /* special items */
    1589              :             case jpiSubscript:
    1590              :             case jpiLast:
    1591              :                 /* item methods */
    1592              :             case jpiType:
    1593              :             case jpiSize:
    1594              :             case jpiAbs:
    1595              :             case jpiFloor:
    1596              :             case jpiCeiling:
    1597              :             case jpiDouble:
    1598              :             case jpiKeyValue:
    1599              :             case jpiBigint:
    1600              :             case jpiBoolean:
    1601              :             case jpiDecimal:
    1602              :             case jpiInteger:
    1603              :             case jpiNumber:
    1604              :             case jpiStringFunc:
    1605              :             case jpiStrReplace:
    1606              :             case jpiStrLower:
    1607              :             case jpiStrUpper:
    1608              :             case jpiStrLtrim:
    1609              :             case jpiStrRtrim:
    1610              :             case jpiStrBtrim:
    1611              :             case jpiStrInitcap:
    1612              :             case jpiStrSplitPart:
    1613          124 :                 status = jpdsNonDateTime;
    1614          124 :                 break;
    1615              : 
    1616           60 :             case jpiTime:
    1617              :             case jpiDate:
    1618              :             case jpiTimestamp:
    1619           60 :                 status = jpdsDateTimeNonZoned;
    1620           60 :                 cxt->mutable = true;
    1621           60 :                 break;
    1622              : 
    1623           20 :             case jpiTimeTz:
    1624              :             case jpiTimestampTz:
    1625           20 :                 status = jpdsDateTimeNonZoned;
    1626           20 :                 cxt->mutable = true;
    1627           20 :                 break;
    1628              : 
    1629              :         }
    1630              : 
    1631          892 :         if (!jspGetNext(jpi, &next))
    1632          492 :             break;
    1633              : 
    1634          400 :         jpi = &next;
    1635              :     }
    1636              : 
    1637          556 :     return status;
    1638              : }
        

Generated by: LCOV version 2.0-1