diff options
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r-- | src/backend/commands/trigger.c | 577 |
1 files changed, 422 insertions, 155 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index cfbd58e4282..15f4cfa8dcb 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.165 2004/05/26 04:41:12 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.166 2004/07/01 00:50:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -50,9 +50,6 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, 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); /* @@ -1639,47 +1636,130 @@ ltrmark:; /* ---------- * Deferred trigger stuff + * + * The DeferredTriggersData struct holds data about pending deferred + * trigger events during the current transaction tree. The struct and + * most of its subsidiary data are kept in TopTransactionContext; however + * the individual event records are kept in CurTransactionContext, so that + * they will easily go away during subtransaction abort. + * + * DeferredTriggersData has the following fields: + * + * state keeps track of the deferred state of each trigger + * (including the global state). This is saved and restored across + * failed subtransactions. + * + * 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 + * unnecessarily; if it's NULL, the scan should start at the head of the + * list. Its 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 of the start of a subtransaction. + * We use to revert them when aborting the subtransaction. + * + * state_stack is a stack of pointers to saved copies of the deferred-trigger + * state data; each subtransaction level that modifies that state first + * saves a copy, which we use to restore the state if we abort. + * + * numpushed and numalloc keep control of allocation and storage in the above + * stacks. numpushed is essentially the current subtransaction nesting depth. + * + * XXX We need to be able to save the per-event data in a file if it grows too + * large. * ---------- */ -typedef struct DeferredTriggersData +/* Per-item data */ +typedef struct DeferredTriggerEventItem { - /* 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; + Oid dti_tgoid; + TransactionId dti_done_xid; + int32 dti_state; +} DeferredTriggerEventItem; -/* ---------- - * deftrig_events, deftrig_event_tail: - * The list of pending deferred trigger events during the current transaction. +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; + + +/* + * Trigger deferral status data. * - * 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. + * We make this a single palloc'd object so it can be copied and freed easily. * - * 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. + * all_isset and all_isdeferred are used to keep track + * of SET CONSTRAINTS ALL {DEFERRED, IMMEDIATE}. * - * XXX Need to be able to shove this data out to a file if it grows too - * large... - * ---------- + * trigstates[] stores per-trigger tgisdeferred settings. */ +typedef struct DeferredTriggerStateData +{ + bool all_isset; + bool all_isdeferred; + int numstates; /* number of trigstates[] entries in use */ + int numalloc; /* allocated size of trigstates[] */ + DeferredTriggerStatusData trigstates[1]; /* VARIABLE LENGTH ARRAY */ +} DeferredTriggerStateData; + +typedef DeferredTriggerStateData *DeferredTriggerState; + +/* Per-transaction data */ +typedef struct DeferredTriggersData +{ + DeferredTriggerState state; + DeferredTriggerEvent events; + DeferredTriggerEvent tail_thisxact; + DeferredTriggerEvent events_imm; + DeferredTriggerEvent *tail_stack; + DeferredTriggerEvent *imm_stack; + DeferredTriggerState *state_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 DeferredTriggerStateCreate(int numalloc); +static DeferredTriggerState DeferredTriggerStateCopy(DeferredTriggerState state); +static DeferredTriggerState DeferredTriggerStateAddItem(DeferredTriggerState state, + Oid tgoid, bool tgisdeferred); + + /* ---------- * deferredTriggerCheckState() * @@ -1690,13 +1770,12 @@ static DeferredTriggers deferredTriggers; static bool deferredTriggerCheckState(Oid tgoid, int32 itemstate) { - MemoryContext oldcxt; - ListCell *sl; - DeferredTriggerStatus trigstate; + bool tgisdeferred; + int i; /* - * Not deferrable triggers (i.e. normal AFTER ROW triggers and - * constraints declared NOT DEFERRABLE, the state is always false. + * For not-deferrable triggers (i.e. normal AFTER ROW triggers and + * constraints declared NOT DEFERRABLE), the state is always false. */ if ((itemstate & TRIGGER_DEFERRED_DEFERRABLE) == 0) return false; @@ -1704,37 +1783,29 @@ deferredTriggerCheckState(Oid tgoid, int32 itemstate) /* * Lookup if we know an individual state for this trigger */ - foreach(sl, deferredTriggers->deftrig_trigstates) + for (i = 0; i < deferredTriggers->state->numstates; i++) { - trigstate = (DeferredTriggerStatus) lfirst(sl); - if (trigstate->dts_tgoid == tgoid) - return trigstate->dts_tgisdeferred; + if (deferredTriggers->state->trigstates[i].dts_tgoid == tgoid) + return deferredTriggers->state->trigstates[i].dts_tgisdeferred; } /* * 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; + 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. + * current and return that. (XXX why do we bother making a state entry?) */ - oldcxt = MemoryContextSwitchTo(deferredTriggers->deftrig_cxt); + tgisdeferred = ((itemstate & TRIGGER_DEFERRED_INITDEFERRED) != 0); + deferredTriggers->state = + DeferredTriggerStateAddItem(deferredTriggers->state, + tgoid, tgisdeferred); - 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); - - return trigstate->dts_tgisdeferred; + return tgisdeferred; } @@ -1747,22 +1818,18 @@ deferredTriggerCheckState(Oid tgoid, int32 itemstate) 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) + Assert(event->dte_next == NULL); + + if (deferredTriggers->tail_thisxact == NULL) { /* first list entry */ - deferredTriggers->deftrig_events = event; - deferredTriggers->deftrig_event_tail = event; + deferredTriggers->events = event; + deferredTriggers->tail_thisxact = event; } else { - deferredTriggers->deftrig_event_tail->dte_next = event; - deferredTriggers->deftrig_event_tail = event; + deferredTriggers->tail_thisxact->dte_next = event; + deferredTriggers->tail_thisxact = event; } } @@ -1915,18 +1982,18 @@ deferredTriggerInvokeEvents(bool immediate_only) /* * 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.) + * firing are those since events_imm. (But if + * events_imm is NULL, we must scan the entire list.) */ - if (immediate_only && deferredTriggers->deftrig_events_imm != NULL) + if (immediate_only && deferredTriggers->events_imm != NULL) { - prev_event = deferredTriggers->deftrig_events_imm; + prev_event = deferredTriggers->events_imm; event = prev_event->dte_next; } else { prev_event = NULL; - event = deferredTriggers->deftrig_events; + event = deferredTriggers->events; } while (event != NULL) @@ -1936,10 +2003,13 @@ deferredTriggerInvokeEvents(bool immediate_only) int i; /* - * Check if event is already completely done. + * Skip executing cancelled events, and events done by transactions + * that are not aborted. */ - if (!(event->dte_event & (TRIGGER_DEFERRED_DONE | - TRIGGER_DEFERRED_CANCELED))) + 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,7 +2018,9 @@ deferredTriggerInvokeEvents(bool immediate_only) */ for (i = 0; i < event->dte_n_items; i++) { - if (event->dte_item[i].dti_state & TRIGGER_DEFERRED_DONE) + 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,6 +2075,7 @@ deferredTriggerInvokeEvents(bool immediate_only) 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,23 +2095,27 @@ deferredTriggerInvokeEvents(bool immediate_only) } else { - /* Done */ - if (immediate_only) + /* + * 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->deftrig_events = next_event; + deferredTriggers->events = next_event; pfree(event); } else { /* - * We will clean up later, but just for paranoia's sake, - * mark the event done. + * Mark the event done. */ event->dte_event |= TRIGGER_DEFERRED_DONE; + event->dte_done_xid = GetCurrentTransactionId(); } } @@ -2046,10 +2123,10 @@ deferredTriggerInvokeEvents(bool immediate_only) } /* Update list tail pointer in case we just deleted tail event */ - deferredTriggers->deftrig_event_tail = prev_event; + deferredTriggers->tail_thisxact = prev_event; /* Set the immediate event pointer for next time */ - deferredTriggers->deftrig_events_imm = prev_event; + deferredTriggers->events_imm = prev_event; /* Release working resources */ if (rel) @@ -2060,23 +2137,6 @@ deferredTriggerInvokeEvents(bool immediate_only) 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() * @@ -2087,34 +2147,24 @@ DeferredTriggerInit(void) 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)); + Assert(deferredTriggers == NULL); - /* - * Create the per transaction memory context - */ - deferredTriggers->deftrig_cxt = AllocSetContextCreate(cxt, - "DeferredTriggerXact", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); + deferredTriggers = (DeferredTriggers) + MemoryContextAlloc(TopTransactionContext, + sizeof(DeferredTriggersData)); /* * 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; + deferredTriggers->state = DeferredTriggerStateCreate(8); + deferredTriggers->events = NULL; + deferredTriggers->events_imm = NULL; + deferredTriggers->tail_thisxact = NULL; + deferredTriggers->tail_stack = NULL; + deferredTriggers->imm_stack = NULL; + deferredTriggers->state_stack = NULL; + deferredTriggers->numalloc = 0; + deferredTriggers->numpushed = 0; } @@ -2156,6 +2206,12 @@ DeferredTriggerEndXact(void) deferredTriggerInvokeEvents(false); + /* + * Forget everything we know about deferred triggers. + * + * Since all the info is in TopTransactionContext or children thereof, + * we need do nothing special to reclaim memory. + */ deferredTriggers = NULL; } @@ -2179,10 +2235,217 @@ DeferredTriggerAbortXact(void) /* * Forget everything we know about deferred triggers. + * + * Since all the info is in TopTransactionContext or children thereof, + * we need do nothing special to reclaim memory. */ deferredTriggers = NULL; } +/* + * DeferredTriggerBeginSubXact() + * + * Start a subtransaction. + */ +void +DeferredTriggerBeginSubXact(void) +{ + /* + * Ignore call if the transaction is in aborted state. + */ + if (deferredTriggers == NULL) + return; + + /* + * Allocate more space in the stacks if needed. + */ + if (deferredTriggers->numpushed == deferredTriggers->numalloc) + { + if (deferredTriggers->numalloc == 0) + { + MemoryContext old_cxt; + + old_cxt = MemoryContextSwitchTo(TopTransactionContext); + +#define DEFTRIG_INITALLOC 8 + deferredTriggers->tail_stack = (DeferredTriggerEvent *) + palloc(DEFTRIG_INITALLOC * sizeof(DeferredTriggerEvent)); + deferredTriggers->imm_stack = (DeferredTriggerEvent *) + palloc(DEFTRIG_INITALLOC * sizeof(DeferredTriggerEvent)); + deferredTriggers->state_stack = (DeferredTriggerState *) + palloc(DEFTRIG_INITALLOC * sizeof(DeferredTriggerState)); + deferredTriggers->numalloc = DEFTRIG_INITALLOC; + + MemoryContextSwitchTo(old_cxt); + } + else + { + /* repalloc will keep the stacks in the same context */ + deferredTriggers->numalloc *= 2; + + deferredTriggers->tail_stack = (DeferredTriggerEvent *) + repalloc(deferredTriggers->tail_stack, + deferredTriggers->numalloc * sizeof(DeferredTriggerEvent)); + deferredTriggers->imm_stack = (DeferredTriggerEvent *) + repalloc(deferredTriggers->imm_stack, + deferredTriggers->numalloc * sizeof(DeferredTriggerEvent)); + deferredTriggers->state_stack = (DeferredTriggerState *) + repalloc(deferredTriggers->state_stack, + deferredTriggers->numalloc * sizeof(DeferredTriggerState)); + } + } + + /* + * 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; + /* State is not saved until/unless changed */ + deferredTriggers->state_stack[deferredTriggers->numpushed] = NULL; + + deferredTriggers->numpushed++; +} + +/* + * DeferredTriggerEndSubXact() + * + * The current subtransaction is ending. + */ +void +DeferredTriggerEndSubXact(bool isCommit) +{ + DeferredTriggerState state; + + /* + * Ignore call if the transaction is in aborted state. + */ + if (deferredTriggers == NULL) + return; + + /* + * Move back the "top of the stack." + */ + Assert(deferredTriggers->numpushed > 0); + + deferredTriggers->numpushed--; + + if (isCommit) + { + /* If we saved a prior state, we don't need it anymore */ + state = deferredTriggers->state_stack[deferredTriggers->numpushed]; + if (state != NULL) + pfree(state); + } + else + { + /* + * Aborting --- 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 CurTransactionContext + * 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. + */ + state = deferredTriggers->state_stack[deferredTriggers->numpushed]; + if (state != NULL) + { + pfree(deferredTriggers->state); + deferredTriggers->state = state; + } + } +} + +/* + * Create an empty DeferredTriggerState with room for numalloc trigstates + */ +static DeferredTriggerState +DeferredTriggerStateCreate(int numalloc) +{ + DeferredTriggerState state; + + /* Behave sanely with numalloc == 0 */ + if (numalloc <= 0) + numalloc = 1; + + /* + * We assume that zeroing will correctly initialize the state values. + */ + state = (DeferredTriggerState) + MemoryContextAllocZero(TopTransactionContext, + sizeof(DeferredTriggerStateData) + + (numalloc - 1) * sizeof(DeferredTriggerStatusData)); + + state->numalloc = numalloc; + + return state; +} + +/* + * Copy a DeferredTriggerState + */ +static DeferredTriggerState +DeferredTriggerStateCopy(DeferredTriggerState origstate) +{ + DeferredTriggerState state; + + state = DeferredTriggerStateCreate(origstate->numstates); + + state->all_isset = origstate->all_isset; + state->all_isdeferred = origstate->all_isdeferred; + state->numstates = origstate->numstates; + memcpy(state->trigstates, origstate->trigstates, + origstate->numstates * sizeof(DeferredTriggerStatusData)); + + return state; +} + +/* + * Add a per-trigger item to a DeferredTriggerState. Returns possibly-changed + * pointer to the state object (it will change if we have to repalloc). + */ +static DeferredTriggerState +DeferredTriggerStateAddItem(DeferredTriggerState state, + Oid tgoid, bool tgisdeferred) +{ + if (state->numstates >= state->numalloc) + { + int newalloc = state->numalloc * 2; + + newalloc = Max(newalloc, 8); /* in case original has size 0 */ + state = (DeferredTriggerState) + repalloc(state, + sizeof(DeferredTriggerStateData) + + (newalloc - 1) * sizeof(DeferredTriggerStatusData)); + state->numalloc = newalloc; + Assert(state->numstates < state->numalloc); + } + + state->trigstates[state->numstates].dts_tgoid = tgoid; + state->trigstates[state->numstates].dts_tgisdeferred = tgisdeferred; + state->numstates++; + + return state; +} /* ---------- * DeferredTriggerSetState() @@ -2193,8 +2456,6 @@ DeferredTriggerAbortXact(void) void DeferredTriggerSetState(ConstraintsSetStmt *stmt) { - ListCell *l; - /* * Ignore call if we aren't in a transaction. */ @@ -2202,6 +2463,17 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt) return; /* + * If in a subtransaction, and we didn't save the current state already, + * save it so it can be restored if the subtransaction aborts. + */ + if (deferredTriggers->numpushed > 0 && + deferredTriggers->state_stack[deferredTriggers->numpushed - 1] == NULL) + { + deferredTriggers->state_stack[deferredTriggers->numpushed - 1] = + DeferredTriggerStateCopy(deferredTriggers->state); + } + + /* * Handle SET CONSTRAINTS ALL ... */ if (stmt->constraints == NIL) @@ -2210,23 +2482,19 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt) * Drop all per-transaction information about individual trigger * states. */ - list_free_deep(deferredTriggers->deftrig_trigstates); - deferredTriggers->deftrig_trigstates = NIL; + deferredTriggers->state->numstates = 0; /* * Set the per-transaction ALL state to known. */ - deferredTriggers->deftrig_all_isset = true; - deferredTriggers->deftrig_all_isdeferred = stmt->deferred; + deferredTriggers->state->all_isset = true; + deferredTriggers->state->all_isdeferred = stmt->deferred; } else { Relation tgrel; - MemoryContext oldcxt; - bool found; - DeferredTriggerStatus state; - ListCell *ls; - List *loid = NIL; + ListCell *l; + List *oidlist = NIL; /* ---------- * Handle SET CONSTRAINTS constraint-name [, ...] @@ -2241,6 +2509,7 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt) ScanKeyData skey; SysScanDesc tgscan; HeapTuple htup; + bool found; /* * Check that only named constraints are set explicitly @@ -2285,7 +2554,7 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt) cname))); constr_oid = HeapTupleGetOid(htup); - loid = lappend_oid(loid, constr_oid); + oidlist = lappend_oid(oidlist, constr_oid); found = true; } @@ -2305,34 +2574,28 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt) * Inside of a transaction block set the trigger states of * individual triggers on transaction level. */ - oldcxt = MemoryContextSwitchTo(deferredTriggers->deftrig_cxt); - - foreach(l, loid) + foreach(l, oidlist) { - found = false; - foreach(ls, deferredTriggers->deftrig_trigstates) + Oid tgoid = lfirst_oid(l); + bool found = false; + int i; + + for (i = 0; i < deferredTriggers->state->numstates; i++) { - state = (DeferredTriggerStatus) lfirst(ls); - if (state->dts_tgoid == lfirst_oid(l)) + if (deferredTriggers->state->trigstates[i].dts_tgoid == tgoid) { - state->dts_tgisdeferred = stmt->deferred; + deferredTriggers->state->trigstates[i].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); + deferredTriggers->state = + DeferredTriggerStateAddItem(deferredTriggers->state, + tgoid, stmt->deferred); } } - - MemoryContextSwitchTo(oldcxt); } /* @@ -2347,14 +2610,14 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt) * entire list, in case some deferred events are now immediately * invokable. */ - deferredTriggers->deftrig_events_imm = NULL; + deferredTriggers->events_imm = NULL; } /* ---------- * DeferredTriggerSaveEvent() * - * Called by ExecAR...Triggers() to add the event to the queue. + * 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,9 +2686,10 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, return; /* - * Create a new event + * Create a new event. We use the CurTransactionContext so the event + * will automatically go away if the subtransaction aborts. */ - oldcxt = MemoryContextSwitchTo(deferredTriggers->deftrig_cxt); + oldcxt = MemoryContextSwitchTo(CurTransactionContext); new_size = offsetof(DeferredTriggerEventData, dte_item[0]) + n_enabled_triggers * sizeof(DeferredTriggerEventItem); @@ -2433,6 +2697,7 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, 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,6 +2714,7 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, 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,6 +2783,7 @@ DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, * the trigger at all. */ new_event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE; + new_event->dte_item[i].dti_done_xid = GetCurrentTransactionId(); } } |