@@ -249,6 +249,7 @@ static bool lazy_scan_new_or_empty(LVRelState *vacrel, Buffer buf,
249249 bool sharelock , Buffer vmbuffer );
250250static void lazy_scan_prune (LVRelState * vacrel , Buffer buf ,
251251 BlockNumber blkno , Page page ,
252+ Buffer vmbuffer , bool all_visible_according_to_vm ,
252253 LVPagePruneState * prunestate );
253254static bool lazy_scan_noprune (LVRelState * vacrel , Buffer buf ,
254255 BlockNumber blkno , Page page ,
@@ -1032,117 +1033,9 @@ lazy_scan_heap(LVRelState *vacrel)
10321033 * tuple headers of remaining items with storage. It also determines
10331034 * if truncating this block is safe.
10341035 */
1035- lazy_scan_prune (vacrel , buf , blkno , page , & prunestate );
1036-
1037- Assert (!prunestate .all_visible || !prunestate .has_lpdead_items );
1038-
1039- /*
1040- * Handle setting visibility map bit based on information from the VM
1041- * (as of last lazy_scan_skip() call), and from prunestate
1042- */
1043- if (!all_visible_according_to_vm && prunestate .all_visible )
1044- {
1045- uint8 flags = VISIBILITYMAP_ALL_VISIBLE ;
1046-
1047- if (prunestate .all_frozen )
1048- {
1049- Assert (!TransactionIdIsValid (prunestate .visibility_cutoff_xid ));
1050- flags |= VISIBILITYMAP_ALL_FROZEN ;
1051- }
1052-
1053- /*
1054- * It should never be the case that the visibility map page is set
1055- * while the page-level bit is clear, but the reverse is allowed
1056- * (if checksums are not enabled). Regardless, set both bits so
1057- * that we get back in sync.
1058- *
1059- * NB: If the heap page is all-visible but the VM bit is not set,
1060- * we don't need to dirty the heap page. However, if checksums
1061- * are enabled, we do need to make sure that the heap page is
1062- * dirtied before passing it to visibilitymap_set(), because it
1063- * may be logged. Given that this situation should only happen in
1064- * rare cases after a crash, it is not worth optimizing.
1065- */
1066- PageSetAllVisible (page );
1067- MarkBufferDirty (buf );
1068- visibilitymap_set (vacrel -> rel , blkno , buf , InvalidXLogRecPtr ,
1069- vmbuffer , prunestate .visibility_cutoff_xid ,
1070- flags );
1071- }
1072-
1073- /*
1074- * As of PostgreSQL 9.2, the visibility map bit should never be set if
1075- * the page-level bit is clear. However, it's possible that the bit
1076- * got cleared after lazy_scan_skip() was called, so we must recheck
1077- * with buffer lock before concluding that the VM is corrupt.
1078- */
1079- else if (all_visible_according_to_vm && !PageIsAllVisible (page ) &&
1080- visibilitymap_get_status (vacrel -> rel , blkno , & vmbuffer ) != 0 )
1081- {
1082- elog (WARNING , "page is not marked all-visible but visibility map bit is set in relation \"%s\" page %u" ,
1083- vacrel -> relname , blkno );
1084- visibilitymap_clear (vacrel -> rel , blkno , vmbuffer ,
1085- VISIBILITYMAP_VALID_BITS );
1086- }
1087-
1088- /*
1089- * It's possible for the value returned by
1090- * GetOldestNonRemovableTransactionId() to move backwards, so it's not
1091- * wrong for us to see tuples that appear to not be visible to
1092- * everyone yet, while PD_ALL_VISIBLE is already set. The real safe
1093- * xmin value never moves backwards, but
1094- * GetOldestNonRemovableTransactionId() is conservative and sometimes
1095- * returns a value that's unnecessarily small, so if we see that
1096- * contradiction it just means that the tuples that we think are not
1097- * visible to everyone yet actually are, and the PD_ALL_VISIBLE flag
1098- * is correct.
1099- *
1100- * There should never be LP_DEAD items on a page with PD_ALL_VISIBLE
1101- * set, however.
1102- */
1103- else if (prunestate .has_lpdead_items && PageIsAllVisible (page ))
1104- {
1105- elog (WARNING , "page containing LP_DEAD items is marked as all-visible in relation \"%s\" page %u" ,
1106- vacrel -> relname , blkno );
1107- PageClearAllVisible (page );
1108- MarkBufferDirty (buf );
1109- visibilitymap_clear (vacrel -> rel , blkno , vmbuffer ,
1110- VISIBILITYMAP_VALID_BITS );
1111- }
1112-
1113- /*
1114- * If the all-visible page is all-frozen but not marked as such yet,
1115- * mark it as all-frozen. Note that all_frozen is only valid if
1116- * all_visible is true, so we must check both prunestate fields.
1117- */
1118- else if (all_visible_according_to_vm && prunestate .all_visible &&
1119- prunestate .all_frozen &&
1120- !VM_ALL_FROZEN (vacrel -> rel , blkno , & vmbuffer ))
1121- {
1122- /*
1123- * Avoid relying on all_visible_according_to_vm as a proxy for the
1124- * page-level PD_ALL_VISIBLE bit being set, since it might have
1125- * become stale -- even when all_visible is set in prunestate
1126- */
1127- if (!PageIsAllVisible (page ))
1128- {
1129- PageSetAllVisible (page );
1130- MarkBufferDirty (buf );
1131- }
1132-
1133- /*
1134- * Set the page all-frozen (and all-visible) in the VM.
1135- *
1136- * We can pass InvalidTransactionId as our visibility_cutoff_xid,
1137- * since a snapshotConflictHorizon sufficient to make everything
1138- * safe for REDO was logged when the page's tuples were frozen.
1139- */
1140- Assert (!TransactionIdIsValid (prunestate .visibility_cutoff_xid ));
1141- visibilitymap_set (vacrel -> rel , blkno , buf , InvalidXLogRecPtr ,
1142- vmbuffer , InvalidTransactionId ,
1143- VISIBILITYMAP_ALL_VISIBLE |
1144- VISIBILITYMAP_ALL_FROZEN );
1145- }
1036+ lazy_scan_prune (vacrel , buf , blkno , page ,
1037+ vmbuffer , all_visible_according_to_vm ,
1038+ & prunestate );
11461039
11471040 /*
11481041 * Final steps for block: drop cleanup lock, record free space in the
@@ -1496,6 +1389,8 @@ lazy_scan_prune(LVRelState *vacrel,
14961389 Buffer buf ,
14971390 BlockNumber blkno ,
14981391 Page page ,
1392+ Buffer vmbuffer ,
1393+ bool all_visible_according_to_vm ,
14991394 LVPagePruneState * prunestate )
15001395{
15011396 Relation rel = vacrel -> rel ;
@@ -1880,6 +1775,115 @@ lazy_scan_prune(LVRelState *vacrel,
18801775 /* Can't truncate this page */
18811776 if (hastup )
18821777 vacrel -> nonempty_pages = blkno + 1 ;
1778+
1779+ Assert (!prunestate -> all_visible || !prunestate -> has_lpdead_items );
1780+
1781+ /*
1782+ * Handle setting visibility map bit based on information from the VM (as
1783+ * of last lazy_scan_skip() call), and from prunestate
1784+ */
1785+ if (!all_visible_according_to_vm && prunestate -> all_visible )
1786+ {
1787+ uint8 flags = VISIBILITYMAP_ALL_VISIBLE ;
1788+
1789+ if (prunestate -> all_frozen )
1790+ {
1791+ Assert (!TransactionIdIsValid (prunestate -> visibility_cutoff_xid ));
1792+ flags |= VISIBILITYMAP_ALL_FROZEN ;
1793+ }
1794+
1795+ /*
1796+ * It should never be the case that the visibility map page is set
1797+ * while the page-level bit is clear, but the reverse is allowed (if
1798+ * checksums are not enabled). Regardless, set both bits so that we
1799+ * get back in sync.
1800+ *
1801+ * NB: If the heap page is all-visible but the VM bit is not set, we
1802+ * don't need to dirty the heap page. However, if checksums are
1803+ * enabled, we do need to make sure that the heap page is dirtied
1804+ * before passing it to visibilitymap_set(), because it may be logged.
1805+ * Given that this situation should only happen in rare cases after a
1806+ * crash, it is not worth optimizing.
1807+ */
1808+ PageSetAllVisible (page );
1809+ MarkBufferDirty (buf );
1810+ visibilitymap_set (vacrel -> rel , blkno , buf , InvalidXLogRecPtr ,
1811+ vmbuffer , prunestate -> visibility_cutoff_xid ,
1812+ flags );
1813+ }
1814+
1815+ /*
1816+ * As of PostgreSQL 9.2, the visibility map bit should never be set if the
1817+ * page-level bit is clear. However, it's possible that the bit got
1818+ * cleared after lazy_scan_skip() was called, so we must recheck with
1819+ * buffer lock before concluding that the VM is corrupt.
1820+ */
1821+ else if (all_visible_according_to_vm && !PageIsAllVisible (page ) &&
1822+ visibilitymap_get_status (vacrel -> rel , blkno , & vmbuffer ) != 0 )
1823+ {
1824+ elog (WARNING , "page is not marked all-visible but visibility map bit is set in relation \"%s\" page %u" ,
1825+ vacrel -> relname , blkno );
1826+ visibilitymap_clear (vacrel -> rel , blkno , vmbuffer ,
1827+ VISIBILITYMAP_VALID_BITS );
1828+ }
1829+
1830+ /*
1831+ * It's possible for the value returned by
1832+ * GetOldestNonRemovableTransactionId() to move backwards, so it's not
1833+ * wrong for us to see tuples that appear to not be visible to everyone
1834+ * yet, while PD_ALL_VISIBLE is already set. The real safe xmin value
1835+ * never moves backwards, but GetOldestNonRemovableTransactionId() is
1836+ * conservative and sometimes returns a value that's unnecessarily small,
1837+ * so if we see that contradiction it just means that the tuples that we
1838+ * think are not visible to everyone yet actually are, and the
1839+ * PD_ALL_VISIBLE flag is correct.
1840+ *
1841+ * There should never be LP_DEAD items on a page with PD_ALL_VISIBLE set,
1842+ * however.
1843+ */
1844+ else if (prunestate -> has_lpdead_items && PageIsAllVisible (page ))
1845+ {
1846+ elog (WARNING , "page containing LP_DEAD items is marked as all-visible in relation \"%s\" page %u" ,
1847+ vacrel -> relname , blkno );
1848+ PageClearAllVisible (page );
1849+ MarkBufferDirty (buf );
1850+ visibilitymap_clear (vacrel -> rel , blkno , vmbuffer ,
1851+ VISIBILITYMAP_VALID_BITS );
1852+ }
1853+
1854+ /*
1855+ * If the all-visible page is all-frozen but not marked as such yet, mark
1856+ * it as all-frozen. Note that all_frozen is only valid if all_visible is
1857+ * true, so we must check both prunestate fields.
1858+ */
1859+ else if (all_visible_according_to_vm && prunestate -> all_visible &&
1860+ prunestate -> all_frozen &&
1861+ !VM_ALL_FROZEN (vacrel -> rel , blkno , & vmbuffer ))
1862+ {
1863+ /*
1864+ * Avoid relying on all_visible_according_to_vm as a proxy for the
1865+ * page-level PD_ALL_VISIBLE bit being set, since it might have become
1866+ * stale -- even when all_visible is set in prunestate
1867+ */
1868+ if (!PageIsAllVisible (page ))
1869+ {
1870+ PageSetAllVisible (page );
1871+ MarkBufferDirty (buf );
1872+ }
1873+
1874+ /*
1875+ * Set the page all-frozen (and all-visible) in the VM.
1876+ *
1877+ * We can pass InvalidTransactionId as our visibility_cutoff_xid,
1878+ * since a snapshotConflictHorizon sufficient to make everything safe
1879+ * for REDO was logged when the page's tuples were frozen.
1880+ */
1881+ Assert (!TransactionIdIsValid (prunestate -> visibility_cutoff_xid ));
1882+ visibilitymap_set (vacrel -> rel , blkno , buf , InvalidXLogRecPtr ,
1883+ vmbuffer , InvalidTransactionId ,
1884+ VISIBILITYMAP_ALL_VISIBLE |
1885+ VISIBILITYMAP_ALL_FROZEN );
1886+ }
18831887}
18841888
18851889/*
0 commit comments