From 6f45093233d79bf719b3c6d1df1d2eae971eead2 Mon Sep 17 00:00:00 2001 From: Mark Dilger Date: Thu, 26 Aug 2021 11:52:50 -0700 Subject: [PATCH v1] WIP patch to support amcheck of sequences --- contrib/amcheck/expected/check_heap.out | 6 +- contrib/amcheck/t/001_verify_heapam.pl | 102 +++++++++++++++++++++++- contrib/amcheck/verify_heapam.c | 10 ++- 3 files changed, 113 insertions(+), 5 deletions(-) diff --git a/contrib/amcheck/expected/check_heap.out b/contrib/amcheck/expected/check_heap.out index ad3086d2aa..c010361025 100644 --- a/contrib/amcheck/expected/check_heap.out +++ b/contrib/amcheck/expected/check_heap.out @@ -180,8 +180,10 @@ CREATE SEQUENCE test_sequence; SELECT * FROM verify_heapam('test_sequence', startblock := NULL, endblock := NULL); -ERROR: cannot check relation "test_sequence" -DETAIL: This operation is not supported for sequences. + blkno | offnum | attnum | msg +-------+--------+--------+----- +(0 rows) + -- Check that foreign tables are rejected CREATE FOREIGN DATA WRAPPER dummy; CREATE SERVER dummy_server FOREIGN DATA WRAPPER dummy; diff --git a/contrib/amcheck/t/001_verify_heapam.pl b/contrib/amcheck/t/001_verify_heapam.pl index 4f720a7ed0..174a132264 100644 --- a/contrib/amcheck/t/001_verify_heapam.pl +++ b/contrib/amcheck/t/001_verify_heapam.pl @@ -8,7 +8,7 @@ use PostgresNode; use TestLib; use Fcntl qw(:seek); -use Test::More tests => 80; +use Test::More tests => 133; my ($node, $result); @@ -21,6 +21,30 @@ $node->append_conf('postgresql.conf', 'autovacuum=off'); $node->start; $node->safe_psql('postgres', q(CREATE EXTENSION amcheck)); +# +# Check a sequence with no corruption +# +fresh_test_sequence('test_seq'); +check_all_options_uncorrupted('test_seq', 'sequence'); + +# +# Check a corrupt sequence +# +corrupt_sequence_relfile('test_seq'); +detects_sequence_corruption("verify_heapam('test_seq')", "corrupted sequence"); +detects_sequence_corruption( + "verify_heapam('test_seq', skip := 'all-visible')", + "corrupted sequence skipping all-visible"); +detects_sequence_corruption( + "verify_heapam('test_seq', skip := 'all-frozen')", + "corrupted sequence skipping all-frozen"); +detects_sequence_corruption( + "verify_heapam('test_seq', check_toast := false)", + "corrupted sequence skipping toast"); +detects_sequence_corruption( + "verify_heapam('test_seq', startblock := 0, endblock := 0)", + "corrupted sequence checking only block zero"); + # # Check a table with data loaded but no corruption, freezing, etc. # @@ -110,6 +134,71 @@ sub fresh_test_table )); } +# (Re)create and populate a test sequence of the given name +sub fresh_test_sequence +{ + my ($seqname) = @_; + + return $node->safe_psql( + 'postgres', qq( + DROP SEQUENCE IF EXISTS $seqname CASCADE; + CREATE SEQUENCE $seqname + INCREMENT BY 13 + MINVALUE 17 + START WITH 23; + BEGIN; + SELECT nextval('$seqname'); + COMMIT; + ALTER SEQUENCE $seqname RESTART; + SELECT setval('$seqname', 40); + SELECT nextval('$seqname'); + SELECT nextval('$seqname'); + SELECT nextval('$seqname'); + ROLLBACK; + BEGIN; + SELECT setval('$seqname', 35); + SELECT nextval('$seqname'); + COMMIT; + )); +} + +# Stops the test node, corrupts the first page of the named sequence, and +# restarts the node. +sub corrupt_sequence_relfile +{ + my ($relname) = @_; + my $relpath = relation_filepath($relname); + + $node->stop; + + my $fh; + open($fh, '+<', $relpath) + or BAIL_OUT("open failed: $!"); + binmode $fh; + + my $size = (stat($fh))[7]; + + # Loop over 2k chunks, to be compatible with all supported page sizes + for (my $start = 0; $start < $size; $start += 2048) + { + # Don't touch the page header + seek($fh, $start + 32, SEEK_SET) + or BAIL_OUT("seek failed: $!"); + + # Corrupt the remainder of the 2k chunk + syswrite( + $fh, + pack("L*", (0xAAA15550, 0xAAA0D550, 0x00010000, + 0x00008000, 0x0000800F, 0x001e8000) x 84) + ) or BAIL_OUT("syswrite failed: $!"); + } + + close($fh) + or BAIL_OUT("close failed: $!"); + + $node->start; +} + # Stops the test node, corrupts the first page of the named relation, and # restarts the node. sub corrupt_first_page @@ -141,6 +230,17 @@ sub corrupt_first_page $node->start; } +sub detects_sequence_corruption +{ + my ($function, $testname) = @_; + + detects_corruption( + $function, + $testname, + qr/\w+/ # accept any corruption message + ); +} + sub detects_heap_corruption { my ($function, $testname) = @_; diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c index 226271923a..85832183e6 100644 --- a/contrib/amcheck/verify_heapam.c +++ b/contrib/amcheck/verify_heapam.c @@ -305,14 +305,20 @@ verify_heapam(PG_FUNCTION_ARGS) */ if (ctx.rel->rd_rel->relkind != RELKIND_RELATION && ctx.rel->rd_rel->relkind != RELKIND_MATVIEW && - ctx.rel->rd_rel->relkind != RELKIND_TOASTVALUE) + ctx.rel->rd_rel->relkind != RELKIND_TOASTVALUE && + ctx.rel->rd_rel->relkind != RELKIND_SEQUENCE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot check relation \"%s\"", RelationGetRelationName(ctx.rel)), errdetail_relkind_not_supported(ctx.rel->rd_rel->relkind))); - if (ctx.rel->rd_rel->relam != HEAP_TABLE_AM_OID) + /* + * Sequences always use heap AM, but they don't show that in the catalogs. + * Other relkinds might be using a different AM, so check. + */ + if (ctx.rel->rd_rel->relkind != RELKIND_SEQUENCE && + ctx.rel->rd_rel->relam != HEAP_TABLE_AM_OID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("only heap AM is supported"))); -- 2.21.1 (Apple Git-122.3)