diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 37d554d158e671e604fdb0c736b7d70a03b0b793..5f08b7fb05d167f48fd916aa33424832e07dd758 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.122 2001/07/06 09:41:36 inoue Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.123 2001/07/12 04:11:12 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -48,11 +48,6 @@
 #include "pgstat.h"
 
 
-XLogRecPtr log_heap_move(Relation reln, Buffer oldbuf, ItemPointerData from,
-			  Buffer newbuf, HeapTuple newtup);
-XLogRecPtr log_heap_clean(Relation reln, Buffer buffer,
-			   char *unused, int unlen);
-
 /* comments are in heap_update */
 static xl_heaptid _locked_tuple_;
 static void _heap_unlock_tuple(void *data);
diff --git a/src/backend/access/transam/transam.c b/src/backend/access/transam/transam.c
index 89a35b2d335d518eac11da7037e12d315fbfa5af..910042fb6245e2e032854381bbe04dda22d3f490 100644
--- a/src/backend/access/transam/transam.c
+++ b/src/backend/access/transam/transam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/transam/transam.c,v 1.44 2001/05/14 20:30:19 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/transam/transam.c,v 1.45 2001/07/12 04:11:13 tgl Exp $
  *
  * NOTES
  *	  This file contains the high level access-method interface to the
@@ -24,6 +24,7 @@
 #include "catalog/catname.h"
 #include "miscadmin.h"
 
+
 static int	RecoveryCheckingEnabled(void);
 static void TransRecover(Relation logRelation);
 static bool TransactionLogTest(TransactionId transactionId, XidStatus status);
@@ -40,29 +41,11 @@ static void TransactionLogUpdate(TransactionId transactionId,
 Relation	LogRelation = (Relation) NULL;
 
 /* ----------------
- *		global variables holding cached transaction id's and statuses.
- * ----------------
- */
-TransactionId cachedTestXid;
-XidStatus	cachedTestXidStatus;
-
-/* ----------------
- *		transaction system constants
+ *		Single-item cache for results of TransactionLogTest.
  * ----------------
  */
-/* ----------------------------------------------------------------
- *		transaction system constants
- *
- *		read the comments for GetNewTransactionId in order to
- *		understand the initial values for AmiTransactionId and
- *		FirstTransactionId. -cim 3/23/90
- * ----------------------------------------------------------------
- */
-TransactionId NullTransactionId = (TransactionId) 0;
-
-TransactionId AmiTransactionId = (TransactionId) 512;
-
-TransactionId FirstTransactionId = (TransactionId) 514;
+static TransactionId cachedTestXid = NullTransactionId;
+static XidStatus	cachedTestXidStatus;
 
 /* ----------------
  *		transaction recovery state variables
@@ -76,7 +59,7 @@ TransactionId FirstTransactionId = (TransactionId) 514;
  *		goes from zero to one. -cim 3/21/90
  * ----------------
  */
-int			RecoveryCheckingEnableState = 0;
+static int		RecoveryCheckingEnableState = 0;
 
 /* ----------------
  *		recovery checking accessors
@@ -203,14 +186,9 @@ TransactionLogUpdate(TransactionId transactionId,		/* trans id to update */
 
 	/*
 	 * update (invalidate) our single item TransactionLogTest cache.
-	 *
-	 * if (status != XID_COMMIT)
-	 *
-	 * What's the hell ?! Why != XID_COMMIT ?!
 	 */
 	TransactionIdStore(transactionId, &cachedTestXid);
 	cachedTestXidStatus = status;
-
 }
 
 /* ----------------------------------------------------------------
@@ -355,17 +333,15 @@ InitializeTransactionLog(void)
 
 	/*
 	 * if we have a virgin database, we initialize the log relation by
-	 * committing the AmiTransactionId (id 512) and we initialize the
+	 * committing the AmiTransactionId and we initialize the
 	 * variable relation by setting the next available transaction id to
-	 * FirstTransactionId (id 514).  OID initialization happens as a side
+	 * FirstTransactionId.  OID initialization happens as a side
 	 * effect of bootstrapping in varsup.c.
 	 */
 	SpinAcquire(OidGenLockId);
 	if (!TransactionIdDidCommit(AmiTransactionId))
 	{
 		TransactionLogUpdate(AmiTransactionId, XID_COMMIT);
-		TransactionIdStore(AmiTransactionId, &cachedTestXid);
-		cachedTestXidStatus = XID_COMMIT;
 		Assert(!IsUnderPostmaster &&
 			   ShmemVariableCache->nextXid <= FirstTransactionId);
 		ShmemVariableCache->nextXid = FirstTransactionId;
diff --git a/src/backend/access/transam/transsup.c b/src/backend/access/transam/transsup.c
index 0a44a018a9aee92d1c8168713da4d7156a887491..3fd6c9d3987f19bf3dd90527f5b33d3eeb75137d 100644
--- a/src/backend/access/transam/transsup.c
+++ b/src/backend/access/transam/transsup.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/transsup.c,v 1.30 2001/03/22 06:16:10 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/transsup.c,v 1.31 2001/07/12 04:11:13 tgl Exp $
  *
  * NOTES
  *	  This file contains support functions for the high
@@ -16,12 +16,67 @@
  *
  *-------------------------------------------------------------------------
  */
-
 #include "postgres.h"
 
 #include "access/xact.h"
 #include "utils/bit.h"
 
+
+/* ----------------
+ *		transaction system version id
+ *
+ *		this is stored on the first page of the log, time and variable
+ *		relations on the first 4 bytes.  This is so that if we improve
+ *		the format of the transaction log after postgres version 2, then
+ *		people won't have to rebuild their databases.
+ *
+ *		TRANS_SYSTEM_VERSION 100 means major version 1 minor version 0.
+ *		Two databases with the same major version should be compatible,
+ *		even if their minor versions differ.
+ *
+ *		XXX This isn't actually being used!
+ * ----------------
+ */
+#define TRANS_SYSTEM_VERSION	200
+
+/* ----------------
+ *		LogRelationContents structure
+ *
+ *		This structure describes the storage of the data in the
+ *		first 128 bytes of the log relation.  This storage is never
+ *		used for transaction status because transaction id's begin
+ *		their numbering at 512.
+ *
+ *		The first 4 bytes of this relation store the version
+ *		number of the transaction system.
+ *
+ *		XXX This isn't actually being used!
+ * ----------------
+ */
+typedef struct LogRelationContentsData
+{
+	XLogRecPtr	LSN;			/* temp hack: LSN is member of any block */
+	/* so should be described in bufmgr */
+	int			TransSystemVersion;
+} LogRelationContentsData;
+
+typedef LogRelationContentsData *LogRelationContents;
+
+
+/* ----------------
+ *		BitIndexOf computes the index of the Nth xid on a given block
+ * ----------------
+ */
+#define BitIndexOf(N)	((N) * 2)
+
+/* ----------------
+ *		transaction page definitions
+ * ----------------
+ */
+#define TP_DataSize				(BLCKSZ - sizeof(XLogRecPtr))
+#define TP_NumXidStatusPerBlock (TP_DataSize * 4)
+
+
 static XidStatus TransBlockGetXidStatus(Block tblock,
 					   TransactionId transactionId);
 static void TransBlockSetXidStatus(Block tblock,
@@ -54,7 +109,7 @@ TransComputeBlockNumber(Relation relation,		/* relation to test */
 														 * test */
 						BlockNumber *blockNumberOutP)
 {
-	long		itemsPerBlock = 0;
+	uint32		itemsPerBlock = 0;
 
 	/*
 	 * we calculate the block number of our transaction by dividing the
@@ -135,10 +190,7 @@ TransBlockGetLastTransactionIdStatus(Block tblock,
 		if (xstatus != XID_INPROGRESS)
 		{
 			if (returnXidP != NULL)
-			{
-				TransactionIdStore(baseXid, returnXidP);
-				TransactionIdAdd(returnXidP, index - 1);
-			}
+				TransactionIdStore(baseXid + (index - 1), returnXidP);
 			break;
 		}
 	}
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index f245a79cedbfa71cc9b175ac3f0059bb62e98570..2b253fc585586949300936787a1d914b14d50155 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -6,7 +6,7 @@
  * Copyright (c) 2000, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/transam/varsup.c,v 1.40 2001/05/25 15:45:32 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/transam/varsup.c,v 1.41 2001/07/12 04:11:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,8 +18,7 @@
 #include "storage/proc.h"
 
 
-/* Number of XIDs and OIDs to prefetch (preallocate) per XLOG write */
-#define VAR_XID_PREFETCH		1024
+/* Number of OIDs to prefetch (preallocate) per XLOG write */
 #define VAR_OID_PREFETCH		8192
 
 /* Spinlocks for serializing generation of XIDs and OIDs, respectively */
@@ -29,10 +28,13 @@ SPINLOCK	OidGenLockId;
 /* pointer to "variable cache" in shared memory (set up by shmem.c) */
 VariableCache ShmemVariableCache = NULL;
 
+
+/*
+ * Allocate the next XID for my new transaction.
+ */
 void
 GetNewTransactionId(TransactionId *xid)
 {
-
 	/*
 	 * During bootstrap initialization, we return the special bootstrap
 	 * transaction id.
@@ -49,10 +51,22 @@ GetNewTransactionId(TransactionId *xid)
 
 	(ShmemVariableCache->nextXid)++;
 
-	SpinRelease(XidGenLockId);
-
+	/*
+	 * Must set MyProc->xid before releasing XidGenLock.  This ensures that
+	 * when GetSnapshotData calls ReadNewTransactionId, all active XIDs
+	 * before the returned value of nextXid are already present in the shared
+	 * PROC array.  Else we have a race condition.
+	 *
+	 * XXX by storing xid into MyProc without acquiring SInvalLock, we are
+	 * relying on fetch/store of an xid to be atomic, else other backends
+	 * might see a partially-set xid here.  But holding both locks at once
+	 * would be a nasty concurrency hit (and at this writing, could cause a
+	 * deadlock against GetSnapshotData).  So for now, assume atomicity.
+	 */
 	if (MyProc != (PROC *) NULL)
 		MyProc->xid = *xid;
+
+	SpinRelease(XidGenLockId);
 }
 
 /*
@@ -61,7 +75,6 @@ GetNewTransactionId(TransactionId *xid)
 void
 ReadNewTransactionId(TransactionId *xid)
 {
-
 	/*
 	 * During bootstrap initialization, we return the special bootstrap
 	 * transaction id.
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 4c18ac5a56a1ae0b0dbec8868a1bfd2babf97f99..6467179231594a6f05d72260533493b7288d2caf 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.105 2001/07/06 21:04:25 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.106 2001/07/12 04:11:13 tgl Exp $
  *
  * NOTES
  *		Transaction aborts can now occur two ways:
@@ -199,14 +199,9 @@ static void StartTransaction(void);
 
 /* ----------------
  *		global variables holding the current transaction state.
- *
- *		Note: when we are running several slave processes, the
- *			  current transaction state data is copied into shared memory
- *			  and the CurrentTransactionState pointer changed to
- *			  point to the shared copy.  All this occurrs in slaves.c
  * ----------------
  */
-TransactionStateData CurrentTransactionStateData = {
+static TransactionStateData CurrentTransactionStateData = {
 	0,							/* transaction id */
 	FirstCommandId,				/* command id */
 	0,							/* scan command id */
@@ -234,29 +229,17 @@ static void *_RollbackData = NULL;
  *		info returned when the system is disabled
  *
  * Apparently a lot of this code is inherited from other prototype systems.
+ *
  * For DisabledStartTime, use a symbolic value to make the relationships clearer.
  * The old value of 1073741823 corresponds to a date in y2004, which is coming closer
  *	every day. It appears that if we return a value guaranteed larger than
  *	any real time associated with a transaction then comparisons in other
  *	modules will still be correct. Let's use BIG_ABSTIME for this. tgl 2/14/97
- *
- *		Note:  I have no idea what the significance of the
- *			   1073741823 in DisabledStartTime.. I just carried
- *			   this over when converting things from the old
- *			   V1 transaction system.  -cim 3/18/90
  * ----------------
  */
-TransactionId DisabledTransactionId = (TransactionId) -1;
+static CommandId	DisabledCommandId = (CommandId) -1;
 
-CommandId	DisabledCommandId = (CommandId) -1;
-
-AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME;	/* 1073741823; */
-
-/* ----------------
- *		overflow flag
- * ----------------
- */
-bool		CommandIdCounterOverflowFlag;
+static AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME;
 
 /* ----------------
  *		catalog creation transaction bootstrapping flag.
@@ -362,7 +345,7 @@ IsAbortedTransactionBlockState(void)
  *		themselves.
  * --------------------------------
  */
-int			SavedTransactionState;
+static int		SavedTransactionState;
 
 void
 OverrideTransactionSystem(bool flag)
@@ -403,12 +386,12 @@ GetCurrentTransactionId(void)
 	 * "disabled" transaction id.
 	 */
 	if (s->state == TRANS_DISABLED)
-		return (TransactionId) DisabledTransactionId;
+		return DisabledTransactionId;
 
 	/*
 	 * otherwise return the current transaction id.
 	 */
-	return (TransactionId) s->transactionIdData;
+	return s->transactionIdData;
 }
 
 
@@ -426,7 +409,7 @@ GetCurrentCommandId(void)
 	 * "disabled" command id.
 	 */
 	if (s->state == TRANS_DISABLED)
-		return (CommandId) DisabledCommandId;
+		return DisabledCommandId;
 
 	return s->commandId;
 }
@@ -441,7 +424,7 @@ GetScanCommandId(void)
 	 * "disabled" command id.
 	 */
 	if (s->state == TRANS_DISABLED)
-		return (CommandId) DisabledCommandId;
+		return DisabledCommandId;
 
 	return s->scanCommandId;
 }
@@ -461,7 +444,7 @@ GetCurrentTransactionStartTime(void)
 	 * "disabled" starting time.
 	 */
 	if (s->state == TRANS_DISABLED)
-		return (AbsoluteTime) DisabledStartTime;
+		return DisabledStartTime;
 
 	return s->startTime;
 }
@@ -479,8 +462,7 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
 	if (AMI_OVERRIDE)
 		return false;
 
-	return (bool)
-		TransactionIdEquals(xid, s->transactionIdData);
+	return TransactionIdEquals(xid, s->transactionIdData);
 }
 
 
