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
