Refactor heap_page_prune_and_freeze() parameters into a struct
authorMelanie Plageman <melanieplageman@gmail.com>
Thu, 20 Nov 2025 15:30:43 +0000 (10:30 -0500)
committerMelanie Plageman <melanieplageman@gmail.com>
Thu, 20 Nov 2025 15:32:14 +0000 (10:32 -0500)
heap_page_prune_and_freeze() had accumulated an unwieldy number of input
parameters and upcoming work to handle VM updates in this function will
add even more.

Introduce a new PruneFreezeParams struct to group the function’s input
parameters, improving readability and maintainability.

Author: Melanie Plageman <melanieplageman@gmail.com>
Reviewed-by: Kirill Reshke <reshkekirill@gmail.com>
Discussion: https://postgr.es/m/yn4zp35kkdsjx6wf47zcfmxgexxt4h2og47pvnw2x5ifyrs3qc%407uw6jyyxuyf7

src/backend/access/heap/pruneheap.c
src/backend/access/heap/vacuumlazy.c
src/include/access/heapam.h
src/tools/pgindent/typedefs.list

index 231bea679c68d7a8548a46324ad2b5c6612881a6..e9e14cb42b7a695345b308c6f1770aebee627947 100644 (file)
@@ -261,12 +261,18 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
            PruneFreezeResult presult;
 
            /*
-            * For now, pass mark_unused_now as false regardless of whether or
-            * not the relation has indexes, since we cannot safely determine
-            * that during on-access pruning with the current implementation.
+            * We don't pass the HEAP_PAGE_PRUNE_MARK_UNUSED_NOW option
+            * regardless of whether or not the relation has indexes, since we
+            * cannot safely determine that during on-access pruning with the
+            * current implementation.
             */
-           heap_page_prune_and_freeze(relation, buffer, vistest, 0,
-                                      NULL, &presult, PRUNE_ON_ACCESS, &dummy_off_loc, NULL, NULL);
+           PruneFreezeParams params = {.relation = relation,.buffer = buffer,
+               .reason = PRUNE_ON_ACCESS,.options = 0,
+               .vistest = vistest,.cutoffs = NULL
+           };
+
+           heap_page_prune_and_freeze(&params, &presult, &dummy_off_loc,
+                                      NULL, NULL);
 
            /*
             * Report the number of tuples reclaimed to pgstats.  This is
@@ -419,60 +425,44 @@ heap_page_will_freeze(Relation relation, Buffer buffer,
  * also need to account for a reduction in the length of the line pointer
  * array following array truncation by us.
  *
- * If the HEAP_PRUNE_FREEZE option is set, we will also freeze tuples if it's
- * required in order to advance relfrozenxid / relminmxid, or if it's
- * considered advantageous for overall system performance to do so now.  The
- * 'cutoffs', 'presult', 'new_relfrozen_xid' and 'new_relmin_mxid' arguments
- * are required when freezing.  When HEAP_PRUNE_FREEZE option is set, we also
- * set presult->all_visible and presult->all_frozen on exit, to indicate if
- * the VM bits can be set.  They are always set to false when the
- * HEAP_PRUNE_FREEZE option is not set, because at the moment only callers
- * that also freeze need that information.
- *
- * vistest is used to distinguish whether tuples are DEAD or RECENTLY_DEAD
- * (see heap_prune_satisfies_vacuum).
- *
- * options:
- *   MARK_UNUSED_NOW indicates that dead items can be set LP_UNUSED during
- *   pruning.
+ * params contains the input parameters used to control freezing and pruning
+ * behavior. See the definition of PruneFreezeParams for more on what each
+ * parameter does.
  *
- *   FREEZE indicates that we will also freeze tuples, and will return
- *   'all_visible', 'all_frozen' flags to the caller.
- *
- * cutoffs contains the freeze cutoffs, established by VACUUM at the beginning
- * of vacuuming the relation.  Required if HEAP_PRUNE_FREEZE option is set.
- * cutoffs->OldestXmin is also used to determine if dead tuples are
- * HEAPTUPLE_RECENTLY_DEAD or HEAPTUPLE_DEAD.
+ * If the HEAP_PAGE_PRUNE_FREEZE option is set in params, we will freeze
+ * tuples if it's required in order to advance relfrozenxid / relminmxid, or
+ * if it's considered advantageous for overall system performance to do so
+ * now.  The 'params.cutoffs', 'presult', 'new_relfrozen_xid' and
+ * 'new_relmin_mxid' arguments are required when freezing.  When
+ * HEAP_PAGE_PRUNE_FREEZE option is passed, we also set presult->all_visible
+ * and presult->all_frozen on exit, to indicate if the VM bits can be set.
+ * They are always set to false when the HEAP_PAGE_PRUNE_FREEZE option is not
+ * passed, because at the moment only callers that also freeze need that
+ * information.
  *
  * presult contains output parameters needed by callers, such as the number of
  * tuples removed and the offsets of dead items on the page after pruning.
  * heap_page_prune_and_freeze() is responsible for initializing it.  Required
  * by all callers.
  *
- * reason indicates why the pruning is performed.  It is included in the WAL
- * record for debugging and analysis purposes, but otherwise has no effect.
- *
  * off_loc is the offset location required by the caller to use in error
  * callback.
  *
  * new_relfrozen_xid and new_relmin_mxid must provided by the caller if the
- * HEAP_PRUNE_FREEZE option is set.  On entry, they contain the oldest XID and
- * multi-XID seen on the relation so far.  They will be updated with oldest
- * values present on the page after pruning.  After processing the whole
- * relation, VACUUM can use these values as the new relfrozenxid/relminmxid
- * for the relation.
+ * HEAP_PAGE_PRUNE_FREEZE option is set in params.  On entry, they contain the
+ * oldest XID and multi-XID seen on the relation so far.  They will be updated
+ * with oldest values present on the page after pruning.  After processing the
+ * whole relation, VACUUM can use these values as the new
+ * relfrozenxid/relminmxid for the relation.
  */
 void
