Dimitri Fontaine <dimi...@2ndquadrant.fr> writes: > Please find attached to this email a patch to implement a new Event > Trigger, fired on the the "table_rewrite" event. As attached, it's meant > as a discussion enabler and only supports ALTER TABLE (and maybe not in > all forms of it). It will need to grow support for VACUUM FULL and > CLUSTER and more before getting commited.
And here's already a new version of it, including support for ALTER TABLE, VACUUM and CLUSTER commands, and documentation. Still is a small patch: doc/src/sgml/event-trigger.sgml | 106 ++++++++++++++++++++ src/backend/commands/cluster.c | 14 ++- src/backend/commands/event_trigger.c | 106 +++++++++++++++++++- src/backend/commands/tablecmds.c | 53 ++++++++-- src/backend/commands/vacuum.c | 3 +- src/backend/utils/cache/evtcache.c | 2 + src/include/commands/cluster.h | 4 +- src/include/commands/event_trigger.h | 1 + src/include/utils/evtcache.h | 3 +- src/test/regress/expected/event_trigger.out | 23 +++++ src/test/regress/sql/event_trigger.sql | 24 +++++ 11 files changed, 322 insertions(+), 17 deletions(-) -- Dimitri Fontaine 06 63 07 10 78 http://2ndQuadrant.fr PostgreSQL : Expertise, Formation et Support
diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml index 6f71a27..08ae838 100644 --- a/doc/src/sgml/event-trigger.sgml +++ b/doc/src/sgml/event-trigger.sgml @@ -65,6 +65,12 @@ </para> <para> + The <literal>table_rewrite</> event occurs just before a table is going to + get rewritten by the commands <literal>ALTER TABLE</literal>, + <literal>CLUSTER</literal> or <literal>VACUUM</literal>. + </para> + + <para> Event triggers (like other functions) cannot be executed in an aborted transaction. Thus, if a DDL command fails with an error, any associated <literal>ddl_command_end</> triggers will not be executed. Conversely, @@ -120,6 +126,7 @@ <entry><literal>ddl_command_start</literal></entry> <entry><literal>ddl_command_end</literal></entry> <entry><literal>sql_drop</literal></entry> + <entry><literal>table_rewrite</literal></entry> </row> </thead> <tbody> @@ -128,510 +135,609 @@ <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER COLLATION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER CONVERSION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER DOMAIN</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER EXTENSION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER FOREIGN DATA WRAPPER</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER FOREIGN TABLE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER FUNCTION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER LANGUAGE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER OPERATOR</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER OPERATOR CLASS</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER OPERATOR FAMILY</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER POLICY</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER SCHEMA</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER SEQUENCE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER SERVER</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER TABLE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>X</literal></entry> </row> <row> <entry align="left"><literal>ALTER TEXT SEARCH CONFIGURATION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER TEXT SEARCH DICTIONARY</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER TEXT SEARCH PARSER</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER TEXT SEARCH TEMPLATE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER TRIGGER</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER TYPE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER USER MAPPING</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>ALTER VIEW</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> + </row> + <row> + <entry align="left"><literal>CLUSTER</literal></entry> + <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>X</literal></entry> </row> <row> <entry align="left"><literal>CREATE AGGREGATE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE CAST</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE COLLATION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE CONVERSION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE DOMAIN</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE EXTENSION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE FOREIGN DATA WRAPPER</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE FOREIGN TABLE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE FUNCTION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE INDEX</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE LANGUAGE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE OPERATOR</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE OPERATOR CLASS</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE OPERATOR FAMILY</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE POLICY</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE RULE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE SCHEMA</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE SEQUENCE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE SERVER</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE TABLE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE TABLE AS</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE TEXT SEARCH CONFIGURATION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE TEXT SEARCH DICTIONARY</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE TEXT SEARCH PARSER</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE TEXT SEARCH TEMPLATE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE TRIGGER</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE TYPE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE USER MAPPING</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>CREATE VIEW</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP AGGREGATE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP CAST</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP COLLATION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP CONVERSION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP DOMAIN</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP EXTENSION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP FOREIGN DATA WRAPPER</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP FOREIGN TABLE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP FUNCTION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP INDEX</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP LANGUAGE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP OPERATOR</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP OPERATOR CLASS</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP OPERATOR FAMILY</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP OWNED</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP POLICY</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP RULE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP SCHEMA</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP SEQUENCE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP SERVER</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP TABLE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP TEXT SEARCH CONFIGURATION</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP TEXT SEARCH DICTIONARY</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP TEXT SEARCH PARSER</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP TEXT SEARCH TEMPLATE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP TRIGGER</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP TYPE</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP USER MAPPING</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>DROP VIEW</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>IMPORT FOREIGN SCHEMA</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> </row> <row> <entry align="left"><literal>SELECT INTO</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>X</literal></entry> <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> + </row> + <row> + <entry align="left"><literal>VACUUM</literal></entry> + <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>-</literal></entry> + <entry align="center"><literal>X</literal></entry> </row> </tbody> </tgroup> diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index ff80b09..fcf11ef 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -31,6 +31,7 @@ #include "catalog/objectaccess.h" #include "catalog/toasting.h" #include "commands/cluster.h" +#include "commands/event_trigger.h" #include "commands/tablecmds.h" #include "commands/vacuum.h" #include "miscadmin.h" @@ -175,7 +176,7 @@ cluster(ClusterStmt *stmt, bool isTopLevel) heap_close(rel, NoLock); /* Do the job. */ - cluster_rel(tableOid, indexOid, false, stmt->verbose); + cluster_rel((Node *)stmt, tableOid, indexOid, false, stmt->verbose); } else { @@ -225,7 +226,8 @@ cluster(ClusterStmt *stmt, bool isTopLevel) /* functions in indexes may want a snapshot set */ PushActiveSnapshot(GetTransactionSnapshot()); /* Do the job. */ - cluster_rel(rvtc->tableOid, rvtc->indexOid, true, stmt->verbose); + cluster_rel((Node *)stmt, + rvtc->tableOid, rvtc->indexOid, true, stmt->verbose); PopActiveSnapshot(); CommitTransactionCommand(); } @@ -256,7 +258,8 @@ cluster(ClusterStmt *stmt, bool isTopLevel) * and error messages should refer to the operation as VACUUM not CLUSTER. */ void -cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose) +cluster_rel(Node *parsetree, + Oid tableOid, Oid indexOid, bool recheck, bool verbose) { Relation OldHeap; @@ -264,6 +267,11 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose) CHECK_FOR_INTERRUPTS(); /* + * Fire off an Event Trigger now, before actually rewriting the table. + */ + EventTriggerTableRewrite((Node *)parsetree); + + /* * We grab exclusive access to the target rel and index for the duration * of the transaction. (This is redundant for the single-transaction * case, since cluster() already did it.) The index lock is taken inside diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 1b8c94b..8bf2d28 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -119,11 +119,14 @@ static void AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId); static event_trigger_command_tag_check_result check_ddl_tag(const char *tag); +static event_trigger_command_tag_check_result check_table_rewrite_ddl_tag( + const char *tag); static void error_duplicate_filter_variable(const char *defname); static Datum filter_list_to_array(List *filterlist); static Oid insert_event_trigger_tuple(char *trigname, char *eventname, Oid evtOwner, Oid funcoid, List *tags); static void validate_ddl_tags(const char *filtervar, List *taglist); +static void validate_table_rewrite_tags(const char *filtervar, List *taglist); static void EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata); /* @@ -154,7 +157,8 @@ CreateEventTrigger(CreateEventTrigStmt *stmt) /* Validate event name. */ if (strcmp(stmt->eventname, "ddl_command_start") != 0 && strcmp(stmt->eventname, "ddl_command_end") != 0 && - strcmp(stmt->eventname, "sql_drop") != 0) + strcmp(stmt->eventname, "sql_drop") != 0 && + strcmp(stmt->eventname, "table_rewrite") != 0) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("unrecognized event name \"%s\"", @@ -183,6 +187,9 @@ CreateEventTrigger(CreateEventTrigStmt *stmt) strcmp(stmt->eventname, "sql_drop") == 0) && tags != NULL) validate_ddl_tags("tag", tags); + else if (strcmp(stmt->eventname, "table_rewrite") == 0 + && tags != NULL) + validate_table_rewrite_tags("tag", tags); /* * Give user a nice error message if an event trigger of the same name @@ -281,6 +288,40 @@ check_ddl_tag(const char *tag) } /* + * Validate DDL command tags. + */ +static void +validate_table_rewrite_tags(const char *filtervar, List *taglist) +{ + ListCell *lc; + + foreach(lc, taglist) + { + const char *tag = strVal(lfirst(lc)); + event_trigger_command_tag_check_result result; + + result = check_table_rewrite_ddl_tag(tag); + if (result == EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + /* translator: %s represents an SQL statement name */ + errmsg("event triggers are not supported for %s", + tag))); + } +} + +static event_trigger_command_tag_check_result +check_table_rewrite_ddl_tag(const char *tag) +{ + if (pg_strcasecmp(tag, "ALTER TABLE") == 0 || + pg_strcasecmp(tag, "CLUSTER") == 0 || + pg_strcasecmp(tag, "VACUUM") == 0) + return EVENT_TRIGGER_COMMAND_TAG_OK; + + return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED; +} + +/* * Complain about a duplicate filter variable. */ static void @@ -641,8 +682,18 @@ EventTriggerCommonSetup(Node *parsetree, const char *dbgtag; dbgtag = CreateCommandTag(parsetree); - if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK) - elog(ERROR, "unexpected command tag \"%s\"", dbgtag); + if (event == EVT_DDLCommandStart || + event == EVT_DDLCommandEnd || + event == EVT_SQLDrop) + { + if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK) + elog(ERROR, "unexpected command tag \"%s\"", dbgtag); + } + else if (event == EVT_TableRewrite) + { + if(check_table_rewrite_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK) + elog(ERROR, "unexpected command tag \"%s\"", dbgtag); + } } #endif @@ -838,6 +889,55 @@ EventTriggerSQLDrop(Node *parsetree) list_free(runlist); } + +/* + * Fire table_rewrite triggers. + */ +void +EventTriggerTableRewrite(Node *parsetree) +{ + List *runlist; + EventTriggerData trigdata; + + /* + * Event Triggers are completely disabled in standalone mode. There are + * (at least) two reasons for this: + * + * 1. A sufficiently broken event trigger might not only render the + * database unusable, but prevent disabling itself to fix the situation. + * In this scenario, restarting in standalone mode provides an escape + * hatch. + * + * 2. BuildEventTriggerCache relies on systable_beginscan_ordered, and + * therefore will malfunction if pg_event_trigger's indexes are damaged. + * To allow recovery from a damaged index, we need some operating mode + * wherein event triggers are disabled. (Or we could implement + * heapscan-and-sort logic for that case, but having disaster recovery + * scenarios depend on code that's otherwise untested isn't appetizing.) + */ + if (!IsUnderPostmaster) + return; + + runlist = EventTriggerCommonSetup(parsetree, + EVT_TableRewrite, + "table_rewrite", + &trigdata); + if (runlist == NIL) + return; + + /* Run the triggers. */ + EventTriggerInvoke(runlist, &trigdata); + + /* Cleanup. */ + list_free(runlist); + + /* + * Make sure anything the event triggers did will be visible to the main + * command. + */ + CommandCounterIncrement(); +} + /* * Invoke each event trigger in a list of event triggers. */ diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index ecdff1e..ccc144c 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -46,6 +46,7 @@ #include "commands/cluster.h" #include "commands/comment.h" #include "commands/defrem.h" +#include "commands/event_trigger.h" #include "commands/policy.h" #include "commands/sequence.h" #include "commands/tablecmds.h" @@ -303,13 +304,15 @@ static void validateForeignKeyConstraint(char *conname, static void createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid); -static void ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode); +static void ATController(AlterTableStmt *parsetree, + Relation rel, List *cmds, bool recurse, LOCKMODE lockmode); static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode); static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode); static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode); -static void ATRewriteTables(List **wqueue, LOCKMODE lockmode); +static void ATRewriteTables(AlterTableStmt *parsetree, + List **wqueue, LOCKMODE lockmode); static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode); static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel); static void ATSimplePermissions(Relation rel, int allowed_targets); @@ -2723,7 +2726,8 @@ AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt) CheckTableNotInUse(rel, "ALTER TABLE"); - ATController(rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt), + ATController(stmt, + rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt), lockmode); } @@ -2746,7 +2750,7 @@ AlterTableInternal(Oid relid, List *cmds, bool recurse) rel = relation_open(relid, lockmode); - ATController(rel, cmds, recurse, lockmode); + ATController(NULL, rel, cmds, recurse, lockmode); } /* @@ -3015,7 +3019,8 @@ AlterTableGetLockLevel(List *cmds) } static void -ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode) +ATController(AlterTableStmt *parsetree, + Relation rel, List *cmds, bool recurse, LOCKMODE lockmode) { List *wqueue = NIL; ListCell *lcmd; @@ -3035,7 +3040,7 @@ ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode) ATRewriteCatalogs(&wqueue, lockmode); /* Phase 3: scan/rewrite tables as needed */ - ATRewriteTables(&wqueue, lockmode); + ATRewriteTables(parsetree, &wqueue, lockmode); } /* @@ -3606,8 +3611,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, * ATRewriteTables: ALTER TABLE phase 3 */ static void -ATRewriteTables(List **wqueue, LOCKMODE lockmode) +ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode) { + bool evt_table_rewrite_fired = false; ListCell *ltab; /* Go through each table that needs to be checked or rewritten */ @@ -3727,6 +3733,22 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode) lockmode); /* + * Fire off an Event Trigger now, before actually rewriting the + * table. + * + * We don't support Event Trigger for nested commands anywhere, + * here included, and parstree is given NULL when comming from + * AlterTableInternal. + * + * And fire it only once. + */ + if (parsetree && !evt_table_rewrite_fired) + { + EventTriggerTableRewrite((Node *)parsetree); + evt_table_rewrite_fired = true; + } + + /* * Copy the heap data into the new table with the desired * modifications, and test the current data within the table * against new constraints generated by ALTER TABLE commands. @@ -3764,7 +3786,24 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode) * generated by ALTER TABLE commands, but don't rebuild data. */ if (tab->constraints != NIL || tab->new_notnull) + { + /* + * Fire off an Event Trigger now, before actually rewriting the + * table. + * + * We don't support Event Trigger for nested commands anywhere, + * here included, and parstree is given NULL when comming from + * AlterTableInternal. + * + * And fire it only once. + */ + if (parsetree && !evt_table_rewrite_fired) + { + EventTriggerTableRewrite((Node *)parsetree); + evt_table_rewrite_fired = true; + } ATRewriteTable(tab, InvalidOid, lockmode); + } /* * If we had SET TABLESPACE but no reason to reconstruct tuples, diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index e5fefa3..779ceb8 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -1278,7 +1278,8 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound) onerel = NULL; /* VACUUM FULL is now a variant of CLUSTER; see cluster.c */ - cluster_rel(relid, InvalidOid, false, + cluster_rel((Node *)vacstmt, + relid, InvalidOid, false, (vacstmt->options & VACOPT_VERBOSE) != 0); } else diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c index ae71bd6..b9d442c 100644 --- a/src/backend/utils/cache/evtcache.c +++ b/src/backend/utils/cache/evtcache.c @@ -169,6 +169,8 @@ BuildEventTriggerCache(void) event = EVT_DDLCommandEnd; else if (strcmp(evtevent, "sql_drop") == 0) event = EVT_SQLDrop; + else if (strcmp(evtevent, "table_rewrite") == 0) + event = EVT_TableRewrite; else continue; diff --git a/src/include/commands/cluster.h b/src/include/commands/cluster.h index f7730a9..80b7433 100644 --- a/src/include/commands/cluster.h +++ b/src/include/commands/cluster.h @@ -19,8 +19,8 @@ extern void cluster(ClusterStmt *stmt, bool isTopLevel); -extern void cluster_rel(Oid tableOid, Oid indexOid, bool recheck, - bool verbose); +extern void cluster_rel(Node *parsetree, + Oid tableOid, Oid indexOid, bool recheck, bool verbose); extern void check_index_is_clusterable(Relation OldHeap, Oid indexOid, bool recheck, LOCKMODE lockmode); extern void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal); diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h index 0233f4c..d7e2958 100644 --- a/src/include/commands/event_trigger.h +++ b/src/include/commands/event_trigger.h @@ -46,6 +46,7 @@ extern bool EventTriggerSupportsObjectClass(ObjectClass objclass); extern void EventTriggerDDLCommandStart(Node *parsetree); extern void EventTriggerDDLCommandEnd(Node *parsetree); extern void EventTriggerSQLDrop(Node *parsetree); +extern void EventTriggerTableRewrite(Node *parsetree); extern bool EventTriggerBeginCompleteQuery(void); extern void EventTriggerEndCompleteQuery(void); diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h index c4c284f..4bbd80a 100644 --- a/src/include/utils/evtcache.h +++ b/src/include/utils/evtcache.h @@ -20,7 +20,8 @@ typedef enum { EVT_DDLCommandStart, EVT_DDLCommandEnd, - EVT_SQLDrop + EVT_SQLDrop, + EVT_TableRewrite } EventTriggerEvent; typedef struct diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out index d4723b2..5b08bd8 100644 --- a/src/test/regress/expected/event_trigger.out +++ b/src/test/regress/expected/event_trigger.out @@ -294,3 +294,26 @@ SELECT * FROM dropped_objects WHERE type = 'schema'; DROP ROLE regression_bob; DROP EVENT TRIGGER regress_event_trigger_drop_objects; DROP EVENT TRIGGER undroppable; +-- test Table Rewrite Event Trigger +CREATE OR REPLACE FUNCTION test_evtrig_no_rewrite() RETURNS event_trigger +LANGUAGE plpgsql AS $$ +BEGIN + RAISE EXCEPTION 'I''m sorry Sir, No Rewrite Allowed.'; +END; +$$; +create event trigger no_rewrite_allowed on table_rewrite + execute procedure test_evtrig_no_rewrite(); +create table rewriteme (id serial primary key, foo float); +insert into rewriteme + select x * 1.001 from generate_series(1, 500) as t(x); +alter table rewriteme alter column foo type numeric; +ERROR: I'm sorry Sir, No Rewrite Allowed. +alter table rewriteme add column baz int default 0; +ERROR: I'm sorry Sir, No Rewrite Allowed. +cluster rewriteme using rewriteme_pkey; +ERROR: I'm sorry Sir, No Rewrite Allowed. +vacuum full rewriteme; +ERROR: I'm sorry Sir, No Rewrite Allowed. +drop table rewriteme; +drop event trigger no_rewrite_allowed; +drop function test_evtrig_no_rewrite(); diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql index 11d2ce5..1fc244e 100644 --- a/src/test/regress/sql/event_trigger.sql +++ b/src/test/regress/sql/event_trigger.sql @@ -206,3 +206,27 @@ DROP ROLE regression_bob; DROP EVENT TRIGGER regress_event_trigger_drop_objects; DROP EVENT TRIGGER undroppable; + +-- test Table Rewrite Event Trigger +CREATE OR REPLACE FUNCTION test_evtrig_no_rewrite() RETURNS event_trigger +LANGUAGE plpgsql AS $$ +BEGIN + RAISE EXCEPTION 'I''m sorry Sir, No Rewrite Allowed.'; +END; +$$; + +create event trigger no_rewrite_allowed on table_rewrite + execute procedure test_evtrig_no_rewrite(); + +create table rewriteme (id serial primary key, foo float); +insert into rewriteme + select x * 1.001 from generate_series(1, 500) as t(x); +alter table rewriteme alter column foo type numeric; +alter table rewriteme add column baz int default 0; + +cluster rewriteme using rewriteme_pkey; +vacuum full rewriteme; + +drop table rewriteme; +drop event trigger no_rewrite_allowed; +drop function test_evtrig_no_rewrite();
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers