Here is what I have staged for commit, which I'm planning to do early next
week barring additional feedback or objections.  I've left out the tests
and extra checks for now.  Those might be worth adding, but I'd like to
make sure we at least get the main fix into November's releases.

-- 
nathan
>From 31b9eaae36fd2d7e8c04fe714f15ca081c1142fe Mon Sep 17 00:00:00 2001
From: Nathan Bossart <[email protected]>
Date: Fri, 30 Oct 2026 11:10:56 -0500
Subject: [PATCH v10 1/1] Fix missing tts_tid assignment in
 ExecForceStoreHeapTuple().

ExecForceStoreHeapTuple() clears the slot before storing the tuple,
which invalidates the slot's TID, but it only sets the TID again
for slots of type TTSOpsHeapTuple.  As a result, tuples stored into
other kinds of slots this way (e.g., those that pass through the
reorder queue in nodeIndexscan.c) have an invalid ctid.  Besides
producing wrong query results, this can lead to data corruption if
such a tuple is locked (e.g., via SELECT ... FOR UPDATE).  To fix,
set the slot's TID in all cases.

Oversight in commit b8d71745ea.

Author: Virender Singla <[email protected]>
Co-authored-by: Greg Burd <[email protected]>
Reviewed-by: Andres Freund <[email protected]>
Reviewed-by: Dilip Kumar <[email protected]>
Reviewed-by: Nikolay Samokhvalov <[email protected]>
Tested-by: Shihao Zhong <[email protected]>
Discussion: 
https://postgr.es/m/CAM6Zo8wZOLnCWRO_tuuXVX9J4N4JN6GsEnk8WJtT0%3D_0zy-1dw%40mail.gmail.com
Discussion: 
https://postgr.es/m/0498c10f-839b-4f68-9994-c29b454e55a4%40app.fastmail.com
Backpatch-through: 14
---
 src/backend/executor/execTuples.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/backend/executor/execTuples.c 
b/src/backend/executor/execTuples.c
index b8e8f52c64c..9bc241aecf9 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -1752,6 +1752,8 @@ ExecForceStoreHeapTuple(HeapTuple tuple,
                                                TupleTableSlot *slot,
                                                bool shouldFree)
 {
+       ItemPointerData tid = tuple->t_self;
+
        if (TTS_IS_HEAPTUPLE(slot))
        {
                ExecStoreHeapTuple(tuple, slot, shouldFree);
@@ -1784,6 +1786,13 @@ ExecForceStoreHeapTuple(HeapTuple tuple,
                        pfree(tuple);
                }
        }
+
+       /*
+        * ExecClearTuple() invalidated the slot's TID, so we need to set it 
again
+        * here.  Note that the tuple might have already been freed, so we use 
the
+        * copy of its TID that we saved above.
+        */
+       slot->tts_tid = tid;
 }
 
 /*
-- 
2.55.0

Reply via email to