事件触发器
Redrock Postgres 提供了事件触发器。和常规触发器(附着在一个表上并且只捕捉 DML 事件)不同,事件触发器对一个特定数据库来说是全局的,并且可以捕捉 DDL 事件。
和常规触发器相似,可以用任何包括了事件触发器支持的过程语言编写事件触发器,但是不能用纯 SQL 编写。
只要与一个事件触发器相关的事件在事件触发器所在的数据库中发生,该事件触发器就会被引发。当前支持的事件是ddl_command_start
、ddl_command_end
、table_rewrite
和sql_drop
。未来的发行版中可能会增加对更多事件的支持。
ddl_command_start
事件就在CREATE
、ALTER
、DROP
、SECURITY LABEL
、COMMENT
、GRANT
或者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 TABLE
和ALTER TYPE
的某些动作重写之前发生。虽然其他控制语句(例如CLUSTER
和VACUUM
)也可以用来重写表,但是它们不会触发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 DATABASE
,ALTER DATABASE
,DROP DATABASE
,COMMENT ON DATABASE
,GRANT ON DATABASE
,REVOKE 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 |
以内部格式表达的该命令的一个完整表现形式。这不能被直接输出,但是可以把它传递给其他函数来得到有关于该命令不同部分的信息。 |
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_type 及address_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();
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();