diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 029b2f2deb7b4ad62a9484f4b8e82d6f664ba73e..e44cf0d45057215fda3d7e68eeecd1c1a0cfa5fc 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -6,7 +6,7 @@
  * Copyright (c) 2000-2009, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.84 2009/04/23 00:23:45 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.85 2009/08/31 02:23:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,11 +16,13 @@
 #include "access/clog.h"
 #include "access/subtrans.h"
 #include "access/transam.h"
+#include "commands/dbcommands.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
 #include "utils/builtins.h"
+#include "utils/syscache.h"
 
 
 /* Number of OIDs to prefetch (preallocate) per XLOG write */
@@ -31,9 +33,14 @@ VariableCache ShmemVariableCache = NULL;
 
 
 /*
- * Allocate the next XID for my new transaction or subtransaction.
+ * Allocate the next XID for a new transaction or subtransaction.
  *
  * The new XID is also stored into MyProc before returning.
+ *
+ * Note: when this is called, we are actually already inside a valid
+ * transaction, since XIDs are now not allocated until the transaction
+ * does something.  So it is safe to do a database lookup if we want to
+ * issue a warning about XID wrap.
  */
 TransactionId
 GetNewTransactionId(bool isSubXact)
@@ -72,6 +79,20 @@ GetNewTransactionId(bool isSubXact)
 	if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidVacLimit) &&
 		TransactionIdIsValid(ShmemVariableCache->xidVacLimit))
 	{
+		/*
+		 * For safety's sake, we release XidGenLock while sending signals,
+		 * warnings, etc.  This is not so much because we care about
+		 * preserving concurrency in this situation, as to avoid any
+		 * possibility of deadlock while doing get_database_name().
+		 * First, copy all the shared values we'll need in this path.
+		 */
+		TransactionId xidWarnLimit = ShmemVariableCache->xidWarnLimit;
+		TransactionId xidStopLimit = ShmemVariableCache->xidStopLimit;
+		TransactionId xidWrapLimit = ShmemVariableCache->xidWrapLimit;
+		Oid		oldest_datoid = ShmemVariableCache->oldestXidDB;
+
+		LWLockRelease(XidGenLock);
+
 		/*
 		 * To avoid swamping the postmaster with signals, we issue the autovac
 		 * request only once per 64K transaction starts.  This still gives
@@ -81,22 +102,50 @@ GetNewTransactionId(bool isSubXact)
 			SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
 
 		if (IsUnderPostmaster &&
-		 TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidStopLimit))
-			ereport(ERROR,
-					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
-					 errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"",
-							NameStr(ShmemVariableCache->limit_datname)),
-					 errhint("Stop the postmaster and use a standalone backend to vacuum database \"%s\".\n"
-							 "You might also need to commit or roll back old prepared transactions.",
-							 NameStr(ShmemVariableCache->limit_datname))));
-		else if (TransactionIdFollowsOrEquals(xid, ShmemVariableCache->xidWarnLimit))
-			ereport(WARNING,
-			(errmsg("database \"%s\" must be vacuumed within %u transactions",
-					NameStr(ShmemVariableCache->limit_datname),
-					ShmemVariableCache->xidWrapLimit - xid),
-			 errhint("To avoid a database shutdown, execute a database-wide VACUUM in \"%s\".\n"
-					 "You might also need to commit or roll back old prepared transactions.",
-					 NameStr(ShmemVariableCache->limit_datname))));
+			TransactionIdFollowsOrEquals(xid, xidStopLimit))
+		{
+			char   *oldest_datname = get_database_name(oldest_datoid);
+
+			/* complain even if that DB has disappeared */
+			if (oldest_datname)
+				ereport(ERROR,
+						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+						 errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"",
+								oldest_datname),
+						 errhint("Stop the postmaster and use a standalone backend to vacuum that database.\n"
+								 "You might also need to commit or roll back old prepared transactions.")));
+			else
+				ereport(ERROR,
+						(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+						 errmsg("database is not accepting commands to avoid wraparound data loss in database with OID %u",
+								oldest_datoid),
+						 errhint("Stop the postmaster and use a standalone backend to vacuum that database.\n"
+								 "You might also need to commit or roll back old prepared transactions.")));
+		}
+		else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit))
+		{
+			char   *oldest_datname = get_database_name(oldest_datoid);
+
+			/* complain even if that DB has disappeared */
+			if (oldest_datname)
+				ereport(WARNING,
+						(errmsg("database \"%s\" must be vacuumed within %u transactions",
+								oldest_datname,
+								xidWrapLimit - xid),
+						 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
+								 "You might also need to commit or roll back old prepared transactions.")));
+			else
+				ereport(WARNING,
+						(errmsg("database with OID %u must be vacuumed within %u transactions",
+								oldest_datoid,
+								xidWrapLimit - xid),
+						 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
+								 "You might also need to commit or roll back old prepared transactions.")));
+		}
+
+		/* Re-acquire lock and start over */
+		LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
+		xid = ShmemVariableCache->nextXid;
 	}
 
 	/*
@@ -199,11 +248,10 @@ ReadNewTransactionId(void)
 /*
  * Determine the last safe XID to allocate given the currently oldest
  * datfrozenxid (ie, the oldest XID that might exist in any database
- * of our cluster).
+ * of our cluster), and the OID of the (or a) database with that value.
  */
 void
-SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
-					  Name oldest_datname)
+SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
 {
 	TransactionId xidVacLimit;
 	TransactionId xidWarnLimit;
@@ -275,14 +323,14 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
 	ShmemVariableCache->xidWarnLimit = xidWarnLimit;
 	ShmemVariableCache->xidStopLimit = xidStopLimit;
 	ShmemVariableCache->xidWrapLimit = xidWrapLimit;
-	namecpy(&ShmemVariableCache->limit_datname, oldest_datname);
+	ShmemVariableCache->oldestXidDB = oldest_datoid;
 	curXid = ShmemVariableCache->nextXid;
 	LWLockRelease(XidGenLock);
 
 	/* Log the info */
 	ereport(DEBUG1,
-	   (errmsg("transaction ID wrap limit is %u, limited by database \"%s\"",
-			   xidWrapLimit, NameStr(*oldest_datname))));
+	   (errmsg("transaction ID wrap limit is %u, limited by database with OID %u",
+			   xidWrapLimit, oldest_datoid)));
 
 	/*
 	 * If past the autovacuum force point, immediately signal an autovac
@@ -297,13 +345,59 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
 
 	/* Give an immediate warning if past the wrap warn point */
 	if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit))
-		ereport(WARNING,
-		   (errmsg("database \"%s\" must be vacuumed within %u transactions",
-				   NameStr(*oldest_datname),
-				   xidWrapLimit - curXid),
-			errhint("To avoid a database shutdown, execute a database-wide VACUUM in \"%s\".\n"
-					"You might also need to commit or roll back old prepared transactions.",
-					NameStr(*oldest_datname))));
+	{
+		char   *oldest_datname = get_database_name(oldest_datoid);
+
+		/*
+		 * Note: it's possible that get_database_name fails and returns NULL,
+		 * for example because the database just got dropped.  We'll still
+		 * warn, even though the warning might now be unnecessary.
+		 */
+		if (oldest_datname)
+			ereport(WARNING,
+					(errmsg("database \"%s\" must be vacuumed within %u transactions",
+							oldest_datname,
+							xidWrapLimit - curXid),
+					 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
+							 "You might also need to commit or roll back old prepared transactions.")));
+		else
+			ereport(WARNING,
+					(errmsg("database with OID %u must be vacuumed within %u transactions",
+							oldest_datoid,
+							xidWrapLimit - curXid),
+					 errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
+							 "You might also need to commit or roll back old prepared transactions.")));
+	}
+}
+
+
+/*
+ * TransactionIdLimitIsValid -- is the shared XID wrap-limit data sane?
+ *
+ * We primarily check whether oldestXidDB is valid.  The cases we have in
+ * mind are that that database was dropped, or the field was reset to zero
+ * by pg_resetxlog.  In either case we should force recalculation of the
+ * wrap limit.  In future we might add some more sanity checks here.
+ */
+bool
+TransactionIdLimitIsValid(void)
+{
+	TransactionId oldestXid;
+	Oid			oldestXidDB;
+
+	/* Locking is probably not really necessary, but let's be careful */
+	LWLockAcquire(XidGenLock, LW_SHARED);
+	oldestXid = ShmemVariableCache->oldestXid;
+	oldestXidDB = ShmemVariableCache->oldestXidDB;
+	LWLockRelease(XidGenLock);
+
+	if (!TransactionIdIsNormal(oldestXid))
+		return false;			/* shouldn't happen, but just in case */
+	if (!SearchSysCacheExists(DATABASEOID,
+							  ObjectIdGetDatum(oldestXidDB),
+							  0, 0, 0))
+		return false;			/* could happen, per comment above */
+	return true;
 }
 
 
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index ed427785517bfac6da4e9fa777827af237571e8f..7f675a985cb8d8a93289532db3ee551ad8bf0fa3 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.349 2009/08/27 07:15:41 heikki Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.350 2009/08/31 02:23:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,6 +34,7 @@
 #include "access/xlogutils.h"
 #include "catalog/catversion.h"
 #include "catalog/pg_control.h"
