From a468549cc9414f6a132ff9927ed3704ce2354698 Mon Sep 17 00:00:00 2001 From: alterego655 <824662526@qq.com> Date: Thu, 22 Oct 2026 17:36:14 +0800 Subject: [PATCH v1 2/2] Test pg_walinspect reads across historical timelines Read a record that crosses into the first segment of a historical timeline with pg_get_wal_record_info(). XLogFindNextRecord() rewinds to the record's start after reading it, which used to select a timeline with no file for that segment. Enable TAP tests for pg_walinspect. --- contrib/pg_walinspect/Makefile | 1 + contrib/pg_walinspect/meson.build | 5 +++ contrib/pg_walinspect/t/001_timeline.pl | 52 +++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 contrib/pg_walinspect/t/001_timeline.pl diff --git a/contrib/pg_walinspect/Makefile b/contrib/pg_walinspect/Makefile index 22090f7716c..77efe3afd7f 100644 --- a/contrib/pg_walinspect/Makefile +++ b/contrib/pg_walinspect/Makefile @@ -10,6 +10,7 @@ EXTENSION = pg_walinspect DATA = pg_walinspect--1.0.sql pg_walinspect--1.0--1.1.sql REGRESS = pg_walinspect oldextversions +TAP_TESTS = 1 REGRESS_OPTS = --temp-config $(top_srcdir)/contrib/pg_walinspect/walinspect.conf diff --git a/contrib/pg_walinspect/meson.build b/contrib/pg_walinspect/meson.build index 82ab186c340..e155a453aa1 100644 --- a/contrib/pg_walinspect/meson.build +++ b/contrib/pg_walinspect/meson.build @@ -27,6 +27,11 @@ tests += { 'name': 'pg_walinspect', 'sd': meson.current_source_dir(), 'bd': meson.current_build_dir(), + 'tap': { + 'tests': [ + 't/001_timeline.pl', + ], + }, 'regress': { 'sql': [ 'pg_walinspect', diff --git a/contrib/pg_walinspect/t/001_timeline.pl b/contrib/pg_walinspect/t/001_timeline.pl new file mode 100644 index 00000000000..6bdef43c6de --- /dev/null +++ b/contrib/pg_walinspect/t/001_timeline.pl @@ -0,0 +1,52 @@ +# Copyright (c) 2026, PostgreSQL Global Development Group + +# Test timeline selection when XLogFindNextRecord() reads a record spanning +# segments and then rewinds to its beginning. +use strict; +use warnings FATAL => 'all'; + +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +my $node = PostgreSQL::Test::Cluster->new('main'); +$node->init(allows_streaming => 1, extra => ['--wal-segsize=1']); +# Keep old segments across the checkpoints that follow each promotion. +$node->append_conf('postgresql.conf', "wal_keep_size = '64MB'"); +$node->start; +$node->safe_psql('postgres', 'CREATE EXTENSION pg_walinspect'); + +# Write a record larger than a segment, starting right after a segment +# switch. It begins in segment N and ends about halfway through N+1. +$node->safe_psql('postgres', 'SELECT pg_switch_wal()'); +my $before = $node->lsn('insert'); +my $record_end = $node->safe_psql('postgres', + "SELECT pg_logical_emit_message(false, 'test', repeat('x', 1536 * 1024), true)" +); +my $record_start = $node->safe_psql( + 'postgres', qq[ + SELECT start_lsn FROM pg_get_wal_records_info('$before', '$record_end') + WHERE resource_manager = 'LogicalMessage'; +]); + +# Restart in standby mode and promote, switching to a new timeline. +# Timeline 2 begins in segment N+1, so it has no copy of segment N. +$node->set_standby_mode; +$node->restart; +$node->promote; + +# Timeline 3 begins in a later segment, so segment N+1 stays on timeline 2. +$node->safe_psql('postgres', 'SELECT pg_switch_wal()'); +$node->set_standby_mode; +$node->restart; +$node->promote; + +# Reading the record leaves the reader on timeline 2's copy of segment N+1. +# Rewinding to the record's start must return to timeline 1's segment N. +my ($ret, $stdout, $stderr) = $node->psql('postgres', + "SELECT end_lsn FROM pg_get_wal_record_info('$record_start')"); +is($stdout, $record_end, + 'reread a cross-segment record after its timeline became historical') + or diag($stderr); + +done_testing(); -- 2.51.0