*
*
* 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);
*
*
* 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
#include "catalog/catname.h"
#include "miscadmin.h"
+
static int RecoveryCheckingEnabled(void);
static void TransRecover(Relation logRelation);
static bool TransactionLogTest(TransactionId transactionId, XidStatus status);
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
* goes from zero to one. -cim 3/21/90
* ----------------
*/
-int RecoveryCheckingEnableState = 0;
+static int RecoveryCheckingEnableState = 0;
/* ----------------
* recovery checking accessors
/*
* update (invalidate) our single item TransactionLogTest cache.
- *
- * if (status != XID_COMMIT)
- *
- * What's the hell ?! Why != XID_COMMIT ?!
*/
TransactionIdStore(transactionId, &cachedTestXid);
cachedTestXidStatus = status;
-
}
/* ----------------------------------------------------------------
/*
* 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;
*
*
* 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,
* test */
BlockNumber *blockNumberOutP)
{
- long itemsPerBlock = 0;
+ uint32 itemsPerBlock = 0;
/*
* we calculate the block number of our transaction by dividing the
if (xstatus != XID_INPROGRESS)
{
if (returnXidP != NULL)
- {
- TransactionIdStore(baseXid, returnXidP);
- TransactionIdAdd(returnXidP, index - 1);
- }
+ TransactionIdStore(baseXid + (index - 1), returnXidP);
break;
}
}
* 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 $
*
*-------------------------------------------------------------------------
*/
#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 */
/* 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.
(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);
}
/*
void
ReadNewTransactionId(TransactionId *xid)
{
-
/*
* During bootstrap initialization, we return the special bootstrap
* transaction id.
*
*
* 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:
/* ----------------
* 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 */
* 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.
* themselves.
* --------------------------------
*/
-int SavedTransactionState;
+static int SavedTransactionState;
void
OverrideTransactionSystem(bool flag)
* "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;
}
* "disabled" command id.
*/
if (s->state == TRANS_DISABLED)
- return (CommandId) DisabledCommandId;
+ return DisabledCommandId;
return s->commandId;
}
* "disabled" command id.
*/
if (s->state == TRANS_DISABLED)
- return (CommandId) DisabledCommandId;
+ return DisabledCommandId;
return s->scanCommandId;
}
* "disabled" starting time.
*/
if (s->state == TRANS_DISABLED)
- return (AbsoluteTime) DisabledStartTime;
+ return DisabledStartTime;
return s->startTime;
}
if (AMI_OVERRIDE)
return false;
- return (bool)
- TransactionIdEquals(xid, s->transactionIdData);
+ return TransactionIdEquals(xid, s->transactionIdData);
}
}
-/* --------------------------------
- * ClearCommandIdCounterOverflowFlag
- * --------------------------------
- */
-#ifdef NOT_USED
-void
-ClearCommandIdCounterOverflowFlag(void)
-{
- CommandIdCounterOverflowFlag = false;
-}
-
-#endif
-
/* --------------------------------
* CommandCounterIncrement
* --------------------------------
{
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;
void
SetScanCommandId(CommandId savedId)
{
-
CurrentTransactionStateData.scanCommandId = savedId;
-
}
/* ----------------------------------------------------------------
/*
* 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)
{
/*-------------------------------------------------------------------------
*
* 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)
{
PG_RETURN_TRANSACTIONID((TransactionId) atol(representation));
}
-/* XXX name for catalogs */
Datum
xidout(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));
}
*
*
* 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
{
typedef struct VRelStats
{
- Oid relid;
BlockNumber rel_pages;
double rel_tuples;
Size min_tlen;
VTupleLink vtlinks;
} VRelStats;
-typedef struct VacRUsage
-{
- struct timeval tv;
- struct rusage ru;
-} VacRUsage;
static MemoryContext vac_context = NULL;
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,
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 *
+ * *
+ ****************************************************************************
+ */
/*
/*
* 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();
}
* 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.
*
{
/* 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();
/*
* 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)
if (VacRelP)
{
-
/*
* we could use the cache here, but it is clearer to use scankeys
* for both vacuum cases, bjm 2000/01/19
}
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;
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
* 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 */
}
/*
- * 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
* 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) ||
{
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;
reindex = true;
if (nindices > 0)
vacrelstats->hasindex = true;
- else
- vacrelstats->hasindex = false;
#ifdef NOT_USED
/*
{
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 */
*/
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
*
* 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,
double num_tuples,
tups_vacuumed,
nkeep,
- nunused,
- ncrash;
+ nunused;
double free_size,
usable_free_size;
Size min_tlen = MaxTupleSize;
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);
offnum <= maxoff;
offnum = OffsetNumberNext(offnum))
{
+ uint16 sv_infomask;
+
itemid = PageGetItemId(page, offnum);
/*
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)
{
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)
{
if (tuple.t_len > max_tlen)
max_tlen = tuple.t_len;
}
- }
+ } /* scan along page */
if (tempPage != (Page) NULL)
{
}
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));
}
chain_tuple_moved;
VacRUsage ru0;
- init_rusage(&ru0);
+ vac_init_rusage(&ru0);
myXID = GetCurrentTransactionId();
myCID = GetCurrentCommandId();
* 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;
* 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)
/* 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 */
* 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);
* 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));
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.
double nitups;
VacRUsage ru0;
- init_rusage(&ru0);
+ vac_init_rusage(&ru0);
/* walk through the entire index */
iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL);
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
VacPage vp;
VacRUsage ru0;
- init_rusage(&ru0);
+ vac_init_rusage(&ru0);
/* walk through the entire index */
iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL);
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
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.
/*
* Initialize usage snapshot.
*/
-static void
-init_rusage(VacRUsage *ru0)
+void
+vac_init_rusage(VacRUsage *ru0)
{
struct timezone tz;
* 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)
{
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
PROC *proc = (PROC *) MAKE_PTR(pOffset);
- if (proc->xid == xid)
+ if (TransactionIdEquals(proc->xid, xid))
{
result = true;
break;
* 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);
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;
}
/*
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++;
*
*
* 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;
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;
QuerySnapshot = GetSnapshotData(false);
Assert(QuerySnapshot != NULL);
-
}
void
FreeXactSnapshot(void)
{
-
if (QuerySnapshot != NULL && QuerySnapshot != SerializableSnapshot)
{
free(QuerySnapshot->xip);
}
SerializableSnapshot = NULL;
-
}
* 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 $
*
*-------------------------------------------------------------------------
*/
#define HEAPAM_H
#include <time.h>
+
#include "access/htup.h"
#include "access/relscan.h"
#include "access/tupmacs.h"
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);
/*-------------------------------------------------------------------------
*
* 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.
typedef VariableCacheData *VariableCache;
+
/* ----------------
* extern declarations
* ----------------
/* 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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
#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
extern void RecordTransactionCommit(void);
-extern TransactionId DisabledTransactionId;
-
extern void XactPushRollback(void (*func) (void *), void *data);
extern void XactPopRollback(void);
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 */
* 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);
* 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 $
*
*-------------------------------------------------------------------------
*/
{
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;
#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;
(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) \
) \
) \
)
+/* 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);