On Sat, Jun 26, 2004 at 07:56:09PM -0400, Tom Lane wrote:

> BTW, it would help to know what parts of the patch you intend to work on
> over the next couple of days.  I'm reviewing and editorializing right
> now with intent to commit soon, so it would be good if we can avoid
> tromping on each others' feet.

Oops, I forgot that I had inadvertently left a kludge in
commands/trigger.c.  Please use this patch for this file instead.

-- 
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
Jude: I wish humans laid eggs
Ringlord: Why would you want humans to lay eggs?
Jude: So I can eat them
Index: include/commands/trigger.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/commands/trigger.h,v
retrieving revision 1.45
diff -c -r1.45 trigger.h
*** include/commands/trigger.h  29 Nov 2003 22:40:59 -0000      1.45
--- include/commands/trigger.h  25 Jun 2004 22:59:40 -0000
***************
*** 151,198 ****
                                         ItemPointer tupleid,
                                         HeapTuple newtuple);
  
- 
- /*
-  * Deferred trigger stuff
-  */
- typedef struct DeferredTriggerStatusData
- {
-       Oid                     dts_tgoid;
-       bool            dts_tgisdeferred;
- } DeferredTriggerStatusData;
- 
- typedef struct DeferredTriggerStatusData *DeferredTriggerStatus;
- 
- typedef struct DeferredTriggerEventItem
- {
-       Oid                     dti_tgoid;
-       int32           dti_state;
- } DeferredTriggerEventItem;
- 
- typedef struct DeferredTriggerEventData *DeferredTriggerEvent;
- 
- typedef struct DeferredTriggerEventData
- {
-       DeferredTriggerEvent dte_next;          /* list link */
-       int32           dte_event;
-       Oid                     dte_relid;
-       ItemPointerData dte_oldctid;
-       ItemPointerData dte_newctid;
-       int32           dte_n_items;
-       /* dte_item is actually a variable-size array, of length dte_n_items */
-       DeferredTriggerEventItem dte_item[1];
- } DeferredTriggerEventData;
- 
- 
  extern void DeferredTriggerInit(void);
  extern void DeferredTriggerBeginXact(void);
  extern void DeferredTriggerEndQuery(void);
  extern void DeferredTriggerEndXact(void);
  extern void DeferredTriggerAbortXact(void);
! 
  extern void DeferredTriggerSetState(ConstraintsSetStmt *stmt);
  
- 
  /*
   * in utils/adt/ri_triggers.c
   */
--- 151,165 ----
                                         ItemPointer tupleid,
                                         HeapTuple newtuple);
  
  extern void DeferredTriggerInit(void);
  extern void DeferredTriggerBeginXact(void);
  extern void DeferredTriggerEndQuery(void);
  extern void DeferredTriggerEndXact(void);
  extern void DeferredTriggerAbortXact(void);
! extern void DeferredTriggerBeginSubXact(void);
! extern void DeferredTriggerEndSubXact(bool isCommit);
  extern void DeferredTriggerSetState(ConstraintsSetStmt *stmt);
  
  /*
   * in utils/adt/ri_triggers.c
   */
Index: backend/commands/trigger.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/commands/trigger.c,v
retrieving revision 1.165
diff -c -r1.165 trigger.c
*** backend/commands/trigger.c  26 May 2004 04:41:12 -0000      1.165
--- backend/commands/trigger.c  26 Jun 2004 18:12:01 -0000
***************
*** 50,58 ****
                                        MemoryContext per_tuple_context);
  static void DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
                                   bool row_trigger, HeapTuple oldtup, HeapTuple 
newtup);
- static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
-                                       Relation rel, TriggerDesc *trigdesc, FmgrInfo 
*finfo,
-                                          MemoryContext per_tuple_context);
  
  
  /*
--- 50,55 ----
***************
*** 1639,1685 ****
  
  /* ----------
   * Deferred trigger stuff
   * ----------
   */
  
! typedef struct DeferredTriggersData
  {
!       /* Internal data is held in a per-transaction memory context */
!       MemoryContext deftrig_cxt;
!       /* ALL DEFERRED or ALL IMMEDIATE */
!       bool            deftrig_all_isset;
!       bool            deftrig_all_isdeferred;
!       /* Per trigger state */
!       List       *deftrig_trigstates;
!       /* List of pending deferred triggers. Previous comment below */
!       DeferredTriggerEvent deftrig_events;
!       DeferredTriggerEvent deftrig_events_imm;
!       DeferredTriggerEvent deftrig_event_tail;
! } DeferredTriggersData;
  
