On 6/21/24 00:07, Tomas Vondra wrote:
> Here's a fix adding the missing headers to pg_combinebackup, and fixing
> some compile-time issues in the ifdef-ed block.
> 
> I've done some basic manual testing today - I plan to test this a bit
> more tomorrow, and I'll also look at integrating this into the existing
> tests.
> 

Here's a bit more complete / cleaned patch, adding the testing changes
in separate parts.

0001 adds the missing headers / fixes the now-accessible code a bit

0002 adds the --copy option for consistency with pg_upgrade

0003 adds the PG_TEST_PG_COMBINEBACKUP_MODE, so that we can override the
copy method for tests

0004 tweaks two of the Cirrus CI tasks to use --clone/--copy-file-range


I believe 0001-0003 are likely non-controversial, although if someone
could take a look at the Perl in 0003 that'd be nice. Also, 0002 seems
nice not only because of consistency with pg_upgrade, but it also makes
0003 easier as we don't need to special-case the default mode etc.

I'm not sure about 0004 - I initially did this mostly to check we have
the right headers on other platforms, but not sure we want to actually
do this. Or maybe we want to test a different combination (e.g. also
test the --clone on Linux)?


regards

-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
From 94082624db5d7608f3fb7a5b9322ee90dc5cfcb5 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <to...@2ndquadrant.com>
Date: Thu, 20 Jun 2024 23:50:22 +0200
Subject: [PATCH 1/4] Add headers needed by pg_combinebackup --clone

The code for cloning files existed, but was inaccessible as it relied on
constants from missing headers. The consequence is that on Linux --clone
always failed with

    error: file cloning not supported on this platform

Fixed by including the missing headers to relevant places. This revealed
a couple compile errors in copy_file_clone(), so fix those too.

Reported-by: Peter Eisentraut
Discussion: https://postgr.es/m/48da4a1f-ccd9-4988-9622-24f37b1de2b4%40eisentraut.org
---
 src/bin/pg_combinebackup/copy_file.c        | 12 +++++++++++-
 src/bin/pg_combinebackup/pg_combinebackup.c |  8 ++++++++
 2 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/src/bin/pg_combinebackup/copy_file.c b/src/bin/pg_combinebackup/copy_file.c
index 08c3b875420..8b50e937346 100644
--- a/src/bin/pg_combinebackup/copy_file.c
+++ b/src/bin/pg_combinebackup/copy_file.c
@@ -13,6 +13,10 @@
 #ifdef HAVE_COPYFILE_H
 #include <copyfile.h>
 #endif
+#ifdef __linux__
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#endif
 #include <fcntl.h>
 #include <limits.h>
 #include <sys/stat.h>
@@ -214,6 +218,9 @@ copy_file_clone(const char *src, const char *dest,
 		pg_fatal("error while cloning file \"%s\" to \"%s\": %m", src, dest);
 #elif defined(__linux__) && defined(FICLONE)
 	{
+		int			src_fd;
+		int			dest_fd;
+
 		if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
 			pg_fatal("could not open file \"%s\": %m", src);
 
@@ -228,8 +235,11 @@ copy_file_clone(const char *src, const char *dest,
 			unlink(dest);
 
 			pg_fatal("error while cloning file \"%s\" to \"%s\": %s",
-					 src, dest);
+					 src, dest, strerror(save_errno));
 		}
+
+		close(src_fd);
+		close(dest_fd);
 	}
 #else
 	pg_fatal("file cloning not supported on this platform");
diff --git a/src/bin/pg_combinebackup/pg_combinebackup.c b/src/bin/pg_combinebackup/pg_combinebackup.c
index 01d7fb98e79..f67ccf76ea2 100644
--- a/src/bin/pg_combinebackup/pg_combinebackup.c
+++ b/src/bin/pg_combinebackup/pg_combinebackup.c
@@ -16,6 +16,14 @@
 #include <fcntl.h>
 #include <limits.h>
 
