diff options
Diffstat (limited to 'src/backend/access/transam')
-rw-r--r-- | src/backend/access/transam/Makefile.inc | 14 | ||||
-rw-r--r-- | src/backend/access/transam/transam.c | 675 | ||||
-rw-r--r-- | src/backend/access/transam/transsup.c | 663 | ||||
-rw-r--r-- | src/backend/access/transam/varsup.c | 606 | ||||
-rw-r--r-- | src/backend/access/transam/xact.c | 1314 | ||||
-rw-r--r-- | src/backend/access/transam/xid.c | 156 |
6 files changed, 3428 insertions, 0 deletions
diff --git a/src/backend/access/transam/Makefile.inc b/src/backend/access/transam/Makefile.inc new file mode 100644 index 00000000000..c4f5b95a0ae --- /dev/null +++ b/src/backend/access/transam/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for access/transam +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= transam.c transsup.c varsup.c xact.c xid.c diff --git a/src/backend/access/transam/transam.c b/src/backend/access/transam/transam.c new file mode 100644 index 00000000000..b3789a8c2c5 --- /dev/null +++ b/src/backend/access/transam/transam.c @@ -0,0 +1,675 @@ +/*------------------------------------------------------------------------- + * + * transam.c-- + * postgres transaction log/time interface routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/transam/transam.c,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ + * + * NOTES + * This file contains the high level access-method interface to the + * transaction system. + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "machine.h" /* in port/ directory (needed for BLCKSZ) */ + +#include "access/heapam.h" +#include "storage/buf.h" +#include "storage/bufmgr.h" + +#include "utils/memutils.h" +#include "utils/mcxt.h" +#include "utils/rel.h" +#include "utils/elog.h" + +#include "utils/nabstime.h" +#include "catalog/catname.h" + +#include "access/transam.h" +#include "access/xact.h" +#include "commands/vacuum.h" /* for VacuumRunning */ + +/* ---------------- + * global variables holding pointers to relations used + * by the transaction system. These are initialized by + * InitializeTransactionLog(). + * ---------------- + */ + +Relation LogRelation = (Relation) NULL; +Relation TimeRelation = (Relation) NULL; +Relation VariableRelation = (Relation) NULL; + +/* ---------------- + * global variables holding cached transaction id's and statuses. + * ---------------- + */ +TransactionId cachedGetCommitTimeXid; +AbsoluteTime cachedGetCommitTime; +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; + +/* ---------------- + * globals that must be reset at abort + * ---------------- + */ +extern bool BuildingBtree; + + +/* ---------------- + * recovery checking accessors + * ---------------- + */ +int +RecoveryCheckingEnabled() +{ + return RecoveryCheckingEnableState; +} + +void +SetRecoveryCheckingEnabled(bool state) +{ + RecoveryCheckingEnableState = (state == true); +} + +/* ---------------------------------------------------------------- + * postgres log/time 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 + * -------------------------------- + */ + +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) { + TransactionIdStore(transactionId, &cachedTestXid); + cachedTestXidStatus = xidstatus; + return (bool) + (status == xidstatus); + } + + /* ---------------- + * here the block didn't contain the information we wanted + * ---------------- + */ + elog(WARN, "TransactionLogTest: failed to get xidstatus"); + + /* + * so lint is happy... + */ + return(false); +} + +/* -------------------------------- + * TransactionLogUpdate + * -------------------------------- + */ +void +TransactionLogUpdate(TransactionId transactionId, /* trans id to update */ + XidStatus status) /* new trans status */ +{ + BlockNumber blockNumber; + bool fail = false; /* success/failure */ + AbsoluteTime currentTime; /* time of this transaction */ + + /* ---------------- + * during initialization we don't record any updates. + * ---------------- + */ + if (! RelationIsValid(LogRelation)) + return; + + /* ---------------- + * get the transaction commit time + * ---------------- + */ + currentTime = getSystemTime(); + + /* ---------------- + * update the log relation + * ---------------- + */ + TransComputeBlockNumber(LogRelation, transactionId, &blockNumber); + TransBlockNumberSetXidStatus(LogRelation, + blockNumber, + transactionId, + status, + &fail); + + /* ---------------- + * update (invalidate) our single item TransactionLogTest cache. + * ---------------- + */ + TransactionIdStore(transactionId, &cachedTestXid); + cachedTestXidStatus = status; + + /* ---------------- + * now we update the time relation, if necessary + * (we only record commit times) + * ---------------- + */ + if (RelationIsValid(TimeRelation) && status == XID_COMMIT) { + TransComputeBlockNumber(TimeRelation, transactionId, &blockNumber); + TransBlockNumberSetCommitTime(TimeRelation, + blockNumber, + transactionId, + currentTime, + &fail); + /* ---------------- + * update (invalidate) our single item GetCommitTime cache. + * ---------------- + */ + TransactionIdStore(transactionId, &cachedGetCommitTimeXid); + cachedGetCommitTime = currentTime; + } + + /* ---------------- + * now we update the "last committed transaction" field + * in the variable relation if we are recording a commit. + * ---------------- + */ + if (RelationIsValid(VariableRelation) && status == XID_COMMIT) + UpdateLastCommittedXid(transactionId); +} + +/* -------------------------------- + * TransactionIdGetCommitTime + * -------------------------------- + */ + +AbsoluteTime /* commit time of transaction id */ +TransactionIdGetCommitTime(TransactionId transactionId) /* transaction id to test */ +{ + BlockNumber blockNumber; + AbsoluteTime commitTime; /* commit time */ + bool fail = false; /* success/failure */ + + /* ---------------- + * return invalid if we aren't running yet... + * ---------------- + */ + if (! RelationIsValid(TimeRelation)) + return INVALID_ABSTIME; + + /* ---------------- + * before going to the buffer manager, check our single + * item cache to see if we didn't just get the commit time + * a moment ago. + * ---------------- + */ + if (TransactionIdEquals(transactionId, cachedGetCommitTimeXid)) + return cachedGetCommitTime; + + /* ---------------- + * compute the item pointer corresponding to the + * page containing our transaction commit time + * ---------------- + */ + TransComputeBlockNumber(TimeRelation, transactionId, &blockNumber); + commitTime = TransBlockNumberGetCommitTime(TimeRelation, + blockNumber, + transactionId, + &fail); + + /* ---------------- + * update our cache and return the transaction commit time + * ---------------- + */ + if (! fail) { + TransactionIdStore(transactionId, &cachedGetCommitTimeXid); + cachedGetCommitTime = commitTime; + return commitTime; + } else + return INVALID_ABSTIME; +} + +/* ---------------------------------------------------------------- + * 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. + * -------------------------------- + */ +void +TransRecover(Relation logRelation) +{ +#if 0 + /* ---------------- + * first get the last recorded transaction in the log. + * ---------------- + */ + TransGetLastRecordedTransaction(logRelation, logLastXid, &fail); + if (fail == true) + elog(WARN, "TransRecover: failed TransGetLastRecordedTransaction"); + + /* ---------------- + * next get the "last" and "next" variables + * ---------------- + */ + VariableRelationGetLastXid(&varLastXid); + VariableRelationGetNextXid(&varNextXid); + + /* ---------------- + * intregity test (1) + * ---------------- + */ + if (TransactionIdIsLessThan(varNextXid, logLastXid)) + elog(WARN, "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() +{ + Relation logRelation; + Relation timeRelation; + 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); + timeRelation = heap_openr(TimeRelationName); + VariableRelation = heap_openr(VariableRelationName); + /* ---------------- + * XXX TransactionLogUpdate requires that LogRelation + * and TimeRelation are valid so we temporarily set + * them so we can initialize things properly. + * This could be done cleaner. + * ---------------- + */ + LogRelation = logRelation; + TimeRelation = timeRelation; + + /* ---------------- + * if we have a virgin database, we initialize the log and time + * 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/time/variable relations. + * ---------------- + */ + TransactionLogUpdate(AmiTransactionId, 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; + TimeRelation = (Relation) NULL; + SpinRelease(OidGenLockId); + + /* ---------------- + * now re-enable the transaction system + * ---------------- + */ + OverrideTransactionSystem(false); + + /* ---------------- + * instantiate the global variables + * ---------------- + */ + LogRelation = logRelation; + TimeRelation = timeRelation; + + /* ---------------- + * 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); +} + +bool /* true if given transaction neither committed nor aborted */ +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; + + /* + * Within TransactionLogUpdate we call UpdateLastCommited() + * which assumes we have exclusive access to pg_variable. + * Therefore we need to get exclusive access before calling + * TransactionLogUpdate. -mer 18 Aug 1992 + */ + SpinAcquire(OidGenLockId); + TransactionLogUpdate(transactionId, XID_COMMIT); + SpinRelease(OidGenLockId); +} + +/* + * TransactionIdAbort -- + * Aborts the transaction associated with the identifier. + * + * Note: + * Assumes transaction identifier is valid. + */ +void +TransactionIdAbort(TransactionId transactionId) +{ + BuildingBtree = false; + + if (VacuumRunning) + vc_abort(); + + if (AMI_OVERRIDE) + return; + + TransactionLogUpdate(transactionId, XID_ABORT); +} + +void +TransactionIdSetInProgress(TransactionId transactionId) +{ + if (AMI_OVERRIDE) + return; + + TransactionLogUpdate(transactionId, XID_INPROGRESS); +} diff --git a/src/backend/access/transam/transsup.c b/src/backend/access/transam/transsup.c new file mode 100644 index 00000000000..a1e5b17ec13 --- /dev/null +++ b/src/backend/access/transam/transsup.c @@ -0,0 +1,663 @@ +/*------------------------------------------------------------------------- + * + * transsup.c-- + * postgres transaction access method support code + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/transsup.c,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ + * + * NOTES + * This file contains support functions for the high + * level access method interface routines found in transam.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "machine.h" /* in port/ directory (needed for BLCKSZ) */ + +#include "storage/buf.h" +#include "storage/bufmgr.h" + +#include "utils/rel.h" +#include "utils/elog.h" +#include "utils/memutils.h" +#include "utils/nabstime.h" + +#include "catalog/heap.h" +#include "access/transam.h" /* where the declarations go */ +#include "access/xact.h" /* where the declarations go */ + +#include "storage/smgr.h" + +/* ---------------------------------------------------------------- + * general support routines + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * AmiTransactionOverride + * + * This function is used to manipulate the bootstrap flag. + * -------------------------------- + */ +void +AmiTransactionOverride(bool flag) +{ + AMI_OVERRIDE = flag; +} + +/* -------------------------------- + * TransComputeBlockNumber + * -------------------------------- + */ +void +TransComputeBlockNumber(Relation relation, /* relation to test */ + TransactionId transactionId, /* transaction id to test */ + BlockNumber *blockNumberOutP) +{ + long itemsPerBlock; + + /* ---------------- + * we calculate the block number of our transaction + * by dividing the transaction id by the number of + * transaction things per block. + * ---------------- + */ + if (relation == LogRelation) + itemsPerBlock = TP_NumXidStatusPerBlock; + else if (relation == TimeRelation) + itemsPerBlock = TP_NumTimePerBlock; + else + elog(WARN, "TransComputeBlockNumber: unknown relation"); + + /* ---------------- + * warning! if the transaction id's get too large + * then a BlockNumber may not be large enough to hold the results + * of our division. + * + * XXX this will all vanish soon when we implement an improved + * transaction id schema -cim 3/23/90 + * + * This has vanished now that xid's are 4 bytes (no longer 5). + * -mer 5/24/92 + * ---------------- + */ + (*blockNumberOutP) = transactionId / itemsPerBlock; +} + + +/* ---------------------------------------------------------------- + * trans block support routines + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * TransBlockGetLastTransactionIdStatus + * + * This returns the status and transaction id of the last + * transaction information recorded on the given TransBlock. + * -------------------------------- + */ + +XidStatus +TransBlockGetLastTransactionIdStatus(Block tblock, + TransactionId baseXid, + TransactionId *returnXidP) +{ + Index index; + Index maxIndex; + bits8 bit1; + bits8 bit2; + BitIndex offset; + XidStatus xstatus; + + /* ---------------- + * sanity check + * ---------------- + */ + Assert((tblock != NULL)); + + /* ---------------- + * search downward from the top of the block data, looking + * for the first Non-in progress transaction status. Since we + * are scanning backward, this will be last recorded transaction + * status on the block. + * ---------------- + */ + maxIndex = TP_NumXidStatusPerBlock; + for (index = maxIndex-1; index>=0; index--) { + offset = BitIndexOf(index); + bit1 = ((bits8) BitArrayBitIsSet((BitArray) tblock, offset++)) << 1; + bit2 = (bits8) BitArrayBitIsSet((BitArray) tblock, offset); + + xstatus = (bit1 | bit2) ; + + /* ---------------- + * here we have the status of some transaction, so test + * if the status is recorded as "in progress". If so, then + * we save the transaction id in the place specified by the caller. + * ---------------- + */ + if (xstatus != XID_INPROGRESS) { + if (returnXidP != NULL) { + TransactionIdStore(baseXid, returnXidP); + TransactionIdAdd(returnXidP, index); + } + break; + } + } + + /* ---------------- + * if we get here and index is 0 it means we couldn't find + * a non-inprogress transaction on the block. For now we just + * return this info to the user. They can check if the return + * status is "in progress" to know this condition has arisen. + * ---------------- + */ + if (index == 0) { + if (returnXidP != NULL) + TransactionIdStore(baseXid, returnXidP); + } + + /* ---------------- + * return the status to the user + * ---------------- + */ + return xstatus; +} + +/* -------------------------------- + * TransBlockGetXidStatus + * + * This returns the status of the desired transaction + * -------------------------------- + */ + +XidStatus +TransBlockGetXidStatus(Block tblock, + TransactionId transactionId) +{ + Index index; + bits8 bit1; + bits8 bit2; + BitIndex offset; + + /* ---------------- + * sanity check + * ---------------- + */ + if (tblock == NULL) { + return XID_INVALID; + } + + /* ---------------- + * calculate the index into the transaction data where + * our transaction status is located + * + * XXX this will be replaced soon when we move to the + * new transaction id scheme -cim 3/23/90 + * + * The old system has now been replaced. -mer 5/24/92 + * ---------------- + */ + index = transactionId % TP_NumXidStatusPerBlock; + + /* ---------------- + * get the data at the specified index + * ---------------- + */ + offset = BitIndexOf(index); + bit1 = ((bits8) BitArrayBitIsSet((BitArray) tblock, offset++)) << 1; + bit2 = (bits8) BitArrayBitIsSet((BitArray) tblock, offset); + + /* ---------------- + * return the transaction status to the caller + * ---------------- + */ + return (XidStatus) + (bit1 | bit2); +} + +/* -------------------------------- + * TransBlockSetXidStatus + * + * This sets the status of the desired transaction + * -------------------------------- + */ +void +TransBlockSetXidStatus(Block tblock, + TransactionId transactionId, + XidStatus xstatus) +{ + Index index; + BitIndex offset; + + /* ---------------- + * sanity check + * ---------------- + */ + if (tblock == NULL) + return; + + /* ---------------- + * calculate the index into the transaction data where + * we sould store our transaction status. + * + * XXX this will be replaced soon when we move to the + * new transaction id scheme -cim 3/23/90 + * + * The new scheme is here -mer 5/24/92 + * ---------------- + */ + index = transactionId % TP_NumXidStatusPerBlock; + + offset = BitIndexOf(index); + + /* ---------------- + * store the transaction value at the specified offset + * ---------------- + */ + switch(xstatus) { + case XID_COMMIT: /* set 10 */ + BitArraySetBit((BitArray) tblock, offset); + BitArrayClearBit((BitArray) tblock, offset + 1); + break; + case XID_ABORT: /* set 01 */ + BitArrayClearBit((BitArray) tblock, offset); + BitArraySetBit((BitArray) tblock, offset + 1); + break; + case XID_INPROGRESS: /* set 00 */ + BitArrayClearBit((BitArray) tblock, offset); + BitArrayClearBit((BitArray) tblock, offset + 1); + break; + default: + elog(NOTICE, + "TransBlockSetXidStatus: invalid status: %d (ignored)", + xstatus); + break; + } +} + +/* -------------------------------- + * TransBlockGetCommitTime + * + * This returns the transaction commit time for the + * specified transaction id in the trans block. + * -------------------------------- + */ +AbsoluteTime +TransBlockGetCommitTime(Block tblock, + TransactionId transactionId) +{ + Index index; + AbsoluteTime *timeArray; + + /* ---------------- + * sanity check + * ---------------- + */ + if (tblock == NULL) + return INVALID_ABSTIME; + + /* ---------------- + * calculate the index into the transaction data where + * our transaction commit time is located + * + * XXX this will be replaced soon when we move to the + * new transaction id scheme -cim 3/23/90 + * + * The new scheme is here. -mer 5/24/92 + * ---------------- + */ + index = transactionId % TP_NumTimePerBlock; + + /* ---------------- + * return the commit time to the caller + * ---------------- + */ + timeArray = (AbsoluteTime *) tblock; + return (AbsoluteTime) + timeArray[ index ]; +} + +/* -------------------------------- + * TransBlockSetCommitTime + * + * This sets the commit time of the specified transaction + * -------------------------------- + */ +void +TransBlockSetCommitTime(Block tblock, + TransactionId transactionId, + AbsoluteTime commitTime) +{ + Index index; + AbsoluteTime *timeArray; + + /* ---------------- + * sanity check + * ---------------- + */ + if (tblock == NULL) + return; + + + /* ---------------- + * calculate the index into the transaction data where + * we sould store our transaction status. + * + * XXX this will be replaced soon when we move to the + * new transaction id scheme -cim 3/23/90 + * + * The new scheme is here. -mer 5/24/92 + * ---------------- + */ + index = transactionId % TP_NumTimePerBlock; + + /* ---------------- + * store the transaction commit time at the specified index + * ---------------- + */ + timeArray = (AbsoluteTime *) tblock; + timeArray[ index ] = commitTime; +} + +/* ---------------------------------------------------------------- + * transam i/o support routines + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * TransBlockNumberGetXidStatus + * -------------------------------- + */ +XidStatus +TransBlockNumberGetXidStatus(Relation relation, + BlockNumber blockNumber, + TransactionId xid, + bool *failP) +{ + Buffer buffer; /* buffer associated with block */ + Block block; /* block containing xstatus */ + XidStatus xstatus; /* recorded status of xid */ + bool localfail; /* bool used if failP = NULL */ + + /* ---------------- + * SOMEDAY place a read lock on the log relation + * That someday is today 5 Aug 1991 -mer + * ---------------- + */ + RelationSetLockForRead(relation); + + /* ---------------- + * get the page containing the transaction information + * ---------------- + */ + buffer = ReadBuffer(relation, blockNumber); + block = BufferGetBlock(buffer); + + /* ---------------- + * get the status from the block. note, for now we always + * return false in failP. + * ---------------- + */ + if (failP == NULL) + failP = &localfail; + (*failP) = false; + + xstatus = TransBlockGetXidStatus(block, xid); + + /* ---------------- + * release the buffer and return the status + * ---------------- + */ + ReleaseBuffer(buffer); + + /* ---------------- + * SOMEDAY release our lock on the log relation + * ---------------- + */ + RelationUnsetLockForRead(relation); + + return + xstatus; +} + +/* -------------------------------- + * TransBlockNumberSetXidStatus + * -------------------------------- + */ +void +TransBlockNumberSetXidStatus(Relation relation, + BlockNumber blockNumber, + TransactionId xid, + XidStatus xstatus, + bool *failP) +{ + Buffer buffer; /* buffer associated with block */ + Block block; /* block containing xstatus */ + bool localfail; /* bool used if failP = NULL */ + + /* ---------------- + * SOMEDAY gain exclusive access to the log relation + * + * That someday is today 5 Aug 1991 -mer + * ---------------- + */ + RelationSetLockForWrite(relation); + + /* ---------------- + * get the block containing the transaction status + * ---------------- + */ + buffer = ReadBuffer(relation, blockNumber); + block = BufferGetBlock(buffer); + + /* ---------------- + * attempt to update the status of the transaction on the block. + * if we are successful, write the block. otherwise release the buffer. + * note, for now we always return false in failP. + * ---------------- + */ + if (failP == NULL) + failP = &localfail; + (*failP) = false; + + TransBlockSetXidStatus(block, xid, xstatus); + + if ((*failP) == false) + WriteBuffer(buffer); + else + ReleaseBuffer(buffer); + + /* ---------------- + * SOMEDAY release our lock on the log relation + * ---------------- + */ + RelationUnsetLockForWrite(relation); +} + +/* -------------------------------- + * TransBlockNumberGetCommitTime + * -------------------------------- + */ +AbsoluteTime +TransBlockNumberGetCommitTime(Relation relation, + BlockNumber blockNumber, + TransactionId xid, + bool *failP) +{ + Buffer buffer; /* buffer associated with block */ + Block block; /* block containing commit time */ + bool localfail; /* bool used if failP = NULL */ + AbsoluteTime xtime; /* commit time */ + + /* ---------------- + * SOMEDAY place a read lock on the time relation + * + * That someday is today 5 Aug. 1991 -mer + * ---------------- + */ + RelationSetLockForRead(relation); + + /* ---------------- + * get the block containing the transaction information + * ---------------- + */ + buffer = ReadBuffer(relation, blockNumber); + block = BufferGetBlock(buffer); + + /* ---------------- + * get the commit time from the block + * note, for now we always return false in failP. + * ---------------- + */ + if (failP == NULL) + failP = &localfail; + (*failP) = false; + + xtime = TransBlockGetCommitTime(block, xid); + + /* ---------------- + * release the buffer and return the commit time + * ---------------- + */ + ReleaseBuffer(buffer); + + /* ---------------- + * SOMEDAY release our lock on the time relation + * ---------------- + */ + RelationUnsetLockForRead(relation); + + if ((*failP) == false) + return xtime; + else + return INVALID_ABSTIME; + +} + +/* -------------------------------- + * TransBlockNumberSetCommitTime + * -------------------------------- + */ +void +TransBlockNumberSetCommitTime(Relation relation, + BlockNumber blockNumber, + TransactionId xid, + AbsoluteTime xtime, + bool *failP) +{ + Buffer buffer; /* buffer associated with block */ + Block block; /* block containing commit time */ + bool localfail; /* bool used if failP = NULL */ + + /* ---------------- + * SOMEDAY gain exclusive access to the time relation + * + * That someday is today 5 Aug. 1991 -mer + * ---------------- + */ + RelationSetLockForWrite(relation); + + /* ---------------- + * get the block containing our commit time + * ---------------- + */ + buffer = ReadBuffer(relation, blockNumber); + block = BufferGetBlock(buffer); + + /* ---------------- + * attempt to update the commit time of the transaction on the block. + * if we are successful, write the block. otherwise release the buffer. + * note, for now we always return false in failP. + * ---------------- + */ + if (failP == NULL) + failP = &localfail; + (*failP) = false; + + TransBlockSetCommitTime(block, xid, xtime); + + if ((*failP) == false) + WriteBuffer(buffer); + else + ReleaseBuffer(buffer); + + /* ---------------- + * SOMEDAY release our lock on the time relation + * ---------------- + */ + RelationUnsetLockForWrite(relation); + +} + +/* -------------------------------- + * TransGetLastRecordedTransaction + * -------------------------------- + */ +void +TransGetLastRecordedTransaction(Relation relation, + TransactionId xid, /* return: transaction id */ + bool *failP) +{ + BlockNumber blockNumber; /* block number */ + Buffer buffer; /* buffer associated with block */ + Block block; /* block containing xid status */ + BlockNumber n; /* number of blocks in the relation */ + TransactionId baseXid; + + (*failP) = false; + + /* ---------------- + * SOMEDAY gain exclusive access to the log relation + * + * That someday is today 5 Aug. 1991 -mer + * It looks to me like we only need to set a read lock here, despite + * the above comment about exclusive access. The block is never + * actually written into, we only check status bits. + * ---------------- + */ + RelationSetLockForRead(relation); + + /* ---------------- + * we assume the last block of the log contains the last + * recorded transaction. If the relation is empty we return + * failure to the user. + * ---------------- + */ + n = RelationGetNumberOfBlocks(relation); + if (n == 0) { + (*failP) = true; + return; + } + + /* ---------------- + * get the block containing the transaction information + * ---------------- + */ + blockNumber = n-1; + buffer = ReadBuffer(relation, blockNumber); + block = BufferGetBlock(buffer); + + /* ---------------- + * get the last xid on the block + * ---------------- + */ + baseXid = blockNumber * TP_NumXidStatusPerBlock; + +/* XXX ???? xid won't get returned! - AY '94 */ + (void) TransBlockGetLastTransactionIdStatus(block, baseXid, &xid); + + ReleaseBuffer(buffer); + + /* ---------------- + * SOMEDAY release our lock on the log relation + * ---------------- + */ + RelationUnsetLockForRead(relation); +} diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c new file mode 100644 index 00000000000..a53cc7d35b1 --- /dev/null +++ b/src/backend/access/transam/varsup.c @@ -0,0 +1,606 @@ +/*------------------------------------------------------------------------- + * + * varsup.c-- + * postgres variable relation support routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/transam/varsup.c,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <math.h> +#include "postgres.h" + +#include "machine.h" /* in port/ directory (needed for BLCKSZ) */ +#include "storage/buf.h" +#include "storage/bufmgr.h" +#include "storage/ipc.h" /* for OIDGENLOCKID */ + +#include "utils/rel.h" +#include "utils/elog.h" + +#include "access/heapam.h" +#include "access/transam.h" /* where the declarations go */ +#include "access/xact.h" /* where the declarations go */ + +#include "catalog/catname.h" + +/* ---------- + * note: we reserve the first 16384 object ids for internal use. + * oid's less than this appear in the .bki files. the choice of + * 16384 is completely arbitrary. + * ---------- + */ +#define BootstrapObjectIdData 16384 + +/* --------------------- + * spin lock for oid generation + * --------------------- + */ +int OidGenLockId; + +/* ---------------------------------------------------------------- + * variable relation query/update routines + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * VariableRelationGetNextXid + * -------------------------------- + */ +void +VariableRelationGetNextXid(TransactionId *xidP) +{ + Buffer buf; + VariableRelationContents var; + + /* ---------------- + * We assume that a spinlock has been acquire to guarantee + * exclusive access to the variable relation. + * ---------------- + */ + + /* ---------------- + * do nothing before things are initialized + * ---------------- + */ + if (! RelationIsValid(VariableRelation)) + return; + + /* ---------------- + * read the variable page, get the the nextXid field and + * release the buffer + * ---------------- + */ + buf = ReadBuffer(VariableRelation, 0); + + if (! BufferIsValid(buf)) + { + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed"); + } + + var = (VariableRelationContents) BufferGetBlock(buf); + + TransactionIdStore(var->nextXidData, xidP); + ReleaseBuffer(buf); +} + +/* -------------------------------- + * VariableRelationGetLastXid + * -------------------------------- + */ +void +VariableRelationGetLastXid(TransactionId *xidP) +{ + Buffer buf; + VariableRelationContents var; + + /* ---------------- + * We assume that a spinlock has been acquire to guarantee + * exclusive access to the variable relation. + * ---------------- + */ + + /* ---------------- + * do nothing before things are initialized + * ---------------- + */ + if (! RelationIsValid(VariableRelation)) + return; + + /* ---------------- + * read the variable page, get the the lastXid field and + * release the buffer + * ---------------- + */ + buf = ReadBuffer(VariableRelation, 0); + + if (! BufferIsValid(buf)) + { + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed"); + } + + var = (VariableRelationContents) BufferGetBlock(buf); + + TransactionIdStore(var->lastXidData, xidP); + + ReleaseBuffer(buf); +} + +/* -------------------------------- + * VariableRelationPutNextXid + * -------------------------------- + */ +void +VariableRelationPutNextXid(TransactionId xid) +{ + Buffer buf; + VariableRelationContents var; + + /* ---------------- + * We assume that a spinlock has been acquire to guarantee + * exclusive access to the variable relation. + * ---------------- + */ + + /* ---------------- + * do nothing before things are initialized + * ---------------- + */ + if (! RelationIsValid(VariableRelation)) + return; + + /* ---------------- + * read the variable page, update the nextXid field and + * write the page back out to disk. + * ---------------- + */ + buf = ReadBuffer(VariableRelation, 0); + + if (! BufferIsValid(buf)) + { + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationPutNextXid: ReadBuffer failed"); + } + + var = (VariableRelationContents) BufferGetBlock(buf); + + TransactionIdStore(xid, &(var->nextXidData)); + + WriteBuffer(buf); +} + +/* -------------------------------- + * VariableRelationPutLastXid + * -------------------------------- + */ +void +VariableRelationPutLastXid(TransactionId xid) +{ + Buffer buf; + VariableRelationContents var; + + /* ---------------- + * We assume that a spinlock has been acquire to guarantee + * exclusive access to the variable relation. + * ---------------- + */ + + /* ---------------- + * do nothing before things are initialized + * ---------------- + */ + if (! RelationIsValid(VariableRelation)) + return; + + /* ---------------- + * read the variable page, update the lastXid field and + * force the page back out to disk. + * ---------------- + */ + buf = ReadBuffer(VariableRelation, 0); + + if (! BufferIsValid(buf)) + { + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationPutLastXid: ReadBuffer failed"); + } + + var = (VariableRelationContents) BufferGetBlock(buf); + + TransactionIdStore(xid, &(var->lastXidData)); + + WriteBuffer(buf); +} + +/* -------------------------------- + * VariableRelationGetNextOid + * -------------------------------- + */ +void +VariableRelationGetNextOid(Oid *oid_return) +{ + Buffer buf; + VariableRelationContents var; + + /* ---------------- + * We assume that a spinlock has been acquire to guarantee + * exclusive access to the variable relation. + * ---------------- + */ + + /* ---------------- + * if the variable relation is not initialized, then we + * assume we are running at bootstrap time and so we return + * an invalid object id -- during this time GetNextBootstrapObjectId + * should be called instead.. + * ---------------- + */ + if (! RelationIsValid(VariableRelation)) { + if (PointerIsValid(oid_return)) + (*oid_return) = InvalidOid; + return; + } + + /* ---------------- + * read the variable page, get the the nextOid field and + * release the buffer + * ---------------- + */ + buf = ReadBuffer(VariableRelation, 0); + + if (! BufferIsValid(buf)) + { + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationGetNextXid: ReadBuffer failed"); + } + + var = (VariableRelationContents) BufferGetBlock(buf); + + if (PointerIsValid(oid_return)) { + + /* ---------------- + * nothing up my sleeve... what's going on here is that this code + * is guaranteed never to be called until all files in data/base/ + * are created, and the template database exists. at that point, + * we want to append a pg_database tuple. the first time we do + * this, the oid stored in pg_variable will be bogus, so we use + * a bootstrap value defined at the top of this file. + * + * this comment no longer holds true. This code is called before + * all of the files in data/base are created and you can't rely + * on system oid's to be less than BootstrapObjectIdData. mer 9/18/91 + * ---------------- + */ + if (OidIsValid(var->nextOid)) + (*oid_return) = var->nextOid; + else + (*oid_return) = BootstrapObjectIdData; + } + + ReleaseBuffer(buf); +} + +/* -------------------------------- + * VariableRelationPutNextOid + * -------------------------------- + */ +void +VariableRelationPutNextOid(Oid *oidP) +{ + Buffer buf; + VariableRelationContents var; + + /* ---------------- + * We assume that a spinlock has been acquire to guarantee + * exclusive access to the variable relation. + * ---------------- + */ + + /* ---------------- + * do nothing before things are initialized + * ---------------- + */ + if (! RelationIsValid(VariableRelation)) + return; + + /* ---------------- + * sanity check + * ---------------- + */ + if (! PointerIsValid(oidP)) + { + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationPutNextOid: invalid oid pointer"); + } + + /* ---------------- + * read the variable page, update the nextXid field and + * write the page back out to disk. + * ---------------- + */ + buf = ReadBuffer(VariableRelation, 0); + + if (! BufferIsValid(buf)) + { + SpinRelease(OidGenLockId); + elog(WARN, "VariableRelationPutNextXid: ReadBuffer failed"); + } + + var = (VariableRelationContents) BufferGetBlock(buf); + + var->nextOid = (*oidP); + + WriteBuffer(buf); +} + +/* ---------------------------------------------------------------- + * transaction id generation support + * ---------------------------------------------------------------- + */ + +/* ---------------- + * GetNewTransactionId + * + * In the version 2 transaction system, transaction id's are + * restricted in several ways. + * + * First, all transaction id's are even numbers (4, 88, 121342, etc). + * This means the binary representation of the number will never + * have the least significent bit set. This bit is reserved to + * indicate that the transaction id does not in fact hold an XID, + * but rather a commit time. This makes it possible for the + * vaccuum daemon to disgard information from the log and time + * relations for committed tuples. This is important when archiving + * tuples to an optical disk because tuples with commit times + * stored in their xid fields will not need to consult the log + * and time relations. + * + * Second, since we may someday preform compression of the data + * in the log and time relations, we cause the numbering of the + * transaction ids to begin at 512. This means that some space + * on the page of the log and time relations corresponding to + * transaction id's 0 - 510 will never be used. This space is + * in fact used to store the version number of the postgres + * transaction log and will someday store compression information + * about the log. + * + * Lastly, rather then access the variable relation each time + * a backend requests a new transction id, we "prefetch" 32 + * transaction id's by incrementing the nextXid stored in the + * var relation by 64 (remember only even xid's are legal) and then + * returning these id's one at a time until they are exhausted. + * This means we reduce the number of accesses to the variable + * relation by 32 for each backend. + * + * Note: 32 has no special significance. We don't want the + * number to be too large because if when the backend + * terminates, we lose the xid's we cached. + * + * ---------------- + */ + +#define VAR_XID_PREFETCH 32 + +static int prefetched_xid_count = 0; +static TransactionId next_prefetched_xid; + +void +GetNewTransactionId(TransactionId *xid) +{ + TransactionId nextid; + + /* ---------------- + * during bootstrap initialization, we return the special + * bootstrap transaction id. + * ---------------- + */ + if (AMI_OVERRIDE) { + TransactionIdStore(AmiTransactionId, xid); + return; + } + + /* ---------------- + * if we run out of prefetched xids, then we get some + * more before handing them out to the caller. + * ---------------- + */ + + if (prefetched_xid_count == 0) { + /* ---------------- + * obtain exclusive access to the variable relation page + * + * get the "next" xid from the variable relation + * and save it in the prefetched id. + * ---------------- + */ + SpinAcquire(OidGenLockId); + VariableRelationGetNextXid(&nextid); + TransactionIdStore(nextid, &next_prefetched_xid); + + /* ---------------- + * now increment the variable relation's next xid + * and reset the prefetched_xid_count. We multiply + * the id by two because our xid's are always even. + * ---------------- + */ + prefetched_xid_count = VAR_XID_PREFETCH; + TransactionIdAdd(&nextid, prefetched_xid_count); + VariableRelationPutNextXid(nextid); + SpinRelease(OidGenLockId); + } + + /* ---------------- + * return the next prefetched xid in the pointer passed by + * the user and decrement the prefetch count. We add two + * to id we return the next time this is called because our + * transaction ids are always even. + * + * XXX Transaction Ids used to be even as the low order bit was + * used to determine commit status. This is no long true so + * we now use even and odd transaction ids. -mer 5/26/92 + * ---------------- + */ + TransactionIdStore(next_prefetched_xid, xid); + TransactionIdAdd(&next_prefetched_xid, 1); + prefetched_xid_count--; +} + +/* ---------------- + * UpdateLastCommittedXid + * ---------------- + */ + +void +UpdateLastCommittedXid(TransactionId xid) +{ + TransactionId lastid; + + + /* we assume that spinlock OidGenLockId has been acquired + * prior to entering this function + */ + + /* ---------------- + * get the "last committed" transaction id from + * the variable relation page. + * ---------------- + */ + VariableRelationGetLastXid(&lastid); + + /* ---------------- + * if the transaction id is greater than the last committed + * transaction then we update the last committed transaction + * in the variable relation. + * ---------------- + */ + if (TransactionIdIsLessThan(lastid, xid)) + VariableRelationPutLastXid(xid); + +} + +/* ---------------------------------------------------------------- + * object id generation support + * ---------------------------------------------------------------- + */ + +/* ---------------- + * GetNewObjectIdBlock + * + * This support function is used to allocate a block of object ids + * of the given size. applications wishing to do their own object + * id assignments should use this + * ---------------- + */ +void +GetNewObjectIdBlock(Oid *oid_return, /* place to return the new object id */ + int oid_block_size) /* number of oids desired */ +{ + Oid nextoid; + + /* ---------------- + * SOMEDAY obtain exclusive access to the variable relation page + * That someday is today -mer 6 Aug 1992 + * ---------------- + */ + SpinAcquire(OidGenLockId); + + /* ---------------- + * get the "next" oid from the variable relation + * and give it to the caller. + * ---------------- + */ + VariableRelationGetNextOid(&nextoid); + if (PointerIsValid(oid_return)) + (*oid_return) = nextoid; + + /* ---------------- + * now increment the variable relation's next oid + * field by the size of the oid block requested. + * ---------------- + */ + nextoid += oid_block_size; + VariableRelationPutNextOid(&nextoid); + + /* ---------------- + * SOMEDAY relinquish our lock on the variable relation page + * That someday is today -mer 6 Apr 1992 + * ---------------- + */ + SpinRelease(OidGenLockId); +} + +/* ---------------- + * GetNewObjectId + * + * This function allocates and parses out object ids. Like + * GetNewTransactionId(), it "prefetches" 32 object ids by + * incrementing the nextOid stored in the var relation by 32 and then + * returning these id's one at a time until they are exhausted. + * This means we reduce the number of accesses to the variable + * relation by 32 for each backend. + * + * Note: 32 has no special significance. We don't want the + * number to be too large because if when the backend + * terminates, we lose the oids we cached. + * + * ---------------- + */ + +#define VAR_OID_PREFETCH 32 + +static int prefetched_oid_count = 0; +static Oid next_prefetched_oid; + +void +GetNewObjectId(Oid *oid_return) /* place to return the new object id */ +{ + /* ---------------- + * if we run out of prefetched oids, then we get some + * more before handing them out to the caller. + * ---------------- + */ + + if (prefetched_oid_count == 0) { + int oid_block_size = VAR_OID_PREFETCH; + + /* ---------------- + * during bootstrap time, we want to allocate oids + * one at a time. Otherwise there might be some + * bootstrap oid's left in the block we prefetch which + * would be passed out after the variable relation was + * initialized. This would be bad. + * ---------------- + */ + if (! RelationIsValid(VariableRelation)) + VariableRelation = heap_openr(VariableRelationName); + + /* ---------------- + * get a new block of prefetched object ids. + * ---------------- + */ + GetNewObjectIdBlock(&next_prefetched_oid, oid_block_size); + + /* ---------------- + * now reset the prefetched_oid_count. + * ---------------- + */ + prefetched_oid_count = oid_block_size; + } + + /* ---------------- + * return the next prefetched oid in the pointer passed by + * the user and decrement the prefetch count. + * ---------------- + */ + if (PointerIsValid(oid_return)) + (*oid_return) = next_prefetched_oid; + + next_prefetched_oid++; + prefetched_oid_count--; +} diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c new file mode 100644 index 00000000000..1798d09d054 --- /dev/null +++ b/src/backend/access/transam/xact.c @@ -0,0 +1,1314 @@ +/*------------------------------------------------------------------------- + * + * xact.c-- + * top level transaction system support routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.1.1.1 1996/07/09 06:21:13 scrappy Exp $ + * + * NOTES + * Transaction aborts can now occur two ways: + * + * 1) system dies from some internal cause (Assert, etc..) + * 2) user types abort + * + * These two cases used to be treated identically, but now + * we need to distinguish them. Why? consider the following + * two situatuons: + * + * case 1 case 2 + * ------ ------ + * 1) user types BEGIN 1) user types BEGIN + * 2) user does something 2) user does something + * 3) user does not like what 3) system aborts for some reason + * she shes and types ABORT + * + * In case 1, we want to abort the transaction and return to the + * default state. In case 2, there may be more commands coming + * our way which are part of the same transaction block and we have + * to ignore these commands until we see an END transaction. + * + * Internal aborts are now handled by AbortTransactionBlock(), just as + * they always have been, and user aborts are now handled by + * UserAbortTransactionBlock(). Both of them rely on AbortTransaction() + * to do all the real work. The only difference is what state we + * enter after AbortTransaction() does it's work: + * + * * AbortTransactionBlock() leaves us in TBLOCK_ABORT and + * * UserAbortTransactionBlock() leaves us in TBLOCK_ENDABORT + * + * NOTES + * This file is an attempt at a redesign of the upper layer + * of the V1 transaction system which was too poorly thought + * out to describe. This new system hopes to be both simpler + * in design, simpler to extend and needs to contain added + * functionality to solve problems beyond the scope of the V1 + * system. (In particuler, communication of transaction + * information between parallel backends has to be supported) + * + * The essential aspects of the transaction system are: + * + * o transaction id generation + * o transaction log updating + * o memory cleanup + * o cache invalidation + * o lock cleanup + * + * Hence, the functional division of the transaction code is + * based on what of the above things need to be done during + * a start/commit/abort transaction. For instance, the + * routine AtCommit_Memory() takes care of all the memory + * cleanup stuff done at commit time. + * + * The code is layered as follows: + * + * StartTransaction + * CommitTransaction + * AbortTransaction + * UserAbortTransaction + * + * are provided to do the lower level work like recording + * the transaction status in the log and doing memory cleanup. + * above these routines are another set of functions: + * + * StartTransactionCommand + * CommitTransactionCommand + * AbortCurrentTransaction + * + * These are the routines used in the postgres main processing + * loop. They are sensitive to the current transaction block state + * and make calls to the lower level routines appropriately. + * + * Support for transaction blocks is provided via the functions: + * + * StartTransactionBlock + * CommitTransactionBlock + * AbortTransactionBlock + * + * These are invoked only in responce to a user "BEGIN", "END", + * or "ABORT" command. The tricky part about these functions + * is that they are called within the postgres main loop, in between + * the StartTransactionCommand() and CommitTransactionCommand(). + * + * For example, consider the following sequence of user commands: + * + * 1) begin + * 2) retrieve (foo.all) + * 3) append foo (bar = baz) + * 4) end + * + * in the main processing loop, this results in the following + * transaction sequence: + * + * / StartTransactionCommand(); + * 1) / ProcessUtility(); << begin + * \ StartTransactionBlock(); + * \ CommitTransactionCommand(); + * + * / StartTransactionCommand(); + * 2) < ProcessQuery(); << retrieve (foo.all) + * \ CommitTransactionCommand(); + * + * / StartTransactionCommand(); + * 3) < ProcessQuery(); << append foo (bar = baz) + * \ CommitTransactionCommand(); + * + * / StartTransactionCommand(); + * 4) / ProcessUtility(); << end + * \ CommitTransactionBlock(); + * \ CommitTransactionCommand(); + * + * The point of this example is to demonstrate the need for + * StartTransactionCommand() and CommitTransactionCommand() to + * be state smart -- they should do nothing in between the calls + * to StartTransactionBlock() and EndTransactionBlock() and + * outside these calls they need to do normal start/commit + * processing. + * + * Furthermore, suppose the "retrieve (foo.all)" caused an abort + * condition. We would then want to abort the transaction and + * ignore all subsequent commands up to the "end". + * -cim 3/23/90 + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "access/xact.h" +#include "commands/async.h" +#include "storage/bufmgr.h" +#include "storage/block.h" +#include "storage/proc.h" +#include "utils/inval.h" +#include "utils/relcache.h" +#include "access/transam.h" +#include "catalog/heap.h" + +/* ---------------- + * 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 = { + 0, /* transaction id */ + FirstCommandId, /* command id */ + 0x0, /* start time */ + TRANS_DEFAULT, /* transaction state */ + TBLOCK_DEFAULT /* transaction block state */ + }; + +TransactionState CurrentTransactionState = + &CurrentTransactionStateData; + +/* ---------------- + * info returned when the system is desabled + * + * 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; + +CommandId DisabledCommandId = (CommandId) -1; + +AbsoluteTime DisabledStartTime = (AbsoluteTime) 1073741823; + +/* ---------------- + * overflow flag + * ---------------- + */ +bool CommandIdCounterOverflowFlag; + +/* ---------------- + * catalog creation transaction bootstrapping flag. + * This should be eliminated and added to the transaction + * state stuff. -cim 3/19/90 + * ---------------- + */ +bool AMI_OVERRIDE = false; + +/* ---------------------------------------------------------------- + * transaction state accessors + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * TranactionFlushEnabled() + * SetTranactionFlushEnabled() + * + * These are used to test and set the "TransactionFlushState" + * varable. If this variable is true (the default), then + * the system will flush all dirty buffers to disk at the end + * of each transaction. If false then we are assuming the + * buffer pool resides in stable main memory, in which case we + * only do writes as necessary. + * -------------------------------- + */ +static int TransactionFlushState = 1; + +int +TransactionFlushEnabled() +{ + return TransactionFlushState; +} + +void +SetTransactionFlushEnabled(bool state) +{ + TransactionFlushState = (state == true); +} + +/* -------------------------------- + * IsTransactionState + * + * This returns true if we are currently running a query + * within an executing transaction. + * -------------------------------- + */ +bool +IsTransactionState() +{ + TransactionState s = CurrentTransactionState; + + switch (s->state) { + case TRANS_DEFAULT: return false; + case TRANS_START: return true; + case TRANS_INPROGRESS: return true; + case TRANS_COMMIT: return true; + case TRANS_ABORT: return true; + case TRANS_DISABLED: return false; + } + /* + * Shouldn't get here, but lint is not happy with this... + */ + return(false); +} + +/* -------------------------------- + * IsAbortedTransactionBlockState + * + * This returns true if we are currently running a query + * within an aborted transaction block. + * -------------------------------- + */ +bool +IsAbortedTransactionBlockState() +{ + TransactionState s = CurrentTransactionState; + + if (s->blockState == TBLOCK_ABORT) + return true; + + return false; +} + +/* -------------------------------- + * OverrideTransactionSystem + * + * This is used to temporarily disable the transaction + * processing system in order to do initialization of + * the transaction system data structures and relations + * themselves. + * -------------------------------- + */ +int SavedTransactionState; + +void +OverrideTransactionSystem(bool flag) +{ + TransactionState s = CurrentTransactionState; + + if (flag == true) { + if (s->state == TRANS_DISABLED) + return; + + SavedTransactionState = s->state; + s->state = TRANS_DISABLED; + } else { + if (s->state != TRANS_DISABLED) + return; + + s->state = SavedTransactionState; + } +} + +/* -------------------------------- + * GetCurrentTransactionId + * + * This returns the id of the current transaction, or + * the id of the "disabled" transaction. + * -------------------------------- + */ +TransactionId +GetCurrentTransactionId() +{ + TransactionState s = CurrentTransactionState; + + /* ---------------- + * if the transaction system is disabled, we return + * the special "disabled" transaction id. + * ---------------- + */ + if (s->state == TRANS_DISABLED) + return (TransactionId) DisabledTransactionId; + + /* ---------------- + * otherwise return the current transaction id. + * ---------------- + */ + return (TransactionId) s->transactionIdData; +} + + +/* -------------------------------- + * GetCurrentCommandId + * -------------------------------- + */ +CommandId +GetCurrentCommandId() +{ + TransactionState s = CurrentTransactionState; + + /* ---------------- + * if the transaction system is disabled, we return + * the special "disabled" command id. + * ---------------- + */ + if (s->state == TRANS_DISABLED) + return (CommandId) DisabledCommandId; + + return s->commandId; +} + + +/* -------------------------------- + * GetCurrentTransactionStartTime + * -------------------------------- + */ +AbsoluteTime +GetCurrentTransactionStartTime() +{ + TransactionState s = CurrentTransactionState; + + /* ---------------- + * if the transaction system is disabled, we return + * the special "disabled" starting time. + * ---------------- + */ + if (s->state == TRANS_DISABLED) + return (AbsoluteTime) DisabledStartTime; + + return s->startTime; +} + + +/* -------------------------------- + * TransactionIdIsCurrentTransactionId + * -------------------------------- + */ +bool +TransactionIdIsCurrentTransactionId(TransactionId xid) +{ + TransactionState s = CurrentTransactionState; + + if (AMI_OVERRIDE) + return false; + + return (bool) + TransactionIdEquals(xid, s->transactionIdData); +} + + +/* -------------------------------- + * CommandIdIsCurrentCommandId + * -------------------------------- + */ +bool +CommandIdIsCurrentCommandId(CommandId cid) +{ + TransactionState s = CurrentTransactionState; + + if (AMI_OVERRIDE) + return false; + + return + (cid == s->commandId) ? true : false; +} + + +/* -------------------------------- + * ClearCommandIdCounterOverflowFlag + * -------------------------------- + */ +void +ClearCommandIdCounterOverflowFlag() +{ + CommandIdCounterOverflowFlag = false; +} + + +/* -------------------------------- + * CommandCounterIncrement + * -------------------------------- + */ +void +CommandCounterIncrement() +{ + CurrentTransactionStateData.commandId += 1; + if (CurrentTransactionStateData.commandId == FirstCommandId) { + CommandIdCounterOverflowFlag = true; + elog(WARN, "You may only have 65535 commands per transaction"); + } + + /* make cache changes visible to me */ + AtCommit_Cache(); + AtStart_Cache(); +} + +/* ---------------------------------------------------------------- + * initialization stuff + * ---------------------------------------------------------------- + */ +void +InitializeTransactionSystem() +{ + InitializeTransactionLog(); +} + +/* ---------------------------------------------------------------- + * StartTransaction stuff + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * AtStart_Cache + * -------------------------------- + */ +void +AtStart_Cache() +{ + DiscardInvalid(); +} + +/* -------------------------------- + * AtStart_Locks + * -------------------------------- + */ +void +AtStart_Locks() +{ + /* + * at present, it is unknown to me what belongs here -cim 3/18/90 + * + * There isn't anything to do at the start of a xact for locks. + * -mer 5/24/92 + */ +} + +/* -------------------------------- + * AtStart_Memory + * -------------------------------- + */ +void +AtStart_Memory() +{ + Portal portal; + MemoryContext portalContext; + + /* ---------------- + * get the blank portal and its memory context + * ---------------- + */ + portal = GetPortalByName(NULL); + portalContext = (MemoryContext) PortalGetHeapMemory(portal); + + /* ---------------- + * tell system to allocate in the blank portal context + * ---------------- + */ + (void) MemoryContextSwitchTo(portalContext); + StartPortalAllocMode(DefaultAllocMode, 0); +} + + +/* ---------------------------------------------------------------- + * CommitTransaction stuff + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * RecordTransactionCommit + * + * Note: the two calls to BufferManagerFlush() exist to ensure + * that data pages are written before log pages. These + * explicit calls should be replaced by a more efficient + * ordered page write scheme in the buffer manager + * -cim 3/18/90 + * -------------------------------- + */ +void +RecordTransactionCommit() +{ + TransactionId xid; + int leak; + + /* ---------------- + * get the current transaction id + * ---------------- + */ + xid = GetCurrentTransactionId(); + + /* ---------------- + * flush the buffer manager pages. Note: if we have stable + * main memory, dirty shared buffers are not flushed + * plai 8/7/90 + * ---------------- + */ + leak = BufferPoolCheckLeak(); + FlushBufferPool(!TransactionFlushEnabled()); + if (leak) ResetBufferPool(); + + /* ---------------- + * have the transaction access methods record the status + * of this transaction id in the pg_log / pg_time relations. + * ---------------- + */ + TransactionIdCommit(xid); + + /* ---------------- + * Now write the log/time info to the disk too. + * ---------------- + */ + leak = BufferPoolCheckLeak(); + FlushBufferPool(!TransactionFlushEnabled()); + if (leak) ResetBufferPool(); +} + + +/* -------------------------------- + * AtCommit_Cache + * -------------------------------- + */ +void +AtCommit_Cache() +{ + /* ---------------- + * Make catalog changes visible to me for the next command. + * Other backends will not process my invalidation messages until + * after I commit and free my locks--though they will do + * unnecessary work if I abort. + * ---------------- + */ + RegisterInvalid(true); +} + +/* -------------------------------- + * AtCommit_Locks + * -------------------------------- + */ +void +AtCommit_Locks() +{ + /* ---------------- + * XXX What if ProcReleaseLocks fails? (race condition?) + * + * Then you're up a creek! -mer 5/24/92 + * ---------------- + */ + ProcReleaseLocks(); +} + +/* -------------------------------- + * AtCommit_Memory + * -------------------------------- + */ +void +AtCommit_Memory() +{ + /* ---------------- + * now that we're "out" of a transaction, have the + * system allocate things in the top memory context instead + * of the blank portal memory context. + * ---------------- + */ + EndPortalAllocMode(); + (void) MemoryContextSwitchTo(TopMemoryContext); +} + +/* ---------------------------------------------------------------- + * AbortTransaction stuff + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * RecordTransactionAbort + * -------------------------------- + */ +void +RecordTransactionAbort() +{ + TransactionId xid; + + /* ---------------- + * get the current transaction id + * ---------------- + */ + xid = GetCurrentTransactionId(); + + /* ---------------- + * have the transaction access methods record the status + * of this transaction id in the pg_log / pg_time relations. + * ---------------- + */ + TransactionIdAbort(xid); + + /* ---------------- + * flush the buffer manager pages. Note: if we have stable + * main memory, dirty shared buffers are not flushed + * plai 8/7/90 + * ---------------- + */ + ResetBufferPool(); +} + +/* -------------------------------- + * AtAbort_Cache + * -------------------------------- + */ +void +AtAbort_Cache() +{ + RegisterInvalid(false); +} + +/* -------------------------------- + * AtAbort_Locks + * -------------------------------- + */ +void +AtAbort_Locks() +{ + /* ---------------- + * XXX What if ProcReleaseLocks() fails? (race condition?) + * + * Then you're up a creek without a paddle! -mer + * ---------------- + */ + ProcReleaseLocks(); +} + + +/* -------------------------------- + * AtAbort_Memory + * -------------------------------- + */ +void +AtAbort_Memory() +{ + /* ---------------- + * after doing an abort transaction, make certain the + * system uses the top memory context rather then the + * portal memory context (until the next transaction). + * ---------------- + */ + (void) MemoryContextSwitchTo(TopMemoryContext); +} + +/* ---------------------------------------------------------------- + * interface routines + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * StartTransaction + * + * -------------------------------- + */ +void +StartTransaction() +{ + TransactionState s = CurrentTransactionState; + + /* ---------------- + * Check the current transaction state. If the transaction system + * is switched off, or if we're already in a transaction, do nothing. + * We're already in a transaction when the monitor sends a null + * command to the backend to flush the comm channel. This is a + * hacky fix to a communications problem, and we keep having to + * deal with it here. We should fix the comm channel code. mao 080891 + * ---------------- + */ + if (s->state == TRANS_DISABLED || s->state == TRANS_INPROGRESS) + return; + + /* ---------------- + * set the current transaction state information + * appropriately during start processing + * ---------------- + */ + s->state = TRANS_START; + + /* ---------------- + * generate a new transaction id + * ---------------- + */ + GetNewTransactionId(&(s->transactionIdData)); + + /* ---------------- + * initialize current transaction state fields + * ---------------- + */ + s->commandId = FirstCommandId; + s->startTime = GetCurrentAbsoluteTime(); + + /* ---------------- + * initialize the various transaction subsystems + * ---------------- + */ + AtStart_Cache(); + AtStart_Locks(); + AtStart_Memory(); + + /* -------------- + initialize temporary relations list + the tempRelList is a list of temporary relations that + are created in the course of the transactions + they need to be destroyed properly at the end of the transactions + */ + InitTempRelList(); + + /* ---------------- + * done with start processing, set current transaction + * state to "in progress" + * ---------------- + */ + s->state = TRANS_INPROGRESS; +} + +/* --------------- + * Tell me if we are currently in progress + * --------------- + */ +bool +CurrentXactInProgress() +{ + return (CurrentTransactionState->state == TRANS_INPROGRESS); +} + +/* -------------------------------- + * CommitTransaction + * + * -------------------------------- + */ +void +CommitTransaction() +{ + TransactionState s = CurrentTransactionState; + + /* ---------------- + * check the current transaction state + * ---------------- + */ + if (s->state == TRANS_DISABLED) + return; + + if (s->state != TRANS_INPROGRESS) + elog(NOTICE, "CommitTransaction and not in in-progress state "); + + /* ---------------- + * set the current transaction state information + * appropriately during the abort processing + * ---------------- + */ + s->state = TRANS_COMMIT; + + /* ---------------- + * do commit processing + * ---------------- + */ + DestroyTempRels(); + AtEOXact_portals(); + RecordTransactionCommit(); + RelationPurgeLocalRelation(true); + AtCommit_Cache(); + AtCommit_Locks(); + AtCommit_Memory(); + + /* ---------------- + * done with commit processing, set current transaction + * state back to default + * ---------------- + */ + s->state = TRANS_DEFAULT; + { /* want this after commit */ + if (IsNormalProcessingMode()) + Async_NotifyAtCommit(); + } +} + +/* -------------------------------- + * AbortTransaction + * + * -------------------------------- + */ +void +AbortTransaction() +{ + TransactionState s = CurrentTransactionState; + + /* ---------------- + * check the current transaction state + * ---------------- + */ + if (s->state == TRANS_DISABLED) + return; + + if (s->state != TRANS_INPROGRESS) + elog(NOTICE, "AbortTransaction and not in in-progress state "); + + /* ---------------- + * set the current transaction state information + * appropriately during the abort processing + * ---------------- + */ + s->state = TRANS_ABORT; + + /* ---------------- + * do abort processing + * ---------------- + */ + AtEOXact_portals(); + RecordTransactionAbort(); + RelationPurgeLocalRelation(false); + DestroyTempRels(); + AtAbort_Cache(); + AtAbort_Locks(); + AtAbort_Memory(); + + /* ---------------- + * done with abort processing, set current transaction + * state back to default + * ---------------- + */ + s->state = TRANS_DEFAULT; + { + /* We need to do this in case another process notified us while + we are in the middle of an aborted transaction. We need to + notify our frontend after we finish the current transaction. + -- jw, 1/3/94 + */ + if (IsNormalProcessingMode()) + Async_NotifyAtAbort(); + } +} + +/* -------------------------------- + * StartTransactionCommand + * -------------------------------- + */ +void +StartTransactionCommand() +{ + TransactionState s = CurrentTransactionState; + + switch(s->blockState) { + /* ---------------- + * if we aren't in a transaction block, we + * just do our usual start transaction. + * ---------------- + */ + case TBLOCK_DEFAULT: + StartTransaction(); + break; + + /* ---------------- + * We should never experience this -- if we do it + * means the BEGIN state was not changed in the previous + * CommitTransactionCommand(). If we get it, we print + * a warning and change to the in-progress state. + * ---------------- + */ + case TBLOCK_BEGIN: + elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_BEGIN"); + s->blockState = TBLOCK_INPROGRESS; + break; + + /* ---------------- + * This is the case when are somewhere in a transaction + * block and about to start a new command. For now we + * do nothing but someday we may do command-local resource + * initialization. + * ---------------- + */ + case TBLOCK_INPROGRESS: + break; + + /* ---------------- + * As with BEGIN, we should never experience this -- + * if we do it means the END state was not changed in the + * previous CommitTransactionCommand(). If we get it, we + * print a warning, commit the transaction, start a new + * transaction and change to the default state. + * ---------------- + */ + case TBLOCK_END: + elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_END"); + s->blockState = TBLOCK_DEFAULT; + CommitTransaction(); + StartTransaction(); + break; + + /* ---------------- + * Here we are in the middle of a transaction block but + * one of the commands caused an abort so we do nothing + * but remain in the abort state. Eventually we will get + * to the "END TRANSACTION" which will set things straight. + * ---------------- + */ + case TBLOCK_ABORT: + break; + + /* ---------------- + * This means we somehow aborted and the last call to + * CommitTransactionCommand() didn't clear the state so + * we remain in the ENDABORT state and mabey next time + * we get to CommitTransactionCommand() the state will + * get reset to default. + * ---------------- + */ + case TBLOCK_ENDABORT: + elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT"); + break; + } +} +/* -------------------------------- + * CommitTransactionCommand + * -------------------------------- + */ +void +CommitTransactionCommand() +{ + TransactionState s = CurrentTransactionState; + + switch(s->blockState) { + /* ---------------- + * if we aren't in a transaction block, we + * just do our usual transaction commit + * ---------------- + */ + case TBLOCK_DEFAULT: + CommitTransaction(); + break; + + /* ---------------- + * This is the case right after we get a "BEGIN TRANSACTION" + * command, but the user hasn't done anything else yet, so + * we change to the "transaction block in progress" state + * and return. + * ---------------- + */ + case TBLOCK_BEGIN: + s->blockState = TBLOCK_INPROGRESS; + break; + + /* ---------------- + * This is the case when we have finished executing a command + * someplace within a transaction block. We increment the + * command counter and return. Someday we may free resources + * local to the command. + * ---------------- + */ + case TBLOCK_INPROGRESS: + CommandCounterIncrement(); + break; + + /* ---------------- + * This is the case when we just got the "END TRANSACTION" + * statement, so we go back to the default state and + * commit the transaction. + * ---------------- + */ + case TBLOCK_END: + s->blockState = TBLOCK_DEFAULT; + CommitTransaction(); + break; + + /* ---------------- + * Here we are in the middle of a transaction block but + * one of the commands caused an abort so we do nothing + * but remain in the abort state. Eventually we will get + * to the "END TRANSACTION" which will set things straight. + * ---------------- + */ + case TBLOCK_ABORT: + break; + + /* ---------------- + * Here we were in an aborted transaction block which + * just processed the "END TRANSACTION" command from the + * user, so now we return the to default state. + * ---------------- + */ + case TBLOCK_ENDABORT: + s->blockState = TBLOCK_DEFAULT; + break; + } +} + +/* -------------------------------- + * AbortCurrentTransaction + * -------------------------------- + */ +void +AbortCurrentTransaction() +{ + TransactionState s = CurrentTransactionState; + + switch(s->blockState) { + /* ---------------- + * if we aren't in a transaction block, we + * just do our usual abort transaction. + * ---------------- + */ + case TBLOCK_DEFAULT: + AbortTransaction(); + break; + + /* ---------------- + * If we are in the TBLOCK_BEGIN it means something + * screwed up right after reading "BEGIN TRANSACTION" + * so we enter the abort state. Eventually an "END + * TRANSACTION" will fix things. + * ---------------- + */ + case TBLOCK_BEGIN: + s->blockState = TBLOCK_ABORT; + AbortTransaction(); + break; + + /* ---------------- + * This is the case when are somewhere in a transaction + * block which aborted so we abort the transaction and + * set the ABORT state. Eventually an "END TRANSACTION" + * will fix things and restore us to a normal state. + * ---------------- + */ + case TBLOCK_INPROGRESS: + s->blockState = TBLOCK_ABORT; + AbortTransaction(); + break; + + /* ---------------- + * Here, the system was fouled up just after the + * user wanted to end the transaction block so we + * abort the transaction and put us back into the + * default state. + * ---------------- + */ + case TBLOCK_END: + s->blockState = TBLOCK_DEFAULT; + AbortTransaction(); + break; + + /* ---------------- + * Here, we are already in an aborted transaction + * state and are waiting for an "END TRANSACTION" to + * come along and lo and behold, we abort again! + * So we just remain in the abort state. + * ---------------- + */ + case TBLOCK_ABORT: + break; + + /* ---------------- + * Here we were in an aborted transaction block which + * just processed the "END TRANSACTION" command but somehow + * aborted again.. since we must have done the abort + * processing, we return to the default state. + * ---------------- + */ + case TBLOCK_ENDABORT: + s->blockState = TBLOCK_DEFAULT; + break; + } +} + +/* ---------------------------------------------------------------- + * transaction block support + * ---------------------------------------------------------------- + */ +/* -------------------------------- + * BeginTransactionBlock + * -------------------------------- + */ +void +BeginTransactionBlock() +{ + TransactionState s = CurrentTransactionState; + + /* ---------------- + * check the current transaction state + * ---------------- + */ + if (s->state == TRANS_DISABLED) + return; + + if (s->blockState != TBLOCK_DEFAULT) + elog(NOTICE, "BeginTransactionBlock and not in default state "); + + /* ---------------- + * set the current transaction block state information + * appropriately during begin processing + * ---------------- + */ + s->blockState = TBLOCK_BEGIN; + + /* ---------------- + * do begin processing + * ---------------- + */ + + /* ---------------- + * done with begin processing, set block state to inprogress + * ---------------- + */ + s->blockState = TBLOCK_INPROGRESS; +} + +/* -------------------------------- + * EndTransactionBlock + * -------------------------------- + */ +void +EndTransactionBlock() +{ + TransactionState s = CurrentTransactionState; + + /* ---------------- + * check the current transaction state + * ---------------- + */ + if (s->state == TRANS_DISABLED) + return; + + if (s->blockState == TBLOCK_INPROGRESS) { + /* ---------------- + * here we are in a transaction block which should commit + * when we get to the upcoming CommitTransactionCommand() + * so we set the state to "END". CommitTransactionCommand() + * will recognize this and commit the transaction and return + * us to the default state + * ---------------- + */ + s->blockState = TBLOCK_END; + return; + } + + if (s->blockState == TBLOCK_ABORT) { + /* ---------------- + * here, we are in a transaction block which aborted + * and since the AbortTransaction() was already done, + * we do whatever is needed and change to the special + * "END ABORT" state. The upcoming CommitTransactionCommand() + * will recognise this and then put us back in the default + * state. + * ---------------- + */ + s->blockState = TBLOCK_ENDABORT; + return; + } + + /* ---------------- + * We should not get here, but if we do, we go to the ENDABORT + * state after printing a warning. The upcoming call to + * CommitTransactionCommand() will then put us back into the + * default state. + * ---------------- + */ + elog(NOTICE, "EndTransactionBlock and not inprogress/abort state "); + s->blockState = TBLOCK_ENDABORT; +} + +/* -------------------------------- + * AbortTransactionBlock + * -------------------------------- + */ +void +AbortTransactionBlock() +{ + TransactionState s = CurrentTransactionState; + + /* ---------------- + * check the current transaction state + * ---------------- + */ + if (s->state == TRANS_DISABLED) + return; + + if (s->blockState == TBLOCK_INPROGRESS) { + /* ---------------- + * here we were inside a transaction block something + * screwed up inside the system so we enter the abort state, + * do the abort processing and then return. + * We remain in the abort state until we see the upcoming + * END TRANSACTION command. + * ---------------- + */ + s->blockState = TBLOCK_ABORT; + + /* ---------------- + * do abort processing and return + * ---------------- + */ + AbortTransaction(); + return; + } + + /* ---------------- + * this case should not be possible, because it would mean + * the user entered an "abort" from outside a transaction block. + * So we print an error message, abort the transaction and + * enter the "ENDABORT" state so we will end up in the default + * state after the upcoming CommitTransactionCommand(). + * ---------------- + */ + elog(NOTICE, "AbortTransactionBlock and not inprogress state"); + AbortTransaction(); + s->blockState = TBLOCK_ENDABORT; +} + +/* -------------------------------- + * UserAbortTransactionBlock + * -------------------------------- + */ +void +UserAbortTransactionBlock() +{ + TransactionState s = CurrentTransactionState; + + /* ---------------- + * check the current transaction state + * ---------------- + */ + if (s->state == TRANS_DISABLED) + return; + + if (s->blockState == TBLOCK_INPROGRESS) { + /* ---------------- + * here we were inside a transaction block and we + * got an abort command from the user, so we move to + * the abort state, do the abort processing and + * then change to the ENDABORT state so we will end up + * in the default state after the upcoming + * CommitTransactionCommand(). + * ---------------- + */ + s->blockState = TBLOCK_ABORT; + + /* ---------------- + * do abort processing + * ---------------- + */ + AbortTransaction(); + + /* ---------------- + * change to the end abort state and return + * ---------------- + */ + s->blockState = TBLOCK_ENDABORT; + return; + } + + /* ---------------- + * this case should not be possible, because it would mean + * the user entered an "abort" from outside a transaction block. + * So we print an error message, abort the transaction and + * enter the "ENDABORT" state so we will end up in the default + * state after the upcoming CommitTransactionCommand(). + * ---------------- + */ + elog(NOTICE, "UserAbortTransactionBlock and not inprogress state"); + AbortTransaction(); + s->blockState = TBLOCK_ENDABORT; +} + +bool +IsTransactionBlock() +{ + TransactionState s = CurrentTransactionState; + + if (s->blockState == TBLOCK_INPROGRESS + || s->blockState == TBLOCK_ENDABORT) { + return (true); + } + + return (false); +} diff --git a/src/backend/access/transam/xid.c b/src/backend/access/transam/xid.c new file mode 100644 index 00000000000..faeeb623d58 --- /dev/null +++ b/src/backend/access/transam/xid.c @@ -0,0 +1,156 @@ +/*------------------------------------------------------------------------- + * + * xid.c-- + * POSTGRES transaction identifier code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/xid.c,v 1.1.1.1 1996/07/09 06:21:14 scrappy Exp $ + * + * OLD COMMENTS + * XXX WARNING + * Much of this file will change when we change our representation + * of transaction ids -cim 3/23/90 + * + * It is time to make the switch from 5 byte to 4 byte transaction ids + * This file was totally reworked. -mer 5/22/92 + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include "postgres.h" +#include "utils/palloc.h" +#include "utils/elog.h" +#include "utils/memutils.h" +#include "utils/nabstime.h" + +extern TransactionId NullTransactionId; +extern TransactionId DisabledTransactionId; +extern TransactionId AmiTransactionId; +extern TransactionId FirstTransactionId; + +/* ---------------------------------------------------------------- + * TransactionIdIsValid + * + * Macro-ize me. + * ---------------------------------------------------------------- + */ +bool +TransactionIdIsValid(TransactionId transactionId) +{ + return ((bool) (transactionId != NullTransactionId) ); +} + +/* XXX char16 name for catalogs */ +TransactionId +xidin(char *representation) +{ + return (atol(representation)); +} + +/* XXX char16 name for catalogs */ +char* +xidout(TransactionId transactionId) +{ +/* return(TransactionIdFormString(transactionId)); */ + char *representation; + + /* maximum 32 bit unsigned integer representation takes 10 chars */ + representation = palloc(11); + + (void)sprintf(representation, "%u", transactionId); + + return (representation); + +} + +/* ---------------------------------------------------------------- + * StoreInvalidTransactionId + * + * Maybe do away with Pointer types in these routines. + * Macro-ize this one. + * ---------------------------------------------------------------- + */ +void +StoreInvalidTransactionId(TransactionId *destination) +{ + *destination = NullTransactionId; +} + +/* ---------------------------------------------------------------- + * TransactionIdStore + * + * Macro-ize this one. + * ---------------------------------------------------------------- + */ +void +TransactionIdStore(TransactionId transactionId, + TransactionId *destination) +{ + *destination = transactionId; +} + +/* ---------------------------------------------------------------- + * TransactionIdEquals + * ---------------------------------------------------------------- + */ +bool +TransactionIdEquals(TransactionId id1, TransactionId id2) +{ + return ((bool) (id1 == id2)); +} + +/* ---------------------------------------------------------------- + * TransactionIdIsLessThan + * ---------------------------------------------------------------- + */ +bool +TransactionIdIsLessThan(TransactionId id1, TransactionId id2) +{ + return ((bool)(id1 < id2)); +} + +/* ---------------------------------------------------------------- + * xideq + * ---------------------------------------------------------------- + */ + +/* + * xideq - returns 1, iff xid1 == xid2 + * 0 else; + */ +bool +xideq(TransactionId xid1, TransactionId xid2) +{ + return( (bool) (xid1 == xid2) ); +} + + + +/* ---------------------------------------------------------------- + * TransactionIdIncrement + * ---------------------------------------------------------------- + */ +void +TransactionIdIncrement(TransactionId *transactionId) +{ + + (*transactionId)++; + if (*transactionId == DisabledTransactionId) + elog(FATAL, "TransactionIdIncrement: exhausted XID's"); + return; +} + +/* ---------------------------------------------------------------- + * TransactionIdAdd + * ---------------------------------------------------------------- + */ +void +TransactionIdAdd(TransactionId *xid, int value) +{ + *xid += value; + return; +} + |