@@ -511,19 +493,6 @@ CommandIdGEScanCommandId(CommandId cid)
 }
 
 
-/* --------------------------------
- *		ClearCommandIdCounterOverflowFlag
- * --------------------------------
- */
-#ifdef NOT_USED
-void
-ClearCommandIdCounterOverflowFlag(void)
-{
-	CommandIdCounterOverflowFlag = false;
-}
-
-#endif
-
 /* --------------------------------
  *		CommandCounterIncrement
  * --------------------------------
@@ -533,10 +502,7 @@ CommandCounterIncrement(void)
 {
 	CurrentTransactionStateData.commandId += 1;
 	if (CurrentTransactionStateData.commandId == FirstCommandId)
-	{
-		CommandIdCounterOverflowFlag = true;
 		elog(ERROR, "You may only have 2^32-1 commands per transaction");
-	}
 
 	CurrentTransactionStateData.scanCommandId = CurrentTransactionStateData.commandId;
 
@@ -551,9 +517,7 @@ CommandCounterIncrement(void)
 void
 SetScanCommandId(CommandId savedId)
 {
-
 	CurrentTransactionStateData.scanCommandId = savedId;
-
 }
 
 /* ----------------------------------------------------------------
@@ -1113,6 +1077,13 @@ AbortTransaction(void)
 	/*
 	 * Let others to know about no transaction in progress - vadim
 	 * 11/26/96
+	 *
+	 * XXX it'd be nice to acquire SInvalLock for this, but too much risk of
+	 * lockup: what if we were holding SInvalLock when we elog'd?  Net effect
+	 * is that we are relying on fetch/store of an xid to be atomic, else
+	 * other backends might see a partially-zeroed xid here.  Would it be
+	 * safe to release spins before we reset xid/xmin?  But see also 
+	 * GetNewTransactionId, which does the same thing.
 	 */
 	if (MyProc != (PROC *) NULL)
 	{
diff --git a/src/backend/access/transam/xid.c b/src/backend/access/transam/xid.c
index 624d6da850c88f2df23ecccf690b73802957206a..9ec40bb2a93b4bb63b904f42673e1dc2ebda95a1 100644
--- a/src/backend/access/transam/xid.c
+++ b/src/backend/access/transam/xid.c
@@ -1,12 +1,12 @@
 /*-------------------------------------------------------------------------
  *
  * xid.c
- *	  POSTGRES transaction identifier type.
+ *	  POSTGRES transaction identifier datatype.
  *
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Id: xid.c,v 1.30 2001/03/22 03:59:18 momjian Exp $
+ *	$Id: xid.c,v 1.31 2001/07/12 04:11:13 tgl Exp $
  *
  * OLD COMMENTS
  * XXX WARNING
@@ -30,12 +30,6 @@
 #define PG_RETURN_TRANSACTIONID(x)	PG_RETURN_UINT32(x)
 
 
-extern TransactionId NullTransactionId;
-extern TransactionId DisabledTransactionId;
-extern TransactionId AmiTransactionId;
-extern TransactionId FirstTransactionId;
-
-/* XXX name for catalogs */
 Datum
 xidin(PG_FUNCTION_ARGS)
 {
@@ -44,7 +38,6 @@ xidin(PG_FUNCTION_ARGS)
 	PG_RETURN_TRANSACTIONID((TransactionId) atol(representation));
 }
 
-/* XXX name for catalogs */
 Datum
 xidout(PG_FUNCTION_ARGS)
 {
@@ -73,15 +66,5 @@ xideq(PG_FUNCTION_ARGS)
 	TransactionId xid1 = PG_GETARG_TRANSACTIONID(0);
 	TransactionId xid2 = PG_GETARG_TRANSACTIONID(1);
 
-	PG_RETURN_BOOL(xid1 == xid2);
-}
-
-/* ----------------------------------------------------------------
- *		TransactionIdAdd
- * ----------------------------------------------------------------
- */
-void
-TransactionIdAdd(TransactionId *xid, int value)
-{
-	*xid += value;
+	PG_RETURN_BOOL(TransactionIdEquals(xid1, xid2));
 }
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 888a50805542d14baaf511caec59eb61d6015979..34bc1e976409c99137851b9ccc3290e5f38c72fc 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.202 2001/07/11 18:38:07 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.203 2001/07/12 04:11:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,18 +16,10 @@
 
 #include <fcntl.h>
 #include <unistd.h>
-#include <time.h>
-#include <sys/time.h>
 #include <sys/types.h>
 #include <sys/file.h>
 #include <sys/stat.h>
 
-#ifndef HAVE_GETRUSAGE
-#include "rusagestub.h"
-#else
-#include <sys/resource.h>
-#endif
-
 #include "access/genam.h"
 #include "access/heapam.h"
 #include "access/xlog.h"
@@ -54,12 +46,6 @@
 
 #include "pgstat.h"
 
-extern XLogRecPtr log_heap_clean(Relation reln, Buffer buffer,
-			   char *unused, int unlen);
-extern XLogRecPtr log_heap_move(Relation reln,
-			  Buffer oldbuf, ItemPointerData from,
-			  Buffer newbuf, HeapTuple newtup);
-
 
 typedef struct VRelListData
 {
@@ -110,7 +96,6 @@ typedef VTupleMoveData *VTupleMove;
 
 typedef struct VRelStats
 {
-	Oid			relid;
 	BlockNumber	rel_pages;
 	double		rel_tuples;
 	Size		min_tlen;
@@ -120,11 +105,6 @@ typedef struct VRelStats
 	VTupleLink	vtlinks;
 } VRelStats;
 
-typedef struct VacRUsage
-{
-	struct timeval	tv;
-	struct rusage	ru;
-} VacRUsage;
 
 static MemoryContext vac_context = NULL;
 
@@ -137,7 +117,8 @@ static TransactionId XmaxRecent;
 static void vacuum_init(void);
 static void vacuum_shutdown(void);
 static VRelList getrels(Name VacRelP, const char *stmttype);
-static void vacuum_rel(Oid relid);
+static void vacuum_rel(Oid relid, VacuumStmt *vacstmt);
+static void full_vacuum_rel(Relation onerel);
 static void scan_heap(VRelStats *vacrelstats, Relation onerel,
 					  VacPageList vacuum_pages, VacPageList fraged_pages);
 static void repair_frag(VRelStats *vacrelstats, Relation onerel,
@@ -164,8 +145,14 @@ static int	vac_cmp_blk(const void *left, const void *right);
 static int	vac_cmp_offno(const void *left, const void *right);
 static int	vac_cmp_vtlinks(const void *left, const void *right);
 static bool enough_space(VacPage vacpage, Size len);
-static void init_rusage(VacRUsage *ru0);
-static char *show_rusage(VacRUsage *ru0);
+
+
+/****************************************************************************
+ *																			*
+ *			Code common to all flavors of VACUUM and ANALYZE				*
+ *																			*
+ ****************************************************************************
+ */
 
 
 /*
@@ -235,17 +222,31 @@ vacuum(VacuumStmt *vacstmt)
 	/*
 	 * Process each selected relation.  We are careful to process
 	 * each relation in a separate transaction in order to avoid holding
-	 * too many locks at one time.
+	 * too many locks at one time.  Also, if we are doing VACUUM ANALYZE,
+	 * the ANALYZE part runs as a separate transaction from the VACUUM
+	 * to further reduce locking.
 	 */
 	for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next)
 	{
 		if (vacstmt->vacuum)
-			vacuum_rel(cur->vrl_relid);
-		/* analyze separately so locking is minimized */
+			vacuum_rel(cur->vrl_relid, vacstmt);
 		if (vacstmt->analyze)
 			analyze_rel(cur->vrl_relid, vacstmt);
 	}
 
+	/*
+	 * If we did a complete vacuum, then flush the init file that relcache.c
+	 * uses to save startup time. The next backend startup will rebuild the
+	 * init file with up-to-date information from pg_class.  This lets the
+	 * optimizer see the stats that we've collected for certain critical
+	 * system indexes.  See relcache.c for more details.
+	 *
+	 * Ignore any failure to unlink the file, since it might not be there if
+	 * no backend has been started since the last vacuum.
+	 */
+	if (vacstmt->vacrel == NULL)
+		unlink(RELCACHE_INIT_FILENAME);
+
 	/* clean up */
 	vacuum_shutdown();
 }
@@ -257,7 +258,7 @@ vacuum(VacuumStmt *vacstmt)
  *		executing concurrently in the same database.  However, there's no
  *		good reason to prevent that, and manually removing lockfiles after
  *		a vacuum crash was a pain for dbadmins.  So, forget about lockfiles,
- *		and just rely on the exclusive lock we grab on each target table
+ *		and just rely on the locks we grab on each target table
  *		to ensure that there aren't two VACUUMs running on the same table
  *		at the same time.
  *
@@ -282,18 +283,6 @@ vacuum_shutdown(void)
 {
 	/* on entry, we are not in a transaction */
 
-	/*
-	 * Flush the init file that relcache.c uses to save startup time. The
-	 * next backend startup will rebuild the init file with up-to-date
-	 * information from pg_class.  This lets the optimizer see the stats
-	 * that we've collected for certain critical system indexes.  See
-	 * relcache.c for more details.
-	 *
-	 * Ignore any failure to unlink the file, since it might not be there if
-	 * no backend has been started since the last vacuum...
-	 */
-	unlink(RELCACHE_INIT_FILENAME);
-
 	/* matches the CommitTransaction in PostgresMain() */
 	StartTransactionCommand();
 
@@ -308,6 +297,9 @@ vacuum_shutdown(void)
 
 /*
  * Build a list of VRelListData nodes for each relation to be processed
+ *
+ * The list is built in vac_context so that it will survive across our
+ * per-relation transactions.
  */
 static VRelList
 getrels(Name VacRelP, const char *stmttype)
@@ -326,7 +318,6 @@ getrels(Name VacRelP, const char *stmttype)
 
 	if (VacRelP)
 	{
-
 		/*
 		 * we could use the cache here, but it is clearer to use scankeys
 		 * for both vacuum cases, bjm 2000/01/19
@@ -344,9 +335,9 @@ getrels(Name VacRelP, const char *stmttype)
 	}
 	else
 	{
-		/* find all relations listed in pg_class */
+		/* find all plain relations listed in pg_class */
 		ScanKeyEntryInitialize(&key, 0x0, Anum_pg_class_relkind,
-							   F_CHAREQ, CharGetDatum('r'));
+							   F_CHAREQ, CharGetDatum(RELKIND_RELATION));
 	}
 
 	vrl = cur = (VRelList) NULL;
@@ -395,11 +386,85 @@ getrels(Name VacRelP, const char *stmttype)
 	return vrl;
 }
 