! /* ----------
!  * deftrig_events, deftrig_event_tail:
!  * The list of pending deferred trigger events during the current transaction.
   *
!  * deftrig_events is the head, deftrig_event_tail is the last entry.
!  * Because this can grow pretty large, we don't use separate List nodes,
!  * but instead thread the list through the dte_next fields of the member
!  * nodes.  Saves just a few bytes per entry, but that adds up.
!  *
!  * deftrig_events_imm holds the tail pointer as of the last
!  * deferredTriggerInvokeEvents call; we can use this to avoid rescanning
!  * entries unnecessarily.  It is NULL if deferredTriggerInvokeEvents
!  * hasn't run since the last state change.
   *
!  * XXX Need to be able to shove this data out to a file if it grows too
!  *       large...
!  * ----------
   */
  
  typedef DeferredTriggersData *DeferredTriggers;
  
  static DeferredTriggers deferredTriggers;
  
  /* ----------
   * deferredTriggerCheckState()
   *
--- 1636,1763 ----
  
  /* ----------
   * Deferred trigger stuff
+  *
+  * The DeferredTriggersData struct holds data about pending deferred
+  * trigger events during the current transaction tree, with the
+  * following fields:
+  *
+  * memcxt is a private memory context that holds most data in this
+  * struct, except the events themselves (those will be saved in
+  * CommitContext) and the state which has its own context.
+  *
+  * state keeps track of the deferred state of each trigger
+  * (including the global state)
+  *
+  * events is the head of the list of events.
+  *
+  * tail_thisxact points to the tail of the list, for the current
+  * transaction (whether main transaction or subtransaction).  We always
+  * append to the list using this pointer.
+  *
+  * events_imm points to the last element scanned by the last
+  * deferredTriggerInvokeEvents call.  We can use this to avoid rescanning
+  * innecessarily; if it's NULL, the scan should start at the head of the
+  * list.  It's name comes from the fact that it's set to the last event fired
+  * by the last call to immediate triggers.
+  *
+  * tail_stack and imm_stack are stacks of pointer, which hold the pointers
+  * to the tail and the "immediate" events as the start of a subtransaction.
+  * We use to revert them when aborting the subtransaction.
+  *
+  * numpushed and numalloc keep control of allocation and storage in the
+  * stacks.
+  *
+  * XXX We need to be able to save this data in a file if it grows too
+  * large.
   * ----------
   */
  
! /* Per-item data */
! typedef struct DeferredTriggerEventItem
  {
!       Oid                     dti_tgoid;
!       TransactionId dti_done_xid;
!       int32           dti_state;
! } DeferredTriggerEventItem;
! 
! typedef struct DeferredTriggerEventData *DeferredTriggerEvent;
! 
! /* Per-event data */
! typedef struct DeferredTriggerEventData
! {
!       DeferredTriggerEvent dte_next;          /* list link */
!       int32           dte_event;
!       Oid                     dte_relid;
!       TransactionId dte_done_xid;
!       ItemPointerData dte_oldctid;
!       ItemPointerData dte_newctid;
!       int32           dte_n_items;
!       /* dte_item is actually a variable-size array, of length dte_n_items */
!       DeferredTriggerEventItem dte_item[1];
! } DeferredTriggerEventData;
! 
! /* Per-trigger status data */
! typedef struct DeferredTriggerStatusData
! {
!       Oid                     dts_tgoid;
!       bool            dts_tgisdeferred;
! } DeferredTriggerStatusData;
  
! typedef struct DeferredTriggerStatusData *DeferredTriggerStatus;
! 
! 
! /*
!  * List of per-trigger status data.
!  * We give it its own memory context so it can be freed easily.
   *
!  * saved is a boolean to flag whether it has been saved by the
!  * current subtransaction.
   *
!  * all_isset and all_isdeferred are used to keep track
!  * of SET CONSTRAINTS ALL {DEFERRED, IMMEDIATE}.
!  *
!  * trigstates is a list of DeferredTriggerStatus structs, which holds
!  * the per-trigger state.
   */
+ typedef struct DeferredTriggerStateData
+ {
+       MemoryContext memcxt;
+       bool    saved;
+       bool    all_isset;
+       bool    all_isdeferred;
+       List   *trigstates;
+ } DeferredTriggerStateData;
+ 
+ typedef DeferredTriggerStateData *DeferredTriggerState;
+ 
+ /* Per-transaction data */
+ typedef struct DeferredTriggersData
+ {
+       MemoryContext                   memcxt;
+       DeferredTriggerState    state;
+       DeferredTriggerEvent    events;
+       DeferredTriggerEvent    tail_thisxact;
+       DeferredTriggerEvent    events_imm;
+       DeferredTriggerEvent   *tail_stack;
+       DeferredTriggerEvent   *imm_stack;
+       int                                             numpushed;
+       int                                             numalloc;
+ } DeferredTriggersData;
  
  typedef DeferredTriggersData *DeferredTriggers;
  
  static DeferredTriggers deferredTriggers;
  
