Re: BUG #19760: CLUSTER loses a row the transaction inserted and then updated in a rolled-back subtransaction

From: Takeshi Ideriha <iderihatakeshi(at)gmail(dot)com>
To: 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 02:55:27
Message-ID: 41175da6-293f-4b8f-ad18-3636f3d8a2f6@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

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.

Regards,
Takeshi Ideriha

In response to

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message Manuel Reyes Bravo 2026-10-16 03:28:59 Re: use-after-free in jsonb_set_lax()
Previous Message John Naylor 2026-10-16 02:23:48 Re: use-after-free in jsonb_set_lax()