Redrock Postgres 文档
主页 切换暗/亮/自动模式 切换暗/亮/自动模式 切换暗/亮/自动模式 返回首页

事件触发器

Redrock Postgres 提供了事件触发器。和常规触发器(附着在一个表上并且只捕捉 DML 事件)不同,事件触发器对一个特定数据库来说是全局的,并且可以捕捉 DDL 事件。

和常规触发器相似,可以用任何包括了事件触发器支持的过程语言编写事件触发器,但是不能用纯 SQL 编写。

事件触发器行为总览

只要与一个事件触发器相关的事件在事件触发器所在的数据库中发生,该事件触发器就会被引发。当前支持的事件是ddl_command_startddl_command_endtable_rewritesql_drop。未来的发行版中可能会增加对更多事件的支持。

ddl_command_start事件就在CREATEALTERDROPSECURITY LABELCOMMENTGRANT或者REVOKE命令的执行之前发生。在事件触发器引发前不会做受影响对象是否存在的检查。不过,一个例外是,这个事件不会为目标是共享对象 — 数据库 — 的 DDL 命令发生,也不会为目标是事件触发器的 DDL 命令发生。事件触发器机制不支持这些对象类型。ddl_command_start也会在SELECT INTO命令的执行之前发生,因为这等价于CREATE TABLE AS

ddl_command_end事件就在同一组命令的执行之后发生。为了得到发生的DDL操作的更多细节,可以从ddl_command_end事件触发器代码中使用集合返回函数pg_event_trigger_ddl_commands()(见 事件触发器函数)。注意该触发器是在那些动作已经发生之后(但是在事务提交前)引发,并且此时相关的系统表数据已被更改。

sql_drop事件为任何删除数据库对象的操作在ddl_command_end事件触发器之前发生。要列出已经被删除的对象,可以从sql_drop事件触发器代码中使用集合返回函数pg_event_trigger_dropped_objects()事件触发器函数)。注意该触发器是在对象已经从系统表删除以后执行,因此不能再查看它们。

table_rewrite事件在表被命令ALTER TABLEALTER TYPE的某些动作重写之前发生。虽然其他控制语句(例如CLUSTERVACUUM)也可以用来重写表,但是它们不会触发table_rewrite事件。

不能在一个中止的事务中执行事件触发器(其他函数也一样)。因此,如果一个 DDL 命令出现错误失败,将不会执行任何相关的ddl_command_end触发器。反过来,如果一个ddl_command_start触发器出现错误失败,将不会引发进一步的事件触发器,并且不会尝试执行该命令本身。类似地,如果一个ddl_command_end触发器出现错误失败,DDL 命令的效果将被回滚,就像其他包含事务中止的情况中那样。

事件触发器触发矩阵 中有事件触发器机制所支持的完整命令列表。

事件触发器通过命令 CREATE EVENT TRIGGER 创建。为了创建一个事件触发器,你必须首先创建一个有特殊返回类型event_trigger的函数。这个函数不一定需要返回一个值,该返回类型仅仅是作为一种信号表示该函数要被作为一个事件触发器调用。

如果对于一个特定的事件定义了多于一个事件触发器,它们将按照触发器名称的字母表顺序被引发。

一个触发器定义也可以指定一个WHEN条件,这样事件触发器(例如ddl_command_start触发器)就可以只对用户希望介入的特定命令触发。这类触发器的通常用法是用于限制用户可能执行的 DDL 操作的范围。

事件触发器触发矩阵

表 1 列出了所有命令的事件触发器支持情况。

表 1. 支持事件触发器的命令标签

