diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2004-08-01 17:32:22 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2004-08-01 17:32:22 +0000 |
commit | efcaf1e868d8399d932e68b8b248bcbd089b2d6b (patch) | |
tree | c6235945f73faab427498dd8662efab5cf2fe5fd /src | |
parent | 9d9cdf82a404f3e2a2d82cefc2c35ded55bb3b2e (diff) | |
download | postgresql-efcaf1e868d8399d932e68b8b248bcbd089b2d6b.tar.gz postgresql-efcaf1e868d8399d932e68b8b248bcbd089b2d6b.zip |
Some mop-up work for savepoints (nested transactions). Store a small
number of active subtransaction XIDs in each backend's PGPROC entry,
and use this to avoid expensive probes into pg_subtrans during
TransactionIdIsInProgress. Extend EOXactCallback API to allow add-on
modules to get control at subxact start/end. (This is deliberately
not compatible with the former API, since any uses of that API probably
need manual review anyway.) Add basic reference documentation for
SAVEPOINT and related commands. Minor other cleanups to check off some
of the open issues for subtransactions.
Alvaro Herrera and Tom Lane.
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/access/transam/varsup.c | 49 | ||||
-rw-r--r-- | src/backend/access/transam/xact.c | 203 | ||||
-rw-r--r-- | src/backend/catalog/index.c | 68 | ||||
-rw-r--r-- | src/backend/storage/ipc/sinval.c | 302 | ||||
-rw-r--r-- | src/backend/tcop/utility.c | 5 | ||||
-rw-r--r-- | src/backend/utils/init/miscinit.c | 20 | ||||
-rw-r--r-- | src/include/access/htup.h | 41 | ||||
-rw-r--r-- | src/include/access/xact.h | 19 | ||||
-rw-r--r-- | src/include/miscadmin.h | 3 | ||||
-rw-r--r-- | src/include/storage/proc.h | 23 | ||||
-rw-r--r-- | src/include/storage/sinval.h | 5 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_exec.c | 52 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_handler.c | 4 | ||||
-rw-r--r-- | src/pl/plpgsql/src/plpgsql.h | 4 |
14 files changed, 523 insertions, 275 deletions
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c index 9d3b0b323aa..81b60c9fda6 100644 --- a/src/backend/access/transam/varsup.c +++ b/src/backend/access/transam/varsup.c @@ -6,7 +6,7 @@ * Copyright (c) 2000-2003, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.56 2004/07/01 00:49:42 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.57 2004/08/01 17:32:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -68,10 +68,10 @@ GetNewTransactionId(bool isSubXact) TransactionIdAdvance(ShmemVariableCache->nextXid); /* - * Must set MyProc->xid before releasing XidGenLock. This ensures - * that when GetSnapshotData calls ReadNewTransactionId, all active - * XIDs before the returned value of nextXid are already present in - * the shared PGPROC array. Else we have a race condition. + * We must store the new XID into the shared PGPROC array before releasing + * XidGenLock. This ensures that when GetSnapshotData calls + * ReadNewTransactionId, all active XIDs before the returned value of + * nextXid are already present in PGPROC. Else we have a race condition. * * XXX by storing xid into MyProc without acquiring SInvalLock, we are * relying on fetch/store of an xid to be atomic, else other backends @@ -82,16 +82,41 @@ GetNewTransactionId(bool isSubXact) * the value only once, rather than assume they can read it multiple * times and get the same answer each time. * + * The same comments apply to the subxact xid count and overflow fields. + * * A solution to the atomic-store problem would be to give each PGPROC - * its own spinlock used only for fetching/storing that PGPROC's xid. - * (SInvalLock would then mean primarily that PGPROCs couldn't be added/ - * removed while holding the lock.) + * its own spinlock used only for fetching/storing that PGPROC's xid + * and related fields. (SInvalLock would then mean primarily that + * PGPROCs couldn't be added/removed while holding the lock.) * - * We don't want a subtransaction to update the stored Xid; we'll check - * if a transaction Xid is a running subxact by checking pg_subtrans. + * If there's no room to fit a subtransaction XID into PGPROC, set the + * cache-overflowed flag instead. This forces readers to look in + * pg_subtrans to map subtransaction XIDs up to top-level XIDs. + * There is a race-condition window, in that the new XID will not + * appear as running until its parent link has been placed into + * pg_subtrans. However, that will happen before anyone could possibly + * have a reason to inquire about the status of the XID, so it seems + * OK. (Snapshots taken during this window *will* include the parent + * XID, so they will deliver the correct answer later on when someone + * does have a reason to inquire.) */ - if (MyProc != NULL && !isSubXact) - MyProc->xid = xid; + if (MyProc != NULL) + { + if (!isSubXact) + MyProc->xid = xid; + else + { + if (MyProc->subxids.nxids < PGPROC_MAX_CACHED_SUBXIDS) + { + MyProc->subxids.xids[MyProc->subxids.nxids] = xid; + MyProc->subxids.nxids++; + } + else + { + MyProc->subxids.overflowed = true; + } + } + } LWLockRelease(XidGenLock); diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index b6758a14b22..486f85be5d9 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.174 2004/07/31 07:39:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.175 2004/08/01 17:32:13 tgl Exp $ * * NOTES * Transaction aborts can now occur two ways: @@ -168,7 +168,6 @@ #include "pgstat.h" - /* * transaction states - transaction state from server perspective */ @@ -230,6 +229,14 @@ typedef struct TransactionStateData typedef TransactionStateData *TransactionState; +/* + * childXids is currently implemented as an integer List, relying on the + * assumption that TransactionIds are no wider than int. We use these + * macros to provide some isolation in case that changes in the future. + */ +#define lfirst_xid(lc) ((TransactionId) lfirst_int(lc)) +#define lappend_xid(list, datum) lappend_int(list, (int) (datum)) + static void AbortTransaction(void); static void AtAbort_Memory(void); @@ -239,7 +246,7 @@ static void AtCommit_Memory(void); static void AtStart_Cache(void); static void AtStart_Memory(void); static void AtStart_ResourceOwner(void); -static void CallEOXactCallbacks(bool isCommit); +static void CallXactCallbacks(XactEvent event, TransactionId parentXid); static void CleanupTransaction(void); static void CommitTransaction(void); static void RecordTransactionAbort(void); @@ -315,16 +322,16 @@ int CommitSiblings = 5; /* number of concurrent xacts needed to /* - * List of add-on end-of-xact callbacks + * List of add-on start- and end-of-xact callbacks */ -typedef struct EOXactCallbackItem +typedef struct XactCallbackItem { - struct EOXactCallbackItem *next; - EOXactCallback callback; + struct XactCallbackItem *next; + XactCallback callback; void *arg; -} EOXactCallbackItem; +} XactCallbackItem; -static EOXactCallbackItem *EOXact_callbacks = NULL; +static XactCallbackItem *Xact_callbacks = NULL; static void (*_RollbackFunc) (void *) = NULL; static void *_RollbackData = NULL; @@ -490,7 +497,7 @@ TransactionIdIsCurrentTransactionId(TransactionId xid) return true; foreach(cell, s->childXids) { - if (TransactionIdEquals(xid, lfirst_int(cell))) + if (TransactionIdEquals(xid, lfirst_xid(cell))) return true; } @@ -877,12 +884,12 @@ AtSubCommit_childXids(void) old_cxt = MemoryContextSwitchTo(s->parent->curTransactionContext); + s->parent->childXids = lappend_xid(s->parent->childXids, + s->transactionIdData); + s->parent->childXids = list_concat(s->parent->childXids, s->childXids); s->childXids = NIL; /* ensure list not doubly referenced */ - s->parent->childXids = lappend_int(s->parent->childXids, - s->transactionIdData); - MemoryContextSwitchTo(old_cxt); } @@ -1083,6 +1090,7 @@ RecordSubTransactionAbort(void) { int nrels; RelFileNode *rptr; + TransactionId xid = GetCurrentTransactionId(); int nchildren; TransactionId *children; @@ -1104,8 +1112,6 @@ RecordSubTransactionAbort(void) */ if (MyLastRecPtr.xrecoff != 0 || MyXactMadeTempRelUpdate || nrels > 0) { - TransactionId xid = GetCurrentTransactionId(); - START_CRIT_SECTION(); /* @@ -1162,6 +1168,15 @@ RecordSubTransactionAbort(void) END_CRIT_SECTION(); } + /* + * We can immediately remove failed XIDs from PGPROC's cache of + * running child XIDs. It's easiest to do it here while we have the + * child XID array at hand, even though in the main-transaction + * case the equivalent work happens just after return from + * RecordTransactionAbort. + */ + XidCacheRemoveRunningXids(xid, nchildren, children); + /* And clean up local data */ if (rptr) pfree(rptr); @@ -1389,6 +1404,11 @@ CommitTransaction(void) LWLockAcquire(SInvalLock, LW_EXCLUSIVE); MyProc->xid = InvalidTransactionId; MyProc->xmin = InvalidTransactionId; + + /* Clear the subtransaction-XID cache too while holding the lock */ + MyProc->subxids.nxids = 0; + MyProc->subxids.overflowed = false; + LWLockRelease(SInvalLock); } @@ -1411,6 +1431,8 @@ CommitTransaction(void) smgrDoPendingDeletes(true); /* smgrcommit already done */ + CallXactCallbacks(XACT_EVENT_COMMIT, InvalidTransactionId); + ResourceOwnerRelease(TopTransactionResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, true, true); @@ -1431,7 +1453,6 @@ CommitTransaction(void) RESOURCE_RELEASE_AFTER_LOCKS, true, true); - CallEOXactCallbacks(true); AtEOXact_GUC(true, false); AtEOXact_SPI(true); AtEOXact_on_commit_actions(true, s->transactionIdData); @@ -1540,6 +1561,11 @@ AbortTransaction(void) LWLockAcquire(SInvalLock, LW_EXCLUSIVE); MyProc->xid = InvalidTransactionId; MyProc->xmin = InvalidTransactionId; + + /* Clear the subtransaction-XID cache too while holding the lock */ + MyProc->subxids.nxids = 0; + MyProc->subxids.overflowed = false; + LWLockRelease(SInvalLock); } @@ -1551,6 +1577,8 @@ AbortTransaction(void) smgrDoPendingDeletes(false); smgrabort(); + CallXactCallbacks(XACT_EVENT_ABORT, InvalidTransactionId); + ResourceOwnerRelease(TopTransactionResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true); @@ -1562,13 +1590,11 @@ AbortTransaction(void) RESOURCE_RELEASE_AFTER_LOCKS, false, true); - CallEOXactCallbacks(false); AtEOXact_GUC(false, false); AtEOXact_SPI(false); AtEOXact_on_commit_actions(false, s->transactionIdData); AtEOXact_Namespace(false); AtEOXact_Files(); - SetReindexProcessing(InvalidOid, InvalidOid); pgstat_count_xact_rollback(); /* @@ -2158,43 +2184,46 @@ IsInTransactionChain(void *stmtNode) /* - * Register or deregister callback functions for end-of-xact cleanup + * Register or deregister callback functions for start- and end-of-xact + * operations. * * These functions are intended for use by dynamically loaded modules. * For built-in modules we generally just hardwire the appropriate calls * (mainly because it's easier to control the order that way, where needed). * - * Note that the callback occurs post-commit or post-abort, so the callback - * functions can only do noncritical cleanup. + * At transaction end, the callback occurs post-commit or post-abort, so the + * callback functions can only do noncritical cleanup. At subtransaction + * start, the callback is called when the subtransaction has finished + * initializing. */ void -RegisterEOXactCallback(EOXactCallback callback, void *arg) +RegisterXactCallback(XactCallback callback, void *arg) { - EOXactCallbackItem *item; + XactCallbackItem *item; - item = (EOXactCallbackItem *) - MemoryContextAlloc(TopMemoryContext, sizeof(EOXactCallbackItem)); + item = (XactCallbackItem *) + MemoryContextAlloc(TopMemoryContext, sizeof(XactCallbackItem)); item->callback = callback; item->arg = arg; - item->next = EOXact_callbacks; - EOXact_callbacks = item; + item->next = Xact_callbacks; + Xact_callbacks = item; } void -UnregisterEOXactCallback(EOXactCallback callback, void *arg) +UnregisterXactCallback(XactCallback callback, void *arg) { - EOXactCallbackItem *item; - EOXactCallbackItem *prev; + XactCallbackItem *item; + XactCallbackItem *prev; prev = NULL; - for (item = EOXact_callbacks; item; prev = item, item = item->next) + for (item = Xact_callbacks; item; prev = item, item = item->next) { if (item->callback == callback && item->arg == arg) { if (prev) prev->next = item->next; else - EOXact_callbacks = item->next; + Xact_callbacks = item->next; pfree(item); break; } @@ -2202,13 +2231,13 @@ UnregisterEOXactCallback(EOXactCallback callback, void *arg) } static void -CallEOXactCallbacks(bool isCommit) +CallXactCallbacks(XactEvent event, TransactionId parentXid) { - EOXactCallbackItem *item; + XactCallbackItem *item; - for (item = EOXact_callbacks; item; item = item->next) + for (item = Xact_callbacks; item; item = item->next) { - (*item->callback) (isCommit, item->arg); + (*item->callback) (event, parentXid, item->arg); } } @@ -2948,32 +2977,11 @@ bool IsSubTransaction(void) { TransactionState s = CurrentTransactionState; - - switch (s->blockState) - { - case TBLOCK_DEFAULT: - case TBLOCK_STARTED: - case TBLOCK_BEGIN: - case TBLOCK_INPROGRESS: - case TBLOCK_END: - case TBLOCK_ABORT: - case TBLOCK_ENDABORT: - return false; - case TBLOCK_SUBBEGIN: - case TBLOCK_SUBINPROGRESS: - case TBLOCK_SUBABORT: - case TBLOCK_SUBEND: - case TBLOCK_SUBENDABORT_ALL: - case TBLOCK_SUBENDABORT: - case TBLOCK_SUBABORT_PENDING: - case TBLOCK_SUBENDABORT_RELEASE: - return true; - } - /* should never get here */ - elog(FATAL, "invalid transaction block state: %s", - BlockStateAsString(s->blockState)); - return false; /* keep compiler quiet */ + if (s->nestingLevel >= 2) + return true; + + return false; } /* @@ -2997,7 +3005,10 @@ StartSubTransaction(void) AtSubStart_ResourceOwner(); /* - * Generate a new Xid and record it in pg_subtrans. + * Generate a new Xid and record it in pg_subtrans. NB: we must make + * the subtrans entry BEFORE the Xid appears anywhere in shared storage, + * such as in the lock table; because until it's made the Xid may not + * appear to be "running" to other backends. See GetNewTransactionId. */ s->transactionIdData = GetNewTransactionId(true); @@ -3020,6 +3031,11 @@ StartSubTransaction(void) s->state = TRANS_INPROGRESS; + /* + * Call start-of-subxact callbacks + */ + CallXactCallbacks(XACT_EVENT_START_SUB, s->parent->transactionIdData); + ShowTransactionState("StartSubTransaction"); } @@ -3037,10 +3053,7 @@ CommitSubTransaction(void) elog(WARNING, "CommitSubTransaction while in %s state", TransStateAsString(s->state)); - /* Pre-commit processing */ - AtSubCommit_Portals(s->parent->transactionIdData, - s->parent->curTransactionOwner); - DeferredTriggerEndSubXact(true); + /* Pre-commit processing goes here -- nothing to do at the moment */ s->state = TRANS_COMMIT; @@ -3050,19 +3063,17 @@ CommitSubTransaction(void) AtSubCommit_childXids(); /* Post-commit cleanup */ - AtSubCommit_smgr(); - - AtEOSubXact_Inval(true); - AtEOSubXact_SPI(true, s->transactionIdData); - + DeferredTriggerEndSubXact(true); + AtSubCommit_Portals(s->parent->transactionIdData, + s->parent->curTransactionOwner); AtEOSubXact_LargeObject(true, s->transactionIdData, s->parent->transactionIdData); + AtSubCommit_Notify(); AtEOSubXact_UpdatePasswordFile(true, s->transactionIdData, s->parent->transactionIdData); - AtEOSubXact_Files(true, s->transactionIdData, - s->parent->transactionIdData); - AtEOSubXact_Namespace(true, s->transactionIdData, - s->parent->transactionIdData); + AtSubCommit_smgr(); + + CallXactCallbacks(XACT_EVENT_COMMIT_SUB, s->parent->transactionIdData); /* * Note that we just release the resource owner's resources and don't @@ -3074,15 +3085,20 @@ CommitSubTransaction(void) ResourceOwnerRelease(s->curTransactionOwner, RESOURCE_RELEASE_BEFORE_LOCKS, true, false); + AtEOSubXact_Inval(true); /* we can skip the LOCKS phase */ ResourceOwnerRelease(s->curTransactionOwner, RESOURCE_RELEASE_AFTER_LOCKS, true, false); - AtSubCommit_Notify(); AtEOXact_GUC(true, true); + AtEOSubXact_SPI(true, s->transactionIdData); AtEOSubXact_on_commit_actions(true, s->transactionIdData, s->parent->transactionIdData); + AtEOSubXact_Namespace(true, s->transactionIdData, + s->parent->transactionIdData); + AtEOSubXact_Files(true, s->transactionIdData, + s->parent->transactionIdData); /* * We need to restore the upper transaction's read-only state, @@ -3134,35 +3150,32 @@ AbortSubTransaction(void) LockWaitCancel(); - AtSubAbort_Memory(); - /* * do abort processing */ - - RecordSubTransactionAbort(); - - /* Post-abort cleanup */ - AtSubAbort_smgr(); + AtSubAbort_Memory(); DeferredTriggerEndSubXact(false); - AtEOSubXact_SPI(false, s->transactionIdData); AtSubAbort_Portals(s->parent->transactionIdData, s->parent->curTransactionOwner); - AtEOSubXact_Inval(false); - AtEOSubXact_LargeObject(false, s->transactionIdData, s->parent->transactionIdData); + AtSubAbort_Notify(); AtEOSubXact_UpdatePasswordFile(false, s->transactionIdData, s->parent->transactionIdData); - AtEOSubXact_Files(false, s->transactionIdData, - s->parent->transactionIdData); - AtEOSubXact_Namespace(false, s->transactionIdData, - s->parent->transactionIdData); + + /* Advertise the fact that we aborted in pg_clog. */ + RecordSubTransactionAbort(); + + /* Post-abort cleanup */ + AtSubAbort_smgr(); + + CallXactCallbacks(XACT_EVENT_ABORT_SUB, s->parent->transactionIdData); ResourceOwnerRelease(s->curTransactionOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, false); + AtEOSubXact_Inval(false); ResourceOwnerRelease(s->curTransactionOwner, RESOURCE_RELEASE_LOCKS, false, false); @@ -3170,10 +3183,14 @@ AbortSubTransaction(void) RESOURCE_RELEASE_AFTER_LOCKS, false, false); - AtSubAbort_Notify(); AtEOXact_GUC(false, true); + AtEOSubXact_SPI(false, s->transactionIdData); AtEOSubXact_on_commit_actions(false, s->transactionIdData, s->parent->transactionIdData); + AtEOSubXact_Namespace(false, s->transactionIdData, + s->parent->transactionIdData); + AtEOSubXact_Files(false, s->transactionIdData, + s->parent->transactionIdData); /* * Reset user id which might have been changed transiently. Here we @@ -3196,8 +3213,6 @@ AbortSubTransaction(void) */ XactReadOnly = s->prevXactReadOnly; - CommandCounterIncrement(); - RESUME_INTERRUPTS(); } @@ -3481,7 +3496,7 @@ xactGetCommittedChildren(TransactionId **ptr) foreach(p, s->childXids) { - TransactionId child = lfirst_int(p); + TransactionId child = lfirst_xid(p); *children++ = child; } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 581799fc5f5..9cb3c56110f 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.234 2004/06/18 06:13:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.235 2004/08/01 17:32:14 tgl Exp $ * * * INTERFACE ROUTINES @@ -1646,7 +1646,6 @@ reindex_index(Oid indexId) { Relation iRel, heapRelation; - IndexInfo *indexInfo; Oid heapId; bool inplace; @@ -1671,8 +1670,6 @@ reindex_index(Oid indexId) /* Open and lock the parent heap relation */ heapRelation = heap_open(heapId, AccessExclusiveLock); - SetReindexProcessing(heapId, indexId); - /* * If it's a shared index, we must do inplace processing (because we * have no way to update relfilenode in other databases). Otherwise @@ -1690,36 +1687,51 @@ reindex_index(Oid indexId) errmsg("shared index \"%s\" can only be reindexed in stand-alone mode", RelationGetRelationName(iRel)))); - /* Fetch info needed for index_build */ - indexInfo = BuildIndexInfo(iRel); - - if (inplace) + PG_TRY(); { + IndexInfo *indexInfo; + + /* Suppress use of the target index while rebuilding it */ + SetReindexProcessing(heapId, indexId); + + /* Fetch info needed for index_build */ + indexInfo = BuildIndexInfo(iRel); + + if (inplace) + { + /* + * Release any buffers associated with this index. If they're + * dirty, they're just dropped without bothering to flush to disk. + */ + DropRelationBuffers(iRel); + + /* Now truncate the actual data */ + RelationTruncate(iRel, 0); + } + else + { + /* + * We'll build a new physical relation for the index. + */ + setNewRelfilenode(iRel); + } + + /* Initialize the index and rebuild */ + index_build(heapRelation, iRel, indexInfo); + /* - * Release any buffers associated with this index. If they're - * dirty, they're just dropped without bothering to flush to disk. + * index_build will close both the heap and index relations (but not + * give up the locks we hold on them). So we're done. */ - DropRelationBuffers(iRel); - - /* Now truncate the actual data */ - RelationTruncate(iRel, 0); } - else + PG_CATCH(); { - /* - * We'll build a new physical relation for the index. - */ - setNewRelfilenode(iRel); + /* Make sure flag gets cleared on error exit */ + ResetReindexProcessing(); + PG_RE_THROW(); } - - /* Initialize the index and rebuild */ - index_build(heapRelation, iRel, indexInfo); - - /* - * index_build will close both the heap and index relations (but not - * give up the locks we hold on them). So we're done. - */ - SetReindexProcessing(InvalidOid, InvalidOid); + PG_END_TRY(); + ResetReindexProcessing(); } /* diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c index f5e909c672c..57e39da4a4d 100644 --- a/src/backend/storage/ipc/sinval.c +++ b/src/backend/storage/ipc/sinval.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.66 2004/07/01 03:13:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.67 2004/08/01 17:32:16 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,6 +28,30 @@ #include "miscadmin.h" +#ifdef XIDCACHE_DEBUG + +/* counters for XidCache measurement */ +static long xc_by_recent_xmin = 0; +static long xc_by_main_xid = 0; +static long xc_by_child_xid = 0; +static long xc_slow_answer = 0; + +#define xc_by_recent_xmin_inc() (xc_by_recent_xmin++) +#define xc_by_main_xid_inc() (xc_by_main_xid++) +#define xc_by_child_xid_inc() (xc_by_child_xid++) +#define xc_slow_answer_inc() (xc_slow_answer++) + +static void DisplayXidCache(int code, Datum arg); + +#else /* !XIDCACHE_DEBUG */ + +#define xc_by_recent_xmin_inc() ((void) 0) +#define xc_by_main_xid_inc() ((void) 0) +#define xc_by_child_xid_inc() ((void) 0) +#define xc_slow_answer_inc() ((void) 0) + +#endif /* XIDCACHE_DEBUG */ + /* * Because backends sitting idle will not be reading sinval events, we * need a way to give an idle backend a swift kick in the rear and make @@ -80,6 +104,10 @@ InitBackendSharedInvalidationState(void) ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("sorry, too many clients already"))); + +#ifdef XIDCACHE_DEBUG + on_proc_exit(DisplayXidCache, (Datum) 0); +#endif /* XIDCACHE_DEBUG */ } /* @@ -393,7 +421,6 @@ ProcessCatchupEvent(void) * to the doomed database, so additional interlocking is needed during * backend startup. */ - bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself) { @@ -429,7 +456,41 @@ DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself) } /* - * TransactionIdIsInProgress -- is given transaction running by some backend + * IsBackendPid -- is a given pid a running backend + */ +bool +IsBackendPid(int pid) +{ + bool result = false; + SISeg *segP = shmInvalBuffer; + ProcState *stateP = segP->procState; + int index; + + LWLockAcquire(SInvalLock, LW_SHARED); + + for (index = 0; index < segP->lastBackend; index++) + { + SHMEM_OFFSET pOffset = stateP[index].procStruct; + + if (pOffset != INVALID_OFFSET) + { + PGPROC *proc = (PGPROC *) MAKE_PTR(pOffset); + + if (proc->pid == pid) + { + result = true; + break; + } + } + } + + LWLockRelease(SInvalLock); + + return result; +} + +/* + * TransactionIdIsInProgress -- is given transaction running in some backend * * There are three possibilities for finding a running transaction: * @@ -439,13 +500,15 @@ DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself) * 2. the given Xid is one of the cached subxact Xids in the PGPROC array. * We can find this out cheaply too. * - * 3. Search the SubTrans tree. This is the slowest, but sadly it has to be - * done always if the other two failed. + * 3. Search the SubTrans tree to find the Xid's topmost parent, and then + * see if that is running according to PGPROC. This is the slowest, but + * sadly it has to be done always if the other two failed, unless we see + * that the cached subxact sets are complete (none have overflowed). * - * SInvalLock has to be held while we do 1 and 2. If we save all the Xids + * SInvalLock has to be held while we do 1 and 2. If we save the top Xids * while doing 1, we can release the SInvalLock while we do 3. This buys back - * some concurrency (we can't retrieve the main Xids from PGPROC again anyway, - * see GetNewTransactionId) + * some concurrency (we can't retrieve the main Xids from PGPROC again anyway; + * see GetNewTransactionId). */ bool TransactionIdIsInProgress(TransactionId xid) @@ -453,13 +516,27 @@ TransactionIdIsInProgress(TransactionId xid) bool result = false; SISeg *segP = shmInvalBuffer; ProcState *stateP = segP->procState; - int i; + int i, + j; int nxids = 0; TransactionId *xids; + TransactionId topxid; + bool locked; + + /* + * Don't bother checking a very old transaction. + */ + if (TransactionIdPrecedes(xid, RecentGlobalXmin)) + { + xc_by_recent_xmin_inc(); + return false; + } - xids = (TransactionId *)palloc(sizeof(TransactionId) * segP->maxBackends); + /* Get workspace to remember main XIDs in */ + xids = (TransactionId *) palloc(sizeof(TransactionId) * segP->maxBackends); LWLockAcquire(SInvalLock, LW_SHARED); + locked = true; for (i = 0; i < segP->lastBackend; i++) { @@ -472,101 +549,90 @@ TransactionIdIsInProgress(TransactionId xid) /* Fetch xid just once - see GetNewTransactionId */ TransactionId pxid = proc->xid; + if (!TransactionIdIsValid(pxid)) + continue; + /* - * check the main Xid (step 1 above) + * Step 1: check the main Xid */ if (TransactionIdEquals(pxid, xid)) { + xc_by_main_xid_inc(); result = true; - break; + goto result_known; } /* - * save the main Xid for step 3. + * We can ignore main Xids that are younger than the target Xid, + * since the target could not possibly be their child. */ - xids[nxids++] = pxid; - -#ifdef NOT_USED - FIXME -- waiting to save the Xids in PGPROC ... + if (TransactionIdPrecedes(xid, pxid)) + continue; /* - * check the saved Xids array (step 2) + * Step 2: check the cached child-Xids arrays */ - for (j = 0; j < PGPROC_MAX_SAVED_XIDS; j++) + for (j = proc->subxids.nxids - 1; j >= 0; j--) { - pxid = proc->savedxids[j]; - - if (!TransactionIdIsValid(pxids)) - break; + /* Fetch xid just once - see GetNewTransactionId */ + TransactionId cxid = proc->subxids.xids[j]; - if (TransactionIdEquals(pxid, xid)) + if (TransactionIdEquals(cxid, xid)) { + xc_by_child_xid_inc(); result = true; - break; + goto result_known; } } -#endif - if (result) - break; + /* + * Save the main Xid for step 3. We only need to remember main + * Xids that have uncached children. (Note: there is no race + * condition here because the overflowed flag cannot be cleared, + * only set, while we hold SInvalLock. So we can't miss an Xid + * that we need to worry about.) + */ + if (proc->subxids.overflowed) + xids[nxids++] = pxid; } } LWLockRelease(SInvalLock); + locked = false; /* - * Step 3: have to check pg_subtrans. Use the saved Xids. - * - * XXX Could save the cached Xids too for further improvement. + * If none of the relevant caches overflowed, we know the Xid is + * not running without looking at pg_subtrans. */ - if (!result) - { - /* this is a potentially expensive call. */ - xid = SubTransGetTopmostTransaction(xid); - - Assert(TransactionIdIsValid(xid)); - - /* - * We don't care if it aborted, because if it did, we won't find - * it in the array. - */ - for (i = 0; i < nxids; i++) - { - if (TransactionIdEquals(xids[i], xid)) - { - result = true; - break; - } - } - } - - pfree(xids); + if (nxids == 0) + goto result_known; - return result; -} - -/* - * IsBackendPid -- is a given pid a running backend - */ -bool -IsBackendPid(int pid) -{ - bool result = false; - SISeg *segP = shmInvalBuffer; - ProcState *stateP = segP->procState; - int index; + /* + * Step 3: have to check pg_subtrans. + * + * At this point, we know it's either a subtransaction of one of the + * Xids in xids[], or it's not running. If it's an already-failed + * subtransaction, we want to say "not running" even though its parent may + * still be running. So first, check pg_clog to see if it's been aborted. + */ + xc_slow_answer_inc(); - LWLockAcquire(SInvalLock, LW_SHARED); + if (TransactionIdDidAbort(xid)) + goto result_known; - for (index = 0; index < segP->lastBackend; index++) + /* + * It isn't aborted, so check whether the transaction tree it + * belongs to is still running (or, more precisely, whether it + * was running when this routine started -- note that we already + * released SInvalLock). + */ + topxid = SubTransGetTopmostTransaction(xid); + Assert(TransactionIdIsValid(topxid)); + if (!TransactionIdEquals(topxid, xid)) { - SHMEM_OFFSET pOffset = stateP[index].procStruct; - - if (pOffset != INVALID_OFFSET) + for (i = 0; i < nxids; i++) { - PGPROC *proc = (PGPROC *) MAKE_PTR(pOffset); - - if (proc->pid == pid) + if (TransactionIdEquals(xids[i], topxid)) { result = true; break; @@ -574,7 +640,11 @@ IsBackendPid(int pid) } } - LWLockRelease(SInvalLock); +result_known: + if (locked) + LWLockRelease(SInvalLock); + + pfree(xids); return result; } @@ -928,3 +998,85 @@ CountEmptyBackendSlots(void) return count; } + +#define XidCacheRemove(i) \ + do { \ + MyProc->subxids.xids[i] = MyProc->subxids.xids[MyProc->subxids.nxids - 1]; \ + MyProc->subxids.nxids--; \ + } while (0) + +/* + * XidCacheRemoveRunningXids + * + * Remove a bunch of TransactionIds from the list of known-running + * subtransactions for my backend. Both the specified xid and those in + * the xids[] array (of length nxids) are removed from the subxids cache. + */ +void +XidCacheRemoveRunningXids(TransactionId xid, int nxids, TransactionId *xids) +{ + int i, j; + + Assert(!TransactionIdEquals(xid, InvalidTransactionId)); + + /* + * We must hold SInvalLock exclusively in order to remove transactions + * from the PGPROC array. (See notes in GetSnapshotData.) It's + * possible this could be relaxed since we know this routine is only + * used to abort subtransactions, but pending closer analysis we'd + * best be conservative. + */ + LWLockAcquire(SInvalLock, LW_EXCLUSIVE); + + /* + * Under normal circumstances xid and xids[] will be in increasing order, + * as will be the entries in subxids. Scan backwards to avoid O(N^2) + * behavior when removing a lot of xids. + */ + for (i = nxids - 1; i >= 0; i--) + { + TransactionId anxid = xids[i]; + + for (j = MyProc->subxids.nxids - 1; j >= 0; j--) + { + if (TransactionIdEquals(MyProc->subxids.xids[j], anxid)) + { + XidCacheRemove(j); + break; + } + } + /* We should have found it, unless the cache has overflowed */ + Assert(j >= 0 || MyProc->subxids.overflowed); + } + + for (j = MyProc->subxids.nxids - 1; j >= 0; j--) + { + if (TransactionIdEquals(MyProc->subxids.xids[j], xid)) + { + XidCacheRemove(j); + break; + } + } + /* We should have found it, unless the cache has overflowed */ + Assert(j >= 0 || MyProc->subxids.overflowed); + + LWLockRelease(SInvalLock); +} + +#ifdef XIDCACHE_DEBUG + +/* + * on_proc_exit hook to print stats about effectiveness of XID cache + */ +static void +DisplayXidCache(int code, Datum arg) +{ + fprintf(stderr, + "XidCache: xmin: %ld, mainxid: %ld, childxid: %ld, slow: %ld\n", + xc_by_recent_xmin, + xc_by_main_xid, + xc_by_child_xid, + xc_slow_answer); +} + +#endif /* XIDCACHE_DEBUG */ diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 6c32c6c3d78..d8d718a1823 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.221 2004/07/27 05:11:03 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.222 2004/08/01 17:32:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -326,8 +326,7 @@ ProcessUtility(Node *parsetree, { /* * START TRANSACTION, as defined by SQL99: - * Identical to BEGIN, except that it takes a few - * additional options. Same code for both. + * Identical to BEGIN. Same code for both. */ case TRANS_STMT_BEGIN: case TRANS_STMT_START: diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 2ae41c013c1..e5e21e8719b 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.129 2004/07/12 00:09:06 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/init/miscinit.c,v 1.130 2004/08/01 17:32:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -115,19 +115,29 @@ ReindexIsProcessingIndex(Oid indexOid) /* * SetReindexProcessing * Set flag that specified heap/index are being reindexed. - * Pass InvalidOid to indicate that reindexing is not active. */ void SetReindexProcessing(Oid heapOid, Oid indexOid) { - /* Args should be both, or neither, InvalidOid */ - Assert((heapOid == InvalidOid) == (indexOid == InvalidOid)); + Assert(OidIsValid(heapOid) && OidIsValid(indexOid)); /* Reindexing is not re-entrant. */ - Assert(indexOid == InvalidOid || currentlyReindexedIndex == InvalidOid); + if (OidIsValid(currentlyReindexedIndex)) + elog(ERROR, "cannot reindex while reindexing"); currentlyReindexedHeap = heapOid; currentlyReindexedIndex = indexOid; } +/* + * ResetReindexProcessing + * Unset reindexing status. + */ +void +ResetReindexProcessing(void) +{ + currentlyReindexedHeap = InvalidOid; + currentlyReindexedIndex = InvalidOid; +} + /* ---------------------------------------------------------------- * database path / name support stuff * ---------------------------------------------------------------- diff --git a/src/include/access/htup.h b/src/include/access/htup.h index 37ba1164ec1..971a279b9c9 100644 --- a/src/include/access/htup.h +++ b/src/include/access/htup.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.67 2004/07/11 18:01:45 tgl Exp $ + * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.68 2004/08/01 17:32:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -68,34 +68,17 @@ * object ID (if HEAP_HASOID is set in t_infomask) * user data fields * - * We store five "virtual" fields Xmin, Cmin, Xmax, Cmax, and Xvac - * in just three physical fields. Xmin is always really stored, but - * Cmin and Xmax share a field, as do Cmax and Xvac. This works because - * we know that there are only a limited number of states that a tuple can - * be in, and that Cmin and Cmax are only interesting for the lifetime of - * the inserting and deleting transactions respectively. We have the - * following possible states of a tuple: + * We store five "virtual" fields Xmin, Cmin, Xmax, Cmax, and Xvac in four + * physical fields. Xmin, Cmin and Xmax are always really stored, but + * Cmax and Xvac share a field. This works because we know that there are + * only a limited number of states that a tuple can be in, and that Cmax + * is only interesting for the lifetime of the deleting transaction. + * This assumes that VACUUM FULL never tries to move a tuple whose Cmax + * is still interesting (ie, delete-in-progress). * - * XMIN CMIN XMAX CMAX XVAC - * - * NEW (never deleted, not moved by vacuum): - * valid valid invalid invalid invalid - * - * DELETED BY CREATING XACT: - * valid valid = XMIN valid invalid - * - * DELETED BY OTHER XACT: - * valid unneeded valid valid invalid - * - * MOVED BY VACUUM FULL: - * valid unneeded maybe-valid unneeded valid - * - * This assumes that VACUUM FULL never tries to move a tuple whose Cmin or - * Cmax is still interesting (ie, insert-in-progress or delete-in-progress). - * - * This table shows that if we use an infomask bit to handle the case - * XMAX=XMIN specially, we never need to store Cmin and Xmax at the same - * time. Nor do we need to store Cmax and Xvac at the same time. + * Note that in 7.3 and 7.4 a similar idea was applied to Xmax and Cmin. + * However, with the advent of subtransactions, a tuple may need both Xmax + * and Cmin simultaneously, so this is no longer possible. * * Following the fixed header fields, the nulls bitmap is stored (beginning * at t_bits). The bitmap is *not* stored if t_infomask shows that there @@ -416,7 +399,7 @@ typedef HeapTupleData *HeapTuple; * WAL record definitions for heapam.c's WAL operations * * XLOG allows to store some information in high 4 bits of log - * record xl_info field + * record xl_info field. We use 3 for opcode and one for init bit. */ #define XLOG_HEAP_INSERT 0x00 #define XLOG_HEAP_DELETE 0x10 diff --git a/src/include/access/xact.h b/src/include/access/xact.h index 532dcf51b0e..16b7de333a4 100644 --- a/src/include/access/xact.h +++ b/src/include/access/xact.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/xact.h,v 1.68 2004/07/31 07:39:19 tgl Exp $ + * $PostgreSQL: pgsql/src/include/access/xact.h,v 1.69 2004/08/01 17:32:19 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -42,9 +42,18 @@ extern bool DefaultXactReadOnly; extern bool XactReadOnly; /* - * end-of-transaction cleanup callbacks for dynamically loaded modules + * start- and end-of-transaction callbacks for dynamically loaded modules */ -typedef void (*EOXactCallback) (bool isCommit, void *arg); +typedef enum +{ + XACT_EVENT_ABORT, + XACT_EVENT_COMMIT, + XACT_EVENT_START_SUB, + XACT_EVENT_ABORT_SUB, + XACT_EVENT_COMMIT_SUB +} XactEvent; + +typedef void (*XactCallback) (XactEvent event, TransactionId parentXid, void *arg); /* ---------------- @@ -118,8 +127,8 @@ extern void AbortOutOfAnyTransaction(void); extern void PreventTransactionChain(void *stmtNode, const char *stmtType); extern void RequireTransactionChain(void *stmtNode, const char *stmtType); extern bool IsInTransactionChain(void *stmtNode); -extern void RegisterEOXactCallback(EOXactCallback callback, void *arg); -extern void UnregisterEOXactCallback(EOXactCallback callback, void *arg); +extern void RegisterXactCallback(XactCallback callback, void *arg); +extern void UnregisterXactCallback(XactCallback callback, void *arg); extern void RecordTransactionCommit(void); diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 0a508861b27..3f7c0946d74 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -13,7 +13,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.163 2004/06/18 06:14:10 tgl Exp $ + * $PostgreSQL: pgsql/src/include/miscadmin.h,v 1.164 2004/08/01 17:32:20 tgl Exp $ * * NOTES * some of the information in this file should be moved to other files. @@ -308,6 +308,7 @@ extern void BaseInit(void); extern void IgnoreSystemIndexes(bool mode); extern bool IsIgnoringSystemIndexes(void); extern void SetReindexProcessing(Oid heapOid, Oid indexOid); +extern void ResetReindexProcessing(void); extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingIndex(Oid indexOid); extern void CreateDataDirLockFile(const char *datadir, bool amPostmaster); diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h index c2411bef073..4f7f39003ad 100644 --- a/src/include/storage/proc.h +++ b/src/include/storage/proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.71 2004/07/21 20:34:49 momjian Exp $ + * $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.72 2004/08/01 17:32:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,6 +21,25 @@ /* + * Each backend advertises up to PGPROC_MAX_CACHED_SUBXIDS TransactionIds + * for non-aborted subtransactions of its current top transaction. These + * have to be treated as running XIDs by other backends. + * + * We also keep track of whether the cache overflowed (ie, the transaction has + * generated at least one subtransaction that didn't fit in the cache). + * If none of the caches have overflowed, we can assume that an XID that's not + * listed anywhere in the PGPROC array is not a running transaction. Else we + * have to look at pg_subtrans. + */ +#define PGPROC_MAX_CACHED_SUBXIDS 64 /* XXX guessed-at value */ + +struct XidCache { + bool overflowed; + int nxids; + TransactionId xids[PGPROC_MAX_CACHED_SUBXIDS]; +}; + +/* * Each backend has a PGPROC struct in shared memory. There is also a list of * currently-unused PGPROC structs that will be reallocated to new backends. * @@ -68,6 +87,8 @@ struct PGPROC SHM_QUEUE procHolders; /* list of PROCLOCK objects for locks held * or awaited by this backend */ + + struct XidCache subxids; /* cache for subtransaction XIDs */ }; /* NOTE: "typedef struct PGPROC PGPROC" appears in storage/lock.h. */ diff --git a/src/include/storage/sinval.h b/src/include/storage/sinval.h index 84769200504..5ac995a29d4 100644 --- a/src/include/storage/sinval.h +++ b/src/include/storage/sinval.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/storage/sinval.h,v 1.35 2004/06/02 21:29:29 momjian Exp $ + * $PostgreSQL: pgsql/src/include/storage/sinval.h,v 1.36 2004/08/01 17:32:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -104,6 +104,9 @@ extern int CountEmptyBackendSlots(void); /* Use "struct PGPROC", not PGPROC, to avoid including proc.h here */ extern struct PGPROC *BackendIdGetProc(BackendId procId); +extern void XidCacheRemoveRunningXids(TransactionId xid, + int nxids, TransactionId *xids); + /* signal handler for catchup events (SIGUSR1) */ extern void CatchupInterruptHandler(SIGNAL_ARGS); diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 4c579223d91..f075b96c0fc 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.111 2004/07/31 23:04:56 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.112 2004/08/01 17:32:21 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -4216,26 +4216,44 @@ exec_set_found(PLpgSQL_execstate * estate, bool state) * structs that are using it as no longer active. */ void -plpgsql_eoxact(bool isCommit, void *arg) +plpgsql_xact_cb(XactEvent event, TransactionId parentXid, void *arg) { PLpgSQL_expr *expr; PLpgSQL_expr *enext; - /* Mark all active exprs as inactive */ - for (expr = active_simple_exprs; expr; expr = enext) + switch (event) { - enext = expr->expr_simple_next; - expr->expr_simple_state = NULL; - expr->expr_simple_next = NULL; + /* + * Nothing to do at subtransaction events + * + * XXX really? Maybe subtransactions need to have their own + * simple_eval_estate? It would get a lot messier, so for now + * let's assume we don't need that. + */ + case XACT_EVENT_START_SUB: + case XACT_EVENT_ABORT_SUB: + case XACT_EVENT_COMMIT_SUB: + break; + + case XACT_EVENT_ABORT: + case XACT_EVENT_COMMIT: + /* Mark all active exprs as inactive */ + for (expr = active_simple_exprs; expr; expr = enext) + { + enext = expr->expr_simple_next; + expr->expr_simple_state = NULL; + expr->expr_simple_next = NULL; + } + active_simple_exprs = NULL; + /* + * If we are doing a clean transaction shutdown, free the EState + * (so that any remaining resources will be released correctly). + * In an abort, we expect the regular abort recovery procedures to + * release everything of interest. + */ + if (event == XACT_EVENT_COMMIT && simple_eval_estate) + FreeExecutorState(simple_eval_estate); + simple_eval_estate = NULL; + break; } - active_simple_exprs = NULL; - /* - * If we are doing a clean transaction shutdown, free the EState - * (so that any remaining resources will be released correctly). - * In an abort, we expect the regular abort recovery procedures to - * release everything of interest. - */ - if (isCommit && simple_eval_estate) - FreeExecutorState(simple_eval_estate); - simple_eval_estate = NULL; } diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c index 5f6a83c11d6..d4e892eb719 100644 --- a/src/pl/plpgsql/src/pl_handler.c +++ b/src/pl/plpgsql/src/pl_handler.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.22 2004/07/31 20:55:44 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.23 2004/08/01 17:32:22 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -66,7 +66,7 @@ plpgsql_init(void) plpgsql_HashTableInit(); - RegisterEOXactCallback(plpgsql_eoxact, NULL); + RegisterXactCallback(plpgsql_xact_cb, NULL); plpgsql_firstcall = 0; } diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index d57d4a7025e..e054d5b25f1 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.49 2004/07/31 23:04:56 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.50 2004/08/01 17:32:22 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -702,7 +702,7 @@ extern Datum plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo); extern HeapTuple plpgsql_exec_trigger(PLpgSQL_function * func, TriggerData *trigdata); -extern void plpgsql_eoxact(bool isCommit, void *arg); +extern void plpgsql_xact_cb(XactEvent event, TransactionId parentXid, void *arg); /* ---------- * Functions for the dynamic string handling in pl_funcs.c |