44use PostgresNode;
55use TestLib;
66
7- use Test::More tests => 65 ;
7+ use Test::More tests => 55 ;
88
99my ($node , $result );
1010
2828#
2929fresh_test_table(' test' );
3030corrupt_first_page(' test' );
31- detects_corruption(
32- " verify_heapam('test')" ,
33- " plain corrupted table" );
34- detects_corruption(
31+ detects_heap_corruption(" verify_heapam('test')" , " plain corrupted table" );
32+ detects_heap_corruption(
3533 " verify_heapam('test', skip := 'all-visible')" ,
3634 " plain corrupted table skipping all-visible" );
37- detects_corruption (
35+ detects_heap_corruption (
3836 " verify_heapam('test', skip := 'all-frozen')" ,
3937 " plain corrupted table skipping all-frozen" );
40- detects_corruption (
38+ detects_heap_corruption (
4139 " verify_heapam('test', check_toast := false)" ,
4240 " plain corrupted table skipping toast" );
43- detects_corruption (
41+ detects_heap_corruption (
4442 " verify_heapam('test', startblock := 0, endblock := 0)" ,
4543 " plain corrupted table checking only block zero" );
4644
5048fresh_test_table(' test' );
5149$node -> safe_psql(' postgres' , q( VACUUM FREEZE test) );
5250corrupt_first_page(' test' );
53- detects_corruption(
54- " verify_heapam('test')" ,
51+ detects_heap_corruption(" verify_heapam('test')" ,
5552 " all-frozen corrupted table" );
5653detects_no_corruption(
5754 " verify_heapam('test', skip := 'all-frozen')" ,
5855 " all-frozen corrupted table skipping all-frozen" );
5956
60- #
61- # Check a corrupt table with corrupt page header
62- #
63- fresh_test_table(' test' );
64- corrupt_first_page_and_header(' test' );
65- detects_corruption(
66- " verify_heapam('test')" ,
67- " corrupted test table with bad page header" );
68-
69- #
70- # Check an uncorrupted table with corrupt toast page header
71- #
72- fresh_test_table(' test' );
73- my $toast = get_toast_for(' test' );
74- corrupt_first_page_and_header($toast );
75- detects_corruption(
76- " verify_heapam('test', check_toast := true)" ,
77- " table with corrupted toast page header checking toast" );
78- detects_no_corruption(
79- " verify_heapam('test', check_toast := false)" ,
80- " table with corrupted toast page header skipping toast" );
81- detects_corruption(
82- " verify_heapam('$toast ')" ,
83- " corrupted toast page header" );
84-
85- #
86- # Check an uncorrupted table with corrupt toast
87- #
88- fresh_test_table(' test' );
89- $toast = get_toast_for(' test' );
90- corrupt_first_page($toast );
91- detects_corruption(
92- " verify_heapam('test', check_toast := true)" ,
93- " table with corrupted toast checking toast" );
94- detects_no_corruption(
95- " verify_heapam('test', check_toast := false)" ,
96- " table with corrupted toast skipping toast" );
97- detects_corruption(
98- " verify_heapam('$toast ')" ,
99- " corrupted toast table" );
100-
101- #
102- # Check an uncorrupted all-frozen table with corrupt toast
103- #
104- fresh_test_table(' test' );
105- $node -> safe_psql(' postgres' , q( VACUUM FREEZE test) );
106- $toast = get_toast_for(' test' );
107- corrupt_first_page($toast );
108- detects_corruption(
109- " verify_heapam('test', check_toast := true)" ,
110- " all-frozen table with corrupted toast checking toast" );
111- detects_no_corruption(
112- " verify_heapam('test', check_toast := false)" ,
113- " all-frozen table with corrupted toast skipping toast" );
114- detects_corruption(
115- " verify_heapam('$toast ')" ,
116- " corrupted toast table of all-frozen table" );
117-
11857# Returns the filesystem path for the named relation.
11958sub relation_filepath
12059{
12160 my ($relname ) = @_ ;
12261
12362 my $pgdata = $node -> data_dir;
124- my $rel = $node -> safe_psql(' postgres' ,
125- qq( SELECT pg_relation_filepath('$relname ')) );
63+ my $rel = $node -> safe_psql(' postgres' ,
64+ qq( SELECT pg_relation_filepath('$relname ')) );
12665 die " path not found for relation $relname " unless defined $rel ;
12766 return " $pgdata /$rel " ;
12867}
@@ -131,7 +70,9 @@ sub relation_filepath
13170sub get_toast_for
13271{
13372 my ($relname ) = @_ ;
134- $node -> safe_psql(' postgres' , qq(
73+
74+ return $node -> safe_psql(
75+ ' postgres' , qq(
13576 SELECT 'pg_toast.' || t.relname
13677 FROM pg_catalog.pg_class c, pg_catalog.pg_class t
13778 WHERE c.relname = '$relname '
@@ -142,7 +83,9 @@ sub get_toast_for
14283sub fresh_test_table
14384{
14485 my ($relname ) = @_ ;
145- $node -> safe_psql(' postgres' , qq(
86+
87+ return $node -> safe_psql(
88+ ' postgres' , qq(
14689 DROP TABLE IF EXISTS $relname CASCADE;
14790 CREATE TABLE $relname (a integer, b text);
14891 ALTER TABLE $relname SET (autovacuum_enabled=false);
@@ -154,55 +97,54 @@ sub fresh_test_table
15497
15598# Stops the test node, corrupts the first page of the named relation, and
15699# restarts the node.
157- sub corrupt_first_page_internal
100+ sub corrupt_first_page
158101{
159- my ($relname , $corrupt_header ) = @_ ;
102+ my ($relname ) = @_ ;
160103 my $relpath = relation_filepath($relname );
161104
162105 $node -> stop;
106+
163107 my $fh ;
164- open ($fh , ' +<' , $relpath );
108+ open ($fh , ' +<' , $relpath )
109+ or BAIL_OUT(" open failed: $! " );
165110 binmode $fh ;
166111
167- # If we corrupt the header, postgres won't allow the page into the buffer.
168- syswrite ($fh , ' \xFF\xFF\xFF\xFF' , 8) if ($corrupt_header );
112+ # Corrupt two line pointers. To be stable across platforms, we use
113+ # 0x55555555 and 0xAAAAAAAA for the two, which are bitwise reverses of each
114+ # other.
115+ seek ($fh , 32, 0)
116+ or BAIL_OUT(" seek failed: $! " );
117+ syswrite ($fh , pack (" L*" , 0x55555555, 0xAAAAAAAA))
118+ or BAIL_OUT(" syswrite failed: $! " );
119+ close ($fh )
120+ or BAIL_OUT(" close failed: $! " );
169121
170- # Corrupt at least the line pointers. Exactly what this corrupts will
171- # depend on the page, as it may run past the line pointers into the user
172- # data. We stop short of writing 2048 bytes (2k), the smallest supported
173- # page size, as we don't want to corrupt the next page.
174- seek ($fh , 32, 0);
175- syswrite ($fh , ' \x77\x77\x77\x77' , 500);
176- close ($fh );
177122 $node -> start;
178123}
179124
180- sub corrupt_first_page
125+ sub detects_heap_corruption
181126{
182- corrupt_first_page_internal($_ [0], undef );
183- }
127+ my ($function , $testname ) = @_ ;
184128
185- sub corrupt_first_page_and_header
186- {
187- corrupt_first_page_internal( $_ [0], 1 );
129+ detects_corruption( $function , $testname ,
130+ qr / line pointer redirection to item at offset \d + exceeds maximum offset \d + /
131+ );
188132}
189133
190134sub detects_corruption
191135{
192- my ($function , $testname ) = @_ ;
136+ my ($function , $testname , $re ) = @_ ;
193137
194- my $result = $node -> safe_psql(' postgres' ,
195- qq( SELECT COUNT(*) > 0 FROM $function ) );
196- is($result , ' t' , $testname );
138+ my $result = $node -> safe_psql(' postgres' , qq( SELECT * FROM $function ) );
139+ like($result , $re , $testname );
197140}
198141
199142sub detects_no_corruption
200143{
201144 my ($function , $testname ) = @_ ;
202145
203- my $result = $node -> safe_psql(' postgres' ,
204- qq( SELECT COUNT(*) = 0 FROM $function ) );
205- is($result , ' t' , $testname );
146+ my $result = $node -> safe_psql(' postgres' , qq( SELECT * FROM $function ) );
147+ is($result , ' ' , $testname );
206148}
207149
208150# Check various options are stable (don't abort) and do not report corruption
@@ -215,6 +157,7 @@ sub detects_no_corruption
215157sub check_all_options_uncorrupted
216158{
217159 my ($relname , $prefix ) = @_ ;
160+
218161 for my $stop (qw( true false) )
219162 {
220163 for my $check_toast (qw( true false) )
@@ -225,11 +168,12 @@ sub check_all_options_uncorrupted
225168 {
226169 for my $endblock (qw( NULL 0) )
227170 {
228- my $opts = " on_error_stop := $stop , " .
229- " check_toast := $check_toast , " .
230- " skip := $skip , " .
231- " startblock := $startblock , " .
232- " endblock := $endblock " ;
171+ my $opts =
172+ " on_error_stop := $stop , "
173+ . " check_toast := $check_toast , "
174+ . " skip := $skip , "
175+ . " startblock := $startblock , "
176+ . " endblock := $endblock " ;
233177
234178 detects_no_corruption(
235179 " verify_heapam('$relname ', $opts )" ,
0 commit comments