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

Generated by: LCOV version 1.14