On 7/8/20 9:54 AM, Andrew Dunstan wrote: > > > > Then, with a little more sprinkling of perl2host the pg_basebackup tests > can be made to work on msys2. > > > I'm going to prepare patches along these lines. > >
After much frustration and gnashing of teeth here's a patch that allows almost all the TAP tests involving symlinks to work as expected on all Windows build environments, without requiring an additional Perl module. I have tested this on a system that is very similar to that running drongo and fairywren, with both msys2 and MSVC builds. I didn't change the name of perl2host - Sufficient unto the day is the evil thereof. But I did modify it a) to allow use of cygpath if available and b) to allow it to succeed if the grandparent directory exists when cygpath isn't available. cheers andrew -- Andrew Dunstan https://www.2ndQuadrant.com PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index d98187c970..e6e2b2762d 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -775,6 +775,7 @@ make check PROVE_TESTS='t/001_test1.pl t/003_test3.pl' <para> The TAP tests require the Perl module <literal>IPC::Run</literal>. This module is available from CPAN or an operating system package. + On Windows, <literal>Win32API::File</literal> is also required . </para> <para> diff --git a/src/bin/pg_basebackup/t/010_pg_basebackup.pl b/src/bin/pg_basebackup/t/010_pg_basebackup.pl index 208df557b8..38b2a35c3b 100644 --- a/src/bin/pg_basebackup/t/010_pg_basebackup.pl +++ b/src/bin/pg_basebackup/t/010_pg_basebackup.pl @@ -211,154 +211,168 @@ $node->command_fails( 'pg_basebackup tar with long name fails'); unlink "$pgdata/$superlongname"; -# The following tests test symlinks. Windows doesn't have symlinks, so -# skip on Windows. +# The following tests are for symlinks. + +# Move pg_replslot out of $pgdata and create a symlink to it. +$node->stop; + +# Set umask so test directories and files are created with group permissions +umask(0027); + +# Enable group permissions on PGDATA +chmod_recursive("$pgdata", 0750, 0640); + +rename("$pgdata/pg_replslot", "$tempdir/pg_replslot") + or BAIL_OUT "could not move $pgdata/pg_replslot"; +dir_symlink("$tempdir/pg_replslot", "$pgdata/pg_replslot") + or BAIL_OUT "could not symlink to $pgdata/pg_replslot"; + +$node->start; + +# Create a temporary directory in the system location and symlink it +# to our physical temp location. That way we can use shorter names +# for the tablespace directories, which hopefully won't run afoul of +# the 99 character length limit. +my $shorter_tempdir = TestLib::tempdir_short . "/tempdir"; +dir_symlink "$tempdir", $shorter_tempdir; + +mkdir "$tempdir/tblspc1"; +my $realTsDir = TestLib::perl2host("$shorter_tempdir/tblspc1"); +my $real_tempdir = TestLib::perl2host($tempdir); +$node->safe_psql('postgres', + "CREATE TABLESPACE tblspc1 LOCATION '$realTsDir';"); +$node->safe_psql('postgres', + "CREATE TABLE test1 (a int) TABLESPACE tblspc1;"); +$node->command_ok([ 'pg_basebackup', '-D', "$real_tempdir/tarbackup2", '-Ft' ], + 'tar format with tablespaces'); +ok(-f "$tempdir/tarbackup2/base.tar", 'backup tar was created'); +my @tblspc_tars = glob "$tempdir/tarbackup2/[0-9]*.tar"; +is(scalar(@tblspc_tars), 1, 'one tablespace tar was created'); +rmtree("$tempdir/tarbackup2"); + +# Create an unlogged table to test that forks other than init are not copied. +$node->safe_psql('postgres', + 'CREATE UNLOGGED TABLE tblspc1_unlogged (id int) TABLESPACE tblspc1;' + ); + +my $tblspc1UnloggedPath = $node->safe_psql('postgres', + q{select pg_relation_filepath('tblspc1_unlogged')}); + +# Make sure main and init forks exist +ok( -f "$pgdata/${tblspc1UnloggedPath}_init", + 'unlogged init fork in tablespace'); +ok(-f "$pgdata/$tblspc1UnloggedPath", 'unlogged main fork in tablespace'); + +# Create files that look like temporary relations to ensure they are ignored +# in a tablespace. +my @tempRelationFiles = qw(t888_888 t888888_888888_vm.1); +my $tblSpc1Id = basename( + dirname( + dirname( + $node->safe_psql( + 'postgres', q{select pg_relation_filepath('test1')})))); + +foreach my $filename (@tempRelationFiles) +{ + append_to_file( + "$shorter_tempdir/tblspc1/$tblSpc1Id/$postgresOid/$filename", + 'TEMP_RELATION'); +} + +$node->command_fails( + [ 'pg_basebackup', '-D', "$tempdir/backup1", '-Fp' ], + 'plain format with tablespaces fails without tablespace mapping'); + +$node->command_ok( + [ + 'pg_basebackup', '-D', "$tempdir/backup1", '-Fp', + "-T$realTsDir=$real_tempdir/tbackup/tblspc1" + ], + 'plain format with tablespaces succeeds with tablespace mapping'); +ok(-d "$tempdir/tbackup/tblspc1", 'tablespace was relocated'); + +# This symlink check is not supported on Windows as -l +# doesn't work with junctions SKIP: { - skip "symlinks not supported on Windows", 18 if ($windows_os); - - # Move pg_replslot out of $pgdata and create a symlink to it. - $node->stop; - - # Set umask so test directories and files are created with group permissions - umask(0027); - - # Enable group permissions on PGDATA - chmod_recursive("$pgdata", 0750, 0640); - - rename("$pgdata/pg_replslot", "$tempdir/pg_replslot") - or BAIL_OUT "could not move $pgdata/pg_replslot"; - symlink("$tempdir/pg_replslot", "$pgdata/pg_replslot") - or BAIL_OUT "could not symlink to $pgdata/pg_replslot"; - - $node->start; - - # Create a temporary directory in the system location and symlink it - # to our physical temp location. That way we can use shorter names - # for the tablespace directories, which hopefully won't run afoul of - # the 99 character length limit. - my $shorter_tempdir = TestLib::tempdir_short . "/tempdir"; - symlink "$tempdir", $shorter_tempdir; - - mkdir "$tempdir/tblspc1"; - $node->safe_psql('postgres', - "CREATE TABLESPACE tblspc1 LOCATION '$shorter_tempdir/tblspc1';"); - $node->safe_psql('postgres', - "CREATE TABLE test1 (a int) TABLESPACE tblspc1;"); - $node->command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup2", '-Ft' ], - 'tar format with tablespaces'); - ok(-f "$tempdir/tarbackup2/base.tar", 'backup tar was created'); - my @tblspc_tars = glob "$tempdir/tarbackup2/[0-9]*.tar"; - is(scalar(@tblspc_tars), 1, 'one tablespace tar was created'); - rmtree("$tempdir/tarbackup2"); - - # Create an unlogged table to test that forks other than init are not copied. - $node->safe_psql('postgres', - 'CREATE UNLOGGED TABLE tblspc1_unlogged (id int) TABLESPACE tblspc1;' - ); - - my $tblspc1UnloggedPath = $node->safe_psql('postgres', - q{select pg_relation_filepath('tblspc1_unlogged')}); - - # Make sure main and init forks exist - ok( -f "$pgdata/${tblspc1UnloggedPath}_init", - 'unlogged init fork in tablespace'); - ok(-f "$pgdata/$tblspc1UnloggedPath", 'unlogged main fork in tablespace'); - - # Create files that look like temporary relations to ensure they are ignored - # in a tablespace. - my @tempRelationFiles = qw(t888_888 t888888_888888_vm.1); - my $tblSpc1Id = basename( - dirname( - dirname( - $node->safe_psql( - 'postgres', q{select pg_relation_filepath('test1')})))); - - foreach my $filename (@tempRelationFiles) - { - append_to_file( - "$shorter_tempdir/tblspc1/$tblSpc1Id/$postgresOid/$filename", - 'TEMP_RELATION'); - } - - $node->command_fails( - [ 'pg_basebackup', '-D', "$tempdir/backup1", '-Fp' ], - 'plain format with tablespaces fails without tablespace mapping'); - - $node->command_ok( - [ - 'pg_basebackup', '-D', "$tempdir/backup1", '-Fp', - "-T$shorter_tempdir/tblspc1=$tempdir/tbackup/tblspc1" - ], - 'plain format with tablespaces succeeds with tablespace mapping'); - ok(-d "$tempdir/tbackup/tblspc1", 'tablespace was relocated'); + skip "symlink check not implemented on Windows", 1 + if ($windows_os); opendir(my $dh, "$pgdata/pg_tblspc") or die; ok( ( grep { - -l "$tempdir/backup1/pg_tblspc/$_" - and readlink "$tempdir/backup1/pg_tblspc/$_" eq - "$tempdir/tbackup/tblspc1" - } readdir($dh)), + -l "$tempdir/backup1/pg_tblspc/$_" + and readlink "$tempdir/backup1/pg_tblspc/$_" eq + "$tempdir/tbackup/tblspc1" + } readdir($dh)), "tablespace symlink was updated"); closedir $dh; +} + +# Group access should be enabled on all backup files +SKIP: +{ + skip "unix-style permissions not supported on Windows", 1 + if ($windows_os); - # Group access should be enabled on all backup files ok(check_mode_recursive("$tempdir/backup1", 0750, 0640), - "check backup dir permissions"); + "check backup dir permissions"); +} + +# Unlogged relation forks other than init should not be copied +my ($tblspc1UnloggedBackupPath) = + $tblspc1UnloggedPath =~ /[^\/]*\/[^\/]*\/[^\/]*$/g; + +ok(-f "$tempdir/tbackup/tblspc1/${tblspc1UnloggedBackupPath}_init", + 'unlogged init fork in tablespace backup'); +ok(!-f "$tempdir/tbackup/tblspc1/$tblspc1UnloggedBackupPath", + 'unlogged main fork not in tablespace backup'); - # Unlogged relation forks other than init should not be copied - my ($tblspc1UnloggedBackupPath) = - $tblspc1UnloggedPath =~ /[^\/]*\/[^\/]*\/[^\/]*$/g; - - ok(-f "$tempdir/tbackup/tblspc1/${tblspc1UnloggedBackupPath}_init", - 'unlogged init fork in tablespace backup'); - ok(!-f "$tempdir/tbackup/tblspc1/$tblspc1UnloggedBackupPath", - 'unlogged main fork not in tablespace backup'); - - # Temp relations should not be copied. - foreach my $filename (@tempRelationFiles) - { - ok( !-f "$tempdir/tbackup/tblspc1/$tblSpc1Id/$postgresOid/$filename", - "[tblspc1]/$postgresOid/$filename not copied"); - - # Also remove temp relation files or tablespace drop will fail. - my $filepath = - "$shorter_tempdir/tblspc1/$tblSpc1Id/$postgresOid/$filename"; - - unlink($filepath) - or BAIL_OUT("unable to unlink $filepath"); - } - - ok( -d "$tempdir/backup1/pg_replslot", - 'pg_replslot symlink copied as directory'); - rmtree("$tempdir/backup1"); - - mkdir "$tempdir/tbl=spc2"; - $node->safe_psql('postgres', "DROP TABLE test1;"); - $node->safe_psql('postgres', "DROP TABLE tblspc1_unlogged;"); - $node->safe_psql('postgres', "DROP TABLESPACE tblspc1;"); - $node->safe_psql('postgres', - "CREATE TABLESPACE tblspc2 LOCATION '$shorter_tempdir/tbl=spc2';"); - $node->command_ok( - [ - 'pg_basebackup', '-D', "$tempdir/backup3", '-Fp', - "-T$shorter_tempdir/tbl\\=spc2=$tempdir/tbackup/tbl\\=spc2" - ], - 'mapping tablespace with = sign in path'); - ok(-d "$tempdir/tbackup/tbl=spc2", - 'tablespace with = sign was relocated'); - $node->safe_psql('postgres', "DROP TABLESPACE tblspc2;"); - rmtree("$tempdir/backup3"); - - mkdir "$tempdir/$superlongname"; - $node->safe_psql('postgres', - "CREATE TABLESPACE tblspc3 LOCATION '$tempdir/$superlongname';"); - $node->command_ok( - [ 'pg_basebackup', '-D', "$tempdir/tarbackup_l3", '-Ft' ], - 'pg_basebackup tar with long symlink target'); - $node->safe_psql('postgres', "DROP TABLESPACE tblspc3;"); - rmtree("$tempdir/tarbackup_l3"); +# Temp relations should not be copied. +foreach my $filename (@tempRelationFiles) +{ + ok( !-f "$tempdir/tbackup/tblspc1/$tblSpc1Id/$postgresOid/$filename", + "[tblspc1]/$postgresOid/$filename not copied"); + + # Also remove temp relation files or tablespace drop will fail. + my $filepath = + "$shorter_tempdir/tblspc1/$tblSpc1Id/$postgresOid/$filename"; + + unlink($filepath) + or BAIL_OUT("unable to unlink $filepath"); } +ok( -d "$tempdir/backup1/pg_replslot", + 'pg_replslot symlink copied as directory'); +rmtree("$tempdir/backup1"); + +mkdir "$tempdir/tbl=spc2"; +$realTsDir = TestLib::perl2host("$shorter_tempdir/tbl=spc2"); +$node->safe_psql('postgres', "DROP TABLE test1;"); +$node->safe_psql('postgres', "DROP TABLE tblspc1_unlogged;"); +$node->safe_psql('postgres', "DROP TABLESPACE tblspc1;"); +$node->safe_psql('postgres', + "CREATE TABLESPACE tblspc2 LOCATION '$realTsDir';"); +$realTsDir =~ s/=/\\=/; +$node->command_ok( + [ + 'pg_basebackup', '-D', "$tempdir/backup3", '-Fp', + "-T$realTsDir=$real_tempdir/tbackup/tbl\\=spc2" + ], + 'mapping tablespace with = sign in path'); +ok(-d "$tempdir/tbackup/tbl=spc2", + 'tablespace with = sign was relocated'); +$node->safe_psql('postgres', "DROP TABLESPACE tblspc2;"); +rmtree("$tempdir/backup3"); + +mkdir "$tempdir/$superlongname"; +$realTsDir = TestLib::perl2host("$shorter_tempdir/$superlongname"); +$node->safe_psql('postgres', + "CREATE TABLESPACE tblspc3 LOCATION '$realTsDir';"); +$node->command_ok( + [ 'pg_basebackup', '-D', "$tempdir/tarbackup_l3", '-Ft' ], + 'pg_basebackup tar with long symlink target'); +$node->safe_psql('postgres', "DROP TABLESPACE tblspc3;"); +rmtree("$tempdir/tarbackup_l3"); + $node->command_ok([ 'pg_basebackup', '-D', "$tempdir/backupR", '-R' ], 'pg_basebackup -R runs'); ok(-f "$tempdir/backupR/postgresql.auto.conf", 'postgresql.auto.conf exists'); diff --git a/src/bin/pg_dump/t/010_dump_connstr.pl b/src/bin/pg_dump/t/010_dump_connstr.pl index abdb07c558..1fd8a78abc 100644 --- a/src/bin/pg_dump/t/010_dump_connstr.pl +++ b/src/bin/pg_dump/t/010_dump_connstr.pl @@ -5,7 +5,7 @@ use PostgresNode; use TestLib; use Test::More; -if ($^O eq 'msys' && `uname -or` =~ /^[2-9].*Msys/) +if ($TestLib::is_msys2) { plan skip_all => 'High bit name tests fail on Msys2'; } diff --git a/src/bin/pg_rewind/t/004_pg_xlog_symlink.pl b/src/bin/pg_rewind/t/004_pg_xlog_symlink.pl index 3813543ee1..fff4758508 100644 --- a/src/bin/pg_rewind/t/004_pg_xlog_symlink.pl +++ b/src/bin/pg_rewind/t/004_pg_xlog_symlink.pl @@ -6,16 +6,7 @@ use warnings; use File::Copy; use File::Path qw(rmtree); use TestLib; -use Test::More; -if ($windows_os) -{ - plan skip_all => 'symlinks not supported on Windows'; - exit; -} -else -{ - plan tests => 5; -} +use Test::More tests => 5; use FindBin; use lib $FindBin::RealBin; @@ -36,7 +27,7 @@ sub run_test # turn pg_wal into a symlink print("moving $test_primary_datadir/pg_wal to $primary_xlogdir\n"); move("$test_primary_datadir/pg_wal", $primary_xlogdir) or die; - symlink($primary_xlogdir, "$test_primary_datadir/pg_wal") or die; + dir_symlink($primary_xlogdir, "$test_primary_datadir/pg_wal") or die; RewindTest::start_primary(); diff --git a/src/test/perl/TestLib.pm b/src/test/perl/TestLib.pm index a7490d2ce7..207e92fdc0 100644 --- a/src/test/perl/TestLib.pm +++ b/src/test/perl/TestLib.pm @@ -67,6 +67,7 @@ our @EXPORT = qw( check_mode_recursive chmod_recursive check_pg_config + dir_symlink system_or_bail system_log run_log @@ -84,10 +85,11 @@ our @EXPORT = qw( command_checks_all $windows_os + $is_msys2 $use_unix_sockets ); -our ($windows_os, $use_unix_sockets, $tmp_check, $log_path, $test_logfile); +our ($windows_os, $is_msys2, $use_unix_sockets, $tmp_check, $log_path, $test_logfile); BEGIN { @@ -114,6 +116,9 @@ BEGIN # Must be set early $windows_os = $Config{osname} eq 'MSWin32' || $Config{osname} eq 'msys'; + # Check if this environment is MSYS2. + $is_msys2 = $^O eq 'msys' && `uname -or` =~ /^[2-9].*Msys/; + if ($windows_os) { require Win32API::File; @@ -137,6 +142,10 @@ BEGIN Set to true when running under Windows, except on Cygwin. +=item C<$is_msys2> + +Set to true when running under MSYS2. + =back =cut @@ -263,9 +272,10 @@ sub tempdir_short =item perl2host() -Translate a Perl file name to a host file name. Currently, this is a no-op +Translate a virtual file name to a host file name. Currently, this is a no-op except for the case of Perl=msys and host=mingw32. The subject need not -exist, but its parent directory must exist. +exist, but its parent or grandparent directory must exist unless cygpath is +available. =cut @@ -273,6 +283,17 @@ sub perl2host { my ($subject) = @_; return $subject unless $Config{osname} eq 'msys'; + if ($is_msys2) + { + # get absolute, windows type path + my $path = qx{cygpath -a -w "$subject"}; + if (! $?) + { + chomp $path; + return $path if $path; + } + # fall through if this didn't work. + } my $here = cwd; my $leaf; if (chdir $subject) @@ -283,7 +304,12 @@ sub perl2host { $leaf = '/' . basename $subject; my $parent = dirname $subject; - chdir $parent or die "could not chdir \"$parent\": $!"; + if (! chdir $parent) + { + $leaf = '/' . basename ($parent) . $leaf; + $parent = dirname $parent; + chdir $parent or die "could not chdir \"$parent\": $!"; + } } # this odd way of calling 'pwd -W' is the only way that seems to work. @@ -602,6 +628,41 @@ sub check_pg_config =pod +=item dir_symlink(oldname, newname) + +Portably create a symlink for a director. On Windows this creates a junction. +Elsewhere it just calls perl's builtin symlink. + +=cut + +sub dir_symlink +{ + my $oldname = shift; + my $newname = shift; + if ($windows_os) + { + $oldname = perl2host($oldname); + $newname = perl2host($newname); + $oldname =~ s,/,\\,g; + $newname =~ s,/,\\,g; + my $cmd = qq{mklink /j "$newname" "$oldname"}; + if ($Config{osname} eq 'msys') + { + # need some indirection on msys + $cmd = qq{echo '$cmd' | \$COMSPEC /Q}; + } + note("dir_symlink cmd: $cmd"); + system($cmd); + } + else + { + symlink $oldname, $newname; + } + die "No $newname" unless -e $newname; +} + +=pod + =back =head1 Test::More-LIKE METHODS