*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.228 2006/11/05 22:42:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.229 2006/11/23 01:14:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
static char *prepareGID;
+/*
+ * Private context for transaction-abort work --- we reserve space for this
+ * at startup to ensure that AbortTransaction and AbortSubTransaction can work
+ * when we've run out of memory.
+ */
+static MemoryContext TransactionAbortContext = NULL;
/*
* List of add-on start- and end-of-xact callbacks
{
TransactionState s = CurrentTransactionState;
+ /*
+ * If this is the first time through, create a private context for
+ * AbortTransaction to work in. By reserving some space now, we can
+ * insulate AbortTransaction from out-of-memory scenarios. Like
+ * ErrorContext, we set it up with slow growth rate and a nonzero
+ * minimum size, so that space will be reserved immediately.
+ */
+ if (TransactionAbortContext == NULL)
+ TransactionAbortContext =
+ AllocSetContextCreate(TopMemoryContext,
+ "TransactionAbortContext",
+ 32 * 1024,
+ 32 * 1024,
+ 32 * 1024);
+
/*
* We shouldn't have a transaction context already.
*/
AtAbort_Memory(void)
{
/*
- * Make sure we are in a valid context (not a child of
- * TopTransactionContext...). Note that it is possible for this code to
- * be called when we aren't in a transaction at all; go directly to
- * TopMemoryContext in that case.
+ * Switch into TransactionAbortContext, which should have some free
+ * space even if nothing else does. We'll work in this context until
+ * we've finished cleaning up.
+ *
+ * It is barely possible to get here when we've not been able to create
+ * TransactionAbortContext yet; if so use TopMemoryContext.
*/
- if (TopTransactionContext != NULL)
- {
- MemoryContextSwitchTo(TopTransactionContext);
-
- /*
- * We do not want to destroy the transaction's global state yet, so we
- * can't free any memory here.
- */
- }
+ if (TransactionAbortContext != NULL)
+ MemoryContextSwitchTo(TransactionAbortContext);
else
MemoryContextSwitchTo(TopMemoryContext);
}
static void
AtSubAbort_Memory(void)
{
- Assert(TopTransactionContext != NULL);
+ Assert(TransactionAbortContext != NULL);
- MemoryContextSwitchTo(TopTransactionContext);
+ MemoryContextSwitchTo(TransactionAbortContext);
}
static void
AtCleanup_Memory(void)
{
+ Assert(CurrentTransactionState->parent == NULL);
+
/*
* Now that we're "out" of a transaction, have the system allocate things
* in the top memory context instead of per-transaction contexts.
*/
MemoryContextSwitchTo(TopMemoryContext);
- Assert(CurrentTransactionState->parent == NULL);
+ /*
+ * Clear the special abort context for next time.
+ */
+ if (TransactionAbortContext != NULL)
+ MemoryContextResetAndDeleteChildren(TransactionAbortContext);
/*
* Release all transaction-local memory.
MemoryContextSwitchTo(s->parent->curTransactionContext);
CurTransactionContext = s->parent->curTransactionContext;
+ /*
+ * Clear the special abort context for next time.
+ */
+ if (TransactionAbortContext != NULL)
+ MemoryContextResetAndDeleteChildren(TransactionAbortContext);
+
/*
* Delete the subxact local memory contexts. Its CurTransactionContext can
* go too (note this also kills CurTransactionContexts from any children
/* Prevent cancel/die interrupt while cleaning up */
HOLD_INTERRUPTS();
+ /* Make sure we have a valid memory context and resource owner */
+ AtAbort_Memory();
+ AtAbort_ResourceOwner();
+
/*
* Release any LW locks we might be holding as quickly as possible.
* (Regular locks, however, must be held till we finish aborting.)
*/
s->state = TRANS_ABORT;
- /* Make sure we have a valid memory context and resource owner */
- AtAbort_Memory();
- AtAbort_ResourceOwner();
-
/*
* Reset user id which might have been changed transiently. We cannot use
* s->currentUser, since it may not be set yet; instead rely on internal
{
TransactionState s = CurrentTransactionState;
- ShowTransactionState("AbortSubTransaction");
-
- if (s->state != TRANS_INPROGRESS)
- elog(WARNING, "AbortSubTransaction while in %s state",
- TransStateAsString(s->state));
-
+ /* Prevent cancel/die interrupt while cleaning up */
HOLD_INTERRUPTS();
- s->state = TRANS_ABORT;
+ /* Make sure we have a valid memory context and resource owner */
+ AtSubAbort_Memory();
+ AtSubAbort_ResourceOwner();
/*
* Release any LW locks we might be holding as quickly as possible.
LockWaitCancel();
/*
- * do abort processing
+ * check the current transaction state
*/
- AtSubAbort_Memory();
- AtSubAbort_ResourceOwner();
+ ShowTransactionState("AbortSubTransaction");
+
+ if (s->state != TRANS_INPROGRESS)
+ elog(WARNING, "AbortSubTransaction while in %s state",
+ TransStateAsString(s->state));
+
+ s->state = TRANS_ABORT;
/*
* We can skip all this stuff if the subxact failed before creating a
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.209 2006/10/04 00:29:51 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.210 2006/11/23 01:14:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* during the current transaction tree. (BEFORE triggers are fired
* immediately so we don't need any persistent state about them.) The struct
* and most of its subsidiary data are kept in TopTransactionContext; however
- * the individual event records are kept in CurTransactionContext, so that
- * they will easily go away during subtransaction abort.
+ * the individual event records are kept in separate contexts, to make them
+ * easy to delete during subtransaction abort.
*
* Because the list of pending events can grow large, we go to some effort
* to minimize memory consumption. We do not use the generic List mechanism
* events is the current list of deferred events. This is global across
* all subtransactions of the current transaction. In a subtransaction
* abort, we know that the events added by the subtransaction are at the
- * end of the list, so it is relatively easy to discard them.
+ * end of the list, so it is relatively easy to discard them. The event
+ * structs themselves are stored in event_cxt if generated by the top-level
+ * transaction, else in per-subtransaction contexts identified by the
+ * entries in cxt_stack.
*
* query_depth is the current depth of nested AfterTriggerBeginQuery calls
* (-1 when the stack is empty).
int query_depth; /* current query list index */
AfterTriggerEventList *query_stack; /* events pending from each query */
int maxquerydepth; /* allocated len of above array */
+ MemoryContext event_cxt; /* top transaction's event context, if any */
+ MemoryContext *cxt_stack; /* per-subtransaction event contexts */
/* these fields are just for resetting at subtrans abort: */
8 * sizeof(AfterTriggerEventList));
afterTriggers->maxquerydepth = 8;
+ /* Context for events is created only when needed */
+ afterTriggers->event_cxt = NULL;
+
/* Subtransaction stack is empty until/unless needed */
+ afterTriggers->cxt_stack = NULL;
afterTriggers->state_stack = NULL;
afterTriggers->events_stack = NULL;
afterTriggers->depth_stack = NULL;
* Forget everything we know about AFTER triggers.
*
* Since all the info is in TopTransactionContext or children thereof, we
- * need do nothing special to reclaim memory.
+ * don't really need to do anything to reclaim memory. However, the
+ * pending-events list could be large, and so it's useful to discard
+ * it as soon as possible --- especially if we are aborting because we
+ * ran out of memory for the list!
+ *
+ * (Note: any event_cxts of child subtransactions could also be
+ * deleted here, but we have no convenient way to find them, so we
+ * leave it to TopTransactionContext reset to clean them up.)
*/
+ if (afterTriggers && afterTriggers->event_cxt)
+ MemoryContextDelete(afterTriggers->event_cxt);
+
afterTriggers = NULL;
}
return;
/*
- * Allocate more space in the stacks if needed.
+ * Allocate more space in the stacks if needed. (Note: because the
+ * minimum nest level of a subtransaction is 2, we waste the first
+ * couple entries of each array; not worth the notational effort to
+ * avoid it.)
*/
while (my_level >= afterTriggers->maxtransdepth)
{
old_cxt = MemoryContextSwitchTo(TopTransactionContext);
#define DEFTRIG_INITALLOC 8
+ afterTriggers->cxt_stack = (MemoryContext *)
+ palloc(DEFTRIG_INITALLOC * sizeof(MemoryContext));
afterTriggers->state_stack = (SetConstraintState *)
palloc(DEFTRIG_INITALLOC * sizeof(SetConstraintState));
afterTriggers->events_stack = (AfterTriggerEventList *)
/* repalloc will keep the stacks in the same context */
int new_alloc = afterTriggers->maxtransdepth * 2;
+ afterTriggers->cxt_stack = (MemoryContext *)
+ repalloc(afterTriggers->cxt_stack,
+ new_alloc * sizeof(MemoryContext));
afterTriggers->state_stack = (SetConstraintState *)
repalloc(afterTriggers->state_stack,
new_alloc * sizeof(SetConstraintState));
/*
* Push the current information into the stack. The SET CONSTRAINTS state
- * is not saved until/unless changed.
+ * is not saved until/unless changed. Likewise, we don't make a
+ * per-subtransaction event context until needed.
*/
+ afterTriggers->cxt_stack[my_level] = NULL;
afterTriggers->state_stack[my_level] = NULL;
afterTriggers->events_stack[my_level] = afterTriggers->events;
afterTriggers->depth_stack[my_level] = afterTriggers->query_depth;
else
{
/*
- * Aborting --- restore the pointers from the stacks.
+ * Aborting. We don't really need to release the subxact's event_cxt,
+ * since it will go away anyway when CurTransactionContext gets reset,
+ * but doing so early in subxact abort helps free space we might need.
+ *
+ * (Note: any event_cxts of child subtransactions could also be
+ * deleted here, but we have no convenient way to find them, so we
+ * leave it to CurTransactionContext reset to clean them up.)
+ */
+ if (afterTriggers->cxt_stack[my_level])
+ {
+ MemoryContextDelete(afterTriggers->cxt_stack[my_level]);
+ /* avoid double delete if repeated aborts */
+ afterTriggers->cxt_stack[my_level] = NULL;
+ }
+
+ /*
+ * Restore the pointers from the stacks.
*/
afterTriggers->events = afterTriggers->events_stack[my_level];
afterTriggers->query_depth = afterTriggers->depth_stack[my_level];
if (afterTriggers->events.tail != NULL)
afterTriggers->events.tail->ate_next = NULL;
- /*
- * We don't need to free the subtransaction's items, since the
- * CurTransactionContext will be reset shortly.
- */
-
/*
* Restore the trigger state. If the saved state is NULL, then this
* subxact didn't save it, so it doesn't need restoring.
{
Relation rel = relinfo->ri_RelationDesc;
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
+ int my_level = GetCurrentTransactionNestLevel();
+ MemoryContext *cxtptr;
AfterTriggerEvent new_event;
int i;
int ntriggers;
}
/*
- * Create a new event. We use the CurTransactionContext so the event
- * will automatically go away if the subtransaction aborts.
+ * If we don't yet have an event context for the current (sub)xact,
+ * create one. Make it a child of CurTransactionContext to ensure it
+ * will go away if the subtransaction aborts.
+ */
+ if (my_level > 1) /* subtransaction? */
+ {
+ Assert(my_level < afterTriggers->maxtransdepth);
+ cxtptr = &afterTriggers->cxt_stack[my_level];
+ }
+ else
+ cxtptr = &afterTriggers->event_cxt;
+ if (*cxtptr == NULL)
+ *cxtptr = AllocSetContextCreate(CurTransactionContext,
+ "AfterTriggerEvents",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+
+ /*
+ * Create a new event.
*/
new_event = (AfterTriggerEvent)
- MemoryContextAlloc(CurTransactionContext,
- sizeof(AfterTriggerEventData));
+ MemoryContextAlloc(*cxtptr, sizeof(AfterTriggerEventData));
new_event->ate_next = NULL;
new_event->ate_event =
(event & TRIGGER_EVENT_OPMASK) |