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-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 {
146 michael@paquier.xyz 125 :GNC 7366 : *result = palloc_object(JsonPathParseResult);
2607 akorotkov@postgresql 126 :CBC 7366 : (*result)->expr = $2;
127 : 7366 : (*result)->lax = $1;
128 : : (void) yynerrs;
129 : : }
130 : 20 : | /* EMPTY */ { *result = NULL; }
131 : : ;
132 : :
133 : : expr_or_predicate:
134 : 7066 : expr { $$ = $1; }
135 : 300 : | predicate { $$ = $1; }
136 : : ;
137 : :
138 : : mode:
139 : 468 : STRICT_P { $$ = false; }
140 : 460 : | LAX_P { $$ = true; }
141 : 6638 : | /* 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); }
2001 peter@eisentraut.org 151 : 472 : | VARIABLE_P { $$ = makeItemVariable(&$1); }
152 : : ;
153 : :
154 : : comp_op:
2607 akorotkov@postgresql 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:
2598 164 : 48 : '(' predicate ')' { $$ = $2; }
2607 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); }
2001 peter@eisentraut.org 173 : 16 : | NOT_P delimited_predicate { $$ = makeItemUnary(jpiNot, $2); }
174 : : | '(' predicate ')' IS_P UNKNOWN_P
2598 akorotkov@postgresql 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;
1228 andrew@dunslane.net 181 [ - + ]: 12 : if (! makeItemLikeRegex($1, &$3, NULL, &jppitem, escontext))
1228 andrew@dunslane.net 182 :UBC 0 : YYABORT;
1228 andrew@dunslane.net 183 :CBC 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:
2607 akorotkov@postgresql 195 : 48 : STRING_P { $$ = makeItemString(&$1); }
196 : 4 : | VARIABLE_P { $$ = makeItemVariable(&$1); }
197 : : ;
198 : :
199 : : path_primary:
200 : 2604 : scalar_value { $$ = $1; }
201 : 7278 : | '$' { $$ = makeItemType(jpiRoot); }
202 : 1732 : | '@' { $$ = makeItemType(jpiCurrent); }
203 : 60 : | LAST_P { $$ = makeItemType(jpiLast); }
204 : : ;
205 : :
206 : : accessor_expr:
207 : 11674 : path_primary { $$ = list_make1($1); }
208 : 60 : | '(' expr ')' accessor_op { $$ = list_make2($2, $4); }
209 : 20 : | '(' predicate ')' accessor_op { $$ = list_make2($2, $4); }
210 : 9552 : | accessor_expr accessor_op { $$ = lappend($1, $2); }
211 : : ;
212 : :
213 : : expr:
214 : 11606 : accessor_expr { $$ = makeItemList($1); }
215 : 108 : | '(' expr ')' { $$ = $2; }
853 peter@eisentraut.org 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); }
2607 akorotkov@postgresql 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 : 1356 : '[' '*' ']' { $$ = makeItemType(jpiAnyArray); }
237 : 340 : | '[' index_list ']' { $$ = makeIndexArray($2); }
238 : : ;
239 : :
240 : : any_level:
1541 peter@eisentraut.org 241 : 188 : INT_P { $$ = pg_strtoint32($1.val); }
2607 akorotkov@postgresql 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 '}'
2598 249 : 92 : { $$ = makeAny($3, $5); }
250 : : ;
251 : :
252 : : accessor_op:
2607 253 : 2632 : '.' key { $$ = $2; }
254 : 116 : | '.' '*' { $$ = makeItemType(jpiAnyKey); }
255 : 1696 : | 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 : : {
831 andrew@dunslane.net 261 [ + + ]: 184 : if (list_length($4) == 0)
262 : 112 : $$ = makeItemBinary(jpiDecimal, NULL, NULL);
263 [ - + ]: 72 : else if (list_length($4) == 1)
831 andrew@dunslane.net 264 :UBC 0 : $$ = makeItemBinary(jpiDecimal, linitial($4), NULL);
831 andrew@dunslane.net 265 [ + - ]:CBC 72 : else if (list_length($4) == 2)
266 : 72 : $$ = makeItemBinary(jpiDecimal, linitial($4), lsecond($4));
267 : : else
831 andrew@dunslane.net 268 [ # # ]:UBC 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 ')'
831 andrew@dunslane.net 274 :CBC 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 ')'
33 andrew@dunslane.net 284 :GNC 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
831 andrew@dunslane.net 297 :CBC 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:
33 andrew@dunslane.net 305 :GNC 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; }
831 andrew@dunslane.net 311 :CBC 112 : | /* EMPTY */ { $$ = NULL; }
312 : : ;
313 : :
314 : : uint_elem:
315 : 184 : INT_P { $$ = makeItemNumeric(&$1); }
316 : : ;
317 : :
318 : : opt_uint_arg:
33 andrew@dunslane.net 319 :GNC 184 : uint_elem { $$ = $1; }
831 andrew@dunslane.net 320 :CBC 712 : | /* EMPTY */ { $$ = NULL; }
321 : : ;
322 : :
323 : : str_elem:
2414 akorotkov@postgresql 324 : 564 : STRING_P { $$ = makeItemString(&$1); }
325 : : ;
326 : :
327 : : opt_str_arg:
33 andrew@dunslane.net 328 :GNC 388 : str_elem { $$ = $1; }
2414 akorotkov@postgresql 329 :CBC 456 : | /* EMPTY */ { $$ = NULL; }
330 : : ;
331 : :
332 : : str_int_args:
33 andrew@dunslane.net 333 :GNC 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:
2607 akorotkov@postgresql 341 :CBC 2632 : 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:
853 peter@eisentraut.org 391 : 28 : ABS_P { $$ = jpiAbs; }
2607 akorotkov@postgresql 392 : 20 : | SIZE_P { $$ = jpiSize; }
853 peter@eisentraut.org 393 : 196 : | TYPE_P { $$ = jpiType; }
394 : 20 : | FLOOR_P { $$ = jpiFloor; }
2607 akorotkov@postgresql 395 : 80 : | DOUBLE_P { $$ = jpiDouble; }
396 : 24 : | CEILING_P { $$ = jpiCeiling; }
397 : 44 : | KEYVALUE_P { $$ = jpiKeyValue; }
831 andrew@dunslane.net 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; }
33 andrew@dunslane.net 404 :GNC 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 *
2598 akorotkov@postgresql 416 :CBC 25474 : makeItemType(JsonPathItemType type)
417 : : {
146 michael@paquier.xyz 418 :GNC 25474 : JsonPathParseItem *v = palloc_object(JsonPathParseItem);
419 : :
2603 akorotkov@postgresql 420 [ - + ]:CBC 25474 : CHECK_FOR_INTERRUPTS();
421 : :
422 : 25474 : v->type = type;
423 : 25474 : v->next = NULL;
424 : :
425 : 25474 : return v;
426 : : }
427 : :
428 : : static JsonPathParseItem *
429 : 3876 : makeItemString(JsonPathString *s)
430 : : {
431 : : JsonPathParseItem *v;
432 : :
433 [ + + ]: 3876 : if (s == NULL)
434 : : {
435 : 76 : v = makeItemType(jpiNull);
436 : : }
437 : : else
438 : : {
439 : 3800 : v = makeItemType(jpiString);
440 : 3800 : v->value.string.val = s->val;
441 : 3800 : v->value.string.len = s->len;
442 : : }
443 : :
444 : 3876 : 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 : 2632 : makeItemKey(JsonPathString *s)
461 : : {
462 : : JsonPathParseItem *v;
463 : :
464 : 2632 : v = makeItemString(s);
465 : 2632 : v->type = jpiKey;
466 : :
467 : 2632 : 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 : : {
1453 peter@eisentraut.org 488 : 120 : JsonPathParseItem *v = makeItemType(jpiBool);
489 : :
2603 akorotkov@postgresql 490 : 120 : v->value.boolean = val;
491 : :
492 : 120 : return v;
493 : : }
494 : :
495 : : static JsonPathParseItem *
2598 496 : 2908 : makeItemBinary(JsonPathItemType type, JsonPathParseItem *la, JsonPathParseItem *ra)
497 : : {
1453 peter@eisentraut.org 498 : 2908 : JsonPathParseItem *v = makeItemType(type);
499 : :
2603 akorotkov@postgresql 500 : 2908 : v->value.args.left = la;
501 : 2908 : v->value.args.right = ra;
502 : :
503 : 2908 : return v;
504 : : }
505 : :
506 : : static JsonPathParseItem *
2598 507 : 3776 : makeItemUnary(JsonPathItemType type, JsonPathParseItem *a)
508 : : {
509 : : JsonPathParseItem *v;
510 : :
2603 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 : 11606 : makeItemList(List *list)
532 : : {
533 : : JsonPathParseItem *head,
534 : : *end;
535 : : ListCell *cell;
536 : :
2486 tgl@sss.pgh.pa.us 537 : 11606 : head = end = (JsonPathParseItem *) linitial(list);
538 : :
539 [ + + ]: 11606 : if (list_length(list) == 1)
2603 akorotkov@postgresql 540 : 4566 : return head;
541 : :
542 : : /* append items to the end of already existing list */
543 [ + + ]: 7048 : while (end->next)
544 : 8 : end = end->next;
545 : :
2045 tgl@sss.pgh.pa.us 546 [ + - + + : 16672 : for_each_from(cell, list, 1)
+ + ]
547 : : {
2603 akorotkov@postgresql 548 : 9632 : JsonPathParseItem *c = (JsonPathParseItem *) lfirst(cell);
549 : :
550 : 9632 : end->next = c;
551 : 9632 : end = c;
552 : : }
553 : :
554 : 7040 : return head;
555 : : }
556 : :
557 : : static JsonPathParseItem *
558 : 340 : makeIndexArray(List *list)
559 : : {
1453 peter@eisentraut.org 560 : 340 : JsonPathParseItem *v = makeItemType(jpiIndexArray);
561 : : ListCell *cell;
562 : 340 : int i = 0;
563 : :
1357 tgl@sss.pgh.pa.us 564 [ - + ]: 340 : Assert(list != NIL);
2603 akorotkov@postgresql 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 : : {
1453 peter@eisentraut.org 572 : 372 : JsonPathParseItem *jpi = lfirst(cell);
573 : :
2603 akorotkov@postgresql 574 [ - + ]: 372 : 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 : : {
1453 peter@eisentraut.org 586 : 236 : JsonPathParseItem *v = makeItemType(jpiAny);
587 : :
2603 akorotkov@postgresql 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 : : {
1453 peter@eisentraut.org 599 : 100 : JsonPathParseItem *v = makeItemType(jpiLikeRegex);
600 : : int i;
601 : : int cflags;
602 : :
2603 akorotkov@postgresql 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. */
2422 tgl@sss.pgh.pa.us 608 : 100 : v->value.like_regex.flags = 0;
2603 akorotkov@postgresql 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':
2422 tgl@sss.pgh.pa.us 617 : 32 : v->value.like_regex.flags |= JSP_REGEX_DOTALL;
2603 akorotkov@postgresql 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;
2512 625 : 36 : case 'q':
626 : 36 : v->value.like_regex.flags |= JSP_REGEX_QUOTE;
627 : 36 : break;
2603 628 : 12 : default:
1228 andrew@dunslane.net 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 */
496 peter@eisentraut.org 640 [ - + ]: 88 : if (!jspConvertRegexFlags(v->value.like_regex.flags, &cflags, escontext))
496 peter@eisentraut.org 641 :UBC 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 : :
1228 andrew@dunslane.net 650 :CBC 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 */
2422 tgl@sss.pgh.pa.us 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)
1228 andrew@dunslane.net 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 : : }
|