+#ifdef HAVE_COPYFILE_H
+#include <copyfile.h>
+#endif
+#ifdef __linux__
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#endif
+
 #include "backup_label.h"
 #include "common/blkreftable.h"
 #include "common/checksum_helper.h"
-- 
2.45.2

From 93ecbe611936279127cebc5ddfefb6ebf1f4730f Mon Sep 17 00:00:00 2001
From: Tomas Vondra <to...@2ndquadrant.com>
Date: Fri, 21 Jun 2024 14:36:04 +0200
Subject: [PATCH 2/4] Introduce pg_combinebackup --copy option

The --copy option simply allows picking the default mode to copy data,
and does not change the behavior. This makes pg_combinebackup options
more consistent with pg_upgrade.

Reported-by: Peter Eisentraut
Discussion: https://postgr.es/m/48da4a1f-ccd9-4988-9622-24f37b1de2b4%40eisentraut.org
---
 doc/src/sgml/ref/pg_combinebackup.sgml      | 10 ++++++++++
 src/bin/pg_combinebackup/pg_combinebackup.c |  7 ++++++-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/ref/pg_combinebackup.sgml b/doc/src/sgml/ref/pg_combinebackup.sgml
index 375307d57bd..091982f62ad 100644
--- a/doc/src/sgml/ref/pg_combinebackup.sgml
+++ b/doc/src/sgml/ref/pg_combinebackup.sgml
@@ -162,6 +162,16 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--copy</option></term>
+      <listitem>
+       <para>
+        Perform regular file copy.  This is the default.  (See also
+        <option>--copy-file-range</option> and <option>--clone</option>.)
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--copy-file-range</option></term>
       <listitem>
diff --git a/src/bin/pg_combinebackup/pg_combinebackup.c b/src/bin/pg_combinebackup/pg_combinebackup.c
index f67ccf76ea2..6ddf5b534b1 100644
--- a/src/bin/pg_combinebackup/pg_combinebackup.c
+++ b/src/bin/pg_combinebackup/pg_combinebackup.c
@@ -139,7 +139,8 @@ main(int argc, char *argv[])
 		{"no-manifest", no_argument, NULL, 2},
 		{"sync-method", required_argument, NULL, 3},
 		{"clone", no_argument, NULL, 4},
-		{"copy-file-range", no_argument, NULL, 5},
+		{"copy", no_argument, NULL, 5},
+		{"copy-file-range", no_argument, NULL, 6},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -209,6 +210,9 @@ main(int argc, char *argv[])
 				opt.copy_method = COPY_METHOD_CLONE;
 				break;
 			case 5:
+				opt.copy_method = COPY_METHOD_COPY;
+				break;
+			case 6:
 				opt.copy_method = COPY_METHOD_COPY_FILE_RANGE;
 				break;
 			default:
@@ -763,6 +767,7 @@ help(const char *progname)
 	printf(_("  -T, --tablespace-mapping=OLDDIR=NEWDIR\n"
 			 "                            relocate tablespace in OLDDIR to NEWDIR\n"));
 	printf(_("      --clone               clone (reflink) instead of copying files\n"));
+	printf(_("      --copy                copy files (default)\n"));
 	printf(_("      --copy-file-range     copy using copy_file_range() syscall\n"));
 	printf(_("      --manifest-checksums=SHA{224,256,384,512}|CRC32C|NONE\n"
 			 "                            use algorithm for manifest checksums\n"));
-- 
2.45.2

From 0b3a9afe25d8837c0dcf5a03b856c8db1f4c26cd Mon Sep 17 00:00:00 2001
From: Tomas Vondra <to...@2ndquadrant.com>
Date: Fri, 21 Jun 2024 14:16:16 +0200
Subject: [PATCH 3/4] Add PG_TEST_PG_COMBINEBACKUP_MODE

This introduces an environment variable PG_TEST_PG_COMBINEBACKUP_MODE
that determines copy mode used by pg_combinebackup in TAP tests. This
defaults to "--copy" but may be set to "--clone" / "--copy-file-range"
to use the alternative stategies.

