Avoid updating inactive_since for invalid replication slots.
[pgsql.git] / contrib / amcheck / t / 005_pitr.pl
blob9da468a72cfc36017b260f6eb9b262542d4f3669
1 # Copyright (c) 2021-2025, PostgreSQL Global Development Group
3 # Test integrity of intermediate states by PITR to those states
4 use strict;
5 use warnings FATAL => 'all';
6 use PostgreSQL::Test::Cluster;
7 use PostgreSQL::Test::Utils;
8 use Test::More;
10 # origin node: generate WAL records of interest.
11 my $origin = PostgreSQL::Test::Cluster->new('origin');
12 $origin->init(has_archiving => 1, allows_streaming => 1);
13 $origin->append_conf('postgresql.conf', 'autovacuum = off');
14 $origin->start;
15 $origin->backup('my_backup');
16 # Create a table with each of 6 PK values spanning 1/4 of a block. Delete the
17 # first four, so one index leaf is eligible for deletion. Make a replication
18 # slot just so pg_walinspect will always have access to later WAL.
19 my $setup = <<EOSQL;
20 BEGIN;
21 CREATE EXTENSION amcheck;
22 CREATE EXTENSION pg_walinspect;
23 CREATE TABLE not_leftmost (c text STORAGE PLAIN);
24 INSERT INTO not_leftmost
25 SELECT repeat(n::text, database_block_size / 4)
26 FROM generate_series(1,6) t(n), pg_control_init();
27 ALTER TABLE not_leftmost ADD CONSTRAINT not_leftmost_pk PRIMARY KEY (c);
28 DELETE FROM not_leftmost WHERE c ~ '^[1-4]';
29 SELECT pg_create_physical_replication_slot('for_walinspect', true, false);
30 COMMIT;
31 EOSQL
32 $origin->safe_psql('postgres', $setup);
33 my $before_vacuum_lsn =
34 $origin->safe_psql('postgres', "SELECT pg_current_wal_lsn()");
35 # VACUUM to delete the aforementioned leaf page. Force an XLogFlush() by
36 # dropping a permanent table. That way, the XLogReader infrastructure can
37 # always see VACUUM's records, even under synchronous_commit=off. Finally,
38 # find the LSN of that VACUUM's last UNLINK_PAGE record.
39 my $vacuum = <<EOSQL;
40 SET synchronous_commit = off;
41 VACUUM (VERBOSE, INDEX_CLEANUP ON) not_leftmost;
42 CREATE TABLE XLogFlush ();
43 DROP TABLE XLogFlush;
44 SELECT max(start_lsn)
45 FROM pg_get_wal_records_info('$before_vacuum_lsn', 'FFFFFFFF/FFFFFFFF')
46 WHERE resource_manager = 'Btree' AND record_type = 'UNLINK_PAGE';
47 EOSQL
48 my $unlink_lsn = $origin->safe_psql('postgres', $vacuum);
49 $origin->stop;
50 die "did not find UNLINK_PAGE record" unless $unlink_lsn;
52 # replica node: amcheck at notable points in the WAL stream
53 my $replica = PostgreSQL::Test::Cluster->new('replica');
54 $replica->init_from_backup($origin, 'my_backup', has_restoring => 1);
55 $replica->append_conf('postgresql.conf',
56 "recovery_target_lsn = '$unlink_lsn'");
57 $replica->append_conf('postgresql.conf', 'recovery_target_inclusive = off');
58 $replica->append_conf('postgresql.conf', 'recovery_target_action = promote');
59 $replica->start;
60 $replica->poll_query_until('postgres', "SELECT pg_is_in_recovery() = 'f';")
61 or die "Timed out while waiting for PITR promotion";
62 # recovery done; run amcheck
63 my $debug = "SET client_min_messages = 'debug1'";
64 my ($rc, $stderr);
65 $rc = $replica->psql(
66 'postgres',
67 "$debug; SELECT bt_index_parent_check('not_leftmost_pk', true)",
68 stderr => \$stderr);
69 print STDERR $stderr, "\n";
70 is($rc, 0, "bt_index_parent_check passes");
71 like(
72 $stderr,
73 qr/interrupted page deletion detected/,
74 "bt_index_parent_check: interrupted page deletion detected");
75 $rc = $replica->psql(
76 'postgres',
77 "$debug; SELECT bt_index_check('not_leftmost_pk', true)",
78 stderr => \$stderr);
79 print STDERR $stderr, "\n";
80 is($rc, 0, "bt_index_check passes");
82 done_testing();