+#include "catalog/pg_database.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
 #include "libpq/pqsignal.h"
@@ -4638,12 +4639,16 @@ BootStrapXLOG(void)
 	checkPoint.nextOid = FirstBootstrapObjectId;
 	checkPoint.nextMulti = FirstMultiXactId;
 	checkPoint.nextMultiOffset = 0;
+	checkPoint.oldestXid = FirstNormalTransactionId;
+	checkPoint.oldestXidDB = TemplateDbOid;
 	checkPoint.time = (pg_time_t) time(NULL);
 
 	ShmemVariableCache->nextXid = checkPoint.nextXid;
 	ShmemVariableCache->nextOid = checkPoint.nextOid;
 	ShmemVariableCache->oidCount = 0;
 	MultiXactSetNextMXact(checkPoint.nextMulti, checkPoint.nextMultiOffset);
+	ShmemVariableCache->oldestXid = checkPoint.oldestXid;
+	ShmemVariableCache->oldestXidDB = checkPoint.oldestXidDB;
 
 	/* Set up the XLOG page header */
 	page->xlp_magic = XLOG_PAGE_MAGIC;
@@ -5355,6 +5360,9 @@ StartupXLOG(void)
 	ereport(DEBUG1,
 			(errmsg("next MultiXactId: %u; next MultiXactOffset: %u",
 					checkPoint.nextMulti, checkPoint.nextMultiOffset)));
+	ereport(DEBUG1,
+			(errmsg("oldest unfrozen transaction ID: %u, in database %u",
+					checkPoint.oldestXid, checkPoint.oldestXidDB)));
 	if (!TransactionIdIsNormal(checkPoint.nextXid))
 		ereport(PANIC,
 				(errmsg("invalid next transaction ID")));
@@ -5363,6 +5371,8 @@ StartupXLOG(void)
 	ShmemVariableCache->nextOid = checkPoint.nextOid;
 	ShmemVariableCache->oidCount = 0;
 	MultiXactSetNextMXact(checkPoint.nextMulti, checkPoint.nextMultiOffset);
