💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 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`。