39.3. 以 C 语言编写触发器函数#
本节介绍触发器函数界面的底层详细信息。仅在以 C 语言编写触发器函数时才需要这些信息。如果您使用的是高级语言,则这些详细信息将为您处理。在大多数情况下,您应该考虑在以 C 语言编写触发器之前使用过程语言。每种过程语言的文档都说明了如何用该语言编写触发器。
触发器函数必须使用“版本 1”函数管理器界面。
当触发器管理器调用函数时,它不会传递任何普通参数,但会传递一个“上下文”指针,指向TriggerData
结构。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;
TupleTableSlot *tg_trigslot;
TupleTableSlot *tg_newslot;
Tuplestorestate *tg_oldtable;
Tuplestorestate *tg_newtable;
const Bitmapset *tg_updatedcols;
} TriggerData;
其中成员定义如下
type
始终为
T_TriggerData
。tg_event
描述函数被调用的事件。您可以使用以下宏来检查
tg_event
TRIGGER_FIRED_BEFORE(tg_event)
如果触发器在操作之前触发,则返回 true。
TRIGGER_FIRED_AFTER(tg_event)
如果触发器在操作之后触发,则返回 true。
TRIGGER_FIRED_INSTEAD(tg_event)
如果触发器代替操作触发,则返回 true。
TRIGGER_FIRED_FOR_ROW(tg_event)
如果触发器针对行级事件触发,则返回 true。
TRIGGER_FIRED_FOR_STATEMENT(tg_event)
如果触发器针对语句级事件触发,则返回 true。
TRIGGER_FIRED_BY_INSERT(tg_event)
如果触发器由
INSERT
命令触发,则返回 true。TRIGGER_FIRED_BY_UPDATE(tg_event)
如果触发器是由
UPDATE
命令触发的,则返回 true。TRIGGER_FIRED_BY_DELETE(tg_event)
如果触发器是由
DELETE
命令触发的,则返回 true。TRIGGER_FIRED_BY_TRUNCATE(tg_event)
如果触发器是由
TRUNCATE
命令触发的,则返回 true。
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; bool tgisclone; Oid tgconstrrelid; Oid tgconstrindid; Oid tgconstraint; bool tgdeferrable; bool tginitdeferred; int16 tgnargs; int16 tgnattr; int16 *tgattr; char **tgargs; char *tgqual; char *tgoldtable; char *tgnewtable; } Trigger;
其中
tgname
是触发器的名称,tgnargs
是tgargs
中的参数数量,tgargs
是CREATE TRIGGER
语句中指定的参数的指针数组。其他成员仅供内部使用。tg_trigslot
包含
tg_trigtuple
的槽,如果没有此类元组,则包含NULL
指针。tg_newslot
包含
tg_newtuple
的槽,如果没有此类元组,则为NULL
指针。tg_oldtable
指向类型为
Tuplestorestate
的结构的指针,其中包含tg_relation
指定的格式中的零行或多行,如果没有OLD TABLE
转换关系,则为NULL
指针。tg_newtable
指向类型为
Tuplestorestate
的结构的指针,其中包含tg_relation
指定的格式中的零行或多行,如果没有NEW TABLE
转换关系,则为NULL
指针。tg_updatedcols
对于
UPDATE
触发器,位图集指示由触发命令更新的列。通用触发器函数可以使用此函数通过不必处理未更改的列来优化操作。例如,若要确定具有属性编号
attnum
(基于 1)的列是否为此位图集的成员,请调用bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, trigdata->tg_updatedcols))
。对于
UPDATE
触发器之外的触发器,此值将为NULL
。
若要允许通过 SPI 发出的查询引用转换表,请参阅SPI_register_trigger_data。
触发器函数必须返回HeapTuple
指针或NULL
指针(不是SQL null 值,即,不要将*isNull
*设置为 true)。如果您不想修改正在操作的行,请务必根据需要返回tg_trigtuple
或tg_newtuple
。