+
 /*
- *	vacuum_rel() -- vacuum one heap relation
+ *	vac_update_relstats() -- update statistics for one relation
  *
- *		This routine vacuums a single heap, cleans out its indices, and
- *		updates its num_pages and num_tuples statistics.
+ *		Update the whole-relation statistics that are kept in its pg_class
+ *		row.  There are additional stats that will be updated if we are
+ *		doing ANALYZE, but we always update these stats.  This routine works
+ *		for both index and heap relation entries in pg_class.
+ *
+ *		We violate no-overwrite semantics here by storing new values for the
+ *		statistics columns directly into the pg_class tuple that's already on
+ *		the page.  The reason for this is that if we updated these tuples in
+ *		the usual way, vacuuming pg_class itself wouldn't work very well ---
+ *		by the time we got done with a vacuum cycle, most of the tuples in
+ *		pg_class would've been obsoleted.  Of course, this only works for
+ *		fixed-size never-null columns, but these are.
+ *
+ *		This routine is shared by full VACUUM, lazy VACUUM, and stand-alone
+ *		ANALYZE.
+ */
+void
+vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples,
+					bool hasindex)
+{
+	Relation	rd;
+	HeapTupleData rtup;
+	HeapTuple	ctup;
+	Form_pg_class pgcform;
+	Buffer		buffer;
+
+	/*
+	 * update number of tuples and number of pages in pg_class
+	 */
+	rd = heap_openr(RelationRelationName, RowExclusiveLock);
+
+	ctup = SearchSysCache(RELOID,
+						  ObjectIdGetDatum(relid),
+						  0, 0, 0);
+	if (!HeapTupleIsValid(ctup))
+		elog(ERROR, "pg_class entry for relid %u vanished during vacuuming",
+			 relid);
+
+	/* get the buffer cache tuple */
+	rtup.t_self = ctup->t_self;
+	ReleaseSysCache(ctup);
+	heap_fetch(rd, SnapshotNow, &rtup, &buffer, NULL);
+
+	/* overwrite the existing statistics in the tuple */
+	pgcform = (Form_pg_class) GETSTRUCT(&rtup);
+	pgcform->relpages = (int32) num_pages;
+	pgcform->reltuples = num_tuples;
+	pgcform->relhasindex = hasindex;
+
+	/* invalidate the tuple in the cache and write the buffer */
+	RelationInvalidateHeapTuple(rd, &rtup);
+	WriteBuffer(buffer);
+
+	heap_close(rd, RowExclusiveLock);
+}
+
+
+/****************************************************************************
+ *																			*
+ *			Code common to both flavors of VACUUM							*
+ *																			*
+ ****************************************************************************
+ */
+
+
+/* XXX Temporary placeholder */
+static void
+lazy_vacuum_rel(Relation onerel)
+{
+	full_vacuum_rel(onerel);
+}
+
+
+/*
+ *	vacuum_rel() -- vacuum one heap relation
  *
  *		Doing one heap at a time incurs extra overhead, since we need to
  *		check that the heap exists again just before we vacuum it.	The
@@ -410,19 +475,11 @@ getrels(Name VacRelP, const char *stmttype)
  *		At entry and exit, we are not inside a transaction.
  */
 static void
-vacuum_rel(Oid relid)
+vacuum_rel(Oid relid, VacuumStmt *vacstmt)
 {
+	LOCKMODE	lmode;
 	Relation	onerel;
 	LockRelId	onerelid;
-	VacPageListData vacuum_pages;		/* List of pages to vacuum and/or
-										 * clean indices */
-	VacPageListData fraged_pages;		/* List of pages with space enough
-										 * for re-using */
-	Relation   *Irel;
-	int32		nindices,
-				i;
-	VRelStats  *vacrelstats;
-	bool		reindex = false;
 	Oid			toast_relid;
 
 	/* Begin a transaction for vacuuming this relation */
@@ -447,7 +504,15 @@ vacuum_rel(Oid relid)
 	}
 
 	/*
-	 * Open the class, get an exclusive lock on it, and check permissions.
+	 * Determine the type of lock we want --- hard exclusive lock for a
+	 * FULL vacuum, but just ShareUpdateExclusiveLock for concurrent
+	 * vacuum.  Either way, we can be sure that no other backend is vacuuming
+	 * the same table.
+	 */
+	lmode = vacstmt->full ? AccessExclusiveLock : ShareUpdateExclusiveLock;
+
+	/*
+	 * Open the class, get an appropriate lock on it, and check permissions.
 	 *
 	 * We allow the user to vacuum a table if he is superuser, the table
 	 * owner, or the database owner (but in the latter case, only if it's
@@ -456,7 +521,7 @@ vacuum_rel(Oid relid)
 	 * Note we choose to treat permissions failure as a NOTICE and keep
 	 * trying to vacuum the rest of the DB --- is this appropriate?
 	 */
