LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_gram.y (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 98.3 % 236 232
Test Date: 2026-02-17 17:20:33 Functions: 100.0 % 13 13
Legend: Lines:     hit not hit

            Line data    Source code
       1              : %{
       2              : /*-------------------------------------------------------------------------
       3              :  *
       4              :  * jsonpath_gram.y
       5              :  *   Grammar definitions for jsonpath datatype
       6              :  *
       7              :  * Transforms tokenized jsonpath into tree of JsonPathParseItem structs.
       8              :  *
       9              :  * Copyright (c) 2019-2026, PostgreSQL Global Development Group
      10              :  *
      11              :  * IDENTIFICATION
      12              :  *  src/backend/utils/adt/jsonpath_gram.y
      13              :  *
      14              :  *-------------------------------------------------------------------------
      15              :  */
      16              : 
      17              : #include "postgres.h"
      18              : 
      19              : #include "catalog/pg_collation.h"
      20              : #include "fmgr.h"
      21              : #include "jsonpath_internal.h"
      22              : #include "miscadmin.h"
      23              : #include "nodes/pg_list.h"
      24              : #include "regex/regex.h"
      25              : #include "utils/builtins.h"
      26              : 
      27              : static JsonPathParseItem *makeItemType(JsonPathItemType type);
      28              : static JsonPathParseItem *makeItemString(JsonPathString *s);
      29              : static JsonPathParseItem *makeItemVariable(JsonPathString *s);
      30              : static JsonPathParseItem *makeItemKey(JsonPathString *s);
      31              : static JsonPathParseItem *makeItemNumeric(JsonPathString *s);
      32              : static JsonPathParseItem *makeItemBool(bool val);
      33              : static JsonPathParseItem *makeItemBinary(JsonPathItemType type,
      34              :                                          JsonPathParseItem *la,
      35              :                                          JsonPathParseItem *ra);
      36              : static JsonPathParseItem *makeItemUnary(JsonPathItemType type,
      37              :                                         JsonPathParseItem *a);
      38              : static JsonPathParseItem *makeItemList(List *list);
      39              : static JsonPathParseItem *makeIndexArray(List *list);
      40              : static JsonPathParseItem *makeAny(int first, int last);
      41              : static bool makeItemLikeRegex(JsonPathParseItem *expr,
      42              :                               JsonPathString *pattern,
      43              :                               JsonPathString *flags,
      44              :                               JsonPathParseItem ** result,
      45              :                               struct Node *escontext);
      46              : 
      47              : /*
      48              :  * Bison doesn't allocate anything that needs to live across parser calls,
      49              :  * so we can easily have it use palloc instead of malloc.  This prevents
      50              :  * memory leaks if we error out during parsing.
      51              :  */
      52              : #define YYMALLOC palloc
      53              : #define YYFREE   pfree
      54              : 
      55              : %}
      56              : 
      57              : /* BISON Declarations */
      58              : %pure-parser
      59              : %expect 0
      60              : %name-prefix="jsonpath_yy"
      61              : %parse-param {JsonPathParseResult **result}
      62              : %parse-param {struct Node *escontext}
      63              : %parse-param {yyscan_t yyscanner}
      64              : %lex-param {JsonPathParseResult **result}
      65              : %lex-param {struct Node *escontext}
      66              : %lex-param {yyscan_t yyscanner}
      67              : 
      68              : %union
      69              : {
      70              :     JsonPathString      str;
      71              :     List               *elems;  /* list of JsonPathParseItem */
      72              :     List               *indexs; /* list of integers */
      73              :     JsonPathParseItem  *value;
      74              :     JsonPathParseResult *result;
      75              :     JsonPathItemType    optype;
      76              :     bool                boolean;
      77              :     int                 integer;
      78              : }
      79              : 
      80              : %token  <str>     TO_P NULL_P TRUE_P FALSE_P IS_P UNKNOWN_P EXISTS_P
      81              : %token  <str>     IDENT_P STRING_P NUMERIC_P INT_P VARIABLE_P
      82              : %token  <str>     OR_P AND_P NOT_P
      83              : %token  <str>     LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
      84              : %token  <str>     ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
      85              : %token  <str>     ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
      86              : %token  <str>     DATETIME_P
      87              : %token  <str>     BIGINT_P BOOLEAN_P DATE_P DECIMAL_P INTEGER_P NUMBER_P
      88              : %token  <str>     STRINGFUNC_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
      89              : 
      90              : %type   <result>  result
      91              : 
      92              : %type   <value>       scalar_value path_primary expr array_accessor
      93              :                     any_path accessor_op key predicate delimited_predicate
      94              :                     index_elem starts_with_initial expr_or_predicate
      95              :                     datetime_template opt_datetime_template csv_elem
      96              :                     datetime_precision opt_datetime_precision
      97              : 
      98              : %type   <elems>       accessor_expr csv_list opt_csv_list
      99              : 
     100              : %type   <indexs>  index_list
     101              : 
     102              : %type   <optype>  comp_op method
     103              : 
     104              : %type   <boolean> mode
     105              : 
     106              : %type   <str>     key_name
     107              : 
     108              : %type   <integer> any_level
     109              : 
     110              : %left   OR_P
     111              : %left   AND_P
     112              : %right  NOT_P
     113              : %left   '+' '-'
     114              : %left   '*' '/' '%'
     115              : %left   UMINUS
     116              : %nonassoc '(' ')'
     117              : 
     118              : /* Grammar follows */
     119              : %%
     120              : 
     121              : result:
     122              :     mode expr_or_predicate          {
     123         5176 :                                         *result = palloc_object(JsonPathParseResult);
     124         5176 :                                         (*result)->expr = $2;
     125         5176 :                                         (*result)->lax = $1;
     126              :                                         (void) yynerrs;
     127              :                                     }
     128           15 :     | /* EMPTY */                   { *result = NULL; }
     129              :     ;
     130              : 
     131              : expr_or_predicate:
     132         4960 :     expr                            { $$ = $1; }
     133          216 :     | predicate                     { $$ = $1; }
     134              :     ;
     135              : 
     136              : mode:
     137          324 :     STRICT_P                        { $$ = false; }
     138          345 :     | LAX_P                         { $$ = true; }
     139         4615 :     | /* EMPTY */                   { $$ = true; }
     140              :     ;
     141              : 
     142              : scalar_value:
     143          417 :     STRING_P                        { $$ = makeItemString(&$1); }
     144           57 :     | NULL_P                        { $$ = makeItemString(NULL); }
     145           69 :     | TRUE_P                        { $$ = makeItemBool(true); }
     146           21 :     | FALSE_P                       { $$ = makeItemBool(false); }
     147          267 :     | NUMERIC_P                     { $$ = makeItemNumeric(&$1); }
     148          768 :     | INT_P                         { $$ = makeItemNumeric(&$1); }
     149          354 :     | VARIABLE_P                    { $$ = makeItemVariable(&$1); }
     150              :     ;
     151              : 
     152              : comp_op:
     153          501 :     EQUAL_P                         { $$ = jpiEqual; }
     154            6 :     | NOTEQUAL_P                    { $$ = jpiNotEqual; }
     155          378 :     | LESS_P                        { $$ = jpiLess; }
     156          222 :     | GREATER_P                     { $$ = jpiGreater; }
     157           21 :     | LESSEQUAL_P                   { $$ = jpiLessOrEqual; }
     158          168 :     | GREATEREQUAL_P                { $$ = jpiGreaterOrEqual; }
     159              :     ;
     160              : 
     161              : delimited_predicate:
     162           36 :     '(' predicate ')'               { $$ = $2; }
     163          132 :     | EXISTS_P '(' expr ')'         { $$ = makeItemUnary(jpiExists, $3); }
     164              :     ;
     165              : 
     166              : predicate:
     167          156 :     delimited_predicate             { $$ = $1; }
     168         1296 :     | expr comp_op expr             { $$ = makeItemBinary($2, $1, $3); }
     169           93 :     | predicate AND_P predicate     { $$ = makeItemBinary(jpiAnd, $1, $3); }
     170           54 :     | predicate OR_P predicate      { $$ = makeItemBinary(jpiOr, $1, $3); }
     171           12 :     | NOT_P delimited_predicate     { $$ = makeItemUnary(jpiNot, $2); }
     172              :     | '(' predicate ')' IS_P UNKNOWN_P
     173           36 :                                     { $$ = makeItemUnary(jpiIsUnknown, $2); }
     174              :     | expr STARTS_P WITH_P starts_with_initial
     175           30 :                                     { $$ = makeItemBinary(jpiStartsWith, $1, $4); }
     176              :     | expr LIKE_REGEX_P STRING_P
     177              :     {
     178              :         JsonPathParseItem *jppitem;
     179            9 :         if (! makeItemLikeRegex($1, &$3, NULL, &jppitem, escontext))
     180            0 :             YYABORT;
     181            6 :         $$ = jppitem;
     182              :     }
     183              :     | expr LIKE_REGEX_P STRING_P FLAG_P STRING_P
     184              :     {
     185              :         JsonPathParseItem *jppitem;
     186           66 :         if (! makeItemLikeRegex($1, &$3, &$5, &jppitem, escontext))
     187            6 :             YYABORT;
     188           54 :         $$ = jppitem;
     189              :     }
     190              :     ;
     191              : 
     192              : starts_with_initial:
     193           27 :     STRING_P                        { $$ = makeItemString(&$1); }
     194            3 :     | VARIABLE_P                    { $$ = makeItemVariable(&$1); }
     195              :     ;
     196              : 
     197              : path_primary:
     198         1953 :     scalar_value                    { $$ = $1; }
     199         5068 :     | '$'                           { $$ = makeItemType(jpiRoot); }
     200         1299 :     | '@'                           { $$ = makeItemType(jpiCurrent); }
     201           45 :     | LAST_P                        { $$ = makeItemType(jpiLast); }
     202              :     ;
     203              : 
     204              : accessor_expr:
     205         8365 :     path_primary                    { $$ = list_make1($1); }
     206           45 :     | '(' expr ')' accessor_op      { $$ = list_make2($2, $4); }
     207           15 :     | '(' predicate ')' accessor_op { $$ = list_make2($2, $4); }
     208         6723 :     | accessor_expr accessor_op     { $$ = lappend($1, $2); }
     209              :     ;
     210              : 
     211              : expr:
     212         8356 :     accessor_expr                   { $$ = makeItemList($1); }
     213           81 :     | '(' expr ')'                  { $$ = $2; }
     214           87 :     | '+' expr %prec UMINUS         { $$ = makeItemUnary(jpiPlus, $2); }
     215          123 :     | '-' expr %prec UMINUS         { $$ = makeItemUnary(jpiMinus, $2); }
     216          105 :     | expr '+' expr                 { $$ = makeItemBinary(jpiAdd, $1, $3); }
     217           51 :     | expr '-' expr                 { $$ = makeItemBinary(jpiSub, $1, $3); }
     218           36 :     | expr '*' expr                 { $$ = makeItemBinary(jpiMul, $1, $3); }
     219           18 :     | expr '/' expr                 { $$ = makeItemBinary(jpiDiv, $1, $3); }
     220            9 :     | expr '%' expr                 { $$ = makeItemBinary(jpiMod, $1, $3); }
     221              :     ;
     222              : 
     223              : index_elem:
     224          255 :     expr                            { $$ = makeItemBinary(jpiSubscript, $1, NULL); }
     225           24 :     | expr TO_P expr                { $$ = makeItemBinary(jpiSubscript, $1, $3); }
     226              :     ;
     227              : 
     228              : index_list:
     229          255 :     index_elem                      { $$ = list_make1($1); }
     230           24 :     | index_list ',' index_elem     { $$ = lappend($1, $3); }
     231              :     ;
     232              : 
     233              : array_accessor:
     234         1004 :     '[' '*' ']'                     { $$ = makeItemType(jpiAnyArray); }
     235          255 :     | '[' index_list ']'            { $$ = makeIndexArray($2); }
     236              :     ;
     237              : 
     238              : any_level:
     239          141 :     INT_P                           { $$ = pg_strtoint32($1.val); }
     240           48 :     | LAST_P                        { $$ = -1; }
     241              :     ;
     242              : 
     243              : any_path:
     244           57 :     ANY_P                           { $$ = makeAny(0, -1); }
     245           51 :     | ANY_P '{' any_level '}'       { $$ = makeAny($3, $3); }
     246              :     | ANY_P '{' any_level TO_P any_level '}'
     247           69 :                                     { $$ = makeAny($3, $5); }
     248              :     ;
     249              : 
     250              : accessor_op:
     251         1942 :     '.' key                         { $$ = $2; }
     252           87 :     | '.' '*'                       { $$ = makeItemType(jpiAnyKey); }
     253         1259 :     | array_accessor                { $$ = $1; }
     254          177 :     | '.' any_path                  { $$ = $2; }
     255          876 :     | '.' method '(' ')'            { $$ = makeItemType($2); }
     256         1140 :     | '?' '(' predicate ')'         { $$ = makeItemUnary(jpiFilter, $3); }
     257              :     | '.' DECIMAL_P '(' opt_csv_list ')'
     258              :         {
     259          138 :             if (list_length($4) == 0)
     260           84 :                 $$ = makeItemBinary(jpiDecimal, NULL, NULL);
     261           54 :             else if (list_length($4) == 1)
     262            0 :                 $$ = makeItemBinary(jpiDecimal, linitial($4), NULL);
     263           54 :             else if (list_length($4) == 2)
     264           54 :                 $$ = makeItemBinary(jpiDecimal, linitial($4), lsecond($4));
     265              :             else
     266            0 :                 ereturn(escontext, false,
     267              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     268              :                          errmsg("invalid input syntax for type %s", "jsonpath"),
     269              :                          errdetail(".decimal() can only have an optional precision[,scale].")));
     270              :         }
     271              :     | '.' DATETIME_P '(' opt_datetime_template ')'
     272          516 :         { $$ = makeItemUnary(jpiDatetime, $4); }
     273              :     | '.' TIME_P '(' opt_datetime_precision ')'
     274          162 :         { $$ = makeItemUnary(jpiTime, $4); }
     275              :     | '.' TIME_TZ_P '(' opt_datetime_precision ')'
     276          147 :         { $$ = makeItemUnary(jpiTimeTz, $4); }
     277              :     | '.' TIMESTAMP_P '(' opt_datetime_precision ')'
     278          168 :         { $$ = makeItemUnary(jpiTimestamp, $4); }
     279              :     | '.' TIMESTAMP_TZ_P '(' opt_datetime_precision ')'
     280          171 :         { $$ = makeItemUnary(jpiTimestampTz, $4); }
     281              :     ;
     282              : 
     283              : csv_elem:
     284              :     INT_P
     285           78 :         { $$ = makeItemNumeric(&$1); }
     286              :     | '+' INT_P %prec UMINUS
     287           15 :         { $$ = makeItemUnary(jpiPlus, makeItemNumeric(&$2)); }
     288              :     | '-' INT_P %prec UMINUS
     289           15 :         { $$ = makeItemUnary(jpiMinus, makeItemNumeric(&$2)); }
     290              :     ;
     291              : 
     292              : csv_list:
     293           54 :     csv_elem                        { $$ = list_make1($1); }
     294           54 :     | csv_list ',' csv_elem         { $$ = lappend($1, $3); }
     295              :     ;
     296              : 
     297              : opt_csv_list:
     298           54 :     csv_list                        { $$ = $1; }
     299           84 :     | /* EMPTY */                   { $$ = NULL; }
     300              :     ;
     301              : 
     302              : datetime_precision:
     303          138 :     INT_P                           { $$ = makeItemNumeric(&$1); }
     304              :     ;
     305              : 
     306              : opt_datetime_precision:
     307          138 :     datetime_precision              { $$ = $1; }
     308          534 :     | /* EMPTY */                   { $$ = NULL; }
     309              :     ;
     310              : 
     311              : datetime_template:
     312          258 :     STRING_P                        { $$ = makeItemString(&$1); }
     313              :     ;
     314              : 
     315              : opt_datetime_template:
     316          258 :     datetime_template               { $$ = $1; }
     317          258 :     | /* EMPTY */                   { $$ = NULL; }
     318              :     ;
     319              : 
     320              : key:
     321         1942 :     key_name                        { $$ = makeItemKey(&$1); }
     322              :     ;
     323              : 
     324              : key_name:
     325              :     IDENT_P
     326              :     | STRING_P
     327              :     | TO_P
     328              :     | NULL_P
     329              :     | TRUE_P
     330              :     | FALSE_P
     331              :     | IS_P
     332              :     | UNKNOWN_P
     333              :     | EXISTS_P
     334              :     | STRICT_P
     335              :     | LAX_P
     336              :     | ABS_P
     337              :     | SIZE_P
     338              :     | TYPE_P
     339              :     | FLOOR_P
     340              :     | DOUBLE_P
     341              :     | CEILING_P
     342              :     | DATETIME_P
     343              :     | KEYVALUE_P
     344              :     | LAST_P
     345              :     | STARTS_P
     346              :     | WITH_P
     347              :     | LIKE_REGEX_P
     348              :     | FLAG_P
     349              :     | BIGINT_P
     350              :     | BOOLEAN_P
     351              :     | DATE_P
     352              :     | DECIMAL_P
     353              :     | INTEGER_P
     354              :     | NUMBER_P
     355              :     | STRINGFUNC_P
     356              :     | TIME_P
     357              :     | TIME_TZ_P
     358              :     | TIMESTAMP_P
     359              :     | TIMESTAMP_TZ_P
     360              :     ;
     361              : 
     362              : method:
     363           21 :     ABS_P                           { $$ = jpiAbs; }
     364           15 :     | SIZE_P                        { $$ = jpiSize; }
     365          126 :     | TYPE_P                        { $$ = jpiType; }
     366           15 :     | FLOOR_P                       { $$ = jpiFloor; }
     367           60 :     | DOUBLE_P                      { $$ = jpiDouble; }
     368           18 :     | CEILING_P                     { $$ = jpiCeiling; }
     369           33 :     | KEYVALUE_P                    { $$ = jpiKeyValue; }
     370           93 :     | BIGINT_P                      { $$ = jpiBigint; }
     371          123 :     | BOOLEAN_P                     { $$ = jpiBoolean; }
     372          117 :     | DATE_P                        { $$ = jpiDate; }
     373           87 :     | INTEGER_P                     { $$ = jpiInteger; }
     374           84 :     | NUMBER_P                      { $$ = jpiNumber; }
     375           87 :     | STRINGFUNC_P                  { $$ = jpiStringFunc; }
     376              :     ;
     377              : %%
     378              : 
     379              : /*
     380              :  * The helper functions below allocate and fill JsonPathParseItem's of various
     381              :  * types.
     382              :  */
     383              : 
     384              : static JsonPathParseItem *
     385        18073 : makeItemType(JsonPathItemType type)
     386              : {
     387        18073 :     JsonPathParseItem *v = palloc_object(JsonPathParseItem);
     388              : 
     389        18073 :     CHECK_FOR_INTERRUPTS();
     390              : 
     391        18073 :     v->type = type;
     392        18073 :     v->next = NULL;
     393              : 
     394        18073 :     return v;
     395              : }
     396              : 
     397              : static JsonPathParseItem *
     398         2701 : makeItemString(JsonPathString *s)
     399              : {
     400              :     JsonPathParseItem *v;
     401              : 
     402         2701 :     if (s == NULL)
     403              :     {
     404           57 :         v = makeItemType(jpiNull);
     405              :     }
     406              :     else
     407              :     {
     408         2644 :         v = makeItemType(jpiString);
     409         2644 :         v->value.string.val = s->val;
     410         2644 :         v->value.string.len = s->len;
     411              :     }
     412              : 
     413         2701 :     return v;
     414              : }
     415              : 
     416              : static JsonPathParseItem *
     417          357 : makeItemVariable(JsonPathString *s)
     418              : {
     419              :     JsonPathParseItem *v;
     420              : 
     421          357 :     v = makeItemType(jpiVariable);
     422          357 :     v->value.string.val = s->val;
     423          357 :     v->value.string.len = s->len;
     424              : 
     425          357 :     return v;
     426              : }
     427              : 
     428              : static JsonPathParseItem *
     429         1942 : makeItemKey(JsonPathString *s)
     430              : {
     431              :     JsonPathParseItem *v;
     432              : 
     433         1942 :     v = makeItemString(s);
     434         1942 :     v->type = jpiKey;
     435              : 
     436         1942 :     return v;
     437              : }
     438              : 
     439              : static JsonPathParseItem *
     440         1281 : makeItemNumeric(JsonPathString *s)
     441              : {
     442              :     JsonPathParseItem *v;
     443              : 
     444         1281 :     v = makeItemType(jpiNumeric);
     445         1281 :     v->value.numeric =
     446         1281 :         DatumGetNumeric(DirectFunctionCall3(numeric_in,
     447              :                                             CStringGetDatum(s->val),
     448              :                                             ObjectIdGetDatum(InvalidOid),
     449              :                                             Int32GetDatum(-1)));
     450              : 
     451         1281 :     return v;
     452              : }
     453              : 
     454              : static JsonPathParseItem *
     455           90 : makeItemBool(bool val)
     456              : {
     457           90 :     JsonPathParseItem *v = makeItemType(jpiBool);
     458              : 
     459           90 :     v->value.boolean = val;
     460              : 
     461           90 :     return v;
     462              : }
     463              : 
     464              : static JsonPathParseItem *
     465         2109 : makeItemBinary(JsonPathItemType type, JsonPathParseItem *la, JsonPathParseItem *ra)
     466              : {
     467         2109 :     JsonPathParseItem *v = makeItemType(type);
     468              : 
     469         2109 :     v->value.args.left = la;
     470         2109 :     v->value.args.right = ra;
     471              : 
     472         2109 :     return v;
     473              : }
     474              : 
     475              : static JsonPathParseItem *
     476         2724 : makeItemUnary(JsonPathItemType type, JsonPathParseItem *a)
     477              : {
     478              :     JsonPathParseItem *v;
     479              : 
     480         2724 :     if (type == jpiPlus && a->type == jpiNumeric && !a->next)
     481           75 :         return a;
     482              : 
     483         2649 :     if (type == jpiMinus && a->type == jpiNumeric && !a->next)
     484              :     {
     485           84 :         v = makeItemType(jpiNumeric);
     486           84 :         v->value.numeric =
     487           84 :             DatumGetNumeric(DirectFunctionCall1(numeric_uminus,
     488              :                                                 NumericGetDatum(a->value.numeric)));
     489           84 :         return v;
     490              :     }
     491              : 
     492         2565 :     v = makeItemType(type);
     493              : 
     494         2565 :     v->value.arg = a;
     495              : 
     496         2565 :     return v;
     497              : }
     498              : 
     499              : static JsonPathParseItem *
     500         8356 : makeItemList(List *list)
     501              : {
     502              :     JsonPathParseItem *head,
     503              :                *end;
     504              :     ListCell   *cell;
     505              : 
     506         8356 :     head = end = (JsonPathParseItem *) linitial(list);
     507              : 
     508         8356 :     if (list_length(list) == 1)
     509         3427 :         return head;
     510              : 
     511              :     /* append items to the end of already existing list */
     512         4935 :     while (end->next)
     513            6 :         end = end->next;
     514              : 
     515        11712 :     for_each_from(cell, list, 1)
     516              :     {
     517         6783 :         JsonPathParseItem *c = (JsonPathParseItem *) lfirst(cell);
     518              : 
     519         6783 :         end->next = c;
     520         6783 :         end = c;
     521              :     }
     522              : 
     523         4929 :     return head;
     524              : }
     525              : 
     526              : static JsonPathParseItem *
     527          255 : makeIndexArray(List *list)
     528              : {
     529          255 :     JsonPathParseItem *v = makeItemType(jpiIndexArray);
     530              :     ListCell   *cell;
     531          255 :     int         i = 0;
     532              : 
     533              :     Assert(list != NIL);
     534          255 :     v->value.array.nelems = list_length(list);
     535              : 
     536          510 :     v->value.array.elems = palloc(sizeof(v->value.array.elems[0]) *
     537          255 :                                   v->value.array.nelems);
     538              : 
     539          534 :     foreach(cell, list)
     540              :     {
     541          279 :         JsonPathParseItem *jpi = lfirst(cell);
     542              : 
     543              :         Assert(jpi->type == jpiSubscript);
     544              : 
     545          279 :         v->value.array.elems[i].from = jpi->value.args.left;
     546          279 :         v->value.array.elems[i++].to = jpi->value.args.right;
     547              :     }
     548              : 
     549          255 :     return v;
     550              : }
     551              : 
     552              : static JsonPathParseItem *
     553          177 : makeAny(int first, int last)
     554              : {
     555          177 :     JsonPathParseItem *v = makeItemType(jpiAny);
     556              : 
     557          177 :     v->value.anybounds.first = (first >= 0) ? first : PG_UINT32_MAX;
     558          177 :     v->value.anybounds.last = (last >= 0) ? last : PG_UINT32_MAX;
     559              : 
     560          177 :     return v;
     561              : }
     562              : 
     563              : static bool
     564           75 : makeItemLikeRegex(JsonPathParseItem *expr, JsonPathString *pattern,
     565              :                   JsonPathString *flags, JsonPathParseItem **result,
     566              :                   struct Node *escontext)
     567              : {
     568           75 :     JsonPathParseItem *v = makeItemType(jpiLikeRegex);
     569              :     int         i;
     570              :     int         cflags;
     571              : 
     572           75 :     v->value.like_regex.expr = expr;
     573           75 :     v->value.like_regex.pattern = pattern->val;
     574           75 :     v->value.like_regex.patternlen = pattern->len;
     575              : 
     576              :     /* Parse the flags string, convert to bitmask.  Duplicate flags are OK. */
     577           75 :     v->value.like_regex.flags = 0;
     578          186 :     for (i = 0; flags && i < flags->len; i++)
     579              :     {
     580          120 :         switch (flags->val[i])
     581              :         {
     582           30 :             case 'i':
     583           30 :                 v->value.like_regex.flags |= JSP_REGEX_ICASE;
     584           30 :                 break;
     585           24 :             case 's':
     586           24 :                 v->value.like_regex.flags |= JSP_REGEX_DOTALL;
     587           24 :                 break;
     588           18 :             case 'm':
     589           18 :                 v->value.like_regex.flags |= JSP_REGEX_MLINE;
     590           18 :                 break;
     591           12 :             case 'x':
     592           12 :                 v->value.like_regex.flags |= JSP_REGEX_WSPACE;
     593           12 :                 break;
     594           27 :             case 'q':
     595           27 :                 v->value.like_regex.flags |= JSP_REGEX_QUOTE;
     596           27 :                 break;
     597            9 :             default:
     598            9 :                 ereturn(escontext, false,
     599              :                         (errcode(ERRCODE_SYNTAX_ERROR),
     600              :                          errmsg("invalid input syntax for type %s", "jsonpath"),
     601              :                          errdetail("Unrecognized flag character \"%.*s\" in LIKE_REGEX predicate.",
     602              :                                    pg_mblen_range(flags->val + i, flags->val + flags->len),
     603              :                                    flags->val + i)));
     604              :                 break;
     605              :         }
     606              :     }
     607              : 
     608              :     /* Convert flags to what pg_regcomp needs */
     609           66 :     if (!jspConvertRegexFlags(v->value.like_regex.flags, &cflags, escontext))
     610            0 :         return false;
     611              : 
     612              :     /* check regex validity */
     613              :     {
     614              :         regex_t     re_tmp;
     615              :         pg_wchar   *wpattern;
     616              :         int         wpattern_len;
     617              :         int         re_result;
     618              : 
     619           63 :         wpattern = (pg_wchar *) palloc((pattern->len + 1) * sizeof(pg_wchar));
     620           63 :         wpattern_len = pg_mb2wchar_with_len(pattern->val,
     621              :                                             wpattern,
     622              :                                             pattern->len);
     623              : 
     624           63 :         if ((re_result = pg_regcomp(&re_tmp, wpattern, wpattern_len, cflags,
     625              :                                     DEFAULT_COLLATION_OID)) != REG_OKAY)
     626              :         {
     627              :             char        errMsg[100];
     628              : 
     629            3 :             pg_regerror(re_result, &re_tmp, errMsg, sizeof(errMsg));
     630            3 :             ereturn(escontext, false,
     631              :                     (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
     632              :                      errmsg("invalid regular expression: %s", errMsg)));
     633              :         }
     634              : 
     635           60 :         pg_regfree(&re_tmp);
     636              :     }
     637              : 
     638           60 :     *result = v;
     639              : 
     640           60 :     return true;
     641              : }
     642              : 
     643              : /*
     644              :  * Convert from XQuery regex flags to those recognized by our regex library.
     645              :  */
     646              : bool
     647          204 : jspConvertRegexFlags(uint32 xflags, int *result, struct Node *escontext)
     648              : {
     649              :     /* By default, XQuery is very nearly the same as Spencer's AREs */
     650          204 :     int         cflags = REG_ADVANCED;
     651              : 
     652              :     /* Ignore-case means the same thing, too, modulo locale issues */
     653          204 :     if (xflags & JSP_REGEX_ICASE)
     654           57 :         cflags |= REG_ICASE;
     655              : 
     656              :     /* Per XQuery spec, if 'q' is specified then 'm', 's', 'x' are ignored */
     657          204 :     if (xflags & JSP_REGEX_QUOTE)
     658              :     {
     659           63 :         cflags &= ~REG_ADVANCED;
     660           63 :         cflags |= REG_QUOTE;
     661              :     }
     662              :     else
     663              :     {
     664              :         /* Note that dotall mode is the default in POSIX */
     665          141 :         if (!(xflags & JSP_REGEX_DOTALL))
     666          108 :             cflags |= REG_NLSTOP;
     667          141 :         if (xflags & JSP_REGEX_MLINE)
     668           30 :             cflags |= REG_NLANCH;
     669              : 
     670              :         /*
     671              :          * XQuery's 'x' mode is related to Spencer's expanded mode, but it's
     672              :          * not really enough alike to justify treating JSP_REGEX_WSPACE as
     673              :          * REG_EXPANDED.  For now we treat 'x' as unimplemented; perhaps in
     674              :          * future we'll modify the regex library to have an option for
     675              :          * XQuery-style ignore-whitespace mode.
     676              :          */
     677          141 :         if (xflags & JSP_REGEX_WSPACE)
     678            3 :             ereturn(escontext, false,
     679              :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     680              :                      errmsg("XQuery \"x\" flag (expanded regular expressions) is not implemented")));
     681              :     }
     682              : 
     683          201 :     *result = cflags;
     684              : 
     685          201 :     return true;
     686              : }
        

Generated by: LCOV version 2.0-1