bool sharelock, Buffer vmbuffer);
static void lazy_scan_prune(LVRelState *vacrel, Buffer buf,
BlockNumber blkno, Page page,
+ Buffer vmbuffer, bool all_visible_according_to_vm,
LVPagePruneState *prunestate);
static bool lazy_scan_noprune(LVRelState *vacrel, Buffer buf,
BlockNumber blkno, Page page,
* tuple headers of remaining items with storage. It also determines
* if truncating this block is safe.
*/
- lazy_scan_prune(vacrel, buf, blkno, page, &prunestate);
-
- Assert(!prunestate.all_visible || !prunestate.has_lpdead_items);
-
- /*
- * Handle setting visibility map bit based on information from the VM
- * (as of last lazy_scan_skip() call), and from prunestate
- */
- if (!all_visible_according_to_vm && prunestate.all_visible)
- {
- uint8 flags = VISIBILITYMAP_ALL_VISIBLE;
-
- if (prunestate.all_frozen)
- {
- Assert(!TransactionIdIsValid(prunestate.visibility_cutoff_xid));
- flags |= VISIBILITYMAP_ALL_FROZEN;
- }
-
- /*
- * It should never be the case that the visibility map page is set
- * while the page-level bit is clear, but the reverse is allowed
- * (if checksums are not enabled). Regardless, set both bits so
- * that we get back in sync.
- *
- * NB: If the heap page is all-visible but the VM bit is not set,
- * we don't need to dirty the heap page. However, if checksums
- * are enabled, we do need to make sure that the heap page is
- * dirtied before passing it to visibilitymap_set(), because it
- * may be logged. Given that this situation should only happen in
- * rare cases after a crash, it is not worth optimizing.
- */
- PageSetAllVisible(page);
- MarkBufferDirty(buf);
- visibilitymap_set(vacrel->rel, blkno, buf, InvalidXLogRecPtr,
- vmbuffer, prunestate.visibility_cutoff_xid,
- flags);
- }
-
- /*
- * As of PostgreSQL 9.2, the visibility map bit should never be set if
- * the page-level bit is clear. However, it's possible that the bit
- * got cleared after lazy_scan_skip() was called, so we must recheck
- * with buffer lock before concluding that the VM is corrupt.
- */
- else if (all_visible_according_to_vm && !PageIsAllVisible(page) &&
- visibilitymap_get_status(vacrel->rel, blkno, &vmbuffer) != 0)
- {
- elog(WARNING, "page is not marked all-visible but visibility map bit is set in relation \"%s\" page %u",
- vacrel->relname, blkno);
- visibilitymap_clear(vacrel->rel, blkno, vmbuffer,
- VISIBILITYMAP_VALID_BITS);
- }
-
- /*
- * It's possible for the value returned by
- * GetOldestNonRemovableTransactionId() to move backwards, so it's not
- * wrong for us to see tuples that appear to not be visible to
- * everyone yet, while PD_ALL_VISIBLE is already set. The real safe
- * xmin value never moves backwards, but
- * GetOldestNonRemovableTransactionId() is conservative and sometimes
- * returns a value that's unnecessarily small, so if we see that
- * contradiction it just means that the tuples that we think are not
- * visible to everyone yet actually are, and the PD_ALL_VISIBLE flag
- * is correct.
- *
- * There should never be LP_DEAD items on a page with PD_ALL_VISIBLE
- * set, however.
- */
- else if (prunestate.has_lpdead_items && PageIsAllVisible(page))
- {
- elog(WARNING, "page containing LP_DEAD items is marked as all-visible in relation \"%s\" page %u",
- vacrel->relname, blkno);
- PageClearAllVisible(page);
- MarkBufferDirty(buf);
- visibilitymap_clear(vacrel->rel, blkno, vmbuffer,
- VISIBILITYMAP_VALID_BITS);
- }
-
- /*
- * If the all-visible page is all-frozen but not marked as such yet,
- * mark it as all-frozen. Note that all_frozen is only valid if
- * all_visible is true, so we must check both prunestate fields.
- */
- else if (all_visible_according_to_vm && prunestate.all_visible &&
- prunestate.all_frozen &&
- !VM_ALL_FROZEN(vacrel->rel, blkno, &vmbuffer))
- {
- /*
- * Avoid relying on all_visible_according_to_vm as a proxy for the
- * page-level PD_ALL_VISIBLE bit being set, since it might have
- * become stale -- even when all_visible is set in prunestate
- */
- if (!PageIsAllVisible(page))
- {
- PageSetAllVisible(page);
- MarkBufferDirty(buf);
- }
-
- /*
- * Set the page all-frozen (and all-visible) in the VM.
- *
- * We can pass InvalidTransactionId as our visibility_cutoff_xid,
- * since a snapshotConflictHorizon sufficient to make everything
- * safe for REDO was logged when the page's tuples were frozen.
- */
- Assert(!TransactionIdIsValid(prunestate.visibility_cutoff_xid));
- visibilitymap_set(vacrel->rel, blkno, buf, InvalidXLogRecPtr,
- vmbuffer, InvalidTransactionId,
- VISIBILITYMAP_ALL_VISIBLE |
- VISIBILITYMAP_ALL_FROZEN);
- }
+ lazy_scan_prune(vacrel, buf, blkno, page,
+ vmbuffer, all_visible_according_to_vm,
+ &prunestate);
/*
* Final steps for block: drop cleanup lock, record free space in the
Buffer buf,
BlockNumber blkno,
Page page,
+ Buffer vmbuffer,
+ bool all_visible_according_to_vm,
LVPagePruneState *prunestate)
{
Relation rel = vacrel->rel;
/* Can't truncate this page */
if (hastup)
vacrel->nonempty_pages = blkno + 1;
+
+ Assert(!prunestate->all_visible || !prunestate->has_lpdead_items);
+
+ /*
+ * Handle setting visibility map bit based on information from the VM (as
+ * of last lazy_scan_skip() call), and from prunestate
+ */
+ if (!all_visible_according_to_vm && prunestate->all_visible)
+ {
+ uint8 flags = VISIBILITYMAP_ALL_VISIBLE;
+
+ if (prunestate->all_frozen)
+ {
+ Assert(!TransactionIdIsValid(prunestate->visibility_cutoff_xid));
+ flags |= VISIBILITYMAP_ALL_FROZEN;
+ }
+
+ /*
+ * It should never be the case that the visibility map page is set
+ * while the page-level bit is clear, but the reverse is allowed (if
+ * checksums are not enabled). Regardless, set both bits so that we
+ * get back in sync.
+ *
+ * NB: If the heap page is all-visible but the VM bit is not set, we
+ * don't need to dirty the heap page. However, if checksums are
+ * enabled, we do need to make sure that the heap page is dirtied
+ * before passing it to visibilitymap_set(), because it may be logged.
+ * Given that this situation should only happen in rare cases after a
+ * crash, it is not worth optimizing.
+ */
+ PageSetAllVisible(page);
+ MarkBufferDirty(buf);
+ visibilitymap_set(vacrel->rel, blkno, buf, InvalidXLogRecPtr,
+ vmbuffer, prunestate->visibility_cutoff_xid,
+ flags);
+ }
+
+ /*
+ * As of PostgreSQL 9.2, the visibility map bit should never be set if the
+ * page-level bit is clear. However, it's possible that the bit got
+ * cleared after lazy_scan_skip() was called, so we must recheck with
+ * buffer lock before concluding that the VM is corrupt.
+ */
+ else if (all_visible_according_to_vm && !PageIsAllVisible(page) &&
+ visibilitymap_get_status(vacrel->rel, blkno, &vmbuffer) != 0)
+ {
+ elog(WARNING, "page is not marked all-visible but visibility map bit is set in relation \"%s\" page %u",
+ vacrel->relname, blkno);
+ visibilitymap_clear(vacrel->rel, blkno, vmbuffer,
+ VISIBILITYMAP_VALID_BITS);
+ }
+
+ /*
+ * It's possible for the value returned by
+ * GetOldestNonRemovableTransactionId() to move backwards, so it's not
+ * wrong for us to see tuples that appear to not be visible to everyone
+ * yet, while PD_ALL_VISIBLE is already set. The real safe xmin value
+ * never moves backwards, but GetOldestNonRemovableTransactionId() is
+ * conservative and sometimes returns a value that's unnecessarily small,
+ * so if we see that contradiction it just means that the tuples that we
+ * think are not visible to everyone yet actually are, and the
+ * PD_ALL_VISIBLE flag is correct.
+ *
+ * There should never be LP_DEAD items on a page with PD_ALL_VISIBLE set,
+ * however.
+ */
+ else if (prunestate->has_lpdead_items && PageIsAllVisible(page))
+ {
+ elog(WARNING, "page containing LP_DEAD items is marked as all-visible in relation \"%s\" page %u",
+ vacrel->relname, blkno);
+ PageClearAllVisible(page);
+ MarkBufferDirty(buf);
+ visibilitymap_clear(vacrel->rel, blkno, vmbuffer,
+ VISIBILITYMAP_VALID_BITS);
+ }
+
+ /*
+ * If the all-visible page is all-frozen but not marked as such yet, mark
+ * it as all-frozen. Note that all_frozen is only valid if all_visible is
+ * true, so we must check both prunestate fields.
+ */
+ else if (all_visible_according_to_vm && prunestate->all_visible &&
+ prunestate->all_frozen &&
+ !VM_ALL_FROZEN(vacrel->rel, blkno, &vmbuffer))
+ {
+ /*
+ * Avoid relying on all_visible_according_to_vm as a proxy for the
+ * page-level PD_ALL_VISIBLE bit being set, since it might have become
+ * stale -- even when all_visible is set in prunestate
+ */
+ if (!PageIsAllVisible(page))
+ {
+ PageSetAllVisible(page);
+ MarkBufferDirty(buf);
+ }
+
+ /*
+ * Set the page all-frozen (and all-visible) in the VM.
+ *
+ * We can pass InvalidTransactionId as our visibility_cutoff_xid,
+ * since a snapshotConflictHorizon sufficient to make everything safe
+ * for REDO was logged when the page's tuples were frozen.
+ */
+ Assert(!TransactionIdIsValid(prunestate->visibility_cutoff_xid));
+ visibilitymap_set(vacrel->rel, blkno, buf, InvalidXLogRecPtr,
+ vmbuffer, InvalidTransactionId,
+ VISIBILITYMAP_ALL_VISIBLE |
+ VISIBILITYMAP_ALL_FROZEN);
+ }
}
/*