-	onerel = heap_open(relid, AccessExclusiveLock);
+	onerel = heap_open(relid, lmode);
 
 	if (! (pg_ownercheck(GetUserId(), RelationGetRelationName(onerel),
 						 RELNAME) ||
@@ -464,47 +529,109 @@ vacuum_rel(Oid relid)
 	{
 		elog(NOTICE, "Skipping \"%s\" --- only table or database owner can VACUUM it",
 			 RelationGetRelationName(onerel));
-		heap_close(onerel, AccessExclusiveLock);
+		heap_close(onerel, lmode);
 		CommitTransactionCommand();
 		return;
 	}
 
 	/*
-	 * Get a session-level exclusive lock too.	This will protect our
-	 * exclusive access to the relation across multiple transactions, so
-	 * that we can vacuum the relation's TOAST table (if any) secure in
-	 * the knowledge that no one is diddling the parent relation.
+	 * Get a session-level lock too. This will protect our access to the
+	 * relation across multiple transactions, so that we can vacuum the
+	 * relation's TOAST table (if any) secure in the knowledge that no one
+	 * is deleting the parent relation.
 	 *
 	 * NOTE: this cannot block, even if someone else is waiting for access,
 	 * because the lock manager knows that both lock requests are from the
 	 * same process.
 	 */
 	onerelid = onerel->rd_lockInfo.lockRelId;
-	LockRelationForSession(&onerelid, AccessExclusiveLock);
+	LockRelationForSession(&onerelid, lmode);
 
 	/*
 	 * Remember the relation's TOAST relation for later
 	 */
 	toast_relid = onerel->rd_rel->reltoastrelid;
 
+	/*
+	 * Do the actual work --- either FULL or "lazy" vacuum
+	 */
+	if (vacstmt->full)
+		full_vacuum_rel(onerel);
+	else
+		lazy_vacuum_rel(onerel);
+
+	/* all done with this class, but hold lock until commit */
+	heap_close(onerel, NoLock);
+
+	/*
+	 * Complete the transaction and free all temporary memory used.
+	 */
+	CommitTransactionCommand();
+
+	/*
+	 * If the relation has a secondary toast rel, vacuum that too while we
+	 * still hold the session lock on the master table.  Note however that
+	 * "analyze" will not get done on the toast table.  This is good,
+	 * because the toaster always uses hardcoded index access and statistics
+	 * are totally unimportant for toast relations.
+	 */
+	if (toast_relid != InvalidOid)
+		vacuum_rel(toast_relid, vacstmt);
+
+	/*
+	 * Now release the session-level lock on the master table.
+	 */
+	UnlockRelationForSession(&onerelid, lmode);
+}
+
+
+/****************************************************************************
+ *																			*
+ *			Code for VACUUM FULL (only)										*
+ *																			*
+ ****************************************************************************
+ */
+
+
+/*
+ *	full_vacuum_rel() -- perform FULL VACUUM for one heap relation
+ *
+ *		This routine vacuums a single heap, cleans out its indices, and
+ *		updates its num_pages and num_tuples statistics.
+ *
+ *		At entry, we have already established a transaction and opened
+ *		and locked the relation.
+ */
+static void
+full_vacuum_rel(Relation onerel)
+{
+	VacPageListData vacuum_pages;		/* List of pages to vacuum and/or
+										 * clean indices */
+	VacPageListData fraged_pages;		/* List of pages with space enough
+										 * for re-using */
+	Relation   *Irel;
+	int32		nindices,
+				i;
+	VRelStats  *vacrelstats;
+	bool		reindex = false;
+
+	if (IsIgnoringSystemIndexes() &&
+		IsSystemRelationName(RelationGetRelationName(onerel)))
+		reindex = true;
+
+	GetXmaxRecent(&XmaxRecent);
+
 	/*
 	 * Set up statistics-gathering machinery.
 	 */
 	vacrelstats = (VRelStats *) palloc(sizeof(VRelStats));
-	vacrelstats->relid = relid;
 	vacrelstats->rel_pages = 0;
 	vacrelstats->rel_tuples = 0;
 	vacrelstats->hasindex = false;
 
-	GetXmaxRecent(&XmaxRecent);
-
-	/* scan it */
-	reindex = false;
+	/* scan the heap */
 	vacuum_pages.num_pages = fraged_pages.num_pages = 0;
 	scan_heap(vacrelstats, onerel, &vacuum_pages, &fraged_pages);
-	if (IsIgnoringSystemIndexes() &&
-		IsSystemRelationName(RelationGetRelationName(onerel)))
-		reindex = true;
 
 	/* Now open all indices of the relation */
 	nindices = 0;
@@ -516,8 +643,6 @@ vacuum_rel(Oid relid)
 		reindex = true;
 	if (nindices > 0)
 		vacrelstats->hasindex = true;
-	else
-		vacrelstats->hasindex = false;
 
 #ifdef NOT_USED
 	/*
@@ -528,7 +653,7 @@ vacuum_rel(Oid relid)
 	{
 		close_indices(nindices, Irel);
 		Irel = (Relation *) NULL;
-		activate_indexes_of_a_table(relid, false);
+		activate_indexes_of_a_table(RelationGetRelid(onerel), false);
 	}
 #endif	 /* NOT_USED */
 
@@ -574,46 +699,25 @@ vacuum_rel(Oid relid)
 			 */
 			i = FlushRelationBuffers(onerel, vacrelstats->rel_pages);
 			if (i < 0)
-				elog(ERROR, "VACUUM (vacuum_rel): FlushRelationBuffers returned %d",
+				elog(ERROR, "VACUUM (full_vacuum_rel): FlushRelationBuffers returned %d",
 					 i);
 		}
 	}
+
 #ifdef NOT_USED
 	if (reindex)
-		activate_indexes_of_a_table(relid, true);
+		activate_indexes_of_a_table(RelationGetRelid(onerel), true);
 #endif	 /* NOT_USED */
 
 	/* update shared free space map with final free space info */
 	vac_update_fsm(onerel, &fraged_pages, vacrelstats->rel_pages);
 
-	/* all done with this class, but hold lock until commit */
-	heap_close(onerel, NoLock);
-
 	/* update statistics in pg_class */
-	vac_update_relstats(vacrelstats->relid, vacrelstats->rel_pages,
+	vac_update_relstats(RelationGetRelid(onerel), vacrelstats->rel_pages,
 						vacrelstats->rel_tuples, vacrelstats->hasindex);
-
-	/*
-	 * Complete the transaction and free all temporary memory used.
-	 */
-	CommitTransactionCommand();
-
-	/*
-	 * If the relation has a secondary toast one, vacuum that too while we
-	 * still hold the session lock on the master table. We don't need to
-	 * propagate "analyze" to it, because the toaster always uses
-	 * hardcoded index access and statistics are totally unimportant for
-	 * toast relations
-	 */
-	if (toast_relid != InvalidOid)
-		vacuum_rel(toast_relid);
-
-	/*
-	 * Now release the session-level lock on the master table.
-	 */
-	UnlockRelationForSession(&onerelid, AccessExclusiveLock);
 }
 
+
 /*
  *	scan_heap() -- scan an open heap relation
  *
@@ -621,7 +725,7 @@ vacuum_rel(Oid relid)
  *		of pages we need to compact free space on and/or clean indexes of
  *		deleted tuples), constructs fraged_pages (list of pages with free
  *		space that tuples could be moved into), and calculates statistics
- *		on the number of live tuples in a heap.
+ *		on the number of live tuples in the heap.
  */
 static void
 scan_heap(VRelStats *vacrelstats, Relation onerel,
@@ -647,8 +751,7 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 	double		num_tuples,
 				tups_vacuumed,
 				nkeep,
-				nunused,
-				ncrash;
+				nunused;
 	double		free_size,
 				usable_free_size;
 	Size		min_tlen = MaxTupleSize;
@@ -660,13 +763,13 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 	int			free_vtlinks = 100;
 	VacRUsage	ru0;
 
-	init_rusage(&ru0);
+	vac_init_rusage(&ru0);
 
 	relname = RelationGetRelationName(onerel);
 	elog(MESSAGE_LEVEL, "--Relation %s--", relname);
 
 	empty_pages = new_pages = changed_pages = empty_end_pages = 0;
-	num_tuples = tups_vacuumed = nkeep = nunused = ncrash = 0;
+	num_tuples = tups_vacuumed = nkeep = nunused = 0;
 	free_size = 0;
 
 	nblocks = RelationGetNumberOfBlocks(onerel);
@@ -727,6 +830,8 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 			 offnum <= maxoff;
 			 offnum = OffsetNumberNext(offnum))
 		{
+			uint16		sv_infomask;
+
 			itemid = PageGetItemId(page, offnum);
 
 			/*
@@ -744,146 +849,31 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 			tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
 			tuple.t_len = ItemIdGetLength(itemid);
 			ItemPointerSet(&(tuple.t_self), blkno, offnum);
-			tupgone = false;
 
-			if (!(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED))
-			{
-				if (tuple.t_data->t_infomask & HEAP_XMIN_INVALID)
-					tupgone = true;
-				else if (tuple.t_data->t_infomask & HEAP_MOVED_OFF)
-				{
-					if (TransactionIdDidCommit((TransactionId)
-											   tuple.t_data->t_cmin))
-					{
-						tuple.t_data->t_infomask |= HEAP_XMIN_INVALID;
-						pgchanged = true;
-						tupgone = true;
-					}
-					else
-					{
-						tuple.t_data->t_infomask |= HEAP_XMIN_COMMITTED;
-						pgchanged = true;
-					}
-				}
-				else if (tuple.t_data->t_infomask & HEAP_MOVED_IN)
-				{
-					if (!TransactionIdDidCommit((TransactionId)
-												tuple.t_data->t_cmin))
-					{
-						tuple.t_data->t_infomask |= HEAP_XMIN_INVALID;
-						pgchanged = true;
-						tupgone = true;
-					}
-					else
-					{
-						tuple.t_data->t_infomask |= HEAP_XMIN_COMMITTED;
-						pgchanged = true;
-					}
-				}
-				else
-				{
-					if (TransactionIdDidAbort(tuple.t_data->t_xmin))
-						tupgone = true;
-					else if (TransactionIdDidCommit(tuple.t_data->t_xmin))
-					{
-						tuple.t_data->t_infomask |= HEAP_XMIN_COMMITTED;
-						pgchanged = true;
-					}
-					else if (!TransactionIdIsInProgress(tuple.t_data->t_xmin))
-					{
-						/*
-						 * Not Aborted, Not Committed, Not in Progress -
-						 * so it's from crashed process. - vadim 11/26/96
-						 */
-						ncrash += 1;
-						tupgone = true;
-					}
-					else
-					{
-						elog(NOTICE, "Rel %s: TID %u/%u: InsertTransactionInProgress %u - can't shrink relation",
-						   relname, blkno, offnum, tuple.t_data->t_xmin);
-						do_shrinking = false;
-					}
-				}
-			}
+			tupgone = false;
+			sv_infomask = tuple.t_data->t_infomask;
 
