PostgreSQL 9.4.4 中文手册
上一页 | 上一级 | 章 36. 触发器 | 下一页

# 36.3. 用C写触发器

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

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

当一个函数被触发器管理器调用时，它不会收到任何普通参数， 而是收到一个指向`TriggerData`结构的"context"指针。 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;
        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_INSTEAD(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`

  是一个指向触发触发器的行的指针。 这是一个正在被插入(INSERT)、删除(DELETE)、或更新(UPDATE)的行。 如果是`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;
          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_trigtuple`或者`tg_newtuple`。

上一页 | 起始页 | 下一页
数据改变的可视性 | 上一级 | 一个完整的触发器例子