+	ShmemVariableCache->oldestXid = checkPoint.oldestXid;
+	ShmemVariableCache->oldestXidDB = checkPoint.oldestXidDB;
 
 	/*
 	 * We must replay WAL entries using the same TimeLineID they were created
@@ -6546,6 +6556,8 @@ CreateCheckPoint(int flags)
 	 */
 	LWLockAcquire(XidGenLock, LW_SHARED);
 	checkPoint.nextXid = ShmemVariableCache->nextXid;
+	checkPoint.oldestXid = ShmemVariableCache->oldestXid;
+	checkPoint.oldestXidDB = ShmemVariableCache->oldestXidDB;
 	LWLockRelease(XidGenLock);
 
 	/* Increase XID epoch if we've wrapped around since last checkpoint */
@@ -6984,6 +6996,8 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
 		ShmemVariableCache->oidCount = 0;
 		MultiXactSetNextMXact(checkPoint.nextMulti,
 							  checkPoint.nextMultiOffset);
+		ShmemVariableCache->oldestXid = checkPoint.oldestXid;
+		ShmemVariableCache->oldestXidDB = checkPoint.oldestXidDB;
 
 		/* ControlFile->checkPointCopy always tracks the latest ckpt XID */
 		ControlFile->checkPointCopy.nextXidEpoch = checkPoint.nextXidEpoch;
@@ -7022,6 +7036,12 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record)
 		}
 		MultiXactAdvanceNextMXact(checkPoint.nextMulti,
 								  checkPoint.nextMultiOffset);
+		if (TransactionIdPrecedes(ShmemVariableCache->oldestXid,
+								  checkPoint.oldestXid))
+		{
+			ShmemVariableCache->oldestXid = checkPoint.oldestXid;
+			ShmemVariableCache->oldestXidDB = checkPoint.oldestXidDB;
+		}
 
 		/* ControlFile->checkPointCopy always tracks the latest ckpt XID */
 		ControlFile->checkPointCopy.nextXidEpoch = checkPoint.nextXidEpoch;
