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

📄 trigger.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 5 页
字号:
 * * maxquerydepth is just the allocated length of query_stack. * * state_stack is a stack of pointers to saved copies of the SET CONSTRAINTS * state data; each subtransaction level that modifies that state first * saves a copy, which we use to restore the state if we abort. * * events_stack is a stack of copies of the events head/tail pointers, * which we use to restore those values during subtransaction abort. * * depth_stack is a stack of copies of subtransaction-start-time query_depth, * which we similarly use to clean up at subtransaction abort. * * firing_stack is a stack of copies of subtransaction-start-time * firing_counter.	We use this to recognize which deferred triggers were * fired (or marked for firing) within an aborted subtransaction. * * We use GetCurrentTransactionNestLevel() to determine the correct array * index in these stacks.  maxtransdepth is the number of allocated entries in * each stack.	(By not keeping our own stack pointer, we can avoid trouble * in cases where errors during subxact abort cause multiple invocations * of AfterTriggerEndSubXact() at the same nesting depth.) */typedef struct AfterTriggersData{	CommandId	firing_counter; /* next firing ID to assign */	SetConstraintState state;	/* the active S C state */	AfterTriggerEventList events;		/* deferred-event list */	int			query_depth;	/* current query list index */	AfterTriggerEventList *query_stack; /* events pending from each query */	int			maxquerydepth;	/* allocated len of above array */	/* these fields are just for resetting at subtrans abort: */	SetConstraintState *state_stack;	/* stacked S C states */	AfterTriggerEventList *events_stack;		/* stacked list pointers */	int		   *depth_stack;	/* stacked query_depths */	CommandId  *firing_stack;	/* stacked firing_counters */	int			maxtransdepth;	/* allocated len of above arrays */} AfterTriggersData;typedef AfterTriggersData *AfterTriggers;static AfterTriggers afterTriggers;static void AfterTriggerExecute(AfterTriggerEvent event,					Relation rel, TriggerDesc *trigdesc,					FmgrInfo *finfo,					Instrumentation *instr,					MemoryContext per_tuple_context);static SetConstraintState SetConstraintStateCreate(int numalloc);static SetConstraintState SetConstraintStateCopy(SetConstraintState state);static SetConstraintState SetConstraintStateAddItem(SetConstraintState state,						  Oid tgoid, bool tgisdeferred);/* ---------- * afterTriggerCheckState() * *	Returns true if the trigger identified by tgoid is actually *	in state DEFERRED. * ---------- */static boolafterTriggerCheckState(Oid tgoid, TriggerEvent eventstate){	SetConstraintState state = afterTriggers->state;	int			i;	/*	 * For not-deferrable triggers (i.e. normal AFTER ROW triggers and	 * constraints declared NOT DEFERRABLE), the state is always false.	 */	if ((eventstate & AFTER_TRIGGER_DEFERRABLE) == 0)		return false;	/*	 * Check if SET CONSTRAINTS has been executed 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;	/*	 * Otherwise return the default state for the trigger.	 */	return ((eventstate & AFTER_TRIGGER_INITDEFERRED) != 0);}/* ---------- * afterTriggerAddEvent() * *	Add a new trigger event to the current query's queue. * ---------- */static voidafterTriggerAddEvent(AfterTriggerEvent event){	AfterTriggerEventList *events;	Assert(event->ate_next == NULL);	/* Must be inside a query */	Assert(afterTriggers->query_depth >= 0);	events = &afterTriggers->query_stack[afterTriggers->query_depth];	if (events->tail == NULL)	{		/* first list entry */		events->head = event;		events->tail = event;	}	else	{		events->tail->ate_next = event;		events->tail = event;	}}/* ---------- * AfterTriggerExecute() * *	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.  (For triggers fired at *	the end of a query, we can even piggyback on the executor's state.) * *	event: 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). *	instr: array of EXPLAIN ANALYZE instrumentation nodes (one per trigger), *		or NULL if no instrumentation is wanted. *	per_tuple_context: memory context to call trigger function in. * ---------- */static voidAfterTriggerExecute(AfterTriggerEvent event,					Relation rel, TriggerDesc *trigdesc,					FmgrInfo *finfo, Instrumentation *instr,					MemoryContext per_tuple_context){	Oid			tgoid = event->ate_tgoid;	TriggerData LocTriggerData;	HeapTupleData oldtuple;	HeapTupleData newtuple;	HeapTuple	rettuple;	Buffer		oldbuffer = InvalidBuffer;	Buffer		newbuffer = InvalidBuffer;	int			tgindx;	/*	 * Locate trigger in trigdesc.	 */	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);	/*	 * If doing EXPLAIN ANALYZE, start charging time to this trigger. We want	 * to include time spent re-fetching tuples in the trigger cost.	 */	if (instr)		InstrStartNode(instr + tgindx);	/*	 * Fetch the required OLD and NEW tuples.	 */	if (ItemPointerIsValid(&(event->ate_oldctid)))	{		ItemPointerCopy(&(event->ate_oldctid), &(oldtuple.t_self));		if (!heap_fetch(rel, SnapshotAny, &oldtuple, &oldbuffer, false, NULL))			elog(ERROR, "failed to fetch old tuple for AFTER trigger");	}	if (ItemPointerIsValid(&(event->ate_newctid)))	{		ItemPointerCopy(&(event->ate_newctid), &(newtuple.t_self));		if (!heap_fetch(rel, SnapshotAny, &newtuple, &newbuffer, false, NULL))			elog(ERROR, "failed to fetch new tuple for AFTER trigger");	}	/*	 * Setup the trigger information	 */	LocTriggerData.type = T_TriggerData;	LocTriggerData.tg_event =		event->ate_event & (TRIGGER_EVENT_OPMASK | TRIGGER_EVENT_ROW);	LocTriggerData.tg_relation = rel;	switch (event->ate_event & TRIGGER_EVENT_OPMASK)	{		case TRIGGER_EVENT_INSERT:			LocTriggerData.tg_trigtuple = &newtuple;			LocTriggerData.tg_newtuple = NULL;			LocTriggerData.tg_trigtuplebuf = newbuffer;			LocTriggerData.tg_newtuplebuf = InvalidBuffer;			break;		case TRIGGER_EVENT_UPDATE:			LocTriggerData.tg_trigtuple = &oldtuple;			LocTriggerData.tg_newtuple = &newtuple;			LocTriggerData.tg_trigtuplebuf = oldbuffer;			LocTriggerData.tg_newtuplebuf = newbuffer;			break;		case TRIGGER_EVENT_DELETE:			LocTriggerData.tg_trigtuple = &oldtuple;			LocTriggerData.tg_newtuple = NULL;			LocTriggerData.tg_trigtuplebuf = oldbuffer;			LocTriggerData.tg_newtuplebuf = InvalidBuffer;			break;	}	MemoryContextReset(per_tuple_context);	/*	 * Call the trigger and throw away any possibly returned updated tuple.	 * (Don't let ExecCallTriggerFunc measure EXPLAIN time.)	 */	rettuple = ExecCallTriggerFunc(&LocTriggerData,								   tgindx,								   finfo,								   NULL,								   per_tuple_context);	if (rettuple != NULL && rettuple != &oldtuple && rettuple != &newtuple)		heap_freetuple(rettuple);	/*	 * Release buffers	 */	if (oldbuffer != InvalidBuffer)		ReleaseBuffer(oldbuffer);	if (newbuffer != InvalidBuffer)		ReleaseBuffer(newbuffer);	/*	 * If doing EXPLAIN ANALYZE, stop charging time to this trigger, and count	 * one "tuple returned" (really the number of firings).	 */	if (instr)		InstrStopNode(instr + tgindx, true);}/* * afterTriggerMarkEvents() * *	Scan the given event list for not yet invoked events.  Mark the ones *	that can be invoked now with the current firing ID. * *	If move_list isn't NULL, events that are not to be invoked now are *	removed from the given list and appended to move_list. * *	When immediate_only is TRUE, do not invoke currently-deferred triggers. *	(This will be FALSE only at main transaction exit.) * *	Returns TRUE if any invokable events were found. */static boolafterTriggerMarkEvents(AfterTriggerEventList *events,					   AfterTriggerEventList *move_list,					   bool immediate_only){	bool		found = false;	AfterTriggerEvent event,				prev_event;	prev_event = NULL;	event = events->head;	while (event != NULL)	{		bool		defer_it = false;		AfterTriggerEvent next_event;		if (!(event->ate_event &			  (AFTER_TRIGGER_DONE | AFTER_TRIGGER_IN_PROGRESS)))		{			/*			 * This trigger hasn't been called or scheduled yet. Check if we			 * should call it now.			 */			if (immediate_only &&				afterTriggerCheckState(event->ate_tgoid, event->ate_event))			{				defer_it = true;			}			else			{				/*				 * Mark it as to be fired in this firing cycle.				 */				event->ate_firing_id = afterTriggers->firing_counter;				event->ate_event |= AFTER_TRIGGER_IN_PROGRESS;				found = true;			}		}		/*		 * If it's deferred, move it to move_list, if requested.		 */		next_event = event->ate_next;		if (defer_it && move_list != NULL)		{			/* Delink it from input list */			if (prev_event)				prev_event->ate_next = next_event;			else				events->head = next_event;			/* and add it to move_list */			event->ate_next = NULL;			if (move_list->tail == NULL)			{				/* first list entry */				move_list->head = event;				move_list->tail = event;			}			else			{				move_list->tail->ate_next = event;				move_list->tail = event;			}		}		else		{			/* Keep it in input list */			prev_event = event;		}		event = next_event;	}	/* Update list tail pointer in case we moved tail event */	events->tail = prev_event;	return found;}/* ---------- * afterTriggerInvokeEvents() * *	Scan the given event list for events that are marked as to be fired *	in the current firing cycle, and fire them. * *	If estate isn't NULL, then we expect that all the firable events are *	for triggers of the relations included in the estate's result relation *	array.	This allows us to re-use the estate's open relations and *	trigger cache info.  When estate is NULL, we have to find the relations *	the hard way. * *	When delete_ok is TRUE, it's okay to delete fully-processed events. *	The events list pointers are updated. * ---------- */static voidafterTriggerInvokeEvents(AfterTriggerEventList *events,						 CommandId firing_id,						 EState *estate,						 bool delete_ok){	AfterTriggerEvent event,				prev_event;	MemoryContext per_tuple_context;	Relation	rel = NULL;	TriggerDesc *trigdesc = NULL;	FmgrInfo   *finfo = NULL;	Instrumentation *instr = NULL;	/* Make a per-tuple memory context for trigger function calls */	per_tuple_context =		AllocSetContextCreate(CurrentMemoryContext,							  "AfterTriggerTupleContext",							  ALLOCSET_DEFAULT_MINSIZE,							  ALLOCSET_DEFAULT_INITSIZE,							  ALLOCSET_DEFAULT_MAXSIZE);	prev_event = NULL;	event = events->head;	while (event != NULL)	{		AfterTriggerEvent next_event;		/*		 * Is it one for me to fire?		 */		if ((event->ate_event & AFTER_TRIGGER_IN_PROGRESS) &&			event->ate_firing_id == firing_id)		{			/*			 * 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->ate_relid)			{				if (estate)				{					/* Find target relation among estate's result rels */					ResultRelInfo *rInfo;					int			nr;					rInfo = estate->es_result_relations;					nr = estate->es_num_result_relations;					while (nr > 0)					{						if (rInfo->ri_RelationDesc->rd_id == event->ate_relid)							break;						rInfo++;						nr--;					}					if (nr <= 0)	/* should not happen */						elog(ERROR, "could not find relation %u among query result relations",							 event->ate_relid);					rel = rInfo->ri_RelationDesc;					trigdesc = rInfo->ri_TrigDesc;					finfo = rInfo->ri_TrigFunctions;					instr = rInfo->ri_TrigInstrument;				}				else				{					/* Hard way: we manage the resources for ourselves */					if (rel)						heap_close(rel, NoLock);					if (trigdesc)						FreeTriggerDesc(trigdesc);					if (finfo)						pfree(finfo);					Assert(instr == NULL);		/* never used in this case */					/*					 * We assume that an appropriate lock is still held by the					 * executor, so grab no new lock here.					 */					rel = heap_open(event->ate_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->ate_relid);					/*	

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -