Refactor heapam.c adding heapgettup_initial_block function
authorDavid Rowley <drowley@postgresql.org>
Thu, 2 Feb 2023 01:17:15 +0000 (14:17 +1300)
committerDavid Rowley <drowley@postgresql.org>
Thu, 2 Feb 2023 01:17:15 +0000 (14:17 +1300)
Here we adjust heapgettup() and heapgettup_pagemode() to move the code
that fetches the first block number to scan out into a helper function.
This removes some code duplication.

Author: Melanie Plageman
Reviewed-by: David Rowley
Discussion: https://postgr.es/m/CAAKRu_bvkhka0CZQun28KTqhuUh5ZqY=_T8QEqZqOL02rpi2bw@mail.gmail.com

src/backend/access/heap/heapam.c

index 0a8bac25f59d53f522a92f3e94f52ff0cc4dbe82..b34e20a71ff0e69b075e7f9e2b1ea6c2a3ad52bd 100644 (file)
@@ -483,6 +483,67 @@ heapgetpage(TableScanDesc sscan, BlockNumber block)
        scan->rs_ntuples = ntup;
 }
 
+/*
+ * heapgettup_initial_block - return the first BlockNumber to scan
+ *
+ * Returns InvalidBlockNumber when there are no blocks to scan.  This can
+ * occur with empty tables and in parallel scans when parallel workers get all
+ * of the pages before we can get a chance to get our first page.
+ */
+static BlockNumber
+heapgettup_initial_block(HeapScanDesc scan, ScanDirection dir)
+{
+       Assert(!scan->rs_inited);
+
+       /* When there are no pages to scan, return InvalidBlockNumber */
+       if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+               return InvalidBlockNumber;
+
+       if (ScanDirectionIsForward(dir))
+       {
+               /* serial scan */
+               if (scan->rs_base.rs_parallel == NULL)
+                       return scan->rs_startblock;
+               else
+               {
+                       /* parallel scan */
+                       table_block_parallelscan_startblock_init(scan->rs_base.rs_rd,
+                                                                                                        scan->rs_parallelworkerdata,
+                                                                                                        (ParallelBlockTableScanDesc) scan->rs_base.rs_parallel);
+
+                       /* may return InvalidBlockNumber if there are no more blocks */
+                       return table_block_parallelscan_nextpage(scan->rs_base.rs_rd,
+                                                                                                        scan->rs_parallelworkerdata,
+                                                                                                        (ParallelBlockTableScanDesc) scan->rs_base.rs_parallel);
+               }
+       }
+       else
+       {
+               /* backward parallel scan not supported */
+               Assert(scan->rs_base.rs_parallel == NULL);
+
+               /*
+                * Disable reporting to syncscan logic in a backwards scan; it's not
+                * very likely anyone else is doing the same thing at the same time,
+                * and much more likely that we'll just bollix things for forward
+                * scanners.
+                */
+               scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
+
+               /*
+                * 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)
+                       return (scan->rs_startblock + scan->rs_numblocks - 1) % scan->rs_nblocks;
+
+               if (scan->rs_startblock > 0)
+                       return scan->rs_startblock - 1;
+
+               return scan->rs_nblocks - 1;
+       }
+}
+
 /* ----------------
  *             heapgettup - fetch next heap tuple
  *
@@ -527,38 +588,19 @@ heapgettup(HeapScanDesc scan,
        {
                if (!scan->rs_inited)
                {
+                       block = heapgettup_initial_block(scan, dir);
+
                        /*
-                        * return null immediately if relation is empty
+                        * Check if we have reached the end of the scan already. This
+                        * could happen if the table is empty or if the parallel workers
+                        * have already finished the scan before we did anything ourselves
                         */
-                       if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+                       if (block == InvalidBlockNumber)
                        {
                                Assert(!BufferIsValid(scan->rs_cbuf));
                                tuple->t_data = NULL;
                                return;
                        }
-                       if (scan->rs_base.rs_parallel != NULL)
-                       {
-                               ParallelBlockTableScanDesc pbscan =
-                               (ParallelBlockTableScanDesc) scan->rs_base.rs_parallel;
-                               ParallelBlockTableScanWorker pbscanwork =
-                               scan->rs_parallelworkerdata;
-
-                               table_block_parallelscan_startblock_init(scan->rs_base.rs_rd,
-                                                                                                                pbscanwork, pbscan);
-
-                               block = table_block_parallelscan_nextpage(scan->rs_base.rs_rd,
-                                                                                                                 pbscanwork, pbscan);
-
-                               /* Other processes might have already finished the scan. */
-                               if (block == InvalidBlockNumber)
-                               {
-                                       Assert(!BufferIsValid(scan->rs_cbuf));
-                                       tuple->t_data = NULL;
-                                       return;
-                               }
-                       }
-                       else
-                               block = scan->rs_startblock;    /* first page */
                        heapgetpage((TableScanDesc) scan, block);
                        lineoff = FirstOffsetNumber;    /* first offnum */
                        scan->rs_inited = true;
@@ -582,60 +624,40 @@ heapgettup(HeapScanDesc scan,
        }
        else
        {
-               /* backward parallel scan not supported */
-               Assert(scan->rs_base.rs_parallel == NULL);
-
                if (!scan->rs_inited)
                {
+                       block = heapgettup_initial_block(scan, dir);
+
                        /*
-                        * return null immediately if relation is empty
+                        * Check if we have reached the end of the scan already. This
+                        * could happen if the table is empty.
                         */
-                       if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+                       if (block == InvalidBlockNumber)
                        {
                                Assert(!BufferIsValid(scan->rs_cbuf));
                                tuple->t_data = NULL;
                                return;
                        }
 
-                       /*
-                        * Disable reporting to syncscan logic in a backwards scan; it's
-                        * not very likely anyone else is doing the same thing at the same
-                        * time, and much more likely that we'll just bollix things for
-                        * forward scanners.
-                        */
-                       scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
-
-                       /*
-                        * 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)
-                               block = (scan->rs_startblock + scan->rs_numblocks - 1) % scan->rs_nblocks;
-                       else if (scan->rs_startblock > 0)
-                               block = scan->rs_startblock - 1;
-                       else
-                               block = scan->rs_nblocks - 1;
                        heapgetpage((TableScanDesc) scan, block);
+                       LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+
+                       page = BufferGetPage(scan->rs_cbuf);
+                       TestForOldSnapshot(snapshot, scan->rs_base.rs_rd, page);
+                       lines = PageGetMaxOffsetNumber(page);
+                       lineoff = lines;        /* final offnum */
+                       scan->rs_inited = true;
                }
                else
                {
                        /* continue from previously returned page/tuple */
                        block = scan->rs_cblock;        /* current page */
-               }
+                       LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
 
-               LockBuffer(scan->rs_cbuf, BUFFER_LOCK_SHARE);
+                       page = BufferGetPage(scan->rs_cbuf);
+                       TestForOldSnapshot(snapshot, scan->rs_base.rs_rd, page);
+                       lines = PageGetMaxOffsetNumber(page);
 
-               page = BufferGetPage(scan->rs_cbuf);
-               TestForOldSnapshot(snapshot, scan->rs_base.rs_rd, page);
-               lines = PageGetMaxOffsetNumber(page);
-
-               if (!scan->rs_inited)
-               {
-                       lineoff = lines;        /* final offnum */
-                       scan->rs_inited = true;
-               }
-               else
-               {
                        /*
                         * The previous returned tuple may have been vacuumed since the
                         * previous scan when we use a non-MVCC snapshot, so we must
@@ -837,38 +859,19 @@ heapgettup_pagemode(HeapScanDesc scan,
        {
                if (!scan->rs_inited)
                {
+                       block = heapgettup_initial_block(scan, dir);
+
                        /*
-                        * return null immediately if relation is empty
+                        * Check if we have reached the end of the scan already. This
+                        * could happen if the table is empty or if the parallel workers
+                        * have already finished the scan before we did anything ourselves
                         */
-                       if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+                       if (block == InvalidBlockNumber)
                        {
                                Assert(!BufferIsValid(scan->rs_cbuf));
                                tuple->t_data = NULL;
                                return;
                        }
-                       if (scan->rs_base.rs_parallel != NULL)
-                       {
-                               ParallelBlockTableScanDesc pbscan =
-                               (ParallelBlockTableScanDesc) scan->rs_base.rs_parallel;
-                               ParallelBlockTableScanWorker pbscanwork =
-                               scan->rs_parallelworkerdata;
-
-                               table_block_parallelscan_startblock_init(scan->rs_base.rs_rd,
-                                                                                                                pbscanwork, pbscan);
-
-                               block = table_block_parallelscan_nextpage(scan->rs_base.rs_rd,
-                                                                                                                 pbscanwork, pbscan);
-
-                               /* Other processes might have already finished the scan. */
-                               if (block == InvalidBlockNumber)
-                               {
-                                       Assert(!BufferIsValid(scan->rs_cbuf));
-                                       tuple->t_data = NULL;
-                                       return;
-                               }
-                       }
-                       else
-                               block = scan->rs_startblock;    /* first page */
                        heapgetpage((TableScanDesc) scan, block);
                        lineindex = 0;
                        scan->rs_inited = true;
@@ -889,58 +892,36 @@ heapgettup_pagemode(HeapScanDesc scan,
        }
        else
        {
-               /* backward parallel scan not supported */
-               Assert(scan->rs_base.rs_parallel == NULL);
-
                if (!scan->rs_inited)
                {
+                       block = heapgettup_initial_block(scan, dir);
+
                        /*
-                        * return null immediately if relation is empty
+                        * Check if we have reached the end of the scan already. This
+                        * could happen if the table is empty.
                         */
-                       if (scan->rs_nblocks == 0 || scan->rs_numblocks == 0)
+                       if (block == InvalidBlockNumber)
                        {
                                Assert(!BufferIsValid(scan->rs_cbuf));
                                tuple->t_data = NULL;
                                return;
                        }
 
-                       /*
-                        * Disable reporting to syncscan logic in a backwards scan; it's
-                        * not very likely anyone else is doing the same thing at the same
-                        * time, and much more likely that we'll just bollix things for
-                        * forward scanners.
-                        */
-                       scan->rs_base.rs_flags &= ~SO_ALLOW_SYNC;
-
-                       /*
-                        * 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)
-                               block = (scan->rs_startblock + scan->rs_numblocks - 1) % scan->rs_nblocks;
-                       else if (scan->rs_startblock > 0)
-                               block = scan->rs_startblock - 1;
-                       else
-                               block = scan->rs_nblocks - 1;
                        heapgetpage((TableScanDesc) scan, block);
+                       page = BufferGetPage(scan->rs_cbuf);
+                       TestForOldSnapshot(scan->rs_base.rs_snapshot, scan->rs_base.rs_rd, page);
+                       lines = scan->rs_ntuples;
+                       lineindex = lines - 1;
+                       scan->rs_inited = true;
                }
                else
                {
                        /* continue from previously returned page/tuple */
                        block = scan->rs_cblock;        /* current page */
-               }
-
-               page = BufferGetPage(scan->rs_cbuf);
-               TestForOldSnapshot(scan->rs_base.rs_snapshot, scan->rs_base.rs_rd, page);
-               lines = scan->rs_ntuples;
 
-               if (!scan->rs_inited)
-               {
-                       lineindex = lines - 1;
-                       scan->rs_inited = true;
-               }
-               else
-               {
+                       page = BufferGetPage(scan->rs_cbuf);
+                       TestForOldSnapshot(scan->rs_base.rs_snapshot, scan->rs_base.rs_rd, page);
+                       lines = scan->rs_ntuples;
                        lineindex = scan->rs_cindex - 1;
                }
                /* block and lineindex now reference the previous visible tid */