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

📄 trigger.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 5 页
字号:
/*------------------------------------------------------------------------- * * trigger.c *	  PostgreSQL TRIGGERs support code. * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.159 2003/10/02 06:34:03 petere Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/genam.h"#include "access/heapam.h"#include "access/xact.h"#include "catalog/catalog.h"#include "catalog/catname.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 "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/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,					FmgrInfo *finfo,					MemoryContext per_tuple_context);static void DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,				   bool row_trigger, HeapTuple oldtup, HeapTuple newtup);static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,					Relation rel, TriggerDesc *trigdesc, FmgrInfo *finfo,					   MemoryContext per_tuple_context);/* * 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;	int16		tgattr[FUNC_MAX_ARGS];	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[FUNC_MAX_ARGS];	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(llast(stmt->funcname)), "RI_FKey_check_", 14) == 0)		{			/* A trigger on FK table. */			needconstrrelid = true;			if (length(stmt->args) > RI_PK_RELNAME_ARGNO)				elem = nth(RI_PK_RELNAME_ARGNO, stmt->args);		}		else if (strncmp(strVal(llast(stmt->funcname)), "RI_FKey_", 8) == 0)		{			/* A trigger on PK table. */			needconstrrelid = true;			if (length(stmt->args) > RI_FK_RELNAME_ARGNO)				elem = nth(RI_FK_RELNAME_ARGNO, stmt->args);		}		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.	 */	trigoid = newoid();	/*	 * 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.	 */	tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);	ScanKeyEntryInitialize(&key, 0,						   Anum_pg_trigger_tgrelid,						   F_OIDEQ,						   ObjectIdGetDatum(RelationGetRelid(rel)));	tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, 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.	 */	MemSet(fargtypes, 0, FUNC_MAX_ARGS * sizeof(Oid));	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)	{		List	   *le;		char	   *args;		int16		nargs = 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(""));	}	MemSet(tgattr, 0, FUNC_MAX_ARGS * sizeof(int16));	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 = RelationGetRelid(tgrel);	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_openr(RelationRelationName, 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 = RelOid_pg_proc;	referenced.objectId = funcoid;	referenced.objectSubId = 0;	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);	if (!forConstraint)	{		referenced.classId = RelOid_pg_class;		referenced.objectId = RelationGetRelid(rel);		referenced.objectSubId = 0;		recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);		if (constrrelid != InvalidOid)		{			referenced.classId = RelOid_pg_class;			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_openr(TriggerRelationName, AccessShareLock);	ScanKeyEntryInitialize(&skey[0], 0x0,						   Anum_pg_trigger_tgrelid, F_OIDEQ,						   ObjectIdGetDatum(relid));	ScanKeyEntryInitialize(&skey[1], 0x0,						   Anum_pg_trigger_tgname, F_NAMEEQ,						   CStringGetDatum(trigname));	tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,								SnapshotNow, 2, skey);	tup = systable_getnext(tgscan);	if (!HeapTupleIsValid(tup))		ereport(ERROR,				(errcode(ERRCODE_UNDEFINED_OBJECT),			  errmsg("trigger \"%s\" for table \"%s\" does not exist",					 trigname, get_rel_name(relid))));	if (!pg_class_ownercheck(relid, GetUserId()))		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,

⌨️ 快捷键说明

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