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