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(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(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 7028 : {
234 : initStringInfo(&yyextra->litbuf);
235 7028 : }
236 7028 :
237 : static char *
238 : litbufdup(yyscan_t yyscanner)
239 7028 : {
240 : return yyextra->litbuf.data;
241 7028 : }
242 :
243 : static void
244 : addlit(char *ytext, int yleng, yyscan_t yyscanner)
245 7026 : {
246 : appendBinaryStringInfo(&yyextra->litbuf, ytext, yleng);
247 7026 : }
248 7026 :
249 : static void
250 : addlitchar(unsigned char ychar, yyscan_t yyscanner)
251 0 : {
252 : appendStringInfoChar(&yyextra->litbuf, ychar);
253 0 : }
254 0 :
255 : void
256 : replication_yyerror(yyscan_t yyscanner, const char *message)
257 0 : {
258 : ereport(ERROR,
259 0 : (errcode(ERRCODE_SYNTAX_ERROR),
260 : errmsg_internal("%s", message)));
261 : }
262 :
263 : void
264 : replication_scanner_init(const char *str, yyscan_t *yyscannerp)
265 9742 : {
266 : yyscan_t yyscanner;
267 : struct replication_yy_extra_type *yyext = palloc0_object(struct replication_yy_extra_type);
268 9742 :
269 : if (yylex_init(yyscannerp) != 0)
270 9742 : elog(ERROR, "yylex_init() failed: %m");
271 0 :
272 : yyscanner = *yyscannerp;
273 9742 :
274 : yyset_extra(yyext, yyscanner);
275 9742 :
276 : yy_scan_string(str, yyscanner);
277 9742 : }
278 9742 :
279 : void
280 : replication_scanner_finish(yyscan_t yyscanner)
281 9742 : {
282 : pfree(yyextra);
283 9742 : yylex_destroy(yyscanner);
284 9742 : }
285 9742 :
286 : /*
287 : * Check to see if the first token of a command is a WalSender keyword.
288 : *
289 : * To keep repl_scanner.l minimal, we don't ask it to know every construct
290 : * that the core lexer knows. Therefore, we daren't lex more than the
291 : * first token of a general SQL command. That will usually look like an
292 : * IDENT token here, although some other cases are possible.
293 : */
294 : bool
295 : replication_scanner_is_replication_command(yyscan_t yyscanner)
296 9742 : {
297 : YYSTYPE dummy;
298 : int first_token = replication_yylex(&dummy, yyscanner);
299 9742 :
300 : switch (first_token)
301 9742 : {
302 : case K_IDENTIFY_SYSTEM:
303 5478 : case K_BASE_BACKUP:
304 : case K_START_REPLICATION:
305 : case K_CREATE_REPLICATION_SLOT:
306 : case K_DROP_REPLICATION_SLOT:
307 : case K_ALTER_REPLICATION_SLOT:
308 : case K_READ_REPLICATION_SLOT:
309 : case K_TIMELINE_HISTORY:
310 : case K_UPLOAD_MANIFEST:
311 : case K_SHOW:
312 : /* Yes; push back the first token so we can parse later. */
313 : yyextra->repl_pushed_back_token = first_token;
314 5478 : return true;
315 5478 : default:
316 4264 : /* Nope; we don't bother to push back the token. */
317 : return false;
318 4264 : }
319 : }
320 :
321 : /*
322 : * Interface functions to make flex use palloc() instead of malloc().
323 : * It'd be better to make these static, but flex insists otherwise.
324 : */
325 :
326 : void *
327 : yyalloc(yy_size_t size, yyscan_t yyscanner)
328 38968 : {
329 : return palloc(size);
330 38968 : }
331 :
332 : void *
333 : yyrealloc(void *ptr, yy_size_t size, yyscan_t yyscanner)
334 0 : {
335 : if (ptr)
336 0 : return repalloc(ptr, size);
337 0 : else
338 : return palloc(size);
339 0 : }
340 :
341 : void
342 : yyfree(void *ptr, yyscan_t yyscanner)
343 48710 : {
344 : if (ptr)
345 48710 : pfree(ptr);
346 38968 : }
|