LCOV - code coverage report
Current view: top level - src/backend/utils/adt - jsonpath_gram.y (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 98.4 % 246 242
Test Date: 2026-04-07 14:16:30 Functions: 100.0 % 13 13
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1