diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index a2c2745d148..0bb9cbc48a6 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -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 diff --git a/src/test/regress/expected/tidrangescan.out b/src/test/regress/expected/tidrangescan.out index 721f3b94e04..3ccef0fd0c2 100644 --- a/src/test/regress/expected/tidrangescan.out +++ b/src/test/regress/expected/tidrangescan.out @@ -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; diff --git a/src/test/regress/sql/tidrangescan.sql b/src/test/regress/sql/tidrangescan.sql index ac09ebb6262..e367292e885 100644 --- a/src/test/regress/sql/tidrangescan.sql +++ b/src/test/regress/sql/tidrangescan.sql @@ -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;