diff options
-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 5bca2233279..33da6632cae 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.227 2008/01/02 23:34:42 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.227.2.1 2008/10/25 03:32:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2596,7 +2596,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, we use its result relation info to avoid repeated * openings and closing of trigger target relations. If it is NULL, we @@ -2608,11 +2610,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; @@ -2638,6 +2641,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) @@ -2699,7 +2703,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 @@ -2712,6 +2719,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 */ @@ -2831,8 +2839,6 @@ AfterTriggerBeginQuery(void) void AfterTriggerEndQuery(EState *estate) { - AfterTriggerEventList *events; - /* Must be inside a transaction */ Assert(afterTriggers != NULL); @@ -2849,18 +2855,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--; @@ -2906,7 +2914,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); @@ -3471,7 +3479,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()); } } |