v2-0003-Add-support-for-the-incremental-backup.patch
text/x-patch
Filename: v2-0003-Add-support-for-the-incremental-backup.patch
Type: text/x-patch
Part: 2
Message:
Re: block-level incremental backup
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v2-0003
Subject: Add support for the incremental backup.
| File | + | − |
|---|---|---|
| doc/src/sgml/protocol.sgml | 49 | 1 |
| doc/src/sgml/ref/pg_basebackup.sgml | 23 | 0 |
| src/backend/access/transam/xlog.c | 18 | 1 |
| src/backend/access/transam/xlogfuncs.c | 4 | 2 |
| src/backend/replication/basebackup.c | 298 | 13 |
| src/backend/storage/file/fd.c | 29 | 0 |
| src/bin/pg_basebackup/t/010_pg_basebackup.pl | 1 | 1 |
| src/include/access/xlog.h | 2 | 1 |
| src/include/replication/basebackup.h | 13 | 0 |
| src/include/storage/fd.h | 1 | 0 |
From 031d3fecd3ceb70de173bd63214fd083a547ae32 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 9 Sep 2019 11:05:50 +0530
Subject: [PATCH v2 3/4] Add support for the incremental backup.
If file is modified 90% or more, we send a whole file else we send
only those blocks which are modified. The file is named .partial and
has following header details:
- magic number, set to 0x494E4352 (4 bytes)
- checksum, of whole file except actual blocks (4 bytes)
- number of blocks in this .partial file (4 bytes)
- all modified block numbers (4 bytes each)
- modified blocks
---
doc/src/sgml/protocol.sgml | 50 ++++-
doc/src/sgml/ref/pg_basebackup.sgml | 23 ++
src/backend/access/transam/xlog.c | 19 +-
src/backend/access/transam/xlogfuncs.c | 6 +-
src/backend/replication/basebackup.c | 311 +++++++++++++++++++++++++--
src/backend/storage/file/fd.c | 29 +++
src/bin/pg_basebackup/t/010_pg_basebackup.pl | 2 +-
src/include/access/xlog.h | 3 +-
src/include/replication/basebackup.h | 13 ++
src/include/storage/fd.h | 1 +
10 files changed, 438 insertions(+), 19 deletions(-)
mode change 100644 => 100755 src/bin/pg_basebackup/t/010_pg_basebackup.pl
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 8027521..21be571 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -2466,7 +2466,7 @@ The commands accepted in replication mode are:
</varlistentry>
<varlistentry>
- <term><literal>BASE_BACKUP</literal> [ <literal>LABEL</literal> <replaceable>'label'</replaceable> ] [ <literal>PROGRESS</literal> ] [ <literal>FAST</literal> ] [ <literal>WAL</literal> ] [ <literal>NOWAIT</literal> ] [ <literal>MAX_RATE</literal> <replaceable>rate</replaceable> ] [ <literal>TABLESPACE_MAP</literal> ] [ <literal>NOVERIFY_CHECKSUMS</literal> ]
+ <term><literal>BASE_BACKUP</literal> [ <literal>LABEL</literal> <replaceable>'label'</replaceable> ] [ <literal>PROGRESS</literal> ] [ <literal>FAST</literal> ] [ <literal>WAL</literal> ] [ <literal>NOWAIT</literal> ] [ <literal>MAX_RATE</literal> <replaceable>rate</replaceable> ] [ <literal>TABLESPACE_MAP</literal> ] [ <literal>NOVERIFY_CHECKSUMS</literal> ] [ <literal>LSN</literal> <replaceable>'lsn'</replaceable> ]
<indexterm><primary>BASE_BACKUP</primary></indexterm>
</term>
<listitem>
@@ -2576,6 +2576,22 @@ The commands accepted in replication mode are:
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>LSN</literal> <replaceable>'lsn'</replaceable></term>
+ <listitem>
+ <para>
+ Includes only those data blocks in backup which has LSN greater than
+ or equal to the given lsn. However, if 90% or more data blocks are
+ modified in the file, then sends the entire file. Otherwise, creates
+ a <filename>.partial</filename> file containing only the blocks which
+ are modified and sends that instead. The <filename>.partial</filename>
+ file has its own header followed by the actual data blocks. Note that
+ only relation files are considered here, all other files are sent as
+ is.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
<para>
@@ -2698,6 +2714,38 @@ The commands accepted in replication mode are:
Owner, group, and file mode are set if the underlying file system on
the server supports it.
</para>
+ <para>
+ An incremental backup's <filename>.partial</filename> file has the
+ following format:
+ <itemizedlist>
+ <listitem>
+ <para>
+ Starts with a 4-byte magic number
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Followed by a 4-byte CRC of the header (containing a magic number,
+ count of the number of blocks, and all block numbers)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Then a 4-byte count of the number of blocks included in the file
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Then the block numbers, each as a 4-byte quantity
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Followed by the actual data blocks in order with the block numbers
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
</listitem>
</varlistentry>
</variablelist>
diff --git a/doc/src/sgml/ref/pg_basebackup.sgml b/doc/src/sgml/ref/pg_basebackup.sgml
index fc9e222..9e2b9b8 100644
--- a/doc/src/sgml/ref/pg_basebackup.sgml
+++ b/doc/src/sgml/ref/pg_basebackup.sgml
@@ -408,6 +408,21 @@ PostgreSQL documentation
</varlistentry>
<varlistentry>
+ <term><option>--lsn=<replaceable class="parameter">LSN</replaceable></option></term>
+ <listitem>
+ <para>
+ Takes an incremental backup, using LSN as a threshold. Only the blocks
+ which are modified after this given LSN will be backed up. The file
+ which has these partial blocks has .partial as an extension. Backup
+ taken in this manner has to be combined with the full backup with the
+ <command>pg_combinebackup</command> utility. The value of LSN should
+ match the <literal>START WAL LOCATION</literal> of previously taken
+ full or incremental backup from <literal>backup_label</literal> file.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-n</option></term>
<term><option>--no-clean</option></term>
<listitem>
@@ -792,6 +807,14 @@ PostgreSQL documentation
<prompt>$</prompt> <userinput>pg_basebackup -D backup/data -T /opt/ts=$(pwd)/backup/ts</userinput>
</screen>
</para>
+
+ <para>
+ To create an incremental backup having LSN greater than or equal to
+ <literal>5/19000060</literal>:
+<screen>
+<prompt>$</prompt> <userinput>pg_basebackup -D incbackup --lsn='5/19000060'</userinput>
+</screen>
+ </para>
</refsect1>
<refsect1>
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 6876537..406c13b 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -10178,7 +10178,8 @@ XLogRecPtr
do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
StringInfo labelfile, List **tablespaces,
StringInfo tblspcmapfile, bool infotbssize,
- bool needtblspcmapfile)
+ bool needtblspcmapfile,
+ XLogRecPtr incremental_reference_lsn)
{
bool exclusive = (labelfile == NULL);
bool backup_started_in_recovery = false;
@@ -10391,6 +10392,18 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
XLogFileName(xlogfilename, starttli, _logSegNo, wal_segment_size);
/*
+ * If we are doing an incremental backup, then passed in reference LSN
+ * must be from past i.e. it should be less than the startpoint.
+ */
+ if (!XLogRecPtrIsInvalid(incremental_reference_lsn) &&
+ incremental_reference_lsn >= startpoint)
+ ereport(ERROR,
+ (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+ errmsg("incremental backup reference lsn %X/%X is in the future",
+ (uint32) (incremental_reference_lsn >> 32),
+ (uint32) incremental_reference_lsn)));
+
+ /*
* Construct tablespace_map file
*/
if (exclusive)
@@ -10506,6 +10519,10 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
appendStringInfo(labelfile, "START TIME: %s\n", strfbuf);
appendStringInfo(labelfile, "LABEL: %s\n", backupidstr);
appendStringInfo(labelfile, "START TIMELINE: %u\n", starttli);
+ if (!XLogRecPtrIsInvalid(incremental_reference_lsn))
+ appendStringInfo(labelfile, "INCREMENTAL BACKUP REFERENCE WAL LOCATION: %X/%X\n",
+ (uint32) (incremental_reference_lsn >> 32),
+ (uint32) incremental_reference_lsn);
/*
* Okay, write the file, or return its contents to caller.
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 8a70503..61598fb 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -89,7 +89,8 @@ pg_start_backup(PG_FUNCTION_ARGS)
if (exclusive)
{
startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL,
- NULL, NULL, false, true);
+ NULL, NULL, false, true,
+ InvalidXLogRecPtr);
}
else
{
@@ -105,7 +106,8 @@ pg_start_backup(PG_FUNCTION_ARGS)
MemoryContextSwitchTo(oldcontext);
startpoint = do_pg_start_backup(backupidstr, fast, NULL, label_file,
- NULL, tblspc_map_file, false, true);
+ NULL, tblspc_map_file, false, true,
+ InvalidXLogRecPtr);
before_shmem_exit(nonexclusive_base_backup_cleanup, (Datum) 0);
}
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index bf15262..90ab3ea 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -83,6 +83,11 @@ static pgoff_t sendCompleteFile(const char *readfilename,
const char *tarfilename, FILE *fp,
struct stat *statbuf, int segmentno,
bool verify_checksum, int *checksum_failures);
+static pgoff_t sendPartialFile(const char *readfilename,
+ const char *tarfilename, FILE *fp,
+ XLogRecPtr incremental_reference_lsn,
+ struct stat *statbuf, int segmentno,
+ bool verify_checksum, int *checksum_failures);
/* Was the backup currently in-progress initiated in recovery mode? */
static bool backup_started_in_recovery = false;
@@ -112,6 +117,11 @@ do { \
(errmsg("could not read from file \"%s\"", filename))); \
} while (0)
+/*
+ * When to send the whole file, % blocks modified (90%)
+ */
+#define WHOLE_FILE_THRESHOLD 0.9
+
/* The actual number of bytes, transfer of which may cause sleep. */
static uint64 throttling_sample;
@@ -127,6 +137,9 @@ static TimestampTz throttled_last;
/* The starting XLOG position of the base backup. */
static XLogRecPtr startptr;
+/* The reference XLOG position for the incremental backup. */
+static XLogRecPtr incremental_reference_lsn;
+
/* Total number of checksum failures during base backup. */
static long long int total_checksum_failures;
@@ -267,7 +280,9 @@ perform_base_backup(basebackup_options *opt)
startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint, &starttli,
labelfile, &tablespaces,
tblspc_map_file,
- opt->progress, opt->sendtblspcmapfile);
+ opt->progress, opt->sendtblspcmapfile,
+ opt->lsn);
+ incremental_reference_lsn = opt->lsn;
/*
* Once do_pg_start_backup has been called, ensure that any failure causes
@@ -665,6 +680,7 @@ parse_basebackup_options(List *options, basebackup_options *opt)
bool o_lsn = false;
MemSet(opt, 0, sizeof(*opt));
+ opt->lsn = InvalidXLogRecPtr;
foreach(lopt, options)
{
DefElem *defel = (DefElem *) lfirst(lopt);
@@ -1407,6 +1423,7 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
int segmentno = 0;
char *segmentpath;
bool verify_checksum = false;
+ char *filename;
fp = AllocateFile(readfilename, "rb");
if (fp == NULL)
@@ -1418,17 +1435,15 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
errmsg("could not open file \"%s\": %m", readfilename)));
}
+ /*
+ * Get the filename (excluding path). As last_dir_separator() includes
+ * the last directory separator, we chop that off by incrementing the
+ * pointer.
+ */
+ filename = last_dir_separator(readfilename) + 1;
+
if (!noverify_checksums && DataChecksumsEnabled())
{
- char *filename;
-
- /*
- * Get the filename (excluding path). As last_dir_separator()
- * includes the last directory separator, we chop that off by
- * incrementing the pointer.
- */
- filename = last_dir_separator(readfilename) + 1;
-
if (is_checksummed_file(readfilename, filename))
{
verify_checksum = true;
@@ -1449,9 +1464,25 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf
}
}
- /* Send complete file to the client. */
- len = sendCompleteFile(readfilename, tarfilename, fp, statbuf, segmentno,
- verify_checksum, &checksum_failures);
+ /*
+ * If incremental backup, see whether the filename is a non-temporary
+ * relation filename or not and can be sent partially. All other files are
+ * sent completely.
+ */
+ if (!XLogRecPtrIsInvalid(incremental_reference_lsn) &&
+ OidIsValid(dboid) && looks_like_non_temp_rel_name(filename))
+ {
+ /* Send partial file to the client. */
+ len = sendPartialFile(readfilename, tarfilename, fp,
+ incremental_reference_lsn, statbuf, segmentno,
+ verify_checksum, &checksum_failures);
+ }
+ else
+ {
+ /* Send complete file to the client. */
+ len = sendCompleteFile(readfilename, tarfilename, fp, statbuf,
+ segmentno, verify_checksum, &checksum_failures);
+ }
/* If the file was truncated while we were sending it, pad it with zeros */
if (len < statbuf->st_size)
@@ -1813,3 +1844,257 @@ sendCompleteFile(const char *readfilename, const char *tarfilename, FILE *fp,
return len;
}
+
+/*
+ * sendPartialFile
+ *
+ * Sends a partial file containing only the blocks which are modified after
+ * given LSN. However, if the file is heavily modified, then we send complete
+ * file instead.
+ */
+static pgoff_t
+sendPartialFile(const char *readfilename, const char *tarfilename, FILE *fp,
+ XLogRecPtr incremental_reference_lsn, struct stat *statbuf,
+ int segmentno, bool verify_checksum, int *checksum_failures)
+{
+ char *buf;
+ off_t cnt;
+ pgoff_t len = 0;
+ bool sendwholefile = false;
+
+ /*
+ * Relation file is segmented at size RELSEG_SIZE * BLCKSZ, so we will
+ * never have size more than that.
+ */
+ Assert(statbuf->st_size <= (RELSEG_SIZE * BLCKSZ));
+
+ /*
+ * We want to read the whole file in memory to see how many blocks were
+ * actually changed. We don't want to do that incrementally as it will
+ * need to reread the file while sending the blocks. palloc() reads
+ * maximum 1GB - 1, and current max relation segment will be of 1GB, thus
+ * we use malloc() here.
+ */
+ buf = (char *) malloc(statbuf->st_size);
+ if (buf == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of memory")));
+
+ if ((cnt = fread(buf, 1, statbuf->st_size, fp)) > 0)
+ {
+ Bitmapset *mod_blocks = NULL;
+ int nmodblocks = 0;
+ int part_size = 0;
+ int part_header_size;
+ int blknum;
+ int blknocnt;
+ partial_file_header *pfh;
+ char *partialtarfilename = NULL;
+
+ /*
+ * A valid relation size is multiple of BLCKSZ. However, if we read
+ * some arbitrary size data, then instead of throwing an error, we
+ * chose to send that file as-is. Inform the same to the client by
+ * emitting a warning. Also, we cannot verify the checksum, if
+ * enabled, emit a warning for that too.
+ */
+ if (cnt % BLCKSZ != 0)
+ {
+ if (verify_checksum)
+ {
+ ereport(WARNING,
+ (errmsg("cannot verify checksum in file \"%s\"",
+ readfilename)));
+ verify_checksum = false;
+ }
+
+ ereport(WARNING,
+ (errmsg("file size (%d) not in multiple of page size (%d), sending whole file",
+ (int) cnt, BLCKSZ)));
+
+ /* File size is not in multiple of BLCKSZ, send as is. */
+ sendwholefile = true;
+ }
+
+ /*
+ * Check each page LSN and see if it is modified after the given LSN or
+ * not. Create a bitmap of all such modified blocks and then decide
+ * whether we want to send a whole file or a partial file. Skip this
+ * check if we decided to send whole file already.
+ */
+ if (!sendwholefile)
+ {
+ XLogRecPtr pglsn;
+ int i;
+ int nblocks = (cnt / BLCKSZ);
+
+ for (i = 0; i < nblocks; i++)
+ {
+ int page_index_in_buf = (BLCKSZ * i);
+ char *page = buf + page_index_in_buf;
+
+ pglsn = PageGetLSN(page);
+
+ if (pglsn >= incremental_reference_lsn)
+ {
+ /*
+ * Verify checksum, if requested, for the modified blocks.
+ */
+ if (verify_checksum)
+ {
+ verify_page_checksum(readfilename, fp, page,
+ (cnt - page_index_in_buf), i,
+ segmentno, checksum_failures);
+
+ /*
+ * If we hit end-of-file, a concurrent truncation must
+ * have occurred, so break out of this loop just as if
+ * the initial fread() returned 0. We'll drop through
+ * to the same code that handles that case. (We must
+ * fix up cnt first, though.)
+ */
+ if (feof(fp))
+ {
+ cnt = page_index_in_buf;
+ break;
+ }
+ }
+
+ mod_blocks = bms_add_member(mod_blocks, i);
+ }
+ }
+
+ nmodblocks = bms_num_members(mod_blocks);
+
+ /*
+ * We need to send whole file if the modified block count is equal
+ * to or greater than the WHOLE_FILE_THRESHOLD. Check that.
+ */
+ if (i > 0 && (nmodblocks / (double) i) >= WHOLE_FILE_THRESHOLD)
+ sendwholefile = true;
+ }
+
+ /*
+ * If sendwholefile is true then we need to send the whole file as is.
+ * Otherwise send a partial file. Instead of sending entire file at a
+ * time, we send data in a series of chunks of size CHUNK_SIZE.
+ * CHUNK_SIZE is arbitrary chosen to 1MB assuming BLCKSZ is of 8K.
+ */
+ if (sendwholefile)
+ {
+#define CHUNK_SIZE (BLCKSZ * 128)
+ int i;
+ int nchunks = cnt / CHUNK_SIZE;
+ off_t sent = 0;
+
+ _tarWriteHeader(tarfilename, NULL, statbuf, false);
+
+ /* Send data in chunks of size CHUNK_SIZE each. */
+ for (i = 0; i < nchunks; i++)
+ {
+ /* Send the chunk as a CopyData message */
+ if (pq_putmessage('d', buf + sent, CHUNK_SIZE))
+ ereport(ERROR,
+ (errmsg("base backup could not send data, aborting backup")));
+
+ throttle(CHUNK_SIZE);
+ sent += CHUNK_SIZE;
+ }
+
+ /* Send remaining data, if present. */
+ if (sent < cnt)
+ {
+ off_t remaining = cnt - sent;
+
+ /* Send the chunk as a CopyData message */
+ if (pq_putmessage('d', buf + sent, remaining))
+ ereport(ERROR,
+ (errmsg("base backup could not send data, aborting backup")));
+
+ throttle(remaining);
+ sent += remaining;
+ }
+
+ free(buf);
+ Assert(sent = cnt);
+
+ return cnt;
+ }
+
+ /* Create a partial file */
+
+ /* Calculate partial file size. */
+ part_header_size = offsetof(partial_file_header, blocknumbers) +
+ (sizeof(uint32) * nmodblocks);
+ part_size = part_header_size + (BLCKSZ * nmodblocks);
+
+ /* Add .partial to filename */
+ partialtarfilename = (char *) palloc(strlen(tarfilename) + 9);
+ snprintf(partialtarfilename, strlen(tarfilename) + 9, "%s.partial", tarfilename);
+
+ statbuf->st_size = part_size;
+ _tarWriteHeader(partialtarfilename, NULL, statbuf, false);
+ pfree(partialtarfilename);
+
+ pfh = (partial_file_header *) palloc(part_header_size);
+ pfh->magic = INCREMENTAL_BACKUP_MAGIC;
+ pfh->nblocks = nmodblocks;
+
+ blknum = -1;
+ blknocnt = 0;
+ while ((blknum = bms_next_member(mod_blocks, blknum)) >= 0)
+ {
+ pfh->blocknumbers[blknocnt] = blknum;
+ /* Calculate CRC for each block to be transferred. */
+ blknocnt++;
+ }
+
+ Assert(blknocnt == nmodblocks);
+
+ /* Now calculate CRC for the header */
+ INIT_CRC32C(pfh->checksum);
+ COMP_CRC32C(pfh->checksum, pfh, part_header_size);
+
+ /* Send header */
+ if (pq_putmessage('d', (char *) pfh, part_header_size))
+ ereport(ERROR,
+ (errmsg("base backup could not send data, aborting backup")));
+ throttle(part_header_size);
+
+ /* Send data blocks */
+ for (blknocnt = 0; blknocnt < nmodblocks; blknocnt++)
+ {
+ int offset = BLCKSZ * pfh->blocknumbers[blknocnt];
+
+ if (pq_putmessage('d', buf + offset, BLCKSZ))
+ ereport(ERROR,
+ (errmsg("base backup could not send data, aborting backup")));
+ throttle(BLCKSZ);
+ }
+
+ Assert(blknocnt == nmodblocks && statbuf->st_size == part_size);
+
+ len = part_size;
+ pfree(pfh);
+ }
+ else
+ {
+ /* Check for fread() error. */
+ if (ferror(fp))
+ {
+ free(buf);
+ ereport(ERROR,
+ (errmsg("could not read from file \"%s\"", readfilename)));
+ }
+
+ /* Send empty file as is */
+ _tarWriteHeader(tarfilename, NULL, statbuf, false);
+ len = cnt;
+ }
+
+ /* free buffer allocated */
+ free(buf);
+
+ return len;
+}
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index 2de2105..3e07d52 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -3111,6 +3111,35 @@ looks_like_temp_rel_name(const char *name)
return true;
}
+/* <digits>, or <digits>.<digits> */
+bool
+looks_like_non_temp_rel_name(const char *name)
+{
+ int pos;
+
+ /* Look for a non-empty string of digits (that isn't too long). */
+ for (pos = 0; isdigit((unsigned char) name[pos]); ++pos)
+ ;
+ if (pos == 0 || pos > OIDCHARS)
+ return false;
+
+ if (name[pos] == '.')
+ {
+ int segchar;
+
+ for (segchar = 1; isdigit((unsigned char) name[pos + segchar]); ++segchar)
+ ;
+ if (segchar <= 1)
+ return false;
+ pos += segchar;
+ }
+
+ /* Now we should be at the end. */
+ if (name[pos] != '\0')
+ return false;
+ return true;
+}
+
/*
* Issue fsync recursively on PGDATA and all its contents.
diff --git a/src/bin/pg_basebackup/t/010_pg_basebackup.pl b/src/bin/pg_basebackup/t/010_pg_basebackup.pl
old mode 100644
new mode 100755
index fd8e187..a425f14
--- a/src/bin/pg_basebackup/t/010_pg_basebackup.pl
+++ b/src/bin/pg_basebackup/t/010_pg_basebackup.pl
@@ -561,7 +561,7 @@ $node->command_fails(
[ 'pg_basebackup', '-D', "$tempdir/lsn_test", '--lsn', "0/INVALID" ],
'pg_basebackup with invalid LSN fails');
$node->command_ok(
- [ 'pg_basebackup', '-D', "$tempdir/lsn_test", '--lsn', "0/ABCDEF01", '--no-verify-checksums' ],
+ [ 'pg_basebackup', '-D', "$tempdir/lsn_test", '--lsn', "0/00000001", '--no-verify-checksums' ],
'pg_basebackup with valid LSN');
rmtree("$tempdir/lsn_test");
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index d519252..155385d 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -347,7 +347,8 @@ typedef enum SessionBackupState
extern XLogRecPtr do_pg_start_backup(const char *backupidstr, bool fast,
TimeLineID *starttli_p, StringInfo labelfile,
List **tablespaces, StringInfo tblspcmapfile, bool infotbssize,
- bool needtblspcmapfile);
+ bool needtblspcmapfile,
+ XLogRecPtr ref_lsn);
extern XLogRecPtr do_pg_stop_backup(char *labelfile, bool waitforarchive,
TimeLineID *stoptli_p);
extern void do_pg_abort_backup(void);
diff --git a/src/include/replication/basebackup.h b/src/include/replication/basebackup.h
index 503a5b9..1b35b08 100644
--- a/src/include/replication/basebackup.h
+++ b/src/include/replication/basebackup.h
@@ -20,6 +20,9 @@
#define MAX_RATE_LOWER 32
#define MAX_RATE_UPPER 1048576
+/* magic number in incremental backup's .partial file */
+#define INCREMENTAL_BACKUP_MAGIC 0x494E4352
+
typedef struct
{
@@ -29,6 +32,16 @@ typedef struct
int64 size;
} tablespaceinfo;
+/* Definition of the partial file header */
+typedef struct
+{
+ uint32 magic;
+ pg_crc32c checksum;
+ uint32 nblocks;
+ uint32 blocknumbers[FLEXIBLE_ARRAY_MEMBER];
+} partial_file_header;
+
+
extern void SendBaseBackup(BaseBackupCmd *cmd);
extern int64 sendTablespace(char *path, bool sizeonly);
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
index d2a8c52..070faf1 100644
--- a/src/include/storage/fd.h
+++ b/src/include/storage/fd.h
@@ -136,6 +136,7 @@ extern void AtEOSubXact_Files(bool isCommit, SubTransactionId mySubid,
SubTransactionId parentSubid);
extern void RemovePgTempFiles(void);
extern bool looks_like_temp_rel_name(const char *name);
+extern bool looks_like_non_temp_rel_name(const char *name);
extern int pg_fsync(int fd);
extern int pg_fsync_no_writethrough(int fd);
--
1.8.3.1