Move BitmapTableScan per-scan setup into a helper
authorMelanie Plageman <[email protected]>
Thu, 30 Jan 2025 20:26:55 +0000 (15:26 -0500)
committerMelanie Plageman <[email protected]>
Thu, 30 Jan 2025 20:28:33 +0000 (15:28 -0500)
Add BitmapTableScanSetup(), a helper which contains all of the code that
must be done on every scan of the table in a bitmap table scan. This
includes scanning the index, building the bitmap, and setting up the
scan descriptors.

Pushing this setup into a helper function makes BitmapHeapNext() more
readable.

Reviewed-by: Nazir Bilal Yavuz <[email protected]>
Discussion: https://postgr.es/m/CAN55FZ1vXu%2BZdT0_MM-i1vbTdfHHf0KR3cK6R5gs6dNNNpyrJw%40mail.gmail.com

src/backend/executor/nodeBitmapHeapscan.c

index be616683f987f71ca7173c2883d74d72696651d9..be0d24d901b5e32fc22d36b9a816ecc2e1373d37 100644 (file)
@@ -48,6 +48,7 @@
 #include "utils/rel.h"
 #include "utils/spccache.h"
 
+static void BitmapTableScanSetup(BitmapHeapScanState *node);
 static TupleTableSlot *BitmapHeapNext(BitmapHeapScanState *node);
 static inline void BitmapDoneInitializingSharedState(ParallelBitmapHeapState *pstate);
 static inline void BitmapAdjustPrefetchIterator(BitmapHeapScanState *node);
