Line data Source code
1 : %top{
2 : /*-------------------------------------------------------------------------
3 : *
4 : * repl_scanner.l
5 : * a lexical scanner for the replication commands
6 : *
7 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 : * Portions Copyright (c) 1994, Regents of the University of California
9 : *
10 : *
11 : * IDENTIFICATION
12 : * src/backend/replication/repl_scanner.l
13 : *
14 : *-------------------------------------------------------------------------
15 : */
16 : #include "postgres.h"
17 :
18 : #include "nodes/parsenodes.h"
19 : #include "utils/builtins.h"
20 : #include "parser/scansup.h"
21 :
22 : /*
23 : * NB: include repl_gram.h only AFTER including walsender_private.h, because
24 : * walsender_private includes headers that define XLogRecPtr.
25 : */
26 : #include "replication/walsender_private.h"
27 : #include "repl_gram.h"
28 : }
29 :
30 : %{
31 : /* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */
32 : #undef fprintf
33 : #define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg)
34 :
35 : static void
36 0 : fprintf_to_ereport(const char *fmt, const char *msg)
37 : {
38 0 : ereport(ERROR, (errmsg_internal("%s", msg)));
39 : }
40 :
41 : struct replication_yy_extra_type
42 : {
43 : /* Pushed-back token (we only handle one) */
44 : int repl_pushed_back_token;
45 :
46 : /* Work area for collecting literals */
47 : StringInfoData litbuf;
48 : };
49 :
50 : static void startlit(yyscan_t yyscanner);
51 : static char *litbufdup(yyscan_t yyscanner);
52 : static void addlit(char *ytext, int yleng, yyscan_t yyscanner);
53 : static void addlitchar(unsigned char ychar, yyscan_t yyscanner);
54 :
55 : /* LCOV_EXCL_START */
56 :
57 : %}
58 :
59 : %option reentrant
60 : %option bison-bridge
61 : %option 8bit
62 : %option never-interactive
63 : %option nodefault
64 : %option noinput
65 : %option nounput
66 : %option noyywrap
67 : %option noyyalloc
68 : %option noyyrealloc
69 : %option noyyfree
70 : %option warn
71 : %option prefix="replication_yy"
72 : %option extra-type="struct replication_yy_extra_type *"
73 :
74 : /*
75 : * Exclusive states:
76 : * <xd> delimited identifiers (double-quoted identifiers)
77 : * <xq> standard single-quoted strings
78 : */
79 : %x xd
80 : %x xq
81 :
82 : space [ \t\n\r\f\v]
83 :
84 : quote '
85 : quotestop {quote}
86 :
87 : /* Extended quote
88 : * xqdouble implements embedded quote, ''''
89 : */
90 : xqstart {quote}
91 : xqdouble {quote}{quote}
92 : xqinside [^']+
93 :
94 : /* Double quote
95 : * Allows embedded spaces and other special characters into identifiers.
96 : */
97 : dquote \"
98 : xdstart {dquote}
99 : xdstop {dquote}
100 : xddouble {dquote}{dquote}
101 : xdinside [^"]+
102 :
103 : digit [0-9]
104 : hexdigit [0-9A-Fa-f]
105 :
106 : ident_start [A-Za-z\200-\377_]
107 : ident_cont [A-Za-z\200-\377_0-9\$]
108 :
109 : identifier {ident_start}{ident_cont}*
110 :
111 : %%
112 :
113 : %{
114 : /* This code is inserted at the start of replication_yylex() */
115 :
116 : /* If we have a pushed-back token, return that. */
117 : if (yyextra->repl_pushed_back_token)
118 : {
119 : int result = yyextra->repl_pushed_back_token;
120 :
121 : yyextra->repl_pushed_back_token = 0;
122 : return result;
123 : }
124 : %}
125 :
126 : BASE_BACKUP { return K_BASE_BACKUP; }
127 : IDENTIFY_SYSTEM { return K_IDENTIFY_SYSTEM; }
128 : READ_REPLICATION_SLOT { return K_READ_REPLICATION_SLOT; }
129 : SHOW { return K_SHOW; }
130 : TIMELINE { return K_TIMELINE; }
131 : START_REPLICATION { return K_START_REPLICATION; }
132 : CREATE_REPLICATION_SLOT { return K_CREATE_REPLICATION_SLOT; }
133 : DROP_REPLICATION_SLOT { return K_DROP_REPLICATION_SLOT; }
134 : ALTER_REPLICATION_SLOT { return K_ALTER_REPLICATION_SLOT; }
135 : TIMELINE_HISTORY { return K_TIMELINE_HISTORY; }
136 : PHYSICAL { return K_PHYSICAL; }
137 : RESERVE_WAL { return K_RESERVE_WAL; }
138 : LOGICAL { return K_LOGICAL; }
139 : SLOT { return K_SLOT; }
140 : TEMPORARY { return K_TEMPORARY; }
141 : TWO_PHASE { return K_TWO_PHASE; }
142 : EXPORT_SNAPSHOT { return K_EXPORT_SNAPSHOT; }
143 : NOEXPORT_SNAPSHOT { return K_NOEXPORT_SNAPSHOT; }
144 : USE_SNAPSHOT { return K_USE_SNAPSHOT; }
145 : WAIT { return K_WAIT; }
146 : UPLOAD_MANIFEST { return K_UPLOAD_MANIFEST; }
147 :
148 : {space}+ { /* do nothing */ }
149 :
150 : {digit}+ {
151 : yylval->uintval = strtoul(yytext, NULL, 10);
152 : return UCONST;
153 : }
154 :
155 : {hexdigit}+\/{hexdigit}+ {
156 : uint32 hi,
157 : lo;
158 : if (sscanf(yytext, "%X/%X", &hi, &lo) != 2)
159 : replication_yyerror(NULL, yyscanner, "invalid streaming start location");
160 : yylval->recptr = ((uint64) hi) << 32 | lo;
161 : return RECPTR;
162 : }
163 :
164 : {xqstart} {
165 : BEGIN(xq);
166 : startlit(yyscanner);
167 : }
168 :
169 : <xq>{quotestop} {
170 : yyless(1);
171 : BEGIN(INITIAL);
172 : yylval->str = litbufdup(yyscanner);
173 : return SCONST;
174 : }
175 :
176 : <xq>{xqdouble} {
177 : addlitchar('\'', yyscanner);
178 : }
179 :
180 : <xq>{xqinside} {
181 : addlit(yytext, yyleng, yyscanner);
182 : }
183 :
184 : {xdstart} {
185 : BEGIN(xd);
186 : startlit(yyscanner);
187 : }
188 :
189 : <xd>{xdstop} {
190 : int len;
191 :
192 : yyless(1);
193 : BEGIN(INITIAL);
194 : yylval->str = litbufdup(yyscanner);
195 : len = strlen(yylval->str);
196 : truncate_identifier(yylval->str, len, true);
197 : return IDENT;
198 : }
199 :
200 : <xd>{xdinside} {
201 : addlit(yytext, yyleng, yyscanner);
202 : }
203 :
204 : {identifier} {
205 : int len = strlen(yytext);
206 :
207 : yylval->str = downcase_truncate_identifier(yytext, len, true);
208 : return IDENT;
209 : }
210 :
211 : . {
212 : /* Any char not recognized above is returned as itself */
213 : return yytext[0];
214 : }
215 :
216 : <xq,xd><<EOF>> { replication_yyerror(NULL, yyscanner, "unterminated quoted string"); }
217 :
218 :
219 : <<EOF>> {
220 : yyterminate();
221 : }
222 :
223 : %%
224 :
225 : /* LCOV_EXCL_STOP */
226 :
227 : /* see scan.l */
228 : #undef yyextra
229 : #define yyextra (((struct yyguts_t *) yyscanner)->yyextra_r)
230 :
231 : static void
232 : startlit(yyscan_t yyscanner)
233 7050 : {
234 : initStringInfo(&yyextra->litbuf);
235 7050 : }
236 7050 :
237 : static char *
238 : litbufdup(yyscan_t yyscanner)
239 7050 : {
240 : return yyextra->litbuf.data;
241 7050 : }
242 :
243 : static void
244 : addlit(char *ytext, int yleng, yyscan_t yyscanner)
245 7048 : {
246 : appendBinaryStringInfo(&yyextra->litbuf, ytext, yleng);
247 7048 : }
248 7048 :
249 : static void
250 : addlitchar(unsigned char ychar, yyscan_t yyscanner)
251 0 : {
252 : appendStringInfoChar(&yyextra->litbuf, ychar);
253 0 : }
254 0 :
255 : /*
256 : * (The first argument is enforced by Bison to match the first argument of
257 : * yyparse(), but it is not used here.)
258 : */
259 : void
260 : replication_yyerror(Node **replication_parse_result_p, yyscan_t yyscanner, const char *message)
261 0 : {
262 : ereport(ERROR,
263 0 : (errcode(ERRCODE_SYNTAX_ERROR),
264 : errmsg_internal("%s", message)));
265 : }
266 :
267 : void
268 : replication_scanner_init(const char *str, yyscan_t *yyscannerp)
269 9806 : {
270 : yyscan_t yyscanner;
271 : struct replication_yy_extra_type *yyext = palloc0_object(struct replication_yy_extra_type);
272 9806 :
273 : if (yylex_init(yyscannerp) != 0)
274 9806 : elog(ERROR, "yylex_init() failed: %m");
275 0 :
276 : yyscanner = *yyscannerp;
277 9806 :
278 : yyset_extra(yyext, yyscanner);
279 9806 :
280 : yy_scan_string(str, yyscanner);
281 9806 : }
282 9806 :
283 : void
284 : replication_scanner_finish(yyscan_t yyscanner)
285 9806 : {
286 : pfree(yyextra);
287 9806 : yylex_destroy(yyscanner);
288 9806 : }
289 9806 :
290 : /*
291 : * Check to see if the first token of a command is a WalSender keyword.
292 : *
293 : * To keep repl_scanner.l minimal, we don't ask it to know every construct
294 : * that the core lexer knows. Therefore, we daren't lex more than the
295 : * first token of a general SQL command. That will usually look like an
296 : * IDENT token here, although some other cases are possible.
297 : */
298 : bool
299 : replication_scanner_is_replication_command(yyscan_t yyscanner)
300 9806 : {
301 : YYSTYPE dummy;
302 : int first_token = replication_yylex(&dummy, yyscanner);
303 9806 :
304 : switch (first_token)
305 9806 : {
306 : case K_IDENTIFY_SYSTEM:
307 5484 : case K_BASE_BACKUP:
308 : case K_START_REPLICATION:
309 : case K_CREATE_REPLICATION_SLOT:
310 : case K_DROP_REPLICATION_SLOT:
311 : case K_ALTER_REPLICATION_SLOT:
312 : case K_READ_REPLICATION_SLOT:
313 : case K_TIMELINE_HISTORY:
314 : case K_UPLOAD_MANIFEST:
315 : case K_SHOW:
316 : /* Yes; push back the first token so we can parse later. */
317 : yyextra->repl_pushed_back_token = first_token;
318 5484 : return true;
319 5484 : default:
320 4322 : /* Nope; we don't bother to push back the token. */
321 : return false;
322 4322 : }
323 : }
324 :
325 : /*
326 : * Interface functions to make flex use palloc() instead of malloc().
327 : * It'd be better to make these static, but flex insists otherwise.
328 : */
329 :
330 : void *
331 : yyalloc(yy_size_t size, yyscan_t yyscanner)
332 39224 : {
333 : return palloc(size);
334 39224 : }
335 :
336 : void *
337 : yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
338 0 : {
339 : if (ptr)
340 0 : return repalloc(ptr, size);
341 0 : else
342 : return palloc(size);
343 0 : }
344 :
345 : void
346 : yyfree(void *ptr, yyscan_t yyscanner)
347 49030 : {
348 : if (ptr)
349 49030 : pfree(ptr);
350 39224 : }
|