LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 632 690 91.6 %
Date: 2024-11-21 08:14:44 Functions: 22 24 91.7 %
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-2024, 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       10700 : jsonpath_in(PG_FUNCTION_ARGS)
      99             : {
     100       10700 :     char       *in = PG_GETARG_CSTRING(0);
     101       10700 :     int         len = strlen(in);
     102             : 
     103       10700 :     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        1886 : jsonpath_out(PG_FUNCTION_ARGS)
     135             : {
     136        1886 :     JsonPath   *in = PG_GETARG_JSONPATH_P(0);
     137             : 
     138        1886 :     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       10700 : jsonPathFromCstring(char *in, int len, struct Node *escontext)
     174             : {
     175       10700 :     JsonPathParseResult *jsonpath = parsejsonpath(in, len, escontext);
     176             :     JsonPath   *res;
     177             :     StringInfoData buf;
     178             : 
     179       10334 :     if (SOFT_ERROR_OCCURRED(escontext))
     180          42 :         return (Datum) 0;
     181             : 
     182       10292 :     if (!jsonpath)
     183           6 :         ereturn(escontext, (Datum) 0,
     184             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     185             :                  errmsg("invalid input syntax for type %s: \"%s\"", "jsonpath",
     186             :                         in)));
     187             : 
     188       10286 :     initStringInfo(&buf);
     189       10286 :     enlargeStringInfo(&buf, 4 * len /* estimation */ );
     190             : 
     191       10286 :     appendStringInfoSpaces(&buf, JSONPATH_HDRSZ);
     192             : 
     193       10286 :     if (!flattenJsonPathParseItem(&buf, NULL, escontext,
     194             :                                   jsonpath->expr, 0, false))
     195          12 :         return (Datum) 0;
     196             : 
     197       10256 :     res = (JsonPath *) buf.data;
     198       10256 :     SET_VARSIZE(res, buf.len);
     199       10256 :     res->header = JSONPATH_VERSION;
     200       10256 :     if (jsonpath->lax)
     201        9608 :         res->header |= JSONPATH_LAX;
     202             : 
     203       10256 :     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        1886 : jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
     214             : {
     215             :     StringInfoData buf;
     216             :     JsonPathItem v;
     217             : 
     218        1886 :     if (!out)
     219             :     {
     220        1886 :         out = &buf;
     221        1886 :         initStringInfo(out);
     222             :     }
     223        1886 :     enlargeStringInfo(out, estimated_len);
     224             : 
     225        1886 :     if (!(in->header & JSONPATH_LAX))
     226          18 :         appendStringInfoString(out, "strict ");
     227             : 
     228        1886 :     jspInit(&v, in);
     229        1886 :     printJsonPathItem(out, &v, false, true);
     230             : 
     231        1886 :     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       35084 : 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       35084 :     int32       pos = buf->len - JSONPATH_HDRSZ;
     245             :     int32       chld;
     246             :     int32       next;
     247       35084 :     int         argNestingLevel = 0;
     248             : 
     249       35084 :     check_stack_depth();
     250       35084 :     CHECK_FOR_INTERRUPTS();
     251             : 
     252       35084 :     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       35084 :     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       35084 :     next = reserveSpaceForItemPointer(buf);
     266             : 
     267       35084 :     switch (item->type)
     268             :     {
     269        5936 :         case jpiString:
     270             :         case jpiVariable:
     271             :         case jpiKey:
     272        5936 :             appendBinaryStringInfo(buf, &item->value.string.len,
     273             :                                    sizeof(item->value.string.len));
     274        5936 :             appendBinaryStringInfo(buf, item->value.string.val,
     275        5936 :                                    item->value.string.len);
     276        5936 :             appendStringInfoChar(buf, '\0');
     277        5936 :             break;
     278        2538 :         case jpiNumeric:
     279        2538 :             appendBinaryStringInfo(buf, item->value.numeric,
     280        2538 :                                    VARSIZE(item->value.numeric));
     281        2538 :             break;
     282         180 :         case jpiBool:
     283         180 :             appendBinaryStringInfo(buf, &item->value.boolean,
     284             :                                    sizeof(item->value.boolean));
     285         180 :             break;
     286        3660 :         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             :             {
     302             :                 /*
     303             :                  * First, reserve place for left/right arg's positions, then
     304             :                  * record both args and sets actual position in reserved
     305             :                  * places.
     306             :                  */
     307        3660 :                 int32       left = reserveSpaceForItemPointer(buf);
     308        3660 :                 int32       right = reserveSpaceForItemPointer(buf);
     309             : 
     310        3660 :                 if (!item->value.args.left)
     311         168 :                     chld = pos;
     312        3492 :                 else if (!flattenJsonPathParseItem(buf, &chld, escontext,
     313             :                                                    item->value.args.left,
     314             :                                                    nestingLevel + argNestingLevel,
     315             :                                                    insideArraySubscript))
     316          12 :                     return false;
     317        3636 :                 *(int32 *) (buf->data + left) = chld - pos;
     318             : 
     319        3636 :                 if (!item->value.args.right)
     320         168 :                     chld = pos;
     321        3468 :                 else if (!flattenJsonPathParseItem(buf, &chld, escontext,
     322             :                                                    item->value.args.right,
     323             :                                                    nestingLevel + argNestingLevel,
     324             :                                                    insideArraySubscript))
     325           0 :                     return false;
     326        3636 :                 *(int32 *) (buf->data + right) = chld - pos;
     327             :             }
     328        3636 :             break;
     329         120 :         case jpiLikeRegex:
     330             :             {
     331             :                 int32       offs;
     332             : 
     333         120 :                 appendBinaryStringInfo(buf,
     334         120 :                                        &item->value.like_regex.flags,
     335             :                                        sizeof(item->value.like_regex.flags));
     336         120 :                 offs = reserveSpaceForItemPointer(buf);
     337         120 :                 appendBinaryStringInfo(buf,
     338         120 :                                        &item->value.like_regex.patternlen,
     339             :                                        sizeof(item->value.like_regex.patternlen));
     340         120 :                 appendBinaryStringInfo(buf, item->value.like_regex.pattern,
     341         120 :                                        item->value.like_regex.patternlen);
     342         120 :                 appendStringInfoChar(buf, '\0');
     343             : 
     344         120 :                 if (!flattenJsonPathParseItem(buf, &chld, escontext,
     345             :                                               item->value.like_regex.expr,
     346             :                                               nestingLevel,
     347             :                                               insideArraySubscript))
     348           0 :                     return false;
     349         120 :                 *(int32 *) (buf->data + offs) = chld - pos;
     350             :             }
     351         120 :             break;
     352        2280 :         case jpiFilter:
     353        2280 :             argNestingLevel++;
     354             :             /* FALLTHROUGH */
     355        5130 :         case jpiIsUnknown:
     356             :         case jpiNot:
     357             :         case jpiPlus:
     358             :         case jpiMinus:
     359             :         case jpiExists:
     360             :         case jpiDatetime:
     361             :         case jpiTime:
     362             :         case jpiTimeTz:
     363             :         case jpiTimestamp:
     364             :         case jpiTimestampTz:
     365             :             {
     366        5130 :                 int32       arg = reserveSpaceForItemPointer(buf);
     367             : 
     368        5130 :                 if (!item->value.arg)
     369        1536 :                     chld = pos;
     370        3594 :                 else if (!flattenJsonPathParseItem(buf, &chld, escontext,
     371             :                                                    item->value.arg,
     372             :                                                    nestingLevel + argNestingLevel,
     373             :                                                    insideArraySubscript))
     374           0 :                     return false;
     375        5124 :                 *(int32 *) (buf->data + arg) = chld - pos;
     376             :             }
     377        5124 :             break;
     378         114 :         case jpiNull:
     379         114 :             break;
     380        9950 :         case jpiRoot:
     381        9950 :             break;
     382        2182 :         case jpiAnyArray:
     383             :         case jpiAnyKey:
     384        2182 :             break;
     385        2568 :         case jpiCurrent:
     386        2568 :             if (nestingLevel <= 0)
     387          18 :                 ereturn(escontext, false,
     388             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     389             :                          errmsg("@ is not allowed in root expressions")));
     390        2550 :             break;
     391          90 :         case jpiLast:
     392          90 :             if (!insideArraySubscript)
     393          12 :                 ereturn(escontext, false,
     394             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     395             :                          errmsg("LAST is allowed only in array subscripts")));
     396          78 :             break;
     397         510 :         case jpiIndexArray:
     398             :             {
     399         510 :                 int32       nelems = item->value.array.nelems;
     400             :                 int         offset;
     401             :                 int         i;
     402             : 
     403         510 :                 appendBinaryStringInfo(buf, &nelems, sizeof(nelems));
     404             : 
     405         510 :                 offset = buf->len;
     406             : 
     407         510 :                 appendStringInfoSpaces(buf, sizeof(int32) * 2 * nelems);
     408             : 
     409        1068 :                 for (i = 0; i < nelems; i++)
     410             :                 {
     411             :                     int32      *ppos;
     412             :                     int32       topos;
     413             :                     int32       frompos;
     414             : 
     415         558 :                     if (!flattenJsonPathParseItem(buf, &frompos, escontext,
     416         558 :                                                   item->value.array.elems[i].from,
     417             :                                                   nestingLevel, true))
     418           0 :                         return false;
     419         558 :                     frompos -= pos;
     420             : 
     421         558 :                     if (item->value.array.elems[i].to)
     422             :                     {
     423          48 :                         if (!flattenJsonPathParseItem(buf, &topos, escontext,
     424          48 :                                                       item->value.array.elems[i].to,
     425             :                                                       nestingLevel, true))
     426           0 :                             return false;
     427          48 :                         topos -= pos;
     428             :                     }
     429             :                     else
     430         510 :                         topos = 0;
     431             : 
     432         558 :                     ppos = (int32 *) &buf->data[offset + i * 2 * sizeof(int32)];
     433             : 
     434         558 :                     ppos[0] = frompos;
     435         558 :                     ppos[1] = topos;
     436             :                 }
     437             :             }
     438         510 :             break;
     439         354 :         case jpiAny:
     440         354 :             appendBinaryStringInfo(buf,
     441         354 :                                    &item->value.anybounds.first,
     442             :                                    sizeof(item->value.anybounds.first));
     443         354 :             appendBinaryStringInfo(buf,
     444         354 :                                    &item->value.anybounds.last,
     445             :                                    sizeof(item->value.anybounds.last));
     446         354 :             break;
     447        1752 :         case jpiType:
     448             :         case jpiSize:
     449             :         case jpiAbs:
     450             :         case jpiFloor:
     451             :         case jpiCeiling:
     452             :         case jpiDouble:
     453             :         case jpiKeyValue:
     454             :         case jpiBigint:
     455             :         case jpiBoolean:
     456             :         case jpiDate:
     457             :         case jpiInteger:
     458             :         case jpiNumber:
     459             :         case jpiStringFunc:
     460        1752 :             break;
     461           0 :         default:
     462           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
     463             :     }
     464             : 
     465       35024 :     if (item->next)
     466             :     {
     467       13518 :         if (!flattenJsonPathParseItem(buf, &chld, escontext,
     468             :                                       item->next, nestingLevel,
     469             :                                       insideArraySubscript))
     470           0 :             return false;
     471       13512 :         chld -= pos;
     472       13512 :         *(int32 *) (buf->data + next) = chld;
     473             :     }
     474             : 
     475       35018 :     if (result)
     476       24762 :         *result = pos;
     477       35018 :     return true;
     478             : }
     479             : 
     480             : /*
     481             :  * Align StringInfo to int by adding zero padding bytes
     482             :  */
     483             : static void
     484       35084 : alignStringInfoInt(StringInfo buf)
     485             : {
     486       35084 :     switch (INTALIGN(buf->len) - buf->len)
     487             :     {
     488       31916 :         case 3:
     489       31916 :             appendStringInfoCharMacro(buf, 0);
     490             :             /* FALLTHROUGH */
     491             :         case 2:
     492       32324 :             appendStringInfoCharMacro(buf, 0);
     493             :             /* FALLTHROUGH */
     494             :         case 1:
     495       34634 :             appendStringInfoCharMacro(buf, 0);
     496             :             /* FALLTHROUGH */
     497             :         default:
     498       35084 :             break;
     499             :     }
     500       35084 : }
     501             : 
     502             : /*
     503             :  * Reserve space for int32 JsonPathItem pointer.  Now zero pointer is written,
     504             :  * actual value will be recorded at '(int32 *) &buf->data[pos]' later.
     505             :  */
     506             : static int32
     507       47654 : reserveSpaceForItemPointer(StringInfo buf)
     508             : {
     509       47654 :     int32       pos = buf->len;
     510       47654 :     int32       ptr = 0;
     511             : 
     512       47654 :     appendBinaryStringInfo(buf, &ptr, sizeof(ptr));
     513             : 
     514       47654 :     return pos;
     515             : }
     516             : 
     517             : /*
     518             :  * Prints text representation of given jsonpath item and all its children.
     519             :  */
     520             : static void
     521        6656 : printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
     522             :                   bool printBracketes)
     523             : {
     524             :     JsonPathItem elem;
     525             :     int         i;
     526             :     int32       len;
     527             :     char       *str;
     528             : 
     529        6656 :     check_stack_depth();
     530        6656 :     CHECK_FOR_INTERRUPTS();
     531             : 
     532        6656 :     switch (v->type)
     533             :     {
     534          42 :         case jpiNull:
     535          42 :             appendStringInfoString(buf, "null");
     536          42 :             break;
     537          84 :         case jpiString:
     538          84 :             str = jspGetString(v, &len);
     539          84 :             escape_json_with_len(buf, str, len);
     540          84 :             break;
     541         980 :         case jpiNumeric:
     542         980 :             if (jspHasNext(v))
     543          84 :                 appendStringInfoChar(buf, '(');
     544         980 :             appendStringInfoString(buf,
     545         980 :                                    DatumGetCString(DirectFunctionCall1(numeric_out,
     546             :                                                                        NumericGetDatum(jspGetNumeric(v)))));
     547         980 :             if (jspHasNext(v))
     548          84 :                 appendStringInfoChar(buf, ')');
     549         980 :             break;
     550          12 :         case jpiBool:
     551          12 :             if (jspGetBool(v))
     552           6 :                 appendStringInfoString(buf, "true");
     553             :             else
     554           6 :                 appendStringInfoString(buf, "false");
     555          12 :             break;
     556         788 :         case jpiAnd:
     557             :         case jpiOr:
     558             :         case jpiEqual:
     559             :         case jpiNotEqual:
     560             :         case jpiLess:
     561             :         case jpiGreater:
     562             :         case jpiLessOrEqual:
     563             :         case jpiGreaterOrEqual:
     564             :         case jpiAdd:
     565             :         case jpiSub:
     566             :         case jpiMul:
     567             :         case jpiDiv:
     568             :         case jpiMod:
     569             :         case jpiStartsWith:
     570         788 :             if (printBracketes)
     571         114 :                 appendStringInfoChar(buf, '(');
     572         788 :             jspGetLeftArg(v, &elem);
     573         788 :             printJsonPathItem(buf, &elem, false,
     574         788 :                               operationPriority(elem.type) <=
     575         788 :                               operationPriority(v->type));
     576         788 :             appendStringInfoChar(buf, ' ');
     577         788 :             appendStringInfoString(buf, jspOperationName(v->type));
     578         788 :             appendStringInfoChar(buf, ' ');
     579         788 :             jspGetRightArg(v, &elem);
     580         788 :             printJsonPathItem(buf, &elem, false,
     581         788 :                               operationPriority(elem.type) <=
     582         788 :                               operationPriority(v->type));
     583         788 :             if (printBracketes)
     584         114 :                 appendStringInfoChar(buf, ')');
     585         788 :             break;
     586          12 :         case jpiNot:
     587          12 :             appendStringInfoString(buf, "!(");
     588          12 :             jspGetArg(v, &elem);
     589          12 :             printJsonPathItem(buf, &elem, false, false);
     590          12 :             appendStringInfoChar(buf, ')');
     591          12 :             break;
     592           6 :         case jpiIsUnknown:
     593           6 :             appendStringInfoChar(buf, '(');
     594           6 :             jspGetArg(v, &elem);
     595           6 :             printJsonPathItem(buf, &elem, false, false);
     596           6 :             appendStringInfoString(buf, ") is unknown");
     597           6 :             break;
     598          48 :         case jpiPlus:
     599             :         case jpiMinus:
     600          48 :             if (printBracketes)
     601          18 :                 appendStringInfoChar(buf, '(');
     602          48 :             appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
     603          48 :             jspGetArg(v, &elem);
     604          48 :             printJsonPathItem(buf, &elem, false,
     605          48 :                               operationPriority(elem.type) <=
     606          48 :                               operationPriority(v->type));
     607          48 :             if (printBracketes)
     608          18 :                 appendStringInfoChar(buf, ')');
     609          48 :             break;
     610         218 :         case jpiAnyArray:
     611         218 :             appendStringInfoString(buf, "[*]");
     612         218 :             break;
     613          12 :         case jpiAnyKey:
     614          12 :             if (inKey)
     615          12 :                 appendStringInfoChar(buf, '.');
     616          12 :             appendStringInfoChar(buf, '*');
     617          12 :             break;
     618          96 :         case jpiIndexArray:
     619          96 :             appendStringInfoChar(buf, '[');
     620         210 :             for (i = 0; i < v->content.array.nelems; i++)
     621             :             {
     622             :                 JsonPathItem from;
     623             :                 JsonPathItem to;
     624         114 :                 bool        range = jspGetArraySubscript(v, &from, &to, i);
     625             : 
     626         114 :                 if (i)
     627          18 :                     appendStringInfoChar(buf, ',');
     628             : 
     629         114 :                 printJsonPathItem(buf, &from, false, false);
     630             : 
     631         114 :                 if (range)
     632             :                 {
     633          12 :                     appendStringInfoString(buf, " to ");
     634          12 :                     printJsonPathItem(buf, &to, false, false);
     635             :                 }
     636             :             }
     637          96 :             appendStringInfoChar(buf, ']');
     638          96 :             break;
     639          48 :         case jpiAny:
     640          48 :             if (inKey)
     641          48 :                 appendStringInfoChar(buf, '.');
     642             : 
     643          48 :             if (v->content.anybounds.first == 0 &&
     644          12 :                 v->content.anybounds.last == PG_UINT32_MAX)
     645           6 :                 appendStringInfoString(buf, "**");
     646          42 :             else if (v->content.anybounds.first == v->content.anybounds.last)
     647             :             {
     648          18 :                 if (v->content.anybounds.first == PG_UINT32_MAX)
     649           6 :                     appendStringInfoString(buf, "**{last}");
     650             :                 else
     651          12 :                     appendStringInfo(buf, "**{%u}",
     652             :                                      v->content.anybounds.first);
     653             :             }
     654          24 :             else if (v->content.anybounds.first == PG_UINT32_MAX)
     655           6 :                 appendStringInfo(buf, "**{last to %u}",
     656             :                                  v->content.anybounds.last);
     657          18 :             else if (v->content.anybounds.last == PG_UINT32_MAX)
     658           6 :                 appendStringInfo(buf, "**{%u to last}",
     659             :                                  v->content.anybounds.first);
     660             :             else
     661          12 :                 appendStringInfo(buf, "**{%u to %u}",
     662             :                                  v->content.anybounds.first,
     663             :                                  v->content.anybounds.last);
     664          48 :             break;
     665        1280 :         case jpiKey:
     666        1280 :             if (inKey)
     667        1280 :                 appendStringInfoChar(buf, '.');
     668        1280 :             str = jspGetString(v, &len);
     669        1280 :             escape_json_with_len(buf, str, len);
     670        1280 :             break;
     671         602 :         case jpiCurrent:
     672             :             Assert(!inKey);
     673         602 :             appendStringInfoChar(buf, '@');
     674         602 :             break;
     675        1568 :         case jpiRoot:
     676             :             Assert(!inKey);
     677        1568 :             appendStringInfoChar(buf, '$');
     678        1568 :             break;
     679          72 :         case jpiVariable:
     680          72 :             appendStringInfoChar(buf, '$');
     681          72 :             str = jspGetString(v, &len);
     682          72 :             escape_json_with_len(buf, str, len);
     683          72 :             break;
     684         530 :         case jpiFilter:
     685         530 :             appendStringInfoString(buf, "?(");
     686         530 :             jspGetArg(v, &elem);
     687         530 :             printJsonPathItem(buf, &elem, false, false);
     688         530 :             appendStringInfoChar(buf, ')');
     689         530 :             break;
     690          24 :         case jpiExists:
     691          24 :             appendStringInfoString(buf, "exists (");
     692          24 :             jspGetArg(v, &elem);
     693          24 :             printJsonPathItem(buf, &elem, false, false);
     694          24 :             appendStringInfoChar(buf, ')');
     695          24 :             break;
     696          30 :         case jpiType:
     697          30 :             appendStringInfoString(buf, ".type()");
     698          30 :             break;
     699           6 :         case jpiSize:
     700           6 :             appendStringInfoString(buf, ".size()");
     701           6 :             break;
     702           6 :         case jpiAbs:
     703           6 :             appendStringInfoString(buf, ".abs()");
     704           6 :             break;
     705           6 :         case jpiFloor:
     706           6 :             appendStringInfoString(buf, ".floor()");
     707           6 :             break;
     708           6 :         case jpiCeiling:
     709           6 :             appendStringInfoString(buf, ".ceiling()");
     710           6 :             break;
     711           6 :         case jpiDouble:
     712           6 :             appendStringInfoString(buf, ".double()");
     713           6 :             break;
     714          12 :         case jpiDatetime:
     715          12 :             appendStringInfoString(buf, ".datetime(");
     716          12 :             if (v->content.arg)
     717             :             {
     718           6 :                 jspGetArg(v, &elem);
     719           6 :                 printJsonPathItem(buf, &elem, false, false);
     720             :             }
     721          12 :             appendStringInfoChar(buf, ')');
     722          12 :             break;
     723           6 :         case jpiKeyValue:
     724           6 :             appendStringInfoString(buf, ".keyvalue()");
     725           6 :             break;
     726          12 :         case jpiLast:
     727          12 :             appendStringInfoString(buf, "last");
     728          12 :             break;
     729          48 :         case jpiLikeRegex:
     730          48 :             if (printBracketes)
     731           0 :                 appendStringInfoChar(buf, '(');
     732             : 
     733          48 :             jspInitByBuffer(&elem, v->base, v->content.like_regex.expr);
     734          48 :             printJsonPathItem(buf, &elem, false,
     735          48 :                               operationPriority(elem.type) <=
     736          48 :                               operationPriority(v->type));
     737             : 
     738          48 :             appendStringInfoString(buf, " like_regex ");
     739             : 
     740          48 :             escape_json_with_len(buf,
     741          48 :                                  v->content.like_regex.pattern,
     742             :                                  v->content.like_regex.patternlen);
     743             : 
     744          48 :             if (v->content.like_regex.flags)
     745             :             {
     746          36 :                 appendStringInfoString(buf, " flag \"");
     747             : 
     748          36 :                 if (v->content.like_regex.flags & JSP_REGEX_ICASE)
     749          30 :                     appendStringInfoChar(buf, 'i');
     750          36 :                 if (v->content.like_regex.flags & JSP_REGEX_DOTALL)
     751          18 :                     appendStringInfoChar(buf, 's');
     752          36 :                 if (v->content.like_regex.flags & JSP_REGEX_MLINE)
     753          12 :                     appendStringInfoChar(buf, 'm');
     754          36 :                 if (v->content.like_regex.flags & JSP_REGEX_WSPACE)
     755           6 :                     appendStringInfoChar(buf, 'x');
     756          36 :                 if (v->content.like_regex.flags & JSP_REGEX_QUOTE)
     757          18 :                     appendStringInfoChar(buf, 'q');
     758             : 
     759          36 :                 appendStringInfoChar(buf, '"');
     760             :             }
     761             : 
     762          48 :             if (printBracketes)
     763           0 :                 appendStringInfoChar(buf, ')');
     764          48 :             break;
     765           6 :         case jpiBigint:
     766           6 :             appendStringInfoString(buf, ".bigint()");
     767           6 :             break;
     768           6 :         case jpiBoolean:
     769           6 :             appendStringInfoString(buf, ".boolean()");
     770           6 :             break;
     771           6 :         case jpiDate:
     772           6 :             appendStringInfoString(buf, ".date()");
     773           6 :             break;
     774          12 :         case jpiDecimal:
     775          12 :             appendStringInfoString(buf, ".decimal(");
     776          12 :             if (v->content.args.left)
     777             :             {
     778           6 :                 jspGetLeftArg(v, &elem);
     779           6 :                 printJsonPathItem(buf, &elem, false, false);
     780             :             }
     781          12 :             if (v->content.args.right)
     782             :             {
     783           6 :                 appendStringInfoChar(buf, ',');
     784           6 :                 jspGetRightArg(v, &elem);
     785           6 :                 printJsonPathItem(buf, &elem, false, false);
     786             :             }
     787          12 :             appendStringInfoChar(buf, ')');
     788          12 :             break;
     789           6 :         case jpiInteger:
     790           6 :             appendStringInfoString(buf, ".integer()");
     791           6 :             break;
     792           6 :         case jpiNumber:
     793           6 :             appendStringInfoString(buf, ".number()");
     794           6 :             break;
     795           6 :         case jpiStringFunc:
     796           6 :             appendStringInfoString(buf, ".string()");
     797           6 :             break;
     798          12 :         case jpiTime:
     799          12 :             appendStringInfoString(buf, ".time(");
     800          12 :             if (v->content.arg)
     801             :             {
     802           6 :                 jspGetArg(v, &elem);
     803           6 :                 printJsonPathItem(buf, &elem, false, false);
     804             :             }
     805          12 :             appendStringInfoChar(buf, ')');
     806          12 :             break;
     807          12 :         case jpiTimeTz:
     808          12 :             appendStringInfoString(buf, ".time_tz(");
     809          12 :             if (v->content.arg)
     810             :             {
     811           6 :                 jspGetArg(v, &elem);
     812           6 :                 printJsonPathItem(buf, &elem, false, false);
     813             :             }
     814          12 :             appendStringInfoChar(buf, ')');
     815          12 :             break;
     816          12 :         case jpiTimestamp:
     817          12 :             appendStringInfoString(buf, ".timestamp(");
     818          12 :             if (v->content.arg)
     819             :             {
     820           6 :                 jspGetArg(v, &elem);
     821           6 :                 printJsonPathItem(buf, &elem, false, false);
     822             :             }
     823          12 :             appendStringInfoChar(buf, ')');
     824          12 :             break;
     825          12 :         case jpiTimestampTz:
     826          12 :             appendStringInfoString(buf, ".timestamp_tz(");
     827          12 :             if (v->content.arg)
     828             :             {
     829           6 :                 jspGetArg(v, &elem);
     830           6 :                 printJsonPathItem(buf, &elem, false, false);
     831             :             }
     832          12 :             appendStringInfoChar(buf, ')');
     833          12 :             break;
     834           0 :         default:
     835           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
     836             :     }
     837             : 
     838        6656 :     if (jspGetNext(v, &elem))
     839        2358 :         printJsonPathItem(buf, &elem, true, true);
     840        6656 : }
     841             : 
     842             : const char *
     843        1532 : jspOperationName(JsonPathItemType type)
     844             : {
     845        1532 :     switch (type)
     846             :     {
     847          30 :         case jpiAnd:
     848          30 :             return "&&";
     849          54 :         case jpiOr:
     850          54 :             return "||";
     851         168 :         case jpiEqual:
     852         168 :             return "==";
     853           6 :         case jpiNotEqual:
     854           6 :             return "!=";
     855         300 :         case jpiLess:
     856         300 :             return "<";
     857          44 :         case jpiGreater:
     858          44 :             return ">";
     859           6 :         case jpiLessOrEqual:
     860           6 :             return "<=";
     861          42 :         case jpiGreaterOrEqual:
     862          42 :             return ">=";
     863          84 :         case jpiAdd:
     864             :         case jpiPlus:
     865          84 :             return "+";
     866          36 :         case jpiSub:
     867             :         case jpiMinus:
     868          36 :             return "-";
     869          24 :         case jpiMul:
     870          24 :             return "*";
     871           6 :         case jpiDiv:
     872           6 :             return "/";
     873           6 :         case jpiMod:
     874           6 :             return "%";
     875           0 :         case jpiType:
     876           0 :             return "type";
     877           6 :         case jpiSize:
     878           6 :             return "size";
     879           6 :         case jpiAbs:
     880           6 :             return "abs";
     881           6 :         case jpiFloor:
     882           6 :             return "floor";
     883           6 :         case jpiCeiling:
     884           6 :             return "ceiling";
     885          60 :         case jpiDouble:
     886          60 :             return "double";
     887          30 :         case jpiDatetime:
     888          30 :             return "datetime";
     889          18 :         case jpiKeyValue:
     890          18 :             return "keyvalue";
     891          12 :         case jpiStartsWith:
     892          12 :             return "starts with";
     893           0 :         case jpiLikeRegex:
     894           0 :             return "like_regex";
     895          78 :         case jpiBigint:
     896          78 :             return "bigint";
     897          72 :         case jpiBoolean:
     898          72 :             return "boolean";
     899          36 :         case jpiDate:
     900          36 :             return "date";
     901          78 :         case jpiDecimal:
     902          78 :             return "decimal";
     903          78 :         case jpiInteger:
     904          78 :             return "integer";
     905          54 :         case jpiNumber:
     906          54 :             return "number";
     907          18 :         case jpiStringFunc:
     908          18 :             return "string";
     909          42 :         case jpiTime:
     910          42 :             return "time";
     911          42 :         case jpiTimeTz:
     912          42 :             return "time_tz";
     913          42 :         case jpiTimestamp:
     914          42 :             return "timestamp";
     915          42 :         case jpiTimestampTz:
     916          42 :             return "timestamp_tz";
     917           0 :         default:
     918           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", type);
     919             :             return NULL;
     920             :     }
     921             : }
     922             : 
     923             : static int
     924        3344 : operationPriority(JsonPathItemType op)
     925             : {
     926        3344 :     switch (op)
     927             :     {
     928         114 :         case jpiOr:
     929         114 :             return 0;
     930          78 :         case jpiAnd:
     931          78 :             return 1;
     932        1282 :         case jpiEqual:
     933             :         case jpiNotEqual:
     934             :         case jpiLess:
     935             :         case jpiGreater:
     936             :         case jpiLessOrEqual:
     937             :         case jpiGreaterOrEqual:
     938             :         case jpiStartsWith:
     939        1282 :             return 2;
     940         252 :         case jpiAdd:
     941             :         case jpiSub:
     942         252 :             return 3;
     943          66 :         case jpiMul:
     944             :         case jpiDiv:
     945             :         case jpiMod:
     946          66 :             return 4;
     947          84 :         case jpiPlus:
     948             :         case jpiMinus:
     949          84 :             return 5;
     950        1468 :         default:
     951        1468 :             return 6;
     952             :     }
     953             : }
     954             : 
     955             : /******************* Support functions for JsonPath *************************/
     956             : 
     957             : /*
     958             :  * Support macros to read stored values
     959             :  */
     960             : 
     961             : #define read_byte(v, b, p) do {         \
     962             :     (v) = *(uint8*)((b) + (p));         \
     963             :     (p) += 1;                           \
     964             : } while(0)                              \
     965             : 
     966             : #define read_int32(v, b, p) do {        \
     967             :     (v) = *(uint32*)((b) + (p));        \
     968             :     (p) += sizeof(int32);               \
     969             : } while(0)                              \
     970             : 
     971             : #define read_int32_n(v, b, p, n) do {   \
     972             :     (v) = (void *)((b) + (p));          \
     973             :     (p) += sizeof(int32) * (n);         \
     974             : } while(0)                              \
     975             : 
     976             : /*
     977             :  * Read root node and fill root node representation
     978             :  */
     979             : void
     980      201518 : jspInit(JsonPathItem *v, JsonPath *js)
     981             : {
     982             :     Assert((js->header & ~JSONPATH_LAX) == JSONPATH_VERSION);
     983      201518 :     jspInitByBuffer(v, js->data, 0);
     984      201518 : }
     985             : 
     986             : /*
     987             :  * Read node from buffer and fill its representation
     988             :  */
     989             : void
     990      687588 : jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
     991             : {
     992      687588 :     v->base = base + pos;
     993             : 
     994      687588 :     read_byte(v->type, base, pos);
     995      687588 :     pos = INTALIGN((uintptr_t) (base + pos)) - (uintptr_t) base;
     996      687588 :     read_int32(v->nextPos, base, pos);
     997             : 
     998      687588 :     switch (v->type)
     999             :     {
    1000      252074 :         case jpiNull:
    1001             :         case jpiRoot:
    1002             :         case jpiCurrent:
    1003             :         case jpiAnyArray:
    1004             :         case jpiAnyKey:
    1005             :         case jpiType:
    1006             :         case jpiSize:
    1007             :         case jpiAbs:
    1008             :         case jpiFloor:
    1009             :         case jpiCeiling:
    1010             :         case jpiDouble:
    1011             :         case jpiKeyValue:
    1012             :         case jpiLast:
    1013             :         case jpiBigint:
    1014             :         case jpiBoolean:
    1015             :         case jpiDate:
    1016             :         case jpiInteger:
    1017             :         case jpiNumber:
    1018             :         case jpiStringFunc:
    1019      252074 :             break;
    1020      203194 :         case jpiString:
    1021             :         case jpiKey:
    1022             :         case jpiVariable:
    1023      203194 :             read_int32(v->content.value.datalen, base, pos);
    1024             :             /* FALLTHROUGH */
    1025      226668 :         case jpiNumeric:
    1026             :         case jpiBool:
    1027      226668 :             v->content.value.data = base + pos;
    1028      226668 :             break;
    1029      101168 :         case jpiAnd:
    1030             :         case jpiOr:
    1031             :         case jpiEqual:
    1032             :         case jpiNotEqual:
    1033             :         case jpiLess:
    1034             :         case jpiGreater:
    1035             :         case jpiLessOrEqual:
    1036             :         case jpiGreaterOrEqual:
    1037             :         case jpiAdd:
    1038             :         case jpiSub:
    1039             :         case jpiMul:
    1040             :         case jpiDiv:
    1041             :         case jpiMod:
    1042             :         case jpiStartsWith:
    1043             :         case jpiDecimal:
    1044      101168 :             read_int32(v->content.args.left, base, pos);
    1045      101168 :             read_int32(v->content.args.right, base, pos);
    1046      101168 :             break;
    1047      106274 :         case jpiNot:
    1048             :         case jpiIsUnknown:
    1049             :         case jpiExists:
    1050             :         case jpiPlus:
    1051             :         case jpiMinus:
    1052             :         case jpiFilter:
    1053             :         case jpiDatetime:
    1054             :         case jpiTime:
    1055             :         case jpiTimeTz:
    1056             :         case jpiTimestamp:
    1057             :         case jpiTimestampTz:
    1058      106274 :             read_int32(v->content.arg, base, pos);
    1059      106274 :             break;
    1060         606 :         case jpiIndexArray:
    1061         606 :             read_int32(v->content.array.nelems, base, pos);
    1062         606 :             read_int32_n(v->content.array.elems, base, pos,
    1063             :                          v->content.array.nelems * 2);
    1064         606 :             break;
    1065         354 :         case jpiAny:
    1066         354 :             read_int32(v->content.anybounds.first, base, pos);
    1067         354 :             read_int32(v->content.anybounds.last, base, pos);
    1068         354 :             break;
    1069         444 :         case jpiLikeRegex:
    1070         444 :             read_int32(v->content.like_regex.flags, base, pos);
    1071         444 :             read_int32(v->content.like_regex.expr, base, pos);
    1072         444 :             read_int32(v->content.like_regex.patternlen, base, pos);
    1073         444 :             v->content.like_regex.pattern = base + pos;
    1074         444 :             break;
    1075           0 :         default:
    1076           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
    1077             :     }
    1078      687588 : }
    1079             : 
    1080             : void
    1081      106070 : jspGetArg(JsonPathItem *v, JsonPathItem *a)
    1082             : {
    1083             :     Assert(v->type == jpiNot ||
    1084             :            v->type == jpiIsUnknown ||
    1085             :            v->type == jpiPlus ||
    1086             :            v->type == jpiMinus ||
    1087             :            v->type == jpiFilter ||
    1088             :            v->type == jpiExists ||
    1089             :            v->type == jpiDatetime ||
    1090             :            v->type == jpiTime ||
    1091             :            v->type == jpiTimeTz ||
    1092             :            v->type == jpiTimestamp ||
    1093             :            v->type == jpiTimestampTz);
    1094             : 
    1095      106070 :     jspInitByBuffer(a, v->base, v->content.arg);
    1096      106070 : }
    1097             : 
    1098             : bool
    1099      452676 : jspGetNext(JsonPathItem *v, JsonPathItem *a)
    1100             : {
    1101      452676 :     if (jspHasNext(v))
    1102             :     {
    1103             :         Assert(v->type == jpiNull ||
    1104             :                v->type == jpiString ||
    1105             :                v->type == jpiNumeric ||
    1106             :                v->type == jpiBool ||
    1107             :                v->type == jpiAnd ||
    1108             :                v->type == jpiOr ||
    1109             :                v->type == jpiNot ||
    1110             :                v->type == jpiIsUnknown ||
    1111             :                v->type == jpiEqual ||
    1112             :                v->type == jpiNotEqual ||
    1113             :                v->type == jpiLess ||
    1114             :                v->type == jpiGreater ||
    1115             :                v->type == jpiLessOrEqual ||
    1116             :                v->type == jpiGreaterOrEqual ||
    1117             :                v->type == jpiAdd ||
    1118             :                v->type == jpiSub ||
    1119             :                v->type == jpiMul ||
    1120             :                v->type == jpiDiv ||
    1121             :                v->type == jpiMod ||
    1122             :                v->type == jpiPlus ||
    1123             :                v->type == jpiMinus ||
    1124             :                v->type == jpiAnyArray ||
    1125             :                v->type == jpiAnyKey ||
    1126             :                v->type == jpiIndexArray ||
    1127             :                v->type == jpiAny ||
    1128             :                v->type == jpiKey ||
    1129             :                v->type == jpiCurrent ||
    1130             :                v->type == jpiRoot ||
    1131             :                v->type == jpiVariable ||
    1132             :                v->type == jpiFilter ||
    1133             :                v->type == jpiExists ||
    1134             :                v->type == jpiType ||
    1135             :                v->type == jpiSize ||
    1136             :                v->type == jpiAbs ||
    1137             :                v->type == jpiFloor ||
    1138             :                v->type == jpiCeiling ||
    1139             :                v->type == jpiDouble ||
    1140             :                v->type == jpiDatetime ||
    1141             :                v->type == jpiKeyValue ||
    1142             :                v->type == jpiLast ||
    1143             :                v->type == jpiStartsWith ||
    1144             :                v->type == jpiLikeRegex ||
    1145             :                v->type == jpiBigint ||
    1146             :                v->type == jpiBoolean ||
    1147             :                v->type == jpiDate ||
    1148             :                v->type == jpiDecimal ||
    1149             :                v->type == jpiInteger ||
    1150             :                v->type == jpiNumber ||
    1151             :                v->type == jpiStringFunc ||
    1152             :                v->type == jpiTime ||
    1153             :                v->type == jpiTimeTz ||
    1154             :                v->type == jpiTimestamp ||
    1155             :                v->type == jpiTimestampTz);
    1156             : 
    1157      202168 :         if (a)
    1158      202168 :             jspInitByBuffer(a, v->base, v->nextPos);
    1159      202168 :         return true;
    1160             :     }
    1161             : 
    1162      250508 :     return false;
    1163             : }
    1164             : 
    1165             : void
    1166      101000 : jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
    1167             : {
    1168             :     Assert(v->type == jpiAnd ||
    1169             :            v->type == jpiOr ||
    1170             :            v->type == jpiEqual ||
    1171             :            v->type == jpiNotEqual ||
    1172             :            v->type == jpiLess ||
    1173             :            v->type == jpiGreater ||
    1174             :            v->type == jpiLessOrEqual ||
    1175             :            v->type == jpiGreaterOrEqual ||
    1176             :            v->type == jpiAdd ||
    1177             :            v->type == jpiSub ||
    1178             :            v->type == jpiMul ||
    1179             :            v->type == jpiDiv ||
    1180             :            v->type == jpiMod ||
    1181             :            v->type == jpiStartsWith ||
    1182             :            v->type == jpiDecimal);
    1183             : 
    1184      101000 :     jspInitByBuffer(a, v->base, v->content.args.left);
    1185      101000 : }
    1186             : 
    1187             : void
    1188       75698 : jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
    1189             : {
    1190             :     Assert(v->type == jpiAnd ||
    1191             :            v->type == jpiOr ||
    1192             :            v->type == jpiEqual ||
    1193             :            v->type == jpiNotEqual ||
    1194             :            v->type == jpiLess ||
    1195             :            v->type == jpiGreater ||
    1196             :            v->type == jpiLessOrEqual ||
    1197             :            v->type == jpiGreaterOrEqual ||
    1198             :            v->type == jpiAdd ||
    1199             :            v->type == jpiSub ||
    1200             :            v->type == jpiMul ||
    1201             :            v->type == jpiDiv ||
    1202             :            v->type == jpiMod ||
    1203             :            v->type == jpiStartsWith ||
    1204             :            v->type == jpiDecimal);
    1205             : 
    1206       75698 :     jspInitByBuffer(a, v->base, v->content.args.right);
    1207       75698 : }
    1208             : 
    1209             : bool
    1210        1434 : jspGetBool(JsonPathItem *v)
    1211             : {
    1212             :     Assert(v->type == jpiBool);
    1213             : 
    1214        1434 :     return (bool) *v->content.value.data;
    1215             : }
    1216             : 
    1217             : Numeric
    1218       21836 : jspGetNumeric(JsonPathItem *v)
    1219             : {
    1220             :     Assert(v->type == jpiNumeric);
    1221             : 
    1222       21836 :     return (Numeric) v->content.value.data;
    1223             : }
    1224             : 
    1225             : char *
    1226      202258 : jspGetString(JsonPathItem *v, int32 *len)
    1227             : {
    1228             :     Assert(v->type == jpiKey ||
    1229             :            v->type == jpiString ||
    1230             :            v->type == jpiVariable);
    1231             : 
    1232      202258 :     if (len)
    1233      202192 :         *len = v->content.value.datalen;
    1234      202258 :     return v->content.value.data;
    1235             : }
    1236             : 
    1237             : bool
    1238         642 : jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to,
    1239             :                      int i)
    1240             : {
    1241             :     Assert(v->type == jpiIndexArray);
    1242             : 
    1243         642 :     jspInitByBuffer(from, v->base, v->content.array.elems[i].from);
    1244             : 
    1245         642 :     if (!v->content.array.elems[i].to)
    1246         594 :         return false;
    1247             : 
    1248          48 :     jspInitByBuffer(to, v->base, v->content.array.elems[i].to);
    1249             : 
    1250          48 :     return true;
    1251             : }
    1252             : 
    1253             : /* SQL/JSON datatype status: */
    1254             : enum JsonPathDatatypeStatus
    1255             : {
    1256             :     jpdsNonDateTime,            /* null, bool, numeric, string, array, object */
    1257             :     jpdsUnknownDateTime,        /* unknown datetime type */
    1258             :     jpdsDateTimeZoned,          /* timetz, timestamptz */
    1259             :     jpdsDateTimeNonZoned,       /* time, timestamp, date */
    1260             : };
    1261             : 
    1262             : /* Context for jspIsMutableWalker() */
    1263             : struct JsonPathMutableContext
    1264             : {
    1265             :     List       *varnames;       /* list of variable names */
    1266             :     List       *varexprs;       /* list of variable expressions */
    1267             :     enum JsonPathDatatypeStatus current;    /* status of @ item */
    1268             :     bool        lax;            /* jsonpath is lax or strict */
    1269             :     bool        mutable;        /* resulting mutability status */
    1270             : };
    1271             : 
    1272             : static enum JsonPathDatatypeStatus jspIsMutableWalker(JsonPathItem *jpi,
    1273             :                                                       struct JsonPathMutableContext *cxt);
    1274             : 
    1275             : /*
    1276             :  * Function to check whether jsonpath expression is mutable to be used in the
    1277             :  * planner function contain_mutable_functions().
    1278             :  */
    1279             : bool
    1280         234 : jspIsMutable(JsonPath *path, List *varnames, List *varexprs)
    1281             : {
    1282             :     struct JsonPathMutableContext cxt;
    1283             :     JsonPathItem jpi;
    1284             : 
    1285         234 :     cxt.varnames = varnames;
    1286         234 :     cxt.varexprs = varexprs;
    1287         234 :     cxt.current = jpdsNonDateTime;
    1288         234 :     cxt.lax = (path->header & JSONPATH_LAX) != 0;
    1289         234 :     cxt.mutable = false;
    1290             : 
    1291         234 :     jspInit(&jpi, path);
    1292         234 :     (void) jspIsMutableWalker(&jpi, &cxt);
    1293             : 
    1294         234 :     return cxt.mutable;
    1295             : }
    1296             : 
    1297             : /*
    1298             :  * Recursive walker for jspIsMutable()
    1299             :  */
    1300             : static enum JsonPathDatatypeStatus
    1301         786 : jspIsMutableWalker(JsonPathItem *jpi, struct JsonPathMutableContext *cxt)
    1302             : {
    1303             :     JsonPathItem next;
    1304         786 :     enum JsonPathDatatypeStatus status = jpdsNonDateTime;
    1305             : 
    1306        1338 :     while (!cxt->mutable)
    1307             :     {
    1308             :         JsonPathItem arg;
    1309             :         enum JsonPathDatatypeStatus leftStatus;
    1310             :         enum JsonPathDatatypeStatus rightStatus;
    1311             : 
    1312        1242 :         switch (jpi->type)
    1313             :         {
    1314         288 :             case jpiRoot:
    1315             :                 Assert(status == jpdsNonDateTime);
    1316         288 :                 break;
    1317             : 
    1318         144 :             case jpiCurrent:
    1319             :                 Assert(status == jpdsNonDateTime);
    1320         144 :                 status = cxt->current;
    1321         144 :                 break;
    1322             : 
    1323         144 :             case jpiFilter:
    1324             :                 {
    1325         144 :                     enum JsonPathDatatypeStatus prevStatus = cxt->current;
    1326             : 
    1327         144 :                     cxt->current = status;
    1328         144 :                     jspGetArg(jpi, &arg);
    1329         144 :                     jspIsMutableWalker(&arg, cxt);
    1330             : 
    1331         144 :                     cxt->current = prevStatus;
    1332         144 :                     break;
    1333             :                 }
    1334             : 
    1335          54 :             case jpiVariable:
    1336             :                 {
    1337             :                     int32       len;
    1338          54 :                     const char *name = jspGetString(jpi, &len);
    1339             :                     ListCell   *lc1;
    1340             :                     ListCell   *lc2;
    1341             : 
    1342             :                     Assert(status == jpdsNonDateTime);
    1343             : 
    1344          60 :                     forboth(lc1, cxt->varnames, lc2, cxt->varexprs)
    1345             :                     {
    1346          54 :                         String     *varname = lfirst_node(String, lc1);
    1347          54 :                         Node       *varexpr = lfirst(lc2);
    1348             : 
    1349          54 :                         if (strncmp(varname->sval, name, len))
    1350           6 :                             continue;
    1351             : 
    1352          48 :                         switch (exprType(varexpr))
    1353             :                         {
    1354          30 :                             case DATEOID:
    1355             :                             case TIMEOID:
    1356             :                             case TIMESTAMPOID:
    1357          30 :                                 status = jpdsDateTimeNonZoned;
    1358          30 :                                 break;
    1359             : 
    1360          12 :                             case TIMETZOID:
    1361             :                             case TIMESTAMPTZOID:
    1362          12 :                                 status = jpdsDateTimeZoned;
    1363          12 :                                 break;
    1364             : 
    1365           6 :                             default:
    1366           6 :                                 status = jpdsNonDateTime;
    1367           6 :                                 break;
    1368             :                         }
    1369             : 
    1370          48 :                         break;
    1371             :                     }
    1372          54 :                     break;
    1373             :                 }
    1374             : 
    1375         180 :             case jpiEqual:
    1376             :             case jpiNotEqual:
    1377             :             case jpiLess:
    1378             :             case jpiGreater:
    1379             :             case jpiLessOrEqual:
    1380             :             case jpiGreaterOrEqual:
    1381             :                 Assert(status == jpdsNonDateTime);
    1382         180 :                 jspGetLeftArg(jpi, &arg);
    1383         180 :                 leftStatus = jspIsMutableWalker(&arg, cxt);
    1384             : 
    1385         180 :                 jspGetRightArg(jpi, &arg);
    1386         180 :                 rightStatus = jspIsMutableWalker(&arg, cxt);
    1387             : 
    1388             :                 /*
    1389             :                  * Comparison of datetime type with different timezone status
    1390             :                  * is mutable.
    1391             :                  */
    1392         180 :                 if (leftStatus != jpdsNonDateTime &&
    1393          72 :                     rightStatus != jpdsNonDateTime &&
    1394          36 :                     (leftStatus == jpdsUnknownDateTime ||
    1395          36 :                      rightStatus == jpdsUnknownDateTime ||
    1396             :                      leftStatus != rightStatus))
    1397          42 :                     cxt->mutable = true;
    1398         180 :                 break;
    1399             : 
    1400           0 :             case jpiNot:
    1401             :             case jpiIsUnknown:
    1402             :             case jpiExists:
    1403             :             case jpiPlus:
    1404             :             case jpiMinus:
    1405             :                 Assert(status == jpdsNonDateTime);
    1406           0 :                 jspGetArg(jpi, &arg);
    1407           0 :                 jspIsMutableWalker(&arg, cxt);
    1408           0 :                 break;
    1409             : 
    1410           0 :             case jpiAnd:
    1411             :             case jpiOr:
    1412             :             case jpiAdd:
    1413             :             case jpiSub:
    1414             :             case jpiMul:
    1415             :             case jpiDiv:
    1416             :             case jpiMod:
    1417             :             case jpiStartsWith:
    1418             :                 Assert(status == jpdsNonDateTime);
    1419           0 :                 jspGetLeftArg(jpi, &arg);
    1420           0 :                 jspIsMutableWalker(&arg, cxt);
    1421           0 :                 jspGetRightArg(jpi, &arg);
    1422           0 :                 jspIsMutableWalker(&arg, cxt);
    1423           0 :                 break;
    1424             : 
    1425          24 :             case jpiIndexArray:
    1426          66 :                 for (int i = 0; i < jpi->content.array.nelems; i++)
    1427             :                 {
    1428             :                     JsonPathItem from;
    1429             :                     JsonPathItem to;
    1430             : 
    1431          42 :                     if (jspGetArraySubscript(jpi, &from, &to, i))
    1432           6 :                         jspIsMutableWalker(&to, cxt);
    1433             : 
    1434          42 :                     jspIsMutableWalker(&from, cxt);
    1435             :                 }
    1436             :                 /* FALLTHROUGH */
    1437             : 
    1438             :             case jpiAnyArray:
    1439          24 :                 if (!cxt->lax)
    1440           0 :                     status = jpdsNonDateTime;
    1441          24 :                 break;
    1442             : 
    1443           0 :             case jpiAny:
    1444           0 :                 if (jpi->content.anybounds.first > 0)
    1445           0 :                     status = jpdsNonDateTime;
    1446           0 :                 break;
    1447             : 
    1448         126 :             case jpiDatetime:
    1449         126 :                 if (jpi->content.arg)
    1450             :                 {
    1451             :                     char       *template;
    1452             : 
    1453          66 :                     jspGetArg(jpi, &arg);
    1454          66 :                     if (arg.type != jpiString)
    1455             :                     {
    1456           0 :                         status = jpdsNonDateTime;
    1457           0 :                         break;  /* there will be runtime error */
    1458             :                     }
    1459             : 
    1460          66 :                     template = jspGetString(&arg, NULL);
    1461          66 :                     if (datetime_format_has_tz(template))
    1462          36 :                         status = jpdsDateTimeZoned;
    1463             :                     else
    1464          30 :                         status = jpdsDateTimeNonZoned;
    1465             :                 }
    1466             :                 else
    1467             :                 {
    1468          60 :                     status = jpdsUnknownDateTime;
    1469             :                 }
    1470         126 :                 break;
    1471             : 
    1472           0 :             case jpiLikeRegex:
    1473             :                 Assert(status == jpdsNonDateTime);
    1474           0 :                 jspInitByBuffer(&arg, jpi->base, jpi->content.like_regex.expr);
    1475           0 :                 jspIsMutableWalker(&arg, cxt);
    1476           0 :                 break;
    1477             : 
    1478             :                 /* literals */
    1479          24 :             case jpiNull:
    1480             :             case jpiString:
    1481             :             case jpiNumeric:
    1482             :             case jpiBool:
    1483          24 :                 break;
    1484             :                 /* accessors */
    1485         138 :             case jpiKey:
    1486             :             case jpiAnyKey:
    1487             :                 /* special items */
    1488             :             case jpiSubscript:
    1489             :             case jpiLast:
    1490             :                 /* item methods */
    1491             :             case jpiType:
    1492             :             case jpiSize:
    1493             :             case jpiAbs:
    1494             :             case jpiFloor:
    1495             :             case jpiCeiling:
    1496             :             case jpiDouble:
    1497             :             case jpiKeyValue:
    1498             :             case jpiBigint:
    1499             :             case jpiBoolean:
    1500             :             case jpiDecimal:
    1501             :             case jpiInteger:
    1502             :             case jpiNumber:
    1503             :             case jpiStringFunc:
    1504         138 :                 status = jpdsNonDateTime;
    1505         138 :                 break;
    1506             : 
    1507          90 :             case jpiTime:
    1508             :             case jpiDate:
    1509             :             case jpiTimestamp:
    1510          90 :                 status = jpdsDateTimeNonZoned;
    1511          90 :                 cxt->mutable = true;
    1512          90 :                 break;
    1513             : 
    1514          30 :             case jpiTimeTz:
    1515             :             case jpiTimestampTz:
    1516          30 :                 status = jpdsDateTimeNonZoned;
    1517          30 :                 cxt->mutable = true;
    1518          30 :                 break;
    1519             : 
    1520             :         }
    1521             : 
    1522        1242 :         if (!jspGetNext(jpi, &next))
    1523         690 :             break;
    1524             : 
    1525         552 :         jpi = &next;
    1526             :     }
    1527             : 
    1528         786 :     return status;
    1529             : }

Generated by: LCOV version 1.14