@@ -212,23 +212,6 @@ typedef struct LVRelState
212212 int64 missed_dead_tuples ; /* # removable, but not removed */
213213} LVRelState ;
214214
215- /*
216- * State returned by lazy_scan_prune()
217- */
218- typedef struct LVPagePruneState
219- {
220- bool has_lpdead_items ; /* includes existing LP_DEAD items */
221-
222- /*
223- * State describes the proper VM bit states to set for the page following
224- * pruning and freezing. all_visible implies !has_lpdead_items, but don't
225- * trust all_frozen result unless all_visible is also set to true.
226- */
227- bool all_visible ; /* Every item visible to all? */
228- bool all_frozen ; /* provided all_visible is also true */
229- TransactionId visibility_cutoff_xid ; /* For recovery conflicts */
230- } LVPagePruneState ;
231-
232215/* Struct for saving and restoring vacuum error information. */
233216typedef struct LVSavedErrInfo
234217{
@@ -250,7 +233,7 @@ static bool lazy_scan_new_or_empty(LVRelState *vacrel, Buffer buf,
250233static void lazy_scan_prune (LVRelState * vacrel , Buffer buf ,
251234 BlockNumber blkno , Page page ,
252235 Buffer vmbuffer , bool all_visible_according_to_vm ,
253- LVPagePruneState * prunestate );
236+ bool * has_lpdead_items );
254237static bool lazy_scan_noprune (LVRelState * vacrel , Buffer buf ,
255238 BlockNumber blkno , Page page ,
256239 bool * has_lpdead_items );
@@ -854,7 +837,7 @@ lazy_scan_heap(LVRelState *vacrel)
854837 Buffer buf ;
855838 Page page ;
856839 bool all_visible_according_to_vm ;
857- LVPagePruneState prunestate ;
840+ bool has_lpdead_items ;
858841
859842 if (blkno == next_unskippable_block )
860843 {
@@ -959,8 +942,6 @@ lazy_scan_heap(LVRelState *vacrel)
959942 page = BufferGetPage (buf );
960943 if (!ConditionalLockBufferForCleanup (buf ))
961944 {
962- bool has_lpdead_items ;
963-
964945 LockBuffer (buf , BUFFER_LOCK_SHARE );
965946
966947 /* Check for new or empty pages before lazy_scan_noprune call */
@@ -1035,7 +1016,7 @@ lazy_scan_heap(LVRelState *vacrel)
10351016 */
10361017 lazy_scan_prune (vacrel , buf , blkno , page ,
10371018 vmbuffer , all_visible_according_to_vm ,
1038- & prunestate );
1019+ & has_lpdead_items );
10391020
10401021 /*
10411022 * Final steps for block: drop cleanup lock, record free space in the
@@ -1056,7 +1037,7 @@ lazy_scan_heap(LVRelState *vacrel)
10561037 */
10571038 if (vacrel -> nindexes == 0
10581039 || !vacrel -> do_index_vacuuming
1059- || !prunestate . has_lpdead_items )
1040+ || !has_lpdead_items )
10601041 {
10611042 Size freespace = PageGetHeapFreeSpace (page );
10621043
@@ -1068,7 +1049,7 @@ lazy_scan_heap(LVRelState *vacrel)
10681049 * visible on upper FSM pages. This is done after vacuuming if the
10691050 * table has indexes.
10701051 */
1071- if (vacrel -> nindexes == 0 && prunestate . has_lpdead_items &&
1052+ if (vacrel -> nindexes == 0 && has_lpdead_items &&
10721053 blkno - next_fsm_block_to_vacuum >= VACUUM_FSM_EVERY_PAGES )
10731054 {
10741055 FreeSpaceMapVacuumRange (vacrel -> rel , next_fsm_block_to_vacuum ,
@@ -1383,6 +1364,14 @@ lazy_scan_new_or_empty(LVRelState *vacrel, Buffer buf, BlockNumber blkno,
13831364 * right after this operation completes instead of in the middle of it. Note that
13841365 * any tuple that becomes dead after the call to heap_page_prune() can't need to
13851366 * be frozen, because it was visible to another session when vacuum started.
1367+ *
1368+ * vmbuffer is the buffer containing the VM block with visibility information
1369+ * for the heap block, blkno. all_visible_according_to_vm is the saved
1370+ * visibility status of the heap block looked up earlier by the caller. We
1371+ * won't rely entirely on this status, as it may be out of date.
1372+ *
1373+ * *has_lpdead_items is set to true or false depending on whether, upon return
1374+ * from this function, any LP_DEAD items are still present on the page.
13861375 */
13871376static void
13881377lazy_scan_prune (LVRelState * vacrel ,
@@ -1391,7 +1380,7 @@ lazy_scan_prune(LVRelState *vacrel,
13911380 Page page ,
13921381 Buffer vmbuffer ,
13931382 bool all_visible_according_to_vm ,
1394- LVPagePruneState * prunestate )
1383+ bool * has_lpdead_items )
13951384{
13961385 Relation rel = vacrel -> rel ;
13971386 OffsetNumber offnum ,
@@ -1404,6 +1393,9 @@ lazy_scan_prune(LVRelState *vacrel,
14041393 recently_dead_tuples ;
14051394 HeapPageFreeze pagefrz ;
14061395 bool hastup = false;
1396+ bool all_visible ,
1397+ all_frozen ;
1398+ TransactionId visibility_cutoff_xid ;
14071399 int64 fpi_before = pgWalUsage .wal_fpi ;
14081400 OffsetNumber deadoffsets [MaxHeapTuplesPerPage ];
14091401 HeapTupleFreeze frozen [MaxHeapTuplesPerPage ];
@@ -1444,14 +1436,22 @@ lazy_scan_prune(LVRelState *vacrel,
14441436 & presult , & vacrel -> offnum );
14451437
14461438 /*
1447- * Now scan the page to collect LP_DEAD items and check for tuples
1448- * requiring freezing among remaining tuples with storage
1439+ * We will update the VM after collecting LP_DEAD items and freezing
1440+ * tuples. Keep track of whether or not the page is all_visible and
1441+ * all_frozen and use this information to update the VM. all_visible
1442+ * implies 0 lpdead_items, but don't trust all_frozen result unless
1443+ * all_visible is also set to true.
1444+ *
1445+ * Also keep track of the visibility cutoff xid for recovery conflicts.
14491446 */
1450- prunestate -> has_lpdead_items = false;
1451- prunestate -> all_visible = true;
1452- prunestate -> all_frozen = true;
1453- prunestate -> visibility_cutoff_xid = InvalidTransactionId ;
1447+ all_visible = true;
1448+ all_frozen = true;
1449+ visibility_cutoff_xid = InvalidTransactionId ;
14541450
1451+ /*
1452+ * Now scan the page to collect LP_DEAD items and update the variables set
1453+ * just above.
1454+ */
14551455 for (offnum = FirstOffsetNumber ;
14561456 offnum <= maxoff ;
14571457 offnum = OffsetNumberNext (offnum ))
@@ -1538,13 +1538,13 @@ lazy_scan_prune(LVRelState *vacrel,
15381538 * asynchronously. See SetHintBits for more info. Check that
15391539 * the tuple is hinted xmin-committed because of that.
15401540 */
1541- if (prunestate -> all_visible )
1541+ if (all_visible )
15421542 {
15431543 TransactionId xmin ;
15441544
15451545 if (!HeapTupleHeaderXminCommitted (htup ))
15461546 {
1547- prunestate -> all_visible = false;
1547+ all_visible = false;
15481548 break ;
15491549 }
15501550
@@ -1556,14 +1556,14 @@ lazy_scan_prune(LVRelState *vacrel,
15561556 if (!TransactionIdPrecedes (xmin ,
15571557 vacrel -> cutoffs .OldestXmin ))
15581558 {
1559- prunestate -> all_visible = false;
1559+ all_visible = false;
15601560 break ;
15611561 }
15621562
15631563 /* Track newest xmin on page. */
1564- if (TransactionIdFollows (xmin , prunestate -> visibility_cutoff_xid ) &&
1564+ if (TransactionIdFollows (xmin , visibility_cutoff_xid ) &&
15651565 TransactionIdIsNormal (xmin ))
1566- prunestate -> visibility_cutoff_xid = xmin ;
1566+ visibility_cutoff_xid = xmin ;
15671567 }
15681568 break ;
15691569 case HEAPTUPLE_RECENTLY_DEAD :
@@ -1574,7 +1574,7 @@ lazy_scan_prune(LVRelState *vacrel,
15741574 * pruning.)
15751575 */
15761576 recently_dead_tuples ++ ;
1577- prunestate -> all_visible = false;
1577+ all_visible = false;
15781578 break ;
15791579 case HEAPTUPLE_INSERT_IN_PROGRESS :
15801580
@@ -1585,11 +1585,11 @@ lazy_scan_prune(LVRelState *vacrel,
15851585 * results. This assumption is a bit shaky, but it is what
15861586 * acquire_sample_rows() does, so be consistent.
15871587 */
1588- prunestate -> all_visible = false;
1588+ all_visible = false;
15891589 break ;
15901590 case HEAPTUPLE_DELETE_IN_PROGRESS :
15911591 /* This is an expected case during concurrent vacuum */
1592- prunestate -> all_visible = false;
1592+ all_visible = false;
15931593
15941594 /*
15951595 * Count such rows as live. As above, we assume the deleting
@@ -1619,7 +1619,7 @@ lazy_scan_prune(LVRelState *vacrel,
16191619 * definitely cannot be set all-frozen in the visibility map later on
16201620 */
16211621 if (!totally_frozen )
1622- prunestate -> all_frozen = false;
1622+ all_frozen = false;
16231623 }
16241624
16251625 /*
@@ -1637,7 +1637,7 @@ lazy_scan_prune(LVRelState *vacrel,
16371637 * page all-frozen afterwards (might not happen until final heap pass).
16381638 */
16391639 if (pagefrz .freeze_required || tuples_frozen == 0 ||
1640- (prunestate -> all_visible && prunestate -> all_frozen &&
1640+ (all_visible && all_frozen &&
16411641 fpi_before != pgWalUsage .wal_fpi ))
16421642 {
16431643 /*
@@ -1675,11 +1675,11 @@ lazy_scan_prune(LVRelState *vacrel,
16751675 * once we're done with it. Otherwise we generate a conservative
16761676 * cutoff by stepping back from OldestXmin.
16771677 */
1678- if (prunestate -> all_visible && prunestate -> all_frozen )
1678+ if (all_visible && all_frozen )
16791679 {
16801680 /* Using same cutoff when setting VM is now unnecessary */
1681- snapshotConflictHorizon = prunestate -> visibility_cutoff_xid ;
1682- prunestate -> visibility_cutoff_xid = InvalidTransactionId ;
1681+ snapshotConflictHorizon = visibility_cutoff_xid ;
1682+ visibility_cutoff_xid = InvalidTransactionId ;
16831683 }
16841684 else
16851685 {
@@ -1702,7 +1702,7 @@ lazy_scan_prune(LVRelState *vacrel,
17021702 */
17031703 vacrel -> NewRelfrozenXid = pagefrz .NoFreezePageRelfrozenXid ;
17041704 vacrel -> NewRelminMxid = pagefrz .NoFreezePageRelminMxid ;
1705- prunestate -> all_frozen = false;
1705+ all_frozen = false;
17061706 tuples_frozen = 0 ; /* avoid miscounts in instrumentation */
17071707 }
17081708
@@ -1715,16 +1715,17 @@ lazy_scan_prune(LVRelState *vacrel,
17151715 */
17161716#ifdef USE_ASSERT_CHECKING
17171717 /* Note that all_frozen value does not matter when !all_visible */
1718- if (prunestate -> all_visible && lpdead_items == 0 )
1718+ if (all_visible && lpdead_items == 0 )
17191719 {
1720- TransactionId cutoff ;
1721- bool all_frozen ;
1720+ TransactionId debug_cutoff ;
1721+ bool debug_all_frozen ;
17221722
1723- if (!heap_page_is_all_visible (vacrel , buf , & cutoff , & all_frozen ))
1723+ if (!heap_page_is_all_visible (vacrel , buf ,
1724+ & debug_cutoff , & debug_all_frozen ))
17241725 Assert (false);
17251726
1726- Assert (!TransactionIdIsValid (cutoff ) ||
1727- cutoff == prunestate -> visibility_cutoff_xid );
1727+ Assert (!TransactionIdIsValid (debug_cutoff ) ||
1728+ debug_cutoff == visibility_cutoff_xid );
17281729 }
17291730#endif
17301731
@@ -1737,7 +1738,6 @@ lazy_scan_prune(LVRelState *vacrel,
17371738 ItemPointerData tmp ;
17381739
17391740 vacrel -> lpdead_item_pages ++ ;
1740- prunestate -> has_lpdead_items = true;
17411741
17421742 ItemPointerSetBlockNumber (& tmp , blkno );
17431743
@@ -1762,7 +1762,7 @@ lazy_scan_prune(LVRelState *vacrel,
17621762 * Now that freezing has been finalized, unset all_visible. It needs
17631763 * to reflect the present state of things, as expected by our caller.
17641764 */
1765- prunestate -> all_visible = false;
1765+ all_visible = false;
17661766 }
17671767
17681768 /* Finally, add page-local counts to whole-VACUUM counts */
@@ -1776,19 +1776,23 @@ lazy_scan_prune(LVRelState *vacrel,
17761776 if (hastup )
17771777 vacrel -> nonempty_pages = blkno + 1 ;
17781778
1779- Assert (!prunestate -> all_visible || !prunestate -> has_lpdead_items );
1779+ /* Did we find LP_DEAD items? */
1780+ * has_lpdead_items = (lpdead_items > 0 );
1781+
1782+ Assert (!all_visible || !(* has_lpdead_items ));
17801783
17811784 /*
17821785 * Handle setting visibility map bit based on information from the VM (as
1783- * of last lazy_scan_skip() call), and from prunestate
1786+ * of last lazy_scan_skip() call), and from all_visible and all_frozen
1787+ * variables
17841788 */
1785- if (!all_visible_according_to_vm && prunestate -> all_visible )
1789+ if (!all_visible_according_to_vm && all_visible )
17861790 {
17871791 uint8 flags = VISIBILITYMAP_ALL_VISIBLE ;
17881792
1789- if (prunestate -> all_frozen )
1793+ if (all_frozen )
17901794 {
1791- Assert (!TransactionIdIsValid (prunestate -> visibility_cutoff_xid ));
1795+ Assert (!TransactionIdIsValid (visibility_cutoff_xid ));
17921796 flags |= VISIBILITYMAP_ALL_FROZEN ;
17931797 }
17941798
@@ -1808,7 +1812,7 @@ lazy_scan_prune(LVRelState *vacrel,
18081812 PageSetAllVisible (page );
18091813 MarkBufferDirty (buf );
18101814 visibilitymap_set (vacrel -> rel , blkno , buf , InvalidXLogRecPtr ,
1811- vmbuffer , prunestate -> visibility_cutoff_xid ,
1815+ vmbuffer , visibility_cutoff_xid ,
18121816 flags );
18131817 }
18141818
@@ -1841,7 +1845,7 @@ lazy_scan_prune(LVRelState *vacrel,
18411845 * There should never be LP_DEAD items on a page with PD_ALL_VISIBLE set,
18421846 * however.
18431847 */
1844- else if (prunestate -> has_lpdead_items && PageIsAllVisible (page ))
1848+ else if (lpdead_items > 0 && PageIsAllVisible (page ))
18451849 {
18461850 elog (WARNING , "page containing LP_DEAD items is marked as all-visible in relation \"%s\" page %u" ,
18471851 vacrel -> relname , blkno );
@@ -1854,16 +1858,15 @@ lazy_scan_prune(LVRelState *vacrel,
18541858 /*
18551859 * If the all-visible page is all-frozen but not marked as such yet, mark
18561860 * it as all-frozen. Note that all_frozen is only valid if all_visible is
1857- * true, so we must check both prunestate fields .
1861+ * true, so we must check both all_visible and all_frozen .
18581862 */
1859- else if (all_visible_according_to_vm && prunestate -> all_visible &&
1860- prunestate -> all_frozen &&
1861- !VM_ALL_FROZEN (vacrel -> rel , blkno , & vmbuffer ))
1863+ else if (all_visible_according_to_vm && all_visible &&
1864+ all_frozen && !VM_ALL_FROZEN (vacrel -> rel , blkno , & vmbuffer ))
18621865 {
18631866 /*
18641867 * Avoid relying on all_visible_according_to_vm as a proxy for the
18651868 * page-level PD_ALL_VISIBLE bit being set, since it might have become
1866- * stale -- even when all_visible is set in prunestate
1869+ * stale -- even when all_visible is set
18671870 */
18681871 if (!PageIsAllVisible (page ))
18691872 {
@@ -1878,7 +1881,7 @@ lazy_scan_prune(LVRelState *vacrel,
18781881 * since a snapshotConflictHorizon sufficient to make everything safe
18791882 * for REDO was logged when the page's tuples were frozen.
18801883 */
1881- Assert (!TransactionIdIsValid (prunestate -> visibility_cutoff_xid ));
1884+ Assert (!TransactionIdIsValid (visibility_cutoff_xid ));
18821885 visibilitymap_set (vacrel -> rel , blkno , buf , InvalidXLogRecPtr ,
18831886 vmbuffer , InvalidTransactionId ,
18841887 VISIBILITYMAP_ALL_VISIBLE |
0 commit comments