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

Generated by: LCOV version 1.14