-			/*
-			 * here we are concerned about tuples with xmin committed and
-			 * xmax unknown or committed
-			 */
-			if (tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED &&
-				!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID))
+			switch (HeapTupleSatisfiesVacuum(tuple.t_data, XmaxRecent))
 			{
-				if (tuple.t_data->t_infomask & HEAP_XMAX_COMMITTED)
-				{
-					if (tuple.t_data->t_infomask & HEAP_MARKED_FOR_UPDATE)
-					{
-						tuple.t_data->t_infomask |= HEAP_XMAX_INVALID;
-						tuple.t_data->t_infomask &=
-							~(HEAP_XMAX_COMMITTED | HEAP_MARKED_FOR_UPDATE);
-						pgchanged = true;
-					}
-					else
-						tupgone = true;
-				}
-				else if (TransactionIdDidAbort(tuple.t_data->t_xmax))
-				{
-					tuple.t_data->t_infomask |= HEAP_XMAX_INVALID;
-					pgchanged = true;
-				}
-				else if (TransactionIdDidCommit(tuple.t_data->t_xmax))
-				{
-					if (tuple.t_data->t_infomask & HEAP_MARKED_FOR_UPDATE)
-					{
-						tuple.t_data->t_infomask |= HEAP_XMAX_INVALID;
-						tuple.t_data->t_infomask &=
-							~(HEAP_XMAX_COMMITTED | HEAP_MARKED_FOR_UPDATE);
-						pgchanged = true;
-					}
-					else
-						tupgone = true;
-				}
-				else if (!TransactionIdIsInProgress(tuple.t_data->t_xmax))
-				{
+				case HEAPTUPLE_DEAD:
+					tupgone = true;	/* we can delete the tuple */
+					break;
+				case HEAPTUPLE_LIVE:
+					break;
+				case HEAPTUPLE_RECENTLY_DEAD:
 					/*
-					 * Not Aborted, Not Committed, Not in Progress - so it
-					 * from crashed process. - vadim 06/02/97
+					 * If tuple is recently deleted then we must not remove
+					 * it from relation.
 					 */
-					tuple.t_data->t_infomask |= HEAP_XMAX_INVALID;
-					tuple.t_data->t_infomask &=
-						~(HEAP_XMAX_COMMITTED | HEAP_MARKED_FOR_UPDATE);
-					pgchanged = true;
-				}
-				else
-				{
-					elog(NOTICE, "Rel %s: TID %u/%u: DeleteTransactionInProgress %u - can't shrink relation",
-						 relname, blkno, offnum, tuple.t_data->t_xmax);
-					do_shrinking = false;
-				}
-
-				/*
-				 * If tuple is recently deleted then we must not remove it
-				 * from relation.
-				 */
-				if (tupgone &&
-					(tuple.t_data->t_infomask & HEAP_XMIN_INVALID) == 0 &&
-					tuple.t_data->t_xmax >= XmaxRecent)
-				{
-					tupgone = false;
 					nkeep += 1;
-					if (!(tuple.t_data->t_infomask & HEAP_XMAX_COMMITTED))
-					{
-						tuple.t_data->t_infomask |= HEAP_XMAX_COMMITTED;
-						pgchanged = true;
-					}
-
 					/*
 					 * If we do shrinking and this tuple is updated one
 					 * then remember it to construct updated tuple
 					 * dependencies.
 					 */
-					if (do_shrinking && !(ItemPointerEquals(&(tuple.t_self),
-											   &(tuple.t_data->t_ctid))))
+					if (do_shrinking &&
+						!(ItemPointerEquals(&(tuple.t_self),
+											&(tuple.t_data->t_ctid))))
 					{
 						if (free_vtlinks == 0)
 						{
@@ -897,17 +887,40 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 						free_vtlinks--;
 						num_vtlinks++;
 					}
-				}
+					break;
+				case HEAPTUPLE_INSERT_IN_PROGRESS:
+					/*
+					 * This should not happen, since we hold exclusive lock
+					 * on the relation; shouldn't we raise an error?
+					 */
+					elog(NOTICE, "Rel %s: TID %u/%u: InsertTransactionInProgress %u - can't shrink relation",
+						 relname, blkno, offnum, tuple.t_data->t_xmin);
+					do_shrinking = false;
+					break;
+				case HEAPTUPLE_DELETE_IN_PROGRESS:
+					/*
+					 * This should not happen, since we hold exclusive lock
+					 * on the relation; shouldn't we raise an error?
+					 */
+					elog(NOTICE, "Rel %s: TID %u/%u: DeleteTransactionInProgress %u - can't shrink relation",
+						 relname, blkno, offnum, tuple.t_data->t_xmax);
+					do_shrinking = false;
+					break;
+				default:
+					elog(ERROR, "Unexpected HeapTupleSatisfiesVacuum result");
+					break;
 			}
 
+			/* check for hint-bit update by HeapTupleSatisfiesVacuum */
+			if (sv_infomask != tuple.t_data->t_infomask)
+				pgchanged = true;
+
 			/*
 			 * Other checks...
 			 */
 			if (!OidIsValid(tuple.t_data->t_oid))
-			{
 				elog(NOTICE, "Rel %s: TID %u/%u: OID IS INVALID. TUPGONE %d.",
-					 relname, blkno, offnum, tupgone);
-			}
+					 relname, blkno, offnum, (int) tupgone);
 
 			if (tupgone)
 			{
@@ -946,7 +959,7 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 				if (tuple.t_len > max_tlen)
 					max_tlen = tuple.t_len;
 			}
-		}
+		} /* scan along page */
 
 		if (tempPage != (Page) NULL)
 		{
@@ -1043,15 +1056,15 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
 	}
 
 	elog(MESSAGE_LEVEL, "Pages %u: Changed %u, reaped %u, Empty %u, New %u; \
-Tup %.0f: Vac %.0f, Keep/VTL %.0f/%u, Crash %.0f, UnUsed %.0f, MinLen %lu, MaxLen %lu; \
+Tup %.0f: Vac %.0f, Keep/VTL %.0f/%u, UnUsed %.0f, MinLen %lu, MaxLen %lu; \
 Re-using: Free/Avail. Space %.0f/%.0f; EndEmpty/Avail. Pages %u/%u. %s",
 		 nblocks, changed_pages, vacuum_pages->num_pages, empty_pages,
 		 new_pages, num_tuples, tups_vacuumed,
-		 nkeep, vacrelstats->num_vtlinks, ncrash,
+		 nkeep, vacrelstats->num_vtlinks,
 		 nunused, (unsigned long) min_tlen, (unsigned long) max_tlen,
 		 free_size, usable_free_size,
 		 empty_end_pages, fraged_pages->num_pages,
-		 show_rusage(&ru0));
+		 vac_show_rusage(&ru0));
 
 }
 
@@ -1113,7 +1126,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 				chain_tuple_moved;
 	VacRUsage	ru0;
 
-	init_rusage(&ru0);
+	vac_init_rusage(&ru0);
 
 	myXID = GetCurrentTransactionId();
 	myCID = GetCurrentCommandId();
@@ -1306,9 +1319,10 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 			 * tuples to another places.
 			 */
 			if ((tuple.t_data->t_infomask & HEAP_UPDATED &&
-				 tuple.t_data->t_xmin >= XmaxRecent) ||
+				 !TransactionIdPrecedes(tuple.t_data->t_xmin, XmaxRecent)) ||
 				(!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID) &&
-				 !(ItemPointerEquals(&(tuple.t_self), &(tuple.t_data->t_ctid)))))
+				 !(ItemPointerEquals(&(tuple.t_self),
+									 &(tuple.t_data->t_ctid)))))
 			{
 				Buffer		Cbuf = buf;
 				Page		Cpage;
@@ -1338,7 +1352,8 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 				 * we have to move to the end of chain.
 				 */
 				while (!(tp.t_data->t_infomask & HEAP_XMAX_INVALID) &&
-				!(ItemPointerEquals(&(tp.t_self), &(tp.t_data->t_ctid))))
+					   !(ItemPointerEquals(&(tp.t_self),
+										   &(tp.t_data->t_ctid))))
 				{
 					Ctid = tp.t_data->t_ctid;
 					if (freeCbuf)
@@ -1422,7 +1437,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 
 					/* All done ? */
 					if (!(tp.t_data->t_infomask & HEAP_UPDATED) ||
-						tp.t_data->t_xmin < XmaxRecent)
+						TransactionIdPrecedes(tp.t_data->t_xmin, XmaxRecent))
 						break;
 
 					/* Well, try to find tuple with old row version */
@@ -1470,7 +1485,8 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 						 * latter, and we are too close to 6.5 release. -
 						 * vadim 06/11/99
 						 */