命令标签 ddl_command_start ddl_command_end sql_drop table_rewrite
ALTER AGGREGATE X X - -
ALTER COLLATION X X - -
ALTER CONVERSION X X - -
ALTER DOMAIN X X - -
ALTER DEFAULT PRIVILEGES X X - -
ALTER EXTENSION X X - -
ALTER FOREIGN DATA WRAPPER X X - -
ALTER FOREIGN TABLE X X X -
ALTER FUNCTION X X - -
ALTER LANGUAGE X X - -
ALTER LARGE OBJECT X X - -
ALTER MATERIALIZED VIEW X X - -
ALTER OPERATOR X X - -
ALTER OPERATOR CLASS X X - -
ALTER OPERATOR FAMILY X X - -
ALTER POLICY X X - -
ALTER PROCEDURE X X - -
ALTER PUBLICATION X X - -
ALTER ROLE X X - -
ALTER ROUTINE X X - -
ALTER SCHEMA X X - -
ALTER SEQUENCE X X - -
ALTER SERVER X X - -
ALTER STATISTICS X X - -
ALTER SUBSCRIPTION X X - -
ALTER TABLE X X X X
ALTER TABLESPACE X X - -
ALTER TEXT SEARCH CONFIGURATION X X - -
ALTER TEXT SEARCH DICTIONARY X X - -
ALTER TEXT SEARCH PARSER X X - -
ALTER TEXT SEARCH TEMPLATE X X - -
ALTER TRIGGER X X - -
ALTER TYPE X X - X
ALTER USER X X - -
ALTER USER MAPPING X X - -
ALTER VIEW X X - -
COMMENT X X - -
CREATE ACCESS METHOD X X - -
CREATE AGGREGATE X X - -
CREATE CAST X X - -
CREATE COLLATION X X - -
CREATE CONVERSION X X - -
CREATE DOMAIN X X - -
CREATE EXTENSION X X - -
CREATE FOREIGN DATA WRAPPER X X - -
CREATE FOREIGN TABLE X X - -
CREATE FUNCTION X X - -
CREATE INDEX X X - -
CREATE LANGUAGE X X - -
CREATE MATERIALIZED VIEW X X - -
CREATE OPERATOR X X - -
CREATE OPERATOR CLASS X X - -
CREATE OPERATOR FAMILY X X - -
CREATE POLICY X X - -
CREATE PROCEDURE X X - -
CREATE PUBLICATION X X - -
CREATE ROLE X X - -
CREATE RULE X X - -
CREATE SCHEMA X X - -
CREATE SEQUENCE X X - -
CREATE SERVER X X - -
CREATE STATISTICS X X - -
CREATE SUBSCRIPTION X X - -
CREATE TABLE X X - -
CREATE TABLE AS X X - -
CREATE TABLESPACE X X - -
CREATE TEXT SEARCH CONFIGURATION X X - -
CREATE TEXT SEARCH DICTIONARY X X - -
CREATE TEXT SEARCH PARSER X X - -
CREATE TEXT SEARCH TEMPLATE X X - -
CREATE TRIGGER X X - -
CREATE TYPE X X - -
CREATE USER X X - -
CREATE USER MAPPING X X - -
CREATE VIEW X X - -
DROP ACCESS METHOD X X X -
DROP AGGREGATE X X X -
DROP CAST X X X -
DROP COLLATION X X X -
DROP CONVERSION X X X -
DROP DOMAIN X X X -
DROP EXTENSION X X X -
DROP FOREIGN DATA WRAPPER X X X -
DROP FOREIGN TABLE X X X -
DROP FUNCTION X X X -
DROP INDEX X X X -
DROP LANGUAGE X X X -
DROP MATERIALIZED VIEW X X X -
DROP OPERATOR X X X -
DROP OPERATOR CLASS X X X -
DROP OPERATOR FAMILY X X X -
DROP OWNED X X X -
DROP POLICY X X X -
DROP PROCEDURE X X X -
DROP PUBLICATION X X X -
DROP ROLE X X X -
DROP ROUTINE X X X -
DROP RULE X X X -
DROP SCHEMA X X X -
DROP SEQUENCE X X X -
DROP SERVER X X X -
DROP STATISTICS X X X -
DROP SUBSCRIPTION X X X -
DROP TABLE X X X -
DROP TABLESPACE X X X -
DROP TEXT SEARCH CONFIGURATION X X X -
DROP TEXT SEARCH DICTIONARY X X X -
DROP TEXT SEARCH PARSER X X X -
DROP TEXT SEARCH TEMPLATE X X X -
DROP TRIGGER X X X -
DROP TYPE X X X -
DROP USER X X X -
DROP USER MAPPING X X X -
DROP VIEW X X X -
GRANT X X - -
IMPORT FOREIGN SCHEMA X X - -
REFRESH MATERIALIZED VIEW X X - -
REVOKE X X - -
SECURITY LABEL X X - -
SELECT INTO X X - -
事件触发器不支持捕捉目标对象是数据库的 DDL 命令,包括:CREATE DATABASEALTER DATABASEDROP DATABASECOMMENT ON DATABASEGRANT ON DATABASEREVOKE ON DATABASE

事件触发器函数

Redrock Postgres 提供了这些助手函数来从事件触发器检索信息。

在命令结束处捕捉更改

pg_event_trigger_ddl_commands () → setof record

当在一个ddl_command_end事件触发器的函数中调用时,pg_event_trigger_ddl_commands返回被每一个用户动作执行的 DDL 命令的列表。如果在其他任何环境中调用这个函数,会发生错误。pg_event_trigger_ddl_commands为每一个被执行的基本命令返回一行,某些只有一个单一 SQL 句子的命令可能会返回多于一行。这个函数返回下面的列:

