第 20 章 触发器
PostgreSQL 有多种服务器端函数接口。服务器端函数可以用 SQL、PLPGSQL、TCL 或 C 编写。除 SQL 外,触发器函数可以用这些语言中的任何一种编写。注意,当前版本不支持语句(STATEMENT)级触发器事件。目前可以把 BEFORE 或 AFTER 指定在一个元组的 INSERT、DELETE 或 UPDATE 上作为触发器事件。
20.1. 触发器创建 #
一旦触发器事件发生,触发器管理器(由执行器调用)就会建立一个 TriggerData 信息结构(下文描述),并调用触发器函数来处理该事件。
必须先定义触发器函数,然后才能创建触发器,它声明为不接受参数并返回 opaque 的函数。如果函数用 C 编写,它必须使用“版本 1”函数管理器接口。
创建触发器的语法如下:
CREATE TRIGGERtrigger[ BEFORE | AFTER ] [ INSERT | DELETE | UPDATE [ OR ... ] ] ONrelationFOR EACH [ ROW | STATEMENT ] EXECUTE PROCEDUREprocedure(args);
其中各个参数为:
-
trigger 触发器的名称在你将来需要删除该触发器时使用。它被用作
DROP TRIGGER命令的一个参数。- BEFORE
AFTER 决定函数是在事件之前还是之后被调用。
- INSERT
DELETE
UPDATE 命令的下一个元素决定哪些事件会触发该函数。可以用 OR 分隔指定多个事件。
relation关系名指出该事件作用于哪个表。
- ROW
STATEMENT FOR EACH 子句决定触发器是针对每个受影响的行触发,还是在整条语句完成之前(或之后)触发。目前只支持 ROW 的情形。
procedure过程名是要被调用的函数。
args通过 TriggerData 结构传给函数的参数。向函数传递参数的目的,是为了让需求相似的不同触发器能够调用同一个函数。
另外,
procedure也可以用于触发不同的关系(这类函数被称为通用触发器函数)。作为同时使用上述两个特性的例子,可以有一个通用函数,它接受两个列名作为参数,把当前用户写入其中一个列,把当前时间戳写入另一个列。这样就可以在 INSERT 事件上编写触发器,例如自动跟踪某个事务表中记录的创建。如果用在 UPDATE 事件上,它还可以作为一个“最近更新”函数使用。
触发器函数向调用它的执行器返回 HeapTuple。对于在 INSERT、DELETE 或 UPDATE 操作之后触发的触发器,该返回值会被忽略,但它允许 BEFORE 触发器:
返回 NULL,以跳过对当前元组的操作(这样该元组就不会被插入/更新/删除)。
返回一个指向另一个元组的指针(仅 INSERT 和 UPDATE),该元组将被插入(如果是 UPDATE,则作为被更新元组的新版本),以取代原来的元组。
注意,CREATE TRIGGER 处理器不执行任何初始化。这一点将来会改变。另外,如果为同一关系上的同一事件定义了多个触发器,触发器的触发顺序是不可预测的。这一点将来也可能会改变。
如果触发器函数执行 SQL 查询(使用 SPI),那么这些查询可能会再次触发触发器。这就是所谓的级联触发器。对级联层数没有明确的限制。
如果一个触发器由 INSERT 触发,而又向同一个关系插入了一个新元组,那么该触发器会再次被触发。目前,对于这类情形没有提供任何同步(等)机制,但这一点将来可能改变。目前,回归测试中的函数 funny_dup17() 使用了一些技巧来停止对自身的递归(级联)……