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(); } }