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