diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index 73c05ed..5e9b5d4 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -1315,7 +1315,7 @@ The commands accepted in walsender mode are: Requests the server to identify itself. Server replies with a result - set of a single row, containing two fields: + set of a single row, containing three fields: @@ -1344,6 +1344,19 @@ The commands accepted in walsender mode are: + + + + xlogpos + + + + Current xlog write location. Useful to get a known location in the + transaction log where streaming can start. + + + + @@ -1520,15 +1533,16 @@ The commands accepted in walsender mode are: - When the backup is started, the server will first send a header in - ordinary result set format, followed by one or more CopyResponse - results, one for PGDATA and one for each additional tablespace other - than pg_default and pg_global. The data in - the CopyResponse results will be a tar format (using ustar00 - extensions) dump of the tablespace contents. + When the backup is started, the server will first send two + ordinary result sets, followed by one or more CopyResponse + results. + + + The first ordinary result set contains the starting position of the + backup, given in XLogRecPtr format as a single column in a single row. - The header is an ordinary resultset with one row for each tablespace. + The second ordinary result set has one row for each tablespace. The fields in this row are: @@ -1561,6 +1575,15 @@ The commands accepted in walsender mode are: + After the two regular result set, one or more CopyResponse results + will be sent, one for PGDATA and one for each additional tablespace other + than pg_default and pg_global. The data in + the CopyResponse results will be a tar format (using ustar00 + extensions) dump of the tablespace contents. After the tar data is + complete, a final ordinary result set will be sent. + + + The tar archive for the data directory and each tablespace will contain all files in the directories, regardless of whether they are PostgreSQL files or other files added to the same @@ -1583,6 +1606,11 @@ The commands accepted in walsender mode are: Owner, group and file mode are set if the underlying filesystem on the server supports it. + + Once all tablespaces have been sent, a final regular result set will + be sent. This result set contains the end position of the + backup, given in XLogRecPtr format as a single column in a single row. + diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index 29284a6..840d577 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -52,6 +52,7 @@ static void SendBackupHeader(List *tablespaces); static void base_backup_cleanup(int code, Datum arg); static void perform_base_backup(basebackup_options *opt, DIR *tblspcdir); static void parse_basebackup_options(List *options, basebackup_options *opt); +static void SendXlogRecPtrResult(XLogRecPtr ptr); /* * Size of each block sent into the tar stream for larger files. @@ -92,6 +93,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) char *labelfile; startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint, &labelfile); + SendXlogRecPtrResult(startptr); PG_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0); { @@ -239,6 +241,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) /* Send CopyDone message for the last tar file */ pq_putemptymessage('c'); } + SendXlogRecPtrResult(endptr); } /* @@ -432,6 +435,42 @@ SendBackupHeader(List *tablespaces) } /* + * Send a single resultset containing just a single + * XlogRecPtr record (in text format) + */ +static void +SendXlogRecPtrResult(XLogRecPtr ptr) +{ + StringInfoData buf; + char str[MAXFNAMELEN]; + + snprintf(str, sizeof(str), "%X/%X", ptr.xlogid, ptr.xrecoff); + + pq_beginmessage(&buf, 'T'); /* RowDescription */ + pq_sendint(&buf, 1, 2); /* 1 field */ + + /* Field header */ + pq_sendstring(&buf, "recptr"); + 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); + pq_sendint(&buf, 0, 4); + pq_sendint(&buf, 0, 2); + pq_endmessage(&buf); + + /* Data row */ + pq_beginmessage(&buf, 'D'); + pq_sendint(&buf, 1, 2); /* number of columns */ + pq_sendint(&buf, strlen(str), 4); /* length */ + pq_sendbytes(&buf, str, strlen(str)); + pq_endmessage(&buf); + + /* Send a CommandComplete message */ + pq_puttextmessage('C', "SELECT"); +} + +/* * Inject a file with given name and content in the output tar stream. */ static void diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index f70458e..78963c1 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -258,19 +258,26 @@ IdentifySystem(void) StringInfoData buf; char sysid[32]; char tli[11]; + char xpos[MAXFNAMELEN]; + XLogRecPtr logptr; /* - * Reply with a result set with one row, two columns. First col is system - * ID, and second is timeline ID + * Reply with a result set with one row, three columns. First col is system + * ID, second is timeline ID, and third is current xlog location. */ snprintf(sysid, sizeof(sysid), UINT64_FORMAT, GetSystemIdentifier()); snprintf(tli, sizeof(tli), "%u", ThisTimeLineID); + logptr = GetInsertRecPtr(); + + snprintf(xpos, sizeof(xpos), "%X/%X", + logptr.xlogid, logptr.xrecoff); + /* Send a RowDescription message */ pq_beginmessage(&buf, 'T'); - pq_sendint(&buf, 2, 2); /* 2 fields */ + pq_sendint(&buf, 3, 2); /* 3 fields */ /* first field */ pq_sendstring(&buf, "systemid"); /* col name */ @@ -289,15 +296,27 @@ IdentifySystem(void) pq_sendint(&buf, 4, 2); /* typlen */ pq_sendint(&buf, 0, 4); /* typmod */ pq_sendint(&buf, 0, 2); /* format code */ + + /* third field */ + pq_sendstring(&buf, "xlogpos"); + pq_sendint(&buf, 0, 4); + pq_sendint(&buf, 0, 2); + pq_sendint(&buf, TEXTOID, 4); + pq_sendint(&buf, -1, 2); + pq_sendint(&buf, 0, 4); + pq_sendint(&buf, 0, 2); pq_endmessage(&buf); /* Send a DataRow message */ pq_beginmessage(&buf, 'D'); - pq_sendint(&buf, 2, 2); /* # of columns */ + pq_sendint(&buf, 3, 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_sendint(&buf, strlen(xpos), 4); /* col3 len */ + pq_sendbytes(&buf, (char *) xpos, strlen(xpos)); + pq_endmessage(&buf); /* Send CommandComplete and ReadyForQuery messages */