transam.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:15k
- /*-------------------------------------------------------------------------
- *
- * transam.c
- * postgres transaction log/time interface routines
- *
- * Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /usr/local/cvsroot/pgsql/src/backend/access/transam/transam.c,v 1.27.2.2 1999/08/08 20:24:12 tgl Exp $
- *
- * NOTES
- * This file contains the high level access-method interface to the
- * transaction system.
- *
- *-------------------------------------------------------------------------
- */
- #include "postgres.h"
- #include "access/heapam.h"
- #include "catalog/catname.h"
- static int RecoveryCheckingEnabled(void);
- static void TransRecover(Relation logRelation);
- static bool TransactionLogTest(TransactionId transactionId, XidStatus status);
- static void TransactionLogUpdate(TransactionId transactionId,
- XidStatus status);
- /* ----------------
- * global variables holding pointers to relations used
- * by the transaction system. These are initialized by
- * InitializeTransactionLog().
- * ----------------
- */
- Relation LogRelation = (Relation) NULL;
- Relation VariableRelation = (Relation) NULL;
- /* ----------------
- * global variables holding cached transaction id's and statuses.
- * ----------------
- */
- TransactionId cachedTestXid;
- XidStatus cachedTestXidStatus;
- /* ----------------
- * transaction system constants
- * ----------------
- */
- /* ----------------------------------------------------------------
- * 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;
- /* ----------------
- * transaction recovery state variables
- *
- * When the transaction system is initialized, we may
- * need to do recovery checking. This decision is decided
- * by the postmaster or the user by supplying the backend
- * with a special flag. In general, we want to do recovery
- * checking whenever we are running without a postmaster
- * or when the number of backends running under the postmaster
- * goes from zero to one. -cim 3/21/90
- * ----------------
- */
- int RecoveryCheckingEnableState = 0;
- /* ------------------
- * spinlock for oid generation
- * -----------------
- */
- extern int OidGenLockId;
- /* ----------------
- * recovery checking accessors
- * ----------------
- */
- static int
- RecoveryCheckingEnabled(void)
- {
- return RecoveryCheckingEnableState;
- }
- #ifdef NOT_USED
- static void
- SetRecoveryCheckingEnabled(bool state)
- {
- RecoveryCheckingEnableState = (state == true);
- }
- #endif
- /* ----------------------------------------------------------------
- * postgres log access method interface
- *
- * TransactionLogTest
- * TransactionLogUpdate
- * ========
- * these functions do work for the interface
- * functions - they search/retrieve and append/update
- * information in the log and time relations.
- * ----------------------------------------------------------------
- */
- /* --------------------------------
- * TransactionLogTest
- * --------------------------------
- */
- static bool /* true/false: does transaction id have
- * specified status? */
- TransactionLogTest(TransactionId transactionId, /* transaction id to test */
- XidStatus status) /* transaction status */
- {
- BlockNumber blockNumber;
- XidStatus xidstatus; /* recorded status of xid */
- bool fail = false; /* success/failure */
- /* ----------------
- * during initialization consider all transactions
- * as having been committed
- * ----------------
- */
- if (!RelationIsValid(LogRelation))
- return (bool) (status == XID_COMMIT);
- /* ----------------
- * before going to the buffer manager, check our single
- * item cache to see if we didn't just check the transaction
- * status a moment ago.
- * ----------------
- */
- if (TransactionIdEquals(transactionId, cachedTestXid))
- return (bool)
- (status == cachedTestXidStatus);
- /* ----------------
- * compute the item pointer corresponding to the
- * page containing our transaction id. We save the item in
- * our cache to speed up things if we happen to ask for the
- * same xid's status more than once.
- * ----------------
- */
- TransComputeBlockNumber(LogRelation, transactionId, &blockNumber);
- xidstatus = TransBlockNumberGetXidStatus(LogRelation,
- blockNumber,
- transactionId,
- &fail);
- if (!fail)
- {
- /*
- * DO NOT cache status for transactions in unknown state !!!
- */
- if (xidstatus == XID_COMMIT || xidstatus == XID_ABORT)
- {
- TransactionIdStore(transactionId, &cachedTestXid);
- cachedTestXidStatus = xidstatus;
- }
- return (bool) (status == xidstatus);
- }
- /* ----------------
- * here the block didn't contain the information we wanted
- * ----------------
- */
- elog(ERROR, "TransactionLogTest: failed to get xidstatus");
- /*
- * so lint is happy...
- */
- return false;
- }
- /* --------------------------------
- * TransactionLogUpdate
- * --------------------------------
- */
- static void
- TransactionLogUpdate(TransactionId transactionId, /* trans id to update */
- XidStatus status) /* new trans status */
- {
- BlockNumber blockNumber;
- bool fail = false; /* success/failure */
- /* ----------------
- * during initialization we don't record any updates.
- * ----------------
- */
- if (!RelationIsValid(LogRelation))
- return;
- /* ----------------
- * update the log relation
- * ----------------
- */
- TransComputeBlockNumber(LogRelation, transactionId, &blockNumber);
- TransBlockNumberSetXidStatus(LogRelation,
- blockNumber,
- transactionId,
- status,
- &fail);
- /*
- * update (invalidate) our single item TransactionLogTest cache.
- *
- * if (status != XID_COMMIT)
- *
- * What's the hell ?! Why != XID_COMMIT ?!
- */
- TransactionIdStore(transactionId, &cachedTestXid);
- cachedTestXidStatus = status;
- }
- /* ----------------------------------------------------------------
- * transaction recovery code
- * ----------------------------------------------------------------
- */
- /* --------------------------------
- * TransRecover
- *
- * preform transaction recovery checking.
- *
- * Note: this should only be preformed if no other backends
- * are running. This is known by the postmaster and
- * conveyed by the postmaster passing a "do recovery checking"
- * flag to the backend.
- *
- * here we get the last recorded transaction from the log,
- * get the "last" and "next" transactions from the variable relation
- * and then preform some integrity tests:
- *
- * 1) No transaction may exist higher then the "next" available
- * transaction recorded in the variable relation. If this is the
- * case then it means either the log or the variable relation
- * has become corrupted.
- *
- * 2) The last committed transaction may not be higher then the
- * next available transaction for the same reason.
- *
- * 3) The last recorded transaction may not be lower then the
- * last committed transaction. (the reverse is ok - it means
- * that some transactions have aborted since the last commit)
- *
- * Here is what the proper situation looks like. The line
- * represents the data stored in the log. 'c' indicates the
- * transaction was recorded as committed, 'a' indicates an
- * abortted transaction and '.' represents information not
- * recorded. These may correspond to in progress transactions.
- *
- * c c a c . . a . . . . . . . . . .
- * | |
- * last next
- *
- * Since "next" is only incremented by GetNewTransactionId() which
- * is called when transactions are started. Hence if there
- * are commits or aborts after "next", then it means we committed
- * or aborted BEFORE we started the transaction. This is the
- * rational behind constraint (1).
- *
- * Likewise, "last" should never greater then "next" for essentially
- * the same reason - it would imply we committed before we started.
- * This is the reasoning for (2).
- *
- * (3) implies we may never have a situation such as:
- *
- * c c a c . . a c . . . . . . . . .
- * | |
- * last next
- *
- * where there is a 'c' greater then "last".
- *
- * Recovery checking is more difficult in the case where
- * several backends are executing concurrently because the
- * transactions may be executing in the other backends.
- * So, we only do recovery stuff when the backend is explicitly
- * passed a flag on the command line.
- * --------------------------------
- */
- static void
- TransRecover(Relation logRelation)
- {
- #ifdef NOT_USED
- /* ----------------
- * first get the last recorded transaction in the log.
- * ----------------
- */
- TransGetLastRecordedTransaction(logRelation, logLastXid, &fail);
- if (fail == true)
- elog(ERROR, "TransRecover: failed TransGetLastRecordedTransaction");
- /* ----------------
- * next get the "last" and "next" variables
- * ----------------
- */
- VariableRelationGetLastXid(&varLastXid);
- VariableRelationGetNextXid(&varNextXid);
- /* ----------------
- * intregity test (1)
- * ----------------
- */
- if (TransactionIdIsLessThan(varNextXid, logLastXid))
- elog(ERROR, "TransRecover: varNextXid < logLastXid");
- /* ----------------
- * intregity test (2)
- * ----------------
- */
- /* ----------------
- * intregity test (3)
- * ----------------
- */
- /* ----------------
- * here we have a valid "
- *
- * **** RESUME HERE ****
- * ----------------
- */
- varNextXid = TransactionIdDup(varLastXid);
- TransactionIdIncrement(&varNextXid);
- VarPut(var, VAR_PUT_LASTXID, varLastXid);
- VarPut(var, VAR_PUT_NEXTXID, varNextXid);
- #endif
- }
- /* ----------------------------------------------------------------
- * Interface functions
- *
- * InitializeTransactionLog
- * ========
- * this function (called near cinit) initializes
- * the transaction log, time and variable relations.
- *
- * TransactionId DidCommit
- * TransactionId DidAbort
- * TransactionId IsInProgress
- * ========
- * these functions test the transaction status of
- * a specified transaction id.
- *
- * TransactionId Commit
- * TransactionId Abort
- * TransactionId SetInProgress
- * ========
- * these functions set the transaction status
- * of the specified xid. TransactionIdCommit() also
- * records the current time in the time relation
- * and updates the variable relation counter.
- *
- * ----------------------------------------------------------------
- */
- /*
- * InitializeTransactionLog
- * Initializes transaction logging.
- */
- void
- InitializeTransactionLog(void)
- {
- Relation logRelation;
- MemoryContext oldContext;
- /* ----------------
- * don't do anything during bootstrapping
- * ----------------
- */
- if (AMI_OVERRIDE)
- return;
- /* ----------------
- * disable the transaction system so the access methods
- * don't interfere during initialization.
- * ----------------
- */
- OverrideTransactionSystem(true);
- /* ----------------
- * make sure allocations occur within the top memory context
- * so that our log management structures are protected from
- * garbage collection at the end of every transaction.
- * ----------------
- */
- oldContext = MemoryContextSwitchTo(TopMemoryContext);
- /* ----------------
- * first open the log and time relations
- * (these are created by amiint so they are guaranteed to exist)
- * ----------------
- */
- logRelation = heap_openr(LogRelationName);
- VariableRelation = heap_openr(VariableRelationName);
- /* ----------------
- * XXX TransactionLogUpdate requires that LogRelation
- * is valid so we temporarily set it so we can initialize
- * things properly. This could be done cleaner.
- * ----------------
- */
- LogRelation = logRelation;
- /* ----------------
- * if we have a virgin database, we initialize the log
- * relation by committing the AmiTransactionId (id 512) and we
- * initialize the variable relation by setting the next available
- * transaction id to FirstTransactionId (id 514). OID initialization
- * happens as a side effect of bootstrapping in varsup.c.
- * ----------------
- */
- SpinAcquire(OidGenLockId);
- if (!TransactionIdDidCommit(AmiTransactionId))
- {
- /* ----------------
- * SOMEDAY initialize the information stored in
- * the headers of the log/variable relations.
- * ----------------
- */
- TransactionLogUpdate(AmiTransactionId, XID_COMMIT);
- TransactionIdStore(AmiTransactionId, &cachedTestXid);
- cachedTestXidStatus = XID_COMMIT;
- VariableRelationPutNextXid(FirstTransactionId);
- }
- else if (RecoveryCheckingEnabled())
- {
- /* ----------------
- * if we have a pre-initialized database and if the
- * perform recovery checking flag was passed then we
- * do our database integrity checking.
- * ----------------
- */
- TransRecover(logRelation);
- }
- LogRelation = (Relation) NULL;
- SpinRelease(OidGenLockId);
- /* ----------------
- * now re-enable the transaction system
- * ----------------
- */
- OverrideTransactionSystem(false);
- /* ----------------
- * instantiate the global variables
- * ----------------
- */
- LogRelation = logRelation;
- /* ----------------
- * restore the memory context to the previous context
- * before we return from initialization.
- * ----------------
- */
- MemoryContextSwitchTo(oldContext);
- }
- /* --------------------------------
- * TransactionId DidCommit
- * TransactionId DidAbort
- * TransactionId IsInProgress
- * --------------------------------
- */
- /*
- * TransactionIdDidCommit
- * True iff transaction associated with the identifier did commit.
- *
- * Note:
- * Assumes transaction identifier is valid.
- */
- bool /* true if given transaction committed */
- TransactionIdDidCommit(TransactionId transactionId)
- {
- if (AMI_OVERRIDE)
- return true;
- return TransactionLogTest(transactionId, XID_COMMIT);
- }
- /*
- * TransactionIdDidAborted
- * True iff transaction associated with the identifier did abort.
- *
- * Note:
- * Assumes transaction identifier is valid.
- * XXX Is this unneeded?
- */
- bool /* true if given transaction aborted */
- TransactionIdDidAbort(TransactionId transactionId)
- {
- if (AMI_OVERRIDE)
- return false;
- return TransactionLogTest(transactionId, XID_ABORT);
- }
- /*
- * Now this func in shmem.c and gives quality answer by scanning
- * PROC structures of all running backend. - vadim 11/26/96
- *
- * Old comments:
- * true if given transaction neither committed nor aborted
- bool
- TransactionIdIsInProgress(TransactionId transactionId)
- {
- if (AMI_OVERRIDE)
- return false;
- return TransactionLogTest(transactionId, XID_INPROGRESS);
- }
- */
- /* --------------------------------
- * TransactionId Commit
- * TransactionId Abort
- * TransactionId SetInProgress
- * --------------------------------
- */
- /*
- * TransactionIdCommit
- * Commits the transaction associated with the identifier.
- *
- * Note:
- * Assumes transaction identifier is valid.
- */
- void
- TransactionIdCommit(TransactionId transactionId)
- {
- if (AMI_OVERRIDE)
- return;
- TransactionLogUpdate(transactionId, XID_COMMIT);
- }
- /*
- * TransactionIdAbort
- * Aborts the transaction associated with the identifier.
- *
- * Note:
- * Assumes transaction identifier is valid.
- */
- void
- TransactionIdAbort(TransactionId transactionId)
- {
- if (AMI_OVERRIDE)
- return;
- TransactionLogUpdate(transactionId, XID_ABORT);
- }