postgresql/src/backend/access/transam/xact.c

1491 lines
37 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* 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.31 1999/02/03 21:15:45 momjian 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.
* (or an ABORT! --djm)
*
* 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
*
*-------------------------------------------------------------------------
*/
1996-10-21 09:15:18 +02:00
/*
* Large object clean up added in CommitTransaction() to prevent buffer leaks.
* [PA, 7/17/98]
* [PA] is Pascal Andr<EFBFBD> <andre@via.ecp.fr>
*/
1996-11-05 12:12:33 +01:00
#include <postgres.h>
1996-10-21 09:15:18 +02:00
1996-11-05 12:12:33 +01:00
#include <access/xact.h>
#include <utils/inval.h>
#include <utils/portal.h>
#include <access/transam.h>
1996-11-05 12:12:33 +01:00
#include <storage/proc.h>
#include <utils/mcxt.h>
#include <catalog/heap.h>
#include <utils/relcache.h>
#include <miscadmin.h>
#include <commands/async.h>
#include <commands/sequence.h>
1996-10-21 09:15:18 +02:00
/* included for _lo_commit [PA, 7/17/98] */
#include <libpq/be-fsstubs.h>
static void AbortTransaction(void);
static void AtAbort_Cache(void);
static void AtAbort_Locks(void);
static void AtAbort_Memory(void);
static void AtCommit_Cache(void);
static void AtCommit_Locks(void);
static void AtCommit_Memory(void);
static void AtStart_Cache(void);
static void AtStart_Locks(void);
static void AtStart_Memory(void);
static void CommitTransaction(void);
static void RecordTransactionAbort(void);
static void RecordTransactionCommit(void);
static void StartTransaction(void);
/* ----------------
* global variables holding the current transaction state.
*
* Note: when we are running several slave processes, the
* current transaction state data is copied into shared memory
* and the CurrentTransactionState pointer changed to
* point to the shared copy. All this occurrs in slaves.c
* ----------------
*/
TransactionStateData CurrentTransactionStateData = {
0, /* transaction id */
FirstCommandId, /* command id */
0x0, /* start time */
TRANS_DEFAULT, /* transaction state */
TBLOCK_DEFAULT /* transaction block state */
};
TransactionState CurrentTransactionState = &CurrentTransactionStateData;
int DefaultXactIsoLevel = XACT_READ_COMMITTED;
int XactIsoLevel;
/* ----------------
* info returned when the system is disabled
1997-03-15 00:21:12 +01:00
*
* Apparently a lot of this code is inherited from other prototype systems.
* For DisabledStartTime, use a symbolic value to make the relationships clearer.
* The old value of 1073741823 corresponds to a date in y2004, which is coming closer
* every day. It appears that if we return a value guaranteed larger than
* any real time associated with a transaction then comparisons in other
* modules will still be correct. Let's use BIG_ABSTIME for this. tgl 2/14/97
*
* Note: I have no idea what the significance of the
* 1073741823 in DisabledStartTime.. I just carried
* this over when converting things from the old
* V1 transaction system. -cim 3/18/90
* ----------------
*/
TransactionId DisabledTransactionId = (TransactionId) -1;
CommandId DisabledCommandId = (CommandId) -1;
AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME; /* 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(void)
{
return TransactionFlushState;
}
#ifdef NOT_USED
void
SetTransactionFlushEnabled(bool state)
{
TransactionFlushState = (state == true);
}
/* --------------------------------
* IsTransactionState
*
* This returns true if we are currently running a query
* within an executing transaction.
* --------------------------------
*/
bool
IsTransactionState(void)
{
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...
*/
1998-09-01 05:29:17 +02:00
return false;
}
#endif
/* --------------------------------
* 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;
}
CommandId
GetScanCommandId()
{
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->scanCommandId;
}
/* --------------------------------
* 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;
}
bool
CommandIdGEScanCommandId(CommandId cid)
{
TransactionState s = CurrentTransactionState;
if (AMI_OVERRIDE)
return false;
return (cid >= s->scanCommandId) ? true : false;
}
/* --------------------------------
* ClearCommandIdCounterOverflowFlag
* --------------------------------
*/
#ifdef NOT_USED
void
ClearCommandIdCounterOverflowFlag()
{
CommandIdCounterOverflowFlag = false;
}
#endif
/* --------------------------------
* CommandCounterIncrement
* --------------------------------
*/
void
CommandCounterIncrement()
{
CurrentTransactionStateData.commandId += 1;
if (CurrentTransactionStateData.commandId == FirstCommandId)
{
CommandIdCounterOverflowFlag = true;
elog(ERROR, "You may only have 2^32-1 commands per transaction");
}
CurrentTransactionStateData.scanCommandId = CurrentTransactionStateData.commandId;
/* make cache changes visible to me */
AtCommit_Cache();
AtStart_Cache();
1998-12-16 12:53:55 +01:00
TransactionIdFlushCache();
}
void
SetScanCommandId(CommandId savedId)
{
CurrentTransactionStateData.scanCommandId = savedId;
}
/* ----------------------------------------------------------------
* initialization stuff
* ----------------------------------------------------------------
*/
void
InitializeTransactionSystem()
{
InitializeTransactionLog();
}
/* ----------------------------------------------------------------
* StartTransaction stuff
* ----------------------------------------------------------------
*/
/* --------------------------------
* AtStart_Cache
* --------------------------------
*/
static void
AtStart_Cache()
{
DiscardInvalid();
}
/* --------------------------------
* AtStart_Locks
* --------------------------------
*/
static 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
* --------------------------------
*/
static 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
* ----------------
*/
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
* --------------------------------
*/
static 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
* --------------------------------
*/
static 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
* --------------------------------
*/
static void
AtCommit_Locks()
{
/* ----------------
* XXX What if ProcReleaseLocks fails? (race condition?)
*
* Then you're up a creek! -mer 5/24/92
* ----------------
*/
ProcReleaseLocks();
}
/* --------------------------------
* AtCommit_Memory
* --------------------------------
*/
static 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();
MemoryContextSwitchTo(TopMemoryContext);
}
/* ----------------------------------------------------------------
* AbortTransaction stuff
* ----------------------------------------------------------------
*/
/* --------------------------------
* RecordTransactionAbort
* --------------------------------
*/
static 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
* --------------------------------
*/
static void
AtAbort_Cache()
{
RegisterInvalid(false);
}
/* --------------------------------
* AtAbort_Locks
* --------------------------------
*/
static void
AtAbort_Locks()
{
/* ----------------
* XXX What if ProcReleaseLocks() fails? (race condition?)
*
* Then you're up a creek without a paddle! -mer
* ----------------
*/
ProcReleaseLocks();
}
/* --------------------------------
* AtAbort_Memory
* --------------------------------
*/
static 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).
* ----------------
*/
MemoryContextSwitchTo(TopMemoryContext);
}
/* ----------------------------------------------------------------
* interface routines
* ----------------------------------------------------------------
*/
/* --------------------------------
* StartTransaction
*
* --------------------------------
*/
static void
StartTransaction()
{
TransactionState s = CurrentTransactionState;
1998-12-16 12:53:55 +01:00
TransactionIdFlushCache();
FreeXactSnapshot();
XactIsoLevel = DefaultXactIsoLevel;
1998-12-16 12:53:55 +01:00
/* ----------------
* 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));
XactLockTableInsert(s->transactionIdData);
/* ----------------
* initialize current transaction state fields
* ----------------
*/
s->commandId = FirstCommandId;
s->scanCommandId = 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
*/
InitNoNameRelList();
/* ----------------
* done with start processing, set current transaction
* state to "in progress"
* ----------------
*/
s->state = TRANS_INPROGRESS;
/*
* Let others to know about current transaction is in progress - vadim
* 11/26/96
*/
if (MyProc != (PROC *) NULL)
MyProc->xid = s->transactionIdData;
}
/* ---------------
* Tell me if we are currently in progress
* ---------------
*/
bool
CurrentXactInProgress()
{
1998-09-01 05:29:17 +02:00
return CurrentTransactionState->state == TRANS_INPROGRESS;
}
/* --------------------------------
* CommitTransaction
*
* --------------------------------
*/
static 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
* ----------------
*/
/* handle commit for large objects [ PA, 7/17/98 ] */
_lo_commit();
/* NOTIFY commit must also come before lower-level cleanup */
AtCommit_Notify();
CloseSequences();
DestroyNoNameRels();
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;
/*
* Let others to know about no transaction in progress - vadim
* 11/26/96
*/
if (MyProc != (PROC *) NULL)
MyProc->xid = InvalidTransactionId;
}
/* --------------------------------
* AbortTransaction
*
* --------------------------------
*/
static void
AbortTransaction()
{
TransactionState s = CurrentTransactionState;
/*
* Let others to know about no transaction in progress - vadim
* 11/26/96
*/
if (MyProc != (PROC *) NULL)
MyProc->xid = InvalidTransactionId;
/* ----------------
* 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
* ----------------
*/
UnlockBuffers();
AtAbort_Notify();
CloseSequences();
AtEOXact_portals();
RecordTransactionAbort();
RelationPurgeLocalRelation(false);
DestroyNoNameRels();
AtAbort_Cache();
AtAbort_Locks();
AtAbort_Memory();
/* ----------------
* done with abort processing, set current transaction
* state back to default
* ----------------
*/
s->state = TRANS_DEFAULT;
}
/* --------------------------------
* 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.
*
* That someday is today, at least for memory allocated by
* command in the BlankPortal' HeapMemory context.
* - vadim 03/25/97
* ----------------
*/
case TBLOCK_INPROGRESS:
CommandCounterIncrement();
#ifdef TBL_FREE_CMD_MEMORY
EndPortalAllocMode();
StartPortalAllocMode(DefaultAllocMode, 0);
#endif
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(void)
{
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(void)
{
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
* --------------------------------
*/
#ifdef NOT_USED
static void
AbortTransactionBlock(void)
{
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 in in-progress state");
AbortTransaction();
s->blockState = TBLOCK_ENDABORT;
}
#endif
/* --------------------------------
* UserAbortTransactionBlock
* --------------------------------
*/
void
UserAbortTransactionBlock()
{
TransactionState s = CurrentTransactionState;
/* ----------------
* check the current transaction state
* ----------------
*/
if (s->state == TRANS_DISABLED)
return;
/*
* if the transaction has already been automatically aborted with an
* error, and the user subsequently types 'abort', allow it. (the
* behavior is the same as if they had typed 'end'.)
*/
if (s->blockState == TBLOCK_ABORT)
{
s->blockState = TBLOCK_ENDABORT;
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 in in-progress state");
AbortTransaction();
s->blockState = TBLOCK_ENDABORT;
}
/* --------------------------------
* AbortOutOfAnyTransaction
*
* This routine is provided for error recovery purposes. It aborts any
* active transaction or transaction block, leaving the system in a known
* idle state.
* --------------------------------
*/
void
AbortOutOfAnyTransaction()
{
TransactionState s = CurrentTransactionState;
/*
* Get out of any low-level transaction
*/
if (s->state != TRANS_DEFAULT)
AbortTransaction();
/*
* Now reset the high-level state
*/
s->blockState = TBLOCK_DEFAULT;
}
bool
IsTransactionBlock()
{
TransactionState s = CurrentTransactionState;
if (s->blockState == TBLOCK_INPROGRESS
|| s->blockState == TBLOCK_ENDABORT)
1998-09-01 05:29:17 +02:00
return true;
1998-09-01 05:29:17 +02:00
return false;
}