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-2025, 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
2366 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}+ {
256 peter@eisentraut.org 123 :CBC 3 : addstring(false, yytext, yyleng, yyscanner);
124 : : }
2366 akorotkov@postgresql 125 : 3 :
2357 126 : 1890 : <xnq>{blank}+ {
256 peter@eisentraut.org 127 : 1890 : yylval->str = yyextra->scanstring;
2357 akorotkov@postgresql 128 : 1890 : BEGIN INITIAL;
256 peter@eisentraut.org 129 : 1890 : return checkKeyword(yyscanner);
130 : : }
131 : :
2357 akorotkov@postgresql 132 :UBC 0 : <xnq>\/\* {
256 peter@eisentraut.org 133 : 0 : yylval->str = yyextra->scanstring;
2357 akorotkov@postgresql 134 : 0 : BEGIN xc;
135 : : }
2366 136 : 0 :
2178 tgl@sss.pgh.pa.us 137 :CBC 2964 : <xnq>({special}|\") {
256 peter@eisentraut.org 138 : 2964 : yylval->str = yyextra->scanstring;
2357 akorotkov@postgresql 139 : 2964 : yyless(0);
140 : 2964 : BEGIN INITIAL;
256 peter@eisentraut.org 141 : 2964 : return checkKeyword(yyscanner);
142 : : }
143 : :
2357 akorotkov@postgresql 144 : 651 : <xnq><<EOF>> {
256 peter@eisentraut.org 145 : 651 : yylval->str = yyextra->scanstring;
2357 akorotkov@postgresql 146 : 651 : BEGIN INITIAL;
256 peter@eisentraut.org 147 : 651 : return checkKeyword(yyscanner);
148 : : }
149 : :
150 : 3 : <xnq,xq,xvq>\\b { addchar(false, '\b', yyscanner); }
2366 akorotkov@postgresql 151 : 3 :
256 peter@eisentraut.org 152 : 3 : <xnq,xq,xvq>\\f { addchar(false, '\f', yyscanner); }
2366 akorotkov@postgresql 153 : 3 :
256 peter@eisentraut.org 154 : 3 : <xnq,xq,xvq>\\n { addchar(false, '\n', yyscanner); }
2366 akorotkov@postgresql 155 : 3 :
256 peter@eisentraut.org 156 : 3 : <xnq,xq,xvq>\\r { addchar(false, '\r', yyscanner); }
2366 akorotkov@postgresql 157 : 3 :
256 peter@eisentraut.org 158 : 6 : <xnq,xq,xvq>\\t { addchar(false, '\t', yyscanner); }
2366 akorotkov@postgresql 159 : 6 :
256 peter@eisentraut.org 160 : 3 : <xnq,xq,xvq>\\v { addchar(false, '\v', yyscanner); }
2366 akorotkov@postgresql 161 : 3 :
987 andrew@dunslane.net 162 : 63 : <xnq,xq,xvq>{unicode}+ {
256 peter@eisentraut.org 163 [ - + ]: 63 : if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
987 andrew@dunslane.net 164 :UBC 0 : yyterminate();
165 : : }
2366 akorotkov@postgresql 166 :CBC 27 :
987 andrew@dunslane.net 167 : 6 : <xnq,xq,xvq>{hex_char} {
256 peter@eisentraut.org 168 [ - + ]: 6 : if (!parseHexChar(yytext, escontext, yyscanner))
987 andrew@dunslane.net 169 :UBC 0 : yyterminate();
170 : : }
2357 akorotkov@postgresql 171 :CBC 6 :
987 andrew@dunslane.net 172 : 18 : <xnq,xq,xvq>{unicode}*{unicodefail} {
256 peter@eisentraut.org 173 : 18 : jsonpath_yyerror(NULL, escontext, yyscanner,
174 : : "invalid Unicode escape sequence");
987 andrew@dunslane.net 175 :UBC 0 : yyterminate();
176 : : }
177 : :
178 : 0 : <xnq,xq,xvq>{hex_fail} {
256 peter@eisentraut.org 179 : 0 : jsonpath_yyerror(NULL, escontext, yyscanner,
180 : : "invalid hexadecimal character sequence");
987 andrew@dunslane.net 181 : 0 : yyterminate();
182 : : }
183 : :
2178 tgl@sss.pgh.pa.us 184 :CBC 3 : <xnq,xq,xvq>{unicode}+\\ {
185 : : /* throw back the \\, and treat as unicode */
186 : 3 : yyless(yyleng - 1);
256 peter@eisentraut.org 187 [ - + ]: 3 : if (!parseUnicode(yytext, yyleng, escontext, yyscanner))
987 andrew@dunslane.net 188 :UBC 0 : yyterminate();
189 : : }
2366 akorotkov@postgresql 190 :CBC 3 :
256 peter@eisentraut.org 191 : 63 : <xnq,xq,xvq>\\. { addchar(false, yytext[1], yyscanner); }
2366 akorotkov@postgresql 192 : 63 :
987 andrew@dunslane.net 193 :UBC 0 : <xnq,xq,xvq>\\ {
256 peter@eisentraut.org 194 : 0 : jsonpath_yyerror(NULL, escontext, yyscanner,
195 : : "unexpected end after backslash");
281 196 : 0 : yyterminate();
197 : : }
198 : :
987 andrew@dunslane.net 199 : 0 : <xq,xvq><<EOF>> {
256 peter@eisentraut.org 200 : 0 : jsonpath_yyerror(NULL, escontext, yyscanner,
201 : : "unterminated quoted string");
281 202 : 0 : yyterminate();
203 : : }
204 : :
2357 akorotkov@postgresql 205 :CBC 899 : <xq>\" {
256 peter@eisentraut.org 206 : 899 : yylval->str = yyextra->scanstring;
2357 akorotkov@postgresql 207 : 899 : BEGIN INITIAL;
208 : 899 : return STRING_P;
209 : : }
210 : :
211 : 51 : <xvq>\" {
256 peter@eisentraut.org 212 : 51 : yylval->str = yyextra->scanstring;
2366 akorotkov@postgresql 213 : 51 : BEGIN INITIAL;
2357 214 : 51 : return VARIABLE_P;
215 : : }
216 : :
256 peter@eisentraut.org 217 : 986 : <xq,xvq>[^\\\"]+ { addstring(false, yytext, yyleng, yyscanner); }
2357 akorotkov@postgresql 218 : 986 :
2357 akorotkov@postgresql 219 :UBC 0 : <xc>\*\/ { BEGIN INITIAL; }
2366 220 : 0 :
2357 221 : 0 : <xc>[^\*]+ { }
2366 222 : 0 :
2357 223 : 0 : <xc>\* { }
2366 224 : 0 :
987 andrew@dunslane.net 225 : 0 : <xc><<EOF>> {
256 peter@eisentraut.org 226 : 0 : jsonpath_yyerror(NULL, escontext, yyscanner,
227 : : "unexpected end of comment");
987 andrew@dunslane.net 228 : 0 : yyterminate();
229 : : }
2357 akorotkov@postgresql 230 :CBC 93 : \&\& { return AND_P; }
2366 231 : 93 :
2357 232 : 54 : \|\| { return OR_P; }
233 : :
234 : 12 : \! { return NOT_P; }
235 : :
236 : 177 : \*\* { return ANY_P; }
237 : :
238 : 378 : \< { return LESS_P; }
239 : :
240 : 21 : \<\= { return LESSEQUAL_P; }
241 : :
242 : 501 : \=\= { return EQUAL_P; }
243 : :
2357 akorotkov@postgresql 244 :UBC 0 : \<\> { return NOTEQUAL_P; }
245 : :
2357 akorotkov@postgresql 246 :CBC 6 : \!\= { return NOTEQUAL_P; }
247 : :
248 : 168 : \>\= { return GREATEREQUAL_P; }
249 : :
250 : 222 : \> { return GREATER_P; }
251 : :
2178 tgl@sss.pgh.pa.us 252 : 306 : \${other}+ {
256 peter@eisentraut.org 253 : 306 : addstring(true, yytext + 1, yyleng - 1, yyscanner);
254 : 306 : addchar(false, '\0', yyscanner);
255 : 306 : yylval->str = yyextra->scanstring;
2357 akorotkov@postgresql 256 : 306 : return VARIABLE_P;
257 : : }
258 : :
259 : 51 : \$\" {
256 peter@eisentraut.org 260 : 51 : addchar(true, '\0', yyscanner);
2357 akorotkov@postgresql 261 : 51 : BEGIN xvq;
262 : : }
2366 263 : 51 :
2357 264 : 23665 : {special} { return *yytext; }
265 : :
266 : 5103 : {blank}+ { /* ignore */ }
267 : 5103 :
2357 akorotkov@postgresql 268 :UBC 0 : \/\* {
256 peter@eisentraut.org 269 : 0 : addchar(true, '\0', yyscanner);
2357 akorotkov@postgresql 270 : 0 : BEGIN xc;
271 : : }
272 : 0 :
2357 akorotkov@postgresql 273 :CBC 150 : {real} {
256 peter@eisentraut.org 274 : 150 : addstring(true, yytext, yyleng, yyscanner);
275 : 150 : addchar(false, '\0', yyscanner);
276 : 150 : yylval->str = yyextra->scanstring;
2357 akorotkov@postgresql 277 : 150 : return NUMERIC_P;
278 : : }
279 : :
280 : 129 : {decimal} {
256 peter@eisentraut.org 281 : 129 : addstring(true, yytext, yyleng, yyscanner);
282 : 129 : addchar(false, '\0', yyscanner);
283 : 129 : yylval->str = yyextra->scanstring;
2357 akorotkov@postgresql 284 : 129 : return NUMERIC_P;
285 : : }
286 : :
916 peter@eisentraut.org 287 : 1140 : {decinteger} {
256 288 : 1140 : addstring(true, yytext, yyleng, yyscanner);
289 : 1140 : addchar(false, '\0', yyscanner);
290 : 1140 : yylval->str = yyextra->scanstring;
916 291 : 1140 : return INT_P;
292 : : }
293 : :
294 : 6 : {hexinteger} {
256 295 : 6 : addstring(true, yytext, yyleng, yyscanner);
296 : 6 : addchar(false, '\0', yyscanner);
297 : 6 : yylval->str = yyextra->scanstring;
916 298 : 6 : return INT_P;
299 : : }
300 : :
301 : 6 : {octinteger} {
256 302 : 6 : addstring(true, yytext, yyleng, yyscanner);
303 : 6 : addchar(false, '\0', yyscanner);
304 : 6 : yylval->str = yyextra->scanstring;
916 305 : 6 : return INT_P;
306 : : }
307 : :
308 : 6 : {bininteger} {
256 309 : 6 : addstring(true, yytext, yyleng, yyscanner);
310 : 6 : addchar(false, '\0', yyscanner);
311 : 6 : yylval->str = yyextra->scanstring;
2357 akorotkov@postgresql 312 : 6 : return INT_P;
313 : : }
314 : :
987 andrew@dunslane.net 315 :UBC 0 : {realfail} {
256 peter@eisentraut.org 316 : 0 : jsonpath_yyerror(NULL, escontext, yyscanner,
317 : : "invalid numeric literal");
987 andrew@dunslane.net 318 : 0 : yyterminate();
319 : : }
320 : : {decinteger_junk} {
256 peter@eisentraut.org 321 :CBC 45 : jsonpath_yyerror(NULL, escontext, yyscanner,
322 : : "trailing junk after numeric literal");
987 andrew@dunslane.net 323 : 12 : yyterminate();
324 : : }
325 : : {decimal_junk} {
256 peter@eisentraut.org 326 : 21 : jsonpath_yyerror(NULL, escontext, yyscanner,
327 : : "trailing junk after numeric literal");
987 andrew@dunslane.net 328 :UBC 0 : yyterminate();
329 : : }
330 : : {real_junk} {
256 peter@eisentraut.org 331 :CBC 3 : jsonpath_yyerror(NULL, escontext, yyscanner,
332 : : "trailing junk after numeric literal");
987 andrew@dunslane.net 333 :UBC 0 : yyterminate();
334 : : }
335 : : \" {
256 peter@eisentraut.org 336 :CBC 953 : addchar(true, '\0', yyscanner);
2357 akorotkov@postgresql 337 : 953 : BEGIN xq;
338 : : }
2366 339 : 953 :
2357 akorotkov@postgresql 340 :UBC 0 : \\ {
341 : 0 : yyless(0);
256 peter@eisentraut.org 342 : 0 : addchar(true, '\0', yyscanner);
2357 akorotkov@postgresql 343 : 0 : BEGIN xnq;
344 : : }
2366 345 : 0 :
2178 tgl@sss.pgh.pa.us 346 :CBC 5505 : {other}+ {
256 peter@eisentraut.org 347 : 5505 : addstring(true, yytext, yyleng, yyscanner);
2178 tgl@sss.pgh.pa.us 348 : 5505 : BEGIN xnq;
349 : : }
350 : 5505 :
2357 akorotkov@postgresql 351 : 5147 : <<EOF>> { yyterminate(); }
352 : :
2366 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
987 andrew@dunslane.net 362 :CBC 162 : jsonpath_yyerror(JsonPathParseResult **result, struct Node *escontext,
363 : : yyscan_t yyscanner,
364 : : const char *message)
365 : : {
255 peter@eisentraut.org 366 : 162 : 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 */
987 andrew@dunslane.net 370 [ + + + - : 162 : if (SOFT_ERROR_OCCURRED(escontext))
+ + ]
371 : 9 : return;
372 : :
2366 akorotkov@postgresql 373 [ + + ]: 153 : if (*yytext == YY_END_OF_BUFFER_CHAR)
374 : : {
987 andrew@dunslane.net 375 [ + - ]: 30 : 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 [ + + ]: 123 : 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, true, FALSE_P, "false"},
417 : : {5, false, FLOOR_P, "floor"},
418 : : {6, false, BIGINT_P, "bigint"},
419 : : {6, false, DOUBLE_P, "double"},
420 : : {6, false, EXISTS_P, "exists"},
421 : : {6, false, NUMBER_P, "number"},
422 : : {6, false, STARTS_P, "starts"},
423 : : {6, false, STRICT_P, "strict"},
424 : : {6, false, STRINGFUNC_P, "string"},
425 : : {7, false, BOOLEAN_P, "boolean"},
426 : : {7, false, CEILING_P, "ceiling"},
427 : : {7, false, DECIMAL_P, "decimal"},
428 : : {7, false, INTEGER_P, "integer"},
429 : : {7, false, TIME_TZ_P, "time_tz"},
430 : : {7, false, UNKNOWN_P, "unknown"},
431 : : {8, false, DATETIME_P, "datetime"},
432 : : {8, false, KEYVALUE_P, "keyvalue"},
433 : : {9, false, TIMESTAMP_P, "timestamp"},
434 : : {10, false, LIKE_REGEX_P, "like_regex"},
435 : : {12, false, TIMESTAMP_TZ_P, "timestamp_tz"},
436 : : };
437 : :
438 : : /*
439 : : * Check if current scanstring value is a keyword
440 : : */
441 : : static enum yytokentype
256 peter@eisentraut.org 442 : 5505 : checkKeyword(yyscan_t yyscanner)
443 : : {
1212 444 : 5505 : int res = IDENT_P;
445 : : int diff;
255 446 : 5505 : const JsonPathKeyword *StopLow = keywords,
447 : 5505 : *StopHigh = keywords + lengthof(keywords),
448 : : *StopMiddle;
449 : :
256 450 [ + + ]: 5505 : if (yyextra->scanstring.len > keywords[lengthof(keywords) - 1].len)
2366 akorotkov@postgresql 451 : 3 : return res;
452 : :
2357 453 [ + + ]: 29568 : while (StopLow < StopHigh)
454 : : {
2366 455 : 27681 : StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
456 : :
256 peter@eisentraut.org 457 [ + + ]: 27681 : if (StopMiddle->len == yyextra->scanstring.len)
458 : 10245 : diff = pg_strncasecmp(StopMiddle->keyword, yyextra->scanstring.val,
459 : 10245 : yyextra->scanstring.len);
460 : : else
461 : 17436 : diff = StopMiddle->len - yyextra->scanstring.len;
462 : :
2366 akorotkov@postgresql 463 [ + + ]: 27681 : if (diff < 0)
464 : 7557 : StopLow = StopMiddle + 1;
465 [ + + ]: 20124 : else if (diff > 0)
466 : 16509 : StopHigh = StopMiddle;
467 : : else
468 : : {
469 [ + + ]: 3615 : if (StopMiddle->lowercase)
256 peter@eisentraut.org 470 : 147 : diff = strncmp(StopMiddle->keyword, yyextra->scanstring.val,
471 : 147 : yyextra->scanstring.len);
472 : :
2366 akorotkov@postgresql 473 [ + - ]: 3615 : if (diff == 0)
474 : 3615 : res = StopMiddle->val;
475 : :
476 : 3615 : break;
477 : : }
478 : : }
479 : :
480 : 5502 : return res;
481 : : }
482 : :
483 : : /*
484 : : * Resize scanstring so that it can append string of given length.
485 : : * Reinitialize if required.
486 : : */
487 : : static void
256 peter@eisentraut.org 488 : 11128 : resizeString(bool init, int appendLen, yyscan_t yyscanner)
489 : : {
2366 akorotkov@postgresql 490 [ + + ]: 11128 : if (init)
491 : : {
256 peter@eisentraut.org 492 : 8252 : yyextra->scanstring.total = Max(32, appendLen);
493 : 8252 : yyextra->scanstring.val = (char *) palloc(yyextra->scanstring.total);
494 : 8252 : yyextra->scanstring.len = 0;
495 : : }
496 : : else
497 : : {
498 [ - + ]: 2876 : if (yyextra->scanstring.len + appendLen >= yyextra->scanstring.total)
499 : : {
256 peter@eisentraut.org 500 [ # # ]:UBC 0 : while (yyextra->scanstring.len + appendLen >= yyextra->scanstring.total)
501 : 0 : yyextra->scanstring.total *= 2;
502 : 0 : yyextra->scanstring.val = repalloc(yyextra->scanstring.val, yyextra->scanstring.total);
503 : : }
504 : : }
2366 akorotkov@postgresql 505 :CBC 11128 : }
506 : :
507 : : /* Add set of bytes at "s" of length "l" to scanstring */
508 : : static void
256 peter@eisentraut.org 509 : 8297 : addstring(bool init, char *s, int l, yyscan_t yyscanner)
510 : : {
511 : 8297 : resizeString(init, l + 1, yyscanner);
512 : 8297 : memcpy(yyextra->scanstring.val + yyextra->scanstring.len, s, l);
513 : 8297 : yyextra->scanstring.len += l;
2357 akorotkov@postgresql 514 : 8297 : }
515 : :
516 : : /* Add single byte "c" to scanstring */
517 : : static void
256 peter@eisentraut.org 518 : 2831 : addchar(bool init, char c, yyscan_t yyscanner)
519 : : {
520 : 2831 : resizeString(init, 1, yyscanner);
521 : 2831 : yyextra->scanstring.val[yyextra->scanstring.len] = c;
2357 akorotkov@postgresql 522 [ + + ]: 2831 : if (c != '\0')
256 peter@eisentraut.org 523 : 84 : yyextra->scanstring.len++;
2366 akorotkov@postgresql 524 : 2831 : }
525 : :
526 : : /* Interface to jsonpath parser */
527 : : JsonPathParseResult *
987 andrew@dunslane.net 528 : 5351 : parsejsonpath(const char *str, int len, struct Node *escontext)
529 : : {
530 : : JsonPathParseResult *parseresult;
531 : : yyscan_t scanner;
532 : : struct jsonpath_yy_extra_type yyext;
533 : :
256 peter@eisentraut.org 534 [ - + ]: 5351 : if (jsonpath_yylex_init(&scanner) != 0)
256 peter@eisentraut.org 535 [ # # ]:UBC 0 : elog(ERROR, "yylex_init() failed: %m");
536 : :
256 peter@eisentraut.org 537 :CBC 5351 : yyset_extra(&yyext, scanner);
538 : :
539 [ + + ]: 5351 : if (len <= 0)
540 : 3 : len = strlen(str);
541 : :
542 : 5351 : jsonpath_yy_scan_bytes(str, len, scanner);
543 : :
544 [ + + ]: 5351 : if (jsonpath_yyparse(&parseresult, escontext, scanner) != 0)
255 545 : 9 : jsonpath_yyerror(NULL, escontext, scanner, "invalid input"); /* shouldn't happen */
546 : :
256 547 : 5168 : jsonpath_yylex_destroy(scanner);
548 : :
2366 akorotkov@postgresql 549 : 5168 : return parseresult;
550 : : }
551 : :
552 : : /* Turn hex character into integer */
553 : : static bool
256 peter@eisentraut.org 554 : 438 : hexval(char c, int *result, struct Node *escontext, yyscan_t yyscanner)
555 : : {
2366 akorotkov@postgresql 556 [ + - + + ]: 438 : if (c >= '0' && c <= '9')
557 : : {
987 andrew@dunslane.net 558 : 294 : *result = c - '0';
559 : 294 : return true;
560 : : }
2366 akorotkov@postgresql 561 [ + + + - ]: 144 : if (c >= 'a' && c <= 'f')
562 : : {
987 andrew@dunslane.net 563 : 126 : *result = c - 'a' + 0xA;
564 : 126 : return true;
565 : : }
2366 akorotkov@postgresql 566 [ + - + - ]: 18 : if (c >= 'A' && c <= 'F')
567 : : {
987 andrew@dunslane.net 568 : 18 : *result = c - 'A' + 0xA;
569 : 18 : return true;
570 : : }
256 peter@eisentraut.org 571 :UBC 0 : jsonpath_yyerror(NULL, escontext, yyscanner, "invalid hexadecimal digit");
987 andrew@dunslane.net 572 : 0 : return false;
573 : : }
574 : :
575 : : /* Add given unicode character to scanstring */
576 : : static bool
256 peter@eisentraut.org 577 :CBC 72 : addUnicodeChar(int ch, struct Node *escontext, yyscan_t yyscanner)
578 : : {
2366 akorotkov@postgresql 579 [ + + ]: 72 : if (ch == 0)
580 : : {
581 : : /* We can't allow this, since our TEXT type doesn't */
987 andrew@dunslane.net 582 [ + - ]: 12 : ereturn(escontext, false,
583 : : (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
584 : : errmsg("unsupported Unicode escape sequence"),
585 : : errdetail("\\u0000 cannot be converted to text.")));
586 : : }
587 : : else
588 : : {
589 : : char cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1];
590 : :
591 : : /*
592 : : * If we're trapping the error status, call the noerror form of the
593 : : * conversion function. Otherwise call the normal form which provides
594 : : * more detailed errors.
595 : : */
596 : :
255 peter@eisentraut.org 597 [ - + - - ]: 60 : if (!escontext || !IsA(escontext, ErrorSaveContext))
987 andrew@dunslane.net 598 : 60 : pg_unicode_to_server(ch, (unsigned char *) cbuf);
987 andrew@dunslane.net 599 [ # # ]:UBC 0 : else if (!pg_unicode_to_server_noerror(ch, (unsigned char *) cbuf))
600 [ # # ]: 0 : ereturn(escontext, false,
601 : : (errcode(ERRCODE_SYNTAX_ERROR),
602 : : errmsg("could not convert Unicode to server encoding")));
256 peter@eisentraut.org 603 :CBC 60 : addstring(false, cbuf, strlen(cbuf), yyscanner);
604 : : }
987 andrew@dunslane.net 605 : 60 : return true;
606 : : }
607 : :
608 : : /* Add unicode character, processing any surrogate pairs */
609 : : static bool
256 peter@eisentraut.org 610 : 108 : addUnicode(int ch, int *hi_surrogate, struct Node *escontext, yyscan_t yyscanner)
611 : : {
2010 tgl@sss.pgh.pa.us 612 [ + + ]: 108 : if (is_utf16_surrogate_first(ch))
613 : : {
2366 akorotkov@postgresql 614 [ + + ]: 30 : if (*hi_surrogate != -1)
987 andrew@dunslane.net 615 [ + - ]: 6 : ereturn(escontext, false,
616 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
617 : : errmsg("invalid input syntax for type %s", "jsonpath"),
618 : : errdetail("Unicode high surrogate must not follow "
619 : : "a high surrogate.")));
2010 tgl@sss.pgh.pa.us 620 : 24 : *hi_surrogate = ch;
987 andrew@dunslane.net 621 : 24 : return true;
622 : : }
2010 tgl@sss.pgh.pa.us 623 [ + + ]: 78 : else if (is_utf16_surrogate_second(ch))
624 : : {
2366 akorotkov@postgresql 625 [ + + ]: 24 : if (*hi_surrogate == -1)
987 andrew@dunslane.net 626 [ + - ]: 12 : ereturn(escontext, false,
627 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
628 : : errmsg("invalid input syntax for type %s", "jsonpath"),
629 : : errdetail("Unicode low surrogate must follow a high "
630 : : "surrogate.")));
2010 tgl@sss.pgh.pa.us 631 : 12 : ch = surrogate_pair_to_codepoint(*hi_surrogate, ch);
2366 akorotkov@postgresql 632 : 12 : *hi_surrogate = -1;
633 : : }
634 [ - + ]: 54 : else if (*hi_surrogate != -1)
635 : : {
987 andrew@dunslane.net 636 [ # # ]:UBC 0 : ereturn(escontext, false,
637 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
638 : : errmsg("invalid input syntax for type %s", "jsonpath"),
639 : : errdetail("Unicode low surrogate must follow a high "
640 : : "surrogate.")));
641 : : }
642 : :
256 peter@eisentraut.org 643 :CBC 66 : return addUnicodeChar(ch, escontext, yyscanner);
644 : : }
645 : :
646 : : /*
647 : : * parseUnicode was adopted from json_lex_string() in
648 : : * src/backend/utils/adt/json.c
649 : : */
650 : : static bool
651 : 66 : parseUnicode(char *s, int l, struct Node *escontext, yyscan_t yyscanner)
652 : : {
2357 akorotkov@postgresql 653 : 66 : int i = 2;
2366 654 : 66 : int hi_surrogate = -1;
655 : :
656 [ + + ]: 144 : for (i = 2; i < l; i += 2) /* skip '\u' */
657 : : {
658 : 108 : int ch = 0;
659 : : int j,
660 : : si;
661 : :
255 peter@eisentraut.org 662 [ + + ]: 108 : if (s[i] == '{') /* parse '\u{XX...}' */
663 : : {
2366 akorotkov@postgresql 664 [ + + + - ]: 84 : while (s[++i] != '}' && i < l)
665 : : {
256 peter@eisentraut.org 666 [ - + ]: 66 : if (!hexval(s[i], &si, escontext, yyscanner))
987 andrew@dunslane.net 667 :UBC 0 : return false;
987 andrew@dunslane.net 668 :CBC 66 : ch = (ch << 4) | si;
669 : : }
255 peter@eisentraut.org 670 : 18 : i++; /* skip '}' */
671 : : }
672 : : else /* parse '\uXXXX' */
673 : : {
2366 akorotkov@postgresql 674 [ + + + - ]: 450 : for (j = 0; j < 4 && i < l; j++)
675 : : {
256 peter@eisentraut.org 676 [ - + ]: 360 : if (!hexval(s[i++], &si, escontext, yyscanner))
987 andrew@dunslane.net 677 :UBC 0 : return false;
987 andrew@dunslane.net 678 :CBC 360 : ch = (ch << 4) | si;
679 : : }
680 : : }
681 : :
255 peter@eisentraut.org 682 [ - + ]: 108 : if (!addUnicode(ch, &hi_surrogate, escontext, yyscanner))
987 andrew@dunslane.net 683 :UBC 0 : return false;
684 : : }
685 : :
2366 akorotkov@postgresql 686 [ + + ]:CBC 36 : if (hi_surrogate != -1)
687 : : {
987 andrew@dunslane.net 688 [ + - ]: 6 : ereturn(escontext, false,
689 : : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
690 : : errmsg("invalid input syntax for type %s", "jsonpath"),
691 : : errdetail("Unicode low surrogate must follow a high "
692 : : "surrogate.")));
693 : : }
694 : :
695 : 30 : return true;
696 : : }
697 : :
698 : : /* Parse sequence of hex-encoded characters */
699 : : static bool
256 peter@eisentraut.org 700 : 6 : parseHexChar(char *s, struct Node *escontext, yyscan_t yyscanner)
701 : : {
702 : : int s2,
703 : : s3,
704 : : ch;
705 : :
706 [ - + ]: 6 : if (!hexval(s[2], &s2, escontext, yyscanner))
987 andrew@dunslane.net 707 :UBC 0 : return false;
256 peter@eisentraut.org 708 [ - + ]:CBC 6 : if (!hexval(s[3], &s3, escontext, yyscanner))
987 andrew@dunslane.net 709 :UBC 0 : return false;
710 : :
987 andrew@dunslane.net 711 :CBC 6 : ch = (s2 << 4) | s3;
712 : :
256 peter@eisentraut.org 713 : 6 : return addUnicodeChar(ch, escontext, yyscanner);
714 : : }
715 : :
716 : : /*
717 : : * Interface functions to make flex use palloc() instead of malloc().
718 : : * It'd be better to make these static, but flex insists otherwise.
719 : : */
720 : :
721 : : void *
722 : 21404 : jsonpath_yyalloc(yy_size_t bytes, yyscan_t yyscanner)
723 : : {
2366 akorotkov@postgresql 724 : 21404 : return palloc(bytes);
725 : : }
726 : :
727 : : void *
256 peter@eisentraut.org 728 :UBC 0 : jsonpath_yyrealloc(void *ptr, yy_size_t bytes, yyscan_t yyscanner)
729 : : {
2366 akorotkov@postgresql 730 [ # # ]: 0 : if (ptr)
731 : 0 : return repalloc(ptr, bytes);
732 : : else
733 : 0 : return palloc(bytes);
734 : : }
735 : :
736 : : void
256 peter@eisentraut.org 737 :CBC 25840 : jsonpath_yyfree(void *ptr, yyscan_t yyscanner)
738 : : {
2366 akorotkov@postgresql 739 [ + + ]: 25840 : if (ptr)
740 : 20672 : pfree(ptr);
741 : 25840 : }
|