⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 trigger.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 5 页
字号:
		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 + -