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