1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-18 17:42:25 +03:00

Invent ResourceOwner mechanism as per my recent proposal, and use it to

keep track of portal-related resources separately from transaction-related
resources.  This allows cursors to work in a somewhat sane fashion with
nested transactions.  For now, cursor behavior is non-subtransactional,
that is a cursor's state does not roll back if you abort a subtransaction
that fetched from the cursor.  We might want to change that later.
This commit is contained in:
Tom Lane
2004-07-17 03:32:14 +00:00
parent f4c069ca8f
commit fe548629c5
41 changed files with 2086 additions and 1192 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.170 2004/07/01 20:11:02 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.171 2004/07/17 03:28:23 tgl Exp $
*
* NOTES
* Transaction aborts can now occur two ways:
@ -144,10 +144,6 @@
#include <time.h>
#include <unistd.h>
#include "access/gistscan.h"
#include "access/hash.h"
#include "access/nbtree.h"
#include "access/rtree.h"
#include "access/subtrans.h"
#include "access/xact.h"
#include "catalog/heap.h"
@ -168,23 +164,73 @@
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/portal.h"
#include "utils/catcache.h"
#include "utils/relcache.h"
#include "utils/resowner.h"
#include "pgstat.h"
/*
* transaction states - transaction state from server perspective
*/
typedef enum TransState
{
TRANS_DEFAULT,
TRANS_START,
TRANS_INPROGRESS,
TRANS_COMMIT,
TRANS_ABORT
} TransState;
/*
* transaction block states - transaction state of client queries
*/
typedef enum TBlockState
{
TBLOCK_DEFAULT,
TBLOCK_STARTED,
TBLOCK_BEGIN,
TBLOCK_INPROGRESS,
TBLOCK_END,
TBLOCK_ABORT,
TBLOCK_ENDABORT,
TBLOCK_SUBBEGIN,
TBLOCK_SUBBEGINABORT,
TBLOCK_SUBINPROGRESS,
TBLOCK_SUBEND,
TBLOCK_SUBABORT,
TBLOCK_SUBENDABORT_OK,
TBLOCK_SUBENDABORT_ERROR
} TBlockState;
/*
* transaction state structure
*/
typedef struct TransactionStateData
{
TransactionId transactionIdData; /* my XID */
CommandId commandId; /* current CID */
TransState state; /* low-level state */
TBlockState blockState; /* high-level state */
int nestingLevel; /* nest depth */
MemoryContext curTransactionContext; /* my xact-lifetime context */
ResourceOwner curTransactionOwner; /* my query resources */
List *childXids; /* subcommitted child XIDs */
AclId currentUser; /* subxact start current_user */
struct TransactionStateData *parent; /* back link to parent */
} TransactionStateData;
typedef TransactionStateData *TransactionState;
static void AbortTransaction(void);
static void AtAbort_Cache(void);
static void AtAbort_Locks(void);
static void AtAbort_Memory(void);
static void AtCleanup_Memory(void);
static void AtCommit_Cache(void);
static void AtCommit_LocalCache(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 AtStart_ResourceOwner(void);
static void CallEOXactCallbacks(bool isCommit);
static void CleanupTransaction(void);
static void CommitTransaction(void);
@ -200,11 +246,11 @@ static void StartAbortedSubTransaction(void);
static void PushTransaction(void);
static void PopTransaction(void);
static void AtSubAbort_Locks(void);
static void AtSubAbort_Memory(void);
static void AtSubCleanup_Memory(void);
static void AtSubCommit_Memory(void);
static void AtSubStart_Memory(void);
static void AtSubStart_ResourceOwner(void);
static void ShowTransactionState(const char *str);
static void ShowTransactionStateRec(TransactionState state);
@ -224,6 +270,7 @@ static TransactionStateData TopTransactionStateData = {
* perspective */
0, /* nesting level */
NULL, /* cur transaction context */
NULL, /* cur transaction resource owner */
NIL, /* subcommitted child Xids */
0, /* entry-time current userid */
NULL /* link to parent state block */
@ -462,8 +509,7 @@ CommandCounterIncrement(void)
SerializableSnapshot->curcid = s->commandId;
/*
* make cache changes visible to me. AtCommit_LocalCache() instead of
* AtCommit_Cache() is called here.
* make cache changes visible to me.
*/
AtCommit_LocalCache();
AtStart_Cache();
@ -484,20 +530,6 @@ AtStart_Cache(void)
AcceptInvalidationMessages();
}
/*
* AtStart_Locks
*/
static void
AtStart_Locks(void)
{
/*
* 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
*/
@ -532,6 +564,29 @@ AtStart_Memory(void)
MemoryContextSwitchTo(CurTransactionContext);
}
/*
* AtStart_ResourceOwner
*/
static void
AtStart_ResourceOwner(void)
{
TransactionState s = CurrentTransactionState;
/*
* We shouldn't have a transaction resource owner already.
*/
Assert(TopTransactionResourceOwner == NULL);
/*
* Create a toplevel resource owner for the transaction.
*/
s->curTransactionOwner = ResourceOwnerCreate(NULL, "TopTransaction");
TopTransactionResourceOwner = s->curTransactionOwner;
CurTransactionResourceOwner = s->curTransactionOwner;
CurrentResourceOwner = s->curTransactionOwner;
}
/* ----------------------------------------------------------------
* StartSubTransaction stuff
* ----------------------------------------------------------------
@ -563,6 +618,28 @@ AtSubStart_Memory(void)
MemoryContextSwitchTo(CurTransactionContext);
}
/*
* AtSubStart_ResourceOwner
*/
static void
AtSubStart_ResourceOwner(void)
{
TransactionState s = CurrentTransactionState;
Assert(s->parent != NULL);
/*
* Create a resource owner for the subtransaction. We make it a
* child of the immediate parent's resource owner.
*/
s->curTransactionOwner =
ResourceOwnerCreate(s->parent->curTransactionOwner,
"SubTransaction");
CurTransactionResourceOwner = s->curTransactionOwner;
CurrentResourceOwner = s->curTransactionOwner;
}
/* ----------------------------------------------------------------
* CommitTransaction stuff
* ----------------------------------------------------------------
@ -581,7 +658,7 @@ RecordTransactionCommit(void)
/* Get data needed for commit record */
nrels = smgrGetPendingDeletes(true, &rptr);
nchildren = xactGetCommittedChildren(&children, false);
nchildren = xactGetCommittedChildren(&children);
/*
* If we made neither any XLOG entries nor any temp-rel updates,
@ -714,23 +791,6 @@ RecordTransactionCommit(void)
}
/*
* AtCommit_Cache
*/
static void
AtCommit_Cache(void)
{
/*
* Clean up the relation cache.
*/
AtEOXact_RelationCache(true);
/*
* Make catalog changes visible to all backends.
*/
AtEOXact_Inval(true);
}
/*
* AtCommit_LocalCache
*/
@ -743,20 +803,6 @@ AtCommit_LocalCache(void)
CommandEndInvalidationMessages();
}
/*
* AtCommit_Locks
*/
static void
AtCommit_Locks(void)
{
/*
* XXX What if ProcReleaseLocks fails? (race condition?)
*
* Then you're up a creek! -mer 5/24/92
*/
ProcReleaseLocks(ReleaseAllExceptSession, 0, NULL);
}
/*
* AtCommit_Memory
*/
@ -878,7 +924,7 @@ RecordTransactionAbort(void)
/* Get data needed for abort record */
nrels = smgrGetPendingDeletes(false, &rptr);
nchildren = xactGetCommittedChildren(&children, false);
nchildren = xactGetCommittedChildren(&children);
/*
* If we made neither any transaction-controlled XLOG entries nor any
@ -979,31 +1025,6 @@ RecordTransactionAbort(void)
pfree(children);
}
/*
* AtAbort_Cache
*/
static void
AtAbort_Cache(void)
{
AtEOXact_RelationCache(false);
AtEOXact_Inval(false);
}
/*
* AtAbort_Locks
*/
static void
AtAbort_Locks(void)
{
/*
* XXX What if ProcReleaseLocks() fails? (race condition?)
*
* Then you're up a creek without a paddle! -mer
*/
ProcReleaseLocks(ReleaseAll, 0, NULL);
}
/*
* AtAbort_Memory
*/
@ -1029,22 +1050,6 @@ AtAbort_Memory(void)
MemoryContextSwitchTo(TopMemoryContext);
}
/*
* AtSubAbort_Locks
*/
static void
AtSubAbort_Locks(void)
{
int nxids;
TransactionId *xids;
nxids = xactGetCommittedChildren(&xids, true);
ProcReleaseLocks(ReleaseGivenXids, nxids, xids);
pfree(xids);
}
/*
* AtSubAbort_Memory
@ -1070,7 +1075,7 @@ RecordSubTransactionAbort(void)
/* Get data needed for abort record */
nrels = smgrGetPendingDeletes(false, &rptr);
nchildren = xactGetCommittedChildren(&children, false);
nchildren = xactGetCommittedChildren(&children);
/*
* If we made neither any transaction-controlled XLOG entries nor any
@ -1241,6 +1246,12 @@ StartTransaction(void)
XactIsoLevel = DefaultXactIsoLevel;
XactReadOnly = DefaultXactReadOnly;
/*
* must initialize resource-management stuff first
*/
AtStart_Memory();
AtStart_ResourceOwner();
/*
* generate a new transaction id
*/
@ -1268,16 +1279,10 @@ StartTransaction(void)
*/
/*
* initialize the various transaction subsystems
* initialize other subsystems for new transaction
*/
AtStart_Memory();
AtStart_Inval();
AtStart_Cache();
AtStart_Locks();
/*
* Tell the trigger manager we're starting a transaction
*/
DeferredTriggerBeginXact();
/*
@ -1380,27 +1385,49 @@ CommitTransaction(void)
* pins); then release locks; then release backend-local resources. We
* want to release locks at the point where any backend waiting for us
* will see our transaction as being fully cleaned up.
*
* Resources that can be associated with individual queries are
* handled by the ResourceOwner mechanism. The other calls here
* are for backend-wide state.
*/
smgrDoPendingDeletes(true);
AtCommit_Cache();
AtEOXact_Buffers(true);
/* smgrcommit already done */
AtCommit_Locks();
ResourceOwnerRelease(TopTransactionResourceOwner,
RESOURCE_RELEASE_BEFORE_LOCKS,
true, true);
/*
* Make catalog changes visible to all backends. This has to happen
* after relcache references are dropped (see comments for
* AtEOXact_RelationCache), but before locks are released (if anyone
* is waiting for lock on a relation we've modified, we want them to
* know about the catalog change before they start using the relation).
*/
AtEOXact_Inval(true);
ResourceOwnerRelease(TopTransactionResourceOwner,
RESOURCE_RELEASE_LOCKS,
true, true);
ResourceOwnerRelease(TopTransactionResourceOwner,
RESOURCE_RELEASE_AFTER_LOCKS,
true, true);
CallEOXactCallbacks(true);
AtEOXact_GUC(true, false);
AtEOXact_SPI(true);
AtEOXact_gist();
AtEOXact_hash();
AtEOXact_nbtree();
AtEOXact_rtree();
AtEOXact_on_commit_actions(true, s->transactionIdData);
AtEOXact_Namespace(true);
AtEOXact_CatCache(true);
AtEOXact_Files();
pgstat_count_xact_commit();
CurrentResourceOwner = NULL;
ResourceOwnerDelete(TopTransactionResourceOwner);
s->curTransactionOwner = NULL;
CurTransactionResourceOwner = NULL;
TopTransactionResourceOwner = NULL;
AtCommit_Memory();
s->nestingLevel = 0;
@ -1504,22 +1531,24 @@ AbortTransaction(void)
*/
smgrDoPendingDeletes(false);
AtAbort_Cache();
AtEOXact_Buffers(false);
smgrabort();
AtAbort_Locks();
ResourceOwnerRelease(TopTransactionResourceOwner,
RESOURCE_RELEASE_BEFORE_LOCKS,
false, true);
AtEOXact_Inval(false);
ResourceOwnerRelease(TopTransactionResourceOwner,
RESOURCE_RELEASE_LOCKS,
false, true);
ResourceOwnerRelease(TopTransactionResourceOwner,
RESOURCE_RELEASE_AFTER_LOCKS,
false, true);
CallEOXactCallbacks(false);
AtEOXact_GUC(false, false);
AtEOXact_SPI(false);
AtEOXact_gist();
AtEOXact_hash();
AtEOXact_nbtree();
AtEOXact_rtree();
AtEOXact_on_commit_actions(false, s->transactionIdData);
AtEOXact_Namespace(false);
AtEOXact_CatCache(false);
AtEOXact_Files();
SetReindexProcessing(InvalidOid, InvalidOid);
pgstat_count_xact_rollback();
@ -1548,6 +1577,13 @@ CleanupTransaction(void)
* do abort cleanup processing
*/
AtCleanup_Portals(); /* now safe to release portal memory */
CurrentResourceOwner = NULL; /* and resource owner */
ResourceOwnerDelete(TopTransactionResourceOwner);
s->curTransactionOwner = NULL;
CurTransactionResourceOwner = NULL;
TopTransactionResourceOwner = NULL;
AtCleanup_Memory(); /* and transaction memory */
s->nestingLevel = 0;
@ -2483,6 +2519,12 @@ StartSubTransaction(void)
s->state = TRANS_START;
/*
* must initialize resource-management stuff first
*/
AtSubStart_Memory();
AtSubStart_ResourceOwner();
/*
* Generate a new Xid and record it in pg_subtrans.
*/
@ -2495,13 +2537,10 @@ StartSubTransaction(void)
*/
s->currentUser = GetUserId();
/* Initialize the various transaction subsystems */
AtSubStart_Memory();
/*
* Initialize other subsystems for new subtransaction
*/
AtSubStart_Inval();
AtSubStart_RelationCache();
AtSubStart_CatCache();
AtSubStart_Buffers();
AtSubStart_smgr();
AtSubStart_Notify();
DeferredTriggerBeginSubXact();
@ -2524,7 +2563,8 @@ CommitSubTransaction(void)
elog(WARNING, "CommitSubTransaction and not in in-progress state");
/* Pre-commit processing */
AtSubCommit_Portals(s->parent->transactionIdData);
AtSubCommit_Portals(s->parent->transactionIdData,
s->parent->curTransactionOwner);
DeferredTriggerEndSubXact(true);
s->state = TRANS_COMMIT;
@ -2539,17 +2579,31 @@ CommitSubTransaction(void)
AtSubEOXact_Inval(true);
AtEOSubXact_SPI(true, s->transactionIdData);
/*
* Note that we just release the resource owner's resources and don't
* delete it. This is because locks are not actually released here.
* The owner object continues to exist as a child of its parent owner
* (namely my parent transaction's resource owner), and the locks
* effectively become that owner object's responsibility.
*/
ResourceOwnerRelease(s->curTransactionOwner,
RESOURCE_RELEASE_BEFORE_LOCKS,
true, false);
/* we can skip the LOCKS phase */
ResourceOwnerRelease(s->curTransactionOwner,
RESOURCE_RELEASE_AFTER_LOCKS,
true, false);
AtSubCommit_Notify();
AtEOXact_GUC(true, true);
AtEOSubXact_gist(s->transactionIdData);
AtEOSubXact_hash(s->transactionIdData);
AtEOSubXact_rtree(s->transactionIdData);
AtEOSubXact_on_commit_actions(true, s->transactionIdData,
s->parent->transactionIdData);
AtEOSubXact_CatCache(true);
AtEOSubXact_RelationCache(true);
AtEOSubXact_Buffers(true);
CurrentResourceOwner = s->parent->curTransactionOwner;
CurTransactionResourceOwner = s->parent->curTransactionOwner;
s->curTransactionOwner = NULL;
AtSubCommit_Memory();
s->state = TRANS_DEFAULT;
@ -2597,20 +2651,25 @@ AbortSubTransaction(void)
AtSubAbort_smgr();
DeferredTriggerEndSubXact(false);
AtSubAbort_Portals();
AtSubEOXact_Inval(false);
AtSubAbort_Locks();
AtEOSubXact_SPI(false, s->transactionIdData);
AtSubAbort_Portals(s->parent->transactionIdData,
s->parent->curTransactionOwner);
AtSubEOXact_Inval(false);
ResourceOwnerRelease(s->curTransactionOwner,
RESOURCE_RELEASE_BEFORE_LOCKS,
false, false);
ResourceOwnerRelease(s->curTransactionOwner,
RESOURCE_RELEASE_LOCKS,
false, false);
ResourceOwnerRelease(s->curTransactionOwner,
RESOURCE_RELEASE_AFTER_LOCKS,
false, false);
AtSubAbort_Notify();
AtEOXact_GUC(false, true);
AtEOSubXact_gist(s->transactionIdData);
AtEOSubXact_hash(s->transactionIdData);
AtEOSubXact_rtree(s->transactionIdData);
AtEOSubXact_on_commit_actions(false, s->transactionIdData,
s->parent->transactionIdData);
AtEOSubXact_RelationCache(false);
AtEOSubXact_CatCache(false);
AtEOSubXact_Buffers(false);
/*
* Reset user id which might have been changed transiently. Here we
@ -2645,6 +2704,12 @@ CleanupSubTransaction(void)
elog(WARNING, "CleanupSubTransaction and not in aborted state");
AtSubCleanup_Portals();
CurrentResourceOwner = s->parent->curTransactionOwner;
CurTransactionResourceOwner = s->parent->curTransactionOwner;
ResourceOwnerDelete(s->curTransactionOwner);
s->curTransactionOwner = NULL;
AtSubCleanup_Memory();
s->state = TRANS_DEFAULT;
@ -2685,6 +2750,7 @@ StartAbortedSubTransaction(void)
* Initialize only what has to be there for CleanupSubTransaction to work.
*/
AtSubStart_Memory();
AtSubStart_ResourceOwner();
s->state = TRANS_ABORT;
@ -2723,6 +2789,7 @@ PushTransaction(void)
*/
s->transactionIdData = p->transactionIdData;
s->curTransactionContext = p->curTransactionContext;
s->curTransactionOwner = p->curTransactionOwner;
s->currentUser = p->currentUser;
CurrentTransactionState = s;
@ -2752,6 +2819,10 @@ PopTransaction(void)
CurTransactionContext = s->parent->curTransactionContext;
MemoryContextSwitchTo(CurTransactionContext);
/* Ditto for ResourceOwner links */
CurTransactionResourceOwner = s->parent->curTransactionOwner;
CurrentResourceOwner = s->parent->curTransactionOwner;
/* Free the old child structure */
pfree(s);
}
@ -2861,11 +2932,9 @@ TransStateAsString(TransState state)
* value is the number of child transactions. *children is set to point to a
* palloc'd array of TransactionIds. If there are no subxacts, *children is
* set to NULL.
*
* If metoo is true, include the current TransactionId.
*/
int
xactGetCommittedChildren(TransactionId **ptr, bool metoo)
xactGetCommittedChildren(TransactionId **ptr)
{
TransactionState s = CurrentTransactionState;
int nchildren;
@ -2873,8 +2942,6 @@ xactGetCommittedChildren(TransactionId **ptr, bool metoo)
ListCell *p;
nchildren = list_length(s->childXids);
if (metoo)
nchildren++;
if (nchildren == 0)
{
*ptr = NULL;
@ -2887,10 +2954,9 @@ xactGetCommittedChildren(TransactionId **ptr, bool metoo)
foreach(p, s->childXids)
{
TransactionId child = lfirst_int(p);
*children++ = (TransactionId)child;
*children++ = child;
}
if (metoo)
*children = s->transactionIdData;
return nchildren;
}