Postgresql 中文操作指南

39.3. Writing Trigger Functions in C #

本节描述了触发器功能接口的底层详细信息。仅在使用 C 编写触发器功能时需要该信息。如果您使用的是高级语言,则无需处理这些详细信息。在大多数情况下,您应该考虑在 C 中编写触发器之前使用过程语言。每个过程语言的文档都说明了如何用该语言编写触发器。

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

当触发器管理器调用函数时,不会传递任何常规参数,但会传递一个“上下文”指针,指向 TriggerData 结构。C 函数可以通过执行宏来检查是否从触发器管理器调用了它们:

CALLED_AS_TRIGGER(fcinfo)

它将扩展为:

((fcinfo)->context != NULL && IsA((fcinfo)->context, TriggerData))

如果返回 true,则可以安全地将 fcinfo→context 转换为类型 TriggerData * 并利用所指向的 TriggerData 结构。该函数必须 not 更改 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

    • Always T_TriggerData.

  • tg_event

    • 描述调用函数的事件。可以使用以下宏来检查 tg_event

  • 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 而触发)和 NULL (如果触发器因 INSERTDELETE 而触发)的指针。如果事件是 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 的参数数量,而 tgargsCREATE 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

    • 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。

要允许通过 SPI 发出的查询引用转换表,请参阅 SPI_register_trigger_data

触发器函数必须返回 HeapTuple 指针或 NULL 指针(not 为 SQL 空值,即不要设置 isNull 为 true)。如果你不想修改正在操作的行,请小心返回 tg_trigtupletg_newtuple(视情况而定)。