名称 类型 描述
classid oid 对象所属的系统表的 OID
objid oid 对象本身的 OID
objsubid integer 对象的子编号(例如列的属性号)
command_tag text 命令标签
object_type text 对象的类型
schema_name text 该对象所属的模式的名称(如果有),如果没有则为NULL。没有引号修饰。
object_identity text 对象标识的文本表现形式,用模式限定。如果必要,出现在该标识中的每一个标识符会被引号修饰。
in_extension bool 如果该命令是一个扩展脚本的一部分则为真
command pg_ddl_command 以内部格式表达的该命令的一个完整表现形式。这不能被直接输出,但是可以把它传递给其他函数来得到有关于该命令不同部分的信息。

处理被 DDL 命令删除的对象

pg_event_trigger_dropped_objects () → setof record

pg_event_trigger_dropped_objects返回被调用sql_drop事件的命令删除的所有对象的列表。如果调用在任何其他上下文中,会引发一个错误。这个函数返回以下列:

名称 类型 描述
classid oid 对象所属的系统表的 OID
objid oid 对象本身的 OID
objsubid integer 对象的子编号(如列的属性号)
original bool 如果这是删除中的一个根对象则为真
normal bool 指示在依赖图中有一个普通依赖关系指向该对象的标志
is_temporary bool 如果该对象是一个临时对象则为真
object_type text 对象的类型
schema_name text 对象所属模式的名称(如果存在);否则为NULL。不应用引用。
object_name text 如果模式和名称的组合能被用于对象的一个唯一标识符,则是对象的名称;否则是NULL。不应用引用,并且名称不是模式限定的。
object_identity text 对象身份的文本表现,模式限定的。每一个以及所有身份中出现的标识符在必要时加引号。
address_names text[] 一个数组,它可以和object_typeaddress_args一起通过pg_get_object_address()函数在一台包含有同类相同名称对象的远程服务器上重建该对象地址。
address_args text[] 上述address_names的补充。

pg_event_trigger_dropped_objects可以被这样用在一个事件触发器中:

CREATE FUNCTION test_event_trigger_for_drops()
  RETURNS event_trigger AS $$
DECLARE
    obj record;
BEGIN
    FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()
    LOOP
        RAISE NOTICE '% dropped object: % %.% %',
                     tg_tag,
                     obj.object_type,
                     obj.schema_name,
                     obj.object_name,
                     obj.object_identity;
    END LOOP;
END
$$ LANGUAGE plpgsql;

CREATE EVENT TRIGGER test_event_trigger_for_drops
    ON sql_drop
    EXECUTE PROCEDURE test_event_trigger_for_drops();

处理表重写事件

表 2 中所示的函数提供刚刚被调用过table_rewrite事件的表的信息。如果在任何其他环境中调用,会发生错误。

表 2. 表重写信息

函数 描述
pg_event_trigger_table_rewrite_oid () → oid 返回将要重写的表的 OID。
pg_event_trigger_table_rewrite_reason () → integer 返回解释重写原因的代码。代码的确切含义取决于版本。

可以在一个这样的事件触发器中使用pg_event_trigger_table_rewrite_oid函数:

CREATE FUNCTION test_event_trigger_table_rewrite_oid()
  RETURNS event_trigger AS $$
BEGIN
  RAISE NOTICE 'rewriting table % for reason %',
                pg_event_trigger_table_rewrite_oid()::regclass,
                pg_event_trigger_table_rewrite_reason();
END;
$$ LANGUAGE plpgsql;

CREATE EVENT TRIGGER test_table_rewrite_oid
    ON table_rewrite
    EXECUTE PROCEDURE test_event_trigger_table_rewrite_oid();

反解析 DDL 命令

pg_ddl_command_deparse ( pg_ddl_command ) → text

函数将一条 DDL 命令从pg_ddl_command类型的内部格式进行反向解析,并以text类型的文本表示形式返回 SQL 语句。这个函数可用于事件触发器,如下所示:

CREATE TABLE ddl_history(
  ord             int,
  op_time         timestamp,
  username        text,
  command_tag     text,
  object_type     text,
  schema_name     text,
  object_identity text,
  command         text
);

CREATE OR REPLACE FUNCTION ddl_trigger_func()
  RETURNS event_trigger AS $$
BEGIN
  INSERT INTO ddl_history
    SELECT ordinality, now(), current_user, command_tag,
           object_type, schema_name, object_identity,
           pg_ddl_command_deparse(command)
      FROM pg_event_trigger_ddl_commands() WITH ORDINALITY;
END;
$$ LANGUAGE plpgsql;

CREATE EVENT TRIGGER deparse_event_trigger
    ON ddl_command_end
    EXECUTE PROCEDURE ddl_trigger_func();