受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

37.3. 用 C 编写触发器函数 #

本节说明触发器函数接口的底层细节。这些信息仅在用 C 编写触发器函数时才需要。如果你使用更高层语言,这些细节会由系统代为处理。在多数情况下,在用 C 编写触发器之前,应先考虑使用过程语言。每种过程语言的文档都会说明如何用该语言编写触发器。

触发器函数必须使用 版本 1 函数管理器接口。

当函数由触发器管理器调用时,不会向它传递任何普通参数,但会传递一个指向 TriggerData 结构的 context 指针。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;
    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

指向触发该触发器的那一行。这是正在被插入、更新或删除的行。如果该触发器因 INSERTDELETE 而触发,那么如果你不想用另一行替换该行(在 INSERT 的情形下)或跳过该操作,就应从函数中返回它。对于外部表上的触发器,此处系统列的值未指定。

tg_newtuple

如果触发器因 UPDATE 而触发,则指向该行的新版本;如果因 INSERTDELETE 而触发,则为 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 是触发器名称, tgnargstgargs 中参数的数量,而 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 空值,也就是说不要把 isNull 设为真)。如果你不想修改正在处理的行,就要小心地根据情况返回适当的 tg_trigtupletg_newtuple

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。