Reported-by: Peter Eisentraut
Discussion: https://postgr.es/m/48da4a1f-ccd9-4988-9622-24f37b1de2b4%40eisentraut.org
---
 .../pg_combinebackup/t/002_compare_backups.pl |  8 +++++-
 src/bin/pg_combinebackup/t/003_timeline.pl    |  8 +++++-
 src/bin/pg_combinebackup/t/004_manifest.pl    |  9 +++++--
 src/bin/pg_combinebackup/t/005_integrity.pl   | 25 +++++++++++--------
 .../pg_combinebackup/t/006_db_file_copy.pl    |  8 +++++-
 src/test/perl/PostgreSQL/Test/Cluster.pm      |  5 ++++
 6 files changed, 48 insertions(+), 15 deletions(-)

diff --git a/src/bin/pg_combinebackup/t/002_compare_backups.pl b/src/bin/pg_combinebackup/t/002_compare_backups.pl
index f032959ef5c..63a0255de15 100644
--- a/src/bin/pg_combinebackup/t/002_compare_backups.pl
+++ b/src/bin/pg_combinebackup/t/002_compare_backups.pl
@@ -9,6 +9,11 @@ use Test::More;
 
 my $tempdir = PostgreSQL::Test::Utils::tempdir_short();
 
+# 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);
@@ -134,7 +139,8 @@ $pitr2->init_from_backup(
 	standby => 1,
 	has_restoring => 1,
 	combine_with_prior => ['backup1'],
-	tablespace_map => { $tsbackup2path => $tspitr2path });
+	tablespace_map => { $tsbackup2path => $tspitr2path },
+	combine_mode => $mode);
 $pitr2->append_conf(
 	'postgresql.conf', qq{
 recovery_target_lsn = '$lsn'
diff --git a/src/bin/pg_combinebackup/t/003_timeline.pl b/src/bin/pg_combinebackup/t/003_timeline.pl
index 52eb642a392..83ab674a244 100644
--- a/src/bin/pg_combinebackup/t/003_timeline.pl
+++ b/src/bin/pg_combinebackup/t/003_timeline.pl
@@ -10,6 +10,11 @@ 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 $node1 = PostgreSQL::Test::Cluster->new('node1');
 $node1->init(has_archiving => 1, allows_streaming => 1);
@@ -68,7 +73,8 @@ $node2->command_ok(
 # Restore the incremental backup and use it to create a new node.
 my $node3 = PostgreSQL::Test::Cluster->new('node3');
 $node3->init_from_backup($node1, 'backup3',
-	combine_with_prior => [ 'backup1', 'backup2' ]);
+	combine_with_prior => [ 'backup1', 'backup2' ],
+	combine_mode => $mode);
 $node3->start();
 
 # Let's insert one more row.
diff --git a/src/bin/pg_combinebackup/t/004_manifest.pl b/src/bin/pg_combinebackup/t/004_manifest.pl
index 0df9fed73a8..6d475163ab9 100644
--- a/src/bin/pg_combinebackup/t/004_manifest.pl
+++ b/src/bin/pg_combinebackup/t/004_manifest.pl
@@ -12,6 +12,11 @@ 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 $node = PostgreSQL::Test::Cluster->new('node');
 $node->init(has_archiving => 1, allows_streaming => 1);
@@ -53,9 +58,9 @@ sub combine_and_test_one_backup
 combine_and_test_one_backup('nomanifest',
 	qr/could not open file.*backup_manifest/,
 	'--no-manifest');
-combine_and_test_one_backup('csum_none', undef, '--manifest-checksums=NONE');
+combine_and_test_one_backup('csum_none', undef, '--manifest-checksums=NONE', $mode);
 combine_and_test_one_backup('csum_sha224',
-	undef, '--manifest-checksums=SHA224');
+	undef, '--manifest-checksums=SHA224', $mode);
 
 # Verify that SHA224 is mentioned in the SHA224 manifest lots of times.
 my $sha224_manifest =
diff --git a/src/bin/pg_combinebackup/t/005_integrity.pl b/src/bin/pg_combinebackup/t/005_integrity.pl
index 636c3cc1b14..3caed13f6ed 100644
--- a/src/bin/pg_combinebackup/t/005_integrity.pl
+++ b/src/bin/pg_combinebackup/t/005_integrity.pl
@@ -13,6 +13,11 @@ 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 $node1 = PostgreSQL::Test::Cluster->new('node1');
 $node1->init(has_archiving => 1, allows_streaming => 1);
@@ -79,13 +84,13 @@ my $resultpath = $node1->backup_dir . '/result';
 
 # Can't combine 2 full backups.
 $node1->command_fails_like(
-	[ 'pg_combinebackup', $backup1path, $backup1path, '-o', $resultpath ],
+	[ 'pg_combinebackup', $backup1path, $backup1path, '-o', $resultpath, $mode ],
 	qr/is a full backup, but only the first backup should be a full backup/,
 	"can't combine full backups");
 
 # Can't combine 2 incremental backups.
 $node1->command_fails_like(
-	[ 'pg_combinebackup', $backup2path, $backup2path, '-o', $resultpath ],
+	[ 'pg_combinebackup', $backup2path, $backup2path, '-o', $resultpath, $mode ],
 	qr/is an incremental backup, but the first backup should be a full backup/,
 	"can't combine full backups");
 
@@ -93,7 +98,7 @@ $node1->command_fails_like(
 $node1->command_fails_like(
 	[
 		'pg_combinebackup', $backup1path, $backupother2path, '-o',
-		$resultpath
+		$resultpath, $mode
 	],
 	qr/expected system identifier.*but found/,
 	"can't combine backups from different nodes");
@@ -106,7 +111,7 @@ copy("$backupother2path/backup_manifest", "$backup2path/backup_manifest")
 $node1->command_fails_like(
 	[
 		'pg_combinebackup', $backup1path, $backup2path, $backup3path,
-		'-o', $resultpath
+		'-o', $resultpath, $mode
 	],
 	qr/ manifest system identifier is .*, but control file has /,
 	"can't combine backups with different manifest system identifier ");
@@ -116,7 +121,7 @@ move("$backup2path/backup_manifest.orig", "$backup2path/backup_manifest")
 
 # Can't omit a required backup.
 $node1->command_fails_like(
-	[ 'pg_combinebackup', $backup1path, $backup3path, '-o', $resultpath ],
+	[ 'pg_combinebackup', $backup1path, $backup3path, '-o', $resultpath, $mode ],
 	qr/starts at LSN.*but expected/,
 	"can't omit a required backup");
 
@@ -124,7 +129,7 @@ $node1->command_fails_like(
 $node1->command_fails_like(
 	[
 		'pg_combinebackup', $backup1path, $backup3path, $backup2path,
-		'-o', $resultpath
+		'-o', $resultpath, $mode
 	],
 	qr/starts at LSN.*but expected/,
 	"can't combine backups in the wrong order");
@@ -133,7 +138,7 @@ $node1->command_fails_like(
 $node1->command_ok(
 	[
 		'pg_combinebackup', $backup1path, $backup2path, $backup3path,
-		'-o', $resultpath
+		'-o', $resultpath, $mode
 	],
 	"can combine 3 matching backups");
 rmtree($resultpath);
@@ -143,19 +148,19 @@ my $synthetic12path = $node1->backup_dir . '/synthetic12';
 $node1->command_ok(
 	[
 		'pg_combinebackup', $backup1path, $backup2path, '-o',
-		$synthetic12path
+		$synthetic12path, $mode
 	],
 	"can combine 2 matching backups");
 
 # Can combine result of previous step with second incremental.
 $node1->command_ok(
-	[ 'pg_combinebackup', $synthetic12path, $backup3path, '-o', $resultpath ],
+	[ 'pg_combinebackup', $synthetic12path, $backup3path, '-o', $resultpath, $mode ],
 	"can combine synthetic backup with later incremental");
 rmtree($resultpath);
 
 # Can't combine result of 1+2 with 2.
 $node1->command_fails_like(
-	[ 'pg_combinebackup', $synthetic12path, $backup2path, '-o', $resultpath ],
+	[ 'pg_combinebackup', $synthetic12path, $backup2path, '-o', $resultpath, $mode ],
 	qr/starts at LSN.*but expected/,
 	"can't combine synthetic backup with included incremental");
 
diff --git a/src/bin/pg_combinebackup/t/006_db_file_copy.pl b/src/bin/pg_combinebackup/t/006_db_file_copy.pl
index d57b550af21..f44788e82bf 100644
--- a/src/bin/pg_combinebackup/t/006_db_file_copy.pl
+++ b/src/bin/pg_combinebackup/t/006_db_file_copy.pl
@@ -7,6 +7,11 @@ 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);
@@ -45,7 +50,8 @@ $primary->command_ok(
 # Recover the incremental backup.
 my $restore = PostgreSQL::Test::Cluster->new('restore');
 $restore->init_from_backup($primary, 'backup2',
-	combine_with_prior => ['backup1']);
+	combine_with_prior => ['backup1'],
+	combine_mode => $mode);
 $restore->start();
 
 # Query the DB.
diff --git a/src/test/perl/PostgreSQL/Test/Cluster.pm b/src/test/perl/PostgreSQL/Test/Cluster.pm
index 83f385a4870..0135c5a795c 100644
--- a/src/test/perl/PostgreSQL/Test/Cluster.pm
+++ b/src/test/perl/PostgreSQL/Test/Cluster.pm
@@ -856,6 +856,11 @@ sub init_from_backup
 				push @combineargs, "-T$olddir=$newdir";
 			}
 		}
+		# use the combine mode (clone/copy-file-range) if specified
+		if (defined $params{combine_mode})
+		{
+			push @combineargs, $params{combine_mode};
+		}
 		push @combineargs, @prior_backup_path, $backup_path, '-o', $data_path;
 		PostgreSQL::Test::Utils::system_or_bail(@combineargs);
 	}
