第 35 章 触发器
本章介绍如何编写触发器函数。触发器函数可以用 C 或某些可用的过程语言编写。目前还无法用 SQL 语言编写触发器函数。
35.1. 触发器行为概述 #
触发器可以定义为在 INSERT、UPDATE 或 DELETE 操作之前或之后执行,执行方式既可以是每个被修改的行一次,也可以是每条
SQL 语句一次。一旦触发器事件发生,触发器的函数就会在恰当的时机被调用以处理该事件。
必须先定义触发器函数,之后才能创建触发器本身。触发器函数必须声明为不接受参数并返回类型 trigger 的函数。(触发器函数通过特殊传入的 TriggerData
结构体接收输入,而不是以普通函数参数的形式。)
创建出合适的触发器函数后,就可以使用CREATE TRIGGER建立触发器。同一个触发器函数可以供多个触发器使用。
触发器函数向调用它的执行器返回一个表行(即类型
HeapTuple 的值)。在操作之前触发的触发器可以作出如下选择:
它可以返回一个
NULL指针,以跳过对当前行的操作(这样该行就不会被插入/更新/删除)。仅对于
INSERT和UPDATE触发器,返回的行会成为将要插入的行,或替换正在更新的行。这使得触发器函数可以修改即将插入或更新的行。
不打算引发上述任何一种行为的 BEFORE 触发器,必须注意把传入的行原样作为结果返回(也就是说,对于 INSERT
和 UPDATE 触发器返回 NEW 行,对于
DELETE 触发器返回 OLD 行)。
对于在操作之后触发的触发器,其返回值会被忽略,因此它们可以返回 NULL。
如果为同一关系上的同一事件定义了多个触发器,它们会按触发器名称的字母顺序触发。对于 BEFORE 触发器,每个触发器返回的、可能已经被修改过的行都会成为下一个触发器的输入。如果任何
BEFORE 触发器返回一个 NULL 指针,则会放弃该操作,并且后续触发器不会再触发。
如果触发器函数执行 SQL 命令,那么这些命令可能会再次触发触发器。这就是所谓的级联触发器。对级联层数没有直接限制。级联还有可能导致同一个触发器被递归调用;例如,一个 INSERT
触发器可能执行一条向同一个表再插入一行的命令,从而导致该
INSERT 触发器再次触发。因此,在这种场景下,避免无限递归是触发器编写者自己的责任。
在定义触发器时,可以为它指定参数。在触发器定义中包含参数,是为了让需求相似的不同触发器能够调用同一个函数。举例来说,可以有一个通用触发器函数,它接受两个列名作为参数,把当前用户写入其中一个列,把当前时间戳写入另一个列。只要编写得当,这个触发器函数就应独立于它所作用的具体表。因此,同一个函数可用于任何具有适当列的表上的 INSERT 事件,例如自动跟踪某个事务表中记录的创建。如果把它定义成
UPDATE 触发器,还可以用来跟踪最近更新事件。