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

Fix incorrect block accounting in TID Range Scans

heapgettup_advance_block() (or heapgettup() and heapgettup_pagemode() in
versions before v16) would incorrectly decrement rs_numblocks when
advancing to the next block.  Ordinarily this was fine, but if the TID
Range scan plan was used by a scrollable cursor and the cursor was fetched
backwards enough to result in another block being required, then
rs_numblocks may have reached 0 prematurely resulting in the scan ending
prematurely.

Reported-by: Jeff Davis <pgsql@j-davis.com>
Author: Ewan Young <kdbase.hack@gmail.com>
Author: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/6b50233fcc0f7d6f4845f083777fa9702d9612e4.camel@j-davis.com
Backpatch-through: 14
This commit is contained in:
David Rowley
2026-09-24 01:20:27 +12:00
parent dede2443ce
commit 25ebf0097f
3 changed files with 49 additions and 9 deletions

View File

@@ -899,7 +899,11 @@ heapgettup_advance_block(HeapScanDesc scan, BlockNumber block, ScanDirection dir
/* check if the limit imposed by heap_setscanlimits() is met */
if (scan->rs_numblocks != InvalidBlockNumber)
{
if (--scan->rs_numblocks == 0)
BlockNumber endblock;
endblock = (scan->rs_startblock + scan->rs_numblocks) %
scan->rs_nblocks;
if (block == endblock)
return InvalidBlockNumber;
}
@@ -907,17 +911,15 @@ heapgettup_advance_block(HeapScanDesc scan, BlockNumber block, ScanDirection dir
}
else
{
/* we're done if the last block is the start position */
/*
* We're done if the last block is the start position. No need to
* check if rs_numblocks was set by heap_setscanlimits() as that only
* changes the end block. The start block is the same with or without
* scan limits.
*/
if (block == scan->rs_startblock)
return InvalidBlockNumber;
/* check if the limit imposed by heap_setscanlimits() is met */
if (scan->rs_numblocks != InvalidBlockNumber)
{
if (--scan->rs_numblocks == 0)
return InvalidBlockNumber;
}
/* wrap to the end of the heap when the last page was page 0 */
if (block == 0)
block = scan->rs_nblocks;

View File

@@ -295,6 +295,32 @@ FETCH LAST c;
(0,10)
(1 row)
COMMIT;
-- Ensure TID Range Scans with scrollable cursors count blocks correctly.
BEGIN;
DECLARE c SCROLL CURSOR FOR SELECT ctid FROM tidrangescan WHERE ctid >= '(0,1)' AND ctid <= '(2,10)';
MOVE FORWARD 25 c;
MOVE BACKWARD 4 c;
FETCH BACKWARD 1 c;
ctid
--------
(1,10)
(1 row)
MOVE BACKWARD ALL c;
FETCH FORWARD 1 c;
ctid
-------
(0,1)
(1 row)
MOVE FORWARD ALL c;
FETCH BACKWARD 1 c;
ctid
--------
(2,10)
(1 row)
COMMIT;
DROP TABLE tidrangescan;
RESET enable_seqscan;

View File

@@ -96,6 +96,18 @@ FETCH FIRST c;
FETCH LAST c;
COMMIT;
-- Ensure TID Range Scans with scrollable cursors count blocks correctly.
BEGIN;
DECLARE c SCROLL CURSOR FOR SELECT ctid FROM tidrangescan WHERE ctid >= '(0,1)' AND ctid <= '(2,10)';
MOVE FORWARD 25 c;
MOVE BACKWARD 4 c;
FETCH BACKWARD 1 c;
MOVE BACKWARD ALL c;
FETCH FORWARD 1 c;
MOVE FORWARD ALL c;
FETCH BACKWARD 1 c;
COMMIT;
DROP TABLE tidrangescan;
RESET enable_seqscan;