| From: | Virender Singla <virender(dot)cse(at)gmail(dot)com> |
|---|---|
| To: | pgsql-hackers(at)lists(dot)postgresql(dot)org |
| Subject: | [PATCH] Corruption Issue: Fix missing tts_tid in ExecForceStoreHeapTuple |
| Date: | 2026-09-01 06:18:43 |
| Message-ID: | CAM6Zo8wZOLnCWRO_tuuXVX9J4N4JN6GsEnk8WJtT0=_0zy-1dw@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi hackers,
We encountered an issue where queries using the index scan reorder queue
(e.g., KNN GiST scans with lossy distance recheck) lose the tuple's
physical ItemPointer (ctid) in the returned slot.
When combined with row locking (e.g., SELECT ... FOR UPDATE), the invalid
TID (4294967295, 0) is passed to heap_lock_tuple(). Because InvalidBlockNumber
equals P_NEW, ReadBuffer() extends the relation on disk before aborting with
"attempted to lock invisible tuple", leaving an orphaned uninitialized block
that fails subsequent sequential scans with "ERROR: invalid page in block N".
This affects all supported versions (PG 14 through master).
=== Cause ===
When tuples are popped from node->iss_ReorderQueue, nodeIndexscan.c calls:
ExecForceStoreHeapTuple(tuple, slot, true);
In src/backend/executor/execTuples.c (TTS_IS_BUFFERTUPLE branch),
ExecClearTuple() resets slot->tts_tid to InvalidItemPointer, but
slot->tts_tid is never updated with tuple->t_self.
=== Reproducer ===
CREATE TABLE t (id int PRIMARY KEY, v circle);
INSERT INTO t SELECT i, circle(point(i, i), 0.5) FROM generate_series(1, 100) i;
CREATE INDEX ON t USING gist (v circle_ops);
-- 1. Emits invalid ctid (4294967295, 0):
SELECT ctid, id FROM t ORDER BY v <-> point(50, 50) LIMIT 1;
-- 2. Fails with "attempted to lock invisible tuple" and extends table on disk:
BEGIN;
SELECT id FROM t ORDER BY v <-> point(50, 50) LIMIT 1 FOR UPDATE;
ROLLBACK;
=== Fix ===
Assign `slot->tts_tid = tuple->t_self;` in the TTS_IS_BUFFERTUPLE branch of
ExecForceStoreHeapTuple(), matching the behavior of
tts_buffer_heap_store_tuple().
Attached is a patch against master including regression test coverage
in gist.sql.
Thanks,
Virender
| Attachment | Content-Type | Size |
|---|---|---|
| fix_exec_force_store_heap_tuple_tid.patch | application/x-patch | 3.8 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Ewan Young | 2026-09-01 06:26:38 | Re: Use pg_neg_s*_overflow() for open-coded negation overflow checks |
| Previous Message | vignesh C | 2026-09-01 06:06:04 | Re: Logical replication row filter loses unchanged toasted columns |