| From: | Ayush Tiwari <ayushtiwari(dot)slg01(at)gmail(dot)com> |
|---|---|
| To: | Takeshi Ideriha <iderihatakeshi(at)gmail(dot)com> |
| Cc: | alpkeles99(at)gmail(dot)com, pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Subject: | Re: BUG #19760: CLUSTER loses a row the transaction inserted and then updated in a rolled-back subtransaction |
| Date: | 2026-10-16 09:09:35 |
| Message-ID: | CAJTYsWXQgkqEpecZK9MCFVV+504oOdueuenRf+xTfNf9BcKE-g@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
Hi,
On Fri, 16 Oct 2026 at 08:25, Takeshi Ideriha <iderihatakeshi(at)gmail(dot)com> wrote:
>
> Hi
>
> Thank you for reporting this.
>
> >
> > CLUSTER (and REPACK on master) silently drops a live row when the same
> > transaction inserted the row and then updated it inside a subtransaction
> > that was rolled back. No error is raised; the row is gone after COMMIT.
> >
> > CREATE TABLE t1(c0 int, c1 int);
> > CREATE INDEX t1_i0 ON t1 (c1);
> > BEGIN;
> > INSERT INTO t1 VALUES (1, 1);
> > SAVEPOINT s1; UPDATE t1 SET c0 = 2; ROLLBACK TO s1;
> > CLUSTER t1 USING t1_i0;
> > SELECT count(*) FROM t1; -- 0, expected 1
> > COMMIT;
> > SELECT count(*) FROM t1; -- 0, expected 1
> >
> While I was reproducing the bug, I noticed that if we SELECT rows before
> CLUSTER, the issue won't happen. This suggests that not setting hint bit
> is very likely to be related to the issue.
>
> ----
> BEGIN;
> INSERT INTO t1 VALUES (1, 1);
> SAVEPOINT s1;
> UPDATE t1 SET c0 = 2;
> ROLLBACK TO s1;
>
> SELECT count(*) FROM t1; -- SELECT before CLUSTER
>
> CLUSTER VERBOSE t1 USING t1_i0;
> SELECT count(*) FROM t1;
> ---
>
> -- RESULT --
> count
> -------
> 1
> (1 row)
>
> INFO: repacking "public.t1" using index scan on "t1_i0"
> INFO: "public.t1": found 0 removable, 1 nonremovable row versions in 1
> pages
> DETAIL: 0 dead row versions cannot be removed yet.
> CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s.
> CLUSTER
> count
> -------
> 1
> (1 row)
>
> COMMIT
> count
> -------
> 1
> (1 row)
>
>
> > REL_18_STABLE 4d62ef92fda. CLUSTER VERBOSE reports a
> > negative "dead row versions cannot be removed yet" count (-1 here).
>
> The value is tups_recently_dead in heapam_relation_copy_for_cluster().
> tups_recently_dead decreases only when rewrite_heap_dead_tuple()
> returnes TRUE in rewriteheap.c. I guess there is something wrong with
> visibility check invloving hint bits in rewriteheap.c. I'll look into
> this further.
I think you are right. It looks like the missing HEAP_XMAX_INVALID
hint in HeapTupleSatisfiesVacuumHorizon() lets the rewrite defer copying
the live original tuple and then discard it when it encounters the aborted
successor.
Regards,
Ayush
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Nitin Motiani | 2026-10-16 09:15:26 | Re: BUG #19761: btree_gist: "cannot subtract infinite dates" when inserting 'infinity' into a GiST index on date |
| Previous Message | Clemenza Zhang | 2026-10-16 08:43:07 | Cached plan can stay stale indefinitely when an invalidation for a planner-added dependency arrives during planning |