1use strict; 2use warnings; 3use Cwd; 4use Config; 5use PostgresNode; 6use TestLib; 7use Test::More tests => 72; 8 9program_help_ok('pg_basebackup'); 10program_version_ok('pg_basebackup'); 11program_options_handling_ok('pg_basebackup'); 12 13my $tempdir = TestLib::tempdir; 14 15my $node = get_new_node('main'); 16 17# Initialize node without replication settings 18$node->init; 19$node->start; 20my $pgdata = $node->data_dir; 21 22$node->command_fails(['pg_basebackup'], 23 'pg_basebackup needs target directory specified'); 24 25# Some Windows ANSI code pages may reject this filename, in which case we 26# quietly proceed without this bit of test coverage. 27if (open my $badchars, '>>', "$tempdir/pgdata/FOO\xe0\xe0\xe0BAR") 28{ 29 print $badchars "test backup of file with non-UTF8 name\n"; 30 close $badchars; 31} 32 33$node->set_replication_conf(); 34system_or_bail 'pg_ctl', '-D', $pgdata, 'reload'; 35 36$node->command_fails( 37 [ 'pg_basebackup', '-D', "$tempdir/backup" ], 38 'pg_basebackup fails because of WAL configuration'); 39 40ok(!-d "$tempdir/backup", 'backup directory was cleaned up'); 41 42$node->command_fails([ 'pg_basebackup', '-D', "$tempdir/backup", '-n' ], 43 'failing run with no-clean option'); 44 45ok(-d "$tempdir/backup", 'backup directory was created and left behind'); 46 47open my $conf, '>>', "$pgdata/postgresql.conf"; 48print $conf "max_replication_slots = 10\n"; 49print $conf "max_wal_senders = 10\n"; 50print $conf "wal_level = replica\n"; 51close $conf; 52$node->restart; 53 54# Write some files to test that they are not copied. 55foreach my $filename ( 56 qw(backup_label tablespace_map postgresql.auto.conf.tmp current_logfiles.tmp) 57 ) 58{ 59 open my $file, '>>', "$pgdata/$filename"; 60 print $file "DONOTCOPY"; 61 close $file; 62} 63 64$node->command_ok([ 'pg_basebackup', '-D', "$tempdir/backup", '-X', 'none' ], 65 'pg_basebackup runs'); 66ok(-f "$tempdir/backup/PG_VERSION", 'backup was created'); 67 68# Only archive_status directory should be copied in pg_wal/. 69is_deeply( 70 [ sort(slurp_dir("$tempdir/backup/pg_wal/")) ], 71 [ sort qw(. .. archive_status) ], 72 'no WAL files copied'); 73 74# Contents of these directories should not be copied. 75foreach my $dirname ( 76 qw(pg_dynshmem pg_notify pg_replslot pg_serial pg_snapshots pg_stat_tmp pg_subtrans) 77 ) 78{ 79 is_deeply( 80 [ sort(slurp_dir("$tempdir/backup/$dirname/")) ], 81 [ sort qw(. ..) ], 82 "contents of $dirname/ not copied"); 83} 84 85# These files should not be copied. 86foreach my $filename ( 87 qw(postgresql.auto.conf.tmp postmaster.opts postmaster.pid tablespace_map current_logfiles.tmp) 88 ) 89{ 90 ok(!-f "$tempdir/backup/$filename", "$filename not copied"); 91} 92 93# Make sure existing backup_label was ignored. 94isnt(slurp_file("$tempdir/backup/backup_label"), 95 'DONOTCOPY', 'existing backup_label not copied'); 96 97$node->command_ok( 98 [ 'pg_basebackup', '-D', "$tempdir/backup2", '--waldir', 99 "$tempdir/xlog2" ], 100 'separate xlog directory'); 101ok(-f "$tempdir/backup2/PG_VERSION", 'backup was created'); 102ok(-d "$tempdir/xlog2/", 'xlog directory was created'); 103 104$node->command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup", '-Ft' ], 105 'tar format'); 106ok(-f "$tempdir/tarbackup/base.tar", 'backup tar was created'); 107 108$node->command_fails( 109 [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T=/foo" ], 110 '-T with empty old directory fails'); 111$node->command_fails( 112 [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T/foo=" ], 113 '-T with empty new directory fails'); 114$node->command_fails( 115 [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', 116 "-T/foo=/bar=/baz" ], 117 '-T with multiple = fails'); 118$node->command_fails( 119 [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-Tfoo=/bar" ], 120 '-T with old directory not absolute fails'); 121$node->command_fails( 122 [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T/foo=bar" ], 123 '-T with new directory not absolute fails'); 124$node->command_fails( 125 [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-Tfoo" ], 126 '-T with invalid format fails'); 127 128# Tar format doesn't support filenames longer than 100 bytes. 129my $superlongname = "superlongname_" . ("x" x 100); 130my $superlongpath = "$pgdata/$superlongname"; 131 132open my $file, '>', "$superlongpath" 133 or die "unable to create file $superlongpath"; 134close $file; 135$node->command_fails( 136 [ 'pg_basebackup', '-D', "$tempdir/tarbackup_l1", '-Ft' ], 137 'pg_basebackup tar with long name fails'); 138unlink "$pgdata/$superlongname"; 139 140# The following tests test symlinks. Windows doesn't have symlinks, so 141# skip on Windows. 142SKIP: 143{ 144 skip "symlinks not supported on Windows", 11 if ($windows_os); 145 146 # Move pg_replslot out of $pgdata and create a symlink to it. 147 $node->stop; 148 149 rename("$pgdata/pg_replslot", "$tempdir/pg_replslot") 150 or BAIL_OUT "could not move $pgdata/pg_replslot"; 151 symlink("$tempdir/pg_replslot", "$pgdata/pg_replslot") 152 or BAIL_OUT "could not symlink to $pgdata/pg_replslot"; 153 154 $node->start; 155 156 # Create a temporary directory in the system location and symlink it 157 # to our physical temp location. That way we can use shorter names 158 # for the tablespace directories, which hopefully won't run afoul of 159 # the 99 character length limit. 160 my $shorter_tempdir = TestLib::tempdir_short . "/tempdir"; 161 symlink "$tempdir", $shorter_tempdir; 162 163 mkdir "$tempdir/tblspc1"; 164 $node->safe_psql('postgres', 165 "CREATE TABLESPACE tblspc1 LOCATION '$shorter_tempdir/tblspc1';"); 166 $node->safe_psql('postgres', 167 "CREATE TABLE test1 (a int) TABLESPACE tblspc1;"); 168 $node->command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup2", '-Ft' ], 169 'tar format with tablespaces'); 170 ok(-f "$tempdir/tarbackup2/base.tar", 'backup tar was created'); 171 my @tblspc_tars = glob "$tempdir/tarbackup2/[0-9]*.tar"; 172 is(scalar(@tblspc_tars), 1, 'one tablespace tar was created'); 173 174 $node->command_fails( 175 [ 'pg_basebackup', '-D', "$tempdir/backup1", '-Fp' ], 176 'plain format with tablespaces fails without tablespace mapping'); 177 178 $node->command_ok( 179 [ 'pg_basebackup', '-D', "$tempdir/backup1", '-Fp', 180 "-T$shorter_tempdir/tblspc1=$tempdir/tbackup/tblspc1" ], 181 'plain format with tablespaces succeeds with tablespace mapping'); 182 ok(-d "$tempdir/tbackup/tblspc1", 'tablespace was relocated'); 183 opendir(my $dh, "$pgdata/pg_tblspc") or die; 184 ok( ( grep { 185 -l "$tempdir/backup1/pg_tblspc/$_" 186 and readlink "$tempdir/backup1/pg_tblspc/$_" eq 187 "$tempdir/tbackup/tblspc1" 188 } readdir($dh)), 189 "tablespace symlink was updated"); 190 closedir $dh; 191 192 ok( -d "$tempdir/backup1/pg_replslot", 193 'pg_replslot symlink copied as directory'); 194 195 mkdir "$tempdir/tbl=spc2"; 196 $node->safe_psql('postgres', "DROP TABLE test1;"); 197 $node->safe_psql('postgres', "DROP TABLESPACE tblspc1;"); 198 $node->safe_psql('postgres', 199 "CREATE TABLESPACE tblspc2 LOCATION '$shorter_tempdir/tbl=spc2';"); 200 $node->command_ok( 201 [ 'pg_basebackup', '-D', "$tempdir/backup3", '-Fp', 202 "-T$shorter_tempdir/tbl\\=spc2=$tempdir/tbackup/tbl\\=spc2" ], 203 'mapping tablespace with = sign in path'); 204 ok(-d "$tempdir/tbackup/tbl=spc2", 205 'tablespace with = sign was relocated'); 206 $node->safe_psql('postgres', "DROP TABLESPACE tblspc2;"); 207 208 mkdir "$tempdir/$superlongname"; 209 $node->safe_psql('postgres', 210 "CREATE TABLESPACE tblspc3 LOCATION '$tempdir/$superlongname';"); 211 $node->command_ok( 212 [ 'pg_basebackup', '-D', "$tempdir/tarbackup_l3", '-Ft' ], 213 'pg_basebackup tar with long symlink target'); 214 $node->safe_psql('postgres', "DROP TABLESPACE tblspc3;"); 215} 216 217$node->command_ok([ 'pg_basebackup', '-D', "$tempdir/backupR", '-R' ], 218 'pg_basebackup -R runs'); 219ok(-f "$tempdir/backupR/recovery.conf", 'recovery.conf was created'); 220my $recovery_conf = slurp_file "$tempdir/backupR/recovery.conf"; 221 222my $port = $node->port; 223like( 224 $recovery_conf, 225 qr/^standby_mode = 'on'\n/m, 226 'recovery.conf sets standby_mode'); 227like( 228 $recovery_conf, 229 qr/^primary_conninfo = '.*port=$port.*'\n/m, 230 'recovery.conf sets primary_conninfo'); 231 232$node->command_ok( 233 [ 'pg_basebackup', '-D', "$tempdir/backupxd" ], 234 'pg_basebackup runs in default xlog mode'); 235ok(grep(/^[0-9A-F]{24}$/, slurp_dir("$tempdir/backupxd/pg_wal")), 236 'WAL files copied'); 237 238$node->command_ok( 239 [ 'pg_basebackup', '-D', "$tempdir/backupxf", '-X', 'fetch' ], 240 'pg_basebackup -X fetch runs'); 241ok(grep(/^[0-9A-F]{24}$/, slurp_dir("$tempdir/backupxf/pg_wal")), 242 'WAL files copied'); 243$node->command_ok( 244 [ 'pg_basebackup', '-D', "$tempdir/backupxs", '-X', 'stream' ], 245 'pg_basebackup -X stream runs'); 246ok(grep(/^[0-9A-F]{24}$/, slurp_dir("$tempdir/backupxf/pg_wal")), 247 'WAL files copied'); 248$node->command_ok( 249 [ 'pg_basebackup', '-D', "$tempdir/backupxst", '-X', 'stream', '-Ft' ], 250 'pg_basebackup -X stream runs in tar mode'); 251ok(-f "$tempdir/backupxst/pg_wal.tar", "tar file was created"); 252$node->command_ok( 253 [ 'pg_basebackup', '-D', 254 "$tempdir/backupnoslot", '-X', 255 'stream', '--no-slot' ], 256 'pg_basebackup -X stream runs with --no-slot'); 257 258$node->command_fails( 259 [ 'pg_basebackup', '-D', 260 "$tempdir/backupxs_sl_fail", '-X', 261 'stream', '-S', 262 'slot1' ], 263 'pg_basebackup fails with nonexistent replication slot'); 264 265$node->safe_psql('postgres', 266 q{SELECT * FROM pg_create_physical_replication_slot('slot1')}); 267my $lsn = $node->safe_psql('postgres', 268 q{SELECT restart_lsn FROM pg_replication_slots WHERE slot_name = 'slot1'} 269); 270is($lsn, '', 'restart LSN of new slot is null'); 271$node->command_fails( 272 [ 'pg_basebackup', '-D', "$tempdir/fail", '-S', 'slot1', '-X', 'none' ], 273 'pg_basebackup with replication slot fails without WAL streaming'); 274$node->command_ok( 275 [ 'pg_basebackup', '-D', "$tempdir/backupxs_sl", '-X', 276 'stream', '-S', 'slot1' ], 277 'pg_basebackup -X stream with replication slot runs'); 278$lsn = $node->safe_psql('postgres', 279 q{SELECT restart_lsn FROM pg_replication_slots WHERE slot_name = 'slot1'} 280); 281like($lsn, qr!^0/[0-9A-Z]{7,8}$!, 'restart LSN of slot has advanced'); 282 283$node->command_ok( 284 [ 'pg_basebackup', '-D', "$tempdir/backupxs_sl_R", '-X', 285 'stream', '-S', 'slot1', '-R' ], 286 'pg_basebackup with replication slot and -R runs'); 287like( 288 slurp_file("$tempdir/backupxs_sl_R/recovery.conf"), 289 qr/^primary_slot_name = 'slot1'\n/m, 290 'recovery.conf sets primary_slot_name'); 291