}
$node->set_replication_conf();
-system_or_bail 'pg_ctl', '-D', $pgdata, 'reload';
+$node->reload;
$node->command_fails(
[ 'pg_basebackup', '-D', "$tempdir/backup" ],
q{SELECT b INTO corrupt2 FROM generate_series(1,2) AS b; ALTER TABLE corrupt2 SET (autovacuum_enabled=false); SELECT pg_relation_filepath('corrupt2')}
);
-# set page header and block sizes
-my $pageheader_size = 24;
+# get block size for corruption steps
my $block_size = $node->safe_psql('postgres', 'SHOW block_size;');
# induce corruption
-system_or_bail 'pg_ctl', '-D', $pgdata, 'stop';
-open $file, '+<', "$pgdata/$file_corrupt1";
-seek($file, $pageheader_size, 0);
-syswrite($file, "\0\0\0\0\0\0\0\0\0");
-close $file;
-system_or_bail 'pg_ctl', '-D', $pgdata, 'start';
+$node->stop;
+$node->corrupt_page_checksum($file_corrupt1, 0);
+$node->start;
$node->command_checks_all(
[ 'pg_basebackup', '-D', "$tempdir/backup_corrupt" ],
rmtree("$tempdir/backup_corrupt");
# induce further corruption in 5 more blocks
-system_or_bail 'pg_ctl', '-D', $pgdata, 'stop';
-open $file, '+<', "$pgdata/$file_corrupt1";
+$node->stop;
for my $i (1 .. 5)
{
- my $offset = $pageheader_size + $i * $block_size;
- seek($file, $offset, 0);
- syswrite($file, "\0\0\0\0\0\0\0\0\0");
+ $node->corrupt_page_checksum($file_corrupt1, $i * $block_size);
}
-close $file;
-system_or_bail 'pg_ctl', '-D', $pgdata, 'start';
+$node->start;
$node->command_checks_all(
[ 'pg_basebackup', '-D', "$tempdir/backup_corrupt2" ],
rmtree("$tempdir/backup_corrupt2");
# induce corruption in a second file
-system_or_bail 'pg_ctl', '-D', $pgdata, 'stop';
-open $file, '+<', "$pgdata/$file_corrupt2";
-seek($file, $pageheader_size, 0);
-syswrite($file, "\0\0\0\0\0\0\0\0\0");
-close $file;
-system_or_bail 'pg_ctl', '-D', $pgdata, 'start';
+$node->stop;
+$node->corrupt_page_checksum($file_corrupt2, 0);
+$node->start;
$node->command_checks_all(
[ 'pg_basebackup', '-D', "$tempdir/backup_corrupt3" ],
my $tablespace = shift;
my $pgdata = $node->data_dir;
+ # Create table and discover its filesystem location.
$node->safe_psql(
'postgres',
"SELECT a INTO $table FROM generate_series(1,10000) AS a;
my $relfilenode_corrupted = $node->safe_psql('postgres',
"SELECT relfilenode FROM pg_class WHERE relname = '$table';");
- # Set page header and block size
- my $pageheader_size = 24;
- my $block_size = $node->safe_psql('postgres', 'SHOW block_size;');
$node->stop;
# Checksums are correct for single relfilenode as the table is not
);
# Time to create some corruption
- open my $file, '+<', "$pgdata/$file_corrupted";
- seek($file, $pageheader_size, 0);
- syswrite($file, "\0\0\0\0\0\0\0\0\0");
- close $file;
+ $node->corrupt_page_checksum($file_corrupted, 0);
# Checksum checks on single relfilenode fail
$node->command_checks_all(
=pod
+=item $node->corrupt_page_checksum(self, file, page_offset)
+
+Intentionally corrupt the checksum field of one page in a file.
+The server must be stopped for this to work reliably.
+
+The file name should be specified relative to the cluster datadir.
+page_offset had better be a multiple of the cluster's block size.
+
+=cut
+
+sub corrupt_page_checksum
+{
+ my ($self, $file, $page_offset) = @_;
+ my $pgdata = $self->data_dir;
+ my $pageheader;
+
+ open my $fh, '+<', "$pgdata/$file" or die "open($file) failed: $!";
+ binmode $fh;
+ sysseek($fh, $page_offset, 0) or die "sysseek failed: $!";
+ sysread($fh, $pageheader, 24) or die "sysread failed: $!";
+ # This inverts the pd_checksum field (only); see struct PageHeaderData
+ $pageheader ^= "\0\0\0\0\0\0\0\0\xff\xff";
+ sysseek($fh, $page_offset, 0) or die "sysseek failed: $!";
+ syswrite($fh, $pageheader) or die "syswrite failed: $!";
+ close $fh;
+
+ return;
+}
+
+=pod
+
=back
=cut