-- 
2.45.2

From 836e20a0ed34b7a9c367674e771fa8cb023e4341 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <to...@2ndquadrant.com>
Date: Fri, 21 Jun 2024 17:55:38 +0200
Subject: [PATCH 4/4] Add PG_TEST_PG_COMBINEBACKUP_MODE to CI tasks

This changes pg_combinebackup mode for two of the Cirrus CI tasks, to
exercise the alternative copy methods in pg_combinebackup tests. We
test --clone on macOS and --copy-file-range on one of the Linux tasks.

Reported-by: Peter Eisentraut
Discussion: https://postgr.es/m/48da4a1f-ccd9-4988-9622-24f37b1de2b4%40eisentraut.org
---
 .cirrus.tasks.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.cirrus.tasks.yml b/.cirrus.tasks.yml
index 33646faeadf..4e029103c7e 100644
--- a/.cirrus.tasks.yml
+++ b/.cirrus.tasks.yml
@@ -318,6 +318,7 @@ task:
 
       env:
         SANITIZER_FLAGS: -fsanitize=address
+        PG_TEST_PG_COMBINEBACKUP_MODE: --copy-file-range
 
       # Normally, the "relation segment" code basically has no coverage in our
       # tests, because we (quite reasonably) don't generate tables large
@@ -432,6 +433,7 @@ task:
     CXXFLAGS: -Og -ggdb
 
     PG_TEST_PG_UPGRADE_MODE: --clone
+    PG_TEST_PG_COMBINEBACKUP_MODE: --clone
 
   <<: *macos_task_template
 
-- 
2.45.2

Reply via email to