Hi, Michael Paquier <michael.paqu...@gmail.com> writes: > 1) This patch is authorizing VACUUM and CLUSTER to use the event > triggers ddl_command_start and ddl_command_end, but aren't those > commands actually not DDLs but control commands?
Reverted in the attached version 3 of the patch. > 6) in_table_rewrite seems unnecessary. Removed in the attached version 3 of the patch. On Sun, Nov 16, 2014 at 5:51 AM, Simon Riggs <si...@2ndquadrant.com> wrote: >> 4) pg_event_trigger_table_rewrite_oid is able to return only one OID, >> which is the one of the table being rewritten, and it is limited to >> one OID because VACUUM, CLUSTER and ALTER TABLE can only run on one >> object at the same time in a single transaction. What about thinking >> that we may have in the future multiple objects rewritten in a single >> transaction, hence multiple OIDs could be fetched? > > Why would this API support something which the normal trigger API > doesn't, just in case we support a feature that hadn't ever been > proposed or discussed? Why can't such a change wait until that feature > arrives? Agreed, unchanged in the attached. Robert Haas <robertmh...@gmail.com> writes: > It seems pretty weird, also, that the event trigger will fire after > we've taken AccessExclusiveLock when you cluster a particular > relation, and before we've taken AccessExclusiveLock when you cluster > database-wide. That's more or less an implementation artifact of the > current code that we're exposing to the use for, really, no good > reason. In the CLUSTER implementation we have only one call site for invoking the Event Trigger, in cluster_rel(). While it's true that in the single relation case, the relation is opened in cluster() then cluster_rel() is called, the opening is done with NoLock in cluster(): rel = heap_open(tableOid, NoLock); My understanding is that the relation locking only happens in cluster_rel() at this line: OldHeap = try_relation_open(tableOid, AccessExclusiveLock); Please help me through the cluster locking strategy here, I feel like I'm missing something obvious, as my conclusion from re-reading the code in lights of your comment is that your comment is not accurate with respect to the current state of the code. Regards, -- Dimitri Fontaine 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..704a377 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,518 +126,625 @@ <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> <row> + <entry align="left"><literal>ANALYZE</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 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>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> @@ -843,4 +956,58 @@ COMMIT; event triggers.) </para> </sect1> + + <sect1 id="event-trigger-table-rewrite-example"> + <title>A Table Rewrite Event Trigger Example</title> + + <para> + Thanks to the <literal>table_rewrite</> event, it is possible to implement + a table rewriting policy only allowing the rewrite in maintenance windows. + </para> + + <para> + Here's an example implementing such a policy. +<programlisting> +create or replace function no_rewrite() + returns event_trigger + language plpgsql as +$$ +--- +--- Implement local Table Rewriting policy: +--- public.foo is not allowed rewriting, ever +--- other tables are only allowed rewriting between 1am and 6am +--- unless they have more than 100 blocks +--- +declare + table_oid oid := pg_event_trigger_table_rewrite_oid(); + current_hour integer := extract('hour' from current_time); + pages integer; + max_pages integer := 100; +begin + if pg_event_trigger_table_rewrite_oid() = 'public.foo'::regclass + then + raise exception 'you''re not allowed to rewrite the table %', + table_oid::regclass; + end if; + + select into pages relpages from pg_class where oid = table_oid; + if pages > max_pages + then + raise exception 'rewrites only allowed for table with less than % pages', + max_pages; + end if; + + if not current_hour between 1 and 6 + then + raise exception 'rewrites only allowed between 1am and 6am'; + end if; +end; +$$; + +create event trigger no_rewrite_allowed + on table_rewrite + execute procedure no_rewrite(); +</programlisting> + </para> + </sect1> </chapter> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 7e5bcd9..24bf78c 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -17540,16 +17540,20 @@ FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger(); <sect1 id="functions-event-triggers"> <title>Event Trigger Functions</title> + <para> + Currently <productname>PostgreSQL</> provides two built-in event trigger + helper function, <function>pg_event_trigger_dropped_objects</> + and <function>pg_event_trigger_table_rewrite_oid</>. + </para> + + <sect2 id="pg-event-trigger-dropped-objects"> + <title>Processing objects dropped by a DDL command.</title> + <indexterm> <primary>pg_event_trigger_dropped_objects</primary> </indexterm> <para> - Currently <productname>PostgreSQL</> provides one built-in event trigger - helper function, <function>pg_event_trigger_dropped_objects</>. - </para> - - <para> <function>pg_event_trigger_dropped_objects</> returns a list of all objects dropped by the command in whose <literal>sql_drop</> event it is called. If called in any other context, @@ -17647,6 +17651,68 @@ CREATE EVENT TRIGGER test_event_trigger_for_drops For more information about event triggers, see <xref linkend="event-triggers">. </para> + </sect2> + + <sect2 id="pg-event-trigger-table-rewrite-oid"> + <title>Handling a Table Rewrite Event</title> + + <indexterm> + <primary>pg_event_trigger_table_rewrite_oid</primary> + </indexterm> + + <para> + <function>pg_event_trigger_table_rewrite_oid</> returns + the <literal>OID</> of the table that is going to be rewritten on-disk by + the current DDL command. If called in any other context, + <function>pg_event_trigger_table_rewrite_oid</> raises an error. + <function>pg_event_trigger_table_rewrite_oid</> returns the following + columns: + + <informaltable> + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><literal>oid</literal></entry> + <entry><type>Oid</type></entry> + <entry>OID of the pg_class entry for the table.</entry> + </row> + </tbody> + </tgroup> + </informaltable> + </para> + + <para> + The <function>pg_event_trigger_table_rewrite_oid</> function can be used + in an event trigger like this: +<programlisting> +create function test_event_trigger_table_rewrite_oid() + returns event_trigger + language plpgsql as +$$ +begin + raise notice 'rewriting table %', pg_event_trigger_table_rewrite_oid()::regclass; +end; +$$; + +create event trigger test_table_rewrite_oid + on table_rewrite + execute procedure test_event_trigger_table_rewrite_oid(); +</programlisting> + </para> + + <para> + For more information about event triggers, + see <xref linkend="event-triggers">. + </para> + </sect2> </sect1> </chapter> diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index ff80b09..64d1312 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, tableOid); + + /* * 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..5d132df 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -42,11 +42,14 @@ #include "utils/syscache.h" #include "tcop/utility.h" - +/* + * Data Structure for sql_drop and table_rewrite Event Trigger support. + */ typedef struct EventTriggerQueryState { slist_head SQLDropList; bool in_sql_drop; + Oid table_rewrite_oid; MemoryContext cxt; struct EventTriggerQueryState *previous; } EventTriggerQueryState; @@ -119,11 +122,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 +160,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 +190,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 @@ -257,6 +267,7 @@ check_ddl_tag(const char *tag) /* * Otherwise, command should be CREATE, ALTER, or DROP. + * Or one of ANALYZE, CLUSTER, VACUUM. */ if (pg_strncasecmp(tag, "CREATE ", 7) == 0) obtypename = tag + 7; @@ -264,6 +275,10 @@ check_ddl_tag(const char *tag) obtypename = tag + 6; else if (pg_strncasecmp(tag, "DROP ", 5) == 0) obtypename = tag + 5; + else if (pg_strncasecmp(tag, "ANALYZE", 7) == 0 || + pg_strncasecmp(tag, "CLUSTER", 7) == 0 || + pg_strncasecmp(tag, "VACUUM", 6) == 0) + return EVENT_TRIGGER_COMMAND_TAG_OK; else return EVENT_TRIGGER_COMMAND_TAG_NOT_RECOGNIZED; @@ -281,6 +296,41 @@ 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 || + pg_strcasecmp(tag, "ANALYZE") == 0 ) + return EVENT_TRIGGER_COMMAND_TAG_OK; + + return EVENT_TRIGGER_COMMAND_TAG_NOT_SUPPORTED; +} + +/* * Complain about a duplicate filter variable. */ static void @@ -641,8 +691,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 +898,77 @@ EventTriggerSQLDrop(Node *parsetree) list_free(runlist); } + +/* + * Fire table_rewrite triggers. + */ +void +EventTriggerTableRewrite(Node *parsetree, Oid tableOid) +{ + List *runlist; + EventTriggerData trigdata; + + elog(DEBUG1, "EventTriggerTableRewrite(%u)", tableOid); + + /* + * 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; + + /* + * Make sure pg_event_trigger_table_rewrite_oid only works when running + * these triggers. Use PG_TRY to ensure table_rewrite_oid is reset even + * when one trigger fails. (This is perhaps not necessary, as the + * currentState variable will be removed shortly by our caller, but it + * seems better to play safe.) + */ + currentEventTriggerState->table_rewrite_oid = tableOid; + + /* Run the triggers. */ + PG_TRY(); + { + EventTriggerInvoke(runlist, &trigdata); + } + PG_CATCH(); + { + currentEventTriggerState->table_rewrite_oid = InvalidOid; + PG_RE_THROW(); + } + PG_END_TRY(); + + currentEventTriggerState->table_rewrite_oid = InvalidOid; + + /* 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. */ @@ -871,6 +1002,8 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata) FunctionCallInfoData fcinfo; PgStat_FunctionCallUsage fcusage; + elog(DEBUG1, "EventTriggerInvoke %u", fnoid); + /* * We want each event trigger to be able to see the results of the * previous event trigger's action. Caller is responsible for any @@ -1026,8 +1159,9 @@ EventTriggerBeginCompleteQuery(void) MemoryContext cxt; /* - * Currently, sql_drop events are the only reason to have event trigger - * state at all; so if there are none, don't install one. + * Currently, sql_drop and table_rewrite events are the only reason to + * have event trigger state at all; so if there are none, don't install + * one. */ if (!trackDroppedObjectsNeeded()) return false; @@ -1041,6 +1175,7 @@ EventTriggerBeginCompleteQuery(void) state->cxt = cxt; slist_init(&(state->SQLDropList)); state->in_sql_drop = false; + state->table_rewrite_oid = InvalidOid; state->previous = currentEventTriggerState; currentEventTriggerState = state; @@ -1080,8 +1215,9 @@ EventTriggerEndCompleteQuery(void) bool trackDroppedObjectsNeeded(void) { - /* true if any sql_drop event trigger exists */ - return list_length(EventCacheLookup(EVT_SQLDrop)) > 0; + /* true if any sql_drop or table_rewrite event trigger exists */ + return list_length(EventCacheLookup(EVT_SQLDrop)) > 0 || + list_length(EventCacheLookup(EVT_TableRewrite)) > 0; } /* @@ -1297,3 +1433,25 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS) return (Datum) 0; } + +/* + * pg_event_trigger_table_rewrite_oid + * + * Make the Oid of the table going to be rewritten available to the user + * function run by the Event Trigger. + */ +Datum +pg_event_trigger_table_rewrite_oid(PG_FUNCTION_ARGS) +{ + /* + * Protect this function from being called out of context + */ + if (!currentEventTriggerState || + currentEventTriggerState->table_rewrite_oid != InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("%s can only be called in a table_rewrite event trigger function", + "pg_event_trigger_table_rewrite_oid()"))); + + PG_RETURN_OID(currentEventTriggerState->table_rewrite_oid); +} diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index ecdff1e..7477dd0 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, tab->relid); + 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, tab->relid); + 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/tcop/utility.c b/src/backend/tcop/utility.c index 4a2a339..c72bc72 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -626,23 +626,6 @@ standard_ProcessUtility(Node *parsetree, } break; - case T_ClusterStmt: - /* we choose to allow this during "read only" transactions */ - PreventCommandDuringRecovery("CLUSTER"); - cluster((ClusterStmt *) parsetree, isTopLevel); - break; - - case T_VacuumStmt: - { - VacuumStmt *stmt = (VacuumStmt *) parsetree; - - /* we choose to allow this during "read only" transactions */ - PreventCommandDuringRecovery((stmt->options & VACOPT_VACUUM) ? - "VACUUM" : "ANALYZE"); - vacuum(stmt, InvalidOid, true, NULL, false, isTopLevel); - } - break; - case T_ExplainStmt: ExplainQuery((ExplainStmt *) parsetree, queryString, params, dest); break; @@ -843,6 +826,74 @@ standard_ProcessUtility(Node *parsetree, } /* + * VACUUM and CLUSTER support the table_rewrite Event Trigger, but they are + * not covered by the ddl_command_{start|end} event triggers, so they need a + * special treatment here. + */ +static bool +ProcessUtilityForTableRewriteSupportOnly(Node *parsetree, + const char *queryString, + ProcessUtilityContext context, + ParamListInfo params, + DestReceiver *dest, + char *completionTag, + bool isTopLevel, + bool needCleanup) +{ + bool done = false; + + PG_TRY(); + { + switch (nodeTag(parsetree)) + { + case T_ClusterStmt: + { + /* we choose to allow this during "read only" transactions */ + PreventCommandDuringRecovery("CLUSTER"); + cluster((ClusterStmt *) parsetree, isTopLevel); + + done = true; + } + break; + + case T_VacuumStmt: + { + VacuumStmt *stmt = (VacuumStmt *) parsetree; + /* we choose to allow this during "read only" transactions */ + PreventCommandDuringRecovery((stmt->options & VACOPT_VACUUM) ? + "VACUUM" : "ANALYZE"); + vacuum(stmt, InvalidOid, true, NULL, false, isTopLevel); + + done = true; + } + break; + + default: + { + /* + * make compiler happy about all those enumeration value we + * are not handling on purpose. + */ + (void) 0; + } + break; + } + } + PG_CATCH(); + { + if (done && needCleanup) + EventTriggerEndCompleteQuery(); + PG_RE_THROW(); + } + PG_END_TRY(); + + if (done && needCleanup) + EventTriggerEndCompleteQuery(); + + return done; +} + +/* * The "Slow" variant of ProcessUtility should only receive statements * supported by the event triggers facility. Therefore, we always * perform the trigger support calls if the context allows it. @@ -862,6 +913,21 @@ ProcessUtilitySlow(Node *parsetree, /* All event trigger calls are done only when isCompleteQuery is true */ needCleanup = isCompleteQuery && EventTriggerBeginCompleteQuery(); + /* + * Take care of commands with support for table_rewrite but no support for + * ddl_command_start and ddl_command_end Event Trigger support, currently + * only CLUSTER and VACUUM. + */ + if (ProcessUtilityForTableRewriteSupportOnly(parsetree, + queryString, + context, + params, + dest, + completionTag, + isTopLevel, + needCleanup)) + return; + /* PG_TRY block is to ensure we call EventTriggerEndCompleteQuery */ PG_TRY(); { 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/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 4736532..b1e0372 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4990,6 +4990,8 @@ DESCR("peek at binary changes from replication slot"); /* event triggers */ DATA(insert OID = 3566 ( pg_event_trigger_dropped_objects PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 2249 "" "{26,26,23,25,25,25,25}" "{o,o,o,o,o,o,o}" "{classid, objid, objsubid, object_type, schema_name, object_name, object_identity}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ )); DESCR("list objects dropped by the current command"); +DATA(insert OID = 4566 ( pg_event_trigger_table_rewrite_oid PGNSP PGUID 12 10 100 0 0 f f f f t t s 0 0 26 "" "{26}" "{o}" "{oid}" _null_ pg_event_trigger_table_rewrite_oid _null_ _null_ _null_ )); +DESCR("return Oid of the table getting rewritten"); /* generic transition functions for ordered-set aggregates */ DATA(insert OID = 3970 ( ordered_set_transition PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2276" _null_ _null_ _null_ _null_ ordered_set_transition _null_ _null_ _null_ )); 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..5080f4f 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, Oid tableOid); extern bool EventTriggerBeginCompleteQuery(void); extern void EventTriggerEndCompleteQuery(void); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index fb1b4a4..02b581d 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -1189,6 +1189,7 @@ extern Datum unique_key_recheck(PG_FUNCTION_ARGS); /* commands/event_trigger.c */ extern Datum pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS); +extern Datum pg_event_trigger_table_rewrite_oid(PG_FUNCTION_ARGS); /* commands/extension.c */ extern Datum pg_available_extensions(PG_FUNCTION_ARGS); 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