PostgreSQL中,常規觸發器依附於單個表並捕獲dml事件。而Event triggers是數據庫全局性的,可以捕獲ddl事件。與常規觸發器一樣,Event triggers可以用任何包含事件觸發器支持的過程語言編寫,也可以用C編寫,但不能用純SQL編寫。
當與事件關聯的事件在定義它的數據庫中發生時,就會觸發Event triggers。在11版本中,僅支持的事件是ddl_command_start,ddl_command_end,table_rewrite和sql_drop。在將來的版本中可能會添加對其他事件的支持。
ddl_command_start事件在執行CREATE,ALTER,DROP,SECURITY LABEL,COMMENT,GRANT或REVOKE命令之前發生。在觸發事件觸發器之前,不檢查受影響的對象是否存在。但是,作為例外,針對共享對象(數據庫,角色和表空間)的DDL命令或針對Event triggers本身的命令不會發生此事件。事件觸發機制不支持這些對象類型。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操作的范圍。
事件觸發觸發矩陣
Command Tag | ddl_command_start |
ddl_command_end |
sql_drop |
table_rewrite |
Notes |
---|---|---|---|---|---|
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 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 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 MAPPING |
X |
X |
- |
- |
|
ALTER VIEW |
X |
X |
- |
- |
|
COMMENT |
X |
X |
- |
- |
Only for local objects |
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 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 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 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 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 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 MAPPING |
X |
X |
X |
- |
|
DROP VIEW |
X |
X |
X |
- |
|
GRANT |
X |
X |
- |
- |
Only for local objects |
IMPORT FOREIGN SCHEMA |
X |
X |
- |
- |
|
REFRESH MATERIALIZED VIEW |
X |
X |
- |
- |
|
REVOKE |
X |
X |
- |
- |
Only for local objects |
SECURITY LABEL |
X |
X |
- |
- |
Only for local objects |
SELECT INTO |
X |
X |
- |
- |
1.創建一張用於存放ddl記錄的表:
CREATE TABLE ddl_history ( ID serial PRIMARY KEY, ddl_date timestamptz, ddl_tag TEXT, object_name TEXT );
2.定義兩個函數:
CREATE OR REPLACE FUNCTION log_ddl ( ) RETURNS event_trigger AS $$ DECLARE audit_query TEXT; r RECORD; BEGIN IF tg_tag <> 'DROP TABLE' THEN r := pg_event_trigger_ddl_commands ( ); INSERT INTO ddl_history ( ddl_date, ddl_tag, object_name ) VALUES ( statement_timestamp( ), tg_tag, r.object_identity ); END IF; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION log_ddl_drop ( ) RETURNS event_trigger AS $$ DECLARE audit_query TEXT; r RECORD; BEGIN IF tg_tag = 'DROP TABLE' THEN FOR r IN SELECT * FROM pg_event_trigger_ddl_commands ( ) LOOP INSERT INTO ddl_history ( ddl_date, ddl_tag, object_name ) VALUES ( statement_timestamp( ), tg_tag, r.object_identity ); END LOOP; END IF; END; $$ LANGUAGE plpgsql;
3.創建兩個事件觸發器
CREATE EVENT TRIGGER log_ddl_info ON ddl_command_end EXECUTE PROCEDURE log_ddl(); CREATE EVENT TRIGGER log_ddl_drop_info ON sql_drop EXECUTE PROCEDURE log_ddl_drop();
4.執行ddl測試
CREATE TABLE testtable (id int, first_name text); ALTER TABLE testtable ADD COLUMN last_name text; ALTER TABLE testtable ADD COLUMN midlname text; ALTER TABLE testtable RENAME COLUMN midlname TO middle_name; ALTER TABLE testtable DROP COLUMN middle_name; DROP TABLE testtable;
SELECT * FROM ddl_history;