From 378472c1b3b2ca9f756a7bb09032db5729b73801 Mon Sep 17 00:00:00 2001
From: Zhijie Hou <houzhijie22@gmail.com>
Date: Mon, 26 Oct 2026 12:00:00 -0300
Subject: [PATCH v3 2/2] Test the clog/proc array window in
 HeapTupleSatisfiesVacuumHorizon()

A HOT update in a subtransaction, released, then a ROLLBACK stopped
after the clog update but before the proc array update, with the
injection point transaction-abort-after-clog.  In that window ANALYZE
must not mark the old tuple HEAP_XMAX_INVALID while the new heap-only
tuple is still in progress, or brin_summarize_range() fails with
"failed to find parent tuple for heap-only tuple".

Author: Zhijie Hou <houzhijie22@gmail.com>
Discussion: https://postgr.es/m/19760-8f9a0eba17bc8c92@postgresql.org
Backpatch-through: 17
---
 .../expected/multixact-aborted-updater.out    | 30 +++++++++++++++++++
 .../specs/multixact-aborted-updater.spec      | 19 ++++++++++++
 2 files changed, 49 insertions(+)

diff --git a/src/test/modules/injection_points/expected/multixact-aborted-updater.out b/src/test/modules/injection_points/expected/multixact-aborted-updater.out
index 4075071..abc3d0a 100644
--- a/src/test/modules/injection_points/expected/multixact-aborted-updater.out
+++ b/src/test/modules/injection_points/expected/multixact-aborted-updater.out
@@ -82,3 +82,33 @@ id|filler
  2|initial
 (1 row)
 
+
+starting permutation: s1ddl s1abort s3brin wake
+step s1ddl: 
+	BEGIN;
+	INSERT INTO mxact_abort VALUES (2, 'v1');
+	SAVEPOINT sp;
+	UPDATE mxact_abort SET filler = 'v2' WHERE id = 2;
+	RELEASE SAVEPOINT sp;
+
+step s1abort: ROLLBACK; <waiting ...>
+step s3brin: 
+	SELECT brin_desummarize_range('mxact_brin_idx', 0);
+	ANALYZE mxact_abort;
+	SELECT brin_summarize_range('mxact_brin_idx', 0);
+
+brin_desummarize_range
+----------------------
+                      
+(1 row)
+
+brin_summarize_range
+--------------------
+                   1
+(1 row)
+
+step wake: 
+	SELECT FROM injection_points_detach('transaction-abort-after-clog');
+	SELECT FROM injection_points_wakeup('transaction-abort-after-clog');
+
+step s1abort: <... completed>
diff --git a/src/test/modules/injection_points/specs/multixact-aborted-updater.spec b/src/test/modules/injection_points/specs/multixact-aborted-updater.spec
index 98d265a..f4c6fc9 100644
--- a/src/test/modules/injection_points/specs/multixact-aborted-updater.spec
+++ b/src/test/modules/injection_points/specs/multixact-aborted-updater.spec
@@ -20,6 +20,7 @@ setup
 	CREATE EXTENSION injection_points;
 
 	CREATE TABLE mxact_abort (id int PRIMARY KEY, filler text);
+	CREATE INDEX mxact_brin_idx ON mxact_abort USING brin (id);
 	INSERT INTO mxact_abort VALUES (1, 'initial');
 }
 
@@ -38,6 +39,13 @@ setup	{
 }
 step s1begin	{ BEGIN; }
 step s1update	{ UPDATE mxact_abort SET filler = 's1' WHERE id = 1; }
+step s1ddl	{
+	BEGIN;
+	INSERT INTO mxact_abort VALUES (2, 'v1');
+	SAVEPOINT sp;
+	UPDATE mxact_abort SET filler = 'v2' WHERE id = 2;
+	RELEASE SAVEPOINT sp;
+}
 step s1abort	{ ROLLBACK; }
 
 # Transaction 2 locks the row in KEY SHARE mode, to cause a multixact to be
@@ -52,6 +60,11 @@ step s3update	{ UPDATE mxact_abort SET filler = 's3' WHERE id = 1; }
 step s3delete	{ DELETE FROM mxact_abort WHERE id = 1; }
 step s3keyupdate	{ UPDATE mxact_abort SET id = 2 WHERE id = 1; }
 step s3check	{ SELECT * FROM mxact_abort ORDER BY id; }
+step s3brin		{
+	SELECT brin_desummarize_range('mxact_brin_idx', 0);
+	ANALYZE mxact_abort;
+	SELECT brin_summarize_range('mxact_brin_idx', 0);
+}
 
 session waker
 step wake		{
@@ -105,3 +118,9 @@ permutation
 	s3keyupdate
 	wake
 	s3check
+
+permutation
+	s1ddl
+	s1abort
+	s3brin
+	wake
-- 
2.56.0

