diff options
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r-- | src/backend/commands/trigger.c | 93 |
1 files changed, 76 insertions, 17 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 1ed15614ce4..58d8cbabfd4 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * 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 $ * *------------------------------------------------------------------------- */ @@ -1801,8 +1801,8 @@ ltrmark:; * 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 @@ -1889,7 +1889,10 @@ typedef struct AfterTriggerEventList * 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). @@ -1931,6 +1934,8 @@ typedef struct AfterTriggersData 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: */ @@ -2464,7 +2469,11 @@ AfterTriggerBeginXact(void) 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; @@ -2626,8 +2635,18 @@ AfterTriggerEndXact(bool isCommit) * 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; } @@ -2649,7 +2668,10 @@ AfterTriggerBeginSubXact(void) 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) { @@ -2660,6 +2682,8 @@ AfterTriggerBeginSubXact(void) 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 *) @@ -2677,6 +2701,9 @@ AfterTriggerBeginSubXact(void) /* 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)); @@ -2695,8 +2722,10 @@ AfterTriggerBeginSubXact(void) /* * 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; @@ -2742,7 +2771,23 @@ AfterTriggerEndSubXact(bool isCommit) 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]; @@ -2754,11 +2799,6 @@ AfterTriggerEndSubXact(bool isCommit) 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. */ @@ -3204,6 +3244,8 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, { Relation rel = relinfo->ri_RelationDesc; TriggerDesc *trigdesc = relinfo->ri_TrigDesc; + int my_level = GetCurrentTransactionNestLevel(); + MemoryContext *cxtptr; AfterTriggerEvent new_event; int i; int ntriggers; @@ -3294,12 +3336,29 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, } /* - * 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) | |