+ static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
+                                       Relation rel, TriggerDesc *trigdesc, FmgrInfo 
*finfo,
+                                          MemoryContext per_tuple_context);
+ 
+ static DeferredTriggerState
+ DeferredTriggerStateCopy(DeferredTriggerState state);
+ 
+ static DeferredTriggerState
+ DeferredTriggerStateCreate(void);
+ 
  /* ----------
   * deferredTriggerCheckState()
   *
***************
*** 1704,1710 ****
        /*
         * Lookup if we know an individual state for this trigger
         */
!       foreach(sl, deferredTriggers->deftrig_trigstates)
        {
                trigstate = (DeferredTriggerStatus) lfirst(sl);
                if (trigstate->dts_tgoid == tgoid)
--- 1782,1788 ----
        /*
         * Lookup if we know an individual state for this trigger
         */
!       foreach(sl, deferredTriggers->state->trigstates)
        {
                trigstate = (DeferredTriggerStatus) lfirst(sl);
                if (trigstate->dts_tgoid == tgoid)
***************
*** 1715,1736 ****
         * No individual state known - so if the user issued a SET CONSTRAINT
         * ALL ..., we return that instead of the triggers default state.
         */
!       if (deferredTriggers->deftrig_all_isset)
!               return deferredTriggers->deftrig_all_isdeferred;
  
        /*
         * No ALL state known either, remember the default state as the
         * current and return that.
         */
!       oldcxt = MemoryContextSwitchTo(deferredTriggers->deftrig_cxt);
  
        trigstate = (DeferredTriggerStatus)
                palloc(sizeof(DeferredTriggerStatusData));
        trigstate->dts_tgoid = tgoid;
        trigstate->dts_tgisdeferred =
                ((itemstate & TRIGGER_DEFERRED_INITDEFERRED) != 0);
!       deferredTriggers->deftrig_trigstates =
!               lappend(deferredTriggers->deftrig_trigstates, trigstate);
  
        MemoryContextSwitchTo(oldcxt);
  
--- 1793,1814 ----
         * No individual state known - so if the user issued a SET CONSTRAINT
         * ALL ..., we return that instead of the triggers default state.
         */
!       if (deferredTriggers->state->all_isset)
!               return deferredTriggers->state->all_isdeferred;
  
        /*
         * No ALL state known either, remember the default state as the
         * current and return that.
         */
!       oldcxt = MemoryContextSwitchTo(deferredTriggers->memcxt);
  
        trigstate = (DeferredTriggerStatus)
                palloc(sizeof(DeferredTriggerStatusData));
        trigstate->dts_tgoid = tgoid;
        trigstate->dts_tgisdeferred =
                ((itemstate & TRIGGER_DEFERRED_INITDEFERRED) != 0);
!       deferredTriggers->state->trigstates =
!               lappend(deferredTriggers->state->trigstates, trigstate);
  
        MemoryContextSwitchTo(oldcxt);
  
***************
*** 1747,1768 ****
  static void
  deferredTriggerAddEvent(DeferredTriggerEvent event)
  {
!       /*
!        * Since the event list could grow quite long, we keep track of the
!        * list tail and append there, rather than just doing a stupid
!        * "lappend". This avoids O(N^2) behavior for large numbers of events.
!        */
!       event->dte_next = NULL;
!       if (deferredTriggers->deftrig_event_tail == NULL)
        {
                /* first list entry */
!               deferredTriggers->deftrig_events = event;
!               deferredTriggers->deftrig_event_tail = event;
        }
        else
        {
!               deferredTriggers->deftrig_event_tail->dte_next = event;
!               deferredTriggers->deftrig_event_tail = event;
        }
  }
  
--- 1825,1842 ----
  static void
  deferredTriggerAddEvent(DeferredTriggerEvent event)
  {
!       Assert(event->dte_next == NULL);
! 
!       if (deferredTriggers->tail_thisxact == NULL)
        {
                /* first list entry */
!               deferredTriggers->events = event;
!               deferredTriggers->tail_thisxact = event;
        }
        else
        {
!               deferredTriggers->tail_thisxact->dte_next = event;
!               deferredTriggers->tail_thisxact = event;
        }
  }
  
***************
*** 1915,1932 ****
  
        /*
         * If immediate_only is true, then the only events that could need
!        * firing are those since deftrig_events_imm.  (But if
!        * deftrig_events_imm is NULL, we must scan the entire list.)
         */
!       if (immediate_only && deferredTriggers->deftrig_events_imm != NULL)
        {
!               prev_event = deferredTriggers->deftrig_events_imm;
                event = prev_event->dte_next;
        }
        else
        {
                prev_event = NULL;
!               event = deferredTriggers->deftrig_events;
        }
  
        while (event != NULL)
--- 1989,2006 ----
  
        /*
         * If immediate_only is true, then the only events that could need
!        * firing are those since events_imm.  (But if
!        * events_imm is NULL, we must scan the entire list.)
         */
!       if (immediate_only && deferredTriggers->events_imm != NULL)
        {
!               prev_event = deferredTriggers->events_imm;
                event = prev_event->dte_next;
        }
        else
        {
                prev_event = NULL;
!               event = deferredTriggers->events;
        }
  
        while (event != NULL)
***************
*** 1936,1945 ****
                int                     i;
  
                /*
!                * Check if event is already completely done.
                 */
!               if (!(event->dte_event & (TRIGGER_DEFERRED_DONE |
!                                                                 
TRIGGER_DEFERRED_CANCELED)))
                {
                        MemoryContextReset(per_tuple_context);
  
--- 2010,2022 ----
                int                     i;
  
                /*
!                * Skip executing cancelled events, and events done by transactions
!                * that are not aborted.
                 */
!               if (!(event->dte_event & TRIGGER_DEFERRED_CANCELED) ||
!                               (event->dte_event & TRIGGER_DEFERRED_DONE &&
!                                TransactionIdIsValid(event->dte_done_xid) &&
!                                !TransactionIdDidAbort(event->dte_done_xid)))
                {
                        MemoryContextReset(per_tuple_context);
  
***************
*** 1948,1954 ****
                         */
                        for (i = 0; i < event->dte_n_items; i++)
                        {
!                               if (event->dte_item[i].dti_state & 
TRIGGER_DEFERRED_DONE)
                                        continue;
  
                                /*
--- 2025,2033 ----
                         */
                        for (i = 0; i < event->dte_n_items; i++)
                        {
!                               if (event->dte_item[i].dti_state & 
TRIGGER_DEFERRED_DONE &&
!                                               
TransactionIdIsValid(event->dte_item[i].dti_done_xid) &&
!                                               
!(TransactionIdDidAbort(event->dte_item[i].dti_done_xid)))
                                        continue;
  
                                /*
***************
*** 2003,2008 ****
--- 2082,2088 ----
                                                                           
per_tuple_context);
  
                                event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE;
+                               event->dte_item[i].dti_done_xid = 
GetCurrentTransactionId();
                        }                                       /* end loop over items 
within event */
                }
  
***************
*** 2022,2044 ****
                }
                else
                {
!                       /* Done */
!                       if (immediate_only)
                        {
                                /* delink it from list and free it */
                                if (prev_event)
                                        prev_event->dte_next = next_event;
                                else
!                                       deferredTriggers->deftrig_events = next_event;
                                pfree(event);
                        }
                        else
                        {
                                /*
!                                * We will clean up later, but just for paranoia's 
sake,
!                                * mark the event done.
                                 */
                                event->dte_event |= TRIGGER_DEFERRED_DONE;
                        }
                }
  
--- 2102,2128 ----
                }
                else
                {
!                       /*
!                        * We can drop an item if it's done, but only if we're not
!                        * inside a subtransaction because it could abort later on.
!                        * We will want to check the item again if it does.
!                        */
!                       if (immediate_only && !IsSubTransaction())
                        {
                                /* delink it from list and free it */
                                if (prev_event)
                                        prev_event->dte_next = next_event;
                                else
!                                       deferredTriggers->events = next_event;
                                pfree(event);
                        }
                        else
                        {
                                /*
!                                * Mark the event done.
                                 */
                                event->dte_event |= TRIGGER_DEFERRED_DONE;
+                               event->dte_done_xid = GetCurrentTransactionId();
                        }
                }
  
***************
*** 2046,2055 ****
        }
  
        /* Update list tail pointer in case we just deleted tail event */
!       deferredTriggers->deftrig_event_tail = prev_event;
  
        /* Set the immediate event pointer for next time */
!       deferredTriggers->deftrig_events_imm = prev_event;
  
        /* Release working resources */
        if (rel)
--- 2130,2139 ----
        }
  
        /* Update list tail pointer in case we just deleted tail event */
!       deferredTriggers->tail_thisxact = prev_event;
  
        /* Set the immediate event pointer for next time */
!       deferredTriggers->events_imm = prev_event;
  
        /* Release working resources */
        if (rel)
***************
*** 2060,2082 ****
        MemoryContextDelete(per_tuple_context);
  }
  
