Line data Source code
1 : %top{
2 : /*
3 : * Scanner for plan advice
4 : *
5 : * Copyright (c) 2000-2026, PostgreSQL Global Development Group
6 : *
7 : * contrib/pg_plan_advice/pgpa_scanner.l
8 : */
9 : #include "postgres.h"
10 :
11 : #include "common/string.h"
12 : #include "nodes/miscnodes.h"
13 : #include "parser/scansup.h"
14 :
15 : #include "pgpa_ast.h"
16 : #include "pgpa_parser.h"
17 :
18 : /*
19 : * Extra data that we pass around when during scanning.
20 : *
21 : * 'litbuf' is used to implement the <xd> exclusive state, which handles
22 : * double-quoted identifiers.
23 : */
24 : typedef struct pgpa_yy_extra_type
25 : {
26 : StringInfoData litbuf;
27 : } pgpa_yy_extra_type;
28 :
29 : }
30 :
31 : %{
32 : /* LCOV_EXCL_START */
33 :
34 : #define YY_DECL \
35 : extern int pgpa_yylex(union YYSTYPE *yylval_param, List **result, \
36 : char **parse_error_msg_p, yyscan_t yyscanner)
37 :
38 : /* No reason to constrain amount of data slurped */
39 : #define YY_READ_BUF_SIZE 16777216
40 :
41 : /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
42 : #undef fprintf
43 : #define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg)
44 :
45 : static void
46 0 : fprintf_to_ereport(const char *fmt, const char *msg)
47 : {
48 0 : ereport(ERROR, (errmsg_internal("%s", msg)));
49 : }
50 : %}
51 :
52 : %option reentrant
53 : %option bison-bridge
54 : %option 8bit
55 : %option never-interactive
56 : %option nodefault
57 : %option noinput
58 : %option nounput
59 : %option noyywrap
60 : %option noyyalloc
61 : %option noyyrealloc
62 : %option noyyfree
63 : %option warn
64 : %option prefix="pgpa_yy"
65 : %option extra-type="pgpa_yy_extra_type *"
66 :
67 : /*
68 : * What follows is a severely stripped-down version of the core scanner. We
69 : * only care about recognizing identifiers with or without identifier quoting
70 : * (i.e. double-quoting), decimal integers, and a small handful of other
71 : * things. Keep these rules in sync with src/backend/parser/scan.l. As in that
72 : * file, we use an exclusive state called 'xc' for C-style comments, and an
73 : * exclusive state called 'xd' for double-quoted identifiers.
74 : */
75 : %x xc
76 : %x xd
77 :
78 : ident_start [A-Za-z\200-\377_]
79 : ident_cont [A-Za-z\200-\377_0-9\$]
80 :
81 : identifier {ident_start}{ident_cont}*
82 :
83 : decdigit [0-9]
84 : decinteger {decdigit}(_?{decdigit})*
85 :
86 : space [ \t\n\r\f\v]
87 : whitespace {space}+
88 :
89 : dquote \"
90 : xdstart {dquote}
91 : xdstop {dquote}
92 : xddouble {dquote}{dquote}
93 : xdinside [^"]+
94 :
95 : xcstart \/\*
96 : xcstop \*+\/
97 : xcinside [^*/]+
98 :
99 : %%
100 :
101 : {whitespace} { /* ignore */ }
102 127944 :
103 323952 : {identifier} {
104 : char *str;
105 : bool fail;
106 : pgpa_advice_tag_type tag;
107 :
108 : /*
109 : * Unlike the core scanner, we don't truncate identifiers
110 : * here. There is no obvious reason to do so.
111 : */
112 323952 : str = downcase_identifier(yytext, yyleng, false, false);
113 323952 : yylval->str = str;
114 :
115 : /*
116 : * If it's not a tag, just return TOK_IDENT; else, return
117 : * a token type based on how further parsing should
118 : * proceed.
119 : */
120 323952 : tag = pgpa_parse_advice_tag(str, &fail);
121 323952 : if (fail)
122 229191 : return TOK_IDENT;
123 94761 : else if (tag == PGPA_TAG_JOIN_ORDER)
124 10933 : return TOK_TAG_JOIN_ORDER;
125 83828 : else if (tag == PGPA_TAG_INDEX_SCAN ||
126 : tag == PGPA_TAG_INDEX_ONLY_SCAN)
127 8714 : return TOK_TAG_INDEX;
128 75114 : else if (tag == PGPA_TAG_SEQ_SCAN ||
129 58315 : tag == PGPA_TAG_TID_SCAN ||
130 55982 : tag == PGPA_TAG_BITMAP_HEAP_SCAN ||
131 12568 : tag == PGPA_TAG_NO_GATHER ||
132 : tag == PGPA_TAG_DO_NOT_SCAN)
133 62727 : return TOK_TAG_SIMPLE;
134 : else
135 12387 : return TOK_TAG_GENERIC;
136 : }
137 :
138 3392 : {decinteger} {
139 : char *endptr;
140 :
141 3392 : errno = 0;
142 3392 : yylval->integer = strtoint(yytext, &endptr, 10);
143 3392 : if (*endptr != '\0' || errno == ERANGE)
144 0 : pgpa_yyerror(result, parse_error_msg_p, yyscanner,
145 : "integer out of range");
146 3392 : return TOK_INTEGER;
147 : }
148 :
149 17 : {xcstart} {
150 17 : BEGIN(xc);
151 : }
152 17 :
153 20532 : {xdstart} {
154 20532 : BEGIN(xd);
155 20532 : resetStringInfo(&yyextra->litbuf);
156 : }
157 20532 :
158 273659 : . { return yytext[0]; }
159 :
160 15 : <xc>{xcstop} {
161 15 : BEGIN(INITIAL);
162 : }
163 15 :
164 10 : <xc>{xcinside} {
165 : /* discard multiple characters without slash or asterisk */
166 : }
167 10 :
168 4 : <xc>. {
169 : /*
170 : * Discard any single character. flex prefers longer
171 : * matches, so this rule will never be picked when we could
172 : * have matched xcstop.
173 : *
174 : * NB: At present, we don't bother to support nested
175 : * C-style comments here, but this logic could be extended
176 : * if that restriction poses a problem.
177 : */
178 : }
179 4 :
180 2 : <xc><<EOF>> {
181 2 : BEGIN(INITIAL);
182 2 : pgpa_yyerror(result, parse_error_msg_p, yyscanner,
183 : "unterminated comment");
184 : }
185 2 :
186 20531 : <xd>{xdstop} {
187 20531 : BEGIN(INITIAL);
188 20531 : if (yyextra->litbuf.len == 0)
189 1 : pgpa_yyerror(result, parse_error_msg_p, yyscanner,
190 : "zero-length delimited identifier");
191 20531 : yylval->str = pstrdup(yyextra->litbuf.data);
192 20531 : return TOK_IDENT;
193 : }
194 :
195 0 : <xd>{xddouble} {
196 0 : appendStringInfoChar(&yyextra->litbuf, '"');
197 : }
198 0 :
199 20530 : <xd>{xdinside} {
200 20530 : appendBinaryStringInfo(&yyextra->litbuf, yytext, yyleng);
201 : }
202 20530 :
203 1 : <xd><<EOF>> {
204 1 : BEGIN(INITIAL);
205 1 : pgpa_yyerror(result, parse_error_msg_p, yyscanner,
206 : "unterminated quoted identifier");
207 : }
208 1 :
209 0 : %%
210 :
211 : /* LCOV_EXCL_STOP */
212 :
213 : /*
214 : * Handler for errors while scanning or parsing advice.
215 : *
216 : * bison passes the error message to us via 'message', and the context is
217 : * available via the 'yytext' macro. We assemble those values into a final
218 : * error text and then arrange to pass it back to the caller of pgpa_yyparse()
219 : * by storing it into *parse_error_msg_p.
220 : */
221 : void
222 19 : pgpa_yyerror(List **result, char **parse_error_msg_p, yyscan_t yyscanner,
223 : const char *message)
224 : {
225 19 : struct yyguts_t *yyg = (struct yyguts_t *) yyscanner; /* needed for yytext
226 : * macro */
227 :
228 :
229 : /* report only the first error in a parse operation */
230 19 : if (*parse_error_msg_p)
231 1 : return;
232 :
233 18 : if (yytext[0])
234 12 : *parse_error_msg_p = psprintf("%s at or near \"%s\"", message, yytext);
235 : else
236 6 : *parse_error_msg_p = psprintf("%s at end of input", message);
237 : }
238 :
239 : /*
240 : * Initialize the advice scanner.
241 : *
242 : * This should be called before parsing begins.
243 : */
244 : void
245 43840 : pgpa_scanner_init(const char *str, yyscan_t *yyscannerp)
246 : {
247 : yyscan_t yyscanner;
248 43840 : pgpa_yy_extra_type *yyext = palloc0_object(pgpa_yy_extra_type);
249 :
250 43840 : if (yylex_init(yyscannerp) != 0)
251 0 : elog(ERROR, "yylex_init() failed: %m");
252 :
253 43840 : yyscanner = *yyscannerp;
254 :
255 43840 : initStringInfo(&yyext->litbuf);
256 43840 : pgpa_yyset_extra(yyext, yyscanner);
257 :
258 43840 : yy_scan_string(str, yyscanner);
259 43840 : }
260 :
261 :
262 : /*
263 : * Shut down the advice scanner.
264 : *
265 : * This should be called after parsing is complete.
266 : */
267 : void
268 43840 : pgpa_scanner_finish(yyscan_t yyscanner)
269 : {
270 43840 : yylex_destroy(yyscanner);
271 43840 : }
272 :
273 : /*
274 : * Interface functions to make flex use palloc() instead of malloc().
275 : * It'd be better to make these static, but flex insists otherwise.
276 : */
277 :
278 : void *
279 175360 : yyalloc(yy_size_t size, yyscan_t yyscanner)
280 : {
281 175360 : return palloc(size);
282 : }
283 :
284 : void *
285 0 : yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
286 : {
287 0 : if (ptr)
288 0 : return repalloc(ptr, size);
289 : else
290 0 : return palloc(size);
291 : }
292 :
293 : void
294 219200 : yyfree(void *ptr, yyscan_t yyscanner)
295 : {
296 219200 : if (ptr)
297 175360 : pfree(ptr);
298 219200 : }
|