@@ -7056,13 +7076,16 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec)
 		CheckPoint *checkpoint = (CheckPoint *) rec;
 
 		appendStringInfo(buf, "checkpoint: redo %X/%X; "
-						 "tli %u; xid %u/%u; oid %u; multi %u; offset %u; %s",
+						 "tli %u; xid %u/%u; oid %u; multi %u; offset %u; "
+						 "oldest xid %u in DB %u; %s",
 						 checkpoint->redo.xlogid, checkpoint->redo.xrecoff,
 						 checkpoint->ThisTimeLineID,
 						 checkpoint->nextXidEpoch, checkpoint->nextXid,
 						 checkpoint->nextOid,
 						 checkpoint->nextMulti,
 						 checkpoint->nextMultiOffset,
+						 checkpoint->oldestXid,
+						 checkpoint->oldestXidDB,
 				 (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online");
 	}
 	else if (info == XLOG_NOOP)
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 1058bd2d31228928032f6f0b3ce3394bd82b92c9..d2b3105e0282f52b6f962fa012736df6f617ccda 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.390 2009/08/24 02:18:31 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.391 2009/08/31 02:23:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -895,8 +895,9 @@ vac_update_datfrozenxid(void)
 	/*
 	 * If we were able to advance datfrozenxid, mark the flat-file copy of
 	 * pg_database for update at commit, and see if we can truncate pg_clog.
+	 * Also force update if the shared XID-wrap-limit info is stale.
 	 */
-	if (dirty)
+	if (dirty || !TransactionIdLimitIsValid())
 	{
 		database_file_update_needed();
 		vac_truncate_clog(newFrozenXid);
@@ -916,7 +917,7 @@ vac_update_datfrozenxid(void)
  *
  *		This routine is shared by full and lazy VACUUM.  Note that it's
  *		only invoked when we've managed to change our DB's datfrozenxid
- *		entry.
+ *		entry, or we found that the shared XID-wrap-limit info is stale.
  */
 static void
 vac_truncate_clog(TransactionId frozenXID)
@@ -925,11 +926,11 @@ vac_truncate_clog(TransactionId frozenXID)
 	Relation	relation;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
-	NameData	oldest_datname;
+	Oid			oldest_datoid;
 	bool		frozenAlreadyWrapped = false;
 
-	/* init oldest_datname to sync with my frozenXID */
-	namestrcpy(&oldest_datname, get_database_name(MyDatabaseId));
+	/* init oldest_datoid to sync with my frozenXID */
+	oldest_datoid = MyDatabaseId;
 
 	/*
 	 * Scan pg_database to compute the minimum datfrozenxid
@@ -958,7 +959,7 @@ vac_truncate_clog(TransactionId frozenXID)
 		else if (TransactionIdPrecedes(dbform->datfrozenxid, frozenXID))
 		{
 			frozenXID = dbform->datfrozenxid;
-			namecpy(&oldest_datname, &dbform->datname);
+			oldest_datoid = HeapTupleGetOid(tuple);
 		}
 	}
 
@@ -987,7 +988,7 @@ vac_truncate_clog(TransactionId frozenXID)
 	 * Update the wrap limit for GetNewTransactionId.  Note: this function
 	 * will also signal the postmaster for an(other) autovac cycle if needed.
 	 */
-	SetTransactionIdLimit(frozenXID, &oldest_datname);
+	SetTransactionIdLimit(frozenXID, oldest_datoid);
 }
 
 
diff --git a/src/backend/utils/init/flatfiles.c b/src/backend/utils/init/flatfiles.c
index fbbd372b1412d22dc467194fde3e0678ceaabee9..38271653f6d82f47f65f35db1d85630b74f6b2a0 100644
--- a/src/backend/utils/init/flatfiles.c
+++ b/src/backend/utils/init/flatfiles.c
@@ -23,7 +23,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.38 2009/08/29 19:26:51 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.39 2009/08/31 02:23:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -166,9 +166,6 @@ name_okay(const char *str)
 
 /*
  * write_database_file: update the flat database file
- *
- * A side effect is to determine the oldest database's datfrozenxid
- * so we can set or update the XID wrap limit.
  */
 static void
 write_database_file(Relation drel)
@@ -180,8 +177,6 @@ write_database_file(Relation drel)
 	mode_t		oumask;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
-	NameData	oldest_datname;
-	TransactionId oldest_datfrozenxid = InvalidTransactionId;
 
 	/*
 	 * Create a temporary filename to be renamed later.  This prevents the
@@ -219,20 +214,6 @@ write_database_file(Relation drel)
 		dattablespace = dbform->dattablespace;
 		datfrozenxid = dbform->datfrozenxid;
 
-		/*
-		 * Identify the oldest datfrozenxid.  This must match the logic in
-		 * vac_truncate_clog() in vacuum.c.
-		 */
-		if (TransactionIdIsNormal(datfrozenxid))
-		{
-			if (oldest_datfrozenxid == InvalidTransactionId ||
-				TransactionIdPrecedes(datfrozenxid, oldest_datfrozenxid))
-			{
-				oldest_datfrozenxid = datfrozenxid;
-				namestrcpy(&oldest_datname, datname);
-			}
-		}
-
 		/*
 		 * Check for illegal characters in the database name.
 		 */
@@ -270,12 +251,6 @@ write_database_file(Relation drel)
 				(errcode_for_file_access(),
 				 errmsg("could not rename file \"%s\" to \"%s\": %m",
 						tempname, filename)));
-
-	/*
-	 * Set the transaction ID wrap limit using the oldest datfrozenxid
-	 */
-	if (oldest_datfrozenxid != InvalidTransactionId)
-		SetTransactionIdLimit(oldest_datfrozenxid, &oldest_datname);
 }
 
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6bc0c197b0ffe4f25ea9d4f98256f6679994cc6e..66904e6b091c52dc961e4f7fccdcf773c8ce595b 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.512 2009/08/29 19:26:51 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.513 2009/08/31 02:23:22 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -1864,6 +1864,7 @@ static struct config_int ConfigureNamesInt[] =
 			NULL
 		},
 		&autovacuum_freeze_max_age,