-						if (Ptp.t_data->t_xmax != tp.t_data->t_xmin)
+						if (!(TransactionIdEquals(Ptp.t_data->t_xmax,
+												  tp.t_data->t_xmin)))
 						{
 							if (freeCbuf)
 								ReleaseBuffer(Cbuf);
@@ -1495,7 +1511,8 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 						 * removed.
 						 */
 						if (Ptp.t_data->t_infomask & HEAP_UPDATED &&
-							Ptp.t_data->t_xmin == Ptp.t_data->t_xmax)
+							TransactionIdEquals(Ptp.t_data->t_xmin,
+												Ptp.t_data->t_xmax))
 						{
 							TransactionIdStore(myXID,
 								(TransactionId *) &(Ptp.t_data->t_cmin));
@@ -1959,7 +1976,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 	elog(MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u; Tuple(s) moved: %u. %s",
 		 RelationGetRelationName(onerel),
 		 nblocks, blkno, num_moved,
-		 show_rusage(&ru0));
+		 vac_show_rusage(&ru0));
 
 	/*
 	 * Reflect the motion of system tuples to catalog cache here.
@@ -2185,7 +2202,7 @@ scan_index(Relation indrel, double num_tuples)
 	double		nitups;
 	VacRUsage	ru0;
 
-	init_rusage(&ru0);
+	vac_init_rusage(&ru0);
 
 	/* walk through the entire index */
 	iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL);
@@ -2206,7 +2223,7 @@ scan_index(Relation indrel, double num_tuples)
 
 	elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %.0f. %s",
 		 RelationGetRelationName(indrel), nipages, nitups,
-		 show_rusage(&ru0));
+		 vac_show_rusage(&ru0));
 
 	/*
 	 * Check for tuple count mismatch.  If the index is partial, then
@@ -2247,7 +2264,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
 	VacPage		vp;
 	VacRUsage	ru0;
 
-	init_rusage(&ru0);
+	vac_init_rusage(&ru0);
 
 	/* walk through the entire index */
 	iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL);
@@ -2293,7 +2310,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
 	elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %.0f: Deleted %u. %s",
 		 RelationGetRelationName(indrel), num_pages,
 		 num_index_tuples - keep_tuples, tups_vacuumed,
-		 show_rusage(&ru0));
+		 vac_show_rusage(&ru0));
 
 	/*
 	 * Check for tuple count mismatch.  If the index is partial, then
@@ -2358,63 +2375,6 @@ tid_reaped(ItemPointer itemptr, VacPageList vacpagelist)
 	return vp;
 }
 
-/*
- *	vac_update_relstats() -- update statistics for one relation
- *
- *		Update the whole-relation statistics that are kept in its pg_class
- *		row.  There are additional stats that will be updated if we are
- *		doing VACUUM ANALYZE, but we always update these stats.
- *
- *		This routine works for both index and heap relation entries in
- *		pg_class.  We violate no-overwrite semantics here by storing new
- *		values for the statistics columns directly into the pg_class
- *		tuple that's already on the page.  The reason for this is that if
- *		we updated these tuples in the usual way, vacuuming pg_class itself
- *		wouldn't work very well --- by the time we got done with a vacuum
- *		cycle, most of the tuples in pg_class would've been obsoleted.
- *		Of course, this only works for fixed-size never-null columns, but
- *		these are.
- */
-void
-vac_update_relstats(Oid relid, BlockNumber num_pages, double num_tuples,
-					bool hasindex)
-{
-	Relation	rd;
-	HeapTupleData rtup;
-	HeapTuple	ctup;
-	Form_pg_class pgcform;
-	Buffer		buffer;
-
-	/*
-	 * update number of tuples and number of pages in pg_class
-	 */
-	rd = heap_openr(RelationRelationName, RowExclusiveLock);
-
-	ctup = SearchSysCache(RELOID,
-						  ObjectIdGetDatum(relid),
-						  0, 0, 0);
-	if (!HeapTupleIsValid(ctup))
-		elog(ERROR, "pg_class entry for relid %u vanished during vacuuming",
-			 relid);
-
-	/* get the buffer cache tuple */
-	rtup.t_self = ctup->t_self;
-	ReleaseSysCache(ctup);
-	heap_fetch(rd, SnapshotNow, &rtup, &buffer, NULL);
-
-	/* overwrite the existing statistics in the tuple */
-	pgcform = (Form_pg_class) GETSTRUCT(&rtup);
-	pgcform->relpages = (int32) num_pages;
-	pgcform->reltuples = num_tuples;
-	pgcform->relhasindex = hasindex;
-
-	/* invalidate the tuple in the cache and write the buffer */
-	RelationInvalidateHeapTuple(rd, &rtup);
-	WriteBuffer(buffer);
-
-	heap_close(rd, RowExclusiveLock);
-}
-
 /*
  * Update the shared Free Space Map with the info we now have about
  * free space in the relation, discarding any old info the map may have.
@@ -2683,8 +2643,8 @@ enough_space(VacPage vacpage, Size len)
 /*
  * Initialize usage snapshot.
  */
