@@ -212,23 +212,6 @@ typedef struct LVRelState
212
212
int64 missed_dead_tuples ; /* # removable, but not removed */
213
213
} LVRelState ;
214
214
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
-
232
215
/* Struct for saving and restoring vacuum error information. */
233
216
typedef struct LVSavedErrInfo
234
217
{
@@ -250,7 +233,7 @@ static bool lazy_scan_new_or_empty(LVRelState *vacrel, Buffer buf,
250
233
static void lazy_scan_prune (LVRelState * vacrel , Buffer buf ,
251
234
BlockNumber blkno , Page page ,
252
235
Buffer vmbuffer , bool all_visible_according_to_vm ,
253
- LVPagePruneState * prunestate );
236
+ bool * has_lpdead_items );
254
237
static bool lazy_scan_noprune (LVRelState * vacrel , Buffer buf ,
255
238
BlockNumber blkno , Page page ,
256
239
bool * has_lpdead_items );
@@ -854,7 +837,7 @@ lazy_scan_heap(LVRelState *vacrel)
854
837
Buffer buf ;
855
838
Page page ;
856
839
bool all_visible_according_to_vm ;
857
- LVPagePruneState prunestate ;
840
+ bool has_lpdead_items ;
858
841
859
842
if (blkno == next_unskippable_block )
860
843
{
@@ -959,8 +942,6 @@ lazy_scan_heap(LVRelState *vacrel)
959
942
page = BufferGetPage (buf );
960
943
if (!ConditionalLockBufferForCleanup (buf ))
961
944
{
962
- bool has_lpdead_items ;
963
-
964
945
LockBuffer (buf , BUFFER_LOCK_SHARE );
965
946
966
947
/* Check for new or empty pages before lazy_scan_noprune call */
@@ -1035,7 +1016,7 @@ lazy_scan_heap(LVRelState *vacrel)
1035
1016
*/
1036
1017
lazy_scan_prune (vacrel , buf , blkno , page ,
1037
1018
vmbuffer , all_visible_according_to_vm ,
1038
- & prunestate );
1019
+ & has_lpdead_items );
1039
1020
1040
1021
/*
1041
1022
* Final steps for block: drop cleanup lock, record free space in the
@@ -1056,7 +1037,7 @@ lazy_scan_heap(LVRelState *vacrel)
1056
1037
*/
1057
1038
if (vacrel -> nindexes == 0
1058
1039
|| !vacrel -> do_index_vacuuming
1059
- || !prunestate . has_lpdead_items )
1040
+ || !has_lpdead_items )
1060
1041
{
1061
1042
Size freespace = PageGetHeapFreeSpace (page );
1062
1043
@@ -1068,7 +1049,7 @@ lazy_scan_heap(LVRelState *vacrel)
1068
1049
* visible on upper FSM pages. This is done after vacuuming if the
1069
1050
* table has indexes.
1070
1051
*/
1071
- if (vacrel -> nindexes == 0 && prunestate . has_lpdead_items &&
1052
+ if (vacrel -> nindexes == 0 && has_lpdead_items &&
1072
1053
blkno - next_fsm_block_to_vacuum >= VACUUM_FSM_EVERY_PAGES )
1073
1054
{
1074
1055
FreeSpaceMapVacuumRange (vacrel -> rel , next_fsm_block_to_vacuum ,
@@ -1383,6 +1364,14 @@ lazy_scan_new_or_empty(LVRelState *vacrel, Buffer buf, BlockNumber blkno,
1383
1364
* right after this operation completes instead of in the middle of it. Note that
1384
1365
* any tuple that becomes dead after the call to heap_page_prune() can't need to
1385
1366
* 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.
1386
1375
*/
1387
1376
static void
1388
1377
lazy_scan_prune (LVRelState * vacrel ,
@@ -1391,7 +1380,7 @@ lazy_scan_prune(LVRelState *vacrel,
1391
1380
Page page ,
1392
1381
Buffer vmbuffer ,
1393
1382
bool all_visible_according_to_vm ,
1394
- LVPagePruneState * prunestate )
1383
+ bool * has_lpdead_items )
1395
1384
{
1396
1385
Relation rel = vacrel -> rel ;
1397
1386
OffsetNumber offnum ,
@@ -1404,6 +1393,9 @@ lazy_scan_prune(LVRelState *vacrel,
1404
1393
recently_dead_tuples ;
1405
1394
HeapPageFreeze pagefrz ;
1406
1395
bool hastup = false;
1396
+ bool all_visible ,
1397
+ all_frozen ;
1398
+ TransactionId visibility_cutoff_xid ;
1407
1399
int64 fpi_before = pgWalUsage .wal_fpi ;
1408
1400
OffsetNumber deadoffsets [MaxHeapTuplesPerPage ];
1409
1401
HeapTupleFreeze frozen [MaxHeapTuplesPerPage ];
@@ -1444,14 +1436,22 @@ lazy_scan_prune(LVRelState *vacrel,
1444
1436
& presult , & vacrel -> offnum );
1445
1437
1446
1438
/*
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.
1449
1446
*/
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 ;
1454
1450
1451
+ /*
1452
+ * Now scan the page to collect LP_DEAD items and update the variables set
1453
+ * just above.
1454
+ */
1455
1455
for (offnum = FirstOffsetNumber ;
1456
1456
offnum <= maxoff ;
1457
1457
offnum = OffsetNumberNext (offnum ))
@@ -1538,13 +1538,13 @@ lazy_scan_prune(LVRelState *vacrel,
1538
1538
* asynchronously. See SetHintBits for more info. Check that
1539
1539
* the tuple is hinted xmin-committed because of that.
1540
1540
*/
1541
- if (prunestate -> all_visible )
1541
+ if (all_visible )
1542
1542
{
1543
1543
TransactionId xmin ;
1544
1544
1545
1545
if (!HeapTupleHeaderXminCommitted (htup ))
1546
1546
{
1547
- prunestate -> all_visible = false;
1547
+ all_visible = false;
1548
1548
break ;
1549
1549
}
1550
1550
@@ -1556,14 +1556,14 @@ lazy_scan_prune(LVRelState *vacrel,
1556
1556
if (!TransactionIdPrecedes (xmin ,
1557
1557
vacrel -> cutoffs .OldestXmin ))
1558
1558
{
1559
- prunestate -> all_visible = false;
1559
+ all_visible = false;
1560
1560
break ;
1561
1561
}
1562
1562
1563
1563
/* Track newest xmin on page. */
1564
- if (TransactionIdFollows (xmin , prunestate -> visibility_cutoff_xid ) &&
1564
+ if (TransactionIdFollows (xmin , visibility_cutoff_xid ) &&
1565
1565
TransactionIdIsNormal (xmin ))
1566
- prunestate -> visibility_cutoff_xid = xmin ;
1566
+ visibility_cutoff_xid = xmin ;
1567
1567
}
1568
1568
break ;
1569
1569
case HEAPTUPLE_RECENTLY_DEAD :
@@ -1574,7 +1574,7 @@ lazy_scan_prune(LVRelState *vacrel,
1574
1574
* pruning.)
1575
1575
*/
1576
1576
recently_dead_tuples ++ ;
1577
- prunestate -> all_visible = false;
1577
+ all_visible = false;
1578
1578
break ;
1579
1579
case HEAPTUPLE_INSERT_IN_PROGRESS :
1580
1580
@@ -1585,11 +1585,11 @@ lazy_scan_prune(LVRelState *vacrel,
1585
1585
* results. This assumption is a bit shaky, but it is what
1586
1586
* acquire_sample_rows() does, so be consistent.
1587
1587
*/
1588
- prunestate -> all_visible = false;
1588
+ all_visible = false;
1589
1589
break ;
1590
1590
case HEAPTUPLE_DELETE_IN_PROGRESS :
1591
1591
/* This is an expected case during concurrent vacuum */
1592
- prunestate -> all_visible = false;
1592
+ all_visible = false;
1593
1593
1594
1594
/*
1595
1595
* Count such rows as live. As above, we assume the deleting
@@ -1619,7 +1619,7 @@ lazy_scan_prune(LVRelState *vacrel,
1619
1619
* definitely cannot be set all-frozen in the visibility map later on
1620
1620
*/
1621
1621
if (!totally_frozen )
1622
- prunestate -> all_frozen = false;
1622
+ all_frozen = false;
1623
1623
}
1624
1624
1625
1625
/*
@@ -1637,7 +1637,7 @@ lazy_scan_prune(LVRelState *vacrel,
1637
1637
* page all-frozen afterwards (might not happen until final heap pass).
1638
1638
*/
1639
1639
if (pagefrz .freeze_required || tuples_frozen == 0 ||
1640
- (prunestate -> all_visible && prunestate -> all_frozen &&
1640
+ (all_visible && all_frozen &&
1641
1641
fpi_before != pgWalUsage .wal_fpi ))
1642
1642
{
1643
1643
/*
@@ -1675,11 +1675,11 @@ lazy_scan_prune(LVRelState *vacrel,
1675
1675
* once we're done with it. Otherwise we generate a conservative
1676
1676
* cutoff by stepping back from OldestXmin.
1677
1677
*/
1678
- if (prunestate -> all_visible && prunestate -> all_frozen )
1678
+ if (all_visible && all_frozen )
1679
1679
{
1680
1680
/* 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 ;
1683
1683
}
1684
1684
else
1685
1685
{
@@ -1702,7 +1702,7 @@ lazy_scan_prune(LVRelState *vacrel,
1702
1702
*/
1703
1703
vacrel -> NewRelfrozenXid = pagefrz .NoFreezePageRelfrozenXid ;
1704
1704
vacrel -> NewRelminMxid = pagefrz .NoFreezePageRelminMxid ;
1705
- prunestate -> all_frozen = false;
1705
+ all_frozen = false;
1706
1706
tuples_frozen = 0 ; /* avoid miscounts in instrumentation */
1707
1707
}
1708
1708
@@ -1715,16 +1715,17 @@ lazy_scan_prune(LVRelState *vacrel,
1715
1715
*/
1716
1716
#ifdef USE_ASSERT_CHECKING
1717
1717
/* 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 )
1719
1719
{
1720
- TransactionId cutoff ;
1721
- bool all_frozen ;
1720
+ TransactionId debug_cutoff ;
1721
+ bool debug_all_frozen ;
1722
1722
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 ))
1724
1725
Assert (false);
1725
1726
1726
- Assert (!TransactionIdIsValid (cutoff ) ||
1727
- cutoff == prunestate -> visibility_cutoff_xid );
1727
+ Assert (!TransactionIdIsValid (debug_cutoff ) ||
1728
+ debug_cutoff == visibility_cutoff_xid );
1728
1729
}
1729
1730
#endif
1730
1731
@@ -1737,7 +1738,6 @@ lazy_scan_prune(LVRelState *vacrel,
1737
1738
ItemPointerData tmp ;
1738
1739
1739
1740
vacrel -> lpdead_item_pages ++ ;
1740
- prunestate -> has_lpdead_items = true;
1741
1741
1742
1742
ItemPointerSetBlockNumber (& tmp , blkno );
1743
1743
@@ -1762,7 +1762,7 @@ lazy_scan_prune(LVRelState *vacrel,
1762
1762
* Now that freezing has been finalized, unset all_visible. It needs
1763
1763
* to reflect the present state of things, as expected by our caller.
1764
1764
*/
1765
- prunestate -> all_visible = false;
1765
+ all_visible = false;
1766
1766
}
1767
1767
1768
1768
/* Finally, add page-local counts to whole-VACUUM counts */
@@ -1776,19 +1776,23 @@ lazy_scan_prune(LVRelState *vacrel,
1776
1776
if (hastup )
1777
1777
vacrel -> nonempty_pages = blkno + 1 ;
1778
1778
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 ));
1780
1783
1781
1784
/*
1782
1785
* 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
1784
1788
*/
1785
- if (!all_visible_according_to_vm && prunestate -> all_visible )
1789
+ if (!all_visible_according_to_vm && all_visible )
1786
1790
{
1787
1791
uint8 flags = VISIBILITYMAP_ALL_VISIBLE ;
1788
1792
1789
- if (prunestate -> all_frozen )
1793
+ if (all_frozen )
1790
1794
{
1791
- Assert (!TransactionIdIsValid (prunestate -> visibility_cutoff_xid ));
1795
+ Assert (!TransactionIdIsValid (visibility_cutoff_xid ));
1792
1796
flags |= VISIBILITYMAP_ALL_FROZEN ;
1793
1797
}
1794
1798
@@ -1808,7 +1812,7 @@ lazy_scan_prune(LVRelState *vacrel,
1808
1812
PageSetAllVisible (page );
1809
1813
MarkBufferDirty (buf );
1810
1814
visibilitymap_set (vacrel -> rel , blkno , buf , InvalidXLogRecPtr ,
1811
- vmbuffer , prunestate -> visibility_cutoff_xid ,
1815
+ vmbuffer , visibility_cutoff_xid ,
1812
1816
flags );
1813
1817
}
1814
1818
@@ -1841,7 +1845,7 @@ lazy_scan_prune(LVRelState *vacrel,
1841
1845
* There should never be LP_DEAD items on a page with PD_ALL_VISIBLE set,
1842
1846
* however.
1843
1847
*/
1844
- else if (prunestate -> has_lpdead_items && PageIsAllVisible (page ))
1848
+ else if (lpdead_items > 0 && PageIsAllVisible (page ))
1845
1849
{
1846
1850
elog (WARNING , "page containing LP_DEAD items is marked as all-visible in relation \"%s\" page %u" ,
1847
1851
vacrel -> relname , blkno );
@@ -1854,16 +1858,15 @@ lazy_scan_prune(LVRelState *vacrel,
1854
1858
/*
1855
1859
* If the all-visible page is all-frozen but not marked as such yet, mark
1856
1860
* 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 .
1858
1862
*/
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 ))
1862
1865
{
1863
1866
/*
1864
1867
* Avoid relying on all_visible_according_to_vm as a proxy for the
1865
1868
* 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
1867
1870
*/
1868
1871
if (!PageIsAllVisible (page ))
1869
1872
{
@@ -1878,7 +1881,7 @@ lazy_scan_prune(LVRelState *vacrel,
1878
1881
* since a snapshotConflictHorizon sufficient to make everything safe
1879
1882
* for REDO was logged when the page's tuples were frozen.
1880
1883
*/
1881
- Assert (!TransactionIdIsValid (prunestate -> visibility_cutoff_xid ));
1884
+ Assert (!TransactionIdIsValid (visibility_cutoff_xid ));
1882
1885
visibilitymap_set (vacrel -> rel , blkno , buf , InvalidXLogRecPtr ,
1883
1886
vmbuffer , InvalidTransactionId ,
1884
1887
VISIBILITYMAP_ALL_VISIBLE |
0 commit comments