- 
- /* ----------
-  * DeferredTriggerInit()
-  *
-  *    Initialize the deferred trigger mechanism. This is called during
-  *    backend startup and is guaranteed to be before the first of all
-  *    transactions.
-  * ----------
-  */
- void
- DeferredTriggerInit(void)
- {
-       /* Nothing to do */
-       ;
- }
- 
- 
  /* ----------
   * DeferredTriggerBeginXact()
   *
--- 2144,2149 ----
***************
*** 2087,2120 ****
  void
  DeferredTriggerBeginXact(void)
  {
!       /*
!        * This will be changed to a special context when the nested
!        * transactions project moves forward.
!        */
!       MemoryContext cxt = TopTransactionContext;
  
!       deferredTriggers = (DeferredTriggers) MemoryContextAlloc(TopTransactionContext,
!                                                                                  
sizeof(DeferredTriggersData));
  
        /*
         * Create the per transaction memory context
         */
!       deferredTriggers->deftrig_cxt = AllocSetContextCreate(cxt,
!                                                                                      
            "DeferredTriggerXact",
!                                                                                      
         ALLOCSET_DEFAULT_MINSIZE,
!                                                                                      
    ALLOCSET_DEFAULT_INITSIZE,
!                                                                                      
    ALLOCSET_DEFAULT_MAXSIZE);
  
        /*
         * If unspecified, constraints default to IMMEDIATE, per SQL
         */
