本章描述触发器函数的低层细节。只有当你用 C 书写触发器函数的时候才需要这些信息。如果你用某种高级语言写触发器,那么系统就会为你处理这些细节。在大多数情况下,你在书写自己的 C 触发器之前应该考虑使用过程语言。每种过程语言的文档里面都有关于如何用该语言书写触发器的解释。
触发器函数必须使用"版本-1"的函数管理器接口。
当一个函数被触发器管理器调用时,它不会收到任何普通参数,而是收到一个指向 TriggerData 结构的"环境"指针。C 函数可以通过执行实际上被扩展为
((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
的宏
CALLED_AS_TRIGGER(fcinfo)
来判断自己是否从触发器管理器中调用的。如果此宏返回真(TRUE),则可以安全地把 fcinfo->context 转换成 TriggerData * 类型然后使用这个指向 TriggerData 的结构。函数本身绝不能更改 TriggerData 结构或者它指向的任何数据。
struct TriggerData 是在 commands/trigger.h 里面定义的:
typedef struct TriggerData { NodeTag type; TriggerEvent tg_event; Relation tg_relation; HeapTuple tg_trigtuple; HeapTuple tg_newtuple; Trigger *tg_trigger; Buffer tg_trigtuplebuf; Buffer tg_newtuplebuf; } TriggerData;
这些成员的定义如下:
总是 T_TriggerData
描述调用函数的事件。你可以用下面的宏检查 tg_event :
如果触发器是在操作前触发,返回真。
如果触发器是在操作后触发,返回真。
如果触发器是行级别事件触发,返回真。
如果触发器是语句级别事件触发,返回真。
如果触发器是由 INSERT 触发,返回真。
如果触发器是由 UPDATE 触发,返回真。
如果触发器是由 DELETE 触发,返回真。
是一个指向描述被触发的关系的结构的指针。请参考 utils/rel.h 获取关于此结构的详细信息。最让人感兴趣的事情是 tg_relation->rd_att(关系行的描述) 和 tg_relation->rd_rel->relname(关系名。这个变量的类型不是 char* 而是 NameData 。如果你需要一份名字的拷贝,用 SPI_getrelname(tg_relation) 获取 char* )。
是一个指向触发触发器的行的指针。这是一个正在被插入(INSERT)、删除(DELETE)、或更新(UPDATE)的行。如果是 INSERT 或 DELETE ,如果你不想用另一条行覆盖此行(INSERT)或忽略操作(INSERT),那么这就是你将返回给执行者的东西。
如果是 UPDATE ,这是一个指向新版本的行的指针,如果是 INSERT 或 DELETE ,则是 NULL 。如果事件是 UPDATE 并且你不想用另一条行替换这条行或忽略操作的话,这就是你将返回给执行者的东西。
是一个指向结构 Trigger 的指针,该结构在 utils/rel.h 里定义:
typedef struct Trigger { Oid tgoid; char *tgname; Oid tgfoid; int16 tgtype; bool tgenabled; bool tgisconstraint; Oid tgconstrrelid; bool tgdeferrable; bool tginitdeferred; int16 tgnargs; int16 tgnattr; int16 *tgattr; char **tgargs; } Trigger;
tgname 是触发器的名称,tgnargs 是在 tgargs 里参数的数量,tgargs 是一个指针数组,数组里每个指针指向在 CREATE TRIGGER 语句里声明的参数。其它成员只在内部使用。
如果没有这样的元组或者没有存储在磁盘缓冲区里,则是包含 tg_trigtuple 或者 InvalidBuffer 的缓冲区。
如果没有这样的元组或者它并未存储在磁盘缓冲区里,那么就是包含 tg_newtuple 或者 InvalidBuffer 的缓冲区。
一个触发器函数必须返回一个 HeapTuple 指针或者一个 NULL 指针(不是 SQL 的 NULL 值,也就是说不要设置 isNull 为真)。请注意如果你不想修改正在被操作的行,那么要根据情况返回 tg_trigtuple 或 tg_newtuple 。