diff options
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r-- | src/backend/commands/trigger.c | 428 |
1 files changed, 232 insertions, 196 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index f4c0ffa0211..1db066681bc 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -90,6 +90,7 @@ static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, int event, bool row_trigger, HeapTuple oldtup, HeapTuple newtup, List *recheckIndexes, Bitmapset *modifiedCols); +static void AfterTriggerEnlargeQueryState(void); /* @@ -3203,9 +3204,7 @@ typedef struct AfterTriggersData int maxtransdepth; /* allocated len of above arrays */ } AfterTriggersData; -typedef AfterTriggersData *AfterTriggers; - -static AfterTriggers afterTriggers; +static AfterTriggersData afterTriggers; static void AfterTriggerExecute(AfterTriggerEvent event, Relation rel, TriggerDesc *trigdesc, @@ -3228,7 +3227,7 @@ GetCurrentFDWTuplestore() { Tuplestorestate *ret; - ret = afterTriggers->fdw_tuplestores[afterTriggers->query_depth]; + ret = afterTriggers.fdw_tuplestores[afterTriggers.query_depth]; if (ret == NULL) { MemoryContext oldcxt; @@ -3255,7 +3254,7 @@ GetCurrentFDWTuplestore() CurrentResourceOwner = saveResourceOwner; MemoryContextSwitchTo(oldcxt); - afterTriggers->fdw_tuplestores[afterTriggers->query_depth] = ret; + afterTriggers.fdw_tuplestores[afterTriggers.query_depth] = ret; } return ret; @@ -3271,7 +3270,7 @@ static bool afterTriggerCheckState(AfterTriggerShared evtshared) { Oid tgoid = evtshared->ats_tgoid; - SetConstraintState state = afterTriggers->state; + SetConstraintState state = afterTriggers.state; int i; /* @@ -3282,19 +3281,22 @@ afterTriggerCheckState(AfterTriggerShared evtshared) return false; /* - * Check if SET CONSTRAINTS has been executed for this specific trigger. + * If constraint state exists, SET CONSTRAINTS might have been executed + * either for this trigger or for all triggers. */ - for (i = 0; i < state->numstates; i++) + if (state != NULL) { - if (state->trigstates[i].sct_tgoid == tgoid) - return state->trigstates[i].sct_tgisdeferred; - } + /* Check for SET CONSTRAINTS for this specific trigger. */ + for (i = 0; i < state->numstates; i++) + { + if (state->trigstates[i].sct_tgoid == tgoid) + return state->trigstates[i].sct_tgisdeferred; + } - /* - * Check if SET CONSTRAINTS ALL has been executed; if so use that. - */ - if (state->all_isset) - return state->all_isdeferred; + /* Check for SET CONSTRAINTS ALL. */ + if (state->all_isset) + return state->all_isdeferred; + } /* * Otherwise return the default state for the trigger. @@ -3331,8 +3333,8 @@ afterTriggerAddEvent(AfterTriggerEventList *events, Size chunksize; /* Create event context if we didn't already */ - if (afterTriggers->event_cxt == NULL) - afterTriggers->event_cxt = + if (afterTriggers.event_cxt == NULL) + afterTriggers.event_cxt = AllocSetContextCreate(TopTransactionContext, "AfterTriggerEvents", ALLOCSET_DEFAULT_MINSIZE, @@ -3375,7 +3377,7 @@ afterTriggerAddEvent(AfterTriggerEventList *events, chunksize /= 2; /* too many shared records */ chunksize = Min(chunksize, MAX_CHUNK_SIZE); } - chunk = MemoryContextAlloc(afterTriggers->event_cxt, chunksize); + chunk = MemoryContextAlloc(afterTriggers.event_cxt, chunksize); chunk->next = NULL; chunk->freeptr = CHUNK_DATA_START(chunk); chunk->endptr = chunk->endfree = (char *) chunk + chunksize; @@ -3706,7 +3708,7 @@ afterTriggerMarkEvents(AfterTriggerEventList *events, /* * Mark it as to be fired in this firing cycle. */ - evtshared->ats_firing_id = afterTriggers->firing_counter; + evtshared->ats_firing_id = afterTriggers.firing_counter; event->ate_flags |= AFTER_TRIGGER_IN_PROGRESS; found = true; } @@ -3898,40 +3900,28 @@ afterTriggerInvokeEvents(AfterTriggerEventList *events, void AfterTriggerBeginXact(void) { - Assert(afterTriggers == NULL); + /* + * Initialize after-trigger state structure to empty + */ + afterTriggers.firing_counter = (CommandId) 1; /* mustn't be 0 */ + afterTriggers.query_depth = -1; /* - * Build empty after-trigger state structure + * Verify that there is no leftover state remaining. If these assertions + * trip, it means that AfterTriggerEndXact wasn't called or didn't clean + * up properly. */ - afterTriggers = (AfterTriggers) - MemoryContextAlloc(TopTransactionContext, - sizeof(AfterTriggersData)); - - afterTriggers->firing_counter = (CommandId) 1; /* mustn't be 0 */ - afterTriggers->state = SetConstraintStateCreate(8); - afterTriggers->events.head = NULL; - afterTriggers->events.tail = NULL; - afterTriggers->events.tailfree = NULL; - afterTriggers->query_depth = -1; - - /* We initialize the arrays to a reasonable size */ - afterTriggers->query_stack = (AfterTriggerEventList *) - MemoryContextAlloc(TopTransactionContext, - 8 * sizeof(AfterTriggerEventList)); - afterTriggers->fdw_tuplestores = (Tuplestorestate **) - MemoryContextAllocZero(TopTransactionContext, - 8 * sizeof(Tuplestorestate *)); - afterTriggers->maxquerydepth = 8; - - /* Context for events is created only when needed */ - afterTriggers->event_cxt = NULL; - - /* Subtransaction stack is empty until/unless needed */ - afterTriggers->state_stack = NULL; - afterTriggers->events_stack = NULL; - afterTriggers->depth_stack = NULL; - afterTriggers->firing_stack = NULL; - afterTriggers->maxtransdepth = 0; + Assert(afterTriggers.state == NULL); + Assert(afterTriggers.query_stack == NULL); + Assert(afterTriggers.fdw_tuplestores == NULL); + Assert(afterTriggers.maxquerydepth == 0); + Assert(afterTriggers.event_cxt == NULL); + Assert(afterTriggers.events.head == NULL); + Assert(afterTriggers.state_stack == NULL); + Assert(afterTriggers.events_stack == NULL); + Assert(afterTriggers.depth_stack == NULL); + Assert(afterTriggers.firing_stack == NULL); + Assert(afterTriggers.maxtransdepth == 0); } @@ -3939,48 +3929,15 @@ AfterTriggerBeginXact(void) * AfterTriggerBeginQuery() * * Called just before we start processing a single query within a - * transaction (or subtransaction). Set up to record AFTER trigger - * events queued by the query. Note that it is allowed to have - * nested queries within a (sub)transaction. + * transaction (or subtransaction). Most of the real work gets deferred + * until somebody actually tries to queue a trigger event. * ---------- */ void AfterTriggerBeginQuery(void) { - AfterTriggerEventList *events; - - /* Must be inside a transaction */ - Assert(afterTriggers != NULL); - /* Increase the query stack depth */ - afterTriggers->query_depth++; - - /* - * Allocate more space in the query stack if needed. - */ - if (afterTriggers->query_depth >= afterTriggers->maxquerydepth) - { - /* repalloc will keep the stack in the same context */ - int old_alloc = afterTriggers->maxquerydepth; - int new_alloc = old_alloc * 2; - - afterTriggers->query_stack = (AfterTriggerEventList *) - repalloc(afterTriggers->query_stack, - new_alloc * sizeof(AfterTriggerEventList)); - afterTriggers->fdw_tuplestores = (Tuplestorestate **) - repalloc(afterTriggers->fdw_tuplestores, - new_alloc * sizeof(Tuplestorestate *)); - /* Clear newly-allocated slots for subsequent lazy initialization. */ - memset(afterTriggers->fdw_tuplestores + old_alloc, - 0, (new_alloc - old_alloc) * sizeof(Tuplestorestate *)); - afterTriggers->maxquerydepth = new_alloc; - } - - /* Initialize this query's list to empty */ - events = &afterTriggers->query_stack[afterTriggers->query_depth]; - events->head = NULL; - events->tail = NULL; - events->tailfree = NULL; + afterTriggers.query_depth++; } @@ -4002,11 +3959,18 @@ AfterTriggerEndQuery(EState *estate) AfterTriggerEventList *events; Tuplestorestate *fdw_tuplestore; - /* Must be inside a transaction */ - Assert(afterTriggers != NULL); - /* Must be inside a query, too */ - Assert(afterTriggers->query_depth >= 0); + Assert(afterTriggers.query_depth >= 0); + + /* + * If we never even got as far as initializing the event stack, there + * certainly won't be any events, so exit quickly. + */ + if (afterTriggers.query_depth >= afterTriggers.maxquerydepth) + { + afterTriggers.query_depth--; + return; + } /* * Process all immediate-mode triggers queued by the query, and move the @@ -4032,10 +3996,10 @@ AfterTriggerEndQuery(EState *estate) */ for (;;) { - events = &afterTriggers->query_stack[afterTriggers->query_depth]; - if (afterTriggerMarkEvents(events, &afterTriggers->events, true)) + events = &afterTriggers.query_stack[afterTriggers.query_depth]; + if (afterTriggerMarkEvents(events, &afterTriggers.events, true)) { - CommandId firing_id = afterTriggers->firing_counter++; + CommandId firing_id = afterTriggers.firing_counter++; /* OK to delete the immediate events after processing them */ if (afterTriggerInvokeEvents(events, firing_id, estate, true)) @@ -4046,15 +4010,15 @@ AfterTriggerEndQuery(EState *estate) } /* Release query-local storage for events, including tuplestore if any */ - fdw_tuplestore = afterTriggers->fdw_tuplestores[afterTriggers->query_depth]; + fdw_tuplestore = afterTriggers.fdw_tuplestores[afterTriggers.query_depth]; if (fdw_tuplestore) { tuplestore_end(fdw_tuplestore); - afterTriggers->fdw_tuplestores[afterTriggers->query_depth] = NULL; + afterTriggers.fdw_tuplestores[afterTriggers.query_depth] = NULL; } - afterTriggerFreeEventList(&afterTriggers->query_stack[afterTriggers->query_depth]); + afterTriggerFreeEventList(&afterTriggers.query_stack[afterTriggers.query_depth]); - afterTriggers->query_depth--; + afterTriggers.query_depth--; } @@ -4075,18 +4039,15 @@ AfterTriggerFireDeferred(void) AfterTriggerEventList *events; bool snap_pushed = false; - /* Must be inside a transaction */ - Assert(afterTriggers != NULL); - - /* ... but not inside a query */ - Assert(afterTriggers->query_depth == -1); + /* Must not be inside a query */ + Assert(afterTriggers.query_depth == -1); /* * If there are any triggers to fire, make sure we have set a snapshot for * them to use. (Since PortalRunUtility doesn't set a snap for COMMIT, we * can't assume ActiveSnapshot is valid on entry.) */ - events = &afterTriggers->events; + events = &afterTriggers.events; if (events->head != NULL) { PushActiveSnapshot(GetTransactionSnapshot()); @@ -4099,7 +4060,7 @@ AfterTriggerFireDeferred(void) */ while (afterTriggerMarkEvents(events, NULL, false)) { - CommandId firing_id = afterTriggers->firing_counter++; + CommandId firing_id = afterTriggers.firing_counter++; if (afterTriggerInvokeEvents(events, firing_id, NULL, true)) break; /* all fired */ @@ -4132,7 +4093,7 @@ void AfterTriggerEndXact(bool isCommit) { /* - * Forget everything we know about AFTER triggers. + * Forget the pending-events list. * * Since all the info is in TopTransactionContext or children thereof, we * don't really need to do anything to reclaim memory. However, the @@ -4140,10 +4101,39 @@ AfterTriggerEndXact(bool isCommit) * soon as possible --- especially if we are aborting because we ran out * of memory for the list! */ - if (afterTriggers && afterTriggers->event_cxt) - MemoryContextDelete(afterTriggers->event_cxt); + if (afterTriggers.event_cxt) + { + MemoryContextDelete(afterTriggers.event_cxt); + afterTriggers.event_cxt = NULL; + afterTriggers.events.head = NULL; + afterTriggers.events.tail = NULL; + afterTriggers.events.tailfree = NULL; + } + + /* + * Forget any subtransaction state as well. Since this can't be very + * large, we let the eventual reset of TopTransactionContext free the + * memory instead of doing it here. + */ + afterTriggers.state_stack = NULL; + afterTriggers.events_stack = NULL; + afterTriggers.depth_stack = NULL; + afterTriggers.firing_stack = NULL; + afterTriggers.maxtransdepth = 0; + + + /* + * Forget the query stack and constrant-related state information. As + * with the subtransaction state information, we don't bother freeing the + * memory here. + */ + afterTriggers.query_stack = NULL; + afterTriggers.fdw_tuplestores = NULL; + afterTriggers.maxquerydepth = 0; + afterTriggers.state = NULL; - afterTriggers = NULL; + /* No more afterTriggers manipulation until next transaction starts. */ + afterTriggers.query_depth = -1; } /* @@ -4157,56 +4147,49 @@ AfterTriggerBeginSubXact(void) int my_level = GetCurrentTransactionNestLevel(); /* - * Ignore call if the transaction is in aborted state. (Probably - * shouldn't happen?) - */ - if (afterTriggers == NULL) - return; - - /* * Allocate more space in the stacks if needed. (Note: because the * minimum nest level of a subtransaction is 2, we waste the first couple * entries of each array; not worth the notational effort to avoid it.) */ - while (my_level >= afterTriggers->maxtransdepth) + while (my_level >= afterTriggers.maxtransdepth) { - if (afterTriggers->maxtransdepth == 0) + if (afterTriggers.maxtransdepth == 0) { MemoryContext old_cxt; old_cxt = MemoryContextSwitchTo(TopTransactionContext); #define DEFTRIG_INITALLOC 8 - afterTriggers->state_stack = (SetConstraintState *) + afterTriggers.state_stack = (SetConstraintState *) palloc(DEFTRIG_INITALLOC * sizeof(SetConstraintState)); - afterTriggers->events_stack = (AfterTriggerEventList *) + afterTriggers.events_stack = (AfterTriggerEventList *) palloc(DEFTRIG_INITALLOC * sizeof(AfterTriggerEventList)); - afterTriggers->depth_stack = (int *) + afterTriggers.depth_stack = (int *) palloc(DEFTRIG_INITALLOC * sizeof(int)); - afterTriggers->firing_stack = (CommandId *) + afterTriggers.firing_stack = (CommandId *) palloc(DEFTRIG_INITALLOC * sizeof(CommandId)); - afterTriggers->maxtransdepth = DEFTRIG_INITALLOC; + afterTriggers.maxtransdepth = DEFTRIG_INITALLOC; MemoryContextSwitchTo(old_cxt); } else { /* repalloc will keep the stacks in the same context */ - int new_alloc = afterTriggers->maxtransdepth * 2; + int new_alloc = afterTriggers.maxtransdepth * 2; - afterTriggers->state_stack = (SetConstraintState *) - repalloc(afterTriggers->state_stack, + afterTriggers.state_stack = (SetConstraintState *) + repalloc(afterTriggers.state_stack, new_alloc * sizeof(SetConstraintState)); - afterTriggers->events_stack = (AfterTriggerEventList *) - repalloc(afterTriggers->events_stack, + afterTriggers.events_stack = (AfterTriggerEventList *) + repalloc(afterTriggers.events_stack, new_alloc * sizeof(AfterTriggerEventList)); - afterTriggers->depth_stack = (int *) - repalloc(afterTriggers->depth_stack, + afterTriggers.depth_stack = (int *) + repalloc(afterTriggers.depth_stack, new_alloc * sizeof(int)); - afterTriggers->firing_stack = (CommandId *) - repalloc(afterTriggers->firing_stack, + afterTriggers.firing_stack = (CommandId *) + repalloc(afterTriggers.firing_stack, new_alloc * sizeof(CommandId)); - afterTriggers->maxtransdepth = new_alloc; + afterTriggers.maxtransdepth = new_alloc; } } @@ -4215,10 +4198,10 @@ AfterTriggerBeginSubXact(void) * is not saved until/unless changed. Likewise, we don't make a * per-subtransaction event context until needed. */ - afterTriggers->state_stack[my_level] = NULL; - afterTriggers->events_stack[my_level] = afterTriggers->events; - afterTriggers->depth_stack[my_level] = afterTriggers->query_depth; - afterTriggers->firing_stack[my_level] = afterTriggers->firing_counter; + afterTriggers.state_stack[my_level] = NULL; + afterTriggers.events_stack[my_level] = afterTriggers.events; + afterTriggers.depth_stack[my_level] = afterTriggers.query_depth; + afterTriggers.firing_stack[my_level] = afterTriggers.firing_counter; } /* @@ -4236,26 +4219,19 @@ AfterTriggerEndSubXact(bool isCommit) CommandId subxact_firing_id; /* - * Ignore call if the transaction is in aborted state. (Probably - * unneeded) - */ - if (afterTriggers == NULL) - return; - - /* * Pop the prior state if needed. */ if (isCommit) { - Assert(my_level < afterTriggers->maxtransdepth); + Assert(my_level < afterTriggers.maxtransdepth); /* If we saved a prior state, we don't need it anymore */ - state = afterTriggers->state_stack[my_level]; + state = afterTriggers.state_stack[my_level]; if (state != NULL) pfree(state); /* this avoids double pfree if error later: */ - afterTriggers->state_stack[my_level] = NULL; - Assert(afterTriggers->query_depth == - afterTriggers->depth_stack[my_level]); + afterTriggers.state_stack[my_level] = NULL; + Assert(afterTriggers.query_depth == + afterTriggers.depth_stack[my_level]); } else { @@ -4264,7 +4240,7 @@ AfterTriggerEndSubXact(bool isCommit) * AfterTriggerBeginSubXact, in which case we mustn't risk touching * stack levels that aren't there. */ - if (my_level >= afterTriggers->maxtransdepth) + if (my_level >= afterTriggers.maxtransdepth) return; /* @@ -4273,42 +4249,46 @@ AfterTriggerEndSubXact(bool isCommit) * subtransaction will not add events to query levels started in a * earlier transaction state. */ - while (afterTriggers->query_depth > afterTriggers->depth_stack[my_level]) + while (afterTriggers.query_depth > afterTriggers.depth_stack[my_level]) { - Tuplestorestate *ts; - - ts = afterTriggers->fdw_tuplestores[afterTriggers->query_depth]; - if (ts) + if (afterTriggers.query_depth < afterTriggers.maxquerydepth) { - tuplestore_end(ts); - afterTriggers->fdw_tuplestores[afterTriggers->query_depth] = NULL; + Tuplestorestate *ts; + + ts = afterTriggers.fdw_tuplestores[afterTriggers.query_depth]; + if (ts) + { + tuplestore_end(ts); + afterTriggers.fdw_tuplestores[afterTriggers.query_depth] = NULL; + } + + afterTriggerFreeEventList(&afterTriggers.query_stack[afterTriggers.query_depth]); } - afterTriggerFreeEventList(&afterTriggers->query_stack[afterTriggers->query_depth]); - afterTriggers->query_depth--; + afterTriggers.query_depth--; } - Assert(afterTriggers->query_depth == - afterTriggers->depth_stack[my_level]); + Assert(afterTriggers.query_depth == + afterTriggers.depth_stack[my_level]); /* * Restore the global deferred-event list to its former length, * discarding any events queued by the subxact. */ - afterTriggerRestoreEventList(&afterTriggers->events, - &afterTriggers->events_stack[my_level]); + afterTriggerRestoreEventList(&afterTriggers.events, + &afterTriggers.events_stack[my_level]); /* * Restore the trigger state. If the saved state is NULL, then this * subxact didn't save it, so it doesn't need restoring. */ - state = afterTriggers->state_stack[my_level]; + state = afterTriggers.state_stack[my_level]; if (state != NULL) { - pfree(afterTriggers->state); - afterTriggers->state = state; + pfree(afterTriggers.state); + afterTriggers.state = state; } /* this avoids double pfree if error later: */ - afterTriggers->state_stack[my_level] = NULL; + afterTriggers.state_stack[my_level] = NULL; /* * Scan for any remaining deferred events that were marked DONE or IN @@ -4318,8 +4298,8 @@ AfterTriggerEndSubXact(bool isCommit) * (This essentially assumes that the current subxact includes all * subxacts started after it.) */ - subxact_firing_id = afterTriggers->firing_stack[my_level]; - for_each_event_chunk(event, chunk, afterTriggers->events) + subxact_firing_id = afterTriggers.firing_stack[my_level]; + for_each_event_chunk(event, chunk, afterTriggers.events) { AfterTriggerShared evtshared = GetTriggerSharedData(event); @@ -4334,6 +4314,66 @@ AfterTriggerEndSubXact(bool isCommit) } } +/* ---------- + * AfterTriggerEnlargeQueryState() + * + * Prepare the necessary state so that we can record AFTER trigger events + * queued by a query. It is allowed to have nested queries within a + * (sub)transaction, so we need to have separate state for each query + * nesting level. + * ---------- + */ +static void +AfterTriggerEnlargeQueryState(void) +{ + int init_depth = afterTriggers.maxquerydepth; + + Assert(afterTriggers.query_depth >= afterTriggers.maxquerydepth); + + if (afterTriggers.maxquerydepth == 0) + { + int new_alloc = Max(afterTriggers.query_depth, 8); + + afterTriggers.query_stack = (AfterTriggerEventList *) + MemoryContextAlloc(TopTransactionContext, + new_alloc * sizeof(AfterTriggerEventList)); + afterTriggers.fdw_tuplestores = (Tuplestorestate **) + MemoryContextAllocZero(TopTransactionContext, + new_alloc * sizeof(Tuplestorestate *)); + afterTriggers.maxquerydepth = new_alloc; + } + else + { + /* repalloc will keep the stack in the same context */ + int old_alloc = afterTriggers.maxquerydepth; + int new_alloc = Max(afterTriggers.query_depth, old_alloc * 2); + + afterTriggers.query_stack = (AfterTriggerEventList *) + repalloc(afterTriggers.query_stack, + new_alloc * sizeof(AfterTriggerEventList)); + afterTriggers.fdw_tuplestores = (Tuplestorestate **) + repalloc(afterTriggers.fdw_tuplestores, + new_alloc * sizeof(Tuplestorestate *)); + /* Clear newly-allocated slots for subsequent lazy initialization. */ + memset(afterTriggers.fdw_tuplestores + old_alloc, + 0, (new_alloc - old_alloc) * sizeof(Tuplestorestate *)); + afterTriggers.maxquerydepth = new_alloc; + } + + /* Initialize new query lists to empty */ + while (init_depth < afterTriggers.maxquerydepth) + { + AfterTriggerEventList *events; + + events = &afterTriggers.query_stack[init_depth]; + events->head = NULL; + events->tail = NULL; + events->tailfree = NULL; + + ++init_depth; + } +} + /* * Create an empty SetConstraintState with room for numalloc trigstates */ @@ -4417,21 +4457,19 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt) { int my_level = GetCurrentTransactionNestLevel(); - /* - * Ignore call if we aren't in a transaction. (Shouldn't happen?) - */ - if (afterTriggers == NULL) - return; + /* If we haven't already done so, initialize our state. */ + if (afterTriggers.state == NULL) + afterTriggers.state = SetConstraintStateCreate(8); /* * 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 (my_level > 1 && - afterTriggers->state_stack[my_level] == NULL) + afterTriggers.state_stack[my_level] == NULL) { - afterTriggers->state_stack[my_level] = - SetConstraintStateCopy(afterTriggers->state); + afterTriggers.state_stack[my_level] = + SetConstraintStateCopy(afterTriggers.state); } /* @@ -4442,13 +4480,13 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt) /* * Forget any previous SET CONSTRAINTS commands in this transaction. */ - afterTriggers->state->numstates = 0; + afterTriggers.state->numstates = 0; /* * Set the per-transaction ALL state to known. */ - afterTriggers->state->all_isset = true; - afterTriggers->state->all_isdeferred = stmt->deferred; + afterTriggers.state->all_isset = true; + afterTriggers.state->all_isdeferred = stmt->deferred; } else { @@ -4622,7 +4660,7 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt) foreach(lc, tgoidlist) { Oid tgoid = lfirst_oid(lc); - SetConstraintState state = afterTriggers->state; + SetConstraintState state = afterTriggers.state; bool found = false; int i; @@ -4637,7 +4675,7 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt) } if (!found) { - afterTriggers->state = + afterTriggers.state = SetConstraintStateAddItem(state, tgoid, stmt->deferred); } } @@ -4656,12 +4694,12 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt) */ if (!stmt->deferred) { - AfterTriggerEventList *events = &afterTriggers->events; + AfterTriggerEventList *events = &afterTriggers.events; bool snapshot_set = false; while (afterTriggerMarkEvents(events, NULL, true)) { - CommandId firing_id = afterTriggers->firing_counter++; + CommandId firing_id = afterTriggers.firing_counter++; /* * Make sure a snapshot has been established in case trigger @@ -4715,12 +4753,8 @@ AfterTriggerPendingOnRel(Oid relid) AfterTriggerEventChunk *chunk; int depth; - /* No-op if we aren't in a transaction. (Shouldn't happen?) */ - if (afterTriggers == NULL) - return false; - /* Scan queued events */ - for_each_event_chunk(event, chunk, afterTriggers->events) + for_each_event_chunk(event, chunk, afterTriggers.events) { AfterTriggerShared evtshared = GetTriggerSharedData(event); @@ -4741,9 +4775,9 @@ AfterTriggerPendingOnRel(Oid relid) * if TRUNCATE/etc is executed by a function or trigger within an updating * query on the same relation, which is pretty perverse, but let's check. */ - for (depth = 0; depth <= afterTriggers->query_depth; depth++) + for (depth = 0; depth <= afterTriggers.query_depth; depth++) { - for_each_event_chunk(event, chunk, afterTriggers->query_stack[depth]) + for_each_event_chunk(event, chunk, afterTriggers.query_stack[depth]) { AfterTriggerShared evtshared = GetTriggerSharedData(event); @@ -4787,15 +4821,17 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, Tuplestorestate *fdw_tuplestore = NULL; /* - * Check state. We use normal tests not Asserts because it is possible to + * Check state. We use a normal test not Assert because it is possible to * reach here in the wrong state given misconfigured RI triggers, in * particular deferring a cascade action trigger. */ - if (afterTriggers == NULL) - elog(ERROR, "AfterTriggerSaveEvent() called outside of transaction"); - if (afterTriggers->query_depth < 0) + if (afterTriggers.query_depth < 0) elog(ERROR, "AfterTriggerSaveEvent() called outside of query"); + /* Be sure we have enough space to record events at this query depth. */ + if (afterTriggers.query_depth >= afterTriggers.maxquerydepth) + AfterTriggerEnlargeQueryState(); + /* * Validate the event code and collect the associated tuple CTIDs. * @@ -4959,7 +4995,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, new_shared.ats_relid = RelationGetRelid(rel); new_shared.ats_firing_id = 0; - afterTriggerAddEvent(&afterTriggers->query_stack[afterTriggers->query_depth], + afterTriggerAddEvent(&afterTriggers.query_stack[afterTriggers.query_depth], &new_event, &new_shared); } |