1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

Fix Parallel TID Range Scan rescans with a different TID range

0ca3b1697 added support for parallel TID Range Scans.  The range of
blocks to scan is stored in the shared phs_startblock and phs_numblock
fields by the first participant to start the scan, but
table_block_parallelscan_reinitialize() didn't reset these, so every
rescan of the Gather reused the block range of the first execution.
This caused rows to be silently missed when a rescan used a different
TID range, e.g:

SELECT r.lo, (SELECT count(*) FROM t WHERE ctid >= (SELECT r.lo))
FROM (VALUES ('(30,0)'::tid), ('(10,0)'), ('(0,0)')) r(lo);

Here we fix this by resetting both fields in
table_block_parallelscan_reinitialize().  As a side effect, a rescanned
Parallel Seq Scan using synchronized scans now gets a new start
location, which is what a new scan would do anyway.

The existing rescan test didn't exercise this, so add one that does.

This was introduced in 0ca3b1697, so backpatch to v19.

Reported-by: Zsolt Parragi <zsolt.parragi@percona.com>
Author: Zsolt Parragi <zsolt.parragi@percona.com>
Author: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: David Rowley <dgrowleyml@gmail.com>
Reviewed-by: shihao zhong <zhong950419@gmail.com>
Discussion: https://postgr.es/m/CAN4CZFOoyCQE483YVGg07_Vr2MjFCTiCLUHUAFCwKqJAE=PN6w@mail.gmail.com
Backpatch-through: 19
This commit is contained in:
David Rowley
2026-10-16 16:49:42 +13:00
parent 5b5e072ef8
commit 93d48c6516
3 changed files with 54 additions and 0 deletions

View File

@@ -434,6 +434,17 @@ table_block_parallelscan_reinitialize(Relation rel, ParallelTableScanDesc pscan)
{
ParallelBlockTableScanDesc bpscan = (ParallelBlockTableScanDesc) pscan;
/*
* Reset the start block and block limit too. These are set by the first
* participant to call table_block_parallelscan_startblock_init(), and a
* rescan may want to scan a different range of blocks, e.g. a TID Range
* Scan with different TID bounds.
*/
SpinLockAcquire(&bpscan->phs_mutex);
bpscan->phs_startblock = InvalidBlockNumber;
bpscan->phs_numblock = InvalidBlockNumber;
SpinLockRelease(&bpscan->phs_mutex);
pg_atomic_write_u64(&bpscan->phs_nallocated, 0);
}

View File

@@ -427,5 +427,36 @@ WHERE t.ctid < '(1,0)';
(0,5) | 5
(5 rows)
-- Parallel rescans with a different TID range on each rescan
EXPLAIN (COSTS OFF)
SELECT r.lo,
(SELECT count(*) FROM parallel_tidrangescan t
WHERE t.ctid >= (SELECT r.lo)) c
FROM (VALUES ('(30,0)'::tid), ('(10,0)'), ('(0,0)')) r(lo);
QUERY PLAN
------------------------------------------------------------------------------
Values Scan on "*VALUES*"
SubPlan expr_1
-> Finalize Aggregate
InitPlan expr_2
-> Result
-> Gather
Workers Planned: 4
-> Partial Aggregate
-> Parallel Tid Range Scan on parallel_tidrangescan t
TID Cond: (ctid >= (InitPlan expr_2).col1)
(10 rows)
SELECT r.lo,
(SELECT count(*) FROM parallel_tidrangescan t
WHERE t.ctid >= (SELECT r.lo)) c
FROM (VALUES ('(30,0)'::tid), ('(10,0)'), ('(0,0)')) r(lo);
lo | c
--------+-----
(30,0) | 50
(10,0) | 150
(0,0) | 200
(3 rows)
ROLLBACK;
RESET enable_seqscan;

View File

@@ -153,5 +153,17 @@ SELECT t.ctid,t2.c FROM parallel_tidrangescan t,
LATERAL (SELECT count(*) c FROM parallel_tidrangescan t2 WHERE t2.ctid <= t.ctid) t2
WHERE t.ctid < '(1,0)';
-- Parallel rescans with a different TID range on each rescan
EXPLAIN (COSTS OFF)
SELECT r.lo,
(SELECT count(*) FROM parallel_tidrangescan t
WHERE t.ctid >= (SELECT r.lo)) c
FROM (VALUES ('(30,0)'::tid), ('(10,0)'), ('(0,0)')) r(lo);
SELECT r.lo,
(SELECT count(*) FROM parallel_tidrangescan t
WHERE t.ctid >= (SELECT r.lo)) c
FROM (VALUES ('(30,0)'::tid), ('(10,0)'), ('(0,0)')) r(lo);
ROLLBACK;
RESET enable_seqscan;