LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 91.0 % 679 618
Test Date: 2026-01-26 10:56:24 Functions: 91.7 % 24 22
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 81.1 % 588 477

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

Generated by: LCOV version 2.3.2-1