+		/* see pg_resetxlog if you change the upper-limit value */
 		200000000, 100000000, 2000000000, NULL, NULL
 	},
 	{
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index b24d20af2f46fc8542f2feb6319b94806cfde23b..e5b6480eb7f0f1e56932792eeef8e31e9878814c 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -6,7 +6,7 @@
  * copyright (c) Oliver Elphick <olly@lfix.co.uk>, 2001;
  * licence: BSD
  *
- * $PostgreSQL: pgsql/src/bin/pg_controldata/pg_controldata.c,v 1.43 2009/06/11 14:49:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_controldata/pg_controldata.c,v 1.44 2009/08/31 02:23:22 tgl Exp $
  */
 #include "postgres_fe.h"
 
@@ -192,6 +192,10 @@ main(int argc, char *argv[])
 		   ControlFile.checkPointCopy.nextMulti);
 	printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
 		   ControlFile.checkPointCopy.nextMultiOffset);
+	printf(_("Latest checkpoint's oldestXID:        %u\n"),
+		   ControlFile.checkPointCopy.oldestXid);
+	printf(_("Latest checkpoint's oldestXID's DB:   %u\n"),
+		   ControlFile.checkPointCopy.oldestXidDB);
 	printf(_("Time of latest checkpoint:            %s\n"),
 		   ckpttime_str);
 	printf(_("Minimum recovery ending location:     %X/%X\n"),
diff --git a/src/bin/pg_resetxlog/pg_resetxlog.c b/src/bin/pg_resetxlog/pg_resetxlog.c
index 076327f9b81e40537dee0619ea3b37ef4c9cbb9e..2124c8d9b0dcbcac3ece81c353d90560b298ec09 100644
--- a/src/bin/pg_resetxlog/pg_resetxlog.c
+++ b/src/bin/pg_resetxlog/pg_resetxlog.c
@@ -23,7 +23,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/pg_resetxlog/pg_resetxlog.c,v 1.74 2009/06/11 14:49:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_resetxlog/pg_resetxlog.c,v 1.75 2009/08/31 02:23:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -307,8 +307,22 @@ main(int argc, char *argv[])
 		ControlFile.checkPointCopy.nextXidEpoch = set_xid_epoch;
 
 	if (set_xid != 0)
+	{
 		ControlFile.checkPointCopy.nextXid = set_xid;
 
+		/*
+		 * For the moment, just set oldestXid to a value that will force
+		 * immediate autovacuum-for-wraparound.  It's not clear whether
+		 * adding user control of this is useful, so let's just do something
+		 * that's reasonably safe.  The magic constant here corresponds to
+		 * the maximum allowed value of autovacuum_freeze_max_age.
+		 */
+		ControlFile.checkPointCopy.oldestXid = set_xid - 2000000000;
+		if (ControlFile.checkPointCopy.oldestXid < FirstNormalTransactionId)
+			ControlFile.checkPointCopy.oldestXid += FirstNormalTransactionId;
+		ControlFile.checkPointCopy.oldestXidDB = InvalidOid;
+	}
+
 	if (set_oid != 0)
 		ControlFile.checkPointCopy.nextOid = set_oid;
 
@@ -476,10 +490,12 @@ GuessControlValues(void)
 	ControlFile.checkPointCopy.redo.xrecoff = SizeOfXLogLongPHD;
 	ControlFile.checkPointCopy.ThisTimeLineID = 1;
 	ControlFile.checkPointCopy.nextXidEpoch = 0;
-	ControlFile.checkPointCopy.nextXid = (TransactionId) 514;	/* XXX */
+	ControlFile.checkPointCopy.nextXid = FirstNormalTransactionId;
 	ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId;
 	ControlFile.checkPointCopy.nextMulti = FirstMultiXactId;
 	ControlFile.checkPointCopy.nextMultiOffset = 0;
+	ControlFile.checkPointCopy.oldestXid = FirstNormalTransactionId;
+	ControlFile.checkPointCopy.oldestXidDB = InvalidOid;
 	ControlFile.checkPointCopy.time = (pg_time_t) time(NULL);
 
 	ControlFile.state = DB_SHUTDOWNED;
@@ -554,6 +570,10 @@ PrintControlValues(bool guessed)
 		   ControlFile.checkPointCopy.nextMulti);
 	printf(_("Latest checkpoint's NextMultiOffset:  %u\n"),
 		   ControlFile.checkPointCopy.nextMultiOffset);