-heap_page_prune_and_freeze(Relation relation, Buffer buffer,
-                          GlobalVisState *vistest,
-                          int options,
-                          struct VacuumCutoffs *cutoffs,
+heap_page_prune_and_freeze(PruneFreezeParams *params,
                           PruneFreezeResult *presult,
-                          PruneReason reason,
                           OffsetNumber *off_loc,
                           TransactionId *new_relfrozen_xid,
                           MultiXactId *new_relmin_mxid)
 {
+   Buffer      buffer = params->buffer;
    Page        page = BufferGetPage(buffer);
    BlockNumber blockno = BufferGetBlockNumber(buffer);
    OffsetNumber offnum,
@@ -486,10 +476,11 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
    int64       fpi_before = pgWalUsage.wal_fpi;
 
    /* Copy parameters to prstate */
-   prstate.vistest = vistest;
-   prstate.mark_unused_now = (options & HEAP_PAGE_PRUNE_MARK_UNUSED_NOW) != 0;
-   prstate.attempt_freeze = (options & HEAP_PAGE_PRUNE_FREEZE) != 0;
-   prstate.cutoffs = cutoffs;
+   prstate.vistest = params->vistest;
+   prstate.mark_unused_now =
+       (params->options & HEAP_PAGE_PRUNE_MARK_UNUSED_NOW) != 0;
+   prstate.attempt_freeze = (params->options & HEAP_PAGE_PRUNE_FREEZE) != 0;
+   prstate.cutoffs = params->cutoffs;
 
    /*
     * Our strategy is to scan the page and make lists of items to change,
@@ -583,7 +574,7 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
    prstate.visibility_cutoff_xid = InvalidTransactionId;
 
    maxoff = PageGetMaxOffsetNumber(page);
-   tup.t_tableOid = RelationGetRelid(relation);
+   tup.t_tableOid = RelationGetRelid(params->relation);
 
    /*
     * Determine HTSV for all tuples, and queue them up for processing as HOT
@@ -786,7 +777,7 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
     * Decide if we want to go ahead with freezing according to the freeze
     * plans we prepared, or not.
     */
-   do_freeze = heap_page_will_freeze(relation, buffer,
+   do_freeze = heap_page_will_freeze(params->relation, buffer,
                                      did_tuple_hint_fpi,
                                      do_prune,
                                      do_hint_prune,
@@ -838,7 +829,7 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
        /*
         * Emit a WAL XLOG_HEAP2_PRUNE* record showing what we did
         */
-       if (RelationNeedsWAL(relation))
+       if (RelationNeedsWAL(params->relation))
        {
            /*
             * The snapshotConflictHorizon for the whole record should be the
@@ -876,11 +867,11 @@ heap_page_prune_and_freeze(Relation relation, Buffer buffer,
            else
                conflict_xid = prstate.latest_xid_removed;
 
-           log_heap_prune_and_freeze(relation, buffer,
+           log_heap_prune_and_freeze(params->relation, buffer,
                                      InvalidBuffer,    /* vmbuffer */
                                      0,    /* vmflags */
                                      conflict_xid,
-                                     true, reason,
+                                     true, params->reason,
                                      prstate.frozen, prstate.nfrozen,
                                      prstate.redirected, prstate.nredirected,
                                      prstate.nowdead, prstate.ndead,
index deb9a3dc0d1ad15141af444b493e06a8d8e15420..c3fc1098e232962b33b55a0a828536adeb6b47ae 100644 (file)
@@ -1965,7 +1965,10 @@ lazy_scan_prune(LVRelState *vacrel,
 {
    Relation    rel = vacrel->rel;
    PruneFreezeResult presult;
-   int         prune_options = 0;
+   PruneFreezeParams params = {.relation = rel,.buffer = buf,
+       .reason = PRUNE_VACUUM_SCAN,.options = HEAP_PAGE_PRUNE_FREEZE,
+       .vistest = vacrel->vistest,.cutoffs = &vacrel->cutoffs
+   };
 
    Assert(BufferGetBlockNumber(buf) == blkno);
 
@@ -1984,12 +1987,11 @@ lazy_scan_prune(LVRelState *vacrel,
     * tuples. Pruning will have determined whether or not the page is
     * all-visible.
     */
-   prune_options = HEAP_PAGE_PRUNE_FREEZE;
    if (vacrel->nindexes == 0)
-       prune_options |= HEAP_PAGE_PRUNE_MARK_UNUSED_NOW;
+       params.options |= HEAP_PAGE_PRUNE_MARK_UNUSED_NOW;
 
-   heap_page_prune_and_freeze(rel, buf, vacrel->vistest, prune_options,
-                              &vacrel->cutoffs, &presult, PRUNE_VACUUM_SCAN,
+   heap_page_prune_and_freeze(&params,
+                              &presult,
                               &vacrel->offnum,
                               &vacrel->NewRelfrozenXid, &vacrel->NewRelminMxid);
 
index 909db73b7bbbfa0e4d066197698b09864d45a6cb..632c4332a8c347e7da311aad428efa3a30ca827e 100644 (file)
@@ -221,6 +221,56 @@ typedef struct HeapPageFreeze
 
 } HeapPageFreeze;
 
+
+/* 'reason' codes for heap_page_prune_and_freeze() */
+typedef enum
+{
+   PRUNE_ON_ACCESS,            /* on-access pruning */
+   PRUNE_VACUUM_SCAN,          /* VACUUM 1st heap pass */
+   PRUNE_VACUUM_CLEANUP,       /* VACUUM 2nd heap pass */
+} PruneReason;
+
+/*
+ * Input parameters to heap_page_prune_and_freeze()
+ */
+typedef struct PruneFreezeParams
+{
+   Relation    relation;       /* relation containing buffer to be pruned */
+   Buffer      buffer;         /* buffer to be pruned */
+
+   /*
+    * The reason pruning was performed.  It is used to set the WAL record
+    * opcode which is used for debugging and analysis purposes.
+    */
+   PruneReason reason;
+
+   /*
+    * Contains flag bits:
+    *
+    * HEAP_PAGE_PRUNE_MARK_UNUSED_NOW indicates that dead items can be set
+    * LP_UNUSED during pruning.
+    *
+    * HEAP_PAGE_PRUNE_FREEZE indicates that we will also freeze tuples, and
+    * will return 'all_visible', 'all_frozen' flags to the caller.
+    */
+   int         options;
+
+   /*
+    * vistest is used to distinguish whether tuples are DEAD or RECENTLY_DEAD
+    * (see heap_prune_satisfies_vacuum).
+    */
+   GlobalVisState *vistest;
+
+   /*
+    * Contains the cutoffs used for freezing. They are required if the
+    * HEAP_PAGE_PRUNE_FREEZE option is set. cutoffs->OldestXmin is also used
+    * to determine if dead tuples are HEAPTUPLE_RECENTLY_DEAD or
+    * HEAPTUPLE_DEAD. Currently only vacuum passes in cutoffs. Vacuum
+    * calculates them once, at the beginning of vacuuming the relation.
+    */
+   struct VacuumCutoffs *cutoffs;
+} PruneFreezeParams;
+
 /*
  * Per-page state returned by heap_page_prune_and_freeze()
  */
@@ -264,13 +314,6 @@ typedef struct PruneFreezeResult
    OffsetNumber deadoffsets[MaxHeapTuplesPerPage];
 } PruneFreezeResult;
 
-/* 'reason' codes for heap_page_prune_and_freeze() */
-typedef enum
-{
-   PRUNE_ON_ACCESS,            /* on-access pruning */
-   PRUNE_VACUUM_SCAN,          /* VACUUM 1st heap pass */
-   PRUNE_VACUUM_CLEANUP,       /* VACUUM 2nd heap pass */
-} PruneReason;
 
 /* ----------------
  *     function prototypes for heap access method
@@ -367,12 +410,8 @@ extern TransactionId heap_index_delete_tuples(Relation rel,
 
 /* in heap/pruneheap.c */
 extern void heap_page_prune_opt(Relation relation, Buffer buffer);
-extern void heap_page_prune_and_freeze(Relation relation, Buffer buffer,
-                                      GlobalVisState *vistest,
-                                      int options,
-                                      struct VacuumCutoffs *cutoffs,
+extern void heap_page_prune_and_freeze(PruneFreezeParams *params,
                                       PruneFreezeResult *presult,
-                                      PruneReason reason,
                                       OffsetNumber *off_loc,
                                       TransactionId *new_relfrozen_xid,
                                       MultiXactId *new_relmin_mxid);
index 57f2a9ccdc5d4ed5dc9738c99a41390f28c0b4b4..c751c25a04d70a0523f5d44a4167f1b462a88db7 100644 (file)
@@ -2348,6 +2348,7 @@ ProjectionPath
 PromptInterruptContext
 ProtocolVersion
 PrsStorage
+PruneFreezeParams
 PruneFreezeResult
 PruneReason
 PruneState