@@ -57,6 +58,107 @@ static inline void BitmapPrefetch(BitmapHeapScanState *node,
 static bool BitmapShouldInitializeSharedState(ParallelBitmapHeapState *pstate);
 
 
+/*
+ * Do the underlying index scan, build the bitmap, set up the parallel state
+ * needed for parallel workers to iterate through the bitmap, and set up the
+ * underlying table scan descriptor.
+ *
+ * For prefetching, we use *two* iterators, one for the pages we are actually
+ * scanning and another that runs ahead of the first for prefetching.
+ * node->prefetch_pages tracks exactly how many pages ahead the prefetch
+ * iterator is.  Also, node->prefetch_target tracks the desired prefetch
+ * distance, which starts small and increases up to the
+ * node->prefetch_maximum.  This is to avoid doing a lot of prefetching in a
+ * scan that stops after a few tuples because of a LIMIT.
+ */
+static void
+BitmapTableScanSetup(BitmapHeapScanState *node)
+{
+   TBMIterator tbmiterator = {0};
+   ParallelBitmapHeapState *pstate = node->pstate;
+   dsa_area   *dsa = node->ss.ps.state->es_query_dsa;
+
+   if (!pstate)
+   {
+       node->tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node));
+
+       if (!node->tbm || !IsA(node->tbm, TIDBitmap))
+           elog(ERROR, "unrecognized result from subplan");
+   }
+   else if (BitmapShouldInitializeSharedState(pstate))
+   {
+       /*
+        * The leader will immediately come out of the function, but others
+        * will be blocked until leader populates the TBM and wakes them up.
+        */
+       node->tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node));
+       if (!node->tbm || !IsA(node->tbm, TIDBitmap))
+           elog(ERROR, "unrecognized result from subplan");
+
+       /*
+        * Prepare to iterate over the TBM. This will return the dsa_pointer
+        * of the iterator state which will be used by multiple processes to
+        * iterate jointly.
+        */
+       pstate->tbmiterator = tbm_prepare_shared_iterate(node->tbm);
+
+#ifdef USE_PREFETCH
+       if (node->prefetch_maximum > 0)
+       {
+           pstate->prefetch_iterator =
+               tbm_prepare_shared_iterate(node->tbm);
+       }
+#endif                         /* USE_PREFETCH */
+
+       /* We have initialized the shared state so wake up others. */
+       BitmapDoneInitializingSharedState(pstate);
+   }
+
+   tbmiterator = tbm_begin_iterate(node->tbm, dsa,
+                                   pstate ?
+                                   pstate->tbmiterator :
+                                   InvalidDsaPointer);
+
+#ifdef USE_PREFETCH
+   if (node->prefetch_maximum > 0)
+       node->prefetch_iterator =
+           tbm_begin_iterate(node->tbm, dsa,
+                             pstate ?
+                             pstate->prefetch_iterator :
+                             InvalidDsaPointer);
+#endif                         /* USE_PREFETCH */
+
+   /*
+    * If this is the first scan of the underlying table, create the table
+    * scan descriptor and begin the scan.
+    */
+   if (!node->ss.ss_currentScanDesc)
+   {
+       bool        need_tuples = false;
+
+       /*
+        * We can potentially skip fetching heap pages if we do not need any
+        * columns of the table, either for checking non-indexable quals or
+        * for returning data.  This test is a bit simplistic, as it checks
+        * the stronger condition that there's no qual or return tlist at all.
+        * But in most cases it's probably not worth working harder than that.
+        */
+       need_tuples = (node->ss.ps.plan->qual != NIL ||
+                      node->ss.ps.plan->targetlist != NIL);
+
+       node->ss.ss_currentScanDesc =
+           table_beginscan_bm(node->ss.ss_currentRelation,
+                              node->ss.ps.state->es_snapshot,
+                              0,
+                              NULL,
+                              need_tuples);
+   }
+
+   node->ss.ss_currentScanDesc->st.rs_tbmiterator = tbmiterator;
+   node->initialized = true;
+}
+
+
 /* ----------------------------------------------------------------
  *     BitmapHeapNext
  *
@@ -68,10 +170,11 @@ BitmapHeapNext(BitmapHeapScanState *node)
 {
    ExprContext *econtext;
    TableScanDesc scan;
-   TIDBitmap  *tbm;
    TupleTableSlot *slot;
+
+#ifdef USE_PREFETCH
    ParallelBitmapHeapState *pstate = node->pstate;
-   dsa_area   *dsa = node->ss.ps.state->es_query_dsa;
+#endif
 
    /*
     * extract necessary information from index scan node
@@ -79,110 +182,15 @@ BitmapHeapNext(BitmapHeapScanState *node)
    econtext = node->ss.ps.ps_ExprContext;
    slot = node->ss.ss_ScanTupleSlot;
    scan = node->ss.ss_currentScanDesc;
-   tbm = node->tbm;
 
    /*
     * If we haven't yet performed the underlying index scan, do it, and begin
     * the iteration over the bitmap.
-    *
-    * For prefetching, we use *two* iterators, one for the pages we are
-    * actually scanning and another that runs ahead of the first for
-    * prefetching.  node->prefetch_pages tracks exactly how many pages ahead
-    * the prefetch iterator is.  Also, node->prefetch_target tracks the
-    * desired prefetch distance, which starts small and increases up to the
-    * node->prefetch_maximum.  This is to avoid doing a lot of prefetching in
-    * a scan that stops after a few tuples because of a LIMIT.
     */
    if (!node->initialized)
    {
-       TBMIterator tbmiterator;
-
-       if (!pstate)
-       {
-           tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node));
-
-           if (!tbm || !IsA(tbm, TIDBitmap))
-               elog(ERROR, "unrecognized result from subplan");
-
-           node->tbm = tbm;
-       }
-       else if (BitmapShouldInitializeSharedState(pstate))
-       {
-           /*
-            * The leader will immediately come out of the function, but
-            * others will be blocked until leader populates the TBM and wakes
-            * them up.
-            */
-           tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node));
-           if (!tbm || !IsA(tbm, TIDBitmap))
-               elog(ERROR, "unrecognized result from subplan");
-
-           node->tbm = tbm;
-
-           /*
-            * Prepare to iterate over the TBM. This will return the
-            * dsa_pointer of the iterator state which will be used by
-            * multiple processes to iterate jointly.
-            */
-           pstate->tbmiterator = tbm_prepare_shared_iterate(tbm);
-
-#ifdef USE_PREFETCH
-           if (node->prefetch_maximum > 0)
-           {
-               pstate->prefetch_iterator =
-                   tbm_prepare_shared_iterate(tbm);
-           }
-#endif                         /* USE_PREFETCH */
-
-           /* We have initialized the shared state so wake up others. */
-           BitmapDoneInitializingSharedState(pstate);
-       }
-
-       tbmiterator = tbm_begin_iterate(tbm, dsa,
-                                       pstate ?
-                                       pstate->tbmiterator :
-                                       InvalidDsaPointer);
-
-#ifdef USE_PREFETCH
-       if (node->prefetch_maximum > 0)
-           node->prefetch_iterator =
-               tbm_begin_iterate(tbm, dsa,
-                                 pstate ?
-                                 pstate->prefetch_iterator :
-                                 InvalidDsaPointer);
-#endif                         /* USE_PREFETCH */
-
-       /*
-        * If this is the first scan of the underlying table, create the table
-        * scan descriptor and begin the scan.
-        */
-       if (!scan)
-       {
-           bool        need_tuples = false;
-
-           /*
-            * We can potentially skip fetching heap pages if we do not need
-            * any columns of the table, either for checking non-indexable
-            * quals or for returning data.  This test is a bit simplistic, as
-            * it checks the stronger condition that there's no qual or return
-            * tlist at all. But in most cases it's probably not worth working
-            * harder than that.
-            */
-           need_tuples = (node->ss.ps.plan->qual != NIL ||
-                          node->ss.ps.plan->targetlist != NIL);
-
-           scan = table_beginscan_bm(node->ss.ss_currentRelation,
-                                     node->ss.ps.state->es_snapshot,
-                                     0,
-                                     NULL,
-                                     need_tuples);
-
-           node->ss.ss_currentScanDesc = scan;
-       }
-
-       scan->st.rs_tbmiterator = tbmiterator;
-       node->initialized = true;
-
+       BitmapTableScanSetup(node);
+       scan = node->ss.ss_currentScanDesc;
        goto new_page;
    }