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

📄 trigger.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 5 页
字号:
/*------------------------------------------------------------------------- * * trigger.c *	  PostgreSQL TRIGGERs support code. * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.195.2.2 2006/01/12 21:49:06 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/genam.h"#include "access/heapam.h"#include "access/xact.h"#include "catalog/catalog.h"#include "catalog/dependency.h"#include "catalog/indexing.h"#include "catalog/namespace.h"#include "catalog/pg_language.h"#include "catalog/pg_proc.h"#include "catalog/pg_trigger.h"#include "catalog/pg_type.h"#include "commands/defrem.h"#include "commands/trigger.h"#include "executor/executor.h"#include "executor/instrument.h"#include "miscadmin.h"#include "nodes/makefuncs.h"#include "parser/parse_func.h"#include "utils/acl.h"#include "utils/builtins.h"#include "utils/fmgroids.h"#include "utils/inval.h"#include "utils/lsyscache.h"#include "utils/memutils.h"#include "utils/syscache.h"static void InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx);static HeapTuple GetTupleForTrigger(EState *estate,				   ResultRelInfo *relinfo,				   ItemPointer tid,				   CommandId cid,				   TupleTableSlot **newSlot);static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,					int tgindx,					FmgrInfo *finfo,					Instrumentation *instr,					MemoryContext per_tuple_context);static void AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event,					  bool row_trigger, HeapTuple oldtup, HeapTuple newtup);/* * Create a trigger.  Returns the OID of the created trigger. * * forConstraint, if true, says that this trigger is being created to * implement a constraint.	The caller will then be expected to make * a pg_depend entry linking the trigger to that constraint (and thereby * to the owning relation(s)). */OidCreateTrigger(CreateTrigStmt *stmt, bool forConstraint){	int16		tgtype;	int2vector *tgattr;	Datum		values[Natts_pg_trigger];	char		nulls[Natts_pg_trigger];	Relation	rel;	AclResult	aclresult;	Relation	tgrel;	SysScanDesc tgscan;	ScanKeyData key;	Relation	pgrel;	HeapTuple	tuple;	Oid			fargtypes[1];	/* dummy */	Oid			funcoid;	Oid			funcrettype;	Oid			trigoid;	int			found = 0;	int			i;	char		constrtrigname[NAMEDATALEN];	char	   *trigname;	char	   *constrname;	Oid			constrrelid = InvalidOid;	ObjectAddress myself,				referenced;	rel = heap_openrv(stmt->relation, AccessExclusiveLock);	if (stmt->constrrel != NULL)		constrrelid = RangeVarGetRelid(stmt->constrrel, false);	else if (stmt->isconstraint)	{		/*		 * If this trigger is a constraint (and a foreign key one) then we		 * really need a constrrelid.  Since we don't have one, we'll try to		 * generate one from the argument information.		 *		 * This is really just a workaround for a long-ago pg_dump bug that		 * omitted the FROM clause in dumped CREATE CONSTRAINT TRIGGER		 * commands.  We don't want to bomb out completely here if we can't		 * determine the correct relation, because that would prevent loading		 * the dump file.  Instead, NOTICE here and ERROR in the trigger.		 */		bool		needconstrrelid = false;		void	   *elem = NULL;		if (strncmp(strVal(lfirst(list_tail((stmt->funcname)))), "RI_FKey_check_", 14) == 0)		{			/* A trigger on FK table. */			needconstrrelid = true;			if (list_length(stmt->args) > RI_PK_RELNAME_ARGNO)				elem = list_nth(stmt->args, RI_PK_RELNAME_ARGNO);		}		else if (strncmp(strVal(lfirst(list_tail((stmt->funcname)))), "RI_FKey_", 8) == 0)		{			/* A trigger on PK table. */			needconstrrelid = true;			if (list_length(stmt->args) > RI_FK_RELNAME_ARGNO)				elem = list_nth(stmt->args, RI_FK_RELNAME_ARGNO);		}		if (elem != NULL)		{			RangeVar   *rel = makeRangeVar(NULL, strVal(elem));			constrrelid = RangeVarGetRelid(rel, true);		}		if (needconstrrelid && constrrelid == InvalidOid)			ereport(NOTICE,					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),					 errmsg("could not determine referenced table for constraint \"%s\"",							stmt->trigname)));	}	if (rel->rd_rel->relkind != RELKIND_RELATION)		ereport(ERROR,				(errcode(ERRCODE_WRONG_OBJECT_TYPE),				 errmsg("\"%s\" is not a table",						RelationGetRelationName(rel))));	if (!allowSystemTableMods && IsSystemRelation(rel))		ereport(ERROR,				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),				 errmsg("permission denied: \"%s\" is a system catalog",						RelationGetRelationName(rel))));	/* permission checks */	if (stmt->isconstraint)	{		/* foreign key constraint trigger */		aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),									  ACL_REFERENCES);		if (aclresult != ACLCHECK_OK)			aclcheck_error(aclresult, ACL_KIND_CLASS,						   RelationGetRelationName(rel));		if (constrrelid != InvalidOid)		{			aclresult = pg_class_aclcheck(constrrelid, GetUserId(),										  ACL_REFERENCES);			if (aclresult != ACLCHECK_OK)				aclcheck_error(aclresult, ACL_KIND_CLASS,							   get_rel_name(constrrelid));		}	}	else	{		/* real trigger */		aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),									  ACL_TRIGGER);		if (aclresult != ACLCHECK_OK)			aclcheck_error(aclresult, ACL_KIND_CLASS,						   RelationGetRelationName(rel));	}	/*	 * Generate the trigger's OID now, so that we can use it in the name if	 * needed.	 */	tgrel = heap_open(TriggerRelationId, RowExclusiveLock);	trigoid = GetNewOid(tgrel);	/*	 * If trigger is an RI constraint, use specified trigger name as	 * constraint name and build a unique trigger name instead. This is mainly	 * for backwards compatibility with CREATE CONSTRAINT TRIGGER commands.	 */	if (stmt->isconstraint)	{		snprintf(constrtrigname, sizeof(constrtrigname),				 "RI_ConstraintTrigger_%u", trigoid);		trigname = constrtrigname;		constrname = stmt->trigname;	}	else	{		trigname = stmt->trigname;		constrname = "";	}	TRIGGER_CLEAR_TYPE(tgtype);	if (stmt->before)		TRIGGER_SETT_BEFORE(tgtype);	if (stmt->row)		TRIGGER_SETT_ROW(tgtype);	for (i = 0; stmt->actions[i]; i++)	{		switch (stmt->actions[i])		{			case 'i':				if (TRIGGER_FOR_INSERT(tgtype))					ereport(ERROR,							(errcode(ERRCODE_SYNTAX_ERROR),							 errmsg("multiple INSERT events specified")));				TRIGGER_SETT_INSERT(tgtype);				break;			case 'd':				if (TRIGGER_FOR_DELETE(tgtype))					ereport(ERROR,							(errcode(ERRCODE_SYNTAX_ERROR),							 errmsg("multiple DELETE events specified")));				TRIGGER_SETT_DELETE(tgtype);				break;			case 'u':				if (TRIGGER_FOR_UPDATE(tgtype))					ereport(ERROR,							(errcode(ERRCODE_SYNTAX_ERROR),							 errmsg("multiple UPDATE events specified")));				TRIGGER_SETT_UPDATE(tgtype);				break;			default:				elog(ERROR, "unrecognized trigger event: %d",					 (int) stmt->actions[i]);				break;		}	}	/*	 * Scan pg_trigger for existing triggers on relation.  We do this mainly	 * because we must count them; a secondary benefit is to give a nice error	 * message if there's already a trigger of the same name. (The unique	 * index on tgrelid/tgname would complain anyway.)	 *	 * NOTE that this is cool only because we have AccessExclusiveLock on the	 * relation, so the trigger set won't be changing underneath us.	 */	ScanKeyInit(&key,				Anum_pg_trigger_tgrelid,				BTEqualStrategyNumber, F_OIDEQ,				ObjectIdGetDatum(RelationGetRelid(rel)));	tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,								SnapshotNow, 1, &key);	while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))	{		Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);		if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)			ereport(ERROR,					(errcode(ERRCODE_DUPLICATE_OBJECT),				  errmsg("trigger \"%s\" for relation \"%s\" already exists",						 trigname, stmt->relation->relname)));		found++;	}	systable_endscan(tgscan);	/*	 * Find and validate the trigger function.	 */	funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false);	funcrettype = get_func_rettype(funcoid);	if (funcrettype != TRIGGEROID)	{		/*		 * We allow OPAQUE just so we can load old dump files.	When we see a		 * trigger function declared OPAQUE, change it to TRIGGER.		 */		if (funcrettype == OPAQUEOID)		{			ereport(WARNING,					(errmsg("changing return type of function %s from \"opaque\" to \"trigger\"",							NameListToString(stmt->funcname))));			SetFunctionReturnType(funcoid, TRIGGEROID);		}		else			ereport(ERROR,					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),					 errmsg("function %s must return type \"trigger\"",							NameListToString(stmt->funcname))));	}	/*	 * Build the new pg_trigger tuple.	 */	MemSet(nulls, ' ', Natts_pg_trigger * sizeof(char));	values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));	values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein,												  CStringGetDatum(trigname));	values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);	values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);	values[Anum_pg_trigger_tgenabled - 1] = BoolGetDatum(true);	values[Anum_pg_trigger_tgisconstraint - 1] = BoolGetDatum(stmt->isconstraint);	values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein,												CStringGetDatum(constrname));	values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);	values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable);	values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred);	if (stmt->args)	{		ListCell   *le;		char	   *args;		int16		nargs = list_length(stmt->args);		int			len = 0;		foreach(le, stmt->args)		{			char	   *ar = strVal(lfirst(le));			len += strlen(ar) + 4;			for (; *ar; ar++)			{				if (*ar == '\\')					len++;			}		}		args = (char *) palloc(len + 1);		args[0] = '\0';		foreach(le, stmt->args)		{			char	   *s = strVal(lfirst(le));			char	   *d = args + strlen(args);			while (*s)			{				if (*s == '\\')					*d++ = '\\';				*d++ = *s++;			}			strcpy(d, "\\000");		}		values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs);		values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,													  CStringGetDatum(args));	}	else	{		values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0);		values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain,														CStringGetDatum(""));	}	/* tgattr is currently always a zero-length array */	tgattr = buildint2vector(NULL, 0);	values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr);	tuple = heap_formtuple(tgrel->rd_att, values, nulls);	/* force tuple to have the desired OID */	HeapTupleSetOid(tuple, trigoid);	/*	 * Insert tuple into pg_trigger.	 */	simple_heap_insert(tgrel, tuple);	CatalogUpdateIndexes(tgrel, tuple);	myself.classId = TriggerRelationId;	myself.objectId = trigoid;	myself.objectSubId = 0;	heap_freetuple(tuple);	heap_close(tgrel, RowExclusiveLock);	pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1]));	pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1]));	/*	 * Update relation's pg_class entry.  Crucial side-effect: other backends	 * (and this one too!) are sent SI message to make them rebuild relcache	 * entries.	 */	pgrel = heap_open(RelationRelationId, RowExclusiveLock);	tuple = SearchSysCacheCopy(RELOID,							   ObjectIdGetDatum(RelationGetRelid(rel)),							   0, 0, 0);	if (!HeapTupleIsValid(tuple))		elog(ERROR, "cache lookup failed for relation %u",			 RelationGetRelid(rel));	((Form_pg_class) GETSTRUCT(tuple))->reltriggers = found + 1;	simple_heap_update(pgrel, &tuple->t_self, tuple);	CatalogUpdateIndexes(pgrel, tuple);	heap_freetuple(tuple);	heap_close(pgrel, RowExclusiveLock);	/*	 * We used to try to update the rel's relcache entry here, but that's	 * fairly pointless since it will happen as a byproduct of the upcoming	 * CommandCounterIncrement...	 */	/*	 * Record dependencies for trigger.  Always place a normal dependency on	 * the function.  If we are doing this in response to an explicit CREATE	 * TRIGGER command, also make trigger be auto-dropped if its relation is	 * dropped or if the FK relation is dropped.  (Auto drop is compatible	 * with our pre-7.3 behavior.)	If the trigger is being made for a	 * constraint, we can skip the relation links; the dependency on the	 * constraint will indirectly depend on the relations.	 */	referenced.classId = ProcedureRelationId;	referenced.objectId = funcoid;	referenced.objectSubId = 0;	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);	if (!forConstraint)	{		referenced.classId = RelationRelationId;		referenced.objectId = RelationGetRelid(rel);		referenced.objectSubId = 0;		recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);		if (constrrelid != InvalidOid)		{			referenced.classId = RelationRelationId;			referenced.objectId = constrrelid;			referenced.objectSubId = 0;			recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);		}	}	/* Keep lock on target rel until end of xact */	heap_close(rel, NoLock);	return trigoid;}/* * DropTrigger - drop an individual trigger by name */voidDropTrigger(Oid relid, const char *trigname, DropBehavior behavior){	Relation	tgrel;	ScanKeyData skey[2];	SysScanDesc tgscan;	HeapTuple	tup;	ObjectAddress object;	/*	 * Find the trigger, verify permissions, set up object address	 */	tgrel = heap_open(TriggerRelationId, AccessShareLock);	ScanKeyInit(&skey[0],				Anum_pg_trigger_tgrelid,				BTEqualStrategyNumber, F_OIDEQ,				ObjectIdGetDatum(relid));	ScanKeyInit(&skey[1],				Anum_pg_trigger_tgname,				BTEqualStrategyNumber, F_NAMEEQ,

⌨️ 快捷键说明

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