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