postgresql/src/bin/pg_upgrade/t/002_pg_upgrade.pl

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

238 lines
7.6 KiB
Perl
Raw Normal View History

Switch the regression tests of pg_upgrade to use TAP tests This simplifies a lot of code in the tests of pg_upgrade without sacrificing its coverage: - Removal of test.sh used for builds with make, that has accumulated over the years tweaks for problems that are solved in a duplicated way by the centralized TAP framework (initialization of the various environment variables PG*, port selection). - Removal of the code in MSVC to test pg_upgrade. This was roughly a duplicate of test.sh adapted for Windows, with an extra footprint of a pg_regress command and all the assumptions behind it. Support for upgrades with older versions is changed, not removed. test.sh was able to set up the regression database on the old instance by launching itself the pg_regress command and a dependency to the source tree of thd old cluster, with tweaks on the command arguments to adapt across the versions used. This created a backward-compatibility dependency with older pg_regress commands, and recent changes like d1029bb have made that much more complicated. Instead, this commit allows tests with older major versions by specifying a path to a SQL dump (taken with pg_dumpall from the old cluster's installation) that will be loaded into the old instance to upgrade instead of running pg_regress, through an optional environment variable called $olddump. This requires a second variable called $oldinstall to point to the base path of the installation of the old cluster. This method is more in line with the buildfarm client that uses a set of static dumps to set up an old instance, so hopefully we will be able to reuse what is introduced in this commit there. The last step of the tests that checks for differences between the two dumps taken still needs to be improved as it can fail, requiring a manual lookup at the dumps. This is not different from the old way of testing where things could fail at the last step. Support for EXTRA_REGRESS_OPTS is kept. vcregress.pl in the MSVC scripts still handles the test of pg_upgrade with its upgradecheck, and bincheck is changed to skip pg_upgrade. Author: Michael Paquier Reviewed-by: Andrew Dunstan, Andres Freund, Rachel Heaton, Tom Lane, Discussion: https://postgr.es/m/YJ8xTmLQkotVLpN5@paquier.xyz
2022-04-01 03:13:50 +02:00
# Set of tests for pg_upgrade, including cross-version checks.
use strict;
use warnings;
use Cwd qw(abs_path getcwd);
use File::Basename qw(dirname);
use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
# Generate a database with a name made of a range of ASCII characters.
sub generate_db
{
my ($node, $from_char, $to_char) = @_;
my $dbname = '';
for my $i ($from_char .. $to_char)
{
next if $i == 7 || $i == 10 || $i == 13; # skip BEL, LF, and CR
$dbname = $dbname . sprintf('%c', $i);
}
$node->run_log(
[ 'createdb', '--host', $node->host, '--port', $node->port, $dbname ]
);
}
# The test of pg_upgrade requires two clusters, an old one and a new one
# that gets upgraded. Before running the upgrade, a logical dump of the
# old cluster is taken, and a second logical dump of the new one is taken
# after the upgrade. The upgrade test passes if there are no differences
# in these two dumps.
# Testing upgrades with an older version of PostgreSQL requires setting up
# two environment variables, as of:
# - "olddump", to point to a dump file that will be used to set up the old
# instance to upgrade from.
# - "oldinstall", to point to the installation path of the old cluster.
if ( (defined($ENV{olddump}) && !defined($ENV{oldinstall}))
|| (!defined($ENV{olddump}) && defined($ENV{oldinstall})))
{
# Not all variables are defined, so leave and die if test is
# done with an older installation.
die "olddump or oldinstall is undefined";
}
# Temporary location for the dumps taken
my $tempdir = PostgreSQL::Test::Utils::tempdir;
# Initialize node to upgrade
my $oldnode = PostgreSQL::Test::Cluster->new('old_node',
install_path => $ENV{oldinstall});
# To increase coverage of non-standard segment size and group access without
# increasing test runtime, run these tests with a custom setting.
# --allow-group-access and --wal-segsize have been added in v11.
$oldnode->init(extra => [ '--wal-segsize', '1', '--allow-group-access' ]);
$oldnode->start;
# The default location of the source code is the root of this directory.
my $srcdir = abs_path("../../..");
# Set up the data of the old instance with a dump or pg_regress.
if (defined($ENV{olddump}))
{
# Use the dump specified.
my $olddumpfile = $ENV{olddump};
die "no dump file found!" unless -e $olddumpfile;
# Load the dump using the "postgres" database as "regression" does
# not exist yet, and we are done here.
$oldnode->command_ok(
[
'psql', '-X', '-f', $olddumpfile,
'--port', $oldnode->port, '--host', $oldnode->host,
'postgres'
]);
}
else
{
# Default is to use pg_regress to set up the old instance.
# Create databases with names covering most ASCII bytes
generate_db($oldnode, 1, 45);
generate_db($oldnode, 46, 90);
generate_db($oldnode, 91, 127);
# Grab any regression options that may be passed down by caller.
my $extra_opts_val = $ENV{EXTRA_REGRESS_OPT} || "";
my @extra_opts = split(/\s+/, $extra_opts_val);
# --dlpath is needed to be able to find the location of regress.so
# and any libraries the regression tests require.
my $dlpath = dirname($ENV{REGRESS_SHLIB});
# --outputdir points to the path where to place the output files.
my $outputdir = $PostgreSQL::Test::Utils::tmp_check;
# --inputdir points to the path of the input files.
my $inputdir = "$srcdir/src/test/regress";
my @regress_command = [
$ENV{PG_REGRESS}, @extra_opts,
'--dlpath', $dlpath,
'--max-concurrent-tests', '20',
'--bindir=', '--host',
$oldnode->host, '--port',
$oldnode->port, '--schedule',
"$srcdir/src/test/regress/parallel_schedule", '--outputdir',
$outputdir, '--inputdir',
$inputdir
];
$oldnode->command_ok(@regress_command,
'regression test run on old instance');
}
# Before dumping, get rid of objects not existing or not supported in later
# versions. This depends on the version of the old server used, and matters
# only if different major versions are used for the dump.
if (defined($ENV{oldinstall}))
{
# Note that upgrade_adapt.sql from the new version is used, to
# cope with an upgrade to this version.
$oldnode->run_log(
[
'psql', '-X',
'-f', "$srcdir/src/bin/pg_upgrade/upgrade_adapt.sql",
'--port', $oldnode->port,
'--host', $oldnode->host,
'regression'
]);
}
# Initialize a new node for the upgrade.
my $newnode = PostgreSQL::Test::Cluster->new('new_node');
$newnode->init(extra => [ '--wal-segsize', '1', '--allow-group-access' ]);
my $newbindir = $newnode->config_data('--bindir');
my $oldbindir = $oldnode->config_data('--bindir');
# Take a dump before performing the upgrade as a base comparison. Note
# that we need to use pg_dumpall from the new node here.
$newnode->command_ok(
[
'pg_dumpall', '--no-sync',
'-d', $oldnode->connstr('postgres'),
'-f', "$tempdir/dump1.sql"
],
'dump before running pg_upgrade');
# After dumping, update references to the old source tree's regress.so
# to point to the new tree.
if (defined($ENV{oldinstall}))
{
# First, fetch all the references to libraries that are not part
# of the default path $libdir.
my $output = $oldnode->safe_psql('regression',
"SELECT DISTINCT probin::text FROM pg_proc WHERE probin NOT LIKE '\$libdir%';"
);
chomp($output);
my @libpaths = split("\n", $output);
my $dump_data = slurp_file("$tempdir/dump1.sql");
my $newregresssrc = "$srcdir/src/test/regress";
foreach (@libpaths)
{
my $libpath = $_;
$libpath = dirname($libpath);
$dump_data =~ s/$libpath/$newregresssrc/g;
}
open my $fh, ">", "$tempdir/dump1.sql" or die "could not open dump file";
print $fh $dump_data;
close $fh;
# This replaces any references to the old tree's regress.so
# the new tree's regress.so. Any references that do *not*
# match $libdir are switched so as this request does not
# depend on the path of the old source tree. This is useful
# when using an old dump. Do the operation on all the databases
# that allow connections so as this includes the regression
# database and anything the user has set up.
$output = $oldnode->safe_psql('postgres',
"SELECT datname FROM pg_database WHERE datallowconn;");
chomp($output);
my @datnames = split("\n", $output);
foreach (@datnames)
{
my $datname = $_;
$oldnode->safe_psql(
$datname, "UPDATE pg_proc SET probin =
regexp_replace(probin, '.*/', '$newregresssrc/')
WHERE probin NOT LIKE '\$libdir/%'");
}
}
# Upgrade the instance.
$oldnode->stop;
command_ok(
[
'pg_upgrade', '--no-sync', '-d', $oldnode->data_dir,
'-D', $newnode->data_dir, '-b', $oldbindir,
'-B', $newbindir, '-p', $oldnode->port,
'-P', $newnode->port
],
'run of pg_upgrade for new instance');
$newnode->start;
# Check if there are any logs coming from pg_upgrade, that would only be
# retained on failure.
my $log_path = $newnode->data_dir . "/pg_upgrade_output.d/log";
if (-d $log_path)
{
foreach my $log (glob("$log_path/*"))
{
note "###########################";
note "Contents of log file $log";
note "###########################";
my $log_contents = slurp_file($log);
print "$log_contents\n";
}
}
# Second dump from the upgraded instance.
$newnode->run_log(
[
'pg_dumpall', '--no-sync',
'-d', $newnode->connstr('postgres'),
'-f', "$tempdir/dump2.sql"
]);
# Compare the two dumps, there should be no differences.
command_ok([ 'diff', '-q', "$tempdir/dump1.sql", "$tempdir/dump2.sql" ],
'old and new dump match after pg_upgrade');
done_testing();