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
|
# Copyright (c) 2021-2025, PostgreSQL Global Development Group
# Tests recovery scenarios where the files are shorter than in the common
# cases, e.g. due to replaying WAL records of a relation that was subsequently
# truncated or dropped.
use strict;
use warnings FATAL => 'all';
use PostgreSQL::Test::Cluster;
use Test::More;
my $node = PostgreSQL::Test::Cluster->new('n1');
$node->init();
# Disable autovacuum to guarantee VACUUM can remove rows / truncate relations
$node->append_conf(
'postgresql.conf', qq[
wal_level = 'replica'
autovacuum = off
]);
$node->start();
# Test: Replay replay of PRUNE records for a pre-existing, then dropped,
# relation
$node->safe_psql(
'postgres', qq[
CREATE TABLE truncme(i int) WITH (fillfactor = 50);
INSERT INTO truncme SELECT generate_series(1, 1000);
UPDATE truncme SET i = 1;
CHECKPOINT; -- ensure relation exists at start of recovery
VACUUM truncme; -- generate prune records
DROP TABLE truncme;
]);
$node->stop('immediate');
ok($node->start(),
'replay of PRUNE records for a pre-existing, then dropped, relation');
# Test: Replay of PRUNE records for a newly created, then dropped, relation
$node->safe_psql(
'postgres', qq[
CREATE TABLE truncme(i int) WITH (fillfactor = 50);
INSERT INTO truncme SELECT generate_series(1, 1000);
UPDATE truncme SET i = 1;
VACUUM truncme; -- generate prune records
DROP TABLE truncme;
]);
$node->stop('immediate');
ok($node->start(),
'replay of PRUNE records for a newly created, then dropped, relation');
# Test: Replay of PRUNE records affecting truncated block. With FPIs used for
# PRUNE.
$node->safe_psql(
'postgres', qq[
CREATE TABLE truncme(i int) WITH (fillfactor = 50);
INSERT INTO truncme SELECT generate_series(1, 1000);
UPDATE truncme SET i = 1;
CHECKPOINT; -- generate FPIs
VACUUM truncme; -- generate prune records
TRUNCATE truncme; -- make blocks non-existing
INSERT INTO truncme SELECT generate_series(1, 10);
]);
$node->stop('immediate');
ok($node->start(),
'replay of PRUNE records affecting truncated block (FPIs)');
is($node->safe_psql('postgres', 'select count(*), sum(i) FROM truncme'),
'10|55', 'table contents as expected after recovery');
$node->safe_psql('postgres', 'DROP TABLE truncme');
# Test replay of PRUNE records for blocks that are later truncated. Without
# FPIs used for PRUNE.
$node->safe_psql(
'postgres', qq[
CREATE TABLE truncme(i int) WITH (fillfactor = 50);
INSERT INTO truncme SELECT generate_series(1, 1000);
UPDATE truncme SET i = 1;
VACUUM truncme; -- generate prune records
TRUNCATE truncme; -- make blocks non-existing
INSERT INTO truncme SELECT generate_series(1, 10);
]);
$node->stop('immediate');
ok($node->start(),
'replay of PRUNE records affecting truncated block (no FPIs)');
is($node->safe_psql('postgres', 'select count(*), sum(i) FROM truncme'),
'10|55', 'table contents as expected after recovery');
$node->safe_psql('postgres', 'DROP TABLE truncme');
# Test: Replay of partial truncation via VACUUM
$node->safe_psql(
'postgres', qq[
CREATE TABLE truncme(i int) WITH (fillfactor = 50);
INSERT INTO truncme SELECT generate_series(1, 1000);
UPDATE truncme SET i = i + 1;
-- ensure a mix of pre/post truncation rows
DELETE FROM truncme WHERE i > 500;
VACUUM truncme; -- should truncate relation
-- rows at TIDs that previously existed
INSERT INTO truncme SELECT generate_series(1000, 1010);
]);
$node->stop('immediate');
ok($node->start(), 'replay of partial truncation via VACUUM');
is( $node->safe_psql(
'postgres', 'select count(*), sum(i), min(i), max(i) FROM truncme'),
'510|136304|2|1010',
'table contents as expected after recovery');
$node->safe_psql('postgres', 'DROP TABLE truncme');
done_testing();
|