!       deferredTriggers->deftrig_all_isdeferred = false;
!       deferredTriggers->deftrig_all_isset = false;
  
!       deferredTriggers->deftrig_trigstates = NIL;
!       deferredTriggers->deftrig_events = NULL;
!       deferredTriggers->deftrig_events_imm = NULL;
!       deferredTriggers->deftrig_event_tail = NULL;
  }
  
  
--- 2154,2185 ----
  void
  DeferredTriggerBeginXact(void)
  {
!       MemoryContext old_cxt = MemoryContextSwitchTo(TopTransactionContext);
  
!       deferredTriggers = (DeferredTriggers) palloc(sizeof(DeferredTriggersData));
  
        /*
         * Create the per transaction memory context
         */
!       deferredTriggers->memcxt = AllocSetContextCreate(TopTransactionContext,
!                                                                                      
                  "DeferredTriggerXact",
!                                                                                      
                  ALLOCSET_DEFAULT_MINSIZE,
!                                                                                      
                  ALLOCSET_DEFAULT_INITSIZE,
!                                                                                      
                  ALLOCSET_DEFAULT_MAXSIZE);
  
        /*
         * If unspecified, constraints default to IMMEDIATE, per SQL
         */
!       deferredTriggers->state = DeferredTriggerStateCreate();
!       deferredTriggers->events = NULL;
!       deferredTriggers->events_imm = NULL;
!       deferredTriggers->tail_thisxact = NULL;
!       deferredTriggers->tail_stack = NULL;
!       deferredTriggers->imm_stack = NULL;
!       deferredTriggers->numalloc = 0;
!       deferredTriggers->numpushed = 0;
  
!       MemoryContextSwitchTo(old_cxt);
  }
  
  
***************
*** 2183,2188 ****
--- 2248,2446 ----
        deferredTriggers = NULL;
  }
  
