Line data Source code
1 : %{
2 : /*-------------------------------------------------------------------------
3 : *
4 : * jsonpath_scan.l
5 : * Lexical parser for jsonpath datatype
6 : *
7 : * Splits jsonpath string into tokens represented as JsonPathString structs.
8 : * Decodes unicode and hex escaped strings.
9 : *
10 : * Copyright (c) 2019-2022, PostgreSQL Global Development Group
11 : *
12 : * IDENTIFICATION
13 : * src/backend/utils/adt/jsonpath_scan.l
14 : *
15 : *-------------------------------------------------------------------------
16 : */
17 :
18 : #include "postgres.h"
19 :
20 : #include "mb/pg_wchar.h"
21 : #include "nodes/pg_list.h"
22 :
23 : static JsonPathString scanstring;
24 :
25 : /* Handles to the buffer that the lexer uses internally */
26 : static YY_BUFFER_STATE scanbufhandle;
27 : static char *scanbuf;
28 : static int scanbuflen;
29 :
30 : static void addstring(bool init, char *s, int l);
31 : static void addchar(bool init, char s);
32 : static enum yytokentype checkKeyword(void);
33 : static void parseUnicode(char *s, int l);
34 : static void parseHexChar(char *s);
35 :
36 : /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
37 : #undef fprintf
38 : #define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg)
39 :
40 : static void
41 0 : fprintf_to_ereport(const char *fmt, const char *msg)
42 : {
43 0 : ereport(ERROR, (errmsg_internal("%s", msg)));
44 : }
45 :
46 : /* LCOV_EXCL_START */
47 :
48 : %}
49 :
50 : %option 8bit
51 : %option never-interactive
52 : %option nodefault
53 : %option noinput
54 : %option nounput
55 : %option noyywrap
56 : %option warn
57 : %option prefix="jsonpath_yy"
58 : %option bison-bridge
59 : %option noyyalloc
60 : %option noyyrealloc
61 : %option noyyfree
62 :
63 : /*
64 : * We use exclusive states for quoted and non-quoted strings,
65 : * quoted variable names and C-style comments.
66 : * Exclusive states:
67 : * <xq> - quoted strings
68 : * <xnq> - non-quoted strings
69 : * <xvq> - quoted variable names
70 : * <xc> - C-style comment
71 : */
72 :
73 : %x xq
74 : %x xnq
75 : %x xvq
76 : %x xc
77 :
78 : special [\?\%\$\.\[\]\{\}\(\)\|\&\!\=\<\>\@\#\,\*:\-\+\/]
79 : blank [ \t\n\r\f]
80 : /* "other" means anything that's not special, blank, or '\' or '"' */
81 : other [^\?\%\$\.\[\]\{\}\(\)\|\&\!\=\<\>\@\#\,\*:\-\+\/\\\" \t\n\r\f]
82 :
83 : digit [0-9]
84 : integer (0|[1-9]{digit}*)
85 : decimal ({integer}\.{digit}*|\.{digit}+)
86 : real ({integer}|{decimal})[Ee][-+]?{digit}+
87 : realfail ({integer}|{decimal})[Ee][-+]
88 :
89 : integer_junk {integer}{other}
90 : decimal_junk {decimal}{other}
91 : real_junk {real}{other}
92 :
93 : hex_dig [0-9A-Fa-f]
94 : unicode \\u({hex_dig}{4}|\{{hex_dig}{1,6}\})
95 : unicodefail \\u({hex_dig}{0,3}|\{{hex_dig}{0,6})
96 : hex_char \\x{hex_dig}{2}
97 : hex_fail \\x{hex_dig}{0,1}
98 :
99 : %%
100 :
101 : <xnq>{other}+ {
102 : addstring(false, yytext, yyleng);
103 : }
104 :
105 : <xnq>{blank}+ {
106 : yylval->str = scanstring;
107 : BEGIN INITIAL;
108 : return checkKeyword();
109 : }
110 :
111 : <xnq>\/\* {
112 : yylval->str = scanstring;
113 : BEGIN xc;
114 : }
115 :
116 : <xnq>({special}|\") {
117 : yylval->str = scanstring;
118 : yyless(0);
119 : BEGIN INITIAL;
120 : return checkKeyword();
121 : }
122 :
123 : <xnq><<EOF>> {
124 : yylval->str = scanstring;
125 : BEGIN INITIAL;
126 : return checkKeyword();
127 : }
128 :
129 : <xnq,xq,xvq>\\b { addchar(false, '\b'); }
130 :
131 : <xnq,xq,xvq>\\f { addchar(false, '\f'); }
132 :
133 : <xnq,xq,xvq>\\n { addchar(false, '\n'); }
134 :
135 : <xnq,xq,xvq>\\r { addchar(false, '\r'); }
136 :
137 : <xnq,xq,xvq>\\t { addchar(false, '\t'); }
138 :
139 : <xnq,xq,xvq>\\v { addchar(false, '\v'); }
140 :
141 : <xnq,xq,xvq>{unicode}+ { parseUnicode(yytext, yyleng); }
142 :
143 : <xnq,xq,xvq>{hex_char} { parseHexChar(yytext); }
144 :
145 : <xnq,xq,xvq>{unicode}*{unicodefail} { yyerror(NULL, "invalid unicode sequence"); }
146 :
147 : <xnq,xq,xvq>{hex_fail} { yyerror(NULL, "invalid hex character sequence"); }
148 :
149 : <xnq,xq,xvq>{unicode}+\\ {
150 : /* throw back the \\, and treat as unicode */
151 : yyless(yyleng - 1);
152 : parseUnicode(yytext, yyleng);
153 : }
154 :
155 : <xnq,xq,xvq>\\. { addchar(false, yytext[1]); }
156 :
157 : <xnq,xq,xvq>\\ { yyerror(NULL, "unexpected end after backslash"); }
158 :
159 : <xq,xvq><<EOF>> { yyerror(NULL, "unexpected end of quoted string"); }
160 :
161 : <xq>\" {
162 : yylval->str = scanstring;
163 : BEGIN INITIAL;
164 : return STRING_P;
165 : }
166 :
167 : <xvq>\" {
168 : yylval->str = scanstring;
169 : BEGIN INITIAL;
170 : return VARIABLE_P;
171 : }
172 :
173 : <xq,xvq>[^\\\"]+ { addstring(false, yytext, yyleng); }
174 :
175 : <xc>\*\/ { BEGIN INITIAL; }
176 :
177 : <xc>[^\*]+ { }
178 :
179 : <xc>\* { }
180 :
181 : <xc><<EOF>> { yyerror(NULL, "unexpected end of comment"); }
182 :
183 : \&\& { return AND_P; }
184 :
185 : \|\| { return OR_P; }
186 :
187 : \! { return NOT_P; }
188 :
189 : \*\* { return ANY_P; }
190 :
191 : \< { return LESS_P; }
192 :
193 : \<\= { return LESSEQUAL_P; }
194 :
195 : \=\= { return EQUAL_P; }
196 :
197 : \<\> { return NOTEQUAL_P; }
198 :
199 : \!\= { return NOTEQUAL_P; }
200 :
201 : \>\= { return GREATEREQUAL_P; }
202 :
203 : \> { return GREATER_P; }
204 :
205 : \${other}+ {
206 : addstring(true, yytext + 1, yyleng - 1);
207 : addchar(false, '\0');
208 : yylval->str = scanstring;
209 : return VARIABLE_P;
210 : }
211 :
212 : \$\" {
213 : addchar(true, '\0');
214 : BEGIN xvq;
215 : }
216 :
217 : {special} { return *yytext; }
218 :
219 : {blank}+ { /* ignore */ }
220 :
221 : \/\* {
222 : addchar(true, '\0');
223 : BEGIN xc;
224 : }
225 :
226 : {real} {
227 : addstring(true, yytext, yyleng);
228 : addchar(false, '\0');
229 : yylval->str = scanstring;
230 : return NUMERIC_P;
231 : }
232 :
233 : {decimal} {
234 : addstring(true, yytext, yyleng);
235 : addchar(false, '\0');
236 : yylval->str = scanstring;
237 : return NUMERIC_P;
238 : }
239 :
240 : {integer} {
241 : addstring(true, yytext, yyleng);
242 : addchar(false, '\0');
243 : yylval->str = scanstring;
244 : return INT_P;
245 : }
246 :
247 : {realfail} { yyerror(NULL, "invalid numeric literal"); }
248 : {integer_junk} { yyerror(NULL, "trailing junk after numeric literal"); }
249 : {decimal_junk} { yyerror(NULL, "trailing junk after numeric literal"); }
250 : {real_junk} { yyerror(NULL, "trailing junk after numeric literal"); }
251 :
252 : \" {
253 : addchar(true, '\0');
254 : BEGIN xq;
255 : }
256 :
257 : \\ {
258 : yyless(0);
259 : addchar(true, '\0');
260 : BEGIN xnq;
261 : }
262 :
263 : {other}+ {
264 : addstring(true, yytext, yyleng);
265 : BEGIN xnq;
266 : }
267 :
268 : <<EOF>> { yyterminate(); }
269 :
270 : %%
271 :
272 : /* LCOV_EXCL_STOP */
273 :
274 : void
275 90 : jsonpath_yyerror(JsonPathParseResult **result, const char *message)
276 : {
277 90 : if (*yytext == YY_END_OF_BUFFER_CHAR)
278 : {
279 0 : ereport(ERROR,
280 : (errcode(ERRCODE_SYNTAX_ERROR),
281 : /* translator: %s is typically "syntax error" */
282 : errmsg("%s at end of jsonpath input", _(message))));
283 : }
284 : else
285 : {
286 90 : ereport(ERROR,
287 : (errcode(ERRCODE_SYNTAX_ERROR),
288 : /* translator: first %s is typically "syntax error" */
289 : errmsg("%s at or near \"%s\" of jsonpath input",
290 : _(message), yytext)));
291 : }
292 : }
293 :
294 : typedef struct JsonPathKeyword
295 : {
296 : int16 len;
297 : bool lowercase;
298 : int val;
299 : const char *keyword;
300 : } JsonPathKeyword;
301 :
302 : /*
303 : * Array of key words should be sorted by length and then
304 : * alphabetical order
305 : */
306 : static const JsonPathKeyword keywords[] = {
307 : { 2, false, IS_P, "is"},
308 : { 2, false, TO_P, "to"},
309 : { 3, false, ABS_P, "abs"},
310 : { 3, false, LAX_P, "lax"},
311 : { 4, false, FLAG_P, "flag"},
312 : { 4, false, LAST_P, "last"},
313 : { 4, true, NULL_P, "null"},
314 : { 4, false, SIZE_P, "size"},
315 : { 4, true, TRUE_P, "true"},
316 : { 4, false, TYPE_P, "type"},
317 : { 4, false, WITH_P, "with"},
318 : { 5, true, FALSE_P, "false"},
319 : { 5, false, FLOOR_P, "floor"},
320 : { 6, false, DOUBLE_P, "double"},
321 : { 6, false, EXISTS_P, "exists"},
322 : { 6, false, STARTS_P, "starts"},
323 : { 6, false, STRICT_P, "strict"},
324 : { 7, false, CEILING_P, "ceiling"},
325 : { 7, false, UNKNOWN_P, "unknown"},
326 : { 8, false, DATETIME_P, "datetime"},
327 : { 8, false, KEYVALUE_P, "keyvalue"},
328 : { 10,false, LIKE_REGEX_P, "like_regex"},
329 : };
330 :
331 : /* Check if current scanstring value is a keyword */
332 : static enum yytokentype
333 7266 : checkKeyword()
334 : {
335 7266 : int res = IDENT_P;
336 : int diff;
337 7266 : const JsonPathKeyword *StopLow = keywords,
338 7266 : *StopHigh = keywords + lengthof(keywords),
339 : *StopMiddle;
340 :
341 7266 : if (scanstring.len > keywords[lengthof(keywords) - 1].len)
342 6 : return res;
343 :
344 35334 : while (StopLow < StopHigh)
345 : {
346 32154 : StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
347 :
348 32154 : if (StopMiddle->len == scanstring.len)
349 9378 : diff = pg_strncasecmp(StopMiddle->keyword, scanstring.val,
350 9378 : scanstring.len);
351 : else
352 22776 : diff = StopMiddle->len - scanstring.len;
353 :
354 32154 : if (diff < 0)
355 7032 : StopLow = StopMiddle + 1;
356 25122 : else if (diff > 0)
357 21042 : StopHigh = StopMiddle;
358 : else
359 : {
360 4080 : if (StopMiddle->lowercase)
361 270 : diff = strncmp(StopMiddle->keyword, scanstring.val,
362 270 : scanstring.len);
363 :
364 4080 : if (diff == 0)
365 4080 : res = StopMiddle->val;
366 :
367 4080 : break;
368 : }
369 : }
370 :
371 7260 : return res;
372 : }
373 :
374 : /*
375 : * Called before any actual parsing is done
376 : */
377 : static void
378 6822 : jsonpath_scanner_init(const char *str, int slen)
379 : {
380 6822 : if (slen <= 0)
381 6 : slen = strlen(str);
382 :
383 : /*
384 : * Might be left over after ereport()
385 : */
386 6822 : yy_init_globals();
387 :
388 : /*
389 : * Make a scan buffer with special termination needed by flex.
390 : */
391 :
392 6822 : scanbuflen = slen;
393 6822 : scanbuf = palloc(slen + 2);
394 6822 : memcpy(scanbuf, str, slen);
395 6822 : scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
396 6822 : scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
397 :
398 6822 : BEGIN(INITIAL);
399 6822 : }
400 :
401 :
402 : /*
403 : * Called after parsing is done to clean up after jsonpath_scanner_init()
404 : */
405 : static void
406 6642 : jsonpath_scanner_finish(void)
407 : {
408 6642 : yy_delete_buffer(scanbufhandle);
409 6642 : pfree(scanbuf);
410 6642 : }
411 :
412 : /*
413 : * Resize scanstring so that it can append string of given length.
414 : * Reinitialize if required.
415 : */
416 : static void
417 15684 : resizeString(bool init, int appendLen)
418 : {
419 15684 : if (init)
420 : {
421 11346 : scanstring.total = Max(32, appendLen);
422 11346 : scanstring.val = (char *) palloc(scanstring.total);
423 11346 : scanstring.len = 0;
424 : }
425 : else
426 : {
427 4338 : if (scanstring.len + appendLen >= scanstring.total)
428 : {
429 0 : while (scanstring.len + appendLen >= scanstring.total)
430 0 : scanstring.total *= 2;
431 0 : scanstring.val = repalloc(scanstring.val, scanstring.total);
432 : }
433 : }
434 15684 : }
435 :
436 : /* Add set of bytes at "s" of length "l" to scanstring */
437 : static void
438 11436 : addstring(bool init, char *s, int l)
439 : {
440 11436 : resizeString(init, l + 1);
441 11436 : memcpy(scanstring.val + scanstring.len, s, l);
442 11436 : scanstring.len += l;
443 11436 : }
444 :
445 : /* Add single byte "c" to scanstring */
446 : static void
447 4248 : addchar(bool init, char c)
448 : {
449 4248 : resizeString(init, 1);
450 4248 : scanstring.val[scanstring.len] = c;
451 4248 : if (c != '\0')
452 168 : scanstring.len++;
453 4248 : }
454 :
455 : /* Interface to jsonpath parser */
456 : JsonPathParseResult *
457 6822 : parsejsonpath(const char *str, int len)
458 : {
459 : JsonPathParseResult *parseresult;
460 :
461 6822 : jsonpath_scanner_init(str, len);
462 :
463 6822 : if (jsonpath_yyparse((void *) &parseresult) != 0)
464 0 : jsonpath_yyerror(NULL, "bogus input"); /* shouldn't happen */
465 :
466 6642 : jsonpath_scanner_finish();
467 :
468 6642 : return parseresult;
469 : }
470 :
471 : /* Turn hex character into integer */
472 : static int
473 876 : hexval(char c)
474 : {
475 876 : if (c >= '0' && c <= '9')
476 588 : return c - '0';
477 288 : if (c >= 'a' && c <= 'f')
478 252 : return c - 'a' + 0xA;
479 36 : if (c >= 'A' && c <= 'F')
480 36 : return c - 'A' + 0xA;
481 0 : jsonpath_yyerror(NULL, "invalid hexadecimal digit");
482 0 : return 0; /* not reached */
483 : }
484 :
485 : /* Add given unicode character to scanstring */
486 : static void
487 144 : addUnicodeChar(int ch)
488 : {
489 144 : if (ch == 0)
490 : {
491 : /* We can't allow this, since our TEXT type doesn't */
492 24 : ereport(ERROR,
493 : (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
494 : errmsg("unsupported Unicode escape sequence"),
495 : errdetail("\\u0000 cannot be converted to text.")));
496 : }
497 : else
498 : {
499 : char cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1];
500 :
501 120 : pg_unicode_to_server(ch, (unsigned char *) cbuf);
502 120 : addstring(false, cbuf, strlen(cbuf));
503 : }
504 120 : }
505 :
506 : /* Add unicode character, processing any surrogate pairs */
507 : static void
508 216 : addUnicode(int ch, int *hi_surrogate)
509 : {
510 216 : if (is_utf16_surrogate_first(ch))
511 : {
512 60 : if (*hi_surrogate != -1)
513 12 : ereport(ERROR,
514 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
515 : errmsg("invalid input syntax for type %s", "jsonpath"),
516 : errdetail("Unicode high surrogate must not follow "
517 : "a high surrogate.")));
518 48 : *hi_surrogate = ch;
519 48 : return;
520 : }
521 156 : else if (is_utf16_surrogate_second(ch))
522 : {
523 48 : if (*hi_surrogate == -1)
524 24 : ereport(ERROR,
525 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
526 : errmsg("invalid input syntax for type %s", "jsonpath"),
527 : errdetail("Unicode low surrogate must follow a high "
528 : "surrogate.")));
529 24 : ch = surrogate_pair_to_codepoint(*hi_surrogate, ch);
530 24 : *hi_surrogate = -1;
531 : }
532 108 : else if (*hi_surrogate != -1)
533 : {
534 0 : ereport(ERROR,
535 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
536 : errmsg("invalid input syntax for type %s", "jsonpath"),
537 : errdetail("Unicode low surrogate must follow a high "
538 : "surrogate.")));
539 : }
540 :
541 132 : addUnicodeChar(ch);
542 : }
543 :
544 : /*
545 : * parseUnicode was adopted from json_lex_string() in
546 : * src/backend/utils/adt/json.c
547 : */
548 : static void
549 132 : parseUnicode(char *s, int l)
550 : {
551 132 : int i = 2;
552 132 : int hi_surrogate = -1;
553 :
554 288 : for (i = 2; i < l; i += 2) /* skip '\u' */
555 : {
556 216 : int ch = 0;
557 : int j;
558 :
559 216 : if (s[i] == '{') /* parse '\u{XX...}' */
560 : {
561 168 : while (s[++i] != '}' && i < l)
562 132 : ch = (ch << 4) | hexval(s[i]);
563 36 : i++; /* skip '}' */
564 : }
565 : else /* parse '\uXXXX' */
566 : {
567 900 : for (j = 0; j < 4 && i < l; j++)
568 720 : ch = (ch << 4) | hexval(s[i++]);
569 : }
570 :
571 216 : addUnicode(ch, &hi_surrogate);
572 : }
573 :
574 72 : if (hi_surrogate != -1)
575 : {
576 12 : ereport(ERROR,
577 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
578 : errmsg("invalid input syntax for type %s", "jsonpath"),
579 : errdetail("Unicode low surrogate must follow a high "
580 : "surrogate.")));
581 : }
582 60 : }
583 :
584 : /* Parse sequence of hex-encoded characters */
585 : static void
586 12 : parseHexChar(char *s)
587 : {
588 12 : int ch = (hexval(s[2]) << 4) |
589 12 : hexval(s[3]);
590 :
591 12 : addUnicodeChar(ch);
592 12 : }
593 :
594 : /*
595 : * Interface functions to make flex use palloc() instead of malloc().
596 : * It'd be better to make these static, but flex insists otherwise.
597 : */
598 :
599 : void *
600 13644 : jsonpath_yyalloc(yy_size_t bytes)
601 : {
602 13644 : return palloc(bytes);
603 : }
604 :
605 : void *
606 0 : jsonpath_yyrealloc(void *ptr, yy_size_t bytes)
607 : {
608 0 : if (ptr)
609 0 : return repalloc(ptr, bytes);
610 : else
611 0 : return palloc(bytes);
612 : }
613 :
614 : void
615 6642 : jsonpath_yyfree(void *ptr)
616 : {
617 6642 : if (ptr)
618 6642 : pfree(ptr);
619 6642 : }
|