Age Owner Branch data TLA Line data Source code
1 : : %top{
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-2024, 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 : : /*
21 : : * NB: include jsonpath_gram.h only AFTER including jsonpath_internal.h,
22 : : * because jsonpath_internal.h contains the declaration for JsonPathString.
23 : : */
24 : : #include "jsonpath_internal.h"
25 : : #include "jsonpath_gram.h"
26 : :
27 : : #include "mb/pg_wchar.h"
28 : : #include "nodes/miscnodes.h"
29 : : #include "nodes/pg_list.h"
30 : : }
31 : :
32 : : %{
33 : : static JsonPathString scanstring;
34 : :
35 : : /* Handles to the buffer that the lexer uses internally */
36 : : static YY_BUFFER_STATE scanbufhandle;
37 : : static char *scanbuf;
38 : : static int scanbuflen;
39 : :
40 : : static void addstring(bool init, char *s, int l);
41 : : static void addchar(bool init, char c);
42 : : static enum yytokentype checkKeyword(void);
43 : : static bool parseUnicode(char *s, int l, struct Node *escontext);
44 : : static bool parseHexChar(char *s, struct Node *escontext);
45 : :
46 : : /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
47 : : #undef fprintf
48 : : #define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg)
49 : :
50 : : static void
1856 akorotkov@postgresql 51 :UBC 0 : fprintf_to_ereport(const char *fmt, const char *msg)
52 : : {
53 [ # # ]: 0 : ereport(ERROR, (errmsg_internal("%s", msg)));
54 : : }
55 : :
56 : : /* LCOV_EXCL_START */
57 : :
58 : : %}
59 : :
60 : : %option 8bit
61 : : %option never-interactive
62 : : %option nodefault
63 : : %option noinput
64 : : %option nounput
65 : : %option noyywrap
66 : : %option warn
67 : : %option prefix="jsonpath_yy"
68 : : %option bison-bridge
69 : : %option noyyalloc
70 : : %option noyyrealloc
71 : : %option noyyfree
72 : :
73 : : /*
74 : : * We use exclusive states for quoted and non-quoted strings,
75 : : * quoted variable names and C-style comments.
76 : : * Exclusive states:
77 : : * <xq> - quoted strings
78 : : * <xnq> - non-quoted strings
79 : : * <xvq> - quoted variable names
80 : : * <xc> - C-style comment
81 : : */
82 : :
83 : : %x xq
84 : : %x xnq
85 : : %x xvq
86 : : %x xc
87 : :
88 : : special [\?\%\$\.\[\]\{\}\(\)\|\&\!\=\<\>\@\#\,\*:\-\+\/]
89 : : blank [ \t\n\r\f]
90 : : /* "other" means anything that's not special, blank, or '\' or '"' */
91 : : other [^\?\%\$\.\[\]\{\}\(\)\|\&\!\=\<\>\@\#\,\*:\-\+\/\\\" \t\n\r\f]
92 : :
93 : : decdigit [0-9]
94 : : hexdigit [0-9A-Fa-f]
95 : : octdigit [0-7]
96 : : bindigit [0-1]
97 : :
98 : : /* DecimalInteger in ECMAScript; must not start with 0 unless it's exactly 0 */
99 : : decinteger (0|[1-9](_?{decdigit})*)
100 : : /* DecimalDigits in ECMAScript; only used as part of other rules */
101 : : decdigits {decdigit}(_?{decdigit})*
102 : : /* Non-decimal integers; in ECMAScript, these must not have underscore after prefix */
103 : : hexinteger 0[xX]{hexdigit}(_?{hexdigit})*
104 : : octinteger 0[oO]{octdigit}(_?{octdigit})*
105 : : bininteger 0[bB]{bindigit}(_?{bindigit})*
106 : :
107 : : decimal ({decinteger}\.{decdigits}?|\.{decdigits})
108 : : real ({decinteger}|{decimal})[Ee][-+]?{decdigits}
109 : : realfail ({decinteger}|{decimal})[Ee][-+]
110 : :
111 : : decinteger_junk {decinteger}{other}
112 : : decimal_junk {decimal}{other}
113 : : real_junk {real}{other}
114 : :
115 : : unicode \\u({hexdigit}{4}|\{{hexdigit}{1,6}\})
116 : : unicodefail \\u({hexdigit}{0,3}|\{{hexdigit}{0,6})
117 : : hex_char \\x{hexdigit}{2}
118 : : hex_fail \\x{hexdigit}{0,1}
119 : :
120 : : %%
121 : :
122 : : <xnq>{other}+ {
123 : : addstring(false, yytext, yyleng);
124 : : }
125 : :
126 : : <xnq>{blank}+ {
127 : : yylval->str = scanstring;
128 : : BEGIN INITIAL;
129 : : return checkKeyword();
130 : : }
131 : :
132 : : <xnq>\/\* {
133 : : yylval->str = scanstring;
134 : : BEGIN xc;
135 : : }
136 : :
137 : : <xnq>({special}|\") {
138 : : yylval->str = scanstring;
139 : : yyless(0);
140 : : BEGIN INITIAL;
141 : : return checkKeyword();
142 : : }
143 : :
144 : : <xnq><<EOF>> {
145 : : yylval->str = scanstring;
146 : : BEGIN INITIAL;
147 : : return checkKeyword();
148 : : }
149 : :
150 : : <xnq,xq,xvq>\\b { addchar(false, '\b'); }
151 : :
152 : : <xnq,xq,xvq>\\f { addchar(false, '\f'); }
153 : :
154 : : <xnq,xq,xvq>\\n { addchar(false, '\n'); }
155 : :
156 : : <xnq,xq,xvq>\\r { addchar(false, '\r'); }
157 : :
158 : : <xnq,xq,xvq>\\t { addchar(false, '\t'); }
159 : :
160 : : <xnq,xq,xvq>\\v { addchar(false, '\v'); }
161 : :
162 : : <xnq,xq,xvq>{unicode}+ {
163 : : if (!parseUnicode(yytext, yyleng, escontext))
164 : : yyterminate();
165 : : }
166 : :
167 : : <xnq,xq,xvq>{hex_char} {
168 : : if (!parseHexChar(yytext, escontext))
169 : : yyterminate();
170 : : }
171 : :
172 : : <xnq,xq,xvq>{unicode}*{unicodefail} {
173 : : jsonpath_yyerror(NULL, escontext,
174 : : "invalid Unicode escape sequence");
175 : : yyterminate();
176 : : }
177 : :
178 : : <xnq,xq,xvq>{hex_fail} {
179 : : jsonpath_yyerror(NULL, escontext,
180 : : "invalid hexadecimal character sequence");
181 : : yyterminate();
182 : : }
183 : :
184 : : <xnq,xq,xvq>{unicode}+\\ {
185 : : /* throw back the \\, and treat as unicode */
186 : : yyless(yyleng - 1);
187 : : if (!parseUnicode(yytext, yyleng, escontext))
188 : : yyterminate();
189 : : }
190 : :
191 : : <xnq,xq,xvq>\\. { addchar(false, yytext[1]); }
192 : :
193 : : <xnq,xq,xvq>\\ {
194 : : jsonpath_yyerror(NULL, escontext,
195 : : "unexpected end after backslash");
196 : : yyterminate();
197 : : }
198 : :
199 : : <xq,xvq><<EOF>> {
200 : : jsonpath_yyerror(NULL, escontext,
201 : : "unterminated quoted string");
202 : : yyterminate();
203 : : }
204 : :
205 : : <xq>\" {
206 : : yylval->str = scanstring;
207 : : BEGIN INITIAL;
208 : : return STRING_P;
209 : : }
210 : :
211 : : <xvq>\" {
212 : : yylval->str = scanstring;
213 : : BEGIN INITIAL;
214 : : return VARIABLE_P;
215 : : }
216 : :
217 : : <xq,xvq>[^\\\"]+ { addstring(false, yytext, yyleng); }
218 : :
219 : : <xc>\*\/ { BEGIN INITIAL; }
220 : :
221 : : <xc>[^\*]+ { }
222 : :
223 : : <xc>\* { }
224 : :
225 : : <xc><<EOF>> {
226 : : jsonpath_yyerror(
227 : : NULL, escontext,
228 : : "unexpected end of comment");
229 : : yyterminate();
230 : : }
231 : : \&\& { return AND_P; }
232 : :
233 : : \|\| { return OR_P; }
234 : :
235 : : \! { return NOT_P; }
236 : :
237 : : \*\* { return ANY_P; }
238 : :
239 : : \< { return LESS_P; }
240 : :
241 : : \<\= { return LESSEQUAL_P; }
242 : :
243 : : \=\= { return EQUAL_P; }
244 : :
245 : : \<\> { return NOTEQUAL_P; }
246 : :
247 : : \!\= { return NOTEQUAL_P; }
248 : :
249 : : \>\= { return GREATEREQUAL_P; }
250 : :
251 : : \> { return GREATER_P; }
252 : :
253 : : \${other}+ {
254 : : addstring(true, yytext + 1, yyleng - 1);
255 : : addchar(false, '\0');
256 : : yylval->str = scanstring;
257 : : return VARIABLE_P;
258 : : }
259 : :
260 : : \$\" {
261 : : addchar(true, '\0');
262 : : BEGIN xvq;
263 : : }
264 : :
265 : : {special} { return *yytext; }
266 : :
267 : : {blank}+ { /* ignore */ }
268 : :
269 : : \/\* {
270 : : addchar(true, '\0');
271 : : BEGIN xc;
272 : : }
273 : :
274 : : {real} {
275 : : addstring(true, yytext, yyleng);
276 : : addchar(false, '\0');
277 : : yylval->str = scanstring;
278 : : return NUMERIC_P;
279 : : }
280 : :
281 : : {decimal} {
282 : : addstring(true, yytext, yyleng);
283 : : addchar(false, '\0');
284 : : yylval->str = scanstring;
285 : : return NUMERIC_P;
286 : : }
287 : :
288 : : {decinteger} {
289 : : addstring(true, yytext, yyleng);
290 : : addchar(false, '\0');
291 : : yylval->str = scanstring;
292 : : return INT_P;
293 : : }
294 : :
295 : : {hexinteger} {
296 : : addstring(true, yytext, yyleng);
297 : : addchar(false, '\0');
298 : : yylval->str = scanstring;
299 : : return INT_P;
300 : : }
301 : :
302 : : {octinteger} {
303 : : addstring(true, yytext, yyleng);
304 : : addchar(false, '\0');
305 : : yylval->str = scanstring;
306 : : return INT_P;
307 : : }
308 : :
309 : : {bininteger} {
310 : : addstring(true, yytext, yyleng);
311 : : addchar(false, '\0');
312 : : yylval->str = scanstring;
313 : : return INT_P;
314 : : }
315 : :
316 : : {realfail} {
317 : : jsonpath_yyerror(
318 : : NULL, escontext,
319 : : "invalid numeric literal");
320 : : yyterminate();
321 : : }
322 : : {decinteger_junk} {
323 : : jsonpath_yyerror(
324 : : NULL, escontext,
325 : : "trailing junk after numeric literal");
326 : : yyterminate();
327 : : }
328 : : {decimal_junk} {
329 : : jsonpath_yyerror(
330 : : NULL, escontext,
331 : : "trailing junk after numeric literal");
332 : : yyterminate();
333 : : }
334 : : {real_junk} {
335 : : jsonpath_yyerror(
336 : : NULL, escontext,
337 : : "trailing junk after numeric literal");
338 : : yyterminate();
339 : : }
340 : : \" {
341 : : addchar(true, '\0');
342 : : BEGIN xq;
343 : : }
344 : :
345 : : \\ {
346 : : yyless(0);
347 : : addchar(true, '\0');
348 : : BEGIN xnq;
349 : : }
350 : :
351 : : {other}+ {
352 : : addstring(true, yytext, yyleng);
353 : : BEGIN xnq;
354 : : }
355 : :
356 : : <<EOF>> { yyterminate(); }
357 : :
358 : : %%
359 : :
360 : : /* LCOV_EXCL_STOP */
361 : :
362 : : void
477 andrew@dunslane.net 363 :CBC 162 : jsonpath_yyerror(JsonPathParseResult **result, struct Node *escontext,
364 : : const char *message)
365 : : {
366 : : /* don't overwrite escontext if it's already been set */
367 [ + + + - : 162 : if (SOFT_ERROR_OCCURRED(escontext))
+ + ]
368 : 9 : return;
369 : :
1856 akorotkov@postgresql 370 [ + + ]: 153 : if (*yytext == YY_END_OF_BUFFER_CHAR)
371 : : {
477 andrew@dunslane.net 372 [ + - ]: 30 : errsave(escontext,
373 : : (errcode(ERRCODE_SYNTAX_ERROR),
374 : : /* translator: %s is typically "syntax error" */
375 : : errmsg("%s at end of jsonpath input", _(message))));
376 : : }
377 : : else
378 : : {
379 [ + + ]: 123 : errsave(escontext,
380 : : (errcode(ERRCODE_SYNTAX_ERROR),
381 : : /* translator: first %s is typically "syntax error" */
382 : : errmsg("%s at or near \"%s\" of jsonpath input",
383 : : _(message), yytext)));
384 : : }
385 : : }
386 : :
387 : : typedef struct JsonPathKeyword
388 : : {
389 : : int16 len;
390 : : bool lowercase;
391 : : int val;
392 : : const char *keyword;
393 : : } JsonPathKeyword;
394 : :
395 : : /*
396 : : * Array of key words should be sorted by length and then
397 : : * alphabetical order
398 : : */
399 : : static const JsonPathKeyword keywords[] = {
400 : : { 2, false, IS_P, "is"},
401 : : { 2, false, TO_P, "to"},
402 : : { 3, false, ABS_P, "abs"},
403 : : { 3, false, LAX_P, "lax"},
404 : : { 4, false, DATE_P, "date"},
405 : : { 4, false, FLAG_P, "flag"},
406 : : { 4, false, LAST_P, "last"},
407 : : { 4, true, NULL_P, "null"},
408 : : { 4, false, SIZE_P, "size"},
409 : : { 4, false, TIME_P, "time"},
410 : : { 4, true, TRUE_P, "true"},
411 : : { 4, false, TYPE_P, "type"},
412 : : { 4, false, WITH_P, "with"},
413 : : { 5, true, FALSE_P, "false"},
414 : : { 5, false, FLOOR_P, "floor"},
415 : : { 6, false, BIGINT_P, "bigint"},
416 : : { 6, false, DOUBLE_P, "double"},
417 : : { 6, false, EXISTS_P, "exists"},
418 : : { 6, false, NUMBER_P, "number"},
419 : : { 6, false, STARTS_P, "starts"},
420 : : { 6, false, STRICT_P, "strict"},
421 : : { 6, false, STRINGFUNC_P, "string"},
422 : : { 7, false, BOOLEAN_P, "boolean"},
423 : : { 7, false, CEILING_P, "ceiling"},
424 : : { 7, false, DECIMAL_P, "decimal"},
425 : : { 7, false, INTEGER_P, "integer"},
426 : : { 7, false, TIME_TZ_P, "time_tz"},
427 : : { 7, false, UNKNOWN_P, "unknown"},
428 : : { 8, false, DATETIME_P, "datetime"},
429 : : { 8, false, KEYVALUE_P, "keyvalue"},
430 : : { 9, false, TIMESTAMP_P, "timestamp"},
431 : : { 10,false, LIKE_REGEX_P, "like_regex"},
432 : : { 12,false, TIMESTAMP_TZ_P, "timestamp_tz"},
433 : : };
434 : :
435 : : /* Check if current scanstring value is a keyword */
436 : : static enum yytokentype
1847 akorotkov@postgresql 437 : 5355 : checkKeyword()
438 : : {
702 peter@eisentraut.org 439 : 5355 : int res = IDENT_P;
440 : : int diff;
1853 akorotkov@postgresql 441 : 5355 : const JsonPathKeyword *StopLow = keywords,
442 : 5355 : *StopHigh = keywords + lengthof(keywords),
443 : : *StopMiddle;
444 : :
1856 445 [ + + ]: 5355 : if (scanstring.len > keywords[lengthof(keywords) - 1].len)
446 : 3 : return res;
447 : :
1847 448 [ + + ]: 28746 : while (StopLow < StopHigh)
449 : : {
1856 450 : 26931 : StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
451 : :
452 [ + + ]: 26931 : if (StopMiddle->len == scanstring.len)
453 : 10023 : diff = pg_strncasecmp(StopMiddle->keyword, scanstring.val,
454 : 10023 : scanstring.len);
455 : : else
456 : 16908 : diff = StopMiddle->len - scanstring.len;
457 : :
458 [ + + ]: 26931 : if (diff < 0)
459 : 7419 : StopLow = StopMiddle + 1;
460 [ + + ]: 19512 : else if (diff > 0)
461 : 15975 : StopHigh = StopMiddle;
462 : : else
463 : : {
464 [ + + ]: 3537 : if (StopMiddle->lowercase)
465 : 147 : diff = strncmp(StopMiddle->keyword, scanstring.val,
466 : 147 : scanstring.len);
467 : :
468 [ + - ]: 3537 : if (diff == 0)
469 : 3537 : res = StopMiddle->val;
470 : :
471 : 3537 : break;
472 : : }
473 : : }
474 : :
475 : 5352 : return res;
476 : : }
477 : :
478 : : /*
479 : : * Called before any actual parsing is done
480 : : */
481 : : static void
482 : 5000 : jsonpath_scanner_init(const char *str, int slen)
483 : : {
484 [ + + ]: 5000 : if (slen <= 0)
485 : 3 : slen = strlen(str);
486 : :
487 : : /*
488 : : * Might be left over after ereport()
489 : : */
490 : 5000 : yy_init_globals();
491 : :
492 : : /*
493 : : * Make a scan buffer with special termination needed by flex.
494 : : */
495 : :
496 : 5000 : scanbuflen = slen;
497 : 5000 : scanbuf = palloc(slen + 2);
498 : 5000 : memcpy(scanbuf, str, slen);
499 : 5000 : scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR;
500 : 5000 : scanbufhandle = yy_scan_buffer(scanbuf, slen + 2);
501 : :
502 : 5000 : BEGIN(INITIAL);
503 : 5000 : }
504 : :
505 : :
506 : : /*
507 : : * Called after parsing is done to clean up after jsonpath_scanner_init()
508 : : */
509 : : static void
510 : 4817 : jsonpath_scanner_finish(void)
511 : : {
512 : 4817 : yy_delete_buffer(scanbufhandle);
513 : 4817 : pfree(scanbuf);
514 : 4817 : }
515 : :
516 : : /*
517 : : * Resize scanstring so that it can append string of given length.
518 : : * Reinitialize if required.
519 : : */
520 : : static void
1847 521 : 10942 : resizeString(bool init, int appendLen)
522 : : {
1856 523 [ + + ]: 10942 : if (init)
524 : : {
1847 525 : 8084 : scanstring.total = Max(32, appendLen);
526 : 8084 : scanstring.val = (char *) palloc(scanstring.total);
1856 527 : 8084 : scanstring.len = 0;
528 : : }
529 : : else
530 : : {
1847 531 [ - + ]: 2858 : if (scanstring.len + appendLen >= scanstring.total)
532 : : {
1847 akorotkov@postgresql 533 [ # # ]:UBC 0 : while (scanstring.len + appendLen >= scanstring.total)
534 : 0 : scanstring.total *= 2;
1856 535 : 0 : scanstring.val = repalloc(scanstring.val, scanstring.total);
536 : : }
537 : : }
1856 akorotkov@postgresql 538 :CBC 10942 : }
539 : :
540 : : /* Add set of bytes at "s" of length "l" to scanstring */
541 : : static void
1847 542 : 8129 : addstring(bool init, char *s, int l)
543 : : {
544 : 8129 : resizeString(init, l + 1);
545 : 8129 : memcpy(scanstring.val + scanstring.len, s, l);
546 : 8129 : scanstring.len += l;
547 : 8129 : }
548 : :
549 : : /* Add single byte "c" to scanstring */
550 : : static void
551 : 2813 : addchar(bool init, char c)
552 : : {
553 : 2813 : resizeString(init, 1);
554 : 2813 : scanstring.val[scanstring.len] = c;
555 [ + + ]: 2813 : if (c != '\0')
1856 556 : 84 : scanstring.len++;
557 : 2813 : }
558 : :
559 : : /* Interface to jsonpath parser */
560 : : JsonPathParseResult *
477 andrew@dunslane.net 561 : 5000 : parsejsonpath(const char *str, int len, struct Node *escontext)
562 : : {
563 : : JsonPathParseResult *parseresult;
564 : :
1856 akorotkov@postgresql 565 : 5000 : jsonpath_scanner_init(str, len);
566 : :
477 andrew@dunslane.net 567 [ + + ]: 5000 : if (jsonpath_yyparse((void *) &parseresult, escontext) != 0)
279 peter@eisentraut.org 568 : 9 : jsonpath_yyerror(NULL, escontext, "invalid input"); /* shouldn't happen */
569 : :
1856 akorotkov@postgresql 570 : 4817 : jsonpath_scanner_finish();
571 : :
572 : 4817 : return parseresult;
573 : : }
574 : :
575 : : /* Turn hex character into integer */
576 : : static bool
477 andrew@dunslane.net 577 : 438 : hexval(char c, int *result, struct Node *escontext)
578 : : {
1856 akorotkov@postgresql 579 [ + - + + ]: 438 : if (c >= '0' && c <= '9')
580 : : {
477 andrew@dunslane.net 581 : 294 : *result = c - '0';
582 : 294 : return true;
583 : : }
1856 akorotkov@postgresql 584 [ + + + - ]: 144 : if (c >= 'a' && c <= 'f')
585 : : {
477 andrew@dunslane.net 586 : 126 : *result = c - 'a' + 0xA;
587 : 126 : return true;
588 : : }
1856 akorotkov@postgresql 589 [ + - + - ]: 18 : if (c >= 'A' && c <= 'F')
590 : : {
477 andrew@dunslane.net 591 : 18 : *result = c - 'A' + 0xA;
592 : 18 : return true;
593 : : }
477 andrew@dunslane.net 594 :UBC 0 : jsonpath_yyerror(NULL, escontext, "invalid hexadecimal digit");
595 : 0 : return false;
596 : : }
597 : :
598 : : /* Add given unicode character to scanstring */
599 : : static bool
477 andrew@dunslane.net 600 :CBC 72 : addUnicodeChar(int ch, struct Node *escontext)
601 : : {
1856 akorotkov@postgresql 602 [ + + ]: 72 : if (ch == 0)
603 : : {
604 : : /* We can't allow this, since our TEXT type doesn't */
477 andrew@dunslane.net 605 [ + - ]: 12 : ereturn(escontext, false,
606 : : (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
607 : : errmsg("unsupported Unicode escape sequence"),
608 : : errdetail("\\u0000 cannot be converted to text.")));
609 : : }
610 : : else
611 : : {
612 : : char cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1];
613 : :
614 : : /*
615 : : * If we're trapping the error status, call the noerror form of the
616 : : * conversion function. Otherwise call the normal form which provides
617 : : * more detailed errors.
618 : : */
619 : :
620 [ - + - - ]: 60 : if (! escontext || ! IsA(escontext, ErrorSaveContext))
621 : 60 : pg_unicode_to_server(ch, (unsigned char *) cbuf);
477 andrew@dunslane.net 622 [ # # ]:UBC 0 : else if (!pg_unicode_to_server_noerror(ch, (unsigned char *) cbuf))
623 [ # # ]: 0 : ereturn(escontext, false,
624 : : (errcode(ERRCODE_SYNTAX_ERROR),
625 : : errmsg("could not convert Unicode to server encoding")));
1500 tgl@sss.pgh.pa.us 626 :CBC 60 : addstring(false, cbuf, strlen(cbuf));
627 : : }
477 andrew@dunslane.net 628 : 60 : return true;
629 : : }
630 : :
631 : : /* Add unicode character, processing any surrogate pairs */
632 : : static bool
633 : 108 : addUnicode(int ch, int *hi_surrogate, struct Node *escontext)
634 : : {
1500 tgl@sss.pgh.pa.us 635 [ + + ]: 108 : if (is_utf16_surrogate_first(ch))
636 : : {
1856 akorotkov@postgresql 637 [ + + ]: 30 : if (*hi_surrogate != -1)
477 andrew@dunslane.net 638 [ + - ]: 6 : ereturn(escontext, false,
639 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
640 : : errmsg("invalid input syntax for type %s", "jsonpath"),
641 : : errdetail("Unicode high surrogate must not follow "
642 : : "a high surrogate.")));
1500 tgl@sss.pgh.pa.us 643 : 24 : *hi_surrogate = ch;
477 andrew@dunslane.net 644 : 24 : return true;
645 : : }
1500 tgl@sss.pgh.pa.us 646 [ + + ]: 78 : else if (is_utf16_surrogate_second(ch))
647 : : {
1856 akorotkov@postgresql 648 [ + + ]: 24 : if (*hi_surrogate == -1)
477 andrew@dunslane.net 649 [ + - ]: 12 : ereturn(escontext, false,
650 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
651 : : errmsg("invalid input syntax for type %s", "jsonpath"),
652 : : errdetail("Unicode low surrogate must follow a high "
653 : : "surrogate.")));
1500 tgl@sss.pgh.pa.us 654 : 12 : ch = surrogate_pair_to_codepoint(*hi_surrogate, ch);
1856 akorotkov@postgresql 655 : 12 : *hi_surrogate = -1;
656 : : }
657 [ - + ]: 54 : else if (*hi_surrogate != -1)
658 : : {
477 andrew@dunslane.net 659 [ # # ]:UBC 0 : ereturn(escontext, false,
660 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
661 : : errmsg("invalid input syntax for type %s", "jsonpath"),
662 : : errdetail("Unicode low surrogate must follow a high "
663 : : "surrogate.")));
664 : : }
665 : :
477 andrew@dunslane.net 666 :CBC 66 : return addUnicodeChar(ch, escontext);
667 : : }
668 : :
669 : : /*
670 : : * parseUnicode was adopted from json_lex_string() in
671 : : * src/backend/utils/adt/json.c
672 : : */
673 : : static bool
674 : 66 : parseUnicode(char *s, int l, struct Node *escontext)
675 : : {
1847 akorotkov@postgresql 676 : 66 : int i = 2;
1856 677 : 66 : int hi_surrogate = -1;
678 : :
679 [ + + ]: 144 : for (i = 2; i < l; i += 2) /* skip '\u' */
680 : : {
681 : 108 : int ch = 0;
682 : : int j, si;
683 : :
684 [ + + ]: 108 : if (s[i] == '{') /* parse '\u{XX...}' */
685 : : {
686 [ + + + - ]: 84 : while (s[++i] != '}' && i < l)
687 : : {
477 andrew@dunslane.net 688 [ - + ]: 66 : if (!hexval(s[i], &si, escontext))
477 andrew@dunslane.net 689 :UBC 0 : return false;
477 andrew@dunslane.net 690 :CBC 66 : ch = (ch << 4) | si;
691 : : }
1785 akapila@postgresql.o 692 : 18 : i++; /* skip '}' */
693 : : }
694 : : else /* parse '\uXXXX' */
695 : : {
1856 akorotkov@postgresql 696 [ + + + - ]: 450 : for (j = 0; j < 4 && i < l; j++)
697 : : {
477 andrew@dunslane.net 698 [ - + ]: 360 : if (!hexval(s[i++], &si, escontext))
477 andrew@dunslane.net 699 :UBC 0 : return false;
477 andrew@dunslane.net 700 :CBC 360 : ch = (ch << 4) | si;
701 : : }
702 : : }
703 : :
704 [ - + ]: 108 : if (! addUnicode(ch, &hi_surrogate, escontext))
477 andrew@dunslane.net 705 :UBC 0 : return false;
706 : : }
707 : :
1856 akorotkov@postgresql 708 [ + + ]:CBC 36 : if (hi_surrogate != -1)
709 : : {
477 andrew@dunslane.net 710 [ + - ]: 6 : ereturn(escontext, false,
711 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
712 : : errmsg("invalid input syntax for type %s", "jsonpath"),
713 : : errdetail("Unicode low surrogate must follow a high "
714 : : "surrogate.")));
715 : : }
716 : :
717 : 30 : return true;
718 : : }
719 : :
720 : : /* Parse sequence of hex-encoded characters */
721 : : static bool
722 : 6 : parseHexChar(char *s, struct Node *escontext)
723 : : {
724 : : int s2, s3, ch;
725 [ - + ]: 6 : if (!hexval(s[2], &s2, escontext))
477 andrew@dunslane.net 726 :UBC 0 : return false;
477 andrew@dunslane.net 727 [ - + ]:CBC 6 : if (!hexval(s[3], &s3, escontext))
477 andrew@dunslane.net 728 :UBC 0 : return false;
729 : :
477 andrew@dunslane.net 730 :CBC 6 : ch = (s2 << 4) | s3;
731 : :
732 : 6 : return addUnicodeChar(ch, escontext);
733 : : }
734 : :
735 : : /*
736 : : * Interface functions to make flex use palloc() instead of malloc().
737 : : * It'd be better to make these static, but flex insists otherwise.
738 : : */
739 : :
740 : : void *
1856 akorotkov@postgresql 741 : 10000 : jsonpath_yyalloc(yy_size_t bytes)
742 : : {
743 : 10000 : return palloc(bytes);
744 : : }
745 : :
746 : : void *
1856 akorotkov@postgresql 747 :UBC 0 : jsonpath_yyrealloc(void *ptr, yy_size_t bytes)
748 : : {
749 [ # # ]: 0 : if (ptr)
750 : 0 : return repalloc(ptr, bytes);
751 : : else
752 : 0 : return palloc(bytes);
753 : : }
754 : :
755 : : void
1856 akorotkov@postgresql 756 :CBC 4817 : jsonpath_yyfree(void *ptr)
757 : : {
758 [ + - ]: 4817 : if (ptr)
759 : 4817 : pfree(ptr);
760 : 4817 : }
|