diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/trigger.c | 30 |
1 files changed, 19 insertions, 11 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 75e0cf150d8..0bc6f10cc79 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.210.2.5 2008/05/27 21:13:25 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.210.2.6 2008/10/25 03:32:51 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2263,7 +2263,9 @@ afterTriggerMarkEvents(AfterTriggerEventList *events, * afterTriggerInvokeEvents() * * Scan the given event list for events that are marked as to be fired - * in the current firing cycle, and fire them. + * in the current firing cycle, and fire them. query_depth is the index in + * afterTriggers->query_stack, or -1 to examine afterTriggers->events. + * (We have to be careful here because query_stack could move under us.) * * If estate isn't NULL, then we expect that all the firable events are * for triggers of the relations included in the estate's result relation @@ -2276,11 +2278,12 @@ afterTriggerMarkEvents(AfterTriggerEventList *events, * ---------- */ static void -afterTriggerInvokeEvents(AfterTriggerEventList *events, +afterTriggerInvokeEvents(int query_depth, CommandId firing_id, EState *estate, bool delete_ok) { + AfterTriggerEventList *events; AfterTriggerEvent event, prev_event; MemoryContext per_tuple_context; @@ -2299,6 +2302,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events, ALLOCSET_DEFAULT_MAXSIZE); prev_event = NULL; + events = (query_depth >= 0) ? &afterTriggers->query_stack[query_depth] : &afterTriggers->events; event = events->head; while (event != NULL) @@ -2416,7 +2420,10 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events, if (prev_event) prev_event->ate_next = next_event; else + { + events = (query_depth >= 0) ? &afterTriggers->query_stack[query_depth] : &afterTriggers->events; events->head = next_event; + } pfree(event); } else @@ -2429,6 +2436,7 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events, } /* Update list tail pointer in case we just deleted tail event */ + events = (query_depth >= 0) ? &afterTriggers->query_stack[query_depth] : &afterTriggers->events; events->tail = prev_event; /* Release working resources */ @@ -2543,8 +2551,6 @@ AfterTriggerBeginQuery(void) void AfterTriggerEndQuery(EState *estate) { - AfterTriggerEventList *events; - /* Must be inside a transaction */ Assert(afterTriggers != NULL); @@ -2561,18 +2567,20 @@ AfterTriggerEndQuery(EState *estate) * IMMEDIATE: all events we have decided to defer will be available for it * to fire. * - * We loop in case a trigger queues more events. + * We loop in case a trigger queues more events at the same query level + * (is that even possible?). Be careful here: firing a trigger could + * result in query_stack being repalloc'd, so we can't save its address + * across afterTriggerInvokeEvents calls. * * If we find no firable events, we don't have to increment * firing_counter. */ - events = &afterTriggers->query_stack[afterTriggers->query_depth]; - while (afterTriggerMarkEvents(events, &afterTriggers->events, true)) + while (afterTriggerMarkEvents(&afterTriggers->query_stack[afterTriggers->query_depth], &afterTriggers->events, true)) { CommandId firing_id = afterTriggers->firing_counter++; /* OK to delete the immediate events after processing them */ - afterTriggerInvokeEvents(events, firing_id, estate, true); + afterTriggerInvokeEvents(afterTriggers->query_depth, firing_id, estate, true); } afterTriggers->query_depth--; @@ -2618,7 +2626,7 @@ AfterTriggerFireDeferred(void) { CommandId firing_id = afterTriggers->firing_counter++; - afterTriggerInvokeEvents(events, firing_id, NULL, true); + afterTriggerInvokeEvents(-1, firing_id, NULL, true); } Assert(events->head == NULL); @@ -3183,7 +3191,7 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt) * but we'd better not if inside a subtransaction, since the * subtransaction could later get rolled back. */ - afterTriggerInvokeEvents(events, firing_id, NULL, + afterTriggerInvokeEvents(-1, firing_id, NULL, !IsSubTransaction()); } } |