📄 trigger.c
字号:
DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE, false, NULL, NULL);}HeapTupleExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple newtuple, CommandId cid){ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; int ntrigs = trigdesc->n_before_row[TRIGGER_EVENT_UPDATE]; int *tgindx = trigdesc->tg_before_row[TRIGGER_EVENT_UPDATE]; TriggerData LocTriggerData; HeapTuple trigtuple; HeapTuple oldtuple; HeapTuple intuple = newtuple; TupleTableSlot *newSlot; int i; trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, cid, &newSlot); if (trigtuple == NULL) return NULL; /* * In READ COMMITTED isolation level it's possible that newtuple was * changed due to concurrent update. */ if (newSlot != NULL) intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot); /* Allocate cache space for fmgr lookup info, if not done yet */ if (relinfo->ri_TrigFunctions == NULL) relinfo->ri_TrigFunctions = (FmgrInfo *) palloc0(trigdesc->numtriggers * sizeof(FmgrInfo)); LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE; LocTriggerData.tg_relation = relinfo->ri_RelationDesc; for (i = 0; i < ntrigs; i++) { Trigger *trigger = &trigdesc->triggers[tgindx[i]]; if (!trigger->tgenabled) continue; LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_newtuple = oldtuple = newtuple; LocTriggerData.tg_trigger = trigger; newtuple = ExecCallTriggerFunc(&LocTriggerData, relinfo->ri_TrigFunctions + tgindx[i], GetPerTupleMemoryContext(estate)); if (oldtuple != newtuple && oldtuple != intuple) heap_freetuple(oldtuple); if (newtuple == NULL) break; } heap_freetuple(trigtuple); return newtuple;}voidExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid, HeapTuple newtuple){ TriggerDesc *trigdesc = relinfo->ri_TrigDesc; if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0) { HeapTuple trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, (CommandId) 0, NULL); DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE, true, trigtuple, newtuple); heap_freetuple(trigtuple); }}static HeapTupleGetTupleForTrigger(EState *estate, ResultRelInfo *relinfo, ItemPointer tid, CommandId cid, TupleTableSlot **newSlot){ Relation relation = relinfo->ri_RelationDesc; HeapTupleData tuple; HeapTuple result; Buffer buffer; if (newSlot != NULL) { int test; /* * mark tuple for update */ *newSlot = NULL; tuple.t_self = *tid;ltrmark:; test = heap_mark4update(relation, &tuple, &buffer, cid); switch (test) { case HeapTupleSelfUpdated: /* treat it as deleted; do not process */ ReleaseBuffer(buffer); return NULL; case HeapTupleMayBeUpdated: break; case HeapTupleUpdated: ReleaseBuffer(buffer); if (XactIsoLevel == XACT_SERIALIZABLE) ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("could not serialize access due to concurrent update"))); else if (!(ItemPointerEquals(&(tuple.t_self), tid))) { TupleTableSlot *epqslot = EvalPlanQual(estate, relinfo->ri_RangeTableIndex, &(tuple.t_self)); if (!(TupIsNull(epqslot))) { *tid = tuple.t_self; *newSlot = epqslot; goto ltrmark; } } /* * if tuple was deleted or PlanQual failed for updated * tuple - we have not process this tuple! */ return NULL; default: ReleaseBuffer(buffer); elog(ERROR, "unrecognized heap_mark4update status: %u", test); return NULL; /* keep compiler quiet */ } } else { PageHeader dp; ItemId lp; buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); if (!BufferIsValid(buffer)) elog(ERROR, "ReadBuffer failed"); dp = (PageHeader) BufferGetPage(buffer); lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); Assert(ItemIdIsUsed(lp)); tuple.t_datamcxt = NULL; tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); tuple.t_len = ItemIdGetLength(lp); tuple.t_self = *tid; } result = heap_copytuple(&tuple); ReleaseBuffer(buffer); return result;}/* ---------- * 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() * * Returns true if the trigger identified by tgoid is actually * in state DEFERRED. * ---------- */static booldeferredTriggerCheckState(Oid tgoid, int32 itemstate){ MemoryContext oldcxt; List *sl; DeferredTriggerStatus trigstate; /* * 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; /* * Lookup if we know an individual state for this trigger */ foreach(sl, deferredTriggers->deftrig_trigstates) { trigstate = (DeferredTriggerStatus) lfirst(sl); if (trigstate->dts_tgoid == tgoid) return trigstate->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; /* * 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); return trigstate->dts_tgisdeferred;}/* ---------- * deferredTriggerAddEvent() * * Add a new trigger event to the queue. * ---------- */static voiddeferredTriggerAddEvent(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; }}/* ---------- * DeferredTriggerExecute() * * Fetch the required tuples back from the heap and fire one * single trigger function. * * Frequently, this will be fired many times in a row for triggers of * a single relation. Therefore, we cache the open relation and provide * fmgr lookup cache space at the caller level. * * event: event currently being fired. * itemno: item within event currently being fired. * rel: open relation for event. * trigdesc: working copy of rel's trigger info. * finfo: array of fmgr lookup cache entries (one per trigger in trigdesc). * per_tuple_context: memory context to call trigger function in. * ---------- */static voidDeferredTriggerExecute(DeferredTriggerEvent event, int itemno, Relation rel, TriggerDesc *trigdesc, FmgrInfo *finfo, MemoryContext per_tuple_context){ Oid tgoid = event->dte_item[itemno].dti_tgoid; TriggerData LocTriggerData; HeapTupleData oldtuple; HeapTupleData newtuple; HeapTuple rettuple; Buffer oldbuffer; Buffer newbuffer; int tgindx; /* * Fetch the required OLD and NEW tuples. */ if (ItemPointerIsValid(&(event->dte_oldctid))) { ItemPointerCopy(&(event->dte_oldctid), &(oldtuple.t_self)); if (!heap_fetch(rel, SnapshotAny, &oldtuple, &oldbuffer, false, NULL)) elog(ERROR, "failed to fetch old tuple for deferred trigger"); } if (ItemPointerIsValid(&(event->dte_newctid))) { ItemPointerCopy(&(event->dte_newctid), &(newtuple.t_self)); if (!heap_fetch(rel, SnapshotAny, &newtuple, &newbuffer, false, NULL)) elog(ERROR, "failed to fetch new tuple for deferred trigger"); } /* * Setup the trigger information */ LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = (event->dte_event & TRIGGER_EVENT_OPMASK) | (event->dte_event & TRIGGER_EVENT_ROW); LocTriggerData.tg_relation = rel; LocTriggerData.tg_trigger = NULL; for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++) { if (trigdesc->triggers[tgindx].tgoid == tgoid) { LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]); break; } } if (LocTriggerData.tg_trigger == NULL) elog(ERROR, "could not find trigger %u", tgoid); switch (event->dte_event & TRIGGER_EVENT_OPMASK) { case TRIGGER_EVENT_INSERT: LocTriggerData.tg_trigtuple = &newtuple; LocTriggerData.tg_newtuple = NULL; break; case TRIGGER_EVENT_UPDATE: LocTriggerData.tg_trigtuple = &oldtuple; LocTriggerData.tg_newtuple = &newtuple; break; case TRIGGER_EVENT_DELETE: LocTriggerData.tg_trigtuple = &oldtuple; LocTriggerData.tg_newtuple = NULL; break; } /* * Call the trigger and throw away any eventually returned updated * tuple. */ rettuple = ExecCallTriggerFunc(&LocTriggerData, finfo + tgindx, per_tuple_context); if (rettuple != NULL && rettuple != &oldtuple && rettuple != &newtuple) heap_freetuple(rettuple); /* * Release buffers */ if (ItemPointerIsValid(&(event->dte_oldctid))) ReleaseBuffer(oldbuffer); if (ItemPointerIsValid(&(event->dte_newctid))) ReleaseBuffer(newbuffer);}/* ---------- * deferredTriggerInvokeEvents() * * Scan the event queue for not yet invoked triggers. Check if they * should be invoked now and do so. * ---------- */static voiddeferredTriggerInvokeEvents(bool immediate_only){ DeferredTriggerEvent event, prev_event; MemoryContext per_tuple_context; Relation rel = NULL; TriggerDesc *trigdesc = NULL; FmgrInfo *finfo = NULL; /* * If immediate_only is true, we remove fully-processed events from * the event queue to recycle space. If immediate_only is false, we * are going to discard the whole event queue on return anyway, so no * need to bother with "retail" pfree's. * * If immediate_only is true, we need only scan from where the end of the * queue was at the previous deferredTriggerInvokeEvents call; any * non-deferred events before that point are already fired. (But if * the deferral state changes, we must reset the saved position to the * beginning of the queue, so as to process all events once with the * new states. See DeferredTriggerSetState.) */ /* Make a per-tuple memory context for trigger function calls */ per_tuple_context = AllocSetContextCreate(CurrentMemoryContext, "DeferredTriggerTupleContext", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* * 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) { bool still_deferred_ones = false; DeferredTriggerEvent next_event; int i; /* * Check if event is already completely done. */ if (!(event->dte_event & (TRIGGER_DEFERRED_DONE | TRIGGER_DEFERRED_CANCELED))) { MemoryContextReset(per_tuple_context); /* * Check each trigger item in the event. */ for (i = 0; i < event->dte_n_items; i++) { if (event->dte_item[i].dti_state & TRIGGER_DEFERRED_DONE) continue; /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -