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