+ /*
+  * DeferredTriggerBeginSubXact()
+  *
+  *    Start a subtransaction.
+  */
+ void
+ DeferredTriggerBeginSubXact(void)
+ {
+       /*
+        * Ignore call if the transaction is in aborted state.
+        */
+       if (deferredTriggers == NULL)
+               return;
+ 
+       /*
+        * Reset the saved flag for the new subtransaction.
+        */
+       deferredTriggers->state->saved = false;
+ 
+       /*
+        * Allocate more space in the stacks if needed.
+        */
+       if (deferredTriggers->numpushed == deferredTriggers->numalloc)
+       {
+               MemoryContext old_cxt;
+ 
+               old_cxt = MemoryContextSwitchTo(deferredTriggers->memcxt);
+ 
+               if (deferredTriggers->numalloc == 0)
+               {
+ #define DEFTRIG_INITALLOC 8
+                       deferredTriggers->tail_stack =
+                               palloc(DEFTRIG_INITALLOC * 
sizeof(DeferredTriggerEvent));
+ 
+                       deferredTriggers->imm_stack =
+                               palloc(DEFTRIG_INITALLOC * 
sizeof(DeferredTriggerEvent));
+ 
+                       deferredTriggers->numalloc = DEFTRIG_INITALLOC;
+               }
+               else
+               {
+                       deferredTriggers->numalloc *= 2;
+ 
+                       deferredTriggers->tail_stack = 
+                               repalloc(deferredTriggers->tail_stack,
+                                                deferredTriggers->numalloc * 
sizeof(DeferredTriggerEvent));
+                       deferredTriggers->imm_stack =
+                               repalloc(deferredTriggers->imm_stack,
+                                               deferredTriggers->numalloc * 
sizeof(DeferredTriggerEvent));
+               }
+ 
+               MemoryContextSwitchTo(old_cxt);
+       }
+ 
+       /*
+        * Push the current list position into the stack and reset the
+        * pointer.
+        */
+       deferredTriggers->tail_stack[deferredTriggers->numpushed] =
+               deferredTriggers->tail_thisxact;
+       deferredTriggers->imm_stack[deferredTriggers->numpushed] =
+               deferredTriggers->events_imm;
+ 
+       deferredTriggers->numpushed++;
+ }
+ 
+ /*
+  * DeferredTriggerEndSubXact()
+  *
+  *    The current subtransaction is aborting.
+  */
+ void
+ DeferredTriggerEndSubXact(bool isCommit)
+ {
+       /*
+        * Ignore call if the transaction is in aborted state.
+        */
+       if (deferredTriggers == NULL)
+               return;
+ 
+       /*
+        * Move back the "top of the stack."
+        */
+       deferredTriggers->numpushed --;
+ 
+       if (!isCommit)
+       {
+               DeferredTriggerState state;
+               /*
+                * Restore the pointers from the stacks.
+                */
+               deferredTriggers->tail_thisxact =
+                       deferredTriggers->tail_stack[deferredTriggers->numpushed];
+               deferredTriggers->events_imm =
+                       deferredTriggers->imm_stack[deferredTriggers->numpushed];
+ 
+               /*
+                * Cleanup the head and the tail of the list.
+                */
+               if (deferredTriggers->tail_thisxact == NULL)
+                       deferredTriggers->events = NULL;
+               else
+                       deferredTriggers->tail_thisxact->dte_next = NULL;
+ 
+               /*
+                * We don't need to free the items, since the CommitContext 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.
+                */
+               if ((state = TransactionGetDeftrigState()) != NULL)
+               {
+                       MemoryContextDelete(deferredTriggers->state->memcxt);
+                       deferredTriggers->state = state;
+               }
+       }
+ }
+ 
+ static DeferredTriggerState
+ DeferredTriggerStateCreate(void)
+ {
+       DeferredTriggerState state;
+       MemoryContext           old_cxt;
+       MemoryContext           memcxt;
+ 
+       /*
+        * Create the DeferredTriggerState memory context
+        */
+       memcxt = AllocSetContextCreate(deferredTriggers->memcxt,
+                                                                  
"DeferredTriggerState",
+                                                                  
ALLOCSET_SMALL_MINSIZE,
+                                                                  
ALLOCSET_SMALL_INITSIZE,
+                                                                  
ALLOCSET_SMALL_MAXSIZE);
+ 
+       old_cxt = MemoryContextSwitchTo(memcxt);
+ 
+       /*
+        * We assume that zeroing will initialize correctly the state values.
+        *
+        * NB: the state struct is allocated in the state->memcxt trigger.
+        * Be sure not to lose the pointer prior to deleting it ...
+        */
+       state = (DeferredTriggerState)
+               palloc0(sizeof(DeferredTriggerStateData));
+ 
+       state->memcxt = memcxt;
+ 
+       MemoryContextSwitchTo(old_cxt);
+ 
+       return state;
+ }
+ 
+ /*
+  * DeferredTriggerStateCopy
+  */
+ static DeferredTriggerState
+ DeferredTriggerStateCopy(DeferredTriggerState origstate)
+ {
+       DeferredTriggerState state;
+       MemoryContext           old_cxt;
+       ListCell                        *cell;
+ 
+       state = DeferredTriggerStateCreate();
+ 
+       old_cxt = MemoryContextSwitchTo(state->memcxt);
+ 
+       state->saved = false;
+       state->all_isset = origstate->all_isset;
+       state->all_isdeferred = origstate->all_isdeferred;
+ 
+       /*
+        * XXX We assume there are not a lot of items on this list.  If there
+        * were, we probably should be using an array of items and not a list
+        * -- this would not only save space on the list itself, but it'd also
+        * make this copying much faster: the whole struct would be a single
+        * memcpy().
+        */
+       foreach(cell, origstate->trigstates)
+       {
+               DeferredTriggerStatus trigstate = (DeferredTriggerStatus)
+                       palloc(sizeof(DeferredTriggerStatusData));
+ 
+               memcpy(trigstate, lfirst(cell), sizeof(DeferredTriggerStatus));
+               state->trigstates = lappend(state->trigstates, trigstate);
+       }
+ 
+       MemoryContextSwitchTo(old_cxt);
+ 
+       return state;
+ }
  
  /* ----------
   * DeferredTriggerSetState()
***************
*** 2193,2200 ****
  void
  DeferredTriggerSetState(ConstraintsSetStmt *stmt)
  {
-       ListCell           *l;
- 
        /*
         * Ignore call if we aren't in a transaction.
         */