-static void
-init_rusage(VacRUsage *ru0)
+void
+vac_init_rusage(VacRUsage *ru0)
 {
 	struct timezone tz;
 
@@ -2698,13 +2658,13 @@ init_rusage(VacRUsage *ru0)
  * tacky, but no one ever claimed that the Postgres backend is
  * threadable...
  */
-static char *
-show_rusage(VacRUsage *ru0)
+const char *
+vac_show_rusage(VacRUsage *ru0)
 {
 	static char result[100];
 	VacRUsage	ru1;
 
-	init_rusage(&ru1);
+	vac_init_rusage(&ru1);
 
 	if (ru1.tv.tv_usec < ru0->tv.tv_usec)
 	{
diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c
index 5794207eb1882755efa2400dc72874584d8e073a..a9b9046702c5ba4186bb155dba18781ed3e7fcb5 100644
--- a/src/backend/storage/ipc/sinval.c
+++ b/src/backend/storage/ipc/sinval.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinval.c,v 1.35 2001/07/06 21:04:26 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/ipc/sinval.c,v 1.36 2001/07/12 04:11:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -194,7 +194,7 @@ TransactionIdIsInProgress(TransactionId xid)
 		{
 			PROC	   *proc = (PROC *) MAKE_PTR(pOffset);
 
-			if (proc->xid == xid)
+			if (TransactionIdEquals(proc->xid, xid))
 			{
 				result = true;
 				break;
@@ -212,15 +212,20 @@ TransactionIdIsInProgress(TransactionId xid)
  *					when all current transaction were started.
  *					It's used by vacuum to decide what deleted
  *					tuples must be preserved in a table.
+ *
+ * Note: we include all currently running xids in the set of considered xids.
+ * This ensures that if a just-started xact has not yet set its snapshot,
+ * when it does set the snapshot it cannot set xmin less than what we compute.
  */
 void
 GetXmaxRecent(TransactionId *XmaxRecent)
 {
 	SISeg	   *segP = shmInvalBuffer;
 	ProcState  *stateP = segP->procState;
+	TransactionId result;
 	int			index;
 
-	*XmaxRecent = GetCurrentTransactionId();
+	result = GetCurrentTransactionId();
 
 	SpinAcquire(SInvalLock);
 
@@ -231,18 +236,24 @@ GetXmaxRecent(TransactionId *XmaxRecent)
 		if (pOffset != INVALID_OFFSET)
 		{
 			PROC	   *proc = (PROC *) MAKE_PTR(pOffset);
-			TransactionId xmin;
+			TransactionId xid;
 
-			xmin = proc->xmin;	/* we don't use spin-locking in
-								 * AbortTransaction() ! */
-			if (proc == MyProc || xmin < FirstTransactionId)
-				continue;
-			if (xmin < *XmaxRecent)
-				*XmaxRecent = xmin;
+			xid = proc->xid;
+			if (! TransactionIdIsSpecial(xid))
+			{
+				if (TransactionIdPrecedes(xid, result))
+					result = xid;
+				xid = proc->xmin;
+				if (! TransactionIdIsSpecial(xid))
+					if (TransactionIdPrecedes(xid, result))
+						result = xid;
+			}
 		}
 	}
 
 	SpinRelease(SInvalLock);
+
+	*XmaxRecent = result;
 }
 
 /*
@@ -291,28 +302,21 @@ GetSnapshotData(bool serializable)
 		if (pOffset != INVALID_OFFSET)
 		{
 			PROC	   *proc = (PROC *) MAKE_PTR(pOffset);
-			TransactionId xid;
+			TransactionId xid = proc->xid;
 
 			/*
-			 * We don't use spin-locking when changing proc->xid in
-			 * GetNewTransactionId() and in AbortTransaction() !..
+			 * Ignore my own proc (dealt with my xid above), procs not
+			 * running a transaction, and xacts started since we read
+			 * the next transaction ID.  There's no need to store XIDs
+			 * above what we got from ReadNewTransactionId, since we'll
+			 * treat them as running anyway.
 			 */
-			xid = proc->xid;
 			if (proc == MyProc ||
-				xid < FirstTransactionId || xid >= snapshot->xmax)
-			{
-
-				/*--------
-				 * Seems that there is no sense to store
-				 * 		xid >= snapshot->xmax
-				 * (what we got from ReadNewTransactionId above)
-				 * in snapshot->xip.  We just assume that all xacts
-				 * with such xid-s are running and may be ignored.
-				 *--------
-				 */
+				TransactionIdIsSpecial(xid) ||
+				! TransactionIdPrecedes(xid, snapshot->xmax))
 				continue;
-			}
-			if (xid < snapshot->xmin)
+
+			if (TransactionIdPrecedes(xid, snapshot->xmin))
 				snapshot->xmin = xid;
 			snapshot->xip[count] = xid;
 			count++;
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index a8ed65afceea126601376e3c85dd8f6e86f0192d..2dd56b6f08e9fa8249ff441ca9d31e1e5aa007f4 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -8,18 +8,18 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.37 2001/01/24 19:43:18 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.38 2001/07/12 04:11:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
-/* #define TQUALDEBUG	1 */
-
 #include "postgres.h"
 
+#include "storage/sinval.h"
 #include "utils/tqual.h"
 
-SnapshotData SnapshotDirtyData;
+
+static SnapshotData SnapshotDirtyData;
 Snapshot	SnapshotDirty = &SnapshotDirtyData;
 
 Snapshot	QuerySnapshot = NULL;
@@ -587,10 +587,138 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
 	return false;
 }
 
+
+/*
+ * HeapTupleSatisfiesVacuum - determine tuple status for VACUUM and related
+ *		operations
+ *
+ * XmaxRecent is a cutoff XID (obtained from GetXmaxRecent()).  Tuples
+ * deleted by XIDs >= XmaxRecent are deemed "recently dead"; they might
+ * still be visible to some open transaction, so we can't remove them,
+ * even if we see that the deleting transaction has committed.
+ *
+ * As with the other HeapTupleSatisfies routines, we may update the tuple's
+ * "hint" status bits if we see that the inserting or deleting transaction
+ * has now committed or aborted.  The caller is responsible for noticing any
+ * change in t_infomask and scheduling a disk write if so.
+ */
+HTSV_Result
+HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId XmaxRecent)
+{
+	/*
+	 * Has inserting transaction committed?
+	 *
+	 * If the inserting transaction aborted, then the tuple was never visible
+	 * to any other transaction, so we can delete it immediately.
+	 */
+	if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))
+	{
+		if (tuple->t_infomask & HEAP_XMIN_INVALID)
+			return HEAPTUPLE_DEAD;
+		else if (tuple->t_infomask & HEAP_MOVED_OFF)
+		{
+			if (TransactionIdDidCommit((TransactionId) tuple->t_cmin))
+			{
+				tuple->t_infomask |= HEAP_XMIN_INVALID;
+				return HEAPTUPLE_DEAD;
+			}
+			/* Assume we can only get here if previous VACUUM aborted, */
+			/* ie, it couldn't still be in progress */
+			tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+		}
+		else if (tuple->t_infomask & HEAP_MOVED_IN)
+		{
+			if (!TransactionIdDidCommit((TransactionId) tuple->t_cmin))
+			{
+				/* Assume we can only get here if previous VACUUM aborted */
+				tuple->t_infomask |= HEAP_XMIN_INVALID;
+				return HEAPTUPLE_DEAD;
+			}
+			tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+		}
+		else if (TransactionIdDidAbort(tuple->t_xmin))
+		{
+			tuple->t_infomask |= HEAP_XMIN_INVALID;
+			return HEAPTUPLE_DEAD;
+		}
+		else if (TransactionIdDidCommit(tuple->t_xmin))
+			tuple->t_infomask |= HEAP_XMIN_COMMITTED;
+		else if (TransactionIdIsInProgress(tuple->t_xmin))
+			return HEAPTUPLE_INSERT_IN_PROGRESS;
+		else
+		{
+			/*
+			 * Not Aborted, Not Committed, Not in Progress -
+			 * so it's from crashed process. - vadim 11/26/96
+			 */
+			tuple->t_infomask |= HEAP_XMIN_INVALID;
+			return HEAPTUPLE_DEAD;
+		}
+		/* Should only get here if we set XMIN_COMMITTED */
+		Assert(tuple->t_infomask & HEAP_XMIN_COMMITTED);
+	}
+
+	/*
+	 * Okay, the inserter committed, so it was good at some point.  Now
+	 * what about the deleting transaction?
+	 */
+	if (tuple->t_infomask & HEAP_XMAX_INVALID)
+		return HEAPTUPLE_LIVE;
+
+	if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
+	{
+		if (TransactionIdDidAbort(tuple->t_xmax))
+		{
+			tuple->t_infomask |= HEAP_XMAX_INVALID;
+			return HEAPTUPLE_LIVE;
+		}
+		else if (TransactionIdDidCommit(tuple->t_xmax))
+			tuple->t_infomask |= HEAP_XMAX_COMMITTED;
+		else if (TransactionIdIsInProgress(tuple->t_xmax))
+			return HEAPTUPLE_DELETE_IN_PROGRESS;
+		else
+		{
+			/*
+			 * Not Aborted, Not Committed, Not in Progress -
+			 * so it's from crashed process. - vadim 06/02/97
+			 */
+			tuple->t_infomask |= HEAP_XMAX_INVALID;
+			return HEAPTUPLE_LIVE;
+		}
+		/* Should only get here if we set XMAX_COMMITTED */
+		Assert(tuple->t_infomask & HEAP_XMAX_COMMITTED);
+	}
+
+	/*
+	 * Deleter committed, but check special cases.
+	 */
+
+	if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
+	{
+		/* "deleting" xact really only marked it for update */
+		return HEAPTUPLE_LIVE;
+	}
+
+	if (TransactionIdEquals(tuple->t_xmin, tuple->t_xmax))
+	{
+		/* inserter also deleted it, so it was never visible to anyone else */
+		return HEAPTUPLE_DEAD;
+	}
+
+	if (!TransactionIdPrecedes(tuple->t_xmax, XmaxRecent))
+	{
+		/* deleting xact is too recent, tuple could still be visible */
+		return HEAPTUPLE_RECENTLY_DEAD;
+	}
+
+	/* Otherwise, it's dead and removable */
+	return HEAPTUPLE_DEAD;
+}
+
+
 void
 SetQuerySnapshot(void)
 {
-
 	/* Initialize snapshot overriding to false */
 	ReferentialIntegritySnapshotOverride = false;
 
@@ -615,13 +743,11 @@ SetQuerySnapshot(void)
 		QuerySnapshot = GetSnapshotData(false);
 
 	Assert(QuerySnapshot != NULL);
-
 }
 
 void
 FreeXactSnapshot(void)
 {
-
 	if (QuerySnapshot != NULL && QuerySnapshot != SerializableSnapshot)
 	{
 		free(QuerySnapshot->xip);
@@ -637,5 +763,4 @@ FreeXactSnapshot(void)
 	}
 
 	SerializableSnapshot = NULL;
-
 }
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 62b91e51623416d2788df6508b128559b0592e12..2f9a6de2ad2a54bf2bbfea7a41e56f54245cb2dd 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: heapam.h,v 1.65 2001/06/22 19:16:23 wieck Exp $
+ * $Id: heapam.h,v 1.66 2001/07/12 04:11:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,7 @@
 #define HEAPAM_H
 
 #include <time.h>
+
 #include "access/htup.h"
 #include "access/relscan.h"
 #include "access/tupmacs.h"
@@ -218,6 +219,11 @@ extern void heap_restrpos(HeapScanDesc scan);
 extern void heap_redo(XLogRecPtr lsn, XLogRecord *rptr);
 extern void heap_undo(XLogRecPtr lsn, XLogRecord *rptr);
 extern void heap_desc(char *buf, uint8 xl_info, char *rec);
+extern XLogRecPtr log_heap_clean(Relation reln, Buffer buffer,
+								 char *unused, int unlen);
+extern XLogRecPtr log_heap_move(Relation reln, Buffer oldbuf,
+								ItemPointerData from,
+								Buffer newbuf, HeapTuple newtup);
 
 /* in common/heaptuple.c */
 extern Size ComputeDataSize(TupleDesc tupleDesc, Datum *value, char *nulls);
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 90d1f359839bdc61ca4bcd4e241e4ca09ac722b7..5d328b3c71433fc17d0a1ac0b0f442b6f4473b3c 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -1,17 +1,13 @@
 /*-------------------------------------------------------------------------
  *
  * transam.h
- *	  postgres transaction access method support code header
+ *	  postgres transaction access method support code
  *
  *
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: transam.h,v 1.35 2001/05/25 15:45:33 momjian Exp $
- *
- *	 NOTES
- *		Transaction System Version 101 now support proper oid
- *		generation and recording in the variable relation.
+ * $Id: transam.h,v 1.36 2001/07/12 04:11:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,77 +16,60 @@
 
 #include "storage/bufmgr.h"
 
+
 /* ----------------
- *		transaction system version id
+ *		Special transaction ID values
  *
- *		this is stored on the first page of the log, time and variable
- *		relations on the first 4 bytes.  This is so that if we improve
- *		the format of the transaction log after postgres version 2, then
- *		people won't have to rebuild their databases.
+ * We do not use any transaction IDs less than 512 --- this leaves the first
+ * 128 bytes of pg_log available for special purposes such as version number
+ * storage.  (Currently, we do not actually use them for anything.)
  *
- *		TRANS_SYSTEM_VERSION 100 means major version 1 minor version 0.
- *		Two databases with the same major version should be compatible,
- *		even if their minor versions differ.
+ * AmiTransactionId is the XID for "bootstrap" operations.  It should always
+ * be considered valid.
+ *
+ * FirstTransactionId is the first "normal" transaction id.
+ * ----------------
+ */
+#define NullTransactionId		((TransactionId) 0)
+#define DisabledTransactionId	((TransactionId) 1)
+#define AmiTransactionId		((TransactionId) 512)
+#define FirstTransactionId		((TransactionId) 514)
+
+/* ----------------
+ *		transaction ID manipulation macros
  * ----------------
  */
-#define TRANS_SYSTEM_VERSION	200
+#define TransactionIdIsValid(xid)		((bool) ((xid) != NullTransactionId))
+#define TransactionIdIsSpecial(xid)		((bool) ((xid) < FirstTransactionId))
+#define TransactionIdEquals(id1, id2)	((bool) ((id1) == (id2)))
+#define TransactionIdPrecedes(id1, id2)	((bool) ((id1) < (id2)))
+#define TransactionIdStore(xid, dest)	\
+	(*((TransactionId*) (dest)) = (TransactionId) (xid))
+#define StoreInvalidTransactionId(dest) \
+	(*((TransactionId*) (dest)) = NullTransactionId)
 
 /* ----------------
- *		transaction id status values
+ *		transaction status values
  *
  *		someday we will use "11" = 3 = XID_COMMIT_CHILD to mean the
  *		commiting of child xactions.
  * ----------------
  */
