aboutsummaryrefslogtreecommitdiff
path: root/src/bin/pg_verify_checksums/t/002_actions.pl
blob: 74ad5ad7235cc66dd8358dee12e3c1b98fdebc61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# Do basic sanity checks supported by pg_verify_checksums using
# an initialized cluster.

use strict;
use warnings;
use PostgresNode;
use TestLib;
use Test::More tests => 45;


# Utility routine to create and check a table with corrupted checksums
# on a wanted tablespace.  Note that this stops and starts the node
# multiple times to perform the checks, leaving the node started
# at the end.
sub check_relation_corruption
{
	my $node = shift;
	my $table = shift;
	my $tablespace = shift;
	my $pgdata = $node->data_dir;

	$node->safe_psql('postgres',
		"SELECT a INTO $table FROM generate_series(1,10000) AS a;
		ALTER TABLE $table SET (autovacuum_enabled=false);");

	$node->safe_psql('postgres',
		"ALTER TABLE ".$table." SET TABLESPACE ".$tablespace.";");

	my $file_corrupted = $node->safe_psql('postgres',
		"SELECT pg_relation_filepath('$table');");
	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
	# corrupted yet.
	command_ok(['pg_verify_checksums',  '-D', $pgdata,
		'-r', $relfilenode_corrupted],
		"succeeds for single relfilenode on tablespace $tablespace with offline cluster");

	# 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;

	# Checksum checks on single relfilenode fail
	$node->command_checks_all([ 'pg_verify_checksums', '-D', $pgdata, '-r',
								$relfilenode_corrupted],
							  1,
							  [qr/Bad checksums:.*1/],
							  [qr/checksum verification failed/],
							  "fails with corrupted data for single relfilenode on tablespace $tablespace");

	# Global checksum checks fail as well
	$node->command_checks_all([ 'pg_verify_checksums', '-D', $pgdata],
							  1,
							  [qr/Bad checksums:.*1/],
							  [qr/checksum verification failed/],
							  "fails with corrupted data on tablespace $tablespace");

	# Drop corrupted table again and make sure there is no more corruption.
	$node->start;
	$node->safe_psql('postgres', "DROP TABLE $table;");
	$node->stop;
	$node->command_ok(['pg_verify_checksums', '-D', $pgdata],
	        "succeeds again after table drop on tablespace $tablespace");

	$node->start;
	return;
}

# Initialize node with checksums enabled.
my $node = get_new_node('node_checksum');
$node->init(extra => ['--data-checksums']);
my $pgdata = $node->data_dir;

# Control file should know that checksums are enabled.
command_like(['pg_controldata', $pgdata],
	     qr/Data page checksum version:.*1/,
		 'checksums enabled in control file');

# These are correct but empty files, so they should pass through.
append_to_file "$pgdata/global/99999", "";
append_to_file "$pgdata/global/99999.123", "";
append_to_file "$pgdata/global/99999_fsm", "";
append_to_file "$pgdata/global/99999_init", "";
append_to_file "$pgdata/global/99999_vm", "";
append_to_file "$pgdata/global/99999_init.123", "";
append_to_file "$pgdata/global/99999_fsm.123", "";
append_to_file "$pgdata/global/99999_vm.123", "";

# These are temporary files and folders with dummy contents, which
# should be ignored by the scan.
append_to_file "$pgdata/global/pgsql_tmp_123", "foo";
mkdir "$pgdata/global/pgsql_tmp";
append_to_file "$pgdata/global/pgsql_tmp/1.1", "foo";

# Checksums pass on a newly-created cluster
command_ok(['pg_verify_checksums',  '-D', $pgdata],
		   "succeeds with offline cluster");

# Checks cannot happen with an online cluster
$node->start;
command_fails(['pg_verify_checksums',  '-D', $pgdata],
			  "fails with online cluster");

# Check corruption of table on default tablespace.
check_relation_corruption($node, 'corrupt1', 'pg_default');

# Create tablespace to check corruptions in a non-default tablespace.
my $basedir = $node->basedir;
my $tablespace_dir = "$basedir/ts_corrupt_dir";
mkdir ($tablespace_dir);
$tablespace_dir = TestLib::real_dir($tablespace_dir);
$node->safe_psql('postgres',
	"CREATE TABLESPACE ts_corrupt LOCATION '$tablespace_dir';");
check_relation_corruption($node, 'corrupt2', 'ts_corrupt');

# Utility routine to check that pg_verify_checksums is able to detect
# correctly-named relation files filled with some corrupted data.
sub fail_corrupt
{
	my $node = shift;
	my $file = shift;
	my $pgdata = $node->data_dir;

	# Create the file with some dummy data in it.
	my $file_name = "$pgdata/global/$file";
	append_to_file $file_name, "foo";

	$node->command_checks_all([ 'pg_verify_checksums', '-D', $pgdata],
						  1,
						  [qr/^$/],
						  [qr/could not read block 0 in file.*$file\":/],
						  "fails for corrupted data in $file");

	# Remove file to prevent future lookup errors on conflicts.
	unlink $file_name;
	return;
}

# Stop instance for the follow-up checks.
$node->stop;

# Authorized relation files filled with corrupted data cause the
# checksum checks to fail.  Make sure to use file names different
# than the previous ones.
fail_corrupt($node, "99990");
fail_corrupt($node, "99990.123");
fail_corrupt($node, "99990_fsm");
fail_corrupt($node, "99990_init");
fail_corrupt($node, "99990_vm");
fail_corrupt($node, "99990_init.123");
fail_corrupt($node, "99990_fsm.123");
fail_corrupt($node, "99990_vm.123");