35.3. 用 C 编写触发器函数 #
本节描述触发器函数接口的底层细节。只有用 C 编写触发器函数时才需要这些信息。如果你使用的是更高层次的语言,那么这些细节会替你处理。每种过程语言的文档说明了如何用该语言编写触发器。
触发器函数必须使用“版本 1”函数管理器接口。
当函数被触发器管理器调用时,它不会被传递任何普通参数,而是被传递一个指向 TriggerData 结构体的“上下文”指针。C 函数可以通过执行宏
CALLED_AS_TRIGGER(fcinfo)
来检查自己是否是被触发器管理器调用的,该宏展开为
((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))
如果它返回真,那么就可以安全地把 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;
} TriggerData;其成员的含义如下:
type总是
T_TriggerData。tg_event描述引发该函数调用的事件。你可以使用下列宏来检查
tg_event:TRIGGER_FIRED_BEFORE(tg_event)如果触发器在操作之前触发则返回真。
TRIGGER_FIRED_AFTER(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命令引发则返回真。
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触发的,那么当你不想(在INSERT的情形下)用另一个行替换该行或跳过该操作时,就应当从函数返回它。tg_newtuple一个指针,指向该行的新版本(如果触发器是针对
UPDATE触发的);如果是针对INSERT或DELETE,则它为NULL。当事件是一次UPDATE而你不想用另一个行替换该行或跳过该操作时,就必须从函数返回它。tg_trigger一个指针,指向
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 tgattr[FUNC_MAX_ARGS]; char **tgargs; } Trigger;其中
tgname是触发器的名称,tgnargs是tgargs中参数的数量,tgargs是一个指针数组,指向CREATE TRIGGER语句中指定的参数。其他成员仅供内部使用。
触发器函数必须返回 NULL 或者一个
HeapTuple 指针。如果你不想修改正在被操作的行,请注意根据情况返回 tg_trigtuple 或
tg_newtuple。