v2_PG19-0008-xlog-Verify-all-block-references-are-used-du.patch
text/x-patch
Filename: v2_PG19-0008-xlog-Verify-all-block-references-are-used-du.patch
Type: text/x-patch
Part: 7
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v2-0008
Subject: xlog: Verify all block references are used during replay
| File | + | − |
|---|---|---|
| src/backend/access/transam/xlog.c | 10 | 0 |
| src/backend/access/transam/xlogreader.c | 4 | 0 |
| src/backend/access/transam/xlogrecovery.c | 16 | 0 |
| src/backend/access/transam/xlogutils.c | 6 | 0 |
| src/include/access/xlogreader.h | 4 | 0 |
From 6474a4b261ca9c78163e5b9679222a3837f9f383 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Wed, 29 Apr 2026 12:08:26 -0400
Subject: [PATCH v2_PG19 8/8] xlog: Verify all block references are used during
replay
It's unclear if this makes sense to actually commit. It requires that
blocks read during recovery go through XLogReadBufferForRedoExtended()
and has a special case for handling XLOG_FPI_FOR_HINT when full page
writes are disabled.
Author: Andres Freund <andres@anarazel.de>
Co-authored-by: Melanie Plageman <melanieplageman@gmail.com>
Discussion: https://postgr.es/m/oqcsevg35xjan2327x5kdfth6q4fgeqboxfo3v3imeyih2uiny%406sez5dzxl6nt
---
src/backend/access/transam/xlog.c | 10 ++++++++++
src/backend/access/transam/xlogreader.c | 4 ++++
src/backend/access/transam/xlogrecovery.c | 16 ++++++++++++++++
src/backend/access/transam/xlogutils.c | 6 ++++++
src/include/access/xlogreader.h | 4 ++++
5 files changed, 40 insertions(+)
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index f0434da40c9..a8069ff729e 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -9081,6 +9081,16 @@ xlog_redo(XLogReaderState *record)
{
if (info == XLOG_FPI)
elog(ERROR, "XLOG_FPI record did not contain a full-page image");
+
+#ifdef USE_ASSERT_CHECKING
+
+ /*
+ * If full_page_writes are disabled, we don't want to error
+ * out because we didn't read the block.
+ */
+ if (info == XLOG_FPI_FOR_HINT)
+ record->record->blocks[block_id].used_read = true;
+#endif
continue;
}
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index 8849610db00..63747aec566 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -1794,6 +1794,10 @@ DecodeXLogRecord(XLogReaderState *state,
blk = &decoded->blocks[block_id];
blk->in_use = true;
+#ifdef USE_ASSERT_CHECKING
+ blk->used_read = false;
+#endif
+
blk->apply_image = false;
COPY_HEADER_FIELD(&fork_flags, sizeof(uint8));
diff --git a/src/backend/access/transam/xlogrecovery.c b/src/backend/access/transam/xlogrecovery.c
index 73b78a83fa7..7e7d0f57412 100644
--- a/src/backend/access/transam/xlogrecovery.c
+++ b/src/backend/access/transam/xlogrecovery.c
@@ -1965,6 +1965,22 @@ ApplyWalRecord(XLogReaderState *xlogreader, XLogRecord *record, TimeLineID *repl
/* Now apply the WAL record itself */
GetRmgr(record->xl_rmid).rm_redo(xlogreader);
+ /*
+ * Verify that all block references were used during replay. This helps
+ * detect bugs in redo routines. Every referenced block needs to be
+ * replayed with XLogReadBufferForRedoExtended(), possibly via a helper,
+ * to ensure that we replay FPIs, extend the relation if necessary, and
+ * other perform other similar protections.
+ */
+#ifdef USE_ASSERT_CHECKING
+ for (int block_id = 0; block_id <= xlogreader->record->max_block_id; block_id++)
+ {
+ DecodedBkpBlock *blk = &xlogreader->record->blocks[block_id];
+
+ Assert(!blk->in_use || blk->used_read);
+ }
+#endif
+
/*
* After redo, check whether the backup pages associated with the WAL
* record are consistent with the existing pages. This check is done only
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index 5fbe39133b8..979d6465159 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -359,6 +359,12 @@ XLogReadBufferForRedoExtended(XLogReaderState *record,
block_id);
}
+#ifdef USE_ASSERT_CHECKING
+ /* Shouldn't have multiple read references to block */
+ Assert(!record->record->blocks[block_id].used_read);
+ record->record->blocks[block_id].used_read = true;
+#endif
+
/*
* Make sure that if the block is marked with WILL_INIT, the caller is
* going to initialize it. And vice versa.
diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h
index 97eae2c1dab..fd2119005e8 100644
--- a/src/include/access/xlogreader.h
+++ b/src/include/access/xlogreader.h
@@ -145,6 +145,10 @@ typedef struct
bool has_data;
char *data;
uint16 data_len;
+
+#ifdef USE_ASSERT_CHECKING
+ bool used_read;
+#endif
} DecodedBkpBlock;
/*
--
2.43.0