]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/jsonpath_gram.y
Get rid of jsonpath_gram.h and jsonpath_scanner.h
[postgresql] / src / backend / utils / adt / jsonpath_gram.y
1 %{
2 /*-------------------------------------------------------------------------
3  *
4  * jsonpath_gram.y
5  *       Grammar definitions for jsonpath datatype
6  *
7  * Copyright (c) 2019, PostgreSQL Global Development Group
8  *
9  * IDENTIFICATION
10  *      src/backend/utils/adt/jsonpath_gram.y
11  *
12  *-------------------------------------------------------------------------
13  */
14
15 #include "postgres.h"
16
17 #include "catalog/pg_collation.h"
18 #include "fmgr.h"
19 #include "miscadmin.h"
20 #include "nodes/pg_list.h"
21 #include "regex/regex.h"
22 #include "utils/builtins.h"
23 #include "utils/jsonpath.h"
24
25 /* struct JsonPathString is shared between scan and gram */
26 typedef struct JsonPathString
27 {
28         char       *val;
29         int                     len;
30         int                     total;
31 }                       JsonPathString;
32
33 union YYSTYPE;
34
35 /* flex 2.5.4 doesn't bother with a decl for this */
36 int     jsonpath_yylex(union YYSTYPE *yylval_param);
37 int     jsonpath_yyparse(JsonPathParseResult **result);
38 void jsonpath_yyerror(JsonPathParseResult **result, const char *message);
39
40 static JsonPathParseItem *makeItemType(int type);
41 static JsonPathParseItem *makeItemString(JsonPathString *s);
42 static JsonPathParseItem *makeItemVariable(JsonPathString *s);
43 static JsonPathParseItem *makeItemKey(JsonPathString *s);
44 static JsonPathParseItem *makeItemNumeric(JsonPathString *s);
45 static JsonPathParseItem *makeItemBool(bool val);
46 static JsonPathParseItem *makeItemBinary(int type, JsonPathParseItem *la,
47                                                                                  JsonPathParseItem *ra);
48 static JsonPathParseItem *makeItemUnary(int type, JsonPathParseItem *a);
49 static JsonPathParseItem *makeItemList(List *list);
50 static JsonPathParseItem *makeIndexArray(List *list);
51 static JsonPathParseItem *makeAny(int first, int last);
52 static JsonPathParseItem *makeItemLikeRegex(JsonPathParseItem *expr,
53                                                                                         JsonPathString *pattern,
54                                                                                         JsonPathString *flags);
55
56 /*
57  * Bison doesn't allocate anything that needs to live across parser calls,
58  * so we can easily have it use palloc instead of malloc.  This prevents
59  * memory leaks if we error out during parsing.  Note this only works with
60  * bison >= 2.0.  However, in bison 1.875 the default is to use alloca()
61  * if possible, so there's not really much problem anyhow, at least if
62  * you're building with gcc.
63  */
64 #define YYMALLOC palloc
65 #define YYFREE   pfree
66
67 %}
68
69 /* BISON Declarations */
70 %pure-parser
71 %expect 0
72 %name-prefix="jsonpath_yy"
73 %error-verbose
74 %parse-param {JsonPathParseResult **result}
75
76 %union {
77         JsonPathString          str;
78         List                            *elems;         /* list of JsonPathParseItem */
79         List                            *indexs;        /* list of integers */
80         JsonPathParseItem       *value;
81         JsonPathParseResult *result;
82         JsonPathItemType        optype;
83         bool                            boolean;
84         int                                     integer;
85 }
86
87 %token  <str>           TO_P NULL_P TRUE_P FALSE_P IS_P UNKNOWN_P EXISTS_P
88 %token  <str>           IDENT_P STRING_P NUMERIC_P INT_P VARIABLE_P
89 %token  <str>           OR_P AND_P NOT_P
90 %token  <str>           LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
91 %token  <str>           ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
92 %token  <str>           ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
93
94 %type   <result>        result
95
96 %type   <value>         scalar_value path_primary expr array_accessor
97                                         any_path accessor_op key predicate delimited_predicate
98                                         index_elem starts_with_initial expr_or_predicate
99
100 %type   <elems>         accessor_expr
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                                                                                 *result = palloc(sizeof(JsonPathParseResult));
126                                                                                 (*result)->expr = $2;
127                                                                                 (*result)->lax = $1;
128                                                                         }
129         | /* EMPTY */                                   { *result = NULL; }
130         ;
131
132 expr_or_predicate:
133         expr                                                    { $$ = $1; }
134         | predicate                                             { $$ = $1; }
135         ;
136
137 mode:
138         STRICT_P                                                { $$ = false; }
139         | LAX_P                                                 { $$ = true; }
140         | /* EMPTY */                                   { $$ = true; }
141         ;
142
143 scalar_value:
144         STRING_P                                                { $$ = makeItemString(&$1); }
145         | NULL_P                                                { $$ = makeItemString(NULL); }
146         | TRUE_P                                                { $$ = makeItemBool(true); }
147         | FALSE_P                                               { $$ = makeItemBool(false); }
148         | NUMERIC_P                                             { $$ = makeItemNumeric(&$1); }
149         | INT_P                                                 { $$ = makeItemNumeric(&$1); }
150         | VARIABLE_P                                    { $$ = makeItemVariable(&$1); }
151         ;
152
153 comp_op:
154         EQUAL_P                                                 { $$ = jpiEqual; }
155         | NOTEQUAL_P                                    { $$ = jpiNotEqual; }
156         | LESS_P                                                { $$ = jpiLess; }
157         | GREATER_P                                             { $$ = jpiGreater; }
158         | LESSEQUAL_P                                   { $$ = jpiLessOrEqual; }
159         | GREATEREQUAL_P                                { $$ = jpiGreaterOrEqual; }
160         ;
161
162 delimited_predicate:
163         '(' predicate ')'                                               { $$ = $2; }
164         | EXISTS_P '(' expr ')'                 { $$ = makeItemUnary(jpiExists, $3); }
165         ;
166
167 predicate:
168         delimited_predicate                             { $$ = $1; }
169         | expr comp_op expr                             { $$ = makeItemBinary($2, $1, $3); }
170         | predicate AND_P predicate             { $$ = makeItemBinary(jpiAnd, $1, $3); }
171         | predicate OR_P predicate              { $$ = makeItemBinary(jpiOr, $1, $3); }
172         | NOT_P delimited_predicate     { $$ = makeItemUnary(jpiNot, $2); }
173         | '(' predicate ')' IS_P UNKNOWN_P      { $$ = makeItemUnary(jpiIsUnknown, $2); }
174         | expr STARTS_P WITH_P starts_with_initial
175                 { $$ = makeItemBinary(jpiStartsWith, $1, $4); }
176         | expr LIKE_REGEX_P STRING_P    { $$ = makeItemLikeRegex($1, &$3, NULL); }
177         | expr LIKE_REGEX_P STRING_P FLAG_P STRING_P
178                                                                         { $$ = makeItemLikeRegex($1, &$3, &$5); }
179         ;
180
181 starts_with_initial:
182         STRING_P                                                { $$ = makeItemString(&$1); }
183         | VARIABLE_P                                    { $$ = makeItemVariable(&$1); }
184         ;
185
186 path_primary:
187         scalar_value                                    { $$ = $1; }
188         | '$'                                                   { $$ = makeItemType(jpiRoot); }
189         | '@'                                                   { $$ = makeItemType(jpiCurrent); }
190         | LAST_P                                                { $$ = makeItemType(jpiLast); }
191         ;
192
193 accessor_expr:
194         path_primary                                    { $$ = list_make1($1); }
195         | '(' expr ')' accessor_op              { $$ = list_make2($2, $4); }
196         | '(' predicate ')' accessor_op { $$ = list_make2($2, $4); }
197         | accessor_expr accessor_op             { $$ = lappend($1, $2); }
198         ;
199
200 expr:
201         accessor_expr                                   { $$ = makeItemList($1); }
202         | '(' expr ')'                                  { $$ = $2; }
203         | '+' expr %prec UMINUS                 { $$ = makeItemUnary(jpiPlus, $2); }
204         | '-' expr %prec UMINUS                 { $$ = makeItemUnary(jpiMinus, $2); }
205         | expr '+' expr                                 { $$ = makeItemBinary(jpiAdd, $1, $3); }
206         | expr '-' expr                                 { $$ = makeItemBinary(jpiSub, $1, $3); }
207         | expr '*' expr                                 { $$ = makeItemBinary(jpiMul, $1, $3); }
208         | expr '/' expr                                 { $$ = makeItemBinary(jpiDiv, $1, $3); }
209         | expr '%' expr                                 { $$ = makeItemBinary(jpiMod, $1, $3); }
210         ;
211
212 index_elem:
213         expr                                                    { $$ = makeItemBinary(jpiSubscript, $1, NULL); }
214         | expr TO_P expr                                { $$ = makeItemBinary(jpiSubscript, $1, $3); }
215         ;
216
217 index_list:
218         index_elem                                              { $$ = list_make1($1); }
219         | index_list ',' index_elem             { $$ = lappend($1, $3); }
220         ;
221
222 array_accessor:
223         '[' '*' ']'                                             { $$ = makeItemType(jpiAnyArray); }
224         | '[' index_list ']'                    { $$ = makeIndexArray($2); }
225         ;
226
227 any_level:
228         INT_P                                                   { $$ = pg_atoi($1.val, 4, 0); }
229         | LAST_P                                                { $$ = -1; }
230         ;
231
232 any_path:
233         ANY_P                                                   { $$ = makeAny(0, -1); }
234         | ANY_P '{' any_level '}'               { $$ = makeAny($3, $3); }
235         | ANY_P '{' any_level TO_P any_level '}'        { $$ = makeAny($3, $5); }
236         ;
237
238 accessor_op:
239         '.' key                                                 { $$ = $2; }
240         | '.' '*'                                               { $$ = makeItemType(jpiAnyKey); }
241         | array_accessor                                { $$ = $1; }
242         | '.' any_path                                  { $$ = $2; }
243         | '.' method '(' ')'                    { $$ = makeItemType($2); }
244         | '?' '(' predicate ')'                 { $$ = makeItemUnary(jpiFilter, $3); }
245         ;
246
247 key:
248         key_name                                                { $$ = makeItemKey(&$1); }
249         ;
250
251 key_name:
252         IDENT_P
253         | STRING_P
254         | TO_P
255         | NULL_P
256         | TRUE_P
257         | FALSE_P
258         | IS_P
259         | UNKNOWN_P
260         | EXISTS_P
261         | STRICT_P
262         | LAX_P
263         | ABS_P
264         | SIZE_P
265         | TYPE_P
266         | FLOOR_P
267         | DOUBLE_P
268         | CEILING_P
269         | KEYVALUE_P
270         | LAST_P
271         | STARTS_P
272         | WITH_P
273         | LIKE_REGEX_P
274         | FLAG_P
275         ;
276
277 method:
278         ABS_P                                                   { $$ = jpiAbs; }
279         | SIZE_P                                                { $$ = jpiSize; }
280         | TYPE_P                                                { $$ = jpiType; }
281         | FLOOR_P                                               { $$ = jpiFloor; }
282         | DOUBLE_P                                              { $$ = jpiDouble; }
283         | CEILING_P                                             { $$ = jpiCeiling; }
284         | KEYVALUE_P                                    { $$ = jpiKeyValue; }
285         ;
286 %%
287
288 static JsonPathParseItem*
289 makeItemType(int type)
290 {
291         JsonPathParseItem* v = palloc(sizeof(*v));
292
293         CHECK_FOR_INTERRUPTS();
294
295         v->type = type;
296         v->next = NULL;
297
298         return v;
299 }
300
301 static JsonPathParseItem*
302 makeItemString(JsonPathString *s)
303 {
304         JsonPathParseItem *v;
305
306         if (s == NULL)
307         {
308                 v = makeItemType(jpiNull);
309         }
310         else
311         {
312                 v = makeItemType(jpiString);
313                 v->value.string.val = s->val;
314                 v->value.string.len = s->len;
315         }
316
317         return v;
318 }
319
320 static JsonPathParseItem *
321 makeItemVariable(JsonPathString *s)
322 {
323         JsonPathParseItem *v;
324
325         v = makeItemType(jpiVariable);
326         v->value.string.val = s->val;
327         v->value.string.len = s->len;
328
329         return v;
330 }
331
332 static JsonPathParseItem *
333 makeItemKey(JsonPathString *s)
334 {
335         JsonPathParseItem *v;
336
337         v = makeItemString(s);
338         v->type = jpiKey;
339
340         return v;
341 }
342
343 static JsonPathParseItem *
344 makeItemNumeric(JsonPathString *s)
345 {
346         JsonPathParseItem               *v;
347
348         v = makeItemType(jpiNumeric);
349         v->value.numeric =
350                 DatumGetNumeric(DirectFunctionCall3(numeric_in,
351                                                                                         CStringGetDatum(s->val), 0, -1));
352
353         return v;
354 }
355
356 static JsonPathParseItem *
357 makeItemBool(bool val)
358 {
359         JsonPathParseItem *v = makeItemType(jpiBool);
360
361         v->value.boolean = val;
362
363         return v;
364 }
365
366 static JsonPathParseItem *
367 makeItemBinary(int type, JsonPathParseItem* la, JsonPathParseItem *ra)
368 {
369         JsonPathParseItem  *v = makeItemType(type);
370
371         v->value.args.left = la;
372         v->value.args.right = ra;
373
374         return v;
375 }
376
377 static JsonPathParseItem *
378 makeItemUnary(int type, JsonPathParseItem* a)
379 {
380         JsonPathParseItem  *v;
381
382         if (type == jpiPlus && a->type == jpiNumeric && !a->next)
383                 return a;
384
385         if (type == jpiMinus && a->type == jpiNumeric && !a->next)
386         {
387                 v = makeItemType(jpiNumeric);
388                 v->value.numeric =
389                         DatumGetNumeric(DirectFunctionCall1(numeric_uminus,
390                                                                                                 NumericGetDatum(a->value.numeric)));
391                 return v;
392         }
393
394         v = makeItemType(type);
395
396         v->value.arg = a;
397
398         return v;
399 }
400
401 static JsonPathParseItem *
402 makeItemList(List *list)
403 {
404         JsonPathParseItem *head, *end;
405         ListCell   *cell = list_head(list);
406
407         head = end = (JsonPathParseItem *) lfirst(cell);
408
409         if (!lnext(cell))
410                 return head;
411
412         /* append items to the end of already existing list */
413         while (end->next)
414                 end = end->next;
415
416         for_each_cell(cell, lnext(cell))
417         {
418                 JsonPathParseItem *c = (JsonPathParseItem *) lfirst(cell);
419
420                 end->next = c;
421                 end = c;
422         }
423
424         return head;
425 }
426
427 static JsonPathParseItem *
428 makeIndexArray(List *list)
429 {
430         JsonPathParseItem       *v = makeItemType(jpiIndexArray);
431         ListCell                        *cell;
432         int                                     i = 0;
433
434         Assert(list_length(list) > 0);
435         v->value.array.nelems = list_length(list);
436
437         v->value.array.elems = palloc(sizeof(v->value.array.elems[0]) *
438                                                                   v->value.array.nelems);
439
440         foreach(cell, list)
441         {
442                 JsonPathParseItem *jpi = lfirst(cell);
443
444                 Assert(jpi->type == jpiSubscript);
445
446                 v->value.array.elems[i].from = jpi->value.args.left;
447                 v->value.array.elems[i++].to = jpi->value.args.right;
448         }
449
450         return v;
451 }
452
453 static JsonPathParseItem *
454 makeAny(int first, int last)
455 {
456         JsonPathParseItem *v = makeItemType(jpiAny);
457
458         v->value.anybounds.first = (first >= 0) ? first : PG_UINT32_MAX;
459         v->value.anybounds.last = (last >= 0) ? last : PG_UINT32_MAX;
460
461         return v;
462 }
463
464 static JsonPathParseItem *
465 makeItemLikeRegex(JsonPathParseItem *expr, JsonPathString *pattern,
466                                   JsonPathString *flags)
467 {
468         JsonPathParseItem *v = makeItemType(jpiLikeRegex);
469         int                     i;
470         int                     cflags = REG_ADVANCED;
471
472         v->value.like_regex.expr = expr;
473         v->value.like_regex.pattern = pattern->val;
474         v->value.like_regex.patternlen = pattern->len;
475         v->value.like_regex.flags = 0;
476
477         for (i = 0; flags && i < flags->len; i++)
478         {
479                 switch (flags->val[i])
480                 {
481                         case 'i':
482                                 v->value.like_regex.flags |= JSP_REGEX_ICASE;
483                                 cflags |= REG_ICASE;
484                                 break;
485                         case 's':
486                                 v->value.like_regex.flags &= ~JSP_REGEX_MLINE;
487                                 v->value.like_regex.flags |= JSP_REGEX_SLINE;
488                                 cflags |= REG_NEWLINE;
489                                 break;
490                         case 'm':
491                                 v->value.like_regex.flags &= ~JSP_REGEX_SLINE;
492                                 v->value.like_regex.flags |= JSP_REGEX_MLINE;
493                                 cflags &= ~REG_NEWLINE;
494                                 break;
495                         case 'x':
496                                 v->value.like_regex.flags |= JSP_REGEX_WSPACE;
497                                 cflags |= REG_EXPANDED;
498                                 break;
499                         default:
500                                 yyerror(NULL, "unrecognized flag of LIKE_REGEX predicate");
501                                 break;
502                 }
503         }
504
505         /* check regex validity */
506         (void) RE_compile_and_cache(cstring_to_text_with_len(pattern->val,
507                                                                                                                  pattern->len),
508                                                                 cflags, DEFAULT_COLLATION_OID);
509
510         return v;
511 }
512
513 #include "jsonpath_scan.c"