LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 447 478 93.5 %
Date: 2020-06-03 11:07:14 Functions: 20 22 90.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * jsonpath.c
       4             :  *   Input/output and supporting routines for jsonpath
       5             :  *
       6             :  * jsonpath expression is a chain of path items.  First path item is $, $var,
       7             :  * literal or arithmetic expression.  Subsequent path items are accessors
       8             :  * (.key, .*, [subscripts], [*]), filters (? (predicate)) and methods (.type(),
       9             :  * .size() etc).
      10             :  *
      11             :  * For instance, structure of path items for simple expression:
      12             :  *
      13             :  *      $.a[*].type()
      14             :  *
      15             :  * is pretty evident:
      16             :  *
      17             :  *      $ => .a => [*] => .type()
      18             :  *
      19             :  * Some path items such as arithmetic operations, predicates or array
      20             :  * subscripts may comprise subtrees.  For instance, more complex expression
      21             :  *
      22             :  *      ($.a + $[1 to 5, 7] ? (@ > 3).double()).type()
      23             :  *
      24             :  * have following structure of path items:
      25             :  *
      26             :  *            +  =>  .type()
      27             :  *        ___/ \___
      28             :  *       /         \
      29             :  *      $ => .a  $  =>  []  => ?  =>  .double()
      30             :  *                        _||_      |
      31             :  *                       /    \     >
      32             :  *                      to    to   / \
      33             :  *                     / \    /   @   3
      34             :  *                    1   5  7
      35             :  *
      36             :  * Binary encoding of jsonpath constitutes a sequence of 4-bytes aligned
      37             :  * variable-length path items connected by links.  Every item has a header
      38             :  * consisting of item type (enum JsonPathItemType) and offset of next item
      39             :  * (zero means no next item).  After the header, item may have payload
      40             :  * depending on item type.  For instance, payload of '.key' accessor item is
      41             :  * length of key name and key name itself.  Payload of '>' arithmetic operator
      42             :  * item is offsets of right and left operands.
      43             :  *
      44             :  * So, binary representation of sample expression above is:
      45             :  * (bottom arrows are next links, top lines are argument links)
      46             :  *
      47             :  *                                _____
      48             :  *       _____                ___/____ \                __
      49             :  *    _ /_    \         _____/__/____ \ \      __    _ /_ \
      50             :  *   / /  \    \       /    /  /     \ \ \    /  \  / /  \ \
      51             :  * +(LR)  $ .a  $  [](* to *, * to *) 1 5 7 ?(A)  >(LR)   @ 3 .double() .type()
      52             :  * |      |  ^  |  ^|                        ^|                   ^        ^
      53             :  * |      |__|  |__||________________________||___________________|        |
      54             :  * |_______________________________________________________________________|
      55             :  *
      56             :  * Copyright (c) 2019-2020, PostgreSQL Global Development Group
      57             :  *
      58             :  * IDENTIFICATION
      59             :  *  src/backend/utils/adt/jsonpath.c
      60             :  *
      61             :  *-------------------------------------------------------------------------
      62             :  */
      63             : 
      64             : #include "postgres.h"
      65             : 
      66             : #include "funcapi.h"
      67             : #include "lib/stringinfo.h"
      68             : #include "libpq/pqformat.h"
      69             : #include "miscadmin.h"
      70             : #include "utils/builtins.h"
      71             : #include "utils/json.h"
      72             : #include "utils/jsonpath.h"
      73             : 
      74             : 
      75             : static Datum jsonPathFromCstring(char *in, int len);
      76             : static char *jsonPathToCstring(StringInfo out, JsonPath *in,
      77             :                                int estimated_len);
      78             : static int  flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
      79             :                                      int nestingLevel, bool insideArraySubscript);
      80             : static void alignStringInfoInt(StringInfo buf);
      81             : static int32 reserveSpaceForItemPointer(StringInfo buf);
      82             : static void printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
      83             :                               bool printBracketes);
      84             : static int  operationPriority(JsonPathItemType op);
      85             : 
      86             : 
      87             : /**************************** INPUT/OUTPUT ********************************/
      88             : 
      89             : /*
      90             :  * jsonpath type input function
      91             :  */
      92             : Datum
      93        2768 : jsonpath_in(PG_FUNCTION_ARGS)
      94             : {
      95        2768 :     char       *in = PG_GETARG_CSTRING(0);
      96        2768 :     int         len = strlen(in);
      97             : 
      98        2768 :     return jsonPathFromCstring(in, len);
      99             : }
     100             : 
     101             : /*
     102             :  * jsonpath type recv function
     103             :  *
     104             :  * The type is sent as text in binary mode, so this is almost the same
     105             :  * as the input function, but it's prefixed with a version number so we
     106             :  * can change the binary format sent in future if necessary. For now,
     107             :  * only version 1 is supported.
     108             :  */
     109             : Datum
     110           0 : jsonpath_recv(PG_FUNCTION_ARGS)
     111             : {
     112           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     113           0 :     int         version = pq_getmsgint(buf, 1);
     114             :     char       *str;
     115             :     int         nbytes;
     116             : 
     117           0 :     if (version == JSONPATH_VERSION)
     118           0 :         str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     119             :     else
     120           0 :         elog(ERROR, "unsupported jsonpath version number: %d", version);
     121             : 
     122           0 :     return jsonPathFromCstring(str, nbytes);
     123             : }
     124             : 
     125             : /*
     126             :  * jsonpath type output function
     127             :  */
     128             : Datum
     129         644 : jsonpath_out(PG_FUNCTION_ARGS)
     130             : {
     131         644 :     JsonPath   *in = PG_GETARG_JSONPATH_P(0);
     132             : 
     133         644 :     PG_RETURN_CSTRING(jsonPathToCstring(NULL, in, VARSIZE(in)));
     134             : }
     135             : 
     136             : /*
     137             :  * jsonpath type send function
     138             :  *
     139             :  * Just send jsonpath as a version number, then a string of text
     140             :  */
     141             : Datum
     142           0 : jsonpath_send(PG_FUNCTION_ARGS)
     143             : {
     144           0 :     JsonPath   *in = PG_GETARG_JSONPATH_P(0);
     145             :     StringInfoData buf;
     146             :     StringInfoData jtext;
     147           0 :     int         version = JSONPATH_VERSION;
     148             : 
     149           0 :     initStringInfo(&jtext);
     150           0 :     (void) jsonPathToCstring(&jtext, in, VARSIZE(in));
     151             : 
     152           0 :     pq_begintypsend(&buf);
     153           0 :     pq_sendint8(&buf, version);
     154           0 :     pq_sendtext(&buf, jtext.data, jtext.len);
     155           0 :     pfree(jtext.data);
     156             : 
     157           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     158             : }
     159             : 
     160             : /*
     161             :  * Converts C-string to a jsonpath value.
     162             :  *
     163             :  * Uses jsonpath parser to turn string into an AST, then
     164             :  * flattenJsonPathParseItem() does second pass turning AST into binary
     165             :  * representation of jsonpath.
     166             :  */
     167             : static Datum
     168        2768 : jsonPathFromCstring(char *in, int len)
     169             : {
     170        2768 :     JsonPathParseResult *jsonpath = parsejsonpath(in, len);
     171             :     JsonPath   *res;
     172             :     StringInfoData buf;
     173             : 
     174        2608 :     initStringInfo(&buf);
     175        2608 :     enlargeStringInfo(&buf, 4 * len /* estimation */ );
     176             : 
     177        2608 :     appendStringInfoSpaces(&buf, JSONPATH_HDRSZ);
     178             : 
     179        2608 :     if (!jsonpath)
     180           4 :         ereport(ERROR,
     181             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     182             :                  errmsg("invalid input syntax for type %s: \"%s\"", "jsonpath",
     183             :                         in)));
     184             : 
     185        2604 :     flattenJsonPathParseItem(&buf, jsonpath->expr, 0, false);
     186             : 
     187        2592 :     res = (JsonPath *) buf.data;
     188        2592 :     SET_VARSIZE(res, buf.len);
     189        2592 :     res->header = JSONPATH_VERSION;
     190        2592 :     if (jsonpath->lax)
     191        2356 :         res->header |= JSONPATH_LAX;
     192             : 
     193        2592 :     PG_RETURN_JSONPATH_P(res);
     194             : }
     195             : 
     196             : /*
     197             :  * Converts jsonpath value to a C-string.
     198             :  *
     199             :  * If 'out' argument is non-null, the resulting C-string is stored inside the
     200             :  * StringBuffer.  The resulting string is always returned.
     201             :  */
     202             : static char *
     203         644 : jsonPathToCstring(StringInfo out, JsonPath *in, int estimated_len)
     204             : {
     205             :     StringInfoData buf;
     206             :     JsonPathItem v;
     207             : 
     208         644 :     if (!out)
     209             :     {
     210         644 :         out = &buf;
     211         644 :         initStringInfo(out);
     212             :     }
     213         644 :     enlargeStringInfo(out, estimated_len);
     214             : 
     215         644 :     if (!(in->header & JSONPATH_LAX))
     216           4 :         appendBinaryStringInfo(out, "strict ", 7);
     217             : 
     218         644 :     jspInit(&v, in);
     219         644 :     printJsonPathItem(out, &v, false, true);
     220             : 
     221         644 :     return out->data;
     222             : }
     223             : 
     224             : /*
     225             :  * Recursive function converting given jsonpath parse item and all its
     226             :  * children into a binary representation.
     227             :  */
     228             : static int
     229       12564 : flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
     230             :                          int nestingLevel, bool insideArraySubscript)
     231             : {
     232             :     /* position from beginning of jsonpath data */
     233       12564 :     int32       pos = buf->len - JSONPATH_HDRSZ;
     234             :     int32       chld;
     235             :     int32       next;
     236       12564 :     int         argNestingLevel = 0;
     237             : 
     238       12564 :     check_stack_depth();
     239       12564 :     CHECK_FOR_INTERRUPTS();
     240             : 
     241       12564 :     appendStringInfoChar(buf, (char) (item->type));
     242             : 
     243             :     /*
     244             :      * We align buffer to int32 because a series of int32 values often goes
     245             :      * after the header, and we want to read them directly by dereferencing
     246             :      * int32 pointer (see jspInitByBuffer()).
     247             :      */
     248       12564 :     alignStringInfoInt(buf);
     249             : 
     250             :     /*
     251             :      * Reserve space for next item pointer.  Actual value will be recorded
     252             :      * later, after next and children items processing.
     253             :      */
     254       12564 :     next = reserveSpaceForItemPointer(buf);
     255             : 
     256       12564 :     switch (item->type)
     257             :     {
     258        2516 :         case jpiString:
     259             :         case jpiVariable:
     260             :         case jpiKey:
     261        2516 :             appendBinaryStringInfo(buf, (char *) &item->value.string.len,
     262             :                                    sizeof(item->value.string.len));
     263        2516 :             appendBinaryStringInfo(buf, item->value.string.val,
     264        2516 :                                    item->value.string.len);
     265        2516 :             appendStringInfoChar(buf, '\0');
     266        2516 :             break;
     267        1024 :         case jpiNumeric:
     268        1024 :             appendBinaryStringInfo(buf, (char *) item->value.numeric,
     269        1024 :                                    VARSIZE(item->value.numeric));
     270        1024 :             break;
     271         104 :         case jpiBool:
     272         104 :             appendBinaryStringInfo(buf, (char *) &item->value.boolean,
     273             :                                    sizeof(item->value.boolean));
     274         104 :             break;
     275        1632 :         case jpiAnd:
     276             :         case jpiOr:
     277             :         case jpiEqual:
     278             :         case jpiNotEqual:
     279             :         case jpiLess:
     280             :         case jpiGreater:
     281             :         case jpiLessOrEqual:
     282             :         case jpiGreaterOrEqual:
     283             :         case jpiAdd:
     284             :         case jpiSub:
     285             :         case jpiMul:
     286             :         case jpiDiv:
     287             :         case jpiMod:
     288             :         case jpiStartsWith:
     289             :             {
     290             :                 /*
     291             :                  * First, reserve place for left/right arg's positions, then
     292             :                  * record both args and sets actual position in reserved
     293             :                  * places.
     294             :                  */
     295        1632 :                 int32       left = reserveSpaceForItemPointer(buf);
     296        1632 :                 int32       right = reserveSpaceForItemPointer(buf);
     297             : 
     298        1632 :                 chld = !item->value.args.left ? pos :
     299        1632 :                     flattenJsonPathParseItem(buf, item->value.args.left,
     300             :                                              nestingLevel + argNestingLevel,
     301             :                                              insideArraySubscript);
     302        1624 :                 *(int32 *) (buf->data + left) = chld - pos;
     303             : 
     304        1624 :                 chld = !item->value.args.right ? pos :
     305        1624 :                     flattenJsonPathParseItem(buf, item->value.args.right,
     306             :                                              nestingLevel + argNestingLevel,
     307             :                                              insideArraySubscript);
     308        1624 :                 *(int32 *) (buf->data + right) = chld - pos;
     309             :             }
     310        1624 :             break;
     311          72 :         case jpiLikeRegex:
     312             :             {
     313             :                 int32       offs;
     314             : 
     315          72 :                 appendBinaryStringInfo(buf,
     316          72 :                                        (char *) &item->value.like_regex.flags,
     317             :                                        sizeof(item->value.like_regex.flags));
     318          72 :                 offs = reserveSpaceForItemPointer(buf);
     319          72 :                 appendBinaryStringInfo(buf,
     320          72 :                                        (char *) &item->value.like_regex.patternlen,
     321             :                                        sizeof(item->value.like_regex.patternlen));
     322          72 :                 appendBinaryStringInfo(buf, item->value.like_regex.pattern,
     323          72 :                                        item->value.like_regex.patternlen);
     324          72 :                 appendStringInfoChar(buf, '\0');
     325             : 
     326          72 :                 chld = flattenJsonPathParseItem(buf, item->value.like_regex.expr,
     327             :                                                 nestingLevel,
     328             :                                                 insideArraySubscript);
     329          72 :                 *(int32 *) (buf->data + offs) = chld - pos;
     330             :             }
     331          72 :             break;
     332        1028 :         case jpiFilter:
     333        1028 :             argNestingLevel++;
     334             :             /* FALLTHROUGH */
     335        1856 :         case jpiIsUnknown:
     336             :         case jpiNot:
     337             :         case jpiPlus:
     338             :         case jpiMinus:
     339             :         case jpiExists:
     340             :         case jpiDatetime:
     341             :             {
     342        1856 :                 int32       arg = reserveSpaceForItemPointer(buf);
     343             : 
     344        1856 :                 chld = !item->value.arg ? pos :
     345        1640 :                     flattenJsonPathParseItem(buf, item->value.arg,
     346             :                                              nestingLevel + argNestingLevel,
     347             :                                              insideArraySubscript);
     348        1852 :                 *(int32 *) (buf->data + arg) = chld - pos;
     349             :             }
     350        1852 :             break;
     351          76 :         case jpiNull:
     352          76 :             break;
     353        2480 :         case jpiRoot:
     354        2480 :             break;
     355         736 :         case jpiAnyArray:
     356             :         case jpiAnyKey:
     357         736 :             break;
     358        1204 :         case jpiCurrent:
     359        1204 :             if (nestingLevel <= 0)
     360           4 :                 ereport(ERROR,
     361             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     362             :                          errmsg("@ is not allowed in root expressions")));
     363        1200 :             break;
     364          60 :         case jpiLast:
     365          60 :             if (!insideArraySubscript)
     366           8 :                 ereport(ERROR,
     367             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     368             :                          errmsg("LAST is allowed only in array subscripts")));
     369          52 :             break;
     370         224 :         case jpiIndexArray:
     371         224 :             {
     372         224 :                 int32       nelems = item->value.array.nelems;
     373             :                 int         offset;
     374             :                 int         i;
     375             : 
     376         224 :                 appendBinaryStringInfo(buf, (char *) &nelems, sizeof(nelems));
     377             : 
     378         224 :                 offset = buf->len;
     379             : 
     380         224 :                 appendStringInfoSpaces(buf, sizeof(int32) * 2 * nelems);
     381             : 
     382         468 :                 for (i = 0; i < nelems; i++)
     383             :                 {
     384             :                     int32      *ppos;
     385             :                     int32       topos;
     386         244 :                     int32       frompos =
     387         244 :                     flattenJsonPathParseItem(buf,
     388         244 :                                              item->value.array.elems[i].from,
     389             :                                              nestingLevel, true) - pos;
     390             : 
     391         244 :                     if (item->value.array.elems[i].to)
     392          28 :                         topos = flattenJsonPathParseItem(buf,
     393          28 :                                                          item->value.array.elems[i].to,
     394             :                                                          nestingLevel, true) - pos;
     395             :                     else
     396         216 :                         topos = 0;
     397             : 
     398         244 :                     ppos = (int32 *) &buf->data[offset + i * 2 * sizeof(int32)];
     399             : 
     400         244 :                     ppos[0] = frompos;
     401         244 :                     ppos[1] = topos;
     402             :                 }
     403             :             }
     404         224 :             break;
     405         236 :         case jpiAny:
     406         236 :             appendBinaryStringInfo(buf,
     407         236 :                                    (char *) &item->value.anybounds.first,
     408             :                                    sizeof(item->value.anybounds.first));
     409         236 :             appendBinaryStringInfo(buf,
     410         236 :                                    (char *) &item->value.anybounds.last,
     411             :                                    sizeof(item->value.anybounds.last));
     412         236 :             break;
     413         344 :         case jpiType:
     414             :         case jpiSize:
     415             :         case jpiAbs:
     416             :         case jpiFloor:
     417             :         case jpiCeiling:
     418             :         case jpiDouble:
     419             :         case jpiKeyValue:
     420         344 :             break;
     421           0 :         default:
     422           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
     423             :     }
     424             : 
     425       12540 :     if (item->next)
     426             :     {
     427        4720 :         chld = flattenJsonPathParseItem(buf, item->next, nestingLevel,
     428             :                                         insideArraySubscript) - pos;
     429        4716 :         *(int32 *) (buf->data + next) = chld;
     430             :     }
     431             : 
     432       12536 :     return pos;
     433             : }
     434             : 
     435             : /*
     436             :  * Align StringInfo to int by adding zero padding bytes
     437             :  */
     438             : static void
     439       12564 : alignStringInfoInt(StringInfo buf)
     440             : {
     441       12564 :     switch (INTALIGN(buf->len) - buf->len)
     442             :     {
     443       11016 :         case 3:
     444       11016 :             appendStringInfoCharMacro(buf, 0);
     445             :             /* FALLTHROUGH */
     446             :         case 2:
     447       11240 :             appendStringInfoCharMacro(buf, 0);
     448             :             /* FALLTHROUGH */
     449             :         case 1:
     450       12428 :             appendStringInfoCharMacro(buf, 0);
     451             :             /* FALLTHROUGH */
     452             :         default:
     453       12564 :             break;
     454             :     }
     455       12564 : }
     456             : 
     457             : /*
     458             :  * Reserve space for int32 JsonPathItem pointer.  Now zero pointer is written,
     459             :  * actual value will be recorded at '(int32 *) &buf->data[pos]' later.
     460             :  */
     461             : static int32
     462       17756 : reserveSpaceForItemPointer(StringInfo buf)
     463             : {
     464       17756 :     int32       pos = buf->len;
     465       17756 :     int32       ptr = 0;
     466             : 
     467       17756 :     appendBinaryStringInfo(buf, (char *) &ptr, sizeof(ptr));
     468             : 
     469       17756 :     return pos;
     470             : }
     471             : 
     472             : /*
     473             :  * Prints text representation of given jsonpath item and all its children.
     474             :  */
     475             : static void
     476        3068 : printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
     477             :                   bool printBracketes)
     478             : {
     479             :     JsonPathItem elem;
     480             :     int         i;
     481             : 
     482        3068 :     check_stack_depth();
     483        3068 :     CHECK_FOR_INTERRUPTS();
     484             : 
     485        3068 :     switch (v->type)
     486             :     {
     487          28 :         case jpiNull:
     488          28 :             appendStringInfoString(buf, "null");
     489          28 :             break;
     490         656 :         case jpiKey:
     491         656 :             if (inKey)
     492         656 :                 appendStringInfoChar(buf, '.');
     493         656 :             escape_json(buf, jspGetString(v, NULL));
     494         656 :             break;
     495          56 :         case jpiString:
     496          56 :             escape_json(buf, jspGetString(v, NULL));
     497          56 :             break;
     498          32 :         case jpiVariable:
     499          32 :             appendStringInfoChar(buf, '$');
     500          32 :             escape_json(buf, jspGetString(v, NULL));
     501          32 :             break;
     502         452 :         case jpiNumeric:
     503         452 :             appendStringInfoString(buf,
     504         452 :                                    DatumGetCString(DirectFunctionCall1(numeric_out,
     505             :                                                                        NumericGetDatum(jspGetNumeric(v)))));
     506         452 :             break;
     507           8 :         case jpiBool:
     508           8 :             if (jspGetBool(v))
     509           4 :                 appendBinaryStringInfo(buf, "true", 4);
     510             :             else
     511           4 :                 appendBinaryStringInfo(buf, "false", 5);
     512           8 :             break;
     513         436 :         case jpiAnd:
     514             :         case jpiOr:
     515             :         case jpiEqual:
     516             :         case jpiNotEqual:
     517             :         case jpiLess:
     518             :         case jpiGreater:
     519             :         case jpiLessOrEqual:
     520             :         case jpiGreaterOrEqual:
     521             :         case jpiAdd:
     522             :         case jpiSub:
     523             :         case jpiMul:
     524             :         case jpiDiv:
     525             :         case jpiMod:
     526             :         case jpiStartsWith:
     527         436 :             if (printBracketes)
     528          76 :                 appendStringInfoChar(buf, '(');
     529         436 :             jspGetLeftArg(v, &elem);
     530         436 :             printJsonPathItem(buf, &elem, false,
     531         436 :                               operationPriority(elem.type) <=
     532         436 :                               operationPriority(v->type));
     533         436 :             appendStringInfoChar(buf, ' ');
     534         436 :             appendStringInfoString(buf, jspOperationName(v->type));
     535         436 :             appendStringInfoChar(buf, ' ');
     536         436 :             jspGetRightArg(v, &elem);
     537         436 :             printJsonPathItem(buf, &elem, false,
     538         436 :                               operationPriority(elem.type) <=
     539         436 :                               operationPriority(v->type));
     540         436 :             if (printBracketes)
     541          76 :                 appendStringInfoChar(buf, ')');
     542         436 :             break;
     543          32 :         case jpiLikeRegex:
     544          32 :             if (printBracketes)
     545           0 :                 appendStringInfoChar(buf, '(');
     546             : 
     547          32 :             jspInitByBuffer(&elem, v->base, v->content.like_regex.expr);
     548          32 :             printJsonPathItem(buf, &elem, false,
     549          32 :                               operationPriority(elem.type) <=
     550          32 :                               operationPriority(v->type));
     551             : 
     552          32 :             appendBinaryStringInfo(buf, " like_regex ", 12);
     553             : 
     554          32 :             escape_json(buf, v->content.like_regex.pattern);
     555             : 
     556          32 :             if (v->content.like_regex.flags)
     557             :             {
     558          24 :                 appendBinaryStringInfo(buf, " flag \"", 7);
     559             : 
     560          24 :                 if (v->content.like_regex.flags & JSP_REGEX_ICASE)
     561          20 :                     appendStringInfoChar(buf, 'i');
     562          24 :                 if (v->content.like_regex.flags & JSP_REGEX_DOTALL)
     563          12 :                     appendStringInfoChar(buf, 's');
     564          24 :                 if (v->content.like_regex.flags & JSP_REGEX_MLINE)
     565           8 :                     appendStringInfoChar(buf, 'm');
     566          24 :                 if (v->content.like_regex.flags & JSP_REGEX_WSPACE)
     567           4 :                     appendStringInfoChar(buf, 'x');
     568          24 :                 if (v->content.like_regex.flags & JSP_REGEX_QUOTE)
     569          12 :                     appendStringInfoChar(buf, 'q');
     570             : 
     571          24 :                 appendStringInfoChar(buf, '"');
     572             :             }
     573             : 
     574          32 :             if (printBracketes)
     575           0 :                 appendStringInfoChar(buf, ')');
     576          32 :             break;
     577          32 :         case jpiPlus:
     578             :         case jpiMinus:
     579          32 :             if (printBracketes)
     580          12 :                 appendStringInfoChar(buf, '(');
     581          32 :             appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
     582          32 :             jspGetArg(v, &elem);
     583          32 :             printJsonPathItem(buf, &elem, false,
     584          32 :                               operationPriority(elem.type) <=
     585          32 :                               operationPriority(v->type));
     586          32 :             if (printBracketes)
     587          12 :                 appendStringInfoChar(buf, ')');
     588          32 :             break;
     589         280 :         case jpiFilter:
     590         280 :             appendBinaryStringInfo(buf, "?(", 2);
     591         280 :             jspGetArg(v, &elem);
     592         280 :             printJsonPathItem(buf, &elem, false, false);
     593         280 :             appendStringInfoChar(buf, ')');
     594         280 :             break;
     595           8 :         case jpiNot:
     596           8 :             appendBinaryStringInfo(buf, "!(", 2);
     597           8 :             jspGetArg(v, &elem);
     598           8 :             printJsonPathItem(buf, &elem, false, false);
     599           8 :             appendStringInfoChar(buf, ')');
     600           8 :             break;
     601           4 :         case jpiIsUnknown:
     602           4 :             appendStringInfoChar(buf, '(');
     603           4 :             jspGetArg(v, &elem);
     604           4 :             printJsonPathItem(buf, &elem, false, false);
     605           4 :             appendBinaryStringInfo(buf, ") is unknown", 12);
     606           4 :             break;
     607          16 :         case jpiExists:
     608          16 :             appendBinaryStringInfo(buf, "exists (", 8);
     609          16 :             jspGetArg(v, &elem);
     610          16 :             printJsonPathItem(buf, &elem, false, false);
     611          16 :             appendStringInfoChar(buf, ')');
     612          16 :             break;
     613         332 :         case jpiCurrent:
     614             :             Assert(!inKey);
     615         332 :             appendStringInfoChar(buf, '@');
     616         332 :             break;
     617         508 :         case jpiRoot:
     618             :             Assert(!inKey);
     619         508 :             appendStringInfoChar(buf, '$');
     620         508 :             break;
     621           8 :         case jpiLast:
     622           8 :             appendBinaryStringInfo(buf, "last", 4);
     623           8 :             break;
     624          44 :         case jpiAnyArray:
     625          44 :             appendBinaryStringInfo(buf, "[*]", 3);
     626          44 :             break;
     627           8 :         case jpiAnyKey:
     628           8 :             if (inKey)
     629           8 :                 appendStringInfoChar(buf, '.');
     630           8 :             appendStringInfoChar(buf, '*');
     631           8 :             break;
     632          40 :         case jpiIndexArray:
     633          40 :             appendStringInfoChar(buf, '[');
     634          92 :             for (i = 0; i < v->content.array.nelems; i++)
     635          52 :             {
     636             :                 JsonPathItem from;
     637             :                 JsonPathItem to;
     638          52 :                 bool        range = jspGetArraySubscript(v, &from, &to, i);
     639             : 
     640          52 :                 if (i)
     641          12 :                     appendStringInfoChar(buf, ',');
     642             : 
     643          52 :                 printJsonPathItem(buf, &from, false, false);
     644             : 
     645          52 :                 if (range)
     646             :                 {
     647           8 :                     appendBinaryStringInfo(buf, " to ", 4);
     648           8 :                     printJsonPathItem(buf, &to, false, false);
     649             :                 }
     650             :             }
     651          40 :             appendStringInfoChar(buf, ']');
     652          40 :             break;
     653          32 :         case jpiAny:
     654          32 :             if (inKey)
     655          32 :                 appendStringInfoChar(buf, '.');
     656             : 
     657          32 :             if (v->content.anybounds.first == 0 &&
     658           8 :                 v->content.anybounds.last == PG_UINT32_MAX)
     659           4 :                 appendBinaryStringInfo(buf, "**", 2);
     660          28 :             else if (v->content.anybounds.first == v->content.anybounds.last)
     661             :             {
     662          12 :                 if (v->content.anybounds.first == PG_UINT32_MAX)
     663           4 :                     appendStringInfo(buf, "**{last}");
     664             :                 else
     665           8 :                     appendStringInfo(buf, "**{%u}",
     666             :                                      v->content.anybounds.first);
     667             :             }
     668          16 :             else if (v->content.anybounds.first == PG_UINT32_MAX)
     669           4 :                 appendStringInfo(buf, "**{last to %u}",
     670             :                                  v->content.anybounds.last);
     671          12 :             else if (v->content.anybounds.last == PG_UINT32_MAX)
     672           4 :                 appendStringInfo(buf, "**{%u to last}",
     673             :                                  v->content.anybounds.first);
     674             :             else
     675           8 :                 appendStringInfo(buf, "**{%u to %u}",
     676             :                                  v->content.anybounds.first,
     677             :                                  v->content.anybounds.last);
     678          32 :             break;
     679          24 :         case jpiType:
     680          24 :             appendBinaryStringInfo(buf, ".type()", 7);
     681          24 :             break;
     682           4 :         case jpiSize:
     683           4 :             appendBinaryStringInfo(buf, ".size()", 7);
     684           4 :             break;
     685           4 :         case jpiAbs:
     686           4 :             appendBinaryStringInfo(buf, ".abs()", 6);
     687           4 :             break;
     688           4 :         case jpiFloor:
     689           4 :             appendBinaryStringInfo(buf, ".floor()", 8);
     690           4 :             break;
     691           4 :         case jpiCeiling:
     692           4 :             appendBinaryStringInfo(buf, ".ceiling()", 10);
     693           4 :             break;
     694           4 :         case jpiDouble:
     695           4 :             appendBinaryStringInfo(buf, ".double()", 9);
     696           4 :             break;
     697           8 :         case jpiDatetime:
     698           8 :             appendBinaryStringInfo(buf, ".datetime(", 10);
     699           8 :             if (v->content.arg)
     700             :             {
     701           4 :                 jspGetArg(v, &elem);
     702           4 :                 printJsonPathItem(buf, &elem, false, false);
     703             :             }
     704           8 :             appendStringInfoChar(buf, ')');
     705           8 :             break;
     706           4 :         case jpiKeyValue:
     707           4 :             appendBinaryStringInfo(buf, ".keyvalue()", 11);
     708           4 :             break;
     709           0 :         default:
     710           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
     711             :     }
     712             : 
     713        3068 :     if (jspGetNext(v, &elem))
     714        1116 :         printJsonPathItem(buf, &elem, true, true);
     715        3068 : }
     716             : 
     717             : const char *
     718         532 : jspOperationName(JsonPathItemType type)
     719             : {
     720         532 :     switch (type)
     721             :     {
     722          20 :         case jpiAnd:
     723          20 :             return "&&";
     724          36 :         case jpiOr:
     725          36 :             return "||";
     726         108 :         case jpiEqual:
     727         108 :             return "==";
     728           4 :         case jpiNotEqual:
     729           4 :             return "!=";
     730         152 :         case jpiLess:
     731         152 :             return "<";
     732          16 :         case jpiGreater:
     733          16 :             return ">";
     734           4 :         case jpiLessOrEqual:
     735           4 :             return "<=";
     736          20 :         case jpiGreaterOrEqual:
     737          20 :             return ">=";
     738          48 :         case jpiPlus:
     739             :         case jpiAdd:
     740          48 :             return "+";
     741          16 :         case jpiMinus:
     742             :         case jpiSub:
     743          16 :             return "-";
     744          16 :         case jpiMul:
     745          16 :             return "*";
     746           4 :         case jpiDiv:
     747           4 :             return "/";
     748           4 :         case jpiMod:
     749           4 :             return "%";
     750           8 :         case jpiStartsWith:
     751           8 :             return "starts with";
     752           0 :         case jpiLikeRegex:
     753           0 :             return "like_regex";
     754           0 :         case jpiType:
     755           0 :             return "type";
     756           4 :         case jpiSize:
     757           4 :             return "size";
     758          12 :         case jpiKeyValue:
     759          12 :             return "keyvalue";
     760          28 :         case jpiDouble:
     761          28 :             return "double";
     762           4 :         case jpiAbs:
     763           4 :             return "abs";
     764           4 :         case jpiFloor:
     765           4 :             return "floor";
     766           4 :         case jpiCeiling:
     767           4 :             return "ceiling";
     768          20 :         case jpiDatetime:
     769          20 :             return "datetime";
     770           0 :         default:
     771           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", type);
     772             :             return NULL;
     773             :     }
     774             : }
     775             : 
     776             : static int
     777        1872 : operationPriority(JsonPathItemType op)
     778             : {
     779        1872 :     switch (op)
     780             :     {
     781          76 :         case jpiOr:
     782          76 :             return 0;
     783          52 :         case jpiAnd:
     784          52 :             return 1;
     785         708 :         case jpiEqual:
     786             :         case jpiNotEqual:
     787             :         case jpiLess:
     788             :         case jpiGreater:
     789             :         case jpiLessOrEqual:
     790             :         case jpiGreaterOrEqual:
     791             :         case jpiStartsWith:
     792         708 :             return 2;
     793         120 :         case jpiAdd:
     794             :         case jpiSub:
     795         120 :             return 3;
     796          44 :         case jpiMul:
     797             :         case jpiDiv:
     798             :         case jpiMod:
     799          44 :             return 4;
     800          56 :         case jpiPlus:
     801             :         case jpiMinus:
     802          56 :             return 5;
     803         816 :         default:
     804         816 :             return 6;
     805             :     }
     806             : }
     807             : 
     808             : /******************* Support functions for JsonPath *************************/
     809             : 
     810             : /*
     811             :  * Support macros to read stored values
     812             :  */
     813             : 
     814             : #define read_byte(v, b, p) do {         \
     815             :     (v) = *(uint8*)((b) + (p));         \
     816             :     (p) += 1;                           \
     817             : } while(0)                              \
     818             : 
     819             : #define read_int32(v, b, p) do {        \
     820             :     (v) = *(uint32*)((b) + (p));        \
     821             :     (p) += sizeof(int32);               \
     822             : } while(0)                              \
     823             : 
     824             : #define read_int32_n(v, b, p, n) do {   \
     825             :     (v) = (void *)((b) + (p));          \
     826             :     (p) += sizeof(int32) * (n);         \
     827             : } while(0)                              \
     828             : 
     829             : /*
     830             :  * Read root node and fill root node representation
     831             :  */
     832             : void
     833      157312 : jspInit(JsonPathItem *v, JsonPath *js)
     834             : {
     835             :     Assert((js->header & ~JSONPATH_LAX) == JSONPATH_VERSION);
     836      157312 :     jspInitByBuffer(v, js->data, 0);
     837      157312 : }
     838             : 
     839             : /*
     840             :  * Read node from buffer and fill its representation
     841             :  */
     842             : void
     843      541108 : jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
     844             : {
     845      541108 :     v->base = base + pos;
     846             : 
     847      541108 :     read_byte(v->type, base, pos);
     848      541108 :     pos = INTALIGN((uintptr_t) (base + pos)) - (uintptr_t) base;
     849      541108 :     read_int32(v->nextPos, base, pos);
     850             : 
     851      541108 :     switch (v->type)
     852             :     {
     853      194664 :         case jpiNull:
     854             :         case jpiRoot:
     855             :         case jpiCurrent:
     856             :         case jpiAnyArray:
     857             :         case jpiAnyKey:
     858             :         case jpiType:
     859             :         case jpiSize:
     860             :         case jpiAbs:
     861             :         case jpiFloor:
     862             :         case jpiCeiling:
     863             :         case jpiDouble:
     864             :         case jpiKeyValue:
     865             :         case jpiLast:
     866      194664 :             break;
     867      165408 :         case jpiKey:
     868             :         case jpiString:
     869             :         case jpiVariable:
     870      165408 :             read_int32(v->content.value.datalen, base, pos);
     871             :             /* FALLTHROUGH */
     872      183492 :         case jpiNumeric:
     873             :         case jpiBool:
     874      183492 :             v->content.value.data = base + pos;
     875      183492 :             break;
     876       80708 :         case jpiAnd:
     877             :         case jpiOr:
     878             :         case jpiAdd:
     879             :         case jpiSub:
     880             :         case jpiMul:
     881             :         case jpiDiv:
     882             :         case jpiMod:
     883             :         case jpiEqual:
     884             :         case jpiNotEqual:
     885             :         case jpiLess:
     886             :         case jpiGreater:
     887             :         case jpiLessOrEqual:
     888             :         case jpiGreaterOrEqual:
     889             :         case jpiStartsWith:
     890       80708 :             read_int32(v->content.args.left, base, pos);
     891       80708 :             read_int32(v->content.args.right, base, pos);
     892       80708 :             break;
     893         296 :         case jpiLikeRegex:
     894         296 :             read_int32(v->content.like_regex.flags, base, pos);
     895         296 :             read_int32(v->content.like_regex.expr, base, pos);
     896         296 :             read_int32(v->content.like_regex.patternlen, base, pos);
     897         296 :             v->content.like_regex.pattern = base + pos;
     898         296 :             break;
     899       81480 :         case jpiNot:
     900             :         case jpiExists:
     901             :         case jpiIsUnknown:
     902             :         case jpiPlus:
     903             :         case jpiMinus:
     904             :         case jpiFilter:
     905             :         case jpiDatetime:
     906       81480 :             read_int32(v->content.arg, base, pos);
     907       81480 :             break;
     908         232 :         case jpiIndexArray:
     909         232 :             read_int32(v->content.array.nelems, base, pos);
     910         232 :             read_int32_n(v->content.array.elems, base, pos,
     911             :                          v->content.array.nelems * 2);
     912         232 :             break;
     913         236 :         case jpiAny:
     914         236 :             read_int32(v->content.anybounds.first, base, pos);
     915         236 :             read_int32(v->content.anybounds.last, base, pos);
     916         236 :             break;
     917           0 :         default:
     918           0 :             elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
     919             :     }
     920      541108 : }
     921             : 
     922             : void
     923       81976 : jspGetArg(JsonPathItem *v, JsonPathItem *a)
     924             : {
     925             :     Assert(v->type == jpiFilter ||
     926             :            v->type == jpiNot ||
     927             :            v->type == jpiIsUnknown ||
     928             :            v->type == jpiExists ||
     929             :            v->type == jpiPlus ||
     930             :            v->type == jpiMinus ||
     931             :            v->type == jpiDatetime);
     932             : 
     933       81976 :     jspInitByBuffer(a, v->base, v->content.arg);
     934       81976 : }
     935             : 
     936             : bool
     937      348592 : jspGetNext(JsonPathItem *v, JsonPathItem *a)
     938             : {
     939      348592 :     if (jspHasNext(v))
     940             :     {
     941             :         Assert(v->type == jpiString ||
     942             :                v->type == jpiNumeric ||
     943             :                v->type == jpiBool ||
     944             :                v->type == jpiNull ||
     945             :                v->type == jpiKey ||
     946             :                v->type == jpiAny ||
     947             :                v->type == jpiAnyArray ||
     948             :                v->type == jpiAnyKey ||
     949             :                v->type == jpiIndexArray ||
     950             :                v->type == jpiFilter ||
     951             :                v->type == jpiCurrent ||
     952             :                v->type == jpiExists ||
     953             :                v->type == jpiRoot ||
     954             :                v->type == jpiVariable ||
     955             :                v->type == jpiLast ||
     956             :                v->type == jpiAdd ||
     957             :                v->type == jpiSub ||
     958             :                v->type == jpiMul ||
     959             :                v->type == jpiDiv ||
     960             :                v->type == jpiMod ||
     961             :                v->type == jpiPlus ||
     962             :                v->type == jpiMinus ||
     963             :                v->type == jpiEqual ||
     964             :                v->type == jpiNotEqual ||
     965             :                v->type == jpiGreater ||
     966             :                v->type == jpiGreaterOrEqual ||
     967             :                v->type == jpiLess ||
     968             :                v->type == jpiLessOrEqual ||
     969             :                v->type == jpiAnd ||
     970             :                v->type == jpiOr ||
     971             :                v->type == jpiNot ||
     972             :                v->type == jpiIsUnknown ||
     973             :                v->type == jpiType ||
     974             :                v->type == jpiSize ||
     975             :                v->type == jpiAbs ||
     976             :                v->type == jpiFloor ||
     977             :                v->type == jpiCeiling ||
     978             :                v->type == jpiDouble ||
     979             :                v->type == jpiDatetime ||
     980             :                v->type == jpiKeyValue ||
     981             :                v->type == jpiStartsWith);
     982             : 
     983      159688 :         if (a)
     984      159688 :             jspInitByBuffer(a, v->base, v->nextPos);
     985      159688 :         return true;
     986             :     }
     987             : 
     988      188904 :     return false;
     989             : }
     990             : 
     991             : void
     992       80708 : jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
     993             : {
     994             :     Assert(v->type == jpiAnd ||
     995             :            v->type == jpiOr ||
     996             :            v->type == jpiEqual ||
     997             :            v->type == jpiNotEqual ||
     998             :            v->type == jpiLess ||
     999             :            v->type == jpiGreater ||
    1000             :            v->type == jpiLessOrEqual ||
    1001             :            v->type == jpiGreaterOrEqual ||
    1002             :            v->type == jpiAdd ||
    1003             :            v->type == jpiSub ||
    1004             :            v->type == jpiMul ||
    1005             :            v->type == jpiDiv ||
    1006             :            v->type == jpiMod ||
    1007             :            v->type == jpiStartsWith);
    1008             : 
    1009       80708 :     jspInitByBuffer(a, v->base, v->content.args.left);
    1010       80708 : }
    1011             : 
    1012             : void
    1013       60856 : jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
    1014             : {
    1015             :     Assert(v->type == jpiAnd ||
    1016             :            v->type == jpiOr ||
    1017             :            v->type == jpiEqual ||
    1018             :            v->type == jpiNotEqual ||
    1019             :            v->type == jpiLess ||
    1020             :            v->type == jpiGreater ||
    1021             :            v->type == jpiLessOrEqual ||
    1022             :            v->type == jpiGreaterOrEqual ||
    1023             :            v->type == jpiAdd ||
    1024             :            v->type == jpiSub ||
    1025             :            v->type == jpiMul ||
    1026             :            v->type == jpiDiv ||
    1027             :            v->type == jpiMod ||
    1028             :            v->type == jpiStartsWith);
    1029             : 
    1030       60856 :     jspInitByBuffer(a, v->base, v->content.args.right);
    1031       60856 : }
    1032             : 
    1033             : bool
    1034         968 : jspGetBool(JsonPathItem *v)
    1035             : {
    1036             :     Assert(v->type == jpiBool);
    1037             : 
    1038         968 :     return (bool) *v->content.value.data;
    1039             : }
    1040             : 
    1041             : Numeric
    1042       17004 : jspGetNumeric(JsonPathItem *v)
    1043             : {
    1044             :     Assert(v->type == jpiNumeric);
    1045             : 
    1046       17004 :     return (Numeric) v->content.value.data;
    1047             : }
    1048             : 
    1049             : char *
    1050      165328 : jspGetString(JsonPathItem *v, int32 *len)
    1051             : {
    1052             :     Assert(v->type == jpiKey ||
    1053             :            v->type == jpiString ||
    1054             :            v->type == jpiVariable);
    1055             : 
    1056      165328 :     if (len)
    1057      164584 :         *len = v->content.value.datalen;
    1058      165328 :     return v->content.value.data;
    1059             : }
    1060             : 
    1061             : bool
    1062         244 : jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to,
    1063             :                      int i)
    1064             : {
    1065             :     Assert(v->type == jpiIndexArray);
    1066             : 
    1067         244 :     jspInitByBuffer(from, v->base, v->content.array.elems[i].from);
    1068             : 
    1069         244 :     if (!v->content.array.elems[i].to)
    1070         216 :         return false;
    1071             : 
    1072          28 :     jspInitByBuffer(to, v->base, v->content.array.elems[i].to);
    1073             : 
    1074          28 :     return true;
    1075             : }

Generated by: LCOV version 1.13