v2_PG19-0006-Add-VM-corruption-check-to-verify_heapam.patch

text/x-patch

Filename: v2_PG19-0006-Add-VM-corruption-check-to-verify_heapam.patch
Type: text/x-patch
Part: 5
Message: Re: Why clearing the VM doesn't require registering vm buffer in wal record

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-0006
Subject: Add VM corruption check to verify_heapam()
File+
contrib/amcheck/verify_heapam.c 19 0
From b6ddc887453446dafc28782d876517293b2b8a88 Mon Sep 17 00:00:00 2001
From: Melanie Plageman <melanieplageman@gmail.com>
Date: Wed, 29 Apr 2026 10:20:20 -0400
Subject: [PATCH v2_PG19 6/8] Add VM corruption check to verify_heapam()

pg_check_visible() can catch cases were tuples are not visible to all
and the VM is set, but it doesn't specifically check page hints. This
doesn't check, for example, an INSERT failing to clear PD_ALL_VISIBLE.

This also helps users who have access to amcheck but not pg_visibility
to diagnose corruption.

Author: Melanie Plageman <melanieplageman@gmail.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/flat/CAAKRu_bn%2Be7F4yPFBgFbnP%2BsyJRKyNK092bjD2LKvZW7O4Svag%40mail.gmail.com
---
 contrib/amcheck/verify_heapam.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index 20ff58aa782..823f05e5916 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -481,6 +481,7 @@ verify_heapam(PG_FUNCTION_ARGS)
 
 	while ((ctx.buffer = read_stream_next_buffer(stream, NULL)) != InvalidBuffer)
 	{
+		uint8		vmbits;
 		OffsetNumber maxoff;
 		OffsetNumber predecessor[MaxOffsetNumber];
 		OffsetNumber successor[MaxOffsetNumber];
@@ -499,6 +500,24 @@ verify_heapam(PG_FUNCTION_ARGS)
 		ctx.blkno = BufferGetBlockNumber(ctx.buffer);
 		ctx.page = BufferGetPage(ctx.buffer);
 
+		/*
+		 * It is corruption if the page-level PD_ALL_VISIBLE flag is clear and
+		 * the visibility map bits corresponding to this heap page are set.
+		 * Any corruption on the VM page itself won't be caught, and won't
+		 * cause visibilitymap_get_status() to fail, because the VM page is
+		 * read with ZERO_ON_ERROR.
+		 */
+		vmbits = visibilitymap_get_status(ctx.rel, ctx.blkno, &vmbuffer);
+
+		if (!PageIsAllVisible(ctx.page) &&
+			(vmbits & VISIBILITYMAP_VALID_BITS) != 0)
+		{
+			ctx.offnum = InvalidOffsetNumber;
+			ctx.attnum = -1;
+			report_corruption(&ctx,
+							  psprintf("page is not marked all-visible in page header but visibility map bit is set"));
+		}
+
 		/* Perform tuple checks */
 		maxoff = PageGetMaxOffsetNumber(ctx.page);
 		for (ctx.offnum = FirstOffsetNumber; ctx.offnum <= maxoff;
-- 
2.43.0