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