2 /*-------------------------------------------------------------------------
5 * Grammar definitions for jsonpath datatype
7 * Copyright (c) 2019, PostgreSQL Global Development Group
10 * src/backend/utils/adt/jsonpath_gram.y
12 *-------------------------------------------------------------------------
17 #include "catalog/pg_collation.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"
25 /* struct JsonPathString is shared between scan and gram */
26 typedef struct JsonPathString
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);
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);
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.
64 #define YYMALLOC palloc
69 /* BISON Declarations */
72 %name-prefix="jsonpath_yy"
74 %parse-param {JsonPathParseResult **result}
78 List *elems; /* list of JsonPathParseItem */
79 List *indexs; /* list of integers */
80 JsonPathParseItem *value;
81 JsonPathParseResult *result;
82 JsonPathItemType optype;
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
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
100 %type <elems> accessor_expr
102 %type <indexs> index_list
104 %type <optype> comp_op method
110 %type <integer> any_level
120 /* Grammar follows */
124 mode expr_or_predicate {
125 *result = palloc(sizeof(JsonPathParseResult));
126 (*result)->expr = $2;
129 | /* EMPTY */ { *result = NULL; }
134 | predicate { $$ = $1; }
138 STRICT_P { $$ = false; }
139 | LAX_P { $$ = true; }
140 | /* EMPTY */ { $$ = true; }
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); }
154 EQUAL_P { $$ = jpiEqual; }
155 | NOTEQUAL_P { $$ = jpiNotEqual; }
156 | LESS_P { $$ = jpiLess; }
157 | GREATER_P { $$ = jpiGreater; }
158 | LESSEQUAL_P { $$ = jpiLessOrEqual; }
159 | GREATEREQUAL_P { $$ = jpiGreaterOrEqual; }
163 '(' predicate ')' { $$ = $2; }
164 | EXISTS_P '(' expr ')' { $$ = makeItemUnary(jpiExists, $3); }
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); }
182 STRING_P { $$ = makeItemString(&$1); }
183 | VARIABLE_P { $$ = makeItemVariable(&$1); }
187 scalar_value { $$ = $1; }
188 | '$' { $$ = makeItemType(jpiRoot); }
189 | '@' { $$ = makeItemType(jpiCurrent); }
190 | LAST_P { $$ = makeItemType(jpiLast); }
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); }
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); }
213 expr { $$ = makeItemBinary(jpiSubscript, $1, NULL); }
214 | expr TO_P expr { $$ = makeItemBinary(jpiSubscript, $1, $3); }
218 index_elem { $$ = list_make1($1); }
219 | index_list ',' index_elem { $$ = lappend($1, $3); }
223 '[' '*' ']' { $$ = makeItemType(jpiAnyArray); }
224 | '[' index_list ']' { $$ = makeIndexArray($2); }
228 INT_P { $$ = pg_atoi($1.val, 4, 0); }
229 | LAST_P { $$ = -1; }
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); }
240 | '.' '*' { $$ = makeItemType(jpiAnyKey); }
241 | array_accessor { $$ = $1; }
242 | '.' any_path { $$ = $2; }
243 | '.' method '(' ')' { $$ = makeItemType($2); }
244 | '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
248 key_name { $$ = makeItemKey(&$1); }
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; }
288 static JsonPathParseItem*
289 makeItemType(int type)
291 JsonPathParseItem* v = palloc(sizeof(*v));
293 CHECK_FOR_INTERRUPTS();
301 static JsonPathParseItem*
302 makeItemString(JsonPathString *s)
304 JsonPathParseItem *v;
308 v = makeItemType(jpiNull);
312 v = makeItemType(jpiString);
313 v->value.string.val = s->val;
314 v->value.string.len = s->len;
320 static JsonPathParseItem *
321 makeItemVariable(JsonPathString *s)
323 JsonPathParseItem *v;
325 v = makeItemType(jpiVariable);
326 v->value.string.val = s->val;
327 v->value.string.len = s->len;
332 static JsonPathParseItem *
333 makeItemKey(JsonPathString *s)
335 JsonPathParseItem *v;
337 v = makeItemString(s);
343 static JsonPathParseItem *
344 makeItemNumeric(JsonPathString *s)
346 JsonPathParseItem *v;
348 v = makeItemType(jpiNumeric);
350 DatumGetNumeric(DirectFunctionCall3(numeric_in,
351 CStringGetDatum(s->val), 0, -1));
356 static JsonPathParseItem *
357 makeItemBool(bool val)
359 JsonPathParseItem *v = makeItemType(jpiBool);
361 v->value.boolean = val;
366 static JsonPathParseItem *
367 makeItemBinary(int type, JsonPathParseItem* la, JsonPathParseItem *ra)
369 JsonPathParseItem *v = makeItemType(type);
371 v->value.args.left = la;
372 v->value.args.right = ra;
377 static JsonPathParseItem *
378 makeItemUnary(int type, JsonPathParseItem* a)
380 JsonPathParseItem *v;
382 if (type == jpiPlus && a->type == jpiNumeric && !a->next)
385 if (type == jpiMinus && a->type == jpiNumeric && !a->next)
387 v = makeItemType(jpiNumeric);
389 DatumGetNumeric(DirectFunctionCall1(numeric_uminus,
390 NumericGetDatum(a->value.numeric)));
394 v = makeItemType(type);
401 static JsonPathParseItem *
402 makeItemList(List *list)
404 JsonPathParseItem *head, *end;
405 ListCell *cell = list_head(list);
407 head = end = (JsonPathParseItem *) lfirst(cell);
412 /* append items to the end of already existing list */
416 for_each_cell(cell, lnext(cell))
418 JsonPathParseItem *c = (JsonPathParseItem *) lfirst(cell);
427 static JsonPathParseItem *
428 makeIndexArray(List *list)
430 JsonPathParseItem *v = makeItemType(jpiIndexArray);
434 Assert(list_length(list) > 0);
435 v->value.array.nelems = list_length(list);
437 v->value.array.elems = palloc(sizeof(v->value.array.elems[0]) *
438 v->value.array.nelems);
442 JsonPathParseItem *jpi = lfirst(cell);
444 Assert(jpi->type == jpiSubscript);
446 v->value.array.elems[i].from = jpi->value.args.left;
447 v->value.array.elems[i++].to = jpi->value.args.right;
453 static JsonPathParseItem *
454 makeAny(int first, int last)
456 JsonPathParseItem *v = makeItemType(jpiAny);
458 v->value.anybounds.first = (first >= 0) ? first : PG_UINT32_MAX;
459 v->value.anybounds.last = (last >= 0) ? last : PG_UINT32_MAX;
464 static JsonPathParseItem *
465 makeItemLikeRegex(JsonPathParseItem *expr, JsonPathString *pattern,
466 JsonPathString *flags)
468 JsonPathParseItem *v = makeItemType(jpiLikeRegex);
470 int cflags = REG_ADVANCED;
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;
477 for (i = 0; flags && i < flags->len; i++)
479 switch (flags->val[i])
482 v->value.like_regex.flags |= JSP_REGEX_ICASE;
486 v->value.like_regex.flags &= ~JSP_REGEX_MLINE;
487 v->value.like_regex.flags |= JSP_REGEX_SLINE;
488 cflags |= REG_NEWLINE;
491 v->value.like_regex.flags &= ~JSP_REGEX_SLINE;
492 v->value.like_regex.flags |= JSP_REGEX_MLINE;
493 cflags &= ~REG_NEWLINE;
496 v->value.like_regex.flags |= JSP_REGEX_WSPACE;
497 cflags |= REG_EXPANDED;
500 yyerror(NULL, "unrecognized flag of LIKE_REGEX predicate");
505 /* check regex validity */
506 (void) RE_compile_and_cache(cstring_to_text_with_len(pattern->val,
508 cflags, DEFAULT_COLLATION_OID);
513 #include "jsonpath_scan.c"