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 */