>From 9907880952f40dfd11795625d659ea723352c376 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 26 Nov 2013 23:57:55 +0100
Subject: [PATCH] Don't sometimes incorrectly increase relfrozenxid in vacuums
 that truncate.

(auto-)vacuum recognizes that it can increase relfrozenxid by checking
whether it has processed all pages of a relation and frozen all that
are older than the new relfrozenxid. Unfortunately it performed that
check after truncating the dead pages of the end of the relation and
used the new number of pages to decide whether all pages have been
scanned.

This can lead to relfrozenxid being increased above the actual oldest
xid which in turn can lead to data loss due to xid wraparounds with
some rows suddently missing. This likely has escaped notice so far
because it takes a large number (~2^31) of xids being used to see the
effect, while a full-table vacuum before that would fix the issue.
---
 src/backend/commands/vacuumlazy.c | 26 ++++++++++++++++++--------
 1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index 6778c7d..5d9d32f 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -226,6 +226,24 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
 	vac_close_indexes(nindexes, Irel, NoLock);
 
 	/*
+	 * Compute whether we actually scanned the whole relation, if we did, we
+	 * can adjust relfrozenxid.
+	 *
+	 * NB: We need to check before truncating the relation, because that will
+	 * change ->rel_pages.
+	 */
+	new_frozen_xid = FreezeLimit;
+	if (vacrelstats->scanned_pages < vacrelstats->rel_pages)
+	{
+		Assert(!scan_all);
+		new_frozen_xid = InvalidTransactionId;
+	}
+
+	new_min_multi = MultiXactCutoff;
+	if (scan_all || vacrelstats->scanned_pages < vacrelstats->rel_pages)
+		new_min_multi = InvalidMultiXactId;
+
+	/*
 	 * Optionally truncate the relation.
 	 *
 	 * Don't even think about it unless we have a shot at releasing a goodly
@@ -269,14 +287,6 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
 	if (new_rel_allvisible > new_rel_pages)
 		new_rel_allvisible = new_rel_pages;
 
-	new_frozen_xid = FreezeLimit;
-	if (vacrelstats->scanned_pages < vacrelstats->rel_pages)
-		new_frozen_xid = InvalidTransactionId;
-
-	new_min_multi = MultiXactCutoff;
-	if (vacrelstats->scanned_pages < vacrelstats->rel_pages)
-		new_min_multi = InvalidMultiXactId;
-
 	vac_update_relstats(onerel,
 						new_rel_pages,
 						new_rel_tuples,
-- 
1.8.3.251.g1462b67

