diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index 80c14fb74cd8d209863f93f8e218e92f064eae87..76c062fd5162f4a12ca78f70575cca431773fa08 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -1460,15 +1460,26 @@ The commands accepted in walsender mode are: </varlistentry> <varlistentry> - <term>BASE_BACKUP <replaceable>options</><literal>;</><replaceable>label</></term> + <term>BASE_BACKUP [<literal>LABEL</literal> <replaceable>'label'</replaceable>] [<literal>PROGRESS</literal>]</term> <listitem> <para> Instructs the server to start streaming a base backup. - The system will automatically be put in backup mode with the label - specified in <replaceable>label</> before the backup is started, and - taken out of it when the backup is complete. The following options - are accepted: + The system will automatically be put in backup mode before the backup + is started, and taken out of it when the backup is complete. The + following options are accepted: <variablelist> + <varlistentry> + <term><literal>LABEL</literal> <replaceable>'label'</replaceable></term> + <listitem> + <para> + Sets the label of the backup. If none is specified, a backup label + of <literal>base backup</literal> will be used. The quoting rules + for the label are the same as a standard SQL string with + <xref linkend="guc-standard-conforming-strings"> turned on. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><literal>PROGRESS</></term> <listitem> diff --git a/src/backend/replication/Makefile b/src/backend/replication/Makefile index 21fc096df3f2235e87ee4e79e9a24d49dab3c5e8..42c6eaf26c33ad3c1ffaf0c6c9bf489bb951b654 100644 --- a/src/backend/replication/Makefile +++ b/src/backend/replication/Makefile @@ -12,6 +12,29 @@ subdir = src/backend/replication top_builddir = ../../.. include $(top_builddir)/src/Makefile.global -OBJS = walsender.o walreceiverfuncs.o walreceiver.o basebackup.o +OBJS = walsender.o walreceiverfuncs.o walreceiver.o basebackup.o \ + repl_gram.o include $(top_srcdir)/src/backend/common.mk + +# repl_scanner is compiled as part of repl_gram +repl_gram.o: repl_scanner.c + +# See notes in src/backend/parser/Makefile about the following two rules + +repl_gram.c: repl_gram.y +ifdef BISON + $(BISON) -d $(BISONFLAGS) -o $@ $< +else + @$(missing) bison $< $@ +endif + +repl_scanner.c: repl_scanner.l +ifdef FLEX + $(FLEX) $(FLEXFLAGS) -o'$@' $< +else + @$(missing) flex $< $@ +endif + +# repl_gram.c and repl_scanner.c are in the distribution tarball, so +# they are not cleaned here. diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index 7929f855f6d17c65eec9eeb65f0c1f127f141848..1ed5e2a6c9eeecdb4642bc619448c330f700035c 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -98,12 +98,10 @@ perform_base_backup(const char *backup_label, List *tablespaces) * pg_stop_backup() for the user. */ void -SendBaseBackup(const char *options) +SendBaseBackup(const char *backup_label, bool progress) { DIR *dir; struct dirent *de; - char *backup_label = strchr(options, ';'); - bool progress = false; List *tablespaces = NIL; tablespaceinfo *ti; MemoryContext backup_context; @@ -119,18 +117,7 @@ SendBaseBackup(const char *options) WalSndSetState(WALSNDSTATE_BACKUP); if (backup_label == NULL) - ereport(FATAL, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("invalid base backup options: %s", options))); - backup_label++; /* Walk past the semicolon */ - - /* Currently the only option string supported is PROGRESS */ - if (strncmp(options, "PROGRESS", 8) == 0) - progress = true; - else if (options[0] != ';') - ereport(FATAL, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("invalid base backup options: %s", options))); + backup_label = "base backup"; if (update_process_title) { diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y new file mode 100644 index 0000000000000000000000000000000000000000..0ef33ddb4f9c494922552d68f2bb19f6368520f4 --- /dev/null +++ b/src/backend/replication/repl_gram.y @@ -0,0 +1,143 @@ +%{ +/*------------------------------------------------------------------------- + * + * repl_gram.y - Parser for the replication commands + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/replication/repl_gram.y + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "replication/replnodes.h" +#include "replication/walsender.h" + +/* Result of the parsing is returned here */ +Node *replication_parse_result; + +/* Location tracking support --- simpler than bison's default */ +#define YYLLOC_DEFAULT(Current, Rhs, N) \ + do { \ + if (N) \ + (Current) = (Rhs)[1]; \ + else \ + (Current) = (Rhs)[0]; \ + } while (0) + +/* + * Bison doesn't allocate anything that needs to live across parser calls, + * so we can easily have it use palloc instead of malloc. This prevents + * memory leaks if we error out during parsing. Note this only works with + * bison >= 2.0. However, in bison 1.875 the default is to use alloca() + * if possible, so there's not really much problem anyhow, at least if + * you're building with gcc. + */ +#define YYMALLOC palloc +#define YYFREE pfree + +#define parser_yyerror(msg) replication_yyerror(msg, yyscanner) +#define parser_errposition(pos) replication_scanner_errposition(pos) + +%} + +%expect 0 +%name-prefix="replication_yy" + +%union { + char *str; + bool boolval; + + XLogRecPtr recptr; + Node *node; +} + +/* Non-keyword tokens */ +%token <str> SCONST +%token <recptr> RECPTR + +/* Keyword tokens. */ +%token K_BASE_BACKUP +%token K_IDENTIFY_SYSTEM +%token K_LABEL +%token K_PROGRESS +%token K_START_REPLICATION + +%type <node> command +%type <node> base_backup start_replication identify_system +%type <boolval> opt_progress +%type <str> opt_label + +%% + +firstcmd: command opt_semicolon + { + replication_parse_result = $1; + } + ; + +opt_semicolon: ';' + | /* EMPTY */ + ; + +command: + identify_system + | base_backup + | start_replication + ; + +/* + * IDENTIFY_SYSTEM + */ +identify_system: + K_IDENTIFY_SYSTEM + { + $$ = (Node *) makeNode(IdentifySystemCmd); + } + ; + +/* + * BASE_BACKUP [LABEL <label>] [PROGRESS] + */ +base_backup: + K_BASE_BACKUP opt_label opt_progress + { + BaseBackupCmd *cmd = (BaseBackupCmd *) makeNode(BaseBackupCmd); + + cmd->label = $2; + cmd->progress = $3; + + $$ = (Node *) cmd; + } + ; + +opt_label: K_LABEL SCONST { $$ = $2; } + | /* EMPTY */ { $$ = NULL; } + ; + +opt_progress: K_PROGRESS { $$ = true; } + | /* EMPTY */ { $$ = false; } + ; + +/* + * START_REPLICATION %X/%X + */ +start_replication: + K_START_REPLICATION RECPTR + { + StartReplicationCmd *cmd; + + cmd = makeNode(StartReplicationCmd); + cmd->startpoint = $2; + + $$ = (Node *) cmd; + } + ; +%% + +#include "repl_scanner.c" diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l new file mode 100644 index 0000000000000000000000000000000000000000..014a72059a48693ff58583fcf9f063fd718d2054 --- /dev/null +++ b/src/backend/replication/repl_scanner.l @@ -0,0 +1,168 @@ +%{ +/*------------------------------------------------------------------------- + * + * repl_scanner.l + * a lexical scanner for the replication commands + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/replication/repl_scanner.l + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ +#undef fprintf +#define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg))) + +/* Handle to the buffer that the lexer uses internally */ +static YY_BUFFER_STATE scanbufhandle; + +static StringInfoData litbuf; + +static void startlit(void); +static char *litbufdup(void); +static void addlit(char *ytext, int yleng); +static void addlitchar(unsigned char ychar); + +%} + +%option 8bit +%option never-interactive +%option nodefault +%option noinput +%option nounput +%option noyywrap +%option warn +%option prefix="replication_yy" + +%x xq + +/* Extended quote + * xqdouble implements embedded quote, '''' + */ +xqstart {quote} +xqdouble {quote}{quote} +xqinside [^']+ + +hexdigit [0-9A-Za-z]+ + +quote ' +quotestop {quote} + +%% + +BASE_BACKUP { return K_BASE_BACKUP; } +IDENTIFY_SYSTEM { return K_IDENTIFY_SYSTEM; } +LABEL { return K_LABEL; } +PROGRESS { return K_PROGRESS; } +START_REPLICATION { return K_START_REPLICATION; } +"," { return ','; } +";" { return ';'; } + +[\n] ; +[\t] ; +" " ; + +{hexdigit}+\/{hexdigit}+ { + if (sscanf(yytext, "%X/%X", &yylval.recptr.xlogid, &yylval.recptr.xrecoff) != 2) + yyerror("invalid streaming start location"); + return RECPTR; + } + +{xqstart} { + BEGIN(xq); + startlit(); + } +<xq>{quotestop} { + yyless(1); + BEGIN(INITIAL); + yylval.str = litbufdup(); + return SCONST; + } +<xq>{xqdouble} { + addlitchar('\''); + } +<xq>{xqinside} { + addlit(yytext, yyleng); + } + +<xq><<EOF>> { yyerror("unterminated quoted string"); } + + +<<EOF>> { + yyterminate(); + } + +. { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("syntax error: unexpected character \"%s\"", yytext))); + } +%% + + +static void +startlit(void) +{ + initStringInfo(&litbuf); +} + +static char * +litbufdup(void) +{ + return litbuf.data; +} + +static void +addlit(char *ytext, int yleng) +{ + appendBinaryStringInfo(&litbuf, ytext, yleng); +} + +static void +addlitchar(unsigned char ychar) +{ + appendStringInfoChar(&litbuf, ychar); +} + +void +yyerror(const char *message) +{ + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg_internal("%s", message))); +} + + +void +replication_scanner_init(const char *str) +{ + Size slen = strlen(str); + char *scanbuf; + + /* + * Might be left over after ereport() + */ + if (YY_CURRENT_BUFFER) + yy_delete_buffer(YY_CURRENT_BUFFER); + + /* + * Make a scan buffer with special termination needed by flex. + */ + scanbuf = (char *) palloc(slen + 2); + memcpy(scanbuf, str, slen); + scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR; + scanbufhandle = yy_scan_buffer(scanbuf, slen + 2); +} + +void +replication_scanner_finish() +{ + yy_delete_buffer(scanbufhandle); + scanbufhandle = NULL; +} diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index 2347567ccd621ad2e8e64d0c0a4ad969caf72918..d078501814c7a55f6fadac59b2da56921893e3cb 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -45,6 +45,7 @@ #include "libpq/pqsignal.h" #include "miscadmin.h" #include "replication/basebackup.h" +#include "replication/replnodes.h" #include "replication/walprotocol.h" #include "replication/walsender.h" #include "storage/fd.h" @@ -99,6 +100,7 @@ static void WalSndXLogSendHandler(SIGNAL_ARGS); static void WalSndLastCycleHandler(SIGNAL_ARGS); /* Prototypes for private functions */ +static bool HandleReplicationCommand(const char *cmd_string); static int WalSndLoop(void); static void InitWalSnd(void); static void WalSndHandshake(void); @@ -106,6 +108,8 @@ static void WalSndKill(int code, Datum arg); static void XLogRead(char *buf, XLogRecPtr recptr, Size nbytes); static bool XLogSend(char *msgbuf, bool *caughtup); static void CheckClosedConnection(void); +static void IdentifySystem(void); +static void StartReplication(StartReplicationCmd * cmd); /* Main entry point for walsender process */ @@ -218,118 +222,14 @@ WalSndHandshake(void) case 'Q': /* Query message */ { const char *query_string; - XLogRecPtr recptr; query_string = pq_getmsgstring(&input_message); pq_getmsgend(&input_message); - if (strcmp(query_string, "IDENTIFY_SYSTEM") == 0) - { - StringInfoData buf; - char sysid[32]; - char tli[11]; - - /* - * Reply with a result set with one row, two columns. - * First col is system ID, and second is timeline ID - */ - - snprintf(sysid, sizeof(sysid), UINT64_FORMAT, - GetSystemIdentifier()); - snprintf(tli, sizeof(tli), "%u", ThisTimeLineID); - - /* Send a RowDescription message */ - pq_beginmessage(&buf, 'T'); - pq_sendint(&buf, 2, 2); /* 2 fields */ - - /* first field */ - pq_sendstring(&buf, "systemid"); /* col name */ - pq_sendint(&buf, 0, 4); /* table oid */ - pq_sendint(&buf, 0, 2); /* attnum */ - pq_sendint(&buf, TEXTOID, 4); /* type oid */ - pq_sendint(&buf, -1, 2); /* typlen */ - pq_sendint(&buf, 0, 4); /* typmod */ - pq_sendint(&buf, 0, 2); /* format code */ - - /* second field */ - pq_sendstring(&buf, "timeline"); /* col name */ - pq_sendint(&buf, 0, 4); /* table oid */ - pq_sendint(&buf, 0, 2); /* attnum */ - pq_sendint(&buf, INT4OID, 4); /* type oid */ - pq_sendint(&buf, 4, 2); /* typlen */ - pq_sendint(&buf, 0, 4); /* typmod */ - pq_sendint(&buf, 0, 2); /* format code */ - pq_endmessage(&buf); - - /* Send a DataRow message */ - pq_beginmessage(&buf, 'D'); - pq_sendint(&buf, 2, 2); /* # of columns */ - pq_sendint(&buf, strlen(sysid), 4); /* col1 len */ - pq_sendbytes(&buf, (char *) &sysid, strlen(sysid)); - pq_sendint(&buf, strlen(tli), 4); /* col2 len */ - pq_sendbytes(&buf, (char *) tli, strlen(tli)); - pq_endmessage(&buf); - - /* Send CommandComplete and ReadyForQuery messages */ - EndCommand("SELECT", DestRemote); - ReadyForQuery(DestRemote); - /* ReadyForQuery did pq_flush for us */ - } - else if (sscanf(query_string, "START_REPLICATION %X/%X", - &recptr.xlogid, &recptr.xrecoff) == 2) - { - StringInfoData buf; - - /* - * Check that we're logging enough information in the - * WAL for log-shipping. - * - * NOTE: This only checks the current value of - * wal_level. Even if the current setting is not - * 'minimal', there can be old WAL in the pg_xlog - * directory that was created with 'minimal'. So this - * is not bulletproof, the purpose is just to give a - * user-friendly error message that hints how to - * configure the system correctly. - */ - if (wal_level == WAL_LEVEL_MINIMAL) - ereport(FATAL, - (errcode(ERRCODE_CANNOT_CONNECT_NOW), - errmsg("standby connections not allowed because wal_level=minimal"))); - - /* Send a CopyBothResponse message, and start streaming */ - pq_beginmessage(&buf, 'W'); - pq_sendbyte(&buf, 0); - pq_sendint(&buf, 0, 2); - pq_endmessage(&buf); - pq_flush(); - - /* - * Initialize position to the received one, then the - * xlog records begin to be shipped from that position - */ - sentPtr = recptr; - - /* break out of the loop */ + if (HandleReplicationCommand(query_string)) replication_started = true; - } - else if (strncmp(query_string, "BASE_BACKUP ", 12) == 0) - { - /* Command is BASE_BACKUP <options>;<label> */ - SendBaseBackup(query_string + strlen("BASE_BACKUP ")); - /* Send CommandComplete and ReadyForQuery messages */ - EndCommand("SELECT", DestRemote); - ReadyForQuery(DestRemote); - /* ReadyForQuery did pq_flush for us */ - } - else - { - ereport(FATAL, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("invalid standby query string: %s", query_string))); - } - break; } + break; case 'X': /* standby is closing the connection */ @@ -350,6 +250,170 @@ WalSndHandshake(void) } } +/* + * IDENTIFY_SYSTEM + */ +static void +IdentifySystem(void) +{ + StringInfoData buf; + char sysid[32]; + char tli[11]; + + /* + * Reply with a result set with one row, two columns. First col is system + * ID, and second is timeline ID + */ + + snprintf(sysid, sizeof(sysid), UINT64_FORMAT, + GetSystemIdentifier()); + snprintf(tli, sizeof(tli), "%u", ThisTimeLineID); + + /* Send a RowDescription message */ + pq_beginmessage(&buf, 'T'); + pq_sendint(&buf, 2, 2); /* 2 fields */ + + /* first field */ + pq_sendstring(&buf, "systemid"); /* col name */ + pq_sendint(&buf, 0, 4); /* table oid */ + pq_sendint(&buf, 0, 2); /* attnum */ + pq_sendint(&buf, TEXTOID, 4); /* type oid */ + pq_sendint(&buf, -1, 2); /* typlen */ + pq_sendint(&buf, 0, 4); /* typmod */ + pq_sendint(&buf, 0, 2); /* format code */ + + /* second field */ + pq_sendstring(&buf, "timeline"); /* col name */ + pq_sendint(&buf, 0, 4); /* table oid */ + pq_sendint(&buf, 0, 2); /* attnum */ + pq_sendint(&buf, INT4OID, 4); /* type oid */ + pq_sendint(&buf, 4, 2); /* typlen */ + pq_sendint(&buf, 0, 4); /* typmod */ + pq_sendint(&buf, 0, 2); /* format code */ + pq_endmessage(&buf); + + /* Send a DataRow message */ + pq_beginmessage(&buf, 'D'); + pq_sendint(&buf, 2, 2); /* # of columns */ + pq_sendint(&buf, strlen(sysid), 4); /* col1 len */ + pq_sendbytes(&buf, (char *) &sysid, strlen(sysid)); + pq_sendint(&buf, strlen(tli), 4); /* col2 len */ + pq_sendbytes(&buf, (char *) tli, strlen(tli)); + pq_endmessage(&buf); + + /* Send CommandComplete and ReadyForQuery messages */ + EndCommand("SELECT", DestRemote); + ReadyForQuery(DestRemote); + /* ReadyForQuery did pq_flush for us */ +} + +/* + * START_REPLICATION + */ +static void +StartReplication(StartReplicationCmd * cmd) +{ + StringInfoData buf; + + /* + * Check that we're logging enough information in the WAL for + * log-shipping. + * + * NOTE: This only checks the current value of wal_level. Even if the + * current setting is not 'minimal', there can be old WAL in the pg_xlog + * directory that was created with 'minimal'. So this is not bulletproof, + * the purpose is just to give a user-friendly error message that hints + * how to configure the system correctly. + */ + if (wal_level == WAL_LEVEL_MINIMAL) + ereport(FATAL, + (errcode(ERRCODE_CANNOT_CONNECT_NOW), + errmsg("standby connections not allowed because wal_level=minimal"))); + + /* Send a CopyBothResponse message, and start streaming */ + pq_beginmessage(&buf, 'W'); + pq_sendbyte(&buf, 0); + pq_sendint(&buf, 0, 2); + pq_endmessage(&buf); + pq_flush(); + + /* + * Initialize position to the received one, then the xlog records begin to + * be shipped from that position + */ + sentPtr = cmd->startpoint; +} + +/* + * Execute an incoming replication command. + */ +static bool +HandleReplicationCommand(const char *cmd_string) +{ + bool replication_started = false; + int parse_rc; + Node *cmd_node; + MemoryContext cmd_context; + MemoryContext old_context; + + elog(DEBUG1, "received replication command: %s", cmd_string); + + cmd_context = AllocSetContextCreate(CurrentMemoryContext, + "Replication command context", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + 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)))); + + cmd_node = replication_parse_result; + + switch (cmd_node->type) + { + case T_IdentifySystemCmd: + IdentifySystem(); + break; + + case T_StartReplicationCmd: + StartReplication((StartReplicationCmd *) cmd_node); + + /* break out of the loop */ + replication_started = true; + break; + + case T_BaseBackupCmd: + { + BaseBackupCmd *cmd = (BaseBackupCmd *) cmd_node; + + SendBaseBackup(cmd->label, cmd->progress); + + /* Send CommandComplete and ReadyForQuery messages */ + EndCommand("SELECT", DestRemote); + ReadyForQuery(DestRemote); + /* ReadyForQuery did pq_flush for us */ + break; + } + + default: + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("invalid standby query string: %s", cmd_string))); + } + + /* done */ + MemoryContextSwitchTo(old_context); + MemoryContextDelete(cmd_context); + + return replication_started; +} + /* * Check if the remote end has closed the connection. */ diff --git a/src/include/replication/basebackup.h b/src/include/replication/basebackup.h index 61e531543c66caa007d059c254f9f3522d7e424e..eb2e1601768a50745f409d3f9b5dcaee63e08106 100644 --- a/src/include/replication/basebackup.h +++ b/src/include/replication/basebackup.h @@ -12,6 +12,6 @@ #ifndef _BASEBACKUP_H #define _BASEBACKUP_H -extern void SendBaseBackup(const char *options); +extern void SendBaseBackup(const char *backup_label, bool progress); #endif /* _BASEBACKUP_H */ diff --git a/src/include/replication/replnodes.h b/src/include/replication/replnodes.h new file mode 100644 index 0000000000000000000000000000000000000000..4f4a1a3bac31f7a6b240c48e2c4cd37b22f5b417 --- /dev/null +++ b/src/include/replication/replnodes.h @@ -0,0 +1,63 @@ +/*------------------------------------------------------------------------- + * + * replnodes.h + * definitions for replication grammar parse nodes + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/replication/replnodes.h + * + *------------------------------------------------------------------------- + */ +#ifndef REPLNODES_H +#define REPLNODES_H + +#include "access/xlogdefs.h" +#include "nodes/primnodes.h" +#include "nodes/value.h" + +/* + * NodeTags for replication parser + */ +typedef enum ReplNodeTag +{ + T_IdentifySystemCmd = 10, + T_BaseBackupCmd, + T_StartReplicationCmd +} ReplNodeTag; + +/* ---------------------- + * IDENTIFY_SYSTEM command + * ---------------------- + */ +typedef struct IdentifySystemCmd +{ + NodeTag type; +} IdentifySystemCmd; + + +/* ---------------------- + * BASE_BACKUP command + * ---------------------- + */ +typedef struct BaseBackupCmd +{ + NodeTag type; + char *label; + bool progress; +} BaseBackupCmd; + + +/* ---------------------- + * START_REPLICATION command + * ---------------------- + */ +typedef struct StartReplicationCmd +{ + NodeTag type; + XLogRecPtr startpoint; +} StartReplicationCmd; + +#endif /* REPLNODES_H */ diff --git a/src/include/replication/walsender.h b/src/include/replication/walsender.h index 9039b240c44e450c8d1a7e16e0c0131fc0c2d0b6..bd9e19320fb75f3c9d5b374a9cbcf7cfb641743e 100644 --- a/src/include/replication/walsender.h +++ b/src/include/replication/walsender.h @@ -13,6 +13,7 @@ #define _WALSENDER_H #include "access/xlog.h" +#include "nodes/nodes.h" #include "storage/latch.h" #include "storage/spin.h" @@ -69,4 +70,16 @@ extern void WalSndSetState(WalSndState state); extern Datum pg_stat_get_wal_senders(PG_FUNCTION_ARGS); +/* + * Internal functions for parsing the replication grammar, in repl_gram.y and + * repl_scanner.l + */ +extern int replication_yyparse(void); +extern int replication_yylex(void); +extern void replication_yyerror(const char *str); +extern void replication_scanner_init(const char *query_string); +extern void replication_scanner_finish(void); + +extern Node *replication_parse_result; + #endif /* _WALSENDER_H */ diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 8d144bdb9f8a0b7c8476b91f3b65e7b9b936e424..29c3c775f888f2fef442a079febe095d9226b4b8 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -70,6 +70,7 @@ sub mkvcbuild $postgres->AddFiles('src\backend\parser','scan.l','gram.y'); $postgres->AddFiles('src\backend\bootstrap','bootscanner.l','bootparse.y'); $postgres->AddFiles('src\backend\utils\misc','guc-file.l'); + $postgres->AddFiles('src\backend\replication', 'repl_scanner.l', 'repl_gram.y'); $postgres->AddDefine('BUILDING_DLL'); $postgres->AddLibrary('wsock32.lib'); $postgres->AddLibrary('ws2_32.lib'); diff --git a/src/tools/msvc/pgbison.bat b/src/tools/msvc/pgbison.bat index 9f30e72b7d4855e2b55e59b54ff16746344fa3bc..24963ab2c857c305743086a94ac0da224317e52a 100755 --- a/src/tools/msvc/pgbison.bat +++ b/src/tools/msvc/pgbison.bat @@ -17,6 +17,7 @@ goto nobison if "%1" == "src\backend\parser\gram.y" call :generate %1 src\backend\parser\gram.c src\backend\parser\gram.h if "%1" == "src\backend\bootstrap\bootparse.y" call :generate %1 src\backend\bootstrap\bootparse.c +if "%1" == "src\backend\replication\repl_gram.y" call :generate %1 src\backend\replication\repl_gram.c if "%1" == "src\pl\plpgsql\src\gram.y" call :generate %1 src\pl\plpgsql\src\pl_gram.c src\pl\plpgsql\src\pl_gram.h if "%1" == "src\interfaces\ecpg\preproc\preproc.y" call :generate %1 src\interfaces\ecpg\preproc\preproc.c src\interfaces\ecpg\preproc\preproc.h if "%1" == "contrib\cube\cubeparse.y" call :generate %1 contrib\cube\cubeparse.c diff --git a/src/tools/msvc/pgflex.bat b/src/tools/msvc/pgflex.bat index 7038fc95e4dade9e9dd9d73beb12384280aca929..7102e5690ecee38b41cd7b2aa2694e6a6ce83ef3 100755 --- a/src/tools/msvc/pgflex.bat +++ b/src/tools/msvc/pgflex.bat @@ -13,6 +13,7 @@ if errorlevel 1 goto noflex if "%1" == "src\backend\parser\scan.l" call :generate %1 src\backend\parser\scan.c -CF if "%1" == "src\backend\bootstrap\bootscanner.l" call :generate %1 src\backend\bootstrap\bootscanner.c if "%1" == "src\backend\utils\misc\guc-file.l" call :generate %1 src\backend\utils\misc\guc-file.c +if "%1" == "src\backend\replication\repl_scanner.l" call :generate %1 src\backend\replication\repl_scanner.c if "%1" == "src\interfaces\ecpg\preproc\pgc.l" call :generate %1 src\interfaces\ecpg\preproc\pgc.c if "%1" == "src\bin\psql\psqlscan.l" call :generate %1 src\bin\psql\psqlscan.c if "%1" == "contrib\cube\cubescan.l" call :generate %1 contrib\cube\cubescan.c