第 39 章 事件触发器
为了补充第 38 章中讨论的触发器机制,PostgreSQL 还提供了事件触发器。与仅附着于单个表并且只捕获 DML 事件的常规触发器不同,事件触发器在某个特定数据库中是全局的,并且能够捕获 DDL 事件。
与常规触发器类似,事件触发器可以用任何支持事件触发器的过程语言编写,也可以用 C 编写,但不能用纯 SQL 编写。
39.1. 事件触发器行为概述 #
只要与事件触发器关联的事件在其定义所在数据库中发生,事件触发器就会被触发。目前仅支持以下事件:ddl_command_start、ddl_command_end、table_rewrite
和 sql_drop。未来版本可能会增加对更多事件的支持。
ddl_command_start 事件发生在
CREATE、ALTER、DROP、SECURITY LABEL、COMMENT、GRANT 或 REVOKE
命令即将执行之前。在事件触发器触发之前,不会检查受影响对象究竟存在还是不存在。但有一个例外:对于以共享对象 — 数据库、角色和表空间 —
为目标的 DDL 命令或者针对事件触发器本身的命令,不会发生该事件。事件触发器机制不支持这些对象类型。ddl_command_start 也会在 SELECT INTO
命令即将执行之前发生,因为它等价于 CREATE TABLE AS。
ddl_command_end 事件发生在上述同一组命令执行之后。要获取这些 DDL 操作的更多细节,可在
ddl_command_end 事件触发器代码中使用集合返回函数
pg_event_trigger_ddl_commands()(见第 9.28 节)。注意,触发器是在这些动作已发生之后(但在事务提交之前)触发的,因此读取系统目录时,看到的已是变更后的状态。
对于任何删除数据库对象的操作,sql_drop 事件都发生在
ddl_command_end 事件触发器之前。要列出已删除的对象,可在
sql_drop 事件触发器代码中使用集合返回函数 pg_event_trigger_dropped_objects()(见第 9.28 节)。注意,触发器是在这些对象已经从系统目录中删除之后执行的,因此已无法再查找它们。
table_rewrite 事件发生在表即将因
ALTER TABLE 和 ALTER TYPE 命令中的某些操作而被重写之前。虽然还有其他控制语句也可以重写表,例如
CLUSTER 和 VACUUM,但它们不会触发
table_rewrite 事件。
事件触发器(与其他函数一样)不能在已中止的事务中执行。因此,如果 DDL 命令因错误而失败,任何关联的 ddl_command_end 触发器都不会执行。反之,如果 ddl_command_start 触发器因错误而失败,后续事件触发器都不会触发,也不会尝试执行该命令本身。类似地,如果
ddl_command_end 触发器因错误而失败,DDL 语句的效果将被回滚,就像包含该语句的事务在任何其他情况下中止时那样。
有关事件触发器机制所支持命令的完整列表,参见第 39.2 节。
事件触发器通过命令 CREATE EVENT TRIGGER 创建。为了创建事件触发器,必须先创建一个返回类型为
event_trigger 的特殊函数。该函数不需要(也不能)返回值;这个返回类型仅用于表明该函数要作为事件触发器被调用。
如果为某个特定事件定义了多个事件触发器,它们将按触发器名称的字母顺序触发。
触发器定义也可以指定一个 WHEN 条件,这样,例如,ddl_command_start 触发器就可以只针对用户希望拦截的特定命令触发。这类触发器的一个常见用途是限制用户可执行的 DDL 操作范围。