* Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.177.4.5 2008/05/27 21:13:50 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.177.4.6 2008/10/25 03:33:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
  * 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.)
  *
  *     When delete_ok is TRUE, it's okay to delete fully-processed events.
  *     The events list pointers are updated.
  * ----------
  */
 static void
-afterTriggerInvokeEvents(AfterTriggerEventList *events,
+afterTriggerInvokeEvents(int query_depth,
                                                 CommandId firing_id,
                                                 bool delete_ok)
 {
+       AfterTriggerEventList *events;
        AfterTriggerEvent event,
                                prev_event;
        MemoryContext per_tuple_context;
                                                          ALLOCSET_DEFAULT_MAXSIZE);
 
        prev_event = NULL;
+       events = (query_depth >= 0) ? &afterTriggers->query_stack[query_depth] : &afterTriggers->events;
        event = events->head;
 
        while (event != NULL)
                        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
        }
 
        /* 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 */
 void
 AfterTriggerEndQuery(void)
 {
-       AfterTriggerEventList *events;
-
        /* Must be inside a transaction */
        Assert(afterTriggers != NULL);
 
         * SET CONSTRAINTS ... 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, true);
+               afterTriggerInvokeEvents(afterTriggers->query_depth, firing_id, true);
        }
 
        afterTriggers->query_depth--;
        {
                CommandId               firing_id = afterTriggers->firing_counter++;
 
-               afterTriggerInvokeEvents(events, firing_id, true);
+               afterTriggerInvokeEvents(-1, firing_id, true);
        }
 
        Assert(events->head == NULL);
                         * level, but we'd better not if inside a subtransaction, since
                         * the subtransaction could later get rolled back.
                         */
-                       afterTriggerInvokeEvents(events, firing_id,
+                       afterTriggerInvokeEvents(-1, firing_id,
                                                                         !IsSubTransaction());
                }
        }