37.3. 用 C 编写触发器函数 #
本节说明触发器函数接口的底层细节。这些信息仅在用 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;
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而触发,那么如果你不想用另一行替换该行(在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 空值,也就是说不要把
isNull 设为真)。如果你不想修改正在处理的行,就要小心地根据情况返回适当的 tg_trigtuple 或
tg_newtuple。