+	printf(_("Latest checkpoint's oldestXID:        %u\n"),
+		   ControlFile.checkPointCopy.oldestXid);
+	printf(_("Latest checkpoint's oldestXID's DB:   %u\n"),
+		   ControlFile.checkPointCopy.oldestXidDB);
 	printf(_("Maximum data alignment:               %u\n"),
 		   ControlFile.maxAlign);
 	/* we don't print floatFormat since can't say much useful about it */
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index b23a663c53a45e716f52f0668139e40aad1d5939..87609e6f81cf8bf177e4b968cebc46c1e3eb1b5c 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/transam.h,v 1.68 2009/05/08 03:21:35 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/access/transam.h,v 1.69 2009/08/31 02:23:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -90,7 +90,7 @@
  * just one struct with different fields that are protected by different
  * LWLocks.
  *
- * Note: xidWrapLimit and limit_datname are not "active" values, but are
+ * Note: xidWrapLimit and oldestXidDB are not "active" values, but are
  * used just to generate useful messages when xidWarnLimit or xidStopLimit
  * are exceeded.
  */
@@ -112,7 +112,7 @@ typedef struct VariableCacheData
 	TransactionId xidWarnLimit; /* start complaining here */
 	TransactionId xidStopLimit; /* refuse to advance nextXid beyond here */
 	TransactionId xidWrapLimit; /* where the world ends */
-	NameData	limit_datname;	/* database that needs vacuumed first */
+	Oid			oldestXidDB;	/* database with minimum datfrozenxid */
 
 	/*
 	 * These fields are protected by ProcArrayLock.
@@ -155,7 +155,8 @@ extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid);
 extern TransactionId GetNewTransactionId(bool isSubXact);
 extern TransactionId ReadNewTransactionId(void);
 extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
-					  Name oldest_datname);
+					  Oid oldest_datoid);
+extern bool TransactionIdLimitIsValid(void);
 extern Oid	GetNewObjectId(void);
 
 #endif   /* TRAMSAM_H */
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index 0312509e65d6f6a413d335ad7df7525164974711..c7a740ca71057541e7291d94b3951a9f054b0753 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_control.h,v 1.43 2009/01/01 17:23:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_control.h,v 1.44 2009/08/31 02:23:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,7 +21,7 @@
 
 
 /* Version identifier for this pg_control format */
-#define PG_CONTROL_VERSION	843
+#define PG_CONTROL_VERSION	851
 
 /*
  * Body of CheckPoint XLOG records.  This is declared here because we keep
@@ -37,6 +37,8 @@ typedef struct CheckPoint
 	Oid			nextOid;		/* next free OID */
 	MultiXactId nextMulti;		/* next free MultiXactId */
 	MultiXactOffset nextMultiOffset;	/* next free MultiXact offset */
+	TransactionId oldestXid;	/* cluster-wide minimum datfrozenxid */
+	Oid			oldestXidDB;	/* database with minimum datfrozenxid */
 	pg_time_t	time;			/* time stamp of checkpoint */
 } CheckPoint;