Fix portability issues in new amcheck test.

The tests added by commit 866e24d47 failed on big-endian machines
due to lack of attention to endianness considerations.  Fix that.

While here, improve a few small cosmetic things, such as running
it through perltidy.

Mark Dilger

Discussion: https://postgr.es/m/30B8E99A-2D9C-48D4-A55C-741C9D5F1563@enterprisedb.com
This commit is contained in:
Tom Lane 2020-10-23 14:02:00 -04:00
parent 87a174c0e7
commit 860593ec3b
1 changed files with 46 additions and 102 deletions

View File

@ -4,7 +4,7 @@ use warnings;
use PostgresNode; use PostgresNode;
use TestLib; use TestLib;
use Test::More tests => 65; use Test::More tests => 55;
my ($node, $result); my ($node, $result);
@ -28,19 +28,17 @@ check_all_options_uncorrupted('test', 'plain');
# #
fresh_test_table('test'); fresh_test_table('test');
corrupt_first_page('test'); corrupt_first_page('test');
detects_corruption( detects_heap_corruption("verify_heapam('test')", "plain corrupted table");
"verify_heapam('test')", detects_heap_corruption(
"plain corrupted table");
detects_corruption(
"verify_heapam('test', skip := 'all-visible')", "verify_heapam('test', skip := 'all-visible')",
"plain corrupted table skipping all-visible"); "plain corrupted table skipping all-visible");
detects_corruption( detects_heap_corruption(
"verify_heapam('test', skip := 'all-frozen')", "verify_heapam('test', skip := 'all-frozen')",
"plain corrupted table skipping all-frozen"); "plain corrupted table skipping all-frozen");
detects_corruption( detects_heap_corruption(
"verify_heapam('test', check_toast := false)", "verify_heapam('test', check_toast := false)",
"plain corrupted table skipping toast"); "plain corrupted table skipping toast");
detects_corruption( detects_heap_corruption(
"verify_heapam('test', startblock := 0, endblock := 0)", "verify_heapam('test', startblock := 0, endblock := 0)",
"plain corrupted table checking only block zero"); "plain corrupted table checking only block zero");
@ -50,79 +48,20 @@ detects_corruption(
fresh_test_table('test'); fresh_test_table('test');
$node->safe_psql('postgres', q(VACUUM FREEZE test)); $node->safe_psql('postgres', q(VACUUM FREEZE test));
corrupt_first_page('test'); corrupt_first_page('test');
detects_corruption( detects_heap_corruption("verify_heapam('test')",
"verify_heapam('test')",
"all-frozen corrupted table"); "all-frozen corrupted table");
detects_no_corruption( detects_no_corruption(
"verify_heapam('test', skip := 'all-frozen')", "verify_heapam('test', skip := 'all-frozen')",
"all-frozen corrupted table skipping all-frozen"); "all-frozen corrupted table skipping all-frozen");
#
# Check a corrupt table with corrupt page header
#
fresh_test_table('test');
corrupt_first_page_and_header('test');
detects_corruption(
"verify_heapam('test')",
"corrupted test table with bad page header");
#
# Check an uncorrupted table with corrupt toast page header
#
fresh_test_table('test');
my $toast = get_toast_for('test');
corrupt_first_page_and_header($toast);
detects_corruption(
"verify_heapam('test', check_toast := true)",
"table with corrupted toast page header checking toast");
detects_no_corruption(
"verify_heapam('test', check_toast := false)",
"table with corrupted toast page header skipping toast");
detects_corruption(
"verify_heapam('$toast')",
"corrupted toast page header");
#
# Check an uncorrupted table with corrupt toast
#
fresh_test_table('test');
$toast = get_toast_for('test');
corrupt_first_page($toast);
detects_corruption(
"verify_heapam('test', check_toast := true)",
"table with corrupted toast checking toast");
detects_no_corruption(
"verify_heapam('test', check_toast := false)",
"table with corrupted toast skipping toast");
detects_corruption(
"verify_heapam('$toast')",
"corrupted toast table");
#
# Check an uncorrupted all-frozen table with corrupt toast
#
fresh_test_table('test');
$node->safe_psql('postgres', q(VACUUM FREEZE test));
$toast = get_toast_for('test');
corrupt_first_page($toast);
detects_corruption(
"verify_heapam('test', check_toast := true)",
"all-frozen table with corrupted toast checking toast");
detects_no_corruption(
"verify_heapam('test', check_toast := false)",
"all-frozen table with corrupted toast skipping toast");
detects_corruption(
"verify_heapam('$toast')",
"corrupted toast table of all-frozen table");
# Returns the filesystem path for the named relation. # Returns the filesystem path for the named relation.
sub relation_filepath sub relation_filepath
{ {
my ($relname) = @_; my ($relname) = @_;
my $pgdata = $node->data_dir; my $pgdata = $node->data_dir;
my $rel = $node->safe_psql('postgres', my $rel = $node->safe_psql('postgres',
qq(SELECT pg_relation_filepath('$relname'))); qq(SELECT pg_relation_filepath('$relname')));
die "path not found for relation $relname" unless defined $rel; die "path not found for relation $relname" unless defined $rel;
return "$pgdata/$rel"; return "$pgdata/$rel";
} }
@ -131,7 +70,9 @@ sub relation_filepath
sub get_toast_for sub get_toast_for
{ {
my ($relname) = @_; my ($relname) = @_;
$node->safe_psql('postgres', qq(
return $node->safe_psql(
'postgres', qq(
SELECT 'pg_toast.' || t.relname SELECT 'pg_toast.' || t.relname
FROM pg_catalog.pg_class c, pg_catalog.pg_class t FROM pg_catalog.pg_class c, pg_catalog.pg_class t
WHERE c.relname = '$relname' WHERE c.relname = '$relname'
@ -142,7 +83,9 @@ sub get_toast_for
sub fresh_test_table sub fresh_test_table
{ {
my ($relname) = @_; my ($relname) = @_;
$node->safe_psql('postgres', qq(
return $node->safe_psql(
'postgres', qq(
DROP TABLE IF EXISTS $relname CASCADE; DROP TABLE IF EXISTS $relname CASCADE;
CREATE TABLE $relname (a integer, b text); CREATE TABLE $relname (a integer, b text);
ALTER TABLE $relname SET (autovacuum_enabled=false); ALTER TABLE $relname SET (autovacuum_enabled=false);
@ -154,55 +97,54 @@ sub fresh_test_table
# Stops the test node, corrupts the first page of the named relation, and # Stops the test node, corrupts the first page of the named relation, and
# restarts the node. # restarts the node.
sub corrupt_first_page_internal sub corrupt_first_page
{ {
my ($relname, $corrupt_header) = @_; my ($relname) = @_;
my $relpath = relation_filepath($relname); my $relpath = relation_filepath($relname);
$node->stop; $node->stop;
my $fh; my $fh;
open($fh, '+<', $relpath); open($fh, '+<', $relpath)
or BAIL_OUT("open failed: $!");
binmode $fh; binmode $fh;
# If we corrupt the header, postgres won't allow the page into the buffer. # Corrupt two line pointers. To be stable across platforms, we use
syswrite($fh, '\xFF\xFF\xFF\xFF', 8) if ($corrupt_header); # 0x55555555 and 0xAAAAAAAA for the two, which are bitwise reverses of each
# other.
seek($fh, 32, 0)
or BAIL_OUT("seek failed: $!");
syswrite($fh, pack("L*", 0x55555555, 0xAAAAAAAA))
or BAIL_OUT("syswrite failed: $!");
close($fh)
or BAIL_OUT("close failed: $!");
# Corrupt at least the line pointers. Exactly what this corrupts will
# depend on the page, as it may run past the line pointers into the user
# data. We stop short of writing 2048 bytes (2k), the smallest supported
# page size, as we don't want to corrupt the next page.
seek($fh, 32, 0);
syswrite($fh, '\x77\x77\x77\x77', 500);
close($fh);
$node->start; $node->start;
} }
sub corrupt_first_page sub detects_heap_corruption
{ {
corrupt_first_page_internal($_[0], undef); my ($function, $testname) = @_;
}
sub corrupt_first_page_and_header detects_corruption($function, $testname,
{ qr/line pointer redirection to item at offset \d+ exceeds maximum offset \d+/
corrupt_first_page_internal($_[0], 1); );
} }
sub detects_corruption sub detects_corruption
{ {
my ($function, $testname) = @_; my ($function, $testname, $re) = @_;
my $result = $node->safe_psql('postgres', my $result = $node->safe_psql('postgres', qq(SELECT * FROM $function));
qq(SELECT COUNT(*) > 0 FROM $function)); like($result, $re, $testname);
is($result, 't', $testname);
} }
sub detects_no_corruption sub detects_no_corruption
{ {
my ($function, $testname) = @_; my ($function, $testname) = @_;
my $result = $node->safe_psql('postgres', my $result = $node->safe_psql('postgres', qq(SELECT * FROM $function));
qq(SELECT COUNT(*) = 0 FROM $function)); is($result, '', $testname);
is($result, 't', $testname);
} }
# Check various options are stable (don't abort) and do not report corruption # Check various options are stable (don't abort) and do not report corruption
@ -215,6 +157,7 @@ sub detects_no_corruption
sub check_all_options_uncorrupted sub check_all_options_uncorrupted
{ {
my ($relname, $prefix) = @_; my ($relname, $prefix) = @_;
for my $stop (qw(true false)) for my $stop (qw(true false))
{ {
for my $check_toast (qw(true false)) for my $check_toast (qw(true false))
@ -225,11 +168,12 @@ sub check_all_options_uncorrupted
{ {
for my $endblock (qw(NULL 0)) for my $endblock (qw(NULL 0))
{ {
my $opts = "on_error_stop := $stop, " . my $opts =
"check_toast := $check_toast, " . "on_error_stop := $stop, "
"skip := $skip, " . . "check_toast := $check_toast, "
"startblock := $startblock, " . . "skip := $skip, "
"endblock := $endblock"; . "startblock := $startblock, "
. "endblock := $endblock";
detects_no_corruption( detects_no_corruption(
"verify_heapam('$relname', $opts)", "verify_heapam('$relname', $opts)",