<listitem>
<para>
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:
</para>
<para>
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term>
+ xlogpos
+ </term>
+ <listitem>
+ <para>
+ Current xlog write location. Useful to get a known location in the
+ transaction log where streaming can start.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
</listitem>
</variablelist>
</para>
<para>
- 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 <literal>pg_default</> and <literal>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.
+ </para>
+ <para>
+ The first ordinary result set contains the starting position of the
+ backup, given in XLogRecPtr format as a single column in a single row.
</para>
<para>
- 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:
<variablelist>
<varlistentry>
</varlistentry>
</variablelist>
</para>
+ <para>
+ After the second regular result set, one or more CopyResponse results
+ will be sent, one for PGDATA and one for each additional tablespace other
+ than <literal>pg_default</> and <literal>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.
+ </para>
+
<para>
The tar archive for the data directory and each tablespace will contain
all files in the directories, regardless of whether they are
Owner, group and file mode are set if the underlying filesystem on
the server supports it.
</para>
+ <para>
+ 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.
+ </para>
</listitem>
</varlistentry>
</variablelist>
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.
char *labelfile;
startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint, &labelfile);
+ SendXlogRecPtrResult(startptr);
PG_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0);
{
/* Send CopyDone message for the last tar file */
pq_putemptymessage('c');
}
+ SendXlogRecPtrResult(endptr);
}
/*
pq_puttextmessage('C', "SELECT");
}
+/*
+ * 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.
*/
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 */
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 */
BaseBackup()
{
PGresult *res;
+ uint32 timeline;
char current_path[MAXPGPATH];
char escaped_label[MAXPGPATH];
int i;
+ char xlogstart[64];
+ char xlogend[64];
/*
* Connect in replication mode to the server
*/
conn = GetConnection();
+ /*
+ * Run IDENFITY_SYSTEM so we can get the timeline
+ */
+ res = PQexec(conn, "IDENTIFY_SYSTEM");
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ {
+ fprintf(stderr, _("%s: could not identify system: %s\n"),
+ progname, PQerrorMessage(conn));
+ disconnect_and_exit(1);
+ }
+ if (PQntuples(res) != 1)
+ {
+ fprintf(stderr, _("%s: could not identify system, got %i rows\n"),
+ progname, PQntuples(res));
+ disconnect_and_exit(1);
+ }
+ timeline = atoi(PQgetvalue(res, 0, 1));
+ PQclear(res);
+
+ /*
+ * Start the actual backup
+ */
PQescapeStringConn(conn, escaped_label, label, sizeof(escaped_label), &i);
snprintf(current_path, sizeof(current_path), "BASE_BACKUP LABEL '%s' %s %s %s",
escaped_label,
}
/*
- * Get the header
+ * Get the starting xlog position
*/
res = PQgetResult(conn);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
progname, PQerrorMessage(conn));
disconnect_and_exit(1);
}
+ if (PQntuples(res) != 1)
+ {
+ fprintf(stderr, _("%s: no start point returned from server.\n"),
+ progname);
+ disconnect_and_exit(1);
+ }
+ strcpy(xlogstart, PQgetvalue(res, 0, 0));
+ if (verbose && includewal)
+ fprintf(stderr, "xlog start point: %s\n", xlogstart);
+ PQclear(res);
+ MemSet(xlogend, 0, sizeof(xlogend));
+
+ /*
+ * Get the header
+ */
+ res = PQgetResult(conn);
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ {
+ fprintf(stderr, _("%s: could not get backup header: %s\n"),
+ progname, PQerrorMessage(conn));
+ disconnect_and_exit(1);
+ }
if (PQntuples(res) < 1)
{
fprintf(stderr, _("%s: no data returned from server.\n"), progname);
}
PQclear(res);
+ /*
+ * Get the stop position
+ */
+ res = PQgetResult(conn);
+ if (PQresultStatus(res) != PGRES_TUPLES_OK)
+ {
+ fprintf(stderr, _("%s: could not get end xlog position from server.\n"),
+ progname);
+ disconnect_and_exit(1);
+ }
+ if (PQntuples(res) != 1)
+ {
+ fprintf(stderr, _("%s: no end point returned from server.\n"),
+ progname);
+ disconnect_and_exit(1);
+ }
+ strcpy(xlogend, PQgetvalue(res, 0, 0));
+ if (verbose && includewal)
+ fprintf(stderr, "xlog end point: %s\n", xlogend);
+ PQclear(res);
+
res = PQgetResult(conn);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{