--- 2451,2456 ----
***************
*** 2202,2207 ****
--- 2458,2473 ----
                return;
  
        /*
+        * Save the current state so it can be restored if the subtransaction
+        * aborts.
+        */
+       if (IsSubTransaction() && !deferredTriggers->state->saved)
+       {
+               
TransactionSetDeftrigState(DeferredTriggerStateCopy(deferredTriggers->state));
+               deferredTriggers->state->saved = true;
+       }
+ 
+       /*
         * Handle SET CONSTRAINTS ALL ...
         */
        if (stmt->constraints == NIL)
***************
*** 2210,2232 ****
                 * Drop all per-transaction information about individual trigger
                 * states.
                 */
!               list_free_deep(deferredTriggers->deftrig_trigstates);
!               deferredTriggers->deftrig_trigstates = NIL;
  
                /*
                 * Set the per-transaction ALL state to known.
                 */
!               deferredTriggers->deftrig_all_isset = true;
!               deferredTriggers->deftrig_all_isdeferred = stmt->deferred;
        }
        else
        {
                Relation        tgrel;
                MemoryContext oldcxt;
!               bool            found;
!               DeferredTriggerStatus state;
!               ListCell   *ls;
!               List       *loid = NIL;
  
                /* ----------
                 * Handle SET CONSTRAINTS constraint-name [, ...]
--- 2476,2496 ----
                 * Drop all per-transaction information about individual trigger
                 * states.
                 */
!               list_free_deep(deferredTriggers->state->trigstates);
!               deferredTriggers->state->trigstates = NIL;
  
                /*
                 * Set the per-transaction ALL state to known.
                 */
!               deferredTriggers->state->all_isset = true;
!               deferredTriggers->state->all_isdeferred = stmt->deferred;
        }
        else
        {
                Relation        tgrel;
                MemoryContext oldcxt;
!               ListCell   *l;
!               List       *oidlist = NIL;
  
                /* ----------
                 * Handle SET CONSTRAINTS constraint-name [, ...]
***************
*** 2241,2246 ****
--- 2505,2511 ----
                        ScanKeyData skey;
                        SysScanDesc tgscan;
                        HeapTuple       htup;
+                       bool            found;
  
                        /*
                         * Check that only named constraints are set explicitly
***************
*** 2285,2291 ****
                                                                        cname)));
  
                                constr_oid = HeapTupleGetOid(htup);
!                               loid = lappend_oid(loid, constr_oid);
                                found = true;
                        }
  
--- 2550,2556 ----
                                                                        cname)));
  
                                constr_oid = HeapTupleGetOid(htup);
!                               oidlist = lappend_oid(oidlist, constr_oid);
                                found = true;
                        }
  
***************
*** 2305,2334 ****
                 * Inside of a transaction block set the trigger states of
                 * individual triggers on transaction level.
                 */
!               oldcxt = MemoryContextSwitchTo(deferredTriggers->deftrig_cxt);
  
!               foreach(l, loid)
                {
!                       found = false;
!                       foreach(ls, deferredTriggers->deftrig_trigstates)
                        {
!                               state = (DeferredTriggerStatus) lfirst(ls);
!                               if (state->dts_tgoid == lfirst_oid(l))
                                {
!                                       state->dts_tgisdeferred = stmt->deferred;
                                        found = true;
                                        break;
                                }
                        }
                        if (!found)
                        {
!                               state = (DeferredTriggerStatus)
!                                       palloc(sizeof(DeferredTriggerStatusData));
!                               state->dts_tgoid = lfirst_oid(l);
!                               state->dts_tgisdeferred = stmt->deferred;
  
!                               deferredTriggers->deftrig_trigstates =
!                                       lappend(deferredTriggers->deftrig_trigstates, 
state);
                        }
                }
  
--- 2570,2604 ----
                 * Inside of a transaction block set the trigger states of
                 * individual triggers on transaction level.
                 */
!               oldcxt = MemoryContextSwitchTo(deferredTriggers->state->memcxt);
  
!               foreach(l, oidlist)
                {
!                       ListCell   *ls;
!                       bool            found = false;
! 
!                       foreach(ls, deferredTriggers->state->trigstates)
                        {
!                               DeferredTriggerStatus status = (DeferredTriggerStatus) 
lfirst(ls);
!                               if (status->dts_tgoid == lfirst_oid(l))
                                {
!                                       status->dts_tgisdeferred = stmt->deferred;
                                        found = true;
                                        break;
                                }
                        }
                        if (!found)
                        {
!                               DeferredTriggerStatus status;
!                          
!                               status = (DeferredTriggerStatus) palloc
!                                       (sizeof(DeferredTriggerStatusData));
! 
!                               status->dts_tgoid = lfirst_oid(l);
!                               status->dts_tgisdeferred = stmt->deferred;
  
!                               deferredTriggers->state->trigstates =
!                                       lappend(deferredTriggers->state->trigstates, 
status);
                        }
                }
  
***************
*** 2347,2360 ****
         * entire list, in case some deferred events are now immediately
         * invokable.
         */
!       deferredTriggers->deftrig_events_imm = NULL;
  }
  
  
  /* ----------
   * DeferredTriggerSaveEvent()
   *
!  *    Called by ExecAR...Triggers() to add the event to the queue.
   *
   *    NOTE: should be called only if we've determined that an event must
   *    be added to the queue.
--- 2617,2630 ----
         * entire list, in case some deferred events are now immediately
         * invokable.
         */