-#define XID_COMMIT			2	/* transaction commited */
-#define XID_ABORT			1	/* transaction aborted */
 #define XID_INPROGRESS		0	/* transaction in progress */
+#define XID_ABORT			1	/* transaction aborted */
+#define XID_COMMIT			2	/* transaction commited */
 #define XID_COMMIT_CHILD	3	/* child xact commited */
 
-typedef unsigned char XidStatus;/* (2 bits) */
+typedef unsigned char XidStatus;	/* (2 bits) */
 
 /* ----------
- *		note: we reserve the first 16384 object ids for internal use.
+ *		We reserve the first 16384 object ids for manual assignment.
  *		oid's less than this appear in the .bki files.  the choice of
  *		16384 is completely arbitrary.
  * ----------
  */
 #define BootstrapObjectIdData 16384
 
-/* ----------------
- *		BitIndexOf computes the index of the Nth xid on a given block
- * ----------------
- */
-#define BitIndexOf(N)	((N) * 2)
-
-/* ----------------
- *		transaction page definitions
- * ----------------
- */
-#define TP_DataSize				(BLCKSZ - sizeof(XLogRecPtr))
-#define TP_NumXidStatusPerBlock (TP_DataSize * 4)
-
-/* ----------------
- *		LogRelationContents structure
- *
- *		This structure describes the storage of the data in the
- *		first 128 bytes of the log relation.  This storage is never
- *		used for transaction status because transaction id's begin
- *		their numbering at 512.
- *
- *		The first 4 bytes of this relation store the version
- *		number of the transaction system.
- * ----------------
- */
-typedef struct LogRelationContentsData
-{
-	XLogRecPtr	LSN;			/* temp hack: LSN is member of any block */
-	/* so should be described in bufmgr */
-	int			TransSystemVersion;
-} LogRelationContentsData;
-
-typedef LogRelationContentsData *LogRelationContents;
-
 /*
  * VariableCache is placed in shmem and used by
  * backends to get next available XID & OID.
@@ -104,6 +83,7 @@ typedef struct VariableCacheData
 
 typedef VariableCacheData *VariableCache;
 
+
 /* ----------------
  *		extern declarations
  * ----------------
@@ -142,16 +122,7 @@ extern void CheckMaxObjectId(Oid assigned_oid);
 /* in transam.c */
 extern Relation LogRelation;
 
-extern TransactionId cachedTestXid;
-extern XidStatus cachedTestXidStatus;
-
-extern TransactionId NullTransactionId;
-extern TransactionId AmiTransactionId;
-extern TransactionId FirstTransactionId;
-
-extern int	RecoveryCheckingEnableState;
-
-/* in transsup.c */
+/* in xact.c */
 extern bool AMI_OVERRIDE;
 
 /* in varsup.c */
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index ab75ec001f0164e3a9213de514bfbcec61db37b3..abbe16ced388628bf74986fdc1eb5cf37e3b11c2 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: xact.h,v 1.33 2001/03/22 04:00:32 momjian Exp $
+ * $Id: xact.h,v 1.34 2001/07/12 04:11:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -67,17 +67,6 @@ typedef TransactionStateData *TransactionState;
 #define TBLOCK_ABORT			4
 #define TBLOCK_ENDABORT			5
 
-/* ----------------
- *		transaction ID manipulation macros
- * ----------------
- */
-#define TransactionIdIsValid(xid)		((bool) ((xid) != NullTransactionId))
-#define TransactionIdEquals(id1, id2)	((bool) ((id1) == (id2)))
-#define TransactionIdStore(xid, dest)	\
-	(*((TransactionId*) (dest)) = (TransactionId) (xid))
-#define StoreInvalidTransactionId(dest) \
-	(*((TransactionId*) (dest)) = NullTransactionId)
-
 /*
  * XLOG allows to store some information in high 4 bits of log
  * record xl_info field
@@ -133,8 +122,6 @@ extern void AbortOutOfAnyTransaction(void);
 
 extern void RecordTransactionCommit(void);
 
-extern TransactionId DisabledTransactionId;
-
 extern void XactPushRollback(void (*func) (void *), void *data);
 extern void XactPopRollback(void);
 
@@ -146,6 +133,5 @@ extern void xact_desc(char *buf, uint8 xl_info, char *rec);
 extern Datum xidin(PG_FUNCTION_ARGS);
 extern Datum xidout(PG_FUNCTION_ARGS);
 extern Datum xideq(PG_FUNCTION_ARGS);
-extern void TransactionIdAdd(TransactionId *xid, int value);
 
 #endif	 /* XACT_H */
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 0d9f66d04b35a8c171b881e8ed004cf4e8ea7da6..ff02055052e04f364ed38269b00dd3e7d48999c7 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -7,23 +7,43 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: vacuum.h,v 1.36 2001/06/27 23:31:39 tgl Exp $
+ * $Id: vacuum.h,v 1.37 2001/07/12 04:11:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef VACUUM_H
 #define VACUUM_H
 
+#include <time.h>
+#include <sys/time.h>
+
+#ifdef HAVE_GETRUSAGE
+#include <sys/resource.h>
+#else
+#include "rusagestub.h"
+#endif
+
 #include "nodes/parsenodes.h"
 #include "storage/block.h"
 
 
+/* State structure for vac_init_rusage/vac_show_rusage */
+typedef struct VacRUsage
+{
+	struct timeval	tv;
+	struct rusage	ru;
+} VacRUsage;
+
+
 /* in commands/vacuum.c */
 extern void vacuum(VacuumStmt *vacstmt);
 extern void vac_update_relstats(Oid relid,
 								BlockNumber num_pages,
 								double num_tuples,
 								bool hasindex);
+extern void vac_init_rusage(VacRUsage *ru0);
+extern const char *vac_show_rusage(VacRUsage *ru0);
+
 /* in commands/analyze.c */
 extern void analyze_rel(Oid relid, VacuumStmt *vacstmt);
 
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index 7c3dece02fe47268544fc3427f2b230abf563c16..86c88892adf040b958818f11320d5cb3ab1091a4 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tqual.h,v 1.31 2001/06/18 21:38:02 momjian Exp $
+ * $Id: tqual.h,v 1.32 2001/07/12 04:11:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,8 +23,9 @@ typedef struct SnapshotData
 {
 	TransactionId xmin;			/* XID < xmin are visible to me */
 	TransactionId xmax;			/* XID >= xmax are invisible to me */
-	uint32		xcnt;			/* # of xact below */
-	TransactionId *xip;			/* array of xacts in progress */
+	uint32		xcnt;			/* # of xact ids in xip[] */
+	TransactionId *xip;			/* array of xact IDs in progress */
+	/* note: all ids in xip[] satisfy xmin <= xip[i] < xmax */
 	ItemPointerData tid;		/* required for Dirty snapshot -:( */
 } SnapshotData;
 
@@ -34,8 +35,8 @@ typedef SnapshotData *Snapshot;
 #define SnapshotSelf				((Snapshot) 0x1)
 #define SnapshotAny					((Snapshot) 0x2)
 
-extern Snapshot SnapshotDirty;
-extern Snapshot QuerySnapshot;
+extern DLLIMPORT Snapshot SnapshotDirty;
+extern DLLIMPORT Snapshot QuerySnapshot;
 extern DLLIMPORT Snapshot SerializableSnapshot;
 
 extern bool ReferentialIntegritySnapshotOverride;
@@ -66,11 +67,11 @@ extern bool ReferentialIntegritySnapshotOverride;
 			(IsSnapshotSelf(snapshot) ? \
 				HeapTupleSatisfiesItself((tuple)->t_data) \
 			: \
-				(IsSnapshotDirty(snapshot) ? \
-					HeapTupleSatisfiesDirty((tuple)->t_data) \
+				(IsSnapshotNow(snapshot) ? \
+					HeapTupleSatisfiesNow((tuple)->t_data) \
 				: \
-					(IsSnapshotNow(snapshot) ? \
-						HeapTupleSatisfiesNow((tuple)->t_data) \
+					(IsSnapshotDirty(snapshot) ? \
+						HeapTupleSatisfiesDirty((tuple)->t_data) \
 					: \
 						HeapTupleSatisfiesSnapshot((tuple)->t_data, snapshot) \
 					) \
@@ -79,18 +80,31 @@ extern bool ReferentialIntegritySnapshotOverride;
 	) \
 )
 
+/* Result codes for HeapTupleSatisfiesUpdate */
 #define HeapTupleMayBeUpdated		0
 #define HeapTupleInvisible			1
 #define HeapTupleSelfUpdated		2
 #define HeapTupleUpdated			3
 #define HeapTupleBeingUpdated		4
 
+/* Result codes for HeapTupleSatisfiesVacuum */
+typedef enum
+{
+	HEAPTUPLE_DEAD,				/* tuple is dead and deletable */
+	HEAPTUPLE_LIVE,				/* tuple is live (committed, no deleter) */
+	HEAPTUPLE_RECENTLY_DEAD,	/* tuple is dead, but not deletable yet */
+	HEAPTUPLE_INSERT_IN_PROGRESS, /* inserting xact is still in progress */
+	HEAPTUPLE_DELETE_IN_PROGRESS /* deleting xact is still in progress */
+} HTSV_Result;
+
 extern bool HeapTupleSatisfiesItself(HeapTupleHeader tuple);
 extern bool HeapTupleSatisfiesNow(HeapTupleHeader tuple);
 extern bool HeapTupleSatisfiesDirty(HeapTupleHeader tuple);
 extern bool HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple,
 						   Snapshot snapshot);
 extern int	HeapTupleSatisfiesUpdate(HeapTuple tuple);
+extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTupleHeader tuple,
+											TransactionId XmaxRecent);
 
 extern Snapshot GetSnapshotData(bool serializable);
 extern void SetQuerySnapshot(void);