本节介绍触发器函数的接口的底层细节。这些信息仅在用 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-based)的列是否是此位图集中的成员,请调用 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。