OSDN Git Service

Create a new HeapTupleSatisfiesVacuum() routine in tqual.c that embodies the
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 12 Jul 2001 04:11:13 +0000 (04:11 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 12 Jul 2001 04:11:13 +0000 (04:11 +0000)
validity checking rules for VACUUM.  Make some other rearrangements of the
VACUUM code to allow more code to be shared between full and lazy VACUUM.
Minor code cleanups and added comments for TransactionId manipulations.

14 files changed:
src/backend/access/heap/heapam.c
src/backend/access/transam/transam.c
src/backend/access/transam/transsup.c
src/backend/access/transam/varsup.c
src/backend/access/transam/xact.c
src/backend/access/transam/xid.c
src/backend/commands/vacuum.c
src/backend/storage/ipc/sinval.c
src/backend/utils/time/tqual.c
src/include/access/heapam.h
src/include/access/transam.h
src/include/access/xact.h
src/include/commands/vacuum.h
src/include/utils/tqual.h

index 37d554d..5f08b7f 100644 (file)
@@ -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
 #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);
index 89a35b2..910042f 100644 (file)
@@ -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;
index 0a44a01..3fd6c9d 100644 (file)
@@ -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
  *
  *-------------------------------------------------------------------------
  */
-
 #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;
                }
        }
index f245a79..2b253fc 100644 (file)
@@ -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.
index 4c18ac5..6467179 100644 (file)
@@ -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);
 }
 
 
@@ -512,19 +494,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)
        {
index 624d6da..9ec40bb 100644 (file)
@@ -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
 #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));
 }
index 888a508..34bc1e9 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 
 #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"
 
 #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,23 +529,23 @@ 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
@@ -488,23 +553,85 @@ vacuum_rel(Oid relid)
        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
@@ -2359,63 +2376,6 @@ tid_reaped(ItemPointer itemptr, VacPageList vacpagelist)
 }
 
 /*
- *     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)
        {
index 5794207..a9b9046 100644 (file)
@@ -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++;
index a8ed65a..2dd56b6 100644 (file)
@@ -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;
-
 }
index 62b91e5..2f9a6de 100644 (file)
@@ -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);
index 90d1f35..5d328b3 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 
 #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 */
index ab75ec0..abbe16c 100644 (file)
@@ -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 */
index 0d9f66d..ff02055 100644 (file)
@@ -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);
 
index 7c3dece..86c8889 100644 (file)
@@ -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);