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