!       deferredTriggers->events_imm = NULL;
  }
  
  
  /* ----------
   * DeferredTriggerSaveEvent()
   *
!  *    Called by ExecA[RS]...Triggers() to add the event to the queue.
   *
   *    NOTE: should be called only if we've determined that an event must
   *    be added to the queue.
***************
*** 2423,2431 ****
                return;
  
        /*
!        * Create a new event
         */
!       oldcxt = MemoryContextSwitchTo(deferredTriggers->deftrig_cxt);
  
        new_size = offsetof(DeferredTriggerEventData, dte_item[0]) +
                n_enabled_triggers * sizeof(DeferredTriggerEventItem);
--- 2693,2702 ----
                return;
  
        /*
!        * Create a new event.  We use the CommitContext so the event
!        * will automatically go away if the subtransaction aborts.
         */
!       oldcxt = MemoryContextSwitchTo(CommitContext);
  
        new_size = offsetof(DeferredTriggerEventData, dte_item[0]) +
                n_enabled_triggers * sizeof(DeferredTriggerEventItem);
***************
*** 2433,2438 ****
--- 2704,2710 ----
        new_event = (DeferredTriggerEvent) palloc(new_size);
        new_event->dte_next = NULL;
        new_event->dte_event = event & TRIGGER_EVENT_OPMASK;
+       new_event->dte_done_xid = InvalidTransactionId;
        if (row_trigger)
                new_event->dte_event |= TRIGGER_EVENT_ROW;
        new_event->dte_relid = rel->rd_id;
***************
*** 2449,2454 ****
--- 2721,2727 ----
  
                ev_item = &(new_event->dte_item[i]);
                ev_item->dti_tgoid = trigger->tgoid;
+               ev_item->dti_done_xid = InvalidTransactionId;
                ev_item->dti_state =
                        ((trigger->tgdeferrable) ?
                         TRIGGER_DEFERRED_DEFERRABLE : 0) |
***************
*** 2517,2522 ****
--- 2790,2796 ----
                                         * the trigger at all.
                                         */
                                        new_event->dte_item[i].dti_state |= 
TRIGGER_DEFERRED_DONE;
+                                       new_event->dte_item[i].dti_done_xid = 
GetCurrentTransactionId();
                                }
                        }
  
---------------------------(end of broadcast)---------------------------
TIP 4: Don't 'kill -9' the postmaster

Reply via email to