aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/replication/repl_gram.y26
-rw-r--r--src/backend/replication/repl_scanner.l87
-rw-r--r--src/backend/replication/walsender.c43
-rw-r--r--src/include/nodes/nodes.h1
-rw-r--r--src/include/nodes/replnodes.h9
-rw-r--r--src/include/replication/walsender_private.h1
6 files changed, 98 insertions, 69 deletions
diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y
index 6d1dbd72e91..3b92180e8cb 100644
--- a/src/backend/replication/repl_gram.y
+++ b/src/backend/replication/repl_gram.y
@@ -25,8 +25,6 @@
/* Result of the parsing is returned here */
Node *replication_parse_result;
-static SQLCmd *make_sqlcmd(void);
-
/*
* Bison doesn't allocate anything that needs to live across parser calls,
@@ -59,7 +57,6 @@ static SQLCmd *make_sqlcmd(void);
%token <str> SCONST IDENT
%token <uintval> UCONST
%token <recptr> RECPTR
-%token T_WORD
/* Keyword tokens. */
%token K_BASE_BACKUP
@@ -95,7 +92,7 @@ static SQLCmd *make_sqlcmd(void);
%type <node> command
%type <node> base_backup start_replication start_logical_replication
create_replication_slot drop_replication_slot identify_system
- read_replication_slot timeline_history show sql_cmd
+ read_replication_slot timeline_history show
%type <list> base_backup_legacy_opt_list generic_option_list
%type <defelt> base_backup_legacy_opt generic_option
%type <uintval> opt_timeline
@@ -129,7 +126,6 @@ command:
| read_replication_slot
| timeline_history
| show
- | sql_cmd
;
/*
@@ -450,10 +446,6 @@ plugin_opt_arg:
| /* EMPTY */ { $$ = NULL; }
;
-sql_cmd:
- IDENT { $$ = (Node *) make_sqlcmd(); }
- ;
-
generic_option_list:
generic_option_list ',' generic_option
{ $$ = lappend($1, $3); }
@@ -514,20 +506,4 @@ ident_or_keyword:
%%
-static SQLCmd *
-make_sqlcmd(void)
-{
- SQLCmd *cmd = makeNode(SQLCmd);
- int tok;
-
- /* Just move lexer to the end of command. */
- for (;;)
- {
- tok = yylex();
- if (tok == ';' || tok == 0)
- break;
- }
- return cmd;
-}
-
#include "repl_scanner.c"
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index 81ac41e9d45..d992bcc2e3c 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -31,6 +31,10 @@ fprintf_to_ereport(const char *fmt, const char *msg)
/* Handle to the buffer that the lexer uses internally */
static YY_BUFFER_STATE scanbufhandle;
+/* Pushed-back token (we only handle one) */
+static int repl_pushed_back_token;
+
+/* Work area for collecting literals */
static StringInfoData litbuf;
static void startlit(void);
@@ -51,7 +55,18 @@ static void addlitchar(unsigned char ychar);
%option warn
%option prefix="replication_yy"
-%x xq xd
+/*
+ * Exclusive states:
+ * <xd> delimited identifiers (double-quoted identifiers)
+ * <xq> standard single-quoted strings
+ */
+%x xd
+%x xq
+
+space [ \t\n\r\f]
+
+quote '
+quotestop {quote}
/* Extended quote
* xqdouble implements embedded quote, ''''
@@ -69,11 +84,8 @@ xdstop {dquote}
xddouble {dquote}{dquote}
xdinside [^"]+
-digit [0-9]+
-hexdigit [0-9A-Za-z]+
-
-quote '
-quotestop {quote}
+digit [0-9]
+hexdigit [0-9A-Fa-f]
ident_start [A-Za-z\200-\377_]
ident_cont [A-Za-z\200-\377_0-9\$]
@@ -82,6 +94,19 @@ identifier {ident_start}{ident_cont}*
%%
+%{
+ /* This code is inserted at the start of replication_yylex() */
+
+ /* If we have a pushed-back token, return that. */
+ if (repl_pushed_back_token)
+ {
+ int result = repl_pushed_back_token;
+
+ repl_pushed_back_token = 0;
+ return result;
+ }
+%}
+
BASE_BACKUP { return K_BASE_BACKUP; }
FAST { return K_FAST; }
IDENTIFY_SYSTEM { return K_IDENTIFY_SYSTEM; }
@@ -112,14 +137,7 @@ WAIT { return K_WAIT; }
MANIFEST { return K_MANIFEST; }
MANIFEST_CHECKSUMS { return K_MANIFEST_CHECKSUMS; }
-"," { return ','; }
-";" { return ';'; }
-"(" { return '('; }
-")" { return ')'; }
-
-[\n] ;
-[\t] ;
-" " ;
+{space}+ { /* do nothing */ }
{digit}+ {
yylval.uintval = strtoul(yytext, NULL, 10);
@@ -181,6 +199,11 @@ MANIFEST_CHECKSUMS { return K_MANIFEST_CHECKSUMS; }
return IDENT;
}
+. {
+ /* Any char not recognized above is returned as itself */
+ return yytext[0];
+ }
+
<xq,xd><<EOF>> { yyerror("unterminated quoted string"); }
@@ -188,9 +211,6 @@ MANIFEST_CHECKSUMS { return K_MANIFEST_CHECKSUMS; }
yyterminate();
}
-. {
- return T_WORD;
- }
%%
/* LCOV_EXCL_STOP */
@@ -250,6 +270,7 @@ replication_scanner_init(const char *str)
/* Make sure we start in proper state */
BEGIN(INITIAL);
+ repl_pushed_back_token = 0;
}
void
@@ -258,3 +279,35 @@ replication_scanner_finish(void)
yy_delete_buffer(scanbufhandle);
scanbufhandle = NULL;
}
+
+/*
+ * Check to see if the first token of a command is a WalSender keyword.
+ *
+ * To keep repl_scanner.l minimal, we don't ask it to know every construct
+ * that the core lexer knows. Therefore, we daren't lex more than the
+ * first token of a general SQL command. That will usually look like an
+ * IDENT token here, although some other cases are possible.
+ */
+bool
+replication_scanner_is_replication_command(void)
+{
+ int first_token = replication_yylex();
+
+ switch (first_token)
+ {
+ case K_IDENTIFY_SYSTEM:
+ case K_BASE_BACKUP:
+ case K_START_REPLICATION:
+ case K_CREATE_REPLICATION_SLOT:
+ case K_DROP_REPLICATION_SLOT:
+ case K_READ_REPLICATION_SLOT:
+ case K_TIMELINE_HISTORY:
+ case K_SHOW:
+ /* Yes; push back the first token so we can parse later. */
+ repl_pushed_back_token = first_token;
+ return true;
+ default:
+ /* Nope; we don't bother to push back the token. */
+ return false;
+ }
+}
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 4cf95ce4392..655760fee3e 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -1630,7 +1630,8 @@ exec_replication_command(const char *cmd_string)
*/
if (MyWalSnd->state == WALSNDSTATE_STOPPING)
ereport(ERROR,
- (errmsg("cannot execute new commands while WAL sender is in stopping mode")));
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("cannot execute new commands while WAL sender is in stopping mode")));
/*
* CREATE_REPLICATION_SLOT ... LOGICAL exports a snapshot until the next
@@ -1641,7 +1642,7 @@ exec_replication_command(const char *cmd_string)
CHECK_FOR_INTERRUPTS();
/*
- * Parse the command.
+ * Prepare to parse and execute the command.
*/
cmd_context = AllocSetContextCreate(CurrentMemoryContext,
"Replication command context",
@@ -1649,34 +1650,42 @@ exec_replication_command(const char *cmd_string)
old_context = MemoryContextSwitchTo(cmd_context);
replication_scanner_init(cmd_string);
- parse_rc = replication_yyparse();
- if (parse_rc != 0)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg_internal("replication command parser returned %d",
- parse_rc)));
- replication_scanner_finish();
-
- cmd_node = replication_parse_result;
/*
- * If it's a SQL command, just clean up our mess and return false; the
- * caller will take care of executing it.
+ * Is it a WalSender command?
*/
- if (IsA(cmd_node, SQLCmd))
+ if (!replication_scanner_is_replication_command())
{
- if (MyDatabaseId == InvalidOid)
- ereport(ERROR,
- (errmsg("cannot execute SQL commands in WAL sender for physical replication")));
+ /* Nope; clean up and get out. */
+ replication_scanner_finish();
MemoryContextSwitchTo(old_context);
MemoryContextDelete(cmd_context);
+ /* XXX this is a pretty random place to make this check */
+ if (MyDatabaseId == InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot execute SQL commands in WAL sender for physical replication")));
+
/* Tell the caller that this wasn't a WalSender command. */
return false;
}
/*
+ * Looks like a WalSender command, so parse it.
+ */
+ parse_rc = replication_yyparse();
+ if (parse_rc != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg_internal("replication command parser returned %d",
+ parse_rc)));
+ replication_scanner_finish();
+
+ cmd_node = replication_parse_result;
+
+ /*
* Report query to various monitoring facilities. For this purpose, we
* report replication commands just like SQL commands.
*/
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index f9ddafd3459..da35f2c2722 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -501,7 +501,6 @@ typedef enum NodeTag
T_ReadReplicationSlotCmd,
T_StartReplicationCmd,
T_TimeLineHistoryCmd,
- T_SQLCmd,
/*
* TAGS FOR RANDOM OTHER STUFF
diff --git a/src/include/nodes/replnodes.h b/src/include/nodes/replnodes.h
index a7723447400..8ae9c517ff2 100644
--- a/src/include/nodes/replnodes.h
+++ b/src/include/nodes/replnodes.h
@@ -108,13 +108,4 @@ typedef struct TimeLineHistoryCmd
TimeLineID timeline;
} TimeLineHistoryCmd;
-/* ----------------------
- * SQL commands
- * ----------------------
- */
-typedef struct SQLCmd
-{
- NodeTag type;
-} SQLCmd;
-
#endif /* REPLNODES_H */
diff --git a/src/include/replication/walsender_private.h b/src/include/replication/walsender_private.h
index e62ef965f70..9631047c6c1 100644
--- a/src/include/replication/walsender_private.h
+++ b/src/include/replication/walsender_private.h
@@ -121,6 +121,7 @@ extern int replication_yylex(void);
extern void replication_yyerror(const char *str) pg_attribute_noreturn();
extern void replication_scanner_init(const char *query_string);
extern void replication_scanner_finish(void);
+extern bool replication_scanner_is_replication_command(void);
extern Node *replication_parse_result;