Fix hypothetical bug in heap backward scans
authorDavid Rowley <[email protected]>
Mon, 25 Jan 2021 06:52:18 +0000 (19:52 +1300)
committerDavid Rowley <[email protected]>
Mon, 25 Jan 2021 06:52:18 +0000 (19:52 +1300)
Both heapgettup() and heapgettup_pagemode() incorrectly set the first page
to scan in a backward scan in which the number of pages to scan was
specified by heap_setscanlimits().  The code incorrectly started the scan
at the end of the relation when startBlk was 0, or otherwise at
startBlk - 1, neither of which is correct when only scanning a subset of
pages.

The fix here checks if heap_setscanlimits() has changed the number of
pages to scan and if so we set the first page to scan as the final page in
the specified range during backward scans.

Proper adjustment of this code was forgotten when heap_setscanlimits() was
added in 7516f5259 back in 9.5.  However, practice, nowhere in core code
performs backward scans after having used heap_setscanlimits(), yet, it is
possible an extension uses the heap functions in this way, hence
backpatch.

An upcoming patch does use heap_setscanlimits() with backward scans, so
this must be fixed before that can go in.

Author: David Rowley
Discussion: https://postgr.es/m/CAApHDvpGc9h0_oVD2CtgBcxCS1N-qDYZSeBRnUh+0CWJA9cMaA@mail.gmail.com
Backpatch-through: 9.5, all supported versions

src/backend/access/heap/heapam.c

index d107e14690c7e50fcd52fdc07c1b6bfd6170c63b..9926e2bd546ae01ef9de976638b02646eb07e11f 100644 (file)
@@ -603,8 +603,14 @@ heapgettup(HeapScanDesc scan,
             * forward scanners.
             */
            scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
-           /* start from last page of the scan */
-           if (scan->rs_startblock > 0)
+
+           /*
+            * Start from last page of the scan.  Ensure we take into account
+            * rs_numblocks if it's been adjusted by heap_setscanlimits().
+            */
+           if (scan->rs_numblocks != InvalidBlockNumber)
+               page = (scan->rs_startblock + scan->rs_numblocks - 1) % scan->rs_nblocks;
+           else if (scan->rs_startblock > 0)
                page = scan->rs_startblock - 1;
            else
                page = scan->rs_nblocks - 1;
@@ -918,8 +924,14 @@ heapgettup_pagemode(HeapScanDesc scan,
             * forward scanners.
             */
            scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
-           /* start from last page of the scan */
-           if (scan->rs_startblock > 0)
+
+           /*
+            * Start from last page of the scan.  Ensure we take into account
+            * rs_numblocks if it's been adjusted by heap_setscanlimits().
+            */
+           if (scan->rs_numblocks != InvalidBlockNumber)
+               page = (scan->rs_startblock + scan->rs_numblocks - 1) % scan->rs_nblocks;
+           else if (scan->rs_startblock > 0)
                page = scan->rs_startblock - 1;
            else
                page = scan->rs_nblocks - 1;