From ee832b9bc7b240b279f0228e624356ca3fd63a27 Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig@2ndquadrant.com>
Date: Tue, 1 Mar 2016 21:21:25 +0800
Subject: [PATCH 4/7] TAP: Add support for taking filesystem level backups

---
 src/test/perl/PostgresNode.pm | 83 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 71 insertions(+), 12 deletions(-)

diff --git a/src/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm
index c18c94a..099bb89 100644
--- a/src/test/perl/PostgresNode.pm
+++ b/src/test/perl/PostgresNode.pm
@@ -463,11 +463,81 @@ sub backup
 	my $port        = $self->port;
 	my $name        = $self->name;
 
-	print "# Taking backup $backup_name from node \"$name\"\n";
+	print "# Taking pg_basebackup $backup_name from node \"$name\"\n";
 	TestLib::system_or_bail("pg_basebackup -D $backup_path -p $port -x");
 	print "# Backup finished\n";
 }
 
+=item $node->backup_fs_hot(backup_name)
+
+Create a backup with a filesystem level copy in $node->backup_dir,
+including transaction logs. Archiving must be enabled as pg_start_backup
+and pg_stop_backup are used. This is not checked or enforced.
+
+The backup name is passed as the backup label to pg_start_backup.
+
+=cut
+
+sub backup_fs_hot
+{
+	my ($self, $backup_name) = @_;
+	$self->_backup_fs($backup_name, 1);
+}
+
+=item $node->backup_fs_cold(backup_name)
+
+Create a backup with a filesystem level copy in $node->backup dir,
+including transaction logs. The server must be stopped as no
+attempt to handle concurrent writes is made.
+
+Use backup or backup_fs_hot if you want to back up a running
+server.
+
+=cut
+
+sub backup_fs_cold
+{
+	my ($self, $backup_name) = @_;
+	$self->_backup_fs($backup_name, 0);
+}
+
+
+# Common sub of backup_fs_hot and backup_fs_cold
+sub _backup_fs
+{
+	my ($self, $backup_name, $hot) = @_;
+	my $backup_path = $self->backup_dir . '/' . $backup_name;
+	my $port        = $self->port;
+	my $name        = $self->name;
+
+	print "# Taking filesystem level backup $backup_name from node \"$name\"\n";
+
+	if ($hot) {
+		my $stdout = $self->psql_check('postgres', "SELECT * FROM pg_start_backup('$backup_name');");
+		print "# pg_start_backup: $stdout\n";
+	}
+
+	RecursiveCopy::copypath($self->data_dir, $backup_path,
+	  filterfn => sub {
+	    my $src = shift;
+	    return $src !~ /\/pg_log\// && $src !~ /\/postmaster.pid$/;
+	  }
+	);
+
+	if ($hot)
+	{
+		# We ignore pg_stop_backup's return value. We also assume archiving
+		# is enabled; otherwise the caller will have to copy the remaining
+		# segments.
+		my $stdout = $self->psql_check('postgres', 'SELECT * FROM pg_stop_backup();');
+		print "# pg_stop_backup: $stdout\n";
+	}
+
+	print "# Backup finished\n";
+}
+
+
+
 =pod
 
 =item $node->init_from_backup(root_node, backup_name)
@@ -917,17 +987,6 @@ Pass additional parameters to psql. Must be an arrayref.
 
 e.g.
 
-	my ($stdout, $stderr, $timed_out);
-	my $cmdret = $psql_expert('postgres', 'SELECT pg_sleep(60)',
-		stdout => \$stdout, stderr => \$stderr,
-		timeout => 30, timed_out => \$timed_out,
-		extra_params => ['--single-transaction'])
-
-will set $cmdret to undef and $timed_out to a true value.
-
-	$psql_expert('postgres', $sql, on_error_die => 1);
-
-dies with an informative message if $sql fails.
 
 =cut
 
-- 
2.1.0

