aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bin/pg_combinebackup/meson.build1
-rw-r--r--src/bin/pg_combinebackup/reconstruct.c8
-rw-r--r--src/bin/pg_combinebackup/t/009_no_full_file.pl66
3 files changed, 75 insertions, 0 deletions
diff --git a/src/bin/pg_combinebackup/meson.build b/src/bin/pg_combinebackup/meson.build
index d142608e949..550d3503269 100644
--- a/src/bin/pg_combinebackup/meson.build
+++ b/src/bin/pg_combinebackup/meson.build
@@ -36,6 +36,7 @@ tests += {
't/006_db_file_copy.pl',
't/007_wal_level_minimal.pl',
't/008_promote.pl',
+ 't/009_no_full_file.pl',
],
}
}
diff --git a/src/bin/pg_combinebackup/reconstruct.c b/src/bin/pg_combinebackup/reconstruct.c
index ae8a5125263..37ae38b6108 100644
--- a/src/bin/pg_combinebackup/reconstruct.c
+++ b/src/bin/pg_combinebackup/reconstruct.c
@@ -326,11 +326,19 @@ reconstruct_from_incremental_file(char *input_filename,
* result, then forget about performing reconstruction and just copy that
* file in its entirety.
*
+ * If we have only incremental files, and there's no full file at any
+ * point in the backup chain, something has gone wrong. Emit an error.
+ *
* Otherwise, reconstruct.
*/
if (copy_source != NULL)
copy_file(copy_source->filename, output_filename,
&checksum_ctx, copy_method, dry_run);
+ else if (sidx == 0 && source[0]->header_length != 0)
+ {
+ pg_fatal("full backup contains unexpected incremental file \"%s\"",
+ source[0]->filename);
+ }
else
{
write_reconstructed_file(input_filename, output_filename,
diff --git a/src/bin/pg_combinebackup/t/009_no_full_file.pl b/src/bin/pg_combinebackup/t/009_no_full_file.pl
new file mode 100644
index 00000000000..8a23fa7a4cf
--- /dev/null
+++ b/src/bin/pg_combinebackup/t/009_no_full_file.pl
@@ -0,0 +1,66 @@
+# Copyright (c) 2021-2024, PostgreSQL Global Development Group
+
+use strict;
+use warnings FATAL => 'all';
+use File::Copy;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Can be changed to test the other modes.
+my $mode = $ENV{PG_TEST_PG_COMBINEBACKUP_MODE} || '--copy';
+
+note "testing using mode $mode";
+
+# Set up a new database instance.
+my $primary = PostgreSQL::Test::Cluster->new('primary');
+$primary->init(has_archiving => 1, allows_streaming => 1);
+$primary->append_conf('postgresql.conf', 'summarize_wal = on');
+$primary->start;
+
+# Take a full backup.
+my $backup1path = $primary->backup_dir . '/backup1';
+$primary->command_ok(
+ [ 'pg_basebackup', '-D', $backup1path, '--no-sync', '-cfast' ],
+ "full backup");
+
+# Take an incremental backup.
+my $backup2path = $primary->backup_dir . '/backup2';
+$primary->command_ok(
+ [
+ 'pg_basebackup', '-D', $backup2path, '--no-sync', '-cfast',
+ '--incremental', $backup1path . '/backup_manifest'
+ ],
+ "incremental backup");
+
+# Find an incremental file in the incremental backup for which there is a full
+# file in the full backup. When we find one, replace the full file with an
+# incremental file.
+my @filelist = grep { /^INCREMENTAL\./ } slurp_dir("$backup2path/base/1");
+my $success = 0;
+for my $iname (@filelist)
+{
+ my $name = $iname;
+ $name =~ s/^INCREMENTAL.//;
+
+ if (-f "$backup1path/base/1/$name")
+ {
+ copy("$backup2path/base/1/$iname", "$backup1path/base/1/$iname")
+ || die "copy $backup2path/base/1/$iname: $!";
+ unlink("$backup1path/base/1/$name")
+ || die "unlink $backup1path/base/1/$name: $!";
+ $success = 1;
+ last;
+ }
+}
+
+# pg_combinebackup should fail.
+my $outpath = $primary->backup_dir . '/out';
+$primary->command_fails_like(
+ [
+ 'pg_combinebackup', $backup1path, $backup2path, '-o', $outpath,
+ ],
+ qr/full backup contains unexpected incremental file/,
+ "pg_combinebackup fails");
+
+done_testing();