LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15beta1 Lines: 529 581 91.0 %
Date: 2022-05-18 03:10:05 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-2022, 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 "funcapi.h"
      67             : #include "lib/stringinfo.h"
      68             : #include "libpq/pqformat.h"
      69             : #include "miscadmin.h"
      70             : #include "nodes/nodeFuncs.h"
      71             : #include "utils/builtins.h"
      72             : #include "utils/formatting.h"
      73             : #include "utils/json.h"
      74             : #include "utils/jsonpath.h"
      75             : 
      76             : 
      77             : static Datum jsonPathFromCstring(char *in, int len);
      78             : static char *jsonPathToCstring(StringInfo out, JsonPath *in,
      79             :                                int estimated_len);
      80             : static int  flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
      81             :                                      int nestingLevel, bool insideArraySubscript);
      82             : static void alignStringInfoInt(StringInfo buf);
      83             : static int32 reserveSpaceForItemPointer(StringInfo buf);
      84             : static void printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
      85             :                               bool printBracketes);
      86             : static int  operationPriority(JsonPathItemType op);
      87             : 
      88             : 
      89             : /**************************** INPUT/OUTPUT ********************************/
      90             : 
      91             : /*
      92             :  * jsonpath type input function
      93             :  */
      94             : Datum
      95        6822 : jsonpath_in(PG_FUNCTION_ARGS)
      96             : {
      97        6822 :     char       *in = PG_GETARG_CSTRING(0);
      98        6822 :     int         len = strlen(in);
      99             : 
     100        6822 :     return jsonPathFromCstring(in, len);
     101             : }
     102             : 
     103             : /*
     104             :  * jsonpath type recv function
     105             :  *
     106             :  * The type is sent as text in binary mode, so this is almost the same
     107             :  * as the input function, but it's prefixed with a version number so we
     108             :  * can change the binary format sent in future if necessary. For now,
     109             :  * only version 1 is supported.
     110             :  */
     111             : Datum
     112           0 : jsonpath_recv(PG_FUNCTION_ARGS)
     113             : {
     114           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     115           0 :     int         version = pq_getmsgint(buf, 1);
     116             :     char       *str;
     117             :     int         nbytes;
     118             : 
     119           0 :     if (version == JSONPATH_VERSION)
     120           0 :         str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     121             :     else
     122           0 :         elog(ERROR, "unsupported jsonpath version number: %d", version);
     123             : 
     124           0 :     return jsonPathFromCstring(str, nbytes);
     125             : }
     126             : 
     127             : /*
     128             :  * jsonpath type output function
     129             :  */
     130             : Datum
     131        1592 : jsonpath_out(PG_FUNCTION_ARGS)
     132             : {
     133        1592 :     JsonPath   *in = PG_GETARG_JSONPATH_P(0);
     134             : 
     135        1592 :     PG_RETURN_CSTRING(jsonPathToCstring(NULL, in, VARSIZE(in)));
     136             : }
     137             : 
     138             : /*
     139             :  * jsonpath type send function
     140             :  *
     141             :  * Just send jsonpath as a version number, then a string of text
     142             :  */
     143             : Datum
     144           0 : jsonpath_send(PG_FUNCTION_ARGS)
     145             : {
     146           0 :     JsonPath   *in = PG_GETARG_JSONPATH_P(0);
     147             :     StringInfoData buf;
     148             :     StringInfoData jtext;
     149           0 :     int         version = JSONPATH_VERSION;
     150             : 
     151           0 :     initStringInfo(&jtext);
     152           0 :     (void) jsonPathToCstring(&jtext, in, VARSIZE(in));
     153             : 
     154           0 :     pq_begintypsend(&buf);
     155           0 :     pq_sendint8(&buf, version);
     156           0 :     pq_sendtext(&buf, jtext.data, jtext.len);
     157           0 :     pfree(jtext.data);
     158             : 
     159           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     160             : }
     161             : 
     162             : /*
     163             :  * Converts C-string to a jsonpath value.
     164             :  *
     165             :  * Uses jsonpath parser to turn string into an AST, then
     166             :  * flattenJsonPathParseItem() does second pass turning AST into binary
     167             :  * representation of jsonpath.
     168             :  */
     169             : static Datum
     170        6822 : jsonPathFromCstring(char *in, int len)
     171             : {
     172        6822 :     JsonPathParseResult *jsonpath = parsejsonpath(in, len);
     173             :     JsonPath   *res;
     174             :     StringInfoData buf;
     175             : 
     176        6642 :     initStringInfo(&buf);
     177        6642 :     enlargeStringInfo(&buf, 4 * len /* estimation */ );
     178             : 
     179        6642 :     appendStringInfoSpaces(&buf, JSONPATH_HDRSZ);
     180             : 
     181        6642 :     if (!jsonpath)
     182           6 :         ereport(ERROR,
     183             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     184             :                  errmsg("invalid input syntax for type %s: \"%s\"", "jsonpath",
     185             :                         in)));
     186             : 
     187        6636 :     flattenJsonPathParseItem(&buf, jsonpath->expr, 0, false);
     188             : 
     189        6618 :     res = (JsonPath *) buf.data;
     190        6618 :     SET_VARSIZE(res, buf.len);
     191        6618 :     res->header = JSONPATH_VERSION;
     192        6618 :     if (jsonpath->lax)
     193        5934 :         res->header |= JSONPATH_LAX;
     194             : 
     195        6618 :     PG_RETURN_JSONPATH_P(res);
     196             : }
     197             : 
     198             : /*
     199             :  * Converts jsonpath value to a C-string.
     200             :  *
     201             :  * If 'out' argument is non-null, the resulting C-string is stored inside the
     202             :  * StringBuffer.  The resulting string is always returned.
     203             :  */
     204             : static char *
     205        1592 : jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
     206             : {
     207             :     StringInfoData buf;
     208             :     JsonPathItem v;
     209             : 
     210        1592 :     if (!out)
     211             :     {
     212        1592 :         out = &buf;
     213        1592 :         initStringInfo(out);
     214             :     }
     215        1592 :     enlargeStringInfo(out, estimated_len);
     216             : 
     217        1592 :     if (!(in->header & JSONPATH_LAX))
     218          30 :         appendBinaryStringInfo(out, "strict ", 7);
     219             : 
     220        1592 :     jspInit(&v, in);
     221        1592 :     printJsonPathItem(out, &v, false, true);
     222             : 
     223        1592 :     return out->data;
     224             : }
     225             : 
     226             : /*
     227             :  * Recursive function converting given jsonpath parse item and all its
     228             :  * children into a binary representation.
     229             :  */
     230             : static int
     231       24336 : flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
     232             :                          int nestingLevel, bool insideArraySubscript)
     233             : {
     234             :     /* position from beginning of jsonpath data */
     235       24336 :     int32       pos = buf->len - JSONPATH_HDRSZ;
     236             :     int32       chld;
     237             :     int32       next;
     238       24336 :     int         argNestingLevel = 0;
     239             : 
     240       24336 :     check_stack_depth();
     241       24336 :     CHECK_FOR_INTERRUPTS();
     242             : 
     243       24336 :     appendStringInfoChar(buf, (char) (item->type));
     244             : 
     245             :     /*
     246             :      * We align buffer to int32 because a series of int32 values often goes
     247             :      * after the header, and we want to read them directly by dereferencing
     248             :      * int32 pointer (see jspInitByBuffer()).
     249             :      */
     250       24336 :     alignStringInfoInt(buf);
     251             : 
     252             :     /*
     253             :      * Reserve space for next item pointer.  Actual value will be recorded
     254             :      * later, after next and children items processing.
     255             :      */
     256       24336 :     next = reserveSpaceForItemPointer(buf);
     257             : 
     258       24336 :     switch (item->type)
     259             :     {
     260        4848 :         case jpiString:
     261             :         case jpiVariable:
     262             :         case jpiKey:
     263        4848 :             appendBinaryStringInfo(buf, (char *) &item->value.string.len,
     264             :                                    sizeof(item->value.string.len));
     265        4848 :             appendBinaryStringInfo(buf, item->value.string.val,
     266        4848 :                                    item->value.string.len);
     267        4848 :             appendStringInfoChar(buf, '\0');
     268        4848 :             break;
     269        1782 :         case jpiNumeric:
     270        1782 :             appendBinaryStringInfo(buf, (char *) item->value.numeric,
     271        1782 :                                    VARSIZE(item->value.numeric));
     272        1782 :             break;
     273         156 :         case jpiBool:
     274         156 :             appendBinaryStringInfo(buf, (char *) &item->value.boolean,
     275             :                                    sizeof(item->value.boolean));
     276         156 :             break;
     277        2730 :         case jpiAnd:
     278             :         case jpiOr:
     279             :         case jpiEqual:
     280             :         case jpiNotEqual:
     281             :         case jpiLess:
     282             :         case jpiGreater:
     283             :         case jpiLessOrEqual:
     284             :         case jpiGreaterOrEqual:
     285             :         case jpiAdd:
     286             :         case jpiSub:
     287             :         case jpiMul:
     288             :         case jpiDiv:
     289             :         case jpiMod:
     290             :         case jpiStartsWith:
     291             :             {
     292             :                 /*
     293             :                  * First, reserve place for left/right arg's positions, then
     294             :                  * record both args and sets actual position in reserved
     295             :                  * places.
     296             :                  */
     297        2730 :                 int32       left = reserveSpaceForItemPointer(buf);
     298        2730 :                 int32       right = reserveSpaceForItemPointer(buf);
     299             : 
     300        2730 :                 chld = !item->value.args.left ? pos :
     301        2730 :                     flattenJsonPathParseItem(buf, item->value.args.left,
     302             :                                              nestingLevel + argNestingLevel,
     303             :                                              insideArraySubscript);
     304        2718 :                 *(int32 *) (buf->data + left) = chld - pos;
     305             : 
     306        2718 :                 chld = !item->value.args.right ? pos :
     307        2718 :                     flattenJsonPathParseItem(buf, item->value.args.right,
     308             :                                              nestingLevel + argNestingLevel,
     309             :                                              insideArraySubscript);
     310        2718 :                 *(int32 *) (buf->data + right) = chld - pos;
     311             :             }
     312        2718 :             break;
     313         108 :         case jpiLikeRegex:
     314             :             {
     315             :                 int32       offs;
     316             : 
     317         108 :                 appendBinaryStringInfo(buf,
     318         108 :                                        (char *) &item->value.like_regex.flags,
     319             :                                        sizeof(item->value.like_regex.flags));
     320         108 :                 offs = reserveSpaceForItemPointer(buf);
     321         108 :                 appendBinaryStringInfo(buf,
     322         108 :                                        (char *) &item->value.like_regex.patternlen,
     323             :                                        sizeof(item->value.like_regex.patternlen));
     324         108 :                 appendBinaryStringInfo(buf, item->value.like_regex.pattern,
     325         108 :                                        item->value.like_regex.patternlen);
     326         108 :                 appendStringInfoChar(buf, '\0');
     327             : 
     328         108 :                 chld = flattenJsonPathParseItem(buf, item->value.like_regex.expr,
     329             :                                                 nestingLevel,
     330             :                                                 insideArraySubscript);
     331         108 :                 *(int32 *) (buf->data + offs) = chld - pos;
     332             :             }
     333         108 :             break;
     334        1752 :         case jpiFilter:
     335        1752 :             argNestingLevel++;
     336             :             /* FALLTHROUGH */
     337        3150 :         case jpiIsUnknown:
     338             :         case jpiNot:
     339             :         case jpiPlus:
     340             :         case jpiMinus:
     341             :         case jpiExists:
     342             :         case jpiDatetime:
     343             :             {
     344        3150 :                 int32       arg = reserveSpaceForItemPointer(buf);
     345             : 
     346        3150 :                 chld = !item->value.arg ? pos :
     347        2754 :                     flattenJsonPathParseItem(buf, item->value.arg,
     348             :                                              nestingLevel + argNestingLevel,
     349             :                                              insideArraySubscript);
     350        3144 :                 *(int32 *) (buf->data + arg) = chld - pos;
     351             :             }
     352        3144 :             break;
     353         114 :         case jpiNull:
     354         114 :             break;
     355        6402 :         case jpiRoot:
     356        6402 :             break;
     357        1626 :         case jpiAnyArray:
     358             :         case jpiAnyKey:
     359        1626 :             break;
     360        2028 :         case jpiCurrent:
     361        2028 :             if (nestingLevel <= 0)
     362           6 :                 ereport(ERROR,
     363             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     364             :                          errmsg("@ is not allowed in root expressions")));
     365        2022 :             break;
     366          90 :         case jpiLast:
     367          90 :             if (!insideArraySubscript)
     368          12 :                 ereport(ERROR,
     369             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     370             :                          errmsg("LAST is allowed only in array subscripts")));
     371          78 :             break;
     372         432 :         case jpiIndexArray:
     373             :             {
     374         432 :                 int32       nelems = item->value.array.nelems;
     375             :                 int         offset;
     376             :                 int         i;
     377             : 
     378         432 :                 appendBinaryStringInfo(buf, (char *) &nelems, sizeof(nelems));
     379             : 
     380         432 :                 offset = buf->len;
     381             : 
     382         432 :                 appendStringInfoSpaces(buf, sizeof(int32) * 2 * nelems);
     383             : 
     384         912 :                 for (i = 0; i < nelems; i++)
     385             :                 {
     386             :                     int32      *ppos;
     387             :                     int32       topos;
     388         480 :                     int32       frompos =
     389         480 :                     flattenJsonPathParseItem(buf,
     390         480 :                                              item->value.array.elems[i].from,
     391             :                                              nestingLevel, true) - pos;
     392             : 
     393         480 :                     if (item->value.array.elems[i].to)
     394          48 :                         topos = flattenJsonPathParseItem(buf,
     395          48 :                                                          item->value.array.elems[i].to,
     396             :                                                          nestingLevel, true) - pos;
     397             :                     else
     398         432 :                         topos = 0;
     399             : 
     400         480 :                     ppos = (int32 *) &buf->data[offset + i * 2 * sizeof(int32)];
     401             : 
     402         480 :                     ppos[0] = frompos;
     403         480 :                     ppos[1] = topos;
     404             :                 }
     405             :             }
     406         432 :             break;
     407         354 :         case jpiAny:
     408         354 :             appendBinaryStringInfo(buf,
     409         354 :                                    (char *) &item->value.anybounds.first,
     410             :                                    sizeof(item->value.anybounds.first));
     411         354 :             appendBinaryStringInfo(buf,
     412         354 :                                    (char *) &item->value.anybounds.last,
     413             :                                    sizeof(item->value.anybounds.last));
     414         354 :             break;
     415         516 :         case jpiType:
     416             :         case jpiSize:
     417             :         case jpiAbs:
     418             :         case jpiFloor:
     419             :         case jpiCeiling:
     420             :         case jpiDouble:
     421             :         case jpiKeyValue:
     422         516 :             break;
     423           0 :         default:
     424           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
     425             :     }
     426             : 
     427       24300 :     if (item->next)
     428             :     {
     429        8862 :         chld = flattenJsonPathParseItem(buf, item->next, nestingLevel,
     430             :                                         insideArraySubscript) - pos;
     431        8856 :         *(int32 *) (buf->data + next) = chld;
     432             :     }
     433             : 
     434       24294 :     return pos;
     435             : }
     436             : 
     437             : /*
     438             :  * Align StringInfo to int by adding zero padding bytes
     439             :  */
     440             : static void
     441       24336 : alignStringInfoInt(StringInfo buf)
     442             : {
     443       24336 :     switch (INTALIGN(buf->len) - buf->len)
     444             :     {
     445       21666 :         case 3:
     446       21666 :             appendStringInfoCharMacro(buf, 0);
     447             :             /* FALLTHROUGH */
     448             :         case 2:
     449       22008 :             appendStringInfoCharMacro(buf, 0);
     450             :             /* FALLTHROUGH */
     451             :         case 1:
     452       24144 :             appendStringInfoCharMacro(buf, 0);
     453             :             /* FALLTHROUGH */
     454             :         default:
     455       24336 :             break;
     456             :     }
     457       24336 : }
     458             : 
     459             : /*
     460             :  * Reserve space for int32 JsonPathItem pointer.  Now zero pointer is written,
     461             :  * actual value will be recorded at '(int32 *) &buf->data[pos]' later.
     462             :  */
     463             : static int32
     464       33054 : reserveSpaceForItemPointer(StringInfo buf)
     465             : {
     466       33054 :     int32       pos = buf->len;
     467       33054 :     int32       ptr = 0;
     468             : 
     469       33054 :     appendBinaryStringInfo(buf, (char *) &ptr, sizeof(ptr));
     470             : 
     471       33054 :     return pos;
     472             : }
     473             : 
     474             : /*
     475             :  * Prints text representation of given jsonpath item and all its children.
     476             :  */
     477             : static void
     478        5954 : printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
     479             :                   bool printBracketes)
     480             : {
     481             :     JsonPathItem elem;
     482             :     int         i;
     483             : 
     484        5954 :     check_stack_depth();
     485        5954 :     CHECK_FOR_INTERRUPTS();
     486             : 
     487        5954 :     switch (v->type)
     488             :     {
     489          42 :         case jpiNull:
     490          42 :             appendStringInfoString(buf, "null");
     491          42 :             break;
     492        1244 :         case jpiKey:
     493        1244 :             if (inKey)
     494        1244 :                 appendStringInfoChar(buf, '.');
     495        1244 :             escape_json(buf, jspGetString(v, NULL));
     496        1244 :             break;
     497          84 :         case jpiString:
     498          84 :             escape_json(buf, jspGetString(v, NULL));
     499          84 :             break;
     500          48 :         case jpiVariable:
     501          48 :             appendStringInfoChar(buf, '$');
     502          48 :             escape_json(buf, jspGetString(v, NULL));
     503          48 :             break;
     504         836 :         case jpiNumeric:
     505         836 :             if (jspHasNext(v))
     506          84 :                 appendStringInfoChar(buf, '(');
     507         836 :             appendStringInfoString(buf,
     508         836 :                                    DatumGetCString(DirectFunctionCall1(numeric_out,
     509             :                                                                        NumericGetDatum(jspGetNumeric(v)))));
     510         836 :             if (jspHasNext(v))
     511          84 :                 appendStringInfoChar(buf, ')');
     512         836 :             break;
     513          12 :         case jpiBool:
     514          12 :             if (jspGetBool(v))
     515           6 :                 appendBinaryStringInfo(buf, "true", 4);
     516             :             else
     517           6 :                 appendBinaryStringInfo(buf, "false", 5);
     518          12 :             break;
     519         740 :         case jpiAnd:
     520             :         case jpiOr:
     521             :         case jpiEqual:
     522             :         case jpiNotEqual:
     523             :         case jpiLess:
     524             :         case jpiGreater:
     525             :         case jpiLessOrEqual:
     526             :         case jpiGreaterOrEqual:
     527             :         case jpiAdd:
     528             :         case jpiSub:
     529             :         case jpiMul:
     530             :         case jpiDiv:
     531             :         case jpiMod:
     532             :         case jpiStartsWith:
     533         740 :             if (printBracketes)
     534         114 :                 appendStringInfoChar(buf, '(');
     535         740 :             jspGetLeftArg(v, &elem);
     536         740 :             printJsonPathItem(buf, &elem, false,
     537         740 :                               operationPriority(elem.type) <=
     538         740 :                               operationPriority(v->type));
     539         740 :             appendStringInfoChar(buf, ' ');
     540         740 :             appendStringInfoString(buf, jspOperationName(v->type));
     541         740 :             appendStringInfoChar(buf, ' ');
     542         740 :             jspGetRightArg(v, &elem);
     543         740 :             printJsonPathItem(buf, &elem, false,
     544         740 :                               operationPriority(elem.type) <=
     545         740 :                               operationPriority(v->type));
     546         740 :             if (printBracketes)
     547         114 :                 appendStringInfoChar(buf, ')');
     548         740 :             break;
     549          48 :         case jpiLikeRegex:
     550          48 :             if (printBracketes)
     551           0 :                 appendStringInfoChar(buf, '(');
     552             : 
     553          48 :             jspInitByBuffer(&elem, v->base, v->content.like_regex.expr);
     554          48 :             printJsonPathItem(buf, &elem, false,
     555          48 :                               operationPriority(elem.type) <=
     556          48 :                               operationPriority(v->type));
     557             : 
     558          48 :             appendBinaryStringInfo(buf, " like_regex ", 12);
     559             : 
     560          48 :             escape_json(buf, v->content.like_regex.pattern);
     561             : 
     562          48 :             if (v->content.like_regex.flags)
     563             :             {
     564          36 :                 appendBinaryStringInfo(buf, " flag \"", 7);
     565             : 
     566          36 :                 if (v->content.like_regex.flags & JSP_REGEX_ICASE)
     567          30 :                     appendStringInfoChar(buf, 'i');
     568          36 :                 if (v->content.like_regex.flags & JSP_REGEX_DOTALL)
     569          18 :                     appendStringInfoChar(buf, 's');
     570          36 :                 if (v->content.like_regex.flags & JSP_REGEX_MLINE)
     571          12 :                     appendStringInfoChar(buf, 'm');
     572          36 :                 if (v->content.like_regex.flags & JSP_REGEX_WSPACE)
     573           6 :                     appendStringInfoChar(buf, 'x');
     574          36 :                 if (v->content.like_regex.flags & JSP_REGEX_QUOTE)
     575          18 :                     appendStringInfoChar(buf, 'q');
     576             : 
     577          36 :                 appendStringInfoChar(buf, '"');
     578             :             }
     579             : 
     580          48 :             if (printBracketes)
     581           0 :                 appendStringInfoChar(buf, ')');
     582          48 :             break;
     583          48 :         case jpiPlus:
     584             :         case jpiMinus:
     585          48 :             if (printBracketes)
     586          18 :                 appendStringInfoChar(buf, '(');
     587          48 :             appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
     588          48 :             jspGetArg(v, &elem);
     589          48 :             printJsonPathItem(buf, &elem, false,
     590          48 :                               operationPriority(elem.type) <=
     591          48 :                               operationPriority(v->type));
     592          48 :             if (printBracketes)
     593          18 :                 appendStringInfoChar(buf, ')');
     594          48 :             break;
     595         506 :         case jpiFilter:
     596         506 :             appendBinaryStringInfo(buf, "?(", 2);
     597         506 :             jspGetArg(v, &elem);
     598         506 :             printJsonPathItem(buf, &elem, false, false);
     599         506 :             appendStringInfoChar(buf, ')');
     600         506 :             break;
     601          12 :         case jpiNot:
     602          12 :             appendBinaryStringInfo(buf, "!(", 2);
     603          12 :             jspGetArg(v, &elem);
     604          12 :             printJsonPathItem(buf, &elem, false, false);
     605          12 :             appendStringInfoChar(buf, ')');
     606          12 :             break;
     607           6 :         case jpiIsUnknown:
     608           6 :             appendStringInfoChar(buf, '(');
     609           6 :             jspGetArg(v, &elem);
     610           6 :             printJsonPathItem(buf, &elem, false, false);
     611           6 :             appendBinaryStringInfo(buf, ") is unknown", 12);
     612           6 :             break;
     613          24 :         case jpiExists:
     614          24 :             appendBinaryStringInfo(buf, "exists (", 8);
     615          24 :             jspGetArg(v, &elem);
     616          24 :             printJsonPathItem(buf, &elem, false, false);
     617          24 :             appendStringInfoChar(buf, ')');
     618          24 :             break;
     619         578 :         case jpiCurrent:
     620             :             Assert(!inKey);
     621         578 :             appendStringInfoChar(buf, '@');
     622         578 :             break;
     623        1346 :         case jpiRoot:
     624             :             Assert(!inKey);
     625        1346 :             appendStringInfoChar(buf, '$');
     626        1346 :             break;
     627          12 :         case jpiLast:
     628          12 :             appendBinaryStringInfo(buf, "last", 4);
     629          12 :             break;
     630         146 :         case jpiAnyArray:
     631         146 :             appendBinaryStringInfo(buf, "[*]", 3);
     632         146 :             break;
     633          12 :         case jpiAnyKey:
     634          12 :             if (inKey)
     635          12 :                 appendStringInfoChar(buf, '.');
     636          12 :             appendStringInfoChar(buf, '*');
     637          12 :             break;
     638          84 :         case jpiIndexArray:
     639          84 :             appendStringInfoChar(buf, '[');
     640         186 :             for (i = 0; i < v->content.array.nelems; i++)
     641             :             {
     642             :                 JsonPathItem from;
     643             :                 JsonPathItem to;
     644         102 :                 bool        range = jspGetArraySubscript(v, &from, &to, i);
     645             : 
     646         102 :                 if (i)
     647          18 :                     appendStringInfoChar(buf, ',');
     648             : 
     649         102 :                 printJsonPathItem(buf, &from, false, false);
     650             : 
     651         102 :                 if (range)
     652             :                 {
     653          12 :                     appendBinaryStringInfo(buf, " to ", 4);
     654          12 :                     printJsonPathItem(buf, &to, false, false);
     655             :                 }
     656             :             }
     657          84 :             appendStringInfoChar(buf, ']');
     658          84 :             break;
     659          48 :         case jpiAny:
     660          48 :             if (inKey)
     661          48 :                 appendStringInfoChar(buf, '.');
     662             : 
     663          48 :             if (v->content.anybounds.first == 0 &&
     664          12 :                 v->content.anybounds.last == PG_UINT32_MAX)
     665           6 :                 appendBinaryStringInfo(buf, "**", 2);
     666          42 :             else if (v->content.anybounds.first == v->content.anybounds.last)
     667             :             {
     668          18 :                 if (v->content.anybounds.first == PG_UINT32_MAX)
     669           6 :                     appendStringInfoString(buf, "**{last}");
     670             :                 else
     671          12 :                     appendStringInfo(buf, "**{%u}",
     672             :                                      v->content.anybounds.first);
     673             :             }
     674          24 :             else if (v->content.anybounds.first == PG_UINT32_MAX)
     675           6 :                 appendStringInfo(buf, "**{last to %u}",
     676             :                                  v->content.anybounds.last);
     677          18 :             else if (v->content.anybounds.last == PG_UINT32_MAX)
     678           6 :                 appendStringInfo(buf, "**{%u to last}",
     679             :                                  v->content.anybounds.first);
     680             :             else
     681          12 :                 appendStringInfo(buf, "**{%u to %u}",
     682             :                                  v->content.anybounds.first,
     683             :                                  v->content.anybounds.last);
     684          48 :             break;
     685          30 :         case jpiType:
     686          30 :             appendBinaryStringInfo(buf, ".type()", 7);
     687          30 :             break;
     688           6 :         case jpiSize:
     689           6 :             appendBinaryStringInfo(buf, ".size()", 7);
     690           6 :             break;
     691           6 :         case jpiAbs:
     692           6 :             appendBinaryStringInfo(buf, ".abs()", 6);
     693           6 :             break;
     694           6 :         case jpiFloor:
     695           6 :             appendBinaryStringInfo(buf, ".floor()", 8);
     696           6 :             break;
     697           6 :         case jpiCeiling:
     698           6 :             appendBinaryStringInfo(buf, ".ceiling()", 10);
     699           6 :             break;
     700           6 :         case jpiDouble:
     701           6 :             appendBinaryStringInfo(buf, ".double()", 9);
     702           6 :             break;
     703          12 :         case jpiDatetime:
     704          12 :             appendBinaryStringInfo(buf, ".datetime(", 10);
     705          12 :             if (v->content.arg)
     706             :             {
     707           6 :                 jspGetArg(v, &elem);
     708           6 :                 printJsonPathItem(buf, &elem, false, false);
     709             :             }
     710          12 :             appendStringInfoChar(buf, ')');
     711          12 :             break;
     712           6 :         case jpiKeyValue:
     713           6 :             appendBinaryStringInfo(buf, ".keyvalue()", 11);
     714           6 :             break;
     715           0 :         default:
     716           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
     717             :     }
     718             : 
     719        5954 :     if (jspGetNext(v, &elem))
     720        2118 :         printJsonPathItem(buf, &elem, true, true);
     721        5954 : }
     722             : 
     723             : const char *
     724         902 : jspOperationName(JsonPathItemType type)
     725             : {
     726         902 :     switch (type)
     727             :     {
     728          30 :         case jpiAnd:
     729          30 :             return "&&";
     730          54 :         case jpiOr:
     731          54 :             return "||";
     732         162 :         case jpiEqual:
     733         162 :             return "==";
     734           6 :         case jpiNotEqual:
     735           6 :             return "!=";
     736         300 :         case jpiLess:
     737         300 :             return "<";
     738          38 :         case jpiGreater:
     739          38 :             return ">";
     740           6 :         case jpiLessOrEqual:
     741           6 :             return "<=";
     742          30 :         case jpiGreaterOrEqual:
     743          30 :             return ">=";
     744          72 :         case jpiPlus:
     745             :         case jpiAdd:
     746          72 :             return "+";
     747          24 :         case jpiMinus:
     748             :         case jpiSub:
     749          24 :             return "-";
     750          24 :         case jpiMul:
     751          24 :             return "*";
     752           6 :         case jpiDiv:
     753           6 :             return "/";
     754           6 :         case jpiMod:
     755           6 :             return "%";
     756          12 :         case jpiStartsWith:
     757          12 :             return "starts with";
     758           0 :         case jpiLikeRegex:
     759           0 :             return "like_regex";
     760           0 :         case jpiType:
     761           0 :             return "type";
     762           6 :         case jpiSize:
     763           6 :             return "size";
     764          18 :         case jpiKeyValue:
     765          18 :             return "keyvalue";
     766          60 :         case jpiDouble:
     767          60 :             return "double";
     768           6 :         case jpiAbs:
     769           6 :             return "abs";
     770           6 :         case jpiFloor:
     771           6 :             return "floor";
     772           6 :         case jpiCeiling:
     773           6 :             return "ceiling";
     774          30 :         case jpiDatetime:
     775          30 :             return "datetime";
     776           0 :         default:
     777           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", type);
     778             :             return NULL;
     779             :     }
     780             : }
     781             : 
     782             : static int
     783        3152 : operationPriority(JsonPathItemType op)
     784             : {
     785        3152 :     switch (op)
     786             :     {
     787         114 :         case jpiOr:
     788         114 :             return 0;
     789          78 :         case jpiAnd:
     790          78 :             return 1;
     791        1234 :         case jpiEqual:
     792             :         case jpiNotEqual:
     793             :         case jpiLess:
     794             :         case jpiGreater:
     795             :         case jpiLessOrEqual:
     796             :         case jpiGreaterOrEqual:
     797             :         case jpiStartsWith:
     798        1234 :             return 2;
     799         180 :         case jpiAdd:
     800             :         case jpiSub:
     801         180 :             return 3;
     802          66 :         case jpiMul:
     803             :         case jpiDiv:
     804             :         case jpiMod:
     805          66 :             return 4;
     806          84 :         case jpiPlus:
     807             :         case jpiMinus:
     808          84 :             return 5;
     809        1396 :         default:
     810        1396 :             return 6;
     811             :     }
     812             : }
     813             : 
     814             : /******************* Support functions for JsonPath *************************/
     815             : 
     816             : /*
     817             :  * Support macros to read stored values
     818             :  */
     819             : 
     820             : #define read_byte(v, b, p) do {         \
     821             :     (v) = *(uint8*)((b) + (p));         \
     822             :     (p) += 1;                           \
     823             : } while(0)                              \
     824             : 
     825             : #define read_int32(v, b, p) do {        \
     826             :     (v) = *(uint32*)((b) + (p));        \
     827             :     (p) += sizeof(int32);               \
     828             : } while(0)                              \
     829             : 
     830             : #define read_int32_n(v, b, p, n) do {   \
     831             :     (v) = (void *)((b) + (p));          \
     832             :     (p) += sizeof(int32) * (n);         \
     833             : } while(0)                              \
     834             : 
     835             : /*
     836             :  * Read root node and fill root node representation
     837             :  */
     838             : void
     839      799646 : jspInit(JsonPathItem *v, JsonPath *js)
     840             : {
     841             :     Assert((js->header & ~JSONPATH_LAX) == JSONPATH_VERSION);
     842      799646 :     jspInitByBuffer(v, js->data, 0);
     843      799646 : }
     844             : 
     845             : /*
     846             :  * Read node from buffer and fill its representation
     847             :  */
     848             : void
     849     1268396 : jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
     850             : {
     851     1268396 :     v->base = base + pos;
     852             : 
     853     1268396 :     read_byte(v->type, base, pos);
     854     1268396 :     pos = INTALIGN((uintptr_t) (base + pos)) - (uintptr_t) base;
     855     1268396 :     read_int32(v->nextPos, base, pos);
     856             : 
     857     1268396 :     switch (v->type)
     858             :     {
     859      846132 :         case jpiNull:
     860             :         case jpiRoot:
     861             :         case jpiCurrent:
     862             :         case jpiAnyArray:
     863             :         case jpiAnyKey:
     864             :         case jpiType:
     865             :         case jpiSize:
     866             :         case jpiAbs:
     867             :         case jpiFloor:
     868             :         case jpiCeiling:
     869             :         case jpiDouble:
     870             :         case jpiKeyValue:
     871             :         case jpiLast:
     872      846132 :             break;
     873      200432 :         case jpiKey:
     874             :         case jpiString:
     875             :         case jpiVariable:
     876      200432 :             read_int32(v->content.value.datalen, base, pos);
     877             :             /* FALLTHROUGH */
     878      222292 :         case jpiNumeric:
     879             :         case jpiBool:
     880      222292 :             v->content.value.data = base + pos;
     881      222292 :             break;
     882       97946 :         case jpiAnd:
     883             :         case jpiOr:
     884             :         case jpiAdd:
     885             :         case jpiSub:
     886             :         case jpiMul:
     887             :         case jpiDiv:
     888             :         case jpiMod:
     889             :         case jpiEqual:
     890             :         case jpiNotEqual:
     891             :         case jpiLess:
     892             :         case jpiGreater:
     893             :         case jpiLessOrEqual:
     894             :         case jpiGreaterOrEqual:
     895             :         case jpiStartsWith:
     896       97946 :             read_int32(v->content.args.left, base, pos);
     897       97946 :             read_int32(v->content.args.right, base, pos);
     898       97946 :             break;
     899         444 :         case jpiLikeRegex:
     900         444 :             read_int32(v->content.like_regex.flags, base, pos);
     901         444 :             read_int32(v->content.like_regex.expr, base, pos);
     902         444 :             read_int32(v->content.like_regex.patternlen, base, pos);
     903         444 :             v->content.like_regex.pattern = base + pos;
     904         444 :             break;
     905      100784 :         case jpiNot:
     906             :         case jpiExists:
     907             :         case jpiIsUnknown:
     908             :         case jpiPlus:
     909             :         case jpiMinus:
     910             :         case jpiFilter:
     911             :         case jpiDatetime:
     912      100784 :             read_int32(v->content.arg, base, pos);
     913      100784 :             break;
     914         444 :         case jpiIndexArray:
     915         444 :             read_int32(v->content.array.nelems, base, pos);
     916         444 :             read_int32_n(v->content.array.elems, base, pos,
     917             :                          v->content.array.nelems * 2);
     918         444 :             break;
     919         354 :         case jpiAny:
     920         354 :             read_int32(v->content.anybounds.first, base, pos);
     921         354 :             read_int32(v->content.anybounds.last, base, pos);
     922         354 :             break;
     923           0 :         default:
     924           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
     925             :     }
     926     1268396 : }
     927             : 
     928             : void
     929      102668 : jspGetArg(JsonPathItem *v, JsonPathItem *a)
     930             : {
     931             :     Assert(v->type == jpiFilter ||
     932             :            v->type == jpiNot ||
     933             :            v->type == jpiIsUnknown ||
     934             :            v->type == jpiExists ||
     935             :            v->type == jpiPlus ||
     936             :            v->type == jpiMinus ||
     937             :            v->type == jpiDatetime);
     938             : 
     939      102668 :     jspInitByBuffer(a, v->base, v->content.arg);
     940      102668 : }
     941             : 
     942             : bool
     943     1037444 : jspGetNext(JsonPathItem *v, JsonPathItem *a)
     944             : {
     945     1037444 :     if (jspHasNext(v))
     946             :     {
     947             :         Assert(v->type == jpiString ||
     948             :                v->type == jpiNumeric ||
     949             :                v->type == jpiBool ||
     950             :                v->type == jpiNull ||
     951             :                v->type == jpiKey ||
     952             :                v->type == jpiAny ||
     953             :                v->type == jpiAnyArray ||
     954             :                v->type == jpiAnyKey ||
     955             :                v->type == jpiIndexArray ||
     956             :                v->type == jpiFilter ||
     957             :                v->type == jpiCurrent ||
     958             :                v->type == jpiExists ||
     959             :                v->type == jpiRoot ||
     960             :                v->type == jpiVariable ||
     961             :                v->type == jpiLast ||
     962             :                v->type == jpiAdd ||
     963             :                v->type == jpiSub ||
     964             :                v->type == jpiMul ||
     965             :                v->type == jpiDiv ||
     966             :                v->type == jpiMod ||
     967             :                v->type == jpiPlus ||
     968             :                v->type == jpiMinus ||
     969             :                v->type == jpiEqual ||
     970             :                v->type == jpiNotEqual ||
     971             :                v->type == jpiGreater ||
     972             :                v->type == jpiGreaterOrEqual ||
     973             :                v->type == jpiLess ||
     974             :                v->type == jpiLessOrEqual ||
     975             :                v->type == jpiAnd ||
     976             :                v->type == jpiOr ||
     977             :                v->type == jpiNot ||
     978             :                v->type == jpiIsUnknown ||
     979             :                v->type == jpiType ||
     980             :                v->type == jpiSize ||
     981             :                v->type == jpiAbs ||
     982             :                v->type == jpiFloor ||
     983             :                v->type == jpiCeiling ||
     984             :                v->type == jpiDouble ||
     985             :                v->type == jpiDatetime ||
     986             :                v->type == jpiKeyValue ||
     987             :                v->type == jpiStartsWith);
     988             : 
     989      194520 :         if (a)
     990      194520 :             jspInitByBuffer(a, v->base, v->nextPos);
     991      194520 :         return true;
     992             :     }
     993             : 
     994      842924 :     return false;
     995             : }
     996             : 
     997             : void
     998       97946 : jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
     999             : {
    1000             :     Assert(v->type == jpiAnd ||
    1001             :            v->type == jpiOr ||
    1002             :            v->type == jpiEqual ||
    1003             :            v->type == jpiNotEqual ||
    1004             :            v->type == jpiLess ||
    1005             :            v->type == jpiGreater ||
    1006             :            v->type == jpiLessOrEqual ||
    1007             :            v->type == jpiGreaterOrEqual ||
    1008             :            v->type == jpiAdd ||
    1009             :            v->type == jpiSub ||
    1010             :            v->type == jpiMul ||
    1011             :            v->type == jpiDiv ||
    1012             :            v->type == jpiMod ||
    1013             :            v->type == jpiStartsWith);
    1014             : 
    1015       97946 :     jspInitByBuffer(a, v->base, v->content.args.left);
    1016       97946 : }
    1017             : 
    1018             : void
    1019       72650 : jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
    1020             : {
    1021             :     Assert(v->type == jpiAnd ||
    1022             :            v->type == jpiOr ||
    1023             :            v->type == jpiEqual ||
    1024             :            v->type == jpiNotEqual ||
    1025             :            v->type == jpiLess ||
    1026             :            v->type == jpiGreater ||
    1027             :            v->type == jpiLessOrEqual ||
    1028             :            v->type == jpiGreaterOrEqual ||
    1029             :            v->type == jpiAdd ||
    1030             :            v->type == jpiSub ||
    1031             :            v->type == jpiMul ||
    1032             :            v->type == jpiDiv ||
    1033             :            v->type == jpiMod ||
    1034             :            v->type == jpiStartsWith);
    1035             : 
    1036       72650 :     jspInitByBuffer(a, v->base, v->content.args.right);
    1037       72650 : }
    1038             : 
    1039             : bool
    1040        1416 : jspGetBool(JsonPathItem *v)
    1041             : {
    1042             :     Assert(v->type == jpiBool);
    1043             : 
    1044        1416 :     return (bool) *v->content.value.data;
    1045             : }
    1046             : 
    1047             : Numeric
    1048       20246 : jspGetNumeric(JsonPathItem *v)
    1049             : {
    1050             :     Assert(v->type == jpiNumeric);
    1051             : 
    1052       20246 :     return (Numeric) v->content.value.data;
    1053             : }
    1054             : 
    1055             : char *
    1056      199658 : jspGetString(JsonPathItem *v, int32 *len)
    1057             : {
    1058             :     Assert(v->type == jpiKey ||
    1059             :            v->type == jpiString ||
    1060             :            v->type == jpiVariable);
    1061             : 
    1062      199658 :     if (len)
    1063      198216 :         *len = v->content.value.datalen;
    1064      199658 :     return v->content.value.data;
    1065             : }
    1066             : 
    1067             : bool
    1068         474 : jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to,
    1069             :                      int i)
    1070             : {
    1071             :     Assert(v->type == jpiIndexArray);
    1072             : 
    1073         474 :     jspInitByBuffer(from, v->base, v->content.array.elems[i].from);
    1074             : 
    1075         474 :     if (!v->content.array.elems[i].to)
    1076         426 :         return false;
    1077             : 
    1078          48 :     jspInitByBuffer(to, v->base, v->content.array.elems[i].to);
    1079             : 
    1080          48 :     return true;
    1081             : }
    1082             : 
    1083             : /* SQL/JSON datatype status: */
    1084             : typedef enum JsonPathDatatypeStatus
    1085             : {
    1086             :     jpdsNonDateTime,            /* null, bool, numeric, string, array, object */
    1087             :     jpdsUnknownDateTime,        /* unknown datetime type */
    1088             :     jpdsDateTimeZoned,          /* timetz, timestamptz */
    1089             :     jpdsDateTimeNonZoned        /* time, timestamp, date */
    1090             : } JsonPathDatatypeStatus;
    1091             : 
    1092             : /* Context for jspIsMutableWalker() */
    1093             : typedef struct JsonPathMutableContext
    1094             : {
    1095             :     List       *varnames;       /* list of variable names */
    1096             :     List       *varexprs;       /* list of variable expressions */
    1097             :     JsonPathDatatypeStatus current; /* status of @ item */
    1098             :     bool        lax;            /* jsonpath is lax or strict */
    1099             :     bool        mutable;        /* resulting mutability status */
    1100             : } JsonPathMutableContext;
    1101             : 
    1102             : /*
    1103             :  * Recursive walker for jspIsMutable()
    1104             :  */
    1105             : static JsonPathDatatypeStatus
    1106         402 : jspIsMutableWalker(JsonPathItem *jpi, JsonPathMutableContext *cxt)
    1107             : {
    1108             :     JsonPathItem next;
    1109         402 :     JsonPathDatatypeStatus status = jpdsNonDateTime;
    1110             : 
    1111         678 :     while (!cxt->mutable)
    1112             :     {
    1113             :         JsonPathItem arg;
    1114             :         JsonPathDatatypeStatus leftStatus;
    1115             :         JsonPathDatatypeStatus rightStatus;
    1116             : 
    1117         672 :         switch (jpi->type)
    1118             :         {
    1119         162 :             case jpiRoot:
    1120             :                 Assert(status == jpdsNonDateTime);
    1121         162 :                 break;
    1122             : 
    1123          66 :             case jpiCurrent:
    1124             :                 Assert(status == jpdsNonDateTime);
    1125          66 :                 status = cxt->current;
    1126          66 :                 break;
    1127             : 
    1128          66 :             case jpiFilter:
    1129             :                 {
    1130          66 :                     JsonPathDatatypeStatus prevStatus = cxt->current;
    1131             : 
    1132          66 :                     cxt->current = status;
    1133          66 :                     jspGetArg(jpi, &arg);
    1134          66 :                     jspIsMutableWalker(&arg, cxt);
    1135             : 
    1136          66 :                     cxt->current = prevStatus;
    1137          66 :                     break;
    1138             :                 }
    1139             : 
    1140          54 :             case jpiVariable:
    1141             :                 {
    1142             :                     int32       len;
    1143          54 :                     const char *name = jspGetString(jpi, &len);
    1144             :                     ListCell   *lc1;
    1145             :                     ListCell   *lc2;
    1146             : 
    1147             :                     Assert(status == jpdsNonDateTime);
    1148             : 
    1149          60 :                     forboth(lc1, cxt->varnames, lc2, cxt->varexprs)
    1150             :                     {
    1151          54 :                         String     *varname = lfirst_node(String, lc1);
    1152          54 :                         Node       *varexpr = lfirst(lc2);
    1153             : 
    1154          54 :                         if (strncmp(varname->sval, name, len))
    1155           6 :                             continue;
    1156             : 
    1157          48 :                         switch (exprType(varexpr))
    1158             :                         {
    1159          30 :                             case DATEOID:
    1160             :                             case TIMEOID:
    1161             :                             case TIMESTAMPOID:
    1162          30 :                                 status = jpdsDateTimeNonZoned;
    1163          30 :                                 break;
    1164             : 
    1165          12 :                             case TIMETZOID:
    1166             :                             case TIMESTAMPTZOID:
    1167          12 :                                 status = jpdsDateTimeZoned;
    1168          12 :                                 break;
    1169             : 
    1170           6 :                             default:
    1171           6 :                                 status = jpdsNonDateTime;
    1172           6 :                                 break;
    1173             :                         }
    1174             : 
    1175          48 :                         break;
    1176             :                     }
    1177          54 :                     break;
    1178             :                 }
    1179             : 
    1180          90 :             case jpiEqual:
    1181             :             case jpiNotEqual:
    1182             :             case jpiLess:
    1183             :             case jpiGreater:
    1184             :             case jpiLessOrEqual:
    1185             :             case jpiGreaterOrEqual:
    1186             :                 Assert(status == jpdsNonDateTime);
    1187          90 :                 jspGetLeftArg(jpi, &arg);
    1188          90 :                 leftStatus = jspIsMutableWalker(&arg, cxt);
    1189             : 
    1190          90 :                 jspGetRightArg(jpi, &arg);
    1191          90 :                 rightStatus = jspIsMutableWalker(&arg, cxt);
    1192             : 
    1193             :                 /*
    1194             :                  * Comparison of datetime type with different timezone status
    1195             :                  * is mutable.
    1196             :                  */
    1197          90 :                 if (leftStatus != jpdsNonDateTime &&
    1198          72 :                     rightStatus != jpdsNonDateTime &&
    1199          36 :                     (leftStatus == jpdsUnknownDateTime ||
    1200          36 :                      rightStatus == jpdsUnknownDateTime ||
    1201             :                      leftStatus != rightStatus))
    1202          42 :                     cxt->mutable = true;
    1203          90 :                 break;
    1204             : 
    1205           0 :             case jpiNot:
    1206             :             case jpiIsUnknown:
    1207             :             case jpiExists:
    1208             :             case jpiPlus:
    1209             :             case jpiMinus:
    1210             :                 Assert(status == jpdsNonDateTime);
    1211           0 :                 jspGetArg(jpi, &arg);
    1212           0 :                 jspIsMutableWalker(&arg, cxt);
    1213           0 :                 break;
    1214             : 
    1215           0 :             case jpiAnd:
    1216             :             case jpiOr:
    1217             :             case jpiAdd:
    1218             :             case jpiSub:
    1219             :             case jpiMul:
    1220             :             case jpiDiv:
    1221             :             case jpiMod:
    1222             :             case jpiStartsWith:
    1223             :                 Assert(status == jpdsNonDateTime);
    1224           0 :                 jspGetLeftArg(jpi, &arg);
    1225           0 :                 jspIsMutableWalker(&arg, cxt);
    1226           0 :                 jspGetRightArg(jpi, &arg);
    1227           0 :                 jspIsMutableWalker(&arg, cxt);
    1228           0 :                 break;
    1229             : 
    1230          24 :             case jpiIndexArray:
    1231          66 :                 for (int i = 0; i < jpi->content.array.nelems; i++)
    1232             :                 {
    1233             :                     JsonPathItem from;
    1234             :                     JsonPathItem to;
    1235             : 
    1236          42 :                     if (jspGetArraySubscript(jpi, &from, &to, i))
    1237           6 :                         jspIsMutableWalker(&to, cxt);
    1238             : 
    1239          42 :                     jspIsMutableWalker(&from, cxt);
    1240             :                 }
    1241             :                 /* FALLTHROUGH */
    1242             : 
    1243             :             case jpiAnyArray:
    1244          24 :                 if (!cxt->lax)
    1245           0 :                     status = jpdsNonDateTime;
    1246          24 :                 break;
    1247             : 
    1248           0 :             case jpiAny:
    1249           0 :                 if (jpi->content.anybounds.first > 0)
    1250           0 :                     status = jpdsNonDateTime;
    1251           0 :                 break;
    1252             : 
    1253         126 :             case jpiDatetime:
    1254         126 :                 if (jpi->content.arg)
    1255             :                 {
    1256             :                     char       *template;
    1257             :                     int         flags;
    1258             : 
    1259          66 :                     jspGetArg(jpi, &arg);
    1260          66 :                     if (arg.type != jpiString)
    1261             :                     {
    1262           0 :                         status = jpdsNonDateTime;
    1263           0 :                         break;  /* there will be runtime error */
    1264             :                     }
    1265             : 
    1266          66 :                     template = jspGetString(&arg, NULL);
    1267          66 :                     flags = datetime_format_flags(template, NULL);
    1268          66 :                     if (flags & DCH_ZONED)
    1269          36 :                         status = jpdsDateTimeZoned;
    1270             :                     else
    1271          30 :                         status = jpdsDateTimeNonZoned;
    1272             :                 }
    1273             :                 else
    1274             :                 {
    1275          60 :                     status = jpdsUnknownDateTime;
    1276             :                 }
    1277         126 :                 break;
    1278             : 
    1279           0 :             case jpiLikeRegex:
    1280             :                 Assert(status == jpdsNonDateTime);
    1281           0 :                 jspInitByBuffer(&arg, jpi->base, jpi->content.like_regex.expr);
    1282           0 :                 jspIsMutableWalker(&arg, cxt);
    1283           0 :                 break;
    1284             : 
    1285             :                 /* literals */
    1286          84 :             case jpiNull:
    1287             :             case jpiString:
    1288             :             case jpiNumeric:
    1289             :             case jpiBool:
    1290             :                 /* accessors */
    1291             :             case jpiKey:
    1292             :             case jpiAnyKey:
    1293             :                 /* special items */
    1294             :             case jpiSubscript:
    1295             :             case jpiLast:
    1296             :                 /* item methods */
    1297             :             case jpiType:
    1298             :             case jpiSize:
    1299             :             case jpiAbs:
    1300             :             case jpiFloor:
    1301             :             case jpiCeiling:
    1302             :             case jpiDouble:
    1303             :             case jpiKeyValue:
    1304          84 :                 status = jpdsNonDateTime;
    1305          84 :                 break;
    1306             :         }
    1307             : 
    1308         672 :         if (!jspGetNext(jpi, &next))
    1309         396 :             break;
    1310             : 
    1311         276 :         jpi = &next;
    1312             :     }
    1313             : 
    1314         402 :     return status;
    1315             : }
    1316             : 
    1317             : /*
    1318             :  * Check whether jsonpath expression is immutable or not.
    1319             :  */
    1320             : bool
    1321         108 : jspIsMutable(JsonPath *path, List *varnames, List *varexprs)
    1322             : {
    1323             :     JsonPathMutableContext cxt;
    1324             :     JsonPathItem jpi;
    1325             : 
    1326         108 :     cxt.varnames = varnames;
    1327         108 :     cxt.varexprs = varexprs;
    1328         108 :     cxt.current = jpdsNonDateTime;
    1329         108 :     cxt.lax = (path->header & JSONPATH_LAX) != 0;
    1330         108 :     cxt.mutable = false;
    1331             : 
    1332         108 :     jspInit(&jpi, path);
    1333         108 :     jspIsMutableWalker(&jpi, &cxt);
    1334             : 
    1335         108 :     return cxt.mutable;
    1336             : }

Generated by: LCOV version 1.14