📄 trigger.c
字号:
* This trigger item hasn't been called yet. Check if we * should call it now. */ if (immediate_only && deferredTriggerCheckState(event->dte_item[i].dti_tgoid, event->dte_item[i].dti_state)) { still_deferred_ones = true; continue; } /* * So let's fire it... but first, open the correct * relation if this is not the same relation as before. */ if (rel == NULL || rel->rd_id != event->dte_relid) { if (rel) heap_close(rel, NoLock); FreeTriggerDesc(trigdesc); if (finfo) pfree(finfo); /* * We assume that an appropriate lock is still held by * the executor, so grab no new lock here. */ rel = heap_open(event->dte_relid, NoLock); /* * Copy relation's trigger info so that we have a * stable copy no matter what the called triggers do. */ trigdesc = CopyTriggerDesc(rel->trigdesc); if (trigdesc == NULL) /* should not happen */ elog(ERROR, "relation %u has no triggers", event->dte_relid); /* * Allocate space to cache fmgr lookup info for * triggers. */ finfo = (FmgrInfo *) palloc0(trigdesc->numtriggers * sizeof(FmgrInfo)); } DeferredTriggerExecute(event, i, rel, trigdesc, finfo, per_tuple_context); event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE; } /* end loop over items within event */ } /* * If it's now completely done, throw it away. * * NB: it's possible the trigger calls above added more events to the * queue, or that calls we will do later will want to add more, so * we have to be careful about maintaining list validity here. */ next_event = event->dte_next; if (still_deferred_ones) { /* Not done, keep in list */ prev_event = event; } 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; } } event = next_event; } /* 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) heap_close(rel, NoLock); FreeTriggerDesc(trigdesc); if (finfo) pfree(finfo); 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. * ---------- */voidDeferredTriggerInit(void){ /* Nothing to do */ ;}/* ---------- * DeferredTriggerBeginXact() * * Called at transaction start (either BEGIN or implicit for single * statement outside of transaction block). * ---------- */voidDeferredTriggerBeginXact(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;}/* ---------- * DeferredTriggerEndQuery() * * Called after one query sent down by the user has completely been * processed. At this time we invoke all outstanding IMMEDIATE triggers. * ---------- */voidDeferredTriggerEndQuery(void){ /* * Ignore call if we aren't in a transaction. */ if (deferredTriggers == NULL) return; deferredTriggerInvokeEvents(true);}/* ---------- * DeferredTriggerEndXact() * * Called just before the current transaction is committed. At this * time we invoke all DEFERRED triggers and tidy up. * ---------- */voidDeferredTriggerEndXact(void){ /* * Ignore call if we aren't in a transaction. */ if (deferredTriggers == NULL) return; deferredTriggerInvokeEvents(false); deferredTriggers = NULL;}/* ---------- * DeferredTriggerAbortXact() * * The current transaction has entered the abort state. * All outstanding triggers are canceled so we simply throw * away anything we know. * ---------- */voidDeferredTriggerAbortXact(void){ /* * Ignore call if we aren't in a transaction. */ if (deferredTriggers == NULL) return; /* * Forget everything we know about deferred triggers. */ deferredTriggers = NULL;}/* ---------- * DeferredTriggerSetState() * * Called for the SET CONSTRAINTS ... utility command. * ---------- */voidDeferredTriggerSetState(ConstraintsSetStmt *stmt){ List *l; /* * Ignore call if we aren't in a transaction. */ if (deferredTriggers == NULL) return; /* * Handle SET CONSTRAINTS ALL ... */ if (stmt->constraints == NIL) { /* * Drop all per-transaction information about individual trigger * states. */ l = deferredTriggers->deftrig_trigstates; while (l != NIL) { List *next = lnext(l); pfree(lfirst(l)); pfree(l); l = next; } 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; List *ls; List *loid = NIL; /* ---------- * Handle SET CONSTRAINTS constraint-name [, ...] * First lookup all trigger Oid's for the constraint names. * ---------- */ tgrel = heap_openr(TriggerRelationName, AccessShareLock); foreach(l, stmt->constraints) { char *cname = strVal(lfirst(l)); ScanKeyData skey; SysScanDesc tgscan; HeapTuple htup; /* * Check that only named constraints are set explicitly */ if (strlen(cname) == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("unnamed constraints cannot be set explicitly"))); /* * Setup to scan pg_trigger by tgconstrname ... */ ScanKeyEntryInitialize(&skey, (bits16) 0x0, (AttrNumber) Anum_pg_trigger_tgconstrname, (RegProcedure) F_NAMEEQ, PointerGetDatum(cname)); tgscan = systable_beginscan(tgrel, TriggerConstrNameIndex, true, SnapshotNow, 1, &skey); /* * ... and search for the constraint trigger row */ found = false; while (HeapTupleIsValid(htup = systable_getnext(tgscan))) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup); Oid constr_oid; /* * If we found some, check that they fit the deferrability * but skip ON <event> RESTRICT ones, since they are * silently never deferrable. */ if (stmt->deferred && !pg_trigger->tgdeferrable && pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_UPD && pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_DEL) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("constraint \"%s\" is not deferrable", cname))); constr_oid = HeapTupleGetOid(htup); loid = lappendo(loid, constr_oid); found = true; } systable_endscan(tgscan); /* * Not found ? */ if (!found) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("constraint \"%s\" does not exist", cname))); } heap_close(tgrel, AccessShareLock); /* * 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 == lfirsto(l)) { state->dts_tgisdeferred = stmt->deferred; found = true; break; } } if (!found) { state = (DeferredTriggerStatus) palloc(sizeof(DeferredTriggerStatusData)); state->dts_tgoid = lfirsto(l); state->dts_tgisdeferred = stmt->deferred; deferredTriggers->deftrig_trigstates = lappend(deferredTriggers->deftrig_trigstates, state); } } MemoryContextSwitchTo(oldcxt); } /* * SQL99 requires that when a constraint is set to IMMEDIATE, any * deferred checks against that constraint must be made when the SET * CONSTRAINTS command is executed -- i.e. the effects of the SET * CONSTRAINTS command applies retroactively. This happens "for free" * since we have already made the necessary modifications to the * constraints, and deferredTriggerEndQuery() is called by * finish_xact_command(). But we must reset * deferredTriggerInvokeEvents' tail pointer to make it rescan the * 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. * ---------- */static voidDeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, HeapTuple oldtup, HeapTuple newtup){ Relation rel = relinfo->ri_RelationDesc; TriggerDesc *trigdesc = relinfo->ri_TrigDesc; MemoryContext oldcxt; DeferredTriggerEvent new_event; int new_size; int i; int ntriggers; int n_enabled_triggers = 0; int *tgindx; ItemPointerData oldctid; ItemPointerData newctid; if (deferredTriggers == NULL) elog(ERROR, "DeferredTriggerSaveEvent() called outside of transaction"); /* * Get the CTID's of OLD and NEW */ if (oldtup != NULL) ItemPointerCopy(&(oldtup->t_self), &(oldctid)); else ItemPointerSetInvalid(&(oldctid)); if (newtup != NULL) ItemPointerCopy(&(newtup->t_self), &(newctid)); else ItemPointerSetInvalid(&(newctid)); if (row_trigger) { ntriggers = trigdesc->n_after_row[event]; tgindx = trigdesc->tg_after_row[event]; } else { ntriggers = trigdesc->n_after_statement[event]; tgindx = trigdesc->tg_after_statement[event]; } /* * Count the number of triggers that are actually enabled. Since we * only add enabled triggers to the queue, we only need allocate * enough space to hold them (and not any disabled triggers that may * be associated with the relation). */ for (i = 0; i < ntriggers; i++) { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; if (trigger->tgenabled) n_enabled_triggers++; } /* * If all the triggers on this relation are disabled, we're done. */ if (n_enabled_triggers == 0) return; /* * Create a new event */ oldcxt = MemoryContextSwitchTo(deferredTriggers->deftrig_cxt); new_size = offsetof(DeferredTriggerEventData, dte_item[0]) + n_enabled_triggers * sizeof(DeferredTriggerEventItem); new_event =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -