LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_gram.y (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 187 187 100.0 %
Date: 2019-09-19 02:07:14 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, 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 "miscadmin.h"
      22             : #include "nodes/pg_list.h"
      23             : #include "regex/regex.h"
      24             : #include "utils/builtins.h"
      25             : #include "utils/jsonpath.h"
      26             : 
      27             : /* struct JsonPathString is shared between scan and gram */
      28             : typedef struct JsonPathString
      29             : {
      30             :     char       *val;
      31             :     int         len;
      32             :     int         total;
      33             : }           JsonPathString;
      34             : 
      35             : union YYSTYPE;
      36             : 
      37             : /* flex 2.5.4 doesn't bother with a decl for this */
      38             : int jsonpath_yylex(union YYSTYPE *yylval_param);
      39             : int jsonpath_yyparse(JsonPathParseResult **result);
      40             : void jsonpath_yyerror(JsonPathParseResult **result, const char *message);
      41             : 
      42             : static JsonPathParseItem *makeItemType(JsonPathItemType type);
      43             : static JsonPathParseItem *makeItemString(JsonPathString *s);
      44             : static JsonPathParseItem *makeItemVariable(JsonPathString *s);
      45             : static JsonPathParseItem *makeItemKey(JsonPathString *s);
      46             : static JsonPathParseItem *makeItemNumeric(JsonPathString *s);
      47             : static JsonPathParseItem *makeItemBool(bool val);
      48             : static JsonPathParseItem *makeItemBinary(JsonPathItemType type,
      49             :                                          JsonPathParseItem *la,
      50             :                                          JsonPathParseItem *ra);
      51             : static JsonPathParseItem *makeItemUnary(JsonPathItemType type,
      52             :                                         JsonPathParseItem *a);
      53             : static JsonPathParseItem *makeItemList(List *list);
      54             : static JsonPathParseItem *makeIndexArray(List *list);
      55             : static JsonPathParseItem *makeAny(int first, int last);
      56             : static JsonPathParseItem *makeItemLikeRegex(JsonPathParseItem *expr,
      57             :                                             JsonPathString *pattern,
      58             :                                             JsonPathString *flags);
      59             : 
      60             : /*
      61             :  * Bison doesn't allocate anything that needs to live across parser calls,
      62             :  * so we can easily have it use palloc instead of malloc.  This prevents
      63             :  * memory leaks if we error out during parsing.  Note this only works with
      64             :  * bison >= 2.0.  However, in bison 1.875 the default is to use alloca()
      65             :  * if possible, so there's not really much problem anyhow, at least if
      66             :  * you're building with gcc.
      67             :  */
      68             : #define YYMALLOC palloc
      69             : #define YYFREE   pfree
      70             : 
      71             : %}
      72             : 
      73             : /* BISON Declarations */
      74             : %pure-parser
      75             : %expect 0
      76             : %name-prefix="jsonpath_yy"
      77             : %error-verbose
      78             : %parse-param {JsonPathParseResult **result}
      79             : 
      80             : %union {
      81             :     JsonPathString      str;
      82             :     List               *elems;  /* list of JsonPathParseItem */
      83             :     List               *indexs; /* list of integers */
      84             :     JsonPathParseItem  *value;
      85             :     JsonPathParseResult *result;
      86             :     JsonPathItemType    optype;
      87             :     bool                boolean;
      88             :     int                 integer;
      89             : }
      90             : 
      91             : %token  <str>     TO_P NULL_P TRUE_P FALSE_P IS_P UNKNOWN_P EXISTS_P
      92             : %token  <str>     IDENT_P STRING_P NUMERIC_P INT_P VARIABLE_P
      93             : %token  <str>     OR_P AND_P NOT_P
      94             : %token  <str>     LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
      95             : %token  <str>     ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
      96             : %token  <str>     ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
      97             : 
      98             : %type   <result>  result
      99             : 
     100             : %type   <value>       scalar_value path_primary expr array_accessor
     101             :                     any_path accessor_op key predicate delimited_predicate
     102             :                     index_elem starts_with_initial expr_or_predicate
     103             : 
     104             : %type   <elems>       accessor_expr
     105             : 
     106             : %type   <indexs>  index_list
     107             : 
     108             : %type   <optype>  comp_op method
     109             : 
     110             : %type   <boolean> mode
     111             : 
     112             : %type   <str>     key_name
     113             : 
     114             : %type   <integer> any_level
     115             : 
     116             : %left   OR_P
     117             : %left   AND_P
     118             : %right  NOT_P
     119             : %left   '+' '-'
     120             : %left   '*' '/' '%'
     121             : %left   UMINUS
     122             : %nonassoc '(' ')'
     123             : 
     124             : /* Grammar follows */
     125             : %%
     126             : 
     127             : result:
     128             :     mode expr_or_predicate          {
     129        2272 :                                         *result = palloc(sizeof(JsonPathParseResult));
     130        2272 :                                         (*result)->expr = $2;
     131        2272 :                                         (*result)->lax = $1;
     132             :                                     }
     133           4 :     | /* EMPTY */                   { *result = NULL; }
     134             :     ;
     135             : 
     136             : expr_or_predicate:
     137        2020 :     expr                            { $$ = $1; }
     138         252 :     | predicate                     { $$ = $1; }
     139             :     ;
     140             : 
     141             : mode:
     142         232 :     STRICT_P                        { $$ = false; }
     143         304 :     | LAX_P                         { $$ = true; }
     144        1852 :     | /* EMPTY */                   { $$ = true; }
     145             :     ;
     146             : 
     147             : scalar_value:
     148         244 :     STRING_P                        { $$ = makeItemString(&$1); }
     149          76 :     | NULL_P                        { $$ = makeItemString(NULL); }
     150          92 :     | TRUE_P                        { $$ = makeItemBool(true); }
     151          12 :     | FALSE_P                       { $$ = makeItemBool(false); }
     152         248 :     | NUMERIC_P                     { $$ = makeItemNumeric(&$1); }
     153         800 :     | INT_P                         { $$ = makeItemNumeric(&$1); }
     154         196 :     | VARIABLE_P                    { $$ = makeItemVariable(&$1); }
     155             :     ;
     156             : 
     157             : comp_op:
     158         536 :     EQUAL_P                         { $$ = jpiEqual; }
     159           8 :     | NOTEQUAL_P                    { $$ = jpiNotEqual; }
     160         276 :     | LESS_P                        { $$ = jpiLess; }
     161         244 :     | GREATER_P                     { $$ = jpiGreater; }
     162          16 :     | LESSEQUAL_P                   { $$ = jpiLessOrEqual; }
     163          44 :     | GREATEREQUAL_P                { $$ = jpiGreaterOrEqual; }
     164             :     ;
     165             : 
     166             : delimited_predicate:
     167          48 :     '(' predicate ')'               { $$ = $2; }
     168         176 :     | EXISTS_P '(' expr ')'         { $$ = makeItemUnary(jpiExists, $3); }
     169             :     ;
     170             : 
     171             : predicate:
     172         208 :     delimited_predicate             { $$ = $1; }
     173        1076 :     | expr comp_op expr             { $$ = makeItemBinary($2, $1, $3); }
     174         112 :     | predicate AND_P predicate     { $$ = makeItemBinary(jpiAnd, $1, $3); }
     175          72 :     | predicate OR_P predicate      { $$ = makeItemBinary(jpiOr, $1, $3); }
     176          16 :     | NOT_P delimited_predicate     { $$ = makeItemUnary(jpiNot, $2); }
     177             :     | '(' predicate ')' IS_P UNKNOWN_P
     178          48 :                                     { $$ = makeItemUnary(jpiIsUnknown, $2); }
     179             :     | expr STARTS_P WITH_P starts_with_initial
     180          40 :                                     { $$ = makeItemBinary(jpiStartsWith, $1, $4); }
     181          12 :     | expr LIKE_REGEX_P STRING_P    { $$ = makeItemLikeRegex($1, &$3, NULL); }
     182             :     | expr LIKE_REGEX_P STRING_P FLAG_P STRING_P
     183          72 :                                     { $$ = makeItemLikeRegex($1, &$3, &$5); }
     184             :     ;
     185             : 
     186             : starts_with_initial:
     187          36 :     STRING_P                        { $$ = makeItemString(&$1); }
     188           4 :     | VARIABLE_P                    { $$ = makeItemVariable(&$1); }
     189             :     ;
     190             : 
     191             : path_primary:
     192        1668 :     scalar_value                    { $$ = $1; }
     193        2220 :     | '$'                           { $$ = makeItemType(jpiRoot); }
     194        1144 :     | '@'                           { $$ = makeItemType(jpiCurrent); }
     195          60 :     | LAST_P                        { $$ = makeItemType(jpiLast); }
     196             :     ;
     197             : 
     198             : accessor_expr:
     199        5092 :     path_primary                    { $$ = list_make1($1); }
     200          52 :     | '(' expr ')' accessor_op      { $$ = list_make2($2, $4); }
     201          20 :     | '(' predicate ')' accessor_op { $$ = list_make2($2, $4); }
     202        3924 :     | accessor_expr accessor_op     { $$ = lappend($1, $2); }
     203             :     ;
     204             : 
     205             : expr:
     206        5052 :     accessor_expr                   { $$ = makeItemList($1); }
     207          48 :     | '(' expr ')'                  { $$ = $2; }
     208         100 :     | '+' expr %prec UMINUS         { $$ = makeItemUnary(jpiPlus, $2); }
     209         148 :     | '-' expr %prec UMINUS         { $$ = makeItemUnary(jpiMinus, $2); }
     210         100 :     | expr '+' expr                 { $$ = makeItemBinary(jpiAdd, $1, $3); }
     211          40 :     | expr '-' expr                 { $$ = makeItemBinary(jpiSub, $1, $3); }
     212          32 :     | expr '*' expr                 { $$ = makeItemBinary(jpiMul, $1, $3); }
     213          24 :     | expr '/' expr                 { $$ = makeItemBinary(jpiDiv, $1, $3); }
     214          12 :     | expr '%' expr                 { $$ = makeItemBinary(jpiMod, $1, $3); }
     215             :     ;
     216             : 
     217             : index_elem:
     218         216 :     expr                            { $$ = makeItemBinary(jpiSubscript, $1, NULL); }
     219          28 :     | expr TO_P expr                { $$ = makeItemBinary(jpiSubscript, $1, $3); }
     220             :     ;
     221             : 
     222             : index_list:
     223         224 :     index_elem                      { $$ = list_make1($1); }
     224          20 :     | index_list ',' index_elem     { $$ = lappend($1, $3); }
     225             :     ;
     226             : 
     227             : array_accessor:
     228         560 :     '[' '*' ']'                     { $$ = makeItemType(jpiAnyArray); }
     229         224 :     | '[' index_list ']'            { $$ = makeIndexArray($2); }
     230             :     ;
     231             : 
     232             : any_level:
     233         188 :     INT_P                           { $$ = pg_atoi($1.val, 4, 0); }
     234          64 :     | LAST_P                        { $$ = -1; }
     235             :     ;
     236             : 
     237             : any_path:
     238          76 :     ANY_P                           { $$ = makeAny(0, -1); }
     239          68 :     | ANY_P '{' any_level '}'       { $$ = makeAny($3, $3); }
     240             :     | ANY_P '{' any_level TO_P any_level '}'
     241          92 :                                     { $$ = makeAny($3, $5); }
     242             :     ;
     243             : 
     244             : accessor_op:
     245        1720 :     '.' key                         { $$ = $2; }
     246          56 :     | '.' '*'                       { $$ = makeItemType(jpiAnyKey); }
     247         784 :     | array_accessor                { $$ = $1; }
     248         236 :     | '.' any_path                  { $$ = $2; }
     249         292 :     | '.' method '(' ')'            { $$ = makeItemType($2); }
     250         908 :     | '?' '(' predicate ')'         { $$ = makeItemUnary(jpiFilter, $3); }
     251             :     ;
     252             : 
     253             : key:
     254        1720 :     key_name                        { $$ = makeItemKey(&$1); }
     255             :     ;
     256             : 
     257             : key_name:
     258             :     IDENT_P
     259             :     | STRING_P
     260             :     | TO_P
     261             :     | NULL_P
     262             :     | TRUE_P
     263             :     | FALSE_P
     264             :     | IS_P
     265             :     | UNKNOWN_P
     266             :     | EXISTS_P
     267             :     | STRICT_P
     268             :     | LAX_P
     269             :     | ABS_P
     270             :     | SIZE_P
     271             :     | TYPE_P
     272             :     | FLOOR_P
     273             :     | DOUBLE_P
     274             :     | CEILING_P
     275             :     | KEYVALUE_P
     276             :     | LAST_P
     277             :     | STARTS_P
     278             :     | WITH_P
     279             :     | LIKE_REGEX_P
     280             :     | FLAG_P
     281             :     ;
     282             : 
     283             : method:
     284          28 :     ABS_P                           { $$ = jpiAbs; }
     285          20 :     | SIZE_P                        { $$ = jpiSize; }
     286          80 :     | TYPE_P                        { $$ = jpiType; }
     287          20 :     | FLOOR_P                       { $$ = jpiFloor; }
     288          76 :     | DOUBLE_P                      { $$ = jpiDouble; }
     289          24 :     | CEILING_P                     { $$ = jpiCeiling; }
     290          44 :     | KEYVALUE_P                    { $$ = jpiKeyValue; }
     291             :     ;
     292             : %%
     293             : 
     294             : /*
     295             :  * The helper functions below allocate and fill JsonPathParseItem's of various
     296             :  * types.
     297             :  */
     298             : 
     299             : static JsonPathParseItem *
     300       11388 : makeItemType(JsonPathItemType type)
     301             : {
     302       11388 :     JsonPathParseItem  *v = palloc(sizeof(*v));
     303             : 
     304       11388 :     CHECK_FOR_INTERRUPTS();
     305             : 
     306       11388 :     v->type = type;
     307       11388 :     v->next = NULL;
     308             : 
     309       11388 :     return v;
     310             : }
     311             : 
     312             : static JsonPathParseItem *
     313        2076 : makeItemString(JsonPathString *s)
     314             : {
     315             :     JsonPathParseItem  *v;
     316             : 
     317        2076 :     if (s == NULL)
     318             :     {
     319          76 :         v = makeItemType(jpiNull);
     320             :     }
     321             :     else
     322             :     {
     323        2000 :         v = makeItemType(jpiString);
     324        2000 :         v->value.string.val = s->val;
     325        2000 :         v->value.string.len = s->len;
     326             :     }
     327             : 
     328        2076 :     return v;
     329             : }
     330             : 
     331             : static JsonPathParseItem *
     332         200 : makeItemVariable(JsonPathString *s)
     333             : {
     334             :     JsonPathParseItem  *v;
     335             : 
     336         200 :     v = makeItemType(jpiVariable);
     337         200 :     v->value.string.val = s->val;
     338         200 :     v->value.string.len = s->len;
     339             : 
     340         200 :     return v;
     341             : }
     342             : 
     343             : static JsonPathParseItem *
     344        1720 : makeItemKey(JsonPathString *s)
     345             : {
     346             :     JsonPathParseItem  *v;
     347             : 
     348        1720 :     v = makeItemString(s);
     349        1720 :     v->type = jpiKey;
     350             : 
     351        1720 :     return v;
     352             : }
     353             : 
     354             : static JsonPathParseItem *
     355        1048 : makeItemNumeric(JsonPathString *s)
     356             : {
     357             :     JsonPathParseItem  *v;
     358             : 
     359        1048 :     v = makeItemType(jpiNumeric);
     360        1048 :     v->value.numeric =
     361        1048 :         DatumGetNumeric(DirectFunctionCall3(numeric_in,
     362             :                                             CStringGetDatum(s->val),
     363             :                                             ObjectIdGetDatum(InvalidOid),
     364             :                                             Int32GetDatum(-1)));
     365             : 
     366        1048 :     return v;
     367             : }
     368             : 
     369             : static JsonPathParseItem *
     370         104 : makeItemBool(bool val)
     371             : {
     372         104 :     JsonPathParseItem  *v = makeItemType(jpiBool);
     373             : 
     374         104 :     v->value.boolean = val;
     375             : 
     376         104 :     return v;
     377             : }
     378             : 
     379             : static JsonPathParseItem *
     380        1752 : makeItemBinary(JsonPathItemType type, JsonPathParseItem *la, JsonPathParseItem *ra)
     381             : {
     382        1752 :     JsonPathParseItem  *v = makeItemType(type);
     383             : 
     384        1752 :     v->value.args.left = la;
     385        1752 :     v->value.args.right = ra;
     386             : 
     387        1752 :     return v;
     388             : }
     389             : 
     390             : static JsonPathParseItem *
     391        1396 : makeItemUnary(JsonPathItemType type, JsonPathParseItem *a)
     392             : {
     393             :     JsonPathParseItem  *v;
     394             : 
     395        1396 :     if (type == jpiPlus && a->type == jpiNumeric && !a->next)
     396          64 :         return a;
     397             : 
     398        1332 :     if (type == jpiMinus && a->type == jpiNumeric && !a->next)
     399             :     {
     400          76 :         v = makeItemType(jpiNumeric);
     401          76 :         v->value.numeric =
     402          76 :             DatumGetNumeric(DirectFunctionCall1(numeric_uminus,
     403             :                                                 NumericGetDatum(a->value.numeric)));
     404          76 :         return v;
     405             :     }
     406             : 
     407        1256 :     v = makeItemType(type);
     408             : 
     409        1256 :     v->value.arg = a;
     410             : 
     411        1256 :     return v;
     412             : }
     413             : 
     414             : static JsonPathParseItem *
     415        5052 : makeItemList(List *list)
     416             : {
     417             :     JsonPathParseItem  *head,
     418             :                        *end;
     419             :     ListCell           *cell;
     420             : 
     421        5052 :     head = end = (JsonPathParseItem *) linitial(list);
     422             : 
     423        5052 :     if (list_length(list) == 1)
     424        2268 :         return head;
     425             : 
     426             :     /* append items to the end of already existing list */
     427        5576 :     while (end->next)
     428           8 :         end = end->next;
     429             : 
     430        6780 :     for_each_cell(cell, list, list_second_cell(list))
     431             :     {
     432        3996 :         JsonPathParseItem *c = (JsonPathParseItem *) lfirst(cell);
     433             : 
     434        3996 :         end->next = c;
     435        3996 :         end = c;
     436             :     }
     437             : 
     438        2784 :     return head;
     439             : }
     440             : 
     441             : static JsonPathParseItem *
     442         224 : makeIndexArray(List *list)
     443             : {
     444         224 :     JsonPathParseItem  *v = makeItemType(jpiIndexArray);
     445             :     ListCell           *cell;
     446         224 :     int                 i = 0;
     447             : 
     448             :     Assert(list_length(list) > 0);
     449         224 :     v->value.array.nelems = list_length(list);
     450             : 
     451         224 :     v->value.array.elems = palloc(sizeof(v->value.array.elems[0]) *
     452         224 :                                   v->value.array.nelems);
     453             : 
     454         468 :     foreach(cell, list)
     455             :     {
     456         244 :         JsonPathParseItem  *jpi = lfirst(cell);
     457             : 
     458             :         Assert(jpi->type == jpiSubscript);
     459             : 
     460         244 :         v->value.array.elems[i].from = jpi->value.args.left;
     461         244 :         v->value.array.elems[i++].to = jpi->value.args.right;
     462             :     }
     463             : 
     464         224 :     return v;
     465             : }
     466             : 
     467             : static JsonPathParseItem *
     468         236 : makeAny(int first, int last)
     469             : {
     470         236 :     JsonPathParseItem  *v = makeItemType(jpiAny);
     471             : 
     472         236 :     v->value.anybounds.first = (first >= 0) ? first : PG_UINT32_MAX;
     473         236 :     v->value.anybounds.last = (last >= 0) ? last : PG_UINT32_MAX;
     474             : 
     475         236 :     return v;
     476             : }
     477             : 
     478             : static JsonPathParseItem *
     479          84 : makeItemLikeRegex(JsonPathParseItem *expr, JsonPathString *pattern,
     480             :                   JsonPathString *flags)
     481             : {
     482          84 :     JsonPathParseItem  *v = makeItemType(jpiLikeRegex);
     483             :     int                 i;
     484             :     int                 cflags;
     485             : 
     486          84 :     v->value.like_regex.expr = expr;
     487          84 :     v->value.like_regex.pattern = pattern->val;
     488          84 :     v->value.like_regex.patternlen = pattern->len;
     489             : 
     490             :     /* Parse the flags string, convert to bitmask.  Duplicate flags are OK. */
     491          84 :     v->value.like_regex.flags = 0;
     492         192 :     for (i = 0; flags && i < flags->len; i++)
     493             :     {
     494         112 :         switch (flags->val[i])
     495             :         {
     496             :             case 'i':
     497          32 :                 v->value.like_regex.flags |= JSP_REGEX_ICASE;
     498          32 :                 break;
     499             :             case 's':
     500          24 :                 v->value.like_regex.flags |= JSP_REGEX_DOTALL;
     501          24 :                 break;
     502             :             case 'm':
     503          16 :                 v->value.like_regex.flags |= JSP_REGEX_MLINE;
     504          16 :                 break;
     505             :             case 'x':
     506           8 :                 v->value.like_regex.flags |= JSP_REGEX_WSPACE;
     507           8 :                 break;
     508             :             case 'q':
     509          28 :                 v->value.like_regex.flags |= JSP_REGEX_QUOTE;
     510          28 :                 break;
     511             :             default:
     512           4 :                 ereport(ERROR,
     513             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     514             :                          errmsg("invalid input syntax for type %s", "jsonpath"),
     515             :                          errdetail("unrecognized flag character \"%c\" in LIKE_REGEX predicate",
     516             :                                    flags->val[i])));
     517             :                 break;
     518             :         }
     519             :     }
     520             : 
     521             :     /* Convert flags to what RE_compile_and_cache needs */
     522          80 :     cflags = jspConvertRegexFlags(v->value.like_regex.flags);
     523             : 
     524             :     /* check regex validity */
     525          76 :     (void) RE_compile_and_cache(cstring_to_text_with_len(pattern->val,
     526             :                                                          pattern->len),
     527             :                                 cflags, DEFAULT_COLLATION_OID);
     528             : 
     529          72 :     return v;
     530             : }
     531             : 
     532             : /*
     533             :  * Convert from XQuery regex flags to those recognized by our regex library.
     534             :  */
     535             : int
     536         264 : jspConvertRegexFlags(uint32 xflags)
     537             : {
     538             :     /* By default, XQuery is very nearly the same as Spencer's AREs */
     539         264 :     int         cflags = REG_ADVANCED;
     540             : 
     541             :     /* Ignore-case means the same thing, too, modulo locale issues */
     542         264 :     if (xflags & JSP_REGEX_ICASE)
     543          68 :         cflags |= REG_ICASE;
     544             : 
     545             :     /* Per XQuery spec, if 'q' is specified then 'm', 's', 'x' are ignored */
     546         264 :     if (xflags & JSP_REGEX_QUOTE)
     547             :     {
     548          76 :         cflags &= ~REG_ADVANCED;
     549          76 :         cflags |= REG_QUOTE;
     550             :     }
     551             :     else
     552             :     {
     553             :         /* Note that dotall mode is the default in POSIX */
     554         188 :         if (!(xflags & JSP_REGEX_DOTALL))
     555         144 :             cflags |= REG_NLSTOP;
     556         188 :         if (xflags & JSP_REGEX_MLINE)
     557          40 :             cflags |= REG_NLANCH;
     558             : 
     559             :         /*
     560             :          * XQuery's 'x' mode is related to Spencer's expanded mode, but it's
     561             :          * not really enough alike to justify treating JSP_REGEX_WSPACE as
     562             :          * REG_EXPANDED.  For now we treat 'x' as unimplemented; perhaps in
     563             :          * future we'll modify the regex library to have an option for
     564             :          * XQuery-style ignore-whitespace mode.
     565             :          */
     566         188 :         if (xflags & JSP_REGEX_WSPACE)
     567           4 :             ereport(ERROR,
     568             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     569             :                      errmsg("XQuery \"x\" flag (expanded regular expressions) is not implemented")));
     570             :     }
     571             : 
     572         260 :     return cflags;
     573             : }
     574             : 
     575             : /*
     576             :  * jsonpath_scan.l is compiled as part of jsonpath_gram.y.  Currently, this is
     577             :  * unavoidable because jsonpath_gram does not create a .h file to export its
     578             :  * token symbols.  If these files ever grow large enough to be worth compiling
     579             :  * separately, that could be fixed; but for now it seems like useless
     580             :  * complication.
     581             :  */
     582             : 
     583             : #include "jsonpath_scan.c"

Generated by: LCOV version 1.13