LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 91.8 % 754 692
Test Date: 2026-07-22 12:15:41 Functions: 91.7 % 24 22
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 84.4 % 353 298

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

Generated by: LCOV version 2.0-1