36.3. 用 C 写触发器

本章描述触发器函数的低层细节。只有当你用 C 书写触发器函数的时候才需要这些信息。 如果你用某种高级语言写触发器,那么系统就会为你处理这些细节。 在大多数情况下,你在书写自己的 C 触发器之前应该考虑使用过程语言。 每种过程语言的文档里面都有关于如何用该语言书写触发器的解释。

触发器函数必须使用"version 1"的函数管理器接口

当一个函数被触发器管理器调用时,它不会收到任何普通参数,而是收到一个 指向TriggerData结构的"context"指针。C 函数可以通过执行下面的宏来检查 它们是否是从触发器管理器调用的,或者不是:

CALLED_AS_TRIGGER(fcinfo)

which expands to:

((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;
    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_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

是一个指向被触发触发器的行的指针。这是一个正在被插入INSERTDELETE的行。 如果是INSERTDELETE触发触发器,并且如果你不想用另一条行覆盖 此行(INSERT)或忽略操作(INSERT),那么这就是你将返回给执行者的东西

tg_newtuple

如果是UPDATE触发的触发器,则是一个指向新版本的行的指针, 如果是INSERTDELETE, 则是NULL。如果事件是UPDATE 并且你不想用另一条行替换这条行或忽略操作的话,这就是你将返回给执行者的东西。

tg_trigger

是一个指向结构Trigger的指针,该结构在utils/rel.h里定义:

typedef struct Trigger
{
    Oid         tgoid;
    char       *tgname;
    Oid         tgfoid;
    int16       tgtype;
    bool        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 的 NULL 值, 也就是说不要设置 isNull为真)。请注意如果你不想修改正在被操作的行, 那么要根据情况返回tg_trigtupletg_newtuple