mirror of https://github.com/postgres/postgres
Avoid calling contrib/amcheck functions with relations that are unsuitable for checking. Specifically, don't attempt verification of temporary relations, or indexes whose pg_index entry indicates that the index is invalid, or not ready. These relations are not supported by any of the contrib/amcheck functions, for reasons that are pretty fundamental. For example, the implementation of REINDEX CONCURRENTLY can add its own "transient" pg_index entries, which has rather unclear implications for the B-Tree verification functions, at least in the general case -- so they just treat it as an error. It falls to the amcheck caller (in this case pg_amcheck) to deal with the situation at a higher level. pg_amcheck now simply treats these conditions as additional "visibility concerns" when it queries system catalogs. This is a little arbitrary. It seems to have the least problems among any of the available alternatives. Author: Mark Dilger <mark.dilger@enterprisedb.com> Reported-By: Alexander Lakhin <exclusion@gmail.com> Reviewed-By: Peter Geoghegan <pg@bowt.ie> Reviewed-By: Robert Haas <robertmhaas@gmail.com> Bug: #17212 Discussion: https://postgr.es/m/17212-34dd4a1d6bba98bf@postgresql.org Backpatch: 14-, where pg_amcheck was introduced.pull/73/head
parent
6df1543abf
commit
d2bf06db37
@ -0,0 +1,233 @@ |
||||
|
||||
# Copyright (c) 2021, PostgreSQL Global Development Group |
||||
|
||||
# This regression test checks the behavior of pg_amcheck in the presence of |
||||
# inappropriate target relations. |
||||
# |
||||
use strict; |
||||
use warnings; |
||||
use PostgresNode; |
||||
use TestLib; |
||||
use Test::More tests => 52; |
||||
|
||||
# Establish a primary and standby server, with temporary and unlogged tables. |
||||
# The temporary tables should not be checked on either system, as pg_amcheck's |
||||
# sessions won't be able to see their contents. The unlogged tables should not |
||||
# be checked on the standby, as they won't have relation forks there. |
||||
# |
||||
my $node_primary = PostgresNode->new('primary'); |
||||
$node_primary->init( |
||||
allows_streaming => 1, |
||||
auth_extra => [ '--create-role', 'repl_role' ]); |
||||
$node_primary->start; |
||||
$node_primary->safe_psql('postgres', qq( |
||||
CREATE EXTENSION amcheck; |
||||
CREATE UNLOGGED TABLE unlogged_heap (i INTEGER[], b BOX); |
||||
INSERT INTO unlogged_heap (i, b) VALUES (ARRAY[1,2,3]::INTEGER[], '((1,2),(3,4))'::BOX); |
||||
CREATE INDEX unlogged_btree ON unlogged_heap USING BTREE (i); |
||||
CREATE INDEX unlogged_brin ON unlogged_heap USING BRIN (b); |
||||
CREATE INDEX unlogged_gin ON unlogged_heap USING GIN (i); |
||||
CREATE INDEX unlogged_gist ON unlogged_heap USING GIST (b); |
||||
CREATE INDEX unlogged_hash ON unlogged_heap USING HASH (i); |
||||
)); |
||||
my $backup_name = 'my_backup'; |
||||
$node_primary->backup($backup_name); |
||||
|
||||
# Hold open a session with temporary tables and indexes defined |
||||
# |
||||
my $in = ''; |
||||
my $out = ''; |
||||
my $timer = IPC::Run::timeout(180); |
||||
my $h = $node_primary->background_psql('postgres', \$in, \$out, $timer); |
||||
$in = qq( |
||||
BEGIN; |
||||
CREATE TEMPORARY TABLE heap_temporary (i INTEGER[], b BOX) ON COMMIT PRESERVE ROWS; |
||||
INSERT INTO heap_temporary (i, b) VALUES (ARRAY[1,2,3]::INTEGER[], '((1,2),(3,4))'::BOX); |
||||
CREATE INDEX btree_temporary ON heap_temporary USING BTREE (i); |
||||
CREATE INDEX brin_temporary ON heap_temporary USING BRIN (b); |
||||
CREATE INDEX gin_temporary ON heap_temporary USING GIN (i); |
||||
CREATE INDEX gist_temporary ON heap_temporary USING GIST (b); |
||||
CREATE INDEX hash_temporary ON heap_temporary USING HASH (i); |
||||
COMMIT; |
||||
BEGIN; |
||||
); |
||||
$h->pump_nb; |
||||
|
||||
my $node_standby = PostgresNode->new('standby'); |
||||
$node_standby->init_from_backup($node_primary, $backup_name, |
||||
has_streaming => 1); |
||||
$node_standby->start; |
||||
$node_primary->wait_for_catchup($node_standby, 'replay', |
||||
$node_primary->lsn('replay')); |
||||
|
||||
# Check that running amcheck functions from SQL against inappropriate targets |
||||
# fails sensibly. This portion of the test arguably belongs in contrib/amcheck |
||||
# rather than here, but we have already set up the necessary test environment, |
||||
# so check here rather than duplicating the environment there. |
||||
# |
||||
my ($stdout, $stderr); |
||||
for my $test ( |
||||
[ $node_standby => "SELECT * FROM verify_heapam('unlogged_heap')", |
||||
qr/^$/, |
||||
"checking unlogged table during recovery does not complain" ], |
||||
|
||||
[ $node_standby => "SELECT * FROM bt_index_check('unlogged_btree')", |
||||
qr/^$/, |
||||
"checking unlogged btree index during recovery does not complain" ], |
||||
|
||||
[ $node_standby => "SELECT * FROM bt_index_check('unlogged_brin')", |
||||
qr/ERROR: only B-Tree indexes are supported as targets for verification.*DETAIL: Relation "unlogged_brin" is not a B-Tree index/s, |
||||
"checking unlogged brin index during recovery fails appropriately" ], |
||||
|
||||
[ $node_standby => "SELECT * FROM bt_index_check('unlogged_gin')", |
||||
qr/ERROR: only B-Tree indexes are supported as targets for verification.*DETAIL: Relation "unlogged_gin" is not a B-Tree index/s, |
||||
"checking unlogged gin index during recovery fails appropriately" ], |
||||
|
||||
[ $node_standby => "SELECT * FROM bt_index_check('unlogged_gist')", |
||||
qr/ERROR: only B-Tree indexes are supported as targets for verification.*DETAIL: Relation "unlogged_gist" is not a B-Tree index/s, |
||||
"checking unlogged gist index during recovery fails appropriately" ], |
||||
|
||||
[ $node_standby => "SELECT * FROM bt_index_check('unlogged_hash')", |
||||
qr/ERROR: only B-Tree indexes are supported as targets for verification.*DETAIL: Relation "unlogged_hash" is not a B-Tree index/s, |
||||
"checking unlogged hash index during recovery fails appropriately" ], |
||||
|
||||
) |
||||
{ |
||||
$test->[0]->psql('postgres', $test->[1], |
||||
stdout => \$stdout, stderr => \$stderr); |
||||
like ($stderr, $test->[2], $test->[3]); |
||||
} |
||||
|
||||
# Verify that --all excludes the temporary relations and handles unlogged |
||||
# relations as appropriate, without raising any warnings or exiting with a |
||||
# non-zero status. |
||||
# |
||||
$node_primary->command_checks_all( |
||||
['pg_amcheck', '--all', '-D', 'template1'], |
||||
0, |
||||
[ qr/^$/ ], |
||||
[ qr/^$/ ], |
||||
'pg_amcheck all objects on primary'); |
||||
|
||||
$node_standby->command_checks_all( |
||||
['pg_amcheck', '--all', '-D', 'template1'], |
||||
0, |
||||
[ qr/^$/ ], |
||||
[ qr/^$/ ], |
||||
'pg_amcheck all objects on standby'); |
||||
|
||||
# Verify that explicitly asking for unlogged relations to be checked does not |
||||
# raise any warnings or exit with a non-zero exit status, even when they cannot |
||||
# be checked due to recovery being in progress. |
||||
# |
||||
# These relations will have no relation fork during recovery, so even without |
||||
# checking them, we can say (by definition) that they are not corrupt, because |
||||
# it is meaningless to declare a non-existent relation fork corrupt. |
||||
# |
||||
$node_primary->command_checks_all( |
||||
['pg_amcheck', '--relation', '*unlogged*'], |
||||
0, |
||||
[ qr/^$/ ], |
||||
[ qr/^$/ ], |
||||
'pg_amcheck unlogged objects on primary'); |
||||
|
||||
$node_standby->command_checks_all( |
||||
['pg_amcheck', '--relation', '*unlogged*'], |
||||
0, |
||||
[ qr/^$/ ], |
||||
[ qr/^$/ ], |
||||
'pg_amcheck unlogged objects on standby'); |
||||
|
||||
# Verify that the --heapallindexed check works on both primary and standby. |
||||
# |
||||
$node_primary->command_checks_all( |
||||
['pg_amcheck', '--all', '-D', 'template1', '--heapallindexed'], |
||||
0, |
||||
[ qr/^$/ ], |
||||
[ qr/^$/ ], |
||||
'pg_amcheck --helpallindexed on primary'); |
||||
|
||||
$node_standby->command_checks_all( |
||||
['pg_amcheck', '--all', '-D', 'template1', '--heapallindexed'], |
||||
0, |
||||
[ qr/^$/ ], |
||||
[ qr/^$/ ], |
||||
'pg_amcheck --helpallindexed on standby'); |
||||
|
||||
# Verify that the --parent-check and --rootdescend options work on the primary. |
||||
# |
||||
$node_primary->command_checks_all( |
||||
['pg_amcheck', '--all', '-D', 'template1', '--rootdescend'], |
||||
0, |
||||
[ qr/^$/ ], |
||||
[ qr/^$/ ], |
||||
'pg_amcheck --rootdescend on primary'); |
||||
|
||||
$node_primary->command_checks_all( |
||||
['pg_amcheck', '--all', '-D', 'template1', '--parent-check'], |
||||
0, |
||||
[ qr/^$/ ], |
||||
[ qr/^$/ ], |
||||
'pg_amcheck --parent-check on primary'); |
||||
|
||||
# Check that the failures on the standby from using --parent-check and |
||||
# --rootdescend are the failures we expect |
||||
# |
||||
$node_standby->command_checks_all( |
||||
['pg_amcheck', '--all', '-D', 'template1', '--rootdescend'], |
||||
2, |
||||
[ qr/ERROR: cannot acquire lock mode ShareLock on database objects while recovery is in progress/, |
||||
qr/btree index "postgres\.pg_catalog\./, |
||||
qr/btree index "postgres\.pg_toast\./, |
||||
], |
||||
[ qr/^$/ ], |
||||
'pg_amcheck --rootdescend on standby'); |
||||
|
||||
$node_standby->command_checks_all( |
||||
['pg_amcheck', '--all', '-D', 'template1', '--parent-check'], |
||||
2, |
||||
[ qr/ERROR: cannot acquire lock mode ShareLock on database objects while recovery is in progress/, |
||||
qr/btree index "postgres\.pg_catalog\./, |
||||
qr/btree index "postgres\.pg_toast\./, |
||||
], |
||||
[ qr/^$/ ], |
||||
'pg_amcheck --parent-check on standby'); |
||||
|
||||
# Bug #17212 |
||||
# |
||||
# Verify that explicitly asking for another session's temporary relations to be |
||||
# checked fails, but only in the sense that nothing matched the parameter. We |
||||
# don't complain that they are uncheckable, only that you gave a --relation |
||||
# option and we didn't find anything checkable matching the pattern. |
||||
# |
||||
$node_primary->command_checks_all( |
||||
['pg_amcheck', '--relation', '*temporary*'], |
||||
1, |
||||
[ qr/^$/ ], |
||||
[ qr/error: no relations to check matching "\*temporary\*"/ ], |
||||
'pg_amcheck temporary objects on primary'); |
||||
|
||||
$node_standby->command_checks_all( |
||||
['pg_amcheck', '--relation', '*temporary*'], |
||||
1, |
||||
[ qr/^$/ ], |
||||
[ qr/error: no relations to check matching "\*temporary\*"/ ], |
||||
'pg_amcheck temporary objects on standby'); |
||||
|
||||
# Verify that a relation pattern which only matches temporary relations draws |
||||
# an error, even when other relation patterns are ok. This differs from the |
||||
# test above in that the set of all relations to check is not empty. |
||||
# |
||||
$node_primary->command_checks_all( |
||||
['pg_amcheck', '--relation', '*temporary*', '--relation', '*unlogged*'], |
||||
1, |
||||
[ qr/^$/ ], |
||||
[ qr/error: no relations to check matching "\*temporary\*"/ ], |
||||
'pg_amcheck temporary objects on primary'); |
||||
|
||||
$node_standby->command_checks_all( |
||||
['pg_amcheck', '--relation', '*temporary*', '--relation', '*unlogged*'], |
||||
1, |
||||
[ qr/^$/ ], |
||||
[ qr/error: no relations to check matching "\*temporary\*"/ ], |
||||
'pg_amcheck temporary objects on standby'); |
Loading…
Reference in new issue