LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_gram.y (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 232 236 98.3 %
Date: 2025-01-18 04:15:08 Functions: 13 13 100.0 %
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-2025, 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       10304 :                                         *result = palloc(sizeof(JsonPathParseResult));
     124       10304 :                                         (*result)->expr = $2;
     125       10304 :                                         (*result)->lax = $1;
     126             :                                         (void) yynerrs;
     127             :                                     }
     128          30 :     | /* EMPTY */                   { *result = NULL; }
     129             :     ;
     130             : 
     131             : expr_or_predicate:
     132        9872 :     expr                            { $$ = $1; }
     133         432 :     | predicate                     { $$ = $1; }
     134             :     ;
     135             : 
     136             : mode:
     137         648 :     STRICT_P                        { $$ = false; }
     138         690 :     | LAX_P                         { $$ = true; }
     139        9182 :     | /* EMPTY */                   { $$ = true; }
     140             :     ;
     141             : 
     142             : scalar_value:
     143         834 :     STRING_P                        { $$ = makeItemString(&$1); }
     144         114 :     | NULL_P                        { $$ = makeItemString(NULL); }
     145         138 :     | TRUE_P                        { $$ = makeItemBool(true); }
     146          42 :     | FALSE_P                       { $$ = makeItemBool(false); }
     147         534 :     | NUMERIC_P                     { $$ = makeItemNumeric(&$1); }
     148        1536 :     | INT_P                         { $$ = makeItemNumeric(&$1); }
     149         708 :     | VARIABLE_P                    { $$ = makeItemVariable(&$1); }
     150             :     ;
     151             : 
     152             : comp_op:
     153        1002 :     EQUAL_P                         { $$ = jpiEqual; }
     154          12 :     | NOTEQUAL_P                    { $$ = jpiNotEqual; }
     155         756 :     | LESS_P                        { $$ = jpiLess; }
     156         444 :     | GREATER_P                     { $$ = jpiGreater; }
     157          42 :     | LESSEQUAL_P                   { $$ = jpiLessOrEqual; }
     158         336 :     | GREATEREQUAL_P                { $$ = jpiGreaterOrEqual; }
     159             :     ;
     160             : 
     161             : delimited_predicate:
     162          72 :     '(' predicate ')'               { $$ = $2; }
     163         264 :     | EXISTS_P '(' expr ')'         { $$ = makeItemUnary(jpiExists, $3); }
     164             :     ;
     165             : 
     166             : predicate:
     167         312 :     delimited_predicate             { $$ = $1; }
     168        2592 :     | expr comp_op expr             { $$ = makeItemBinary($2, $1, $3); }
     169         186 :     | predicate AND_P predicate     { $$ = makeItemBinary(jpiAnd, $1, $3); }
     170         108 :     | predicate OR_P predicate      { $$ = makeItemBinary(jpiOr, $1, $3); }
     171          24 :     | NOT_P delimited_predicate     { $$ = makeItemUnary(jpiNot, $2); }
     172             :     | '(' predicate ')' IS_P UNKNOWN_P
     173          72 :                                     { $$ = makeItemUnary(jpiIsUnknown, $2); }
     174             :     | expr STARTS_P WITH_P starts_with_initial
     175          60 :                                     { $$ = makeItemBinary(jpiStartsWith, $1, $4); }
     176             :     | expr LIKE_REGEX_P STRING_P
     177             :     {
     178             :         JsonPathParseItem *jppitem;
     179          18 :         if (! makeItemLikeRegex($1, &$3, NULL, &jppitem, escontext))
     180           0 :             YYABORT;
     181          12 :         $$ = jppitem;
     182             :     }
     183             :     | expr LIKE_REGEX_P STRING_P FLAG_P STRING_P
     184             :     {
     185             :         JsonPathParseItem *jppitem;
     186         132 :         if (! makeItemLikeRegex($1, &$3, &$5, &jppitem, escontext))
     187          12 :             YYABORT;
     188         108 :         $$ = jppitem;
     189             :     }
     190             :     ;
     191             : 
     192             : starts_with_initial:
     193          54 :     STRING_P                        { $$ = makeItemString(&$1); }
     194           6 :     | VARIABLE_P                    { $$ = makeItemVariable(&$1); }
     195             :     ;
     196             : 
     197             : path_primary:
     198        3906 :     scalar_value                    { $$ = $1; }
     199       10088 :     | '$'                           { $$ = makeItemType(jpiRoot); }
     200        2598 :     | '@'                           { $$ = makeItemType(jpiCurrent); }
     201          90 :     | LAST_P                        { $$ = makeItemType(jpiLast); }
     202             :     ;
     203             : 
     204             : accessor_expr:
     205       16682 :     path_primary                    { $$ = list_make1($1); }
     206          90 :     | '(' expr ')' accessor_op      { $$ = list_make2($2, $4); }
     207          30 :     | '(' predicate ')' accessor_op { $$ = list_make2($2, $4); }
     208       13398 :     | accessor_expr accessor_op     { $$ = lappend($1, $2); }
     209             :     ;
     210             : 
     211             : expr:
     212       16664 :     accessor_expr                   { $$ = makeItemList($1); }
     213         162 :     | '(' expr ')'                  { $$ = $2; }
     214         174 :     | '+' expr %prec UMINUS         { $$ = makeItemUnary(jpiPlus, $2); }
     215         246 :     | '-' expr %prec UMINUS         { $$ = makeItemUnary(jpiMinus, $2); }
     216         210 :     | expr '+' expr                 { $$ = makeItemBinary(jpiAdd, $1, $3); }
     217         102 :     | expr '-' expr                 { $$ = makeItemBinary(jpiSub, $1, $3); }
     218          72 :     | expr '*' expr                 { $$ = makeItemBinary(jpiMul, $1, $3); }
     219          36 :     | expr '/' expr                 { $$ = makeItemBinary(jpiDiv, $1, $3); }
     220          18 :     | expr '%' expr                 { $$ = makeItemBinary(jpiMod, $1, $3); }
     221             :     ;
     222             : 
     223             : index_elem:
     224         510 :     expr                            { $$ = makeItemBinary(jpiSubscript, $1, NULL); }
     225          48 :     | expr TO_P expr                { $$ = makeItemBinary(jpiSubscript, $1, $3); }
     226             :     ;
     227             : 
     228             : index_list:
     229         510 :     index_elem                      { $$ = list_make1($1); }
     230          48 :     | index_list ',' index_elem     { $$ = lappend($1, $3); }
     231             :     ;
     232             : 
     233             : array_accessor:
     234        2008 :     '[' '*' ']'                     { $$ = makeItemType(jpiAnyArray); }
     235         510 :     | '[' index_list ']'            { $$ = makeIndexArray($2); }
     236             :     ;
     237             : 
     238             : any_level:
     239         282 :     INT_P                           { $$ = pg_strtoint32($1.val); }
     240          96 :     | LAST_P                        { $$ = -1; }
     241             :     ;
     242             : 
     243             : any_path:
     244         114 :     ANY_P                           { $$ = makeAny(0, -1); }
     245         102 :     | ANY_P '{' any_level '}'       { $$ = makeAny($3, $3); }
     246             :     | ANY_P '{' any_level TO_P any_level '}'
     247         138 :                                     { $$ = makeAny($3, $5); }
     248             :     ;
     249             : 
     250             : accessor_op:
     251        3836 :     '.' key                         { $$ = $2; }
     252         174 :     | '.' '*'                       { $$ = makeItemType(jpiAnyKey); }
     253        2518 :     | array_accessor                { $$ = $1; }
     254         354 :     | '.' any_path                  { $$ = $2; }
     255        1752 :     | '.' method '(' ')'            { $$ = makeItemType($2); }
     256        2280 :     | '?' '(' predicate ')'         { $$ = makeItemUnary(jpiFilter, $3); }
     257             :     | '.' DECIMAL_P '(' opt_csv_list ')'
     258             :         {
     259         276 :             if (list_length($4) == 0)
     260         168 :                 $$ = makeItemBinary(jpiDecimal, NULL, NULL);
     261         108 :             else if (list_length($4) == 1)
     262           0 :                 $$ = makeItemBinary(jpiDecimal, linitial($4), NULL);
     263         108 :             else if (list_length($4) == 2)
     264         108 :                 $$ = 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        1032 :         { $$ = makeItemUnary(jpiDatetime, $4); }
     273             :     | '.' TIME_P '(' opt_datetime_precision ')'
     274         324 :         { $$ = makeItemUnary(jpiTime, $4); }
     275             :     | '.' TIME_TZ_P '(' opt_datetime_precision ')'
     276         294 :         { $$ = makeItemUnary(jpiTimeTz, $4); }
     277             :     | '.' TIMESTAMP_P '(' opt_datetime_precision ')'
     278         336 :         { $$ = makeItemUnary(jpiTimestamp, $4); }
     279             :     | '.' TIMESTAMP_TZ_P '(' opt_datetime_precision ')'
     280         342 :         { $$ = makeItemUnary(jpiTimestampTz, $4); }
     281             :     ;
     282             : 
     283             : csv_elem:
     284             :     INT_P
     285         156 :         { $$ = makeItemNumeric(&$1); }
     286             :     | '+' INT_P %prec UMINUS
     287          30 :         { $$ = makeItemUnary(jpiPlus, makeItemNumeric(&$2)); }
     288             :     | '-' INT_P %prec UMINUS
     289          30 :         { $$ = makeItemUnary(jpiMinus, makeItemNumeric(&$2)); }
     290             :     ;
     291             : 
     292             : csv_list:
     293         108 :     csv_elem                        { $$ = list_make1($1); }
     294         108 :     | csv_list ',' csv_elem         { $$ = lappend($1, $3); }
     295             :     ;
     296             : 
     297             : opt_csv_list:
     298         108 :     csv_list                        { $$ = $1; }
     299         168 :     | /* EMPTY */                   { $$ = NULL; }
     300             :     ;
     301             : 
     302             : datetime_precision:
     303         276 :     INT_P                           { $$ = makeItemNumeric(&$1); }
     304             :     ;
     305             : 
     306             : opt_datetime_precision:
     307         276 :     datetime_precision              { $$ = $1; }
     308        1068 :     | /* EMPTY */                   { $$ = NULL; }
     309             :     ;
     310             : 
     311             : datetime_template:
     312         516 :     STRING_P                        { $$ = makeItemString(&$1); }
     313             :     ;
     314             : 
     315             : opt_datetime_template:
     316         516 :     datetime_template               { $$ = $1; }
     317         516 :     | /* EMPTY */                   { $$ = NULL; }
     318             :     ;
     319             : 
     320             : key:
     321        3836 :     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          42 :     ABS_P                           { $$ = jpiAbs; }
     364          30 :     | SIZE_P                        { $$ = jpiSize; }
     365         252 :     | TYPE_P                        { $$ = jpiType; }
     366          30 :     | FLOOR_P                       { $$ = jpiFloor; }
     367         120 :     | DOUBLE_P                      { $$ = jpiDouble; }
     368          36 :     | CEILING_P                     { $$ = jpiCeiling; }
     369          66 :     | KEYVALUE_P                    { $$ = jpiKeyValue; }
     370         186 :     | BIGINT_P                      { $$ = jpiBigint; }
     371         246 :     | BOOLEAN_P                     { $$ = jpiBoolean; }
     372         234 :     | DATE_P                        { $$ = jpiDate; }
     373         174 :     | INTEGER_P                     { $$ = jpiInteger; }
     374         168 :     | NUMBER_P                      { $$ = jpiNumber; }
     375         174 :     | 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       36050 : makeItemType(JsonPathItemType type)
     386             : {
     387       36050 :     JsonPathParseItem *v = palloc(sizeof(*v));
     388             : 
     389       36050 :     CHECK_FOR_INTERRUPTS();
     390             : 
     391       36050 :     v->type = type;
     392       36050 :     v->next = NULL;
     393             : 
     394       36050 :     return v;
     395             : }
     396             : 
     397             : static JsonPathParseItem *
     398        5354 : makeItemString(JsonPathString *s)
     399             : {
     400             :     JsonPathParseItem *v;
     401             : 
     402        5354 :     if (s == NULL)
     403             :     {
     404         114 :         v = makeItemType(jpiNull);
     405             :     }
     406             :     else
     407             :     {
     408        5240 :         v = makeItemType(jpiString);
     409        5240 :         v->value.string.val = s->val;
     410        5240 :         v->value.string.len = s->len;
     411             :     }
     412             : 
     413        5354 :     return v;
     414             : }
     415             : 
     416             : static JsonPathParseItem *
     417         714 : makeItemVariable(JsonPathString *s)
     418             : {
     419             :     JsonPathParseItem *v;
     420             : 
     421         714 :     v = makeItemType(jpiVariable);
     422         714 :     v->value.string.val = s->val;
     423         714 :     v->value.string.len = s->len;
     424             : 
     425         714 :     return v;
     426             : }
     427             : 
     428             : static JsonPathParseItem *
     429        3836 : makeItemKey(JsonPathString *s)
     430             : {
     431             :     JsonPathParseItem *v;
     432             : 
     433        3836 :     v = makeItemString(s);
     434        3836 :     v->type = jpiKey;
     435             : 
     436        3836 :     return v;
     437             : }
     438             : 
     439             : static JsonPathParseItem *
     440        2562 : makeItemNumeric(JsonPathString *s)
     441             : {
     442             :     JsonPathParseItem *v;
     443             : 
     444        2562 :     v = makeItemType(jpiNumeric);
     445        2562 :     v->value.numeric =
     446        2562 :         DatumGetNumeric(DirectFunctionCall3(numeric_in,
     447             :                                             CStringGetDatum(s->val),
     448             :                                             ObjectIdGetDatum(InvalidOid),
     449             :                                             Int32GetDatum(-1)));
     450             : 
     451        2562 :     return v;
     452             : }
     453             : 
     454             : static JsonPathParseItem *
     455         180 : makeItemBool(bool val)
     456             : {
     457         180 :     JsonPathParseItem *v = makeItemType(jpiBool);
     458             : 
     459         180 :     v->value.boolean = val;
     460             : 
     461         180 :     return v;
     462             : }
     463             : 
     464             : static JsonPathParseItem *
     465        4218 : makeItemBinary(JsonPathItemType type, JsonPathParseItem *la, JsonPathParseItem *ra)
     466             : {
     467        4218 :     JsonPathParseItem *v = makeItemType(type);
     468             : 
     469        4218 :     v->value.args.left = la;
     470        4218 :     v->value.args.right = ra;
     471             : 
     472        4218 :     return v;
     473             : }
     474             : 
     475             : static JsonPathParseItem *
     476        5448 : makeItemUnary(JsonPathItemType type, JsonPathParseItem *a)
     477             : {
     478             :     JsonPathParseItem *v;
     479             : 
     480        5448 :     if (type == jpiPlus && a->type == jpiNumeric && !a->next)
     481         150 :         return a;
     482             : 
     483        5298 :     if (type == jpiMinus && a->type == jpiNumeric && !a->next)
     484             :     {
     485         168 :         v = makeItemType(jpiNumeric);
     486         168 :         v->value.numeric =
     487         168 :             DatumGetNumeric(DirectFunctionCall1(numeric_uminus,
     488             :                                                 NumericGetDatum(a->value.numeric)));
     489         168 :         return v;
     490             :     }
     491             : 
     492        5130 :     v = makeItemType(type);
     493             : 
     494        5130 :     v->value.arg = a;
     495             : 
     496        5130 :     return v;
     497             : }
     498             : 
     499             : static JsonPathParseItem *
     500       16664 : makeItemList(List *list)
     501             : {
     502             :     JsonPathParseItem *head,
     503             :                *end;
     504             :     ListCell   *cell;
     505             : 
     506       16664 :     head = end = (JsonPathParseItem *) linitial(list);
     507             : 
     508       16664 :     if (list_length(list) == 1)
     509        6854 :         return head;
     510             : 
     511             :     /* append items to the end of already existing list */
     512        9822 :     while (end->next)
     513          12 :         end = end->next;
     514             : 
     515       23328 :     for_each_from(cell, list, 1)
     516             :     {
     517       13518 :         JsonPathParseItem *c = (JsonPathParseItem *) lfirst(cell);
     518             : 
     519       13518 :         end->next = c;
     520       13518 :         end = c;
     521             :     }
     522             : 
     523        9810 :     return head;
     524             : }
     525             : 
     526             : static JsonPathParseItem *
     527         510 : makeIndexArray(List *list)
     528             : {
     529         510 :     JsonPathParseItem *v = makeItemType(jpiIndexArray);
     530             :     ListCell   *cell;
     531         510 :     int         i = 0;
     532             : 
     533             :     Assert(list != NIL);
     534         510 :     v->value.array.nelems = list_length(list);
     535             : 
     536        1020 :     v->value.array.elems = palloc(sizeof(v->value.array.elems[0]) *
     537         510 :                                   v->value.array.nelems);
     538             : 
     539        1068 :     foreach(cell, list)
     540             :     {
     541         558 :         JsonPathParseItem *jpi = lfirst(cell);
     542             : 
     543             :         Assert(jpi->type == jpiSubscript);
     544             : 
     545         558 :         v->value.array.elems[i].from = jpi->value.args.left;
     546         558 :         v->value.array.elems[i++].to = jpi->value.args.right;
     547             :     }
     548             : 
     549         510 :     return v;
     550             : }
     551             : 
     552             : static JsonPathParseItem *
     553         354 : makeAny(int first, int last)
     554             : {
     555         354 :     JsonPathParseItem *v = makeItemType(jpiAny);
     556             : 
     557         354 :     v->value.anybounds.first = (first >= 0) ? first : PG_UINT32_MAX;
     558         354 :     v->value.anybounds.last = (last >= 0) ? last : PG_UINT32_MAX;
     559             : 
     560         354 :     return v;
     561             : }
     562             : 
     563             : static bool
     564         150 : makeItemLikeRegex(JsonPathParseItem *expr, JsonPathString *pattern,
     565             :                   JsonPathString *flags, JsonPathParseItem **result,
     566             :                   struct Node *escontext)
     567             : {
     568         150 :     JsonPathParseItem *v = makeItemType(jpiLikeRegex);
     569             :     int         i;
     570             :     int         cflags;
     571             : 
     572         150 :     v->value.like_regex.expr = expr;
     573         150 :     v->value.like_regex.pattern = pattern->val;
     574         150 :     v->value.like_regex.patternlen = pattern->len;
     575             : 
     576             :     /* Parse the flags string, convert to bitmask.  Duplicate flags are OK. */
     577         150 :     v->value.like_regex.flags = 0;
     578         372 :     for (i = 0; flags && i < flags->len; i++)
     579             :     {
     580         240 :         switch (flags->val[i])
     581             :         {
     582          60 :             case 'i':
     583          60 :                 v->value.like_regex.flags |= JSP_REGEX_ICASE;
     584          60 :                 break;
     585          48 :             case 's':
     586          48 :                 v->value.like_regex.flags |= JSP_REGEX_DOTALL;
     587          48 :                 break;
     588          36 :             case 'm':
     589          36 :                 v->value.like_regex.flags |= JSP_REGEX_MLINE;
     590          36 :                 break;
     591          24 :             case 'x':
     592          24 :                 v->value.like_regex.flags |= JSP_REGEX_WSPACE;
     593          24 :                 break;
     594          54 :             case 'q':
     595          54 :                 v->value.like_regex.flags |= JSP_REGEX_QUOTE;
     596          54 :                 break;
     597          18 :             default:
     598          18 :                 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(flags->val + i), flags->val + i)));
     603             :                 break;
     604             :         }
     605             :     }
     606             : 
     607             :     /* Convert flags to what pg_regcomp needs */
     608         132 :     if (!jspConvertRegexFlags(v->value.like_regex.flags, &cflags, escontext))
     609           0 :         return false;
     610             : 
     611             :     /* check regex validity */
     612             :     {
     613             :         regex_t     re_tmp;
     614             :         pg_wchar   *wpattern;
     615             :         int         wpattern_len;
     616             :         int         re_result;
     617             : 
     618         126 :         wpattern = (pg_wchar *) palloc((pattern->len + 1) * sizeof(pg_wchar));
     619         126 :         wpattern_len = pg_mb2wchar_with_len(pattern->val,
     620             :                                             wpattern,
     621             :                                             pattern->len);
     622             : 
     623         126 :         if ((re_result = pg_regcomp(&re_tmp, wpattern, wpattern_len, cflags,
     624             :                                     DEFAULT_COLLATION_OID)) != REG_OKAY)
     625             :         {
     626             :             char        errMsg[100];
     627             : 
     628           6 :             pg_regerror(re_result, &re_tmp, errMsg, sizeof(errMsg));
     629           6 :             ereturn(escontext, false,
     630             :                     (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
     631             :                      errmsg("invalid regular expression: %s", errMsg)));
     632             :         }
     633             : 
     634         120 :         pg_regfree(&re_tmp);
     635             :     }
     636             : 
     637         120 :     *result = v;
     638             : 
     639         120 :     return true;
     640             : }
     641             : 
     642             : /*
     643             :  * Convert from XQuery regex flags to those recognized by our regex library.
     644             :  */
     645             : bool
     646         408 : jspConvertRegexFlags(uint32 xflags, int *result, struct Node *escontext)
     647             : {
     648             :     /* By default, XQuery is very nearly the same as Spencer's AREs */
     649         408 :     int         cflags = REG_ADVANCED;
     650             : 
     651             :     /* Ignore-case means the same thing, too, modulo locale issues */
     652         408 :     if (xflags & JSP_REGEX_ICASE)
     653         114 :         cflags |= REG_ICASE;
     654             : 
     655             :     /* Per XQuery spec, if 'q' is specified then 'm', 's', 'x' are ignored */
     656         408 :     if (xflags & JSP_REGEX_QUOTE)
     657             :     {
     658         126 :         cflags &= ~REG_ADVANCED;
     659         126 :         cflags |= REG_QUOTE;
     660             :     }
     661             :     else
     662             :     {
     663             :         /* Note that dotall mode is the default in POSIX */
     664         282 :         if (!(xflags & JSP_REGEX_DOTALL))
     665         216 :             cflags |= REG_NLSTOP;
     666         282 :         if (xflags & JSP_REGEX_MLINE)
     667          60 :             cflags |= REG_NLANCH;
     668             : 
     669             :         /*
     670             :          * XQuery's 'x' mode is related to Spencer's expanded mode, but it's
     671             :          * not really enough alike to justify treating JSP_REGEX_WSPACE as
     672             :          * REG_EXPANDED.  For now we treat 'x' as unimplemented; perhaps in
     673             :          * future we'll modify the regex library to have an option for
     674             :          * XQuery-style ignore-whitespace mode.
     675             :          */
     676         282 :         if (xflags & JSP_REGEX_WSPACE)
     677           6 :             ereturn(escontext, false,
     678             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     679             :                      errmsg("XQuery \"x\" flag (expanded regular expressions) is not implemented")));
     680             :     }
     681             : 
     682         402 :     *result = cflags;
     683             : 
     684         402 :     return true;
     685             : }

Generated by: LCOV version 1.14