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> Discussion: https://postgr.es/m/6b50233fcc0f7d6f4845f083777fa9702d9612e4.camel@j-davis.com Backpatch-through: 14
This commit is contained in:
@@ -767,8 +767,14 @@ heapgettup(HeapScanDesc scan,
|
||||
*/
|
||||
if (backward)
|
||||
{
|
||||
finished = (page == scan->rs_startblock) ||
|
||||
(scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
finished = (page == scan->rs_startblock);
|
||||
|
||||
if (page == 0)
|
||||
page = scan->rs_nblocks;
|
||||
page--;
|
||||
@@ -789,8 +795,19 @@ heapgettup(HeapScanDesc scan,
|
||||
page++;
|
||||
if (page >= scan->rs_nblocks)
|
||||
page = 0;
|
||||
finished = (page == scan->rs_startblock) ||
|
||||
(scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
|
||||
|
||||
if (page == scan->rs_startblock)
|
||||
finished = true;
|
||||
else if (scan->rs_numblocks != InvalidBlockNumber)
|
||||
{
|
||||
BlockNumber endblock;
|
||||
|
||||
endblock = (scan->rs_startblock + scan->rs_numblocks) %
|
||||
scan->rs_nblocks;
|
||||
finished = (page == endblock);
|
||||
}
|
||||
else
|
||||
finished = false;
|
||||
|
||||
/*
|
||||
* Report our new scan position for synchronization purposes. We
|
||||
@@ -1076,8 +1093,13 @@ heapgettup_pagemode(HeapScanDesc scan,
|
||||
*/
|
||||
if (backward)
|
||||
{
|
||||
finished = (page == scan->rs_startblock) ||
|
||||
(scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
finished = (page == scan->rs_startblock);
|
||||
if (page == 0)
|
||||
page = scan->rs_nblocks;
|
||||
page--;
|
||||
@@ -1098,8 +1120,18 @@ heapgettup_pagemode(HeapScanDesc scan,
|
||||
page++;
|
||||
if (page >= scan->rs_nblocks)
|
||||
page = 0;
|
||||
finished = (page == scan->rs_startblock) ||
|
||||
(scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks == 0 : false);
|
||||
if (page == scan->rs_startblock)
|
||||
finished = true;
|
||||
else if (scan->rs_numblocks != InvalidBlockNumber)
|
||||
{
|
||||
BlockNumber endblock;
|
||||
|
||||
endblock = (scan->rs_startblock + scan->rs_numblocks) %
|
||||
scan->rs_nblocks;
|
||||
finished = (page == endblock);
|
||||
}
|
||||
else
|
||||
finished = false;
|
||||
|
||||
/*
|
||||
* Report our new scan position for synchronization purposes. We
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user