From 87efed93b6978443303372cf7b51b069e53c5b8b Mon Sep 17 00:00:00 2001
From: Kevin Rocker <me@kevinrocker.com>
Date: Mon, 28 Sep 2026 02:32:16 +0200
Subject: [PATCH v8 1/3] Don't call vacuum_delay_point() with a buffer lock
 held in ANALYZE.

vacuum_delay_point() and CHECK_FOR_INTERRUPTS() cannot process pending
interrupts while interrupts are held off.  A vacuum delay point may
additionally sleep while retaining a buffer content lock.

Since commit fcb9c977aa5, ANALYZE reaching a delay point while holding
a buffer content lock can crash assert-enabled builds and leave the
page permanently blocked for writers in production builds.

Move the ANALYZE delay point before scan_analyze_next_block().

Reported-by: Greg Burd <greg@burd.me>
Author: Kevin Rocker <me@kevinrocker.com>
Author: Andrey Borodin <amborodin@acm.org>
Reviewed-by: Neil Chen <carpenter.nail.cz@gmail.com>
Tested-by: Greg Burd <greg@burd.me>
Discussion: https://postgr.es/m/492c6247-43d3-477b-8981-fb0c56767b38%40app.fastmail.com
Backpatch-through: 19
---
 src/backend/commands/analyze.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index d0498b14da1..4cd1cf87fda 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -1337,10 +1337,13 @@ acquire_sample_rows(Relation onerel, int elevel,
 										0);
 
 	/* Outer loop over blocks to sample */
-	while (table_scan_analyze_next_block(scan, stream))
+	for (;;)
 	{
 		vacuum_delay_point(true);
 
+		if (!table_scan_analyze_next_block(scan, stream))
+			break;
+
 		while (table_scan_analyze_next_tuple(scan, &liverows, &deadrows, slot))
 		{
 			/*
-- 
2.54.0

