Commit b9f3a929 authored by Tom Lane's avatar Tom Lane

Create a new HeapTupleSatisfiesVacuum() routine in tqual.c that embodies the

validity checking rules for VACUUM.  Make some other rearrangements of the
VACUUM code to allow more code to be shared between full and lazy VACUUM.
Minor code cleanups and added comments for TransactionId manipulations.
parent eaafc9d6
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * INTERFACE ROUTINES
...@@ -48,11 +48,6 @@ ...@@ -48,11 +48,6 @@
#include "pgstat.h" #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 */ /* comments are in heap_update */
static xl_heaptid _locked_tuple_; static xl_heaptid _locked_tuple_;
static void _heap_unlock_tuple(void *data); static void _heap_unlock_tuple(void *data);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* This file contains the high level access-method interface to the * This file contains the high level access-method interface to the
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "catalog/catname.h" #include "catalog/catname.h"
#include "miscadmin.h" #include "miscadmin.h"
static int RecoveryCheckingEnabled(void); static int RecoveryCheckingEnabled(void);
static void TransRecover(Relation logRelation); static void TransRecover(Relation logRelation);
static bool TransactionLogTest(TransactionId transactionId, XidStatus status); static bool TransactionLogTest(TransactionId transactionId, XidStatus status);
...@@ -40,29 +41,11 @@ static void TransactionLogUpdate(TransactionId transactionId, ...@@ -40,29 +41,11 @@ static void TransactionLogUpdate(TransactionId transactionId,
Relation LogRelation = (Relation) NULL; Relation LogRelation = (Relation) NULL;
/* ---------------- /* ----------------
* global variables holding cached transaction id's and statuses. * Single-item cache for results of TransactionLogTest.
* ----------------
*/
TransactionId cachedTestXid;
XidStatus cachedTestXidStatus;
/* ----------------
* transaction system constants
* ---------------- * ----------------
*/ */
/* ---------------------------------------------------------------- static TransactionId cachedTestXid = NullTransactionId;
* transaction system constants static XidStatus cachedTestXidStatus;
*
* 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;
/* ---------------- /* ----------------
* transaction recovery state variables * transaction recovery state variables
...@@ -76,7 +59,7 @@ TransactionId FirstTransactionId = (TransactionId) 514; ...@@ -76,7 +59,7 @@ TransactionId FirstTransactionId = (TransactionId) 514;
* goes from zero to one. -cim 3/21/90 * goes from zero to one. -cim 3/21/90
* ---------------- * ----------------
*/ */
int RecoveryCheckingEnableState = 0; static int RecoveryCheckingEnableState = 0;
/* ---------------- /* ----------------
* recovery checking accessors * recovery checking accessors
...@@ -203,14 +186,9 @@ TransactionLogUpdate(TransactionId transactionId, /* trans id to update */ ...@@ -203,14 +186,9 @@ TransactionLogUpdate(TransactionId transactionId, /* trans id to update */
/* /*
* update (invalidate) our single item TransactionLogTest cache. * update (invalidate) our single item TransactionLogTest cache.
*
* if (status != XID_COMMIT)
*
* What's the hell ?! Why != XID_COMMIT ?!
*/ */
TransactionIdStore(transactionId, &cachedTestXid); TransactionIdStore(transactionId, &cachedTestXid);
cachedTestXidStatus = status; cachedTestXidStatus = status;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
...@@ -355,17 +333,15 @@ InitializeTransactionLog(void) ...@@ -355,17 +333,15 @@ InitializeTransactionLog(void)
/* /*
* if we have a virgin database, we initialize the log relation by * 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 * 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. * effect of bootstrapping in varsup.c.
*/ */
SpinAcquire(OidGenLockId); SpinAcquire(OidGenLockId);
if (!TransactionIdDidCommit(AmiTransactionId)) if (!TransactionIdDidCommit(AmiTransactionId))
{ {
TransactionLogUpdate(AmiTransactionId, XID_COMMIT); TransactionLogUpdate(AmiTransactionId, XID_COMMIT);
TransactionIdStore(AmiTransactionId, &cachedTestXid);
cachedTestXidStatus = XID_COMMIT;
Assert(!IsUnderPostmaster && Assert(!IsUnderPostmaster &&
ShmemVariableCache->nextXid <= FirstTransactionId); ShmemVariableCache->nextXid <= FirstTransactionId);
ShmemVariableCache->nextXid = FirstTransactionId; ShmemVariableCache->nextXid = FirstTransactionId;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* This file contains support functions for the high * This file contains support functions for the high
...@@ -16,12 +16,67 @@ ...@@ -16,12 +16,67 @@
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include "access/xact.h" #include "access/xact.h"
#include "utils/bit.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, static XidStatus TransBlockGetXidStatus(Block tblock,
TransactionId transactionId); TransactionId transactionId);
static void TransBlockSetXidStatus(Block tblock, static void TransBlockSetXidStatus(Block tblock,
...@@ -54,7 +109,7 @@ TransComputeBlockNumber(Relation relation, /* relation to test */ ...@@ -54,7 +109,7 @@ TransComputeBlockNumber(Relation relation, /* relation to test */
* test */ * test */
BlockNumber *blockNumberOutP) BlockNumber *blockNumberOutP)
{ {
long itemsPerBlock = 0; uint32 itemsPerBlock = 0;
/* /*
* we calculate the block number of our transaction by dividing the * we calculate the block number of our transaction by dividing the
...@@ -135,10 +190,7 @@ TransBlockGetLastTransactionIdStatus(Block tblock, ...@@ -135,10 +190,7 @@ TransBlockGetLastTransactionIdStatus(Block tblock,
if (xstatus != XID_INPROGRESS) if (xstatus != XID_INPROGRESS)
{ {
if (returnXidP != NULL) if (returnXidP != NULL)
{ TransactionIdStore(baseXid + (index - 1), returnXidP);
TransactionIdStore(baseXid, returnXidP);
TransactionIdAdd(returnXidP, index - 1);
}
break; break;
} }
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Copyright (c) 2000, PostgreSQL Global Development Group * Copyright (c) 2000, PostgreSQL Global Development Group
* *
* IDENTIFICATION * 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 @@ ...@@ -18,8 +18,7 @@
#include "storage/proc.h" #include "storage/proc.h"
/* Number of XIDs and OIDs to prefetch (preallocate) per XLOG write */ /* Number of OIDs to prefetch (preallocate) per XLOG write */
#define VAR_XID_PREFETCH 1024
#define VAR_OID_PREFETCH 8192 #define VAR_OID_PREFETCH 8192
/* Spinlocks for serializing generation of XIDs and OIDs, respectively */ /* Spinlocks for serializing generation of XIDs and OIDs, respectively */
...@@ -29,10 +28,13 @@ SPINLOCK OidGenLockId; ...@@ -29,10 +28,13 @@ SPINLOCK OidGenLockId;
/* pointer to "variable cache" in shared memory (set up by shmem.c) */ /* pointer to "variable cache" in shared memory (set up by shmem.c) */
VariableCache ShmemVariableCache = NULL; VariableCache ShmemVariableCache = NULL;
/*
* Allocate the next XID for my new transaction.
*/
void void
GetNewTransactionId(TransactionId *xid) GetNewTransactionId(TransactionId *xid)
{ {
/* /*
* During bootstrap initialization, we return the special bootstrap * During bootstrap initialization, we return the special bootstrap
* transaction id. * transaction id.
...@@ -49,10 +51,22 @@ GetNewTransactionId(TransactionId *xid) ...@@ -49,10 +51,22 @@ GetNewTransactionId(TransactionId *xid)
(ShmemVariableCache->nextXid)++; (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) if (MyProc != (PROC *) NULL)
MyProc->xid = *xid; MyProc->xid = *xid;
SpinRelease(XidGenLockId);
} }
/* /*
...@@ -61,7 +75,6 @@ GetNewTransactionId(TransactionId *xid) ...@@ -61,7 +75,6 @@ GetNewTransactionId(TransactionId *xid)
void void
ReadNewTransactionId(TransactionId *xid) ReadNewTransactionId(TransactionId *xid)
{ {
/* /*
* During bootstrap initialization, we return the special bootstrap * During bootstrap initialization, we return the special bootstrap
* transaction id. * transaction id.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* Transaction aborts can now occur two ways: * Transaction aborts can now occur two ways:
...@@ -199,14 +199,9 @@ static void StartTransaction(void); ...@@ -199,14 +199,9 @@ static void StartTransaction(void);
/* ---------------- /* ----------------
* global variables holding the current transaction state. * 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 */ 0, /* transaction id */
FirstCommandId, /* command id */ FirstCommandId, /* command id */
0, /* scan command id */ 0, /* scan command id */
...@@ -234,29 +229,17 @@ static void *_RollbackData = NULL; ...@@ -234,29 +229,17 @@ static void *_RollbackData = NULL;
* info returned when the system is disabled * info returned when the system is disabled
* *
* Apparently a lot of this code is inherited from other prototype systems. * Apparently a lot of this code is inherited from other prototype systems.
*
* For DisabledStartTime, use a symbolic value to make the relationships clearer. * 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 * 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 * every day. It appears that if we return a value guaranteed larger than
* any real time associated with a transaction then comparisons in other * 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 * 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; static AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME;
AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME; /* 1073741823; */
/* ----------------
* overflow flag
* ----------------
*/
bool CommandIdCounterOverflowFlag;
/* ---------------- /* ----------------
* catalog creation transaction bootstrapping flag. * catalog creation transaction bootstrapping flag.
...@@ -362,7 +345,7 @@ IsAbortedTransactionBlockState(void) ...@@ -362,7 +345,7 @@ IsAbortedTransactionBlockState(void)
* themselves. * themselves.
* -------------------------------- * --------------------------------
*/ */
int SavedTransactionState; static int SavedTransactionState;
void void
OverrideTransactionSystem(bool flag) OverrideTransactionSystem(bool flag)
...@@ -403,12 +386,12 @@ GetCurrentTransactionId(void) ...@@ -403,12 +386,12 @@ GetCurrentTransactionId(void)
* "disabled" transaction id. * "disabled" transaction id.
*/ */
if (s->state == TRANS_DISABLED) if (s->state == TRANS_DISABLED)
return (TransactionId) DisabledTransactionId; return DisabledTransactionId;
/* /*
* otherwise return the current transaction id. * otherwise return the current transaction id.
*/ */
return (TransactionId) s->transactionIdData; return s->transactionIdData;
} }
...@@ -426,7 +409,7 @@ GetCurrentCommandId(void) ...@@ -426,7 +409,7 @@ GetCurrentCommandId(void)
* "disabled" command id. * "disabled" command id.
*/ */
if (s->state == TRANS_DISABLED) if (s->state == TRANS_DISABLED)
return (CommandId) DisabledCommandId; return DisabledCommandId;
return s->commandId; return s->commandId;
} }
...@@ -441,7 +424,7 @@ GetScanCommandId(void) ...@@ -441,7 +424,7 @@ GetScanCommandId(void)
* "disabled" command id. * "disabled" command id.
*/ */
if (s->state == TRANS_DISABLED) if (s->state == TRANS_DISABLED)
return (CommandId) DisabledCommandId; return DisabledCommandId;
return s->scanCommandId; return s->scanCommandId;
} }
...@@ -461,7 +444,7 @@ GetCurrentTransactionStartTime(void) ...@@ -461,7 +444,7 @@ GetCurrentTransactionStartTime(void)
* "disabled" starting time. * "disabled" starting time.
*/ */
if (s->state == TRANS_DISABLED) if (s->state == TRANS_DISABLED)
return (AbsoluteTime) DisabledStartTime; return DisabledStartTime;
return s->startTime; return s->startTime;
} }
...@@ -479,8 +462,7 @@ TransactionIdIsCurrentTransactionId(TransactionId xid) ...@@ -479,8 +462,7 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
if (AMI_OVERRIDE) if (AMI_OVERRIDE)
return false; return false;
return (bool) return TransactionIdEquals(xid, s->transactionIdData);
TransactionIdEquals(xid, s->transactionIdData);
} }
...@@ -511,19 +493,6 @@ CommandIdGEScanCommandId(CommandId cid) ...@@ -511,19 +493,6 @@ CommandIdGEScanCommandId(CommandId cid)
} }
/* --------------------------------
* ClearCommandIdCounterOverflowFlag
* --------------------------------
*/
#ifdef NOT_USED
void
ClearCommandIdCounterOverflowFlag(void)
{
CommandIdCounterOverflowFlag = false;
}
#endif
/* -------------------------------- /* --------------------------------
* CommandCounterIncrement * CommandCounterIncrement
* -------------------------------- * --------------------------------
...@@ -533,10 +502,7 @@ CommandCounterIncrement(void) ...@@ -533,10 +502,7 @@ CommandCounterIncrement(void)
{ {
CurrentTransactionStateData.commandId += 1; CurrentTransactionStateData.commandId += 1;
if (CurrentTransactionStateData.commandId == FirstCommandId) if (CurrentTransactionStateData.commandId == FirstCommandId)
{
CommandIdCounterOverflowFlag = true;
elog(ERROR, "You may only have 2^32-1 commands per transaction"); elog(ERROR, "You may only have 2^32-1 commands per transaction");
}
CurrentTransactionStateData.scanCommandId = CurrentTransactionStateData.commandId; CurrentTransactionStateData.scanCommandId = CurrentTransactionStateData.commandId;
...@@ -551,9 +517,7 @@ CommandCounterIncrement(void) ...@@ -551,9 +517,7 @@ CommandCounterIncrement(void)
void void
SetScanCommandId(CommandId savedId) SetScanCommandId(CommandId savedId)
{ {
CurrentTransactionStateData.scanCommandId = savedId; CurrentTransactionStateData.scanCommandId = savedId;
} }
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
...@@ -1113,6 +1077,13 @@ AbortTransaction(void) ...@@ -1113,6 +1077,13 @@ AbortTransaction(void)
/* /*
* Let others to know about no transaction in progress - vadim * Let others to know about no transaction in progress - vadim
* 11/26/96 * 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) if (MyProc != (PROC *) NULL)
{ {
......
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* xid.c * xid.c
* POSTGRES transaction identifier type. * POSTGRES transaction identifier datatype.
* *
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 * OLD COMMENTS
* XXX WARNING * XXX WARNING
...@@ -30,12 +30,6 @@ ...@@ -30,12 +30,6 @@
#define PG_RETURN_TRANSACTIONID(x) PG_RETURN_UINT32(x) #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 Datum
xidin(PG_FUNCTION_ARGS) xidin(PG_FUNCTION_ARGS)
{ {
...@@ -44,7 +38,6 @@ xidin(PG_FUNCTION_ARGS) ...@@ -44,7 +38,6 @@ xidin(PG_FUNCTION_ARGS)
PG_RETURN_TRANSACTIONID((TransactionId) atol(representation)); PG_RETURN_TRANSACTIONID((TransactionId) atol(representation));
} }
/* XXX name for catalogs */
Datum Datum
xidout(PG_FUNCTION_ARGS) xidout(PG_FUNCTION_ARGS)
{ {
...@@ -73,15 +66,5 @@ xideq(PG_FUNCTION_ARGS) ...@@ -73,15 +66,5 @@ xideq(PG_FUNCTION_ARGS)
TransactionId xid1 = PG_GETARG_TRANSACTIONID(0); TransactionId xid1 = PG_GETARG_TRANSACTIONID(0);
TransactionId xid2 = PG_GETARG_TRANSACTIONID(1); TransactionId xid2 = PG_GETARG_TRANSACTIONID(1);
PG_RETURN_BOOL(xid1 == xid2); PG_RETURN_BOOL(TransactionIdEquals(xid1, xid2));
}
/* ----------------------------------------------------------------
* TransactionIdAdd
* ----------------------------------------------------------------
*/
void
TransactionIdAdd(TransactionId *xid, int value)
{
*xid += value;
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -16,18 +16,10 @@
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/file.h> #include <sys/file.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifndef HAVE_GETRUSAGE
#include "rusagestub.h"
#else
#include <sys/resource.h>
#endif
#include "access/genam.h" #include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "access/xlog.h" #include "access/xlog.h"
...@@ -54,12 +46,6 @@ ...@@ -54,12 +46,6 @@
#include "pgstat.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 VRelListData
{ {
...@@ -110,7 +96,6 @@ typedef VTupleMoveData *VTupleMove; ...@@ -110,7 +96,6 @@ typedef VTupleMoveData *VTupleMove;
typedef struct VRelStats typedef struct VRelStats
{ {
Oid relid;
BlockNumber rel_pages; BlockNumber rel_pages;
double rel_tuples; double rel_tuples;
Size min_tlen; Size min_tlen;
...@@ -120,11 +105,6 @@ typedef struct VRelStats ...@@ -120,11 +105,6 @@ typedef struct VRelStats
VTupleLink vtlinks; VTupleLink vtlinks;
} VRelStats; } VRelStats;
typedef struct VacRUsage
{
struct timeval tv;
struct rusage ru;
} VacRUsage;
static MemoryContext vac_context = NULL; static MemoryContext vac_context = NULL;
...@@ -137,7 +117,8 @@ static TransactionId XmaxRecent; ...@@ -137,7 +117,8 @@ static TransactionId XmaxRecent;
static void vacuum_init(void); static void vacuum_init(void);
static void vacuum_shutdown(void); static void vacuum_shutdown(void);
static VRelList getrels(Name VacRelP, const char *stmttype); 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, static void scan_heap(VRelStats *vacrelstats, Relation onerel,
VacPageList vacuum_pages, VacPageList fraged_pages); VacPageList vacuum_pages, VacPageList fraged_pages);
static void repair_frag(VRelStats *vacrelstats, Relation onerel, static void repair_frag(VRelStats *vacrelstats, Relation onerel,
...@@ -164,8 +145,14 @@ static int vac_cmp_blk(const void *left, const void *right); ...@@ -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_offno(const void *left, const void *right);
static int vac_cmp_vtlinks(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 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) ...@@ -235,17 +222,31 @@ vacuum(VacuumStmt *vacstmt)
/* /*
* Process each selected relation. We are careful to process * Process each selected relation. We are careful to process
* each relation in a separate transaction in order to avoid holding * 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) for (cur = vrl; cur != (VRelList) NULL; cur = cur->vrl_next)
{ {
if (vacstmt->vacuum) if (vacstmt->vacuum)
vacuum_rel(cur->vrl_relid); vacuum_rel(cur->vrl_relid, vacstmt);
/* analyze separately so locking is minimized */
if (vacstmt->analyze) if (vacstmt->analyze)
analyze_rel(cur->vrl_relid, vacstmt); 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 */ /* clean up */
vacuum_shutdown(); vacuum_shutdown();
} }
...@@ -257,7 +258,7 @@ vacuum(VacuumStmt *vacstmt) ...@@ -257,7 +258,7 @@ vacuum(VacuumStmt *vacstmt)
* executing concurrently in the same database. However, there's no * executing concurrently in the same database. However, there's no
* good reason to prevent that, and manually removing lockfiles after * good reason to prevent that, and manually removing lockfiles after
* a vacuum crash was a pain for dbadmins. So, forget about lockfiles, * 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 * to ensure that there aren't two VACUUMs running on the same table
* at the same time. * at the same time.
* *
...@@ -282,18 +283,6 @@ vacuum_shutdown(void) ...@@ -282,18 +283,6 @@ vacuum_shutdown(void)
{ {
/* on entry, we are not in a transaction */ /* 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() */ /* matches the CommitTransaction in PostgresMain() */
StartTransactionCommand(); StartTransactionCommand();
...@@ -308,6 +297,9 @@ vacuum_shutdown(void) ...@@ -308,6 +297,9 @@ vacuum_shutdown(void)
/* /*
* Build a list of VRelListData nodes for each relation to be processed * 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 static VRelList
getrels(Name VacRelP, const char *stmttype) getrels(Name VacRelP, const char *stmttype)
...@@ -326,7 +318,6 @@ getrels(Name VacRelP, const char *stmttype) ...@@ -326,7 +318,6 @@ getrels(Name VacRelP, const char *stmttype)
if (VacRelP) if (VacRelP)
{ {
/* /*
* we could use the cache here, but it is clearer to use scankeys * we could use the cache here, but it is clearer to use scankeys
* for both vacuum cases, bjm 2000/01/19 * for both vacuum cases, bjm 2000/01/19
...@@ -344,9 +335,9 @@ getrels(Name VacRelP, const char *stmttype) ...@@ -344,9 +335,9 @@ getrels(Name VacRelP, const char *stmttype)
} }
else else
{ {
/* find all relations listed in pg_class */ /* find all plain relations listed in pg_class */
ScanKeyEntryInitialize(&key, 0x0, Anum_pg_class_relkind, ScanKeyEntryInitialize(&key, 0x0, Anum_pg_class_relkind,
F_CHAREQ, CharGetDatum('r')); F_CHAREQ, CharGetDatum(RELKIND_RELATION));
} }
vrl = cur = (VRelList) NULL; vrl = cur = (VRelList) NULL;
...@@ -395,11 +386,85 @@ getrels(Name VacRelP, const char *stmttype) ...@@ -395,11 +386,85 @@ getrels(Name VacRelP, const char *stmttype)
return vrl; 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 * Update the whole-relation statistics that are kept in its pg_class
* updates its num_pages and num_tuples statistics. * 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 * 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 * check that the heap exists again just before we vacuum it. The
...@@ -410,19 +475,11 @@ getrels(Name VacRelP, const char *stmttype) ...@@ -410,19 +475,11 @@ getrels(Name VacRelP, const char *stmttype)
* At entry and exit, we are not inside a transaction. * At entry and exit, we are not inside a transaction.
*/ */
static void static void
vacuum_rel(Oid relid) vacuum_rel(Oid relid, VacuumStmt *vacstmt)
{ {
LOCKMODE lmode;
Relation onerel; Relation onerel;
LockRelId onerelid; 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; Oid toast_relid;
/* Begin a transaction for vacuuming this relation */ /* Begin a transaction for vacuuming this relation */
...@@ -447,7 +504,15 @@ vacuum_rel(Oid relid) ...@@ -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 * 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 * owner, or the database owner (but in the latter case, only if it's
...@@ -456,7 +521,7 @@ vacuum_rel(Oid relid) ...@@ -456,7 +521,7 @@ vacuum_rel(Oid relid)
* Note we choose to treat permissions failure as a NOTICE and keep * Note we choose to treat permissions failure as a NOTICE and keep
* trying to vacuum the rest of the DB --- is this appropriate? * 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), if (! (pg_ownercheck(GetUserId(), RelationGetRelationName(onerel),
RELNAME) || RELNAME) ||
...@@ -464,47 +529,109 @@ vacuum_rel(Oid relid) ...@@ -464,47 +529,109 @@ vacuum_rel(Oid relid)
{ {
elog(NOTICE, "Skipping \"%s\" --- only table or database owner can VACUUM it", elog(NOTICE, "Skipping \"%s\" --- only table or database owner can VACUUM it",
RelationGetRelationName(onerel)); RelationGetRelationName(onerel));
heap_close(onerel, AccessExclusiveLock); heap_close(onerel, lmode);
CommitTransactionCommand(); CommitTransactionCommand();
return; return;
} }
/* /*
* Get a session-level exclusive lock too. This will protect our * Get a session-level lock too. This will protect our access to the
* exclusive access to the relation across multiple transactions, so * relation across multiple transactions, so that we can vacuum the
* that we can vacuum the relation's TOAST table (if any) secure in * relation's TOAST table (if any) secure in the knowledge that no one
* the knowledge that no one is diddling the parent relation. * is deleting the parent relation.
* *
* NOTE: this cannot block, even if someone else is waiting for access, * NOTE: this cannot block, even if someone else is waiting for access,
* because the lock manager knows that both lock requests are from the * because the lock manager knows that both lock requests are from the
* same process. * same process.
*/ */
onerelid = onerel->rd_lockInfo.lockRelId; onerelid = onerel->rd_lockInfo.lockRelId;
LockRelationForSession(&onerelid, AccessExclusiveLock); LockRelationForSession(&onerelid, lmode);
/* /*
* Remember the relation's TOAST relation for later * Remember the relation's TOAST relation for later
*/ */
toast_relid = onerel->rd_rel->reltoastrelid; 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. * Set up statistics-gathering machinery.
*/ */
vacrelstats = (VRelStats *) palloc(sizeof(VRelStats)); vacrelstats = (VRelStats *) palloc(sizeof(VRelStats));
vacrelstats->relid = relid;
vacrelstats->rel_pages = 0; vacrelstats->rel_pages = 0;
vacrelstats->rel_tuples = 0; vacrelstats->rel_tuples = 0;
vacrelstats->hasindex = false; vacrelstats->hasindex = false;
GetXmaxRecent(&XmaxRecent); /* scan the heap */
/* scan it */
reindex = false;
vacuum_pages.num_pages = fraged_pages.num_pages = 0; vacuum_pages.num_pages = fraged_pages.num_pages = 0;
scan_heap(vacrelstats, onerel, &vacuum_pages, &fraged_pages); scan_heap(vacrelstats, onerel, &vacuum_pages, &fraged_pages);
if (IsIgnoringSystemIndexes() &&
IsSystemRelationName(RelationGetRelationName(onerel)))
reindex = true;
/* Now open all indices of the relation */ /* Now open all indices of the relation */
nindices = 0; nindices = 0;
...@@ -516,8 +643,6 @@ vacuum_rel(Oid relid) ...@@ -516,8 +643,6 @@ vacuum_rel(Oid relid)
reindex = true; reindex = true;
if (nindices > 0) if (nindices > 0)
vacrelstats->hasindex = true; vacrelstats->hasindex = true;
else
vacrelstats->hasindex = false;
#ifdef NOT_USED #ifdef NOT_USED
/* /*
...@@ -528,7 +653,7 @@ vacuum_rel(Oid relid) ...@@ -528,7 +653,7 @@ vacuum_rel(Oid relid)
{ {
close_indices(nindices, Irel); close_indices(nindices, Irel);
Irel = (Relation *) NULL; Irel = (Relation *) NULL;
activate_indexes_of_a_table(relid, false); activate_indexes_of_a_table(RelationGetRelid(onerel), false);
} }
#endif /* NOT_USED */ #endif /* NOT_USED */
...@@ -574,46 +699,25 @@ vacuum_rel(Oid relid) ...@@ -574,46 +699,25 @@ vacuum_rel(Oid relid)
*/ */
i = FlushRelationBuffers(onerel, vacrelstats->rel_pages); i = FlushRelationBuffers(onerel, vacrelstats->rel_pages);
if (i < 0) if (i < 0)
elog(ERROR, "VACUUM (vacuum_rel): FlushRelationBuffers returned %d", elog(ERROR, "VACUUM (full_vacuum_rel): FlushRelationBuffers returned %d",
i); i);
} }
} }
#ifdef NOT_USED #ifdef NOT_USED
if (reindex) if (reindex)
activate_indexes_of_a_table(relid, true); activate_indexes_of_a_table(RelationGetRelid(onerel), true);
#endif /* NOT_USED */ #endif /* NOT_USED */
/* update shared free space map with final free space info */ /* update shared free space map with final free space info */
vac_update_fsm(onerel, &fraged_pages, vacrelstats->rel_pages); 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 */ /* 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); 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 * scan_heap() -- scan an open heap relation
* *
...@@ -621,7 +725,7 @@ vacuum_rel(Oid relid) ...@@ -621,7 +725,7 @@ vacuum_rel(Oid relid)
* of pages we need to compact free space on and/or clean indexes of * of pages we need to compact free space on and/or clean indexes of
* deleted tuples), constructs fraged_pages (list of pages with free * deleted tuples), constructs fraged_pages (list of pages with free
* space that tuples could be moved into), and calculates statistics * 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 static void
scan_heap(VRelStats *vacrelstats, Relation onerel, scan_heap(VRelStats *vacrelstats, Relation onerel,
...@@ -647,8 +751,7 @@ scan_heap(VRelStats *vacrelstats, Relation onerel, ...@@ -647,8 +751,7 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
double num_tuples, double num_tuples,
tups_vacuumed, tups_vacuumed,
nkeep, nkeep,
nunused, nunused;
ncrash;
double free_size, double free_size,
usable_free_size; usable_free_size;
Size min_tlen = MaxTupleSize; Size min_tlen = MaxTupleSize;
...@@ -660,13 +763,13 @@ scan_heap(VRelStats *vacrelstats, Relation onerel, ...@@ -660,13 +763,13 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
int free_vtlinks = 100; int free_vtlinks = 100;
VacRUsage ru0; VacRUsage ru0;
init_rusage(&ru0); vac_init_rusage(&ru0);
relname = RelationGetRelationName(onerel); relname = RelationGetRelationName(onerel);
elog(MESSAGE_LEVEL, "--Relation %s--", relname); elog(MESSAGE_LEVEL, "--Relation %s--", relname);
empty_pages = new_pages = changed_pages = empty_end_pages = 0; 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; free_size = 0;
nblocks = RelationGetNumberOfBlocks(onerel); nblocks = RelationGetNumberOfBlocks(onerel);
...@@ -727,6 +830,8 @@ scan_heap(VRelStats *vacrelstats, Relation onerel, ...@@ -727,6 +830,8 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
offnum <= maxoff; offnum <= maxoff;
offnum = OffsetNumberNext(offnum)) offnum = OffsetNumberNext(offnum))
{ {
uint16 sv_infomask;
itemid = PageGetItemId(page, offnum); itemid = PageGetItemId(page, offnum);
/* /*
...@@ -744,146 +849,31 @@ scan_heap(VRelStats *vacrelstats, Relation onerel, ...@@ -744,146 +849,31 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid); tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
tuple.t_len = ItemIdGetLength(itemid); tuple.t_len = ItemIdGetLength(itemid);
ItemPointerSet(&(tuple.t_self), blkno, offnum); ItemPointerSet(&(tuple.t_self), blkno, offnum);
tupgone = false;
if (!(tuple.t_data->t_infomask & HEAP_XMIN_COMMITTED)) tupgone = false;
{ sv_infomask = tuple.t_data->t_infomask;
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;
}
}
}
/* switch (HeapTupleSatisfiesVacuum(tuple.t_data, XmaxRecent))
* 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))
{ {
if (tuple.t_data->t_infomask & HEAP_XMAX_COMMITTED) case HEAPTUPLE_DEAD:
{ tupgone = true; /* we can delete the tuple */
if (tuple.t_data->t_infomask & HEAP_MARKED_FOR_UPDATE) break;
{ case HEAPTUPLE_LIVE:
tuple.t_data->t_infomask |= HEAP_XMAX_INVALID; break;
tuple.t_data->t_infomask &= case HEAPTUPLE_RECENTLY_DEAD:
~(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))
{
/* /*
* Not Aborted, Not Committed, Not in Progress - so it * If tuple is recently deleted then we must not remove
* from crashed process. - vadim 06/02/97 * 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; 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 * If we do shrinking and this tuple is updated one
* then remember it to construct updated tuple * then remember it to construct updated tuple
* dependencies. * dependencies.
*/ */
if (do_shrinking && !(ItemPointerEquals(&(tuple.t_self), if (do_shrinking &&
&(tuple.t_data->t_ctid)))) !(ItemPointerEquals(&(tuple.t_self),
&(tuple.t_data->t_ctid))))
{ {
if (free_vtlinks == 0) if (free_vtlinks == 0)
{ {
...@@ -897,17 +887,40 @@ scan_heap(VRelStats *vacrelstats, Relation onerel, ...@@ -897,17 +887,40 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
free_vtlinks--; free_vtlinks--;
num_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... * Other checks...
*/ */
if (!OidIsValid(tuple.t_data->t_oid)) if (!OidIsValid(tuple.t_data->t_oid))
{
elog(NOTICE, "Rel %s: TID %u/%u: OID IS INVALID. TUPGONE %d.", elog(NOTICE, "Rel %s: TID %u/%u: OID IS INVALID. TUPGONE %d.",
relname, blkno, offnum, tupgone); relname, blkno, offnum, (int) tupgone);
}
if (tupgone) if (tupgone)
{ {
...@@ -946,7 +959,7 @@ scan_heap(VRelStats *vacrelstats, Relation onerel, ...@@ -946,7 +959,7 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
if (tuple.t_len > max_tlen) if (tuple.t_len > max_tlen)
max_tlen = tuple.t_len; max_tlen = tuple.t_len;
} }
} } /* scan along page */
if (tempPage != (Page) NULL) if (tempPage != (Page) NULL)
{ {
...@@ -1043,15 +1056,15 @@ scan_heap(VRelStats *vacrelstats, Relation onerel, ...@@ -1043,15 +1056,15 @@ scan_heap(VRelStats *vacrelstats, Relation onerel,
} }
elog(MESSAGE_LEVEL, "Pages %u: Changed %u, reaped %u, Empty %u, New %u; \ 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", Re-using: Free/Avail. Space %.0f/%.0f; EndEmpty/Avail. Pages %u/%u. %s",
nblocks, changed_pages, vacuum_pages->num_pages, empty_pages, nblocks, changed_pages, vacuum_pages->num_pages, empty_pages,
new_pages, num_tuples, tups_vacuumed, new_pages, num_tuples, tups_vacuumed,
nkeep, vacrelstats->num_vtlinks, ncrash, nkeep, vacrelstats->num_vtlinks,
nunused, (unsigned long) min_tlen, (unsigned long) max_tlen, nunused, (unsigned long) min_tlen, (unsigned long) max_tlen,
free_size, usable_free_size, free_size, usable_free_size,
empty_end_pages, fraged_pages->num_pages, empty_end_pages, fraged_pages->num_pages,
show_rusage(&ru0)); vac_show_rusage(&ru0));
} }
...@@ -1113,7 +1126,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, ...@@ -1113,7 +1126,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
chain_tuple_moved; chain_tuple_moved;
VacRUsage ru0; VacRUsage ru0;
init_rusage(&ru0); vac_init_rusage(&ru0);
myXID = GetCurrentTransactionId(); myXID = GetCurrentTransactionId();
myCID = GetCurrentCommandId(); myCID = GetCurrentCommandId();
...@@ -1306,9 +1319,10 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, ...@@ -1306,9 +1319,10 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
* tuples to another places. * tuples to another places.
*/ */
if ((tuple.t_data->t_infomask & HEAP_UPDATED && 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) && (!(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; Buffer Cbuf = buf;
Page Cpage; Page Cpage;
...@@ -1338,7 +1352,8 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, ...@@ -1338,7 +1352,8 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
* we have to move to the end of chain. * we have to move to the end of chain.
*/ */
while (!(tp.t_data->t_infomask & HEAP_XMAX_INVALID) && 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; Ctid = tp.t_data->t_ctid;
if (freeCbuf) if (freeCbuf)
...@@ -1422,7 +1437,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, ...@@ -1422,7 +1437,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
/* All done ? */ /* All done ? */
if (!(tp.t_data->t_infomask & HEAP_UPDATED) || if (!(tp.t_data->t_infomask & HEAP_UPDATED) ||
tp.t_data->t_xmin < XmaxRecent) TransactionIdPrecedes(tp.t_data->t_xmin, XmaxRecent))
break; break;
/* Well, try to find tuple with old row version */ /* Well, try to find tuple with old row version */
...@@ -1470,7 +1485,8 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, ...@@ -1470,7 +1485,8 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
* latter, and we are too close to 6.5 release. - * latter, and we are too close to 6.5 release. -
* vadim 06/11/99 * 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) if (freeCbuf)
ReleaseBuffer(Cbuf); ReleaseBuffer(Cbuf);
...@@ -1495,7 +1511,8 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, ...@@ -1495,7 +1511,8 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
* removed. * removed.
*/ */
if (Ptp.t_data->t_infomask & HEAP_UPDATED && 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, TransactionIdStore(myXID,
(TransactionId *) &(Ptp.t_data->t_cmin)); (TransactionId *) &(Ptp.t_data->t_cmin));
...@@ -1959,7 +1976,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, ...@@ -1959,7 +1976,7 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
elog(MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u; Tuple(s) moved: %u. %s", elog(MESSAGE_LEVEL, "Rel %s: Pages: %u --> %u; Tuple(s) moved: %u. %s",
RelationGetRelationName(onerel), RelationGetRelationName(onerel),
nblocks, blkno, num_moved, nblocks, blkno, num_moved,
show_rusage(&ru0)); vac_show_rusage(&ru0));
/* /*
* Reflect the motion of system tuples to catalog cache here. * Reflect the motion of system tuples to catalog cache here.
...@@ -2185,7 +2202,7 @@ scan_index(Relation indrel, double num_tuples) ...@@ -2185,7 +2202,7 @@ scan_index(Relation indrel, double num_tuples)
double nitups; double nitups;
VacRUsage ru0; VacRUsage ru0;
init_rusage(&ru0); vac_init_rusage(&ru0);
/* walk through the entire index */ /* walk through the entire index */
iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL); iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL);
...@@ -2206,7 +2223,7 @@ scan_index(Relation indrel, double num_tuples) ...@@ -2206,7 +2223,7 @@ scan_index(Relation indrel, double num_tuples)
elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %.0f. %s", elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %.0f. %s",
RelationGetRelationName(indrel), nipages, nitups, RelationGetRelationName(indrel), nipages, nitups,
show_rusage(&ru0)); vac_show_rusage(&ru0));
/* /*
* Check for tuple count mismatch. If the index is partial, then * Check for tuple count mismatch. If the index is partial, then
...@@ -2247,7 +2264,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel, ...@@ -2247,7 +2264,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
VacPage vp; VacPage vp;
VacRUsage ru0; VacRUsage ru0;
init_rusage(&ru0); vac_init_rusage(&ru0);
/* walk through the entire index */ /* walk through the entire index */
iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL); iscan = index_beginscan(indrel, false, 0, (ScanKey) NULL);
...@@ -2293,7 +2310,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel, ...@@ -2293,7 +2310,7 @@ vacuum_index(VacPageList vacpagelist, Relation indrel,
elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %.0f: Deleted %u. %s", elog(MESSAGE_LEVEL, "Index %s: Pages %u; Tuples %.0f: Deleted %u. %s",
RelationGetRelationName(indrel), num_pages, RelationGetRelationName(indrel), num_pages,
num_index_tuples - keep_tuples, tups_vacuumed, 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 * Check for tuple count mismatch. If the index is partial, then
...@@ -2358,63 +2375,6 @@ tid_reaped(ItemPointer itemptr, VacPageList vacpagelist) ...@@ -2358,63 +2375,6 @@ tid_reaped(ItemPointer itemptr, VacPageList vacpagelist)
return vp; 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 * 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. * free space in the relation, discarding any old info the map may have.
...@@ -2683,8 +2643,8 @@ enough_space(VacPage vacpage, Size len) ...@@ -2683,8 +2643,8 @@ enough_space(VacPage vacpage, Size len)
/* /*
* Initialize usage snapshot. * Initialize usage snapshot.
*/ */
static void void
init_rusage(VacRUsage *ru0) vac_init_rusage(VacRUsage *ru0)
{ {
struct timezone tz; struct timezone tz;
...@@ -2698,13 +2658,13 @@ init_rusage(VacRUsage *ru0) ...@@ -2698,13 +2658,13 @@ init_rusage(VacRUsage *ru0)
* tacky, but no one ever claimed that the Postgres backend is * tacky, but no one ever claimed that the Postgres backend is
* threadable... * threadable...
*/ */
static char * const char *
show_rusage(VacRUsage *ru0) vac_show_rusage(VacRUsage *ru0)
{ {
static char result[100]; static char result[100];
VacRUsage ru1; VacRUsage ru1;
init_rusage(&ru1); vac_init_rusage(&ru1);
if (ru1.tv.tv_usec < ru0->tv.tv_usec) if (ru1.tv.tv_usec < ru0->tv.tv_usec)
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -194,7 +194,7 @@ TransactionIdIsInProgress(TransactionId xid)
{ {
PROC *proc = (PROC *) MAKE_PTR(pOffset); PROC *proc = (PROC *) MAKE_PTR(pOffset);
if (proc->xid == xid) if (TransactionIdEquals(proc->xid, xid))
{ {
result = true; result = true;
break; break;
...@@ -212,15 +212,20 @@ TransactionIdIsInProgress(TransactionId xid) ...@@ -212,15 +212,20 @@ TransactionIdIsInProgress(TransactionId xid)
* when all current transaction were started. * when all current transaction were started.
* It's used by vacuum to decide what deleted * It's used by vacuum to decide what deleted
* tuples must be preserved in a table. * 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 void
GetXmaxRecent(TransactionId *XmaxRecent) GetXmaxRecent(TransactionId *XmaxRecent)
{ {
SISeg *segP = shmInvalBuffer; SISeg *segP = shmInvalBuffer;
ProcState *stateP = segP->procState; ProcState *stateP = segP->procState;
TransactionId result;
int index; int index;
*XmaxRecent = GetCurrentTransactionId(); result = GetCurrentTransactionId();
SpinAcquire(SInvalLock); SpinAcquire(SInvalLock);
...@@ -231,18 +236,24 @@ GetXmaxRecent(TransactionId *XmaxRecent) ...@@ -231,18 +236,24 @@ GetXmaxRecent(TransactionId *XmaxRecent)
if (pOffset != INVALID_OFFSET) if (pOffset != INVALID_OFFSET)
{ {
PROC *proc = (PROC *) MAKE_PTR(pOffset); PROC *proc = (PROC *) MAKE_PTR(pOffset);
TransactionId xmin; TransactionId xid;
xmin = proc->xmin; /* we don't use spin-locking in xid = proc->xid;
* AbortTransaction() ! */ if (! TransactionIdIsSpecial(xid))
if (proc == MyProc || xmin < FirstTransactionId) {
continue; if (TransactionIdPrecedes(xid, result))
if (xmin < *XmaxRecent) result = xid;
*XmaxRecent = xmin; xid = proc->xmin;
if (! TransactionIdIsSpecial(xid))
if (TransactionIdPrecedes(xid, result))
result = xid;
}
} }
} }
SpinRelease(SInvalLock); SpinRelease(SInvalLock);
*XmaxRecent = result;
} }
/* /*
...@@ -291,28 +302,21 @@ GetSnapshotData(bool serializable) ...@@ -291,28 +302,21 @@ GetSnapshotData(bool serializable)
if (pOffset != INVALID_OFFSET) if (pOffset != INVALID_OFFSET)
{ {
PROC *proc = (PROC *) MAKE_PTR(pOffset); PROC *proc = (PROC *) MAKE_PTR(pOffset);
TransactionId xid; TransactionId xid = proc->xid;
/* /*
* We don't use spin-locking when changing proc->xid in * Ignore my own proc (dealt with my xid above), procs not
* GetNewTransactionId() and in AbortTransaction() !.. * 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 || if (proc == MyProc ||
xid < FirstTransactionId || xid >= snapshot->xmax) TransactionIdIsSpecial(xid) ||
{ ! TransactionIdPrecedes(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.
*--------
*/
continue; continue;
}
if (xid < snapshot->xmin) if (TransactionIdPrecedes(xid, snapshot->xmin))
snapshot->xmin = xid; snapshot->xmin = xid;
snapshot->xip[count] = xid; snapshot->xip[count] = xid;
count++; count++;
......
...@@ -8,18 +8,18 @@ ...@@ -8,18 +8,18 @@
* *
* *
* IDENTIFICATION * 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 "postgres.h"
#include "storage/sinval.h"
#include "utils/tqual.h" #include "utils/tqual.h"
SnapshotData SnapshotDirtyData;
static SnapshotData SnapshotDirtyData;
Snapshot SnapshotDirty = &SnapshotDirtyData; Snapshot SnapshotDirty = &SnapshotDirtyData;
Snapshot QuerySnapshot = NULL; Snapshot QuerySnapshot = NULL;
...@@ -587,10 +587,138 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot) ...@@ -587,10 +587,138 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
return false; 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 void
SetQuerySnapshot(void) SetQuerySnapshot(void)
{ {
/* Initialize snapshot overriding to false */ /* Initialize snapshot overriding to false */
ReferentialIntegritySnapshotOverride = false; ReferentialIntegritySnapshotOverride = false;
...@@ -615,13 +743,11 @@ SetQuerySnapshot(void) ...@@ -615,13 +743,11 @@ SetQuerySnapshot(void)
QuerySnapshot = GetSnapshotData(false); QuerySnapshot = GetSnapshotData(false);
Assert(QuerySnapshot != NULL); Assert(QuerySnapshot != NULL);
} }
void void
FreeXactSnapshot(void) FreeXactSnapshot(void)
{ {
if (QuerySnapshot != NULL && QuerySnapshot != SerializableSnapshot) if (QuerySnapshot != NULL && QuerySnapshot != SerializableSnapshot)
{ {
free(QuerySnapshot->xip); free(QuerySnapshot->xip);
...@@ -637,5 +763,4 @@ FreeXactSnapshot(void) ...@@ -637,5 +763,4 @@ FreeXactSnapshot(void)
} }
SerializableSnapshot = NULL; SerializableSnapshot = NULL;
} }
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -15,6 +15,7 @@
#define HEAPAM_H #define HEAPAM_H
#include <time.h> #include <time.h>
#include "access/htup.h" #include "access/htup.h"
#include "access/relscan.h" #include "access/relscan.h"
#include "access/tupmacs.h" #include "access/tupmacs.h"
...@@ -218,6 +219,11 @@ extern void heap_restrpos(HeapScanDesc scan); ...@@ -218,6 +219,11 @@ extern void heap_restrpos(HeapScanDesc scan);
extern void heap_redo(XLogRecPtr lsn, XLogRecord *rptr); extern void heap_redo(XLogRecPtr lsn, XLogRecord *rptr);
extern void heap_undo(XLogRecPtr lsn, XLogRecord *rptr); extern void heap_undo(XLogRecPtr lsn, XLogRecord *rptr);
extern void heap_desc(char *buf, uint8 xl_info, char *rec); 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 */ /* in common/heaptuple.c */
extern Size ComputeDataSize(TupleDesc tupleDesc, Datum *value, char *nulls); extern Size ComputeDataSize(TupleDesc tupleDesc, Datum *value, char *nulls);
......
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* transam.h * 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) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: transam.h,v 1.35 2001/05/25 15:45:33 momjian Exp $ * $Id: transam.h,v 1.36 2001/07/12 04:11:13 tgl Exp $
*
* NOTES
* Transaction System Version 101 now support proper oid
* generation and recording in the variable relation.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -20,77 +16,60 @@ ...@@ -20,77 +16,60 @@
#include "storage/bufmgr.h" #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 * We do not use any transaction IDs less than 512 --- this leaves the first
* relations on the first 4 bytes. This is so that if we improve * 128 bytes of pg_log available for special purposes such as version number
* the format of the transaction log after postgres version 2, then * storage. (Currently, we do not actually use them for anything.)
* people won't have to rebuild their databases.
* *
* TRANS_SYSTEM_VERSION 100 means major version 1 minor version 0. * AmiTransactionId is the XID for "bootstrap" operations. It should always
* Two databases with the same major version should be compatible, * be considered valid.
* even if their minor versions differ. *
* 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 * someday we will use "11" = 3 = XID_COMMIT_CHILD to mean the
* commiting of child xactions. * 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_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 */ #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 * oid's less than this appear in the .bki files. the choice of
* 16384 is completely arbitrary. * 16384 is completely arbitrary.
* ---------- * ----------
*/ */
#define BootstrapObjectIdData 16384 #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 * VariableCache is placed in shmem and used by
* backends to get next available XID & OID. * backends to get next available XID & OID.
...@@ -104,6 +83,7 @@ typedef struct VariableCacheData ...@@ -104,6 +83,7 @@ typedef struct VariableCacheData
typedef VariableCacheData *VariableCache; typedef VariableCacheData *VariableCache;
/* ---------------- /* ----------------
* extern declarations * extern declarations
* ---------------- * ----------------
...@@ -142,16 +122,7 @@ extern void CheckMaxObjectId(Oid assigned_oid); ...@@ -142,16 +122,7 @@ extern void CheckMaxObjectId(Oid assigned_oid);
/* in transam.c */ /* in transam.c */
extern Relation LogRelation; extern Relation LogRelation;
extern TransactionId cachedTestXid; /* in xact.c */
extern XidStatus cachedTestXidStatus;
extern TransactionId NullTransactionId;
extern TransactionId AmiTransactionId;
extern TransactionId FirstTransactionId;
extern int RecoveryCheckingEnableState;
/* in transsup.c */
extern bool AMI_OVERRIDE; extern bool AMI_OVERRIDE;
/* in varsup.c */ /* in varsup.c */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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; ...@@ -67,17 +67,6 @@ typedef TransactionStateData *TransactionState;
#define TBLOCK_ABORT 4 #define TBLOCK_ABORT 4
#define TBLOCK_ENDABORT 5 #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 * XLOG allows to store some information in high 4 bits of log
* record xl_info field * record xl_info field
...@@ -133,8 +122,6 @@ extern void AbortOutOfAnyTransaction(void); ...@@ -133,8 +122,6 @@ extern void AbortOutOfAnyTransaction(void);
extern void RecordTransactionCommit(void); extern void RecordTransactionCommit(void);
extern TransactionId DisabledTransactionId;
extern void XactPushRollback(void (*func) (void *), void *data); extern void XactPushRollback(void (*func) (void *), void *data);
extern void XactPopRollback(void); extern void XactPopRollback(void);
...@@ -146,6 +133,5 @@ extern void xact_desc(char *buf, uint8 xl_info, char *rec); ...@@ -146,6 +133,5 @@ extern void xact_desc(char *buf, uint8 xl_info, char *rec);
extern Datum xidin(PG_FUNCTION_ARGS); extern Datum xidin(PG_FUNCTION_ARGS);
extern Datum xidout(PG_FUNCTION_ARGS); extern Datum xidout(PG_FUNCTION_ARGS);
extern Datum xideq(PG_FUNCTION_ARGS); extern Datum xideq(PG_FUNCTION_ARGS);
extern void TransactionIdAdd(TransactionId *xid, int value);
#endif /* XACT_H */ #endif /* XACT_H */
...@@ -7,23 +7,43 @@ ...@@ -7,23 +7,43 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 #ifndef VACUUM_H
#define 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 "nodes/parsenodes.h"
#include "storage/block.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 */ /* in commands/vacuum.c */
extern void vacuum(VacuumStmt *vacstmt); extern void vacuum(VacuumStmt *vacstmt);
extern void vac_update_relstats(Oid relid, extern void vac_update_relstats(Oid relid,
BlockNumber num_pages, BlockNumber num_pages,
double num_tuples, double num_tuples,
bool hasindex); bool hasindex);
extern void vac_init_rusage(VacRUsage *ru0);
extern const char *vac_show_rusage(VacRUsage *ru0);
/* in commands/analyze.c */ /* in commands/analyze.c */
extern void analyze_rel(Oid relid, VacuumStmt *vacstmt); extern void analyze_rel(Oid relid, VacuumStmt *vacstmt);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 ...@@ -23,8 +23,9 @@ typedef struct SnapshotData
{ {
TransactionId xmin; /* XID < xmin are visible to me */ TransactionId xmin; /* XID < xmin are visible to me */
TransactionId xmax; /* XID >= xmax are invisible to me */ TransactionId xmax; /* XID >= xmax are invisible to me */
uint32 xcnt; /* # of xact below */ uint32 xcnt; /* # of xact ids in xip[] */
TransactionId *xip; /* array of xacts in progress */ TransactionId *xip; /* array of xact IDs in progress */
/* note: all ids in xip[] satisfy xmin <= xip[i] < xmax */
ItemPointerData tid; /* required for Dirty snapshot -:( */ ItemPointerData tid; /* required for Dirty snapshot -:( */
} SnapshotData; } SnapshotData;
...@@ -34,8 +35,8 @@ typedef SnapshotData *Snapshot; ...@@ -34,8 +35,8 @@ typedef SnapshotData *Snapshot;
#define SnapshotSelf ((Snapshot) 0x1) #define SnapshotSelf ((Snapshot) 0x1)
#define SnapshotAny ((Snapshot) 0x2) #define SnapshotAny ((Snapshot) 0x2)
extern Snapshot SnapshotDirty; extern DLLIMPORT Snapshot SnapshotDirty;
extern Snapshot QuerySnapshot; extern DLLIMPORT Snapshot QuerySnapshot;
extern DLLIMPORT Snapshot SerializableSnapshot; extern DLLIMPORT Snapshot SerializableSnapshot;
extern bool ReferentialIntegritySnapshotOverride; extern bool ReferentialIntegritySnapshotOverride;
...@@ -66,11 +67,11 @@ extern bool ReferentialIntegritySnapshotOverride; ...@@ -66,11 +67,11 @@ extern bool ReferentialIntegritySnapshotOverride;
(IsSnapshotSelf(snapshot) ? \ (IsSnapshotSelf(snapshot) ? \
HeapTupleSatisfiesItself((tuple)->t_data) \ HeapTupleSatisfiesItself((tuple)->t_data) \
: \ : \
(IsSnapshotDirty(snapshot) ? \ (IsSnapshotNow(snapshot) ? \
HeapTupleSatisfiesDirty((tuple)->t_data) \ HeapTupleSatisfiesNow((tuple)->t_data) \
: \ : \
(IsSnapshotNow(snapshot) ? \ (IsSnapshotDirty(snapshot) ? \
HeapTupleSatisfiesNow((tuple)->t_data) \ HeapTupleSatisfiesDirty((tuple)->t_data) \
: \ : \
HeapTupleSatisfiesSnapshot((tuple)->t_data, snapshot) \ HeapTupleSatisfiesSnapshot((tuple)->t_data, snapshot) \
) \ ) \
...@@ -79,18 +80,31 @@ extern bool ReferentialIntegritySnapshotOverride; ...@@ -79,18 +80,31 @@ extern bool ReferentialIntegritySnapshotOverride;
) \ ) \
) )
/* Result codes for HeapTupleSatisfiesUpdate */
#define HeapTupleMayBeUpdated 0 #define HeapTupleMayBeUpdated 0
#define HeapTupleInvisible 1 #define HeapTupleInvisible 1
#define HeapTupleSelfUpdated 2 #define HeapTupleSelfUpdated 2
#define HeapTupleUpdated 3 #define HeapTupleUpdated 3
#define HeapTupleBeingUpdated 4 #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 HeapTupleSatisfiesItself(HeapTupleHeader tuple);
extern bool HeapTupleSatisfiesNow(HeapTupleHeader tuple); extern bool HeapTupleSatisfiesNow(HeapTupleHeader tuple);
extern bool HeapTupleSatisfiesDirty(HeapTupleHeader tuple); extern bool HeapTupleSatisfiesDirty(HeapTupleHeader tuple);
extern bool HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, extern bool HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple,
Snapshot snapshot); Snapshot snapshot);
extern int HeapTupleSatisfiesUpdate(HeapTuple tuple); extern int HeapTupleSatisfiesUpdate(HeapTuple tuple);
extern HTSV_Result HeapTupleSatisfiesVacuum(HeapTupleHeader tuple,
TransactionId XmaxRecent);
extern Snapshot GetSnapshotData(bool serializable); extern Snapshot GetSnapshotData(bool serializable);
extern void SetQuerySnapshot(void); extern void SetQuerySnapshot(void);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment