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