[1mdiff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml[m
[1mindex 71241c8..492a4ed 100644[m
[1m--- a/doc/src/sgml/event-trigger.sgml[m
[1m+++ b/doc/src/sgml/event-trigger.sgml[m
[36m@@ -27,17 +27,19 @@[m
    <para>[m
      An event trigger fires whenever the event with which it is associated[m
      occurs in the database in which it is defined. Currently, the only[m
[31m-     supported events are <literal>ddl_command_start</>[m
[31m-     and <literal>ddl_command_end</>. Support for additional events may be[m
[31m-     added in future releases.[m
[32m+[m[32m     supported events are[m
[32m+[m[32m     <literal>ddl_command_start</>,[m
[32m+[m[32m     <literal>ddl_command_end</>[m
[32m+[m[32m     and <literal>sql_drop</>.[m
[32m+[m[32m     Support for additional events may be added in future releases.[m
    </para>[m
 [m
    <para>[m
      The <literal>ddl_command_start</> event occurs just before the[m
      execution of a <literal>CREATE</>, <literal>ALTER</>, or <literal>DROP</>[m
      command.  As an exception, however, this event does not occur for[m
[31m-     DDL commands targeting shared objects - databases, roles, and tablespaces[m
[31m-     - or for command targeting event triggers themselves.  The event trigger[m
[32m+[m[32m     DDL commands targeting shared objects &mdash; databases, roles, and tablespaces[m
[32m+[m[32m     &mdash; or for command targeting event triggers themselves.  The event trigger[m
      mechanism does not support these object types.[m
      <literal>ddl_command_start</> also occurs just before the execution of a[m
      <literal>SELECT INTO</literal> command, since this is equivalent to[m
[36m@@ -46,6 +48,17 @@[m
    </para>[m
 [m
    <para>[m
[32m+[m[32m    The <literal>sql_drop</> event occurs just before the[m
[32m+[m[32m    <literal>ddl_command_end</> event trigger for any operation that drops[m
[32m+[m[32m    database objects.  To list the objects that have been dropped, use the set[m
[32m+[m[32m    returning function <literal>pg_event_trigger_dropped_objects()</> from your[m
[32m+[m[32m    <literal>sql_drop</> event trigger code (see[m
[32m+[m[32m    <xref linkend="functions-event-triggers">). Note that[m
[32m+[m[32m    the trigger is executed after the objects have been deleted from the[m
[32m+[m[32m    system catalogs, so it's not possible to look them up anymore.[m
[32m+[m[32m   </para>[m
[32m+[m
[32m+[m[32m   <para>[m
      Event triggers (like other functions) cannot be executed in an aborted[m
      transaction.  Thus, if a DDL command fails with an error, any associated[m
      <literal>ddl_command_end</> triggers will not be executed.  Conversely,[m
[36m@@ -99,6 +112,7 @@[m
         <entry>command tag</entry>[m
         <entry><literal>ddl_command_start</literal></entry>[m
         <entry><literal>ddl_command_end</literal></entry>[m
[32m+[m[32m        <entry><literal>sql_drop</literal></entry>[m
        </row>[m
       </thead>[m
       <tbody>[m
[36m@@ -106,401 +120,487 @@[m
         <entry align="left"><literal>ALTER AGGREGATE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER COLLATION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER CONVERSION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER DOMAIN</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER EXTENSION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER FOREIGN DATA WRAPPER</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER FOREIGN TABLE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER FUNCTION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER LANGUAGE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER OPERATOR</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER OPERATOR CLASS</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER OPERATOR FAMILY</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER SCHEMA</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER SEQUENCE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER SERVER</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER TABLE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER TEXT SEARCH CONFIGURATION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER TEXT SEARCH DICTIONARY</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER TEXT SEARCH PARSER</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER TEXT SEARCH TEMPLATE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER TRIGGER</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER TYPE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER USER MAPPING</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>ALTER VIEW</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE AGGREGATE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE CAST</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE COLLATION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE CONVERSION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE DOMAIN</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE EXTENSION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE FOREIGN DATA WRAPPER</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE FOREIGN TABLE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE FUNCTION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE INDEX</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE LANGUAGE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE OPERATOR</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE OPERATOR CLASS</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE OPERATOR FAMILY</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE RULE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE SCHEMA</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE SEQUENCE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE SERVER</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE TABLE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE TABLE AS</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE TEXT SEARCH CONFIGURATION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE TEXT SEARCH DICTIONARY</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE TEXT SEARCH PARSER</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE TEXT SEARCH TEMPLATE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE TRIGGER</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE TYPE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE USER MAPPING</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>CREATE VIEW</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP AGGREGATE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP CAST</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP COLLATION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP CONVERSION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP DOMAIN</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP EXTENSION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP FOREIGN DATA WRAPPER</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP FOREIGN TABLE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP FUNCTION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP INDEX</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP LANGUAGE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP OPERATOR</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP OPERATOR CLASS</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP OPERATOR FAMILY</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m       </row>[m
[32m+[m[32m       <row>[m
[32m+[m[32m        <entry align="left"><literal>DROP OWNED</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP RULE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP SCHEMA</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP SEQUENCE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP SERVER</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP TABLE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP TEXT SEARCH CONFIGURATION</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP TEXT SEARCH DICTIONARY</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP TEXT SEARCH PARSER</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP TEXT SEARCH TEMPLATE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP TRIGGER</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP TYPE</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP USER MAPPING</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>DROP VIEW</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>X</literal></entry>[m
        </row>[m
        <row>[m
         <entry align="left"><literal>SELECT INTO</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
         <entry align="center"><literal>X</literal></entry>[m
[32m+[m[32m        <entry align="center"><literal>-</literal></entry>[m
        </row>[m
       </tbody>[m
      </tgroup>[m
[1mdiff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml[m
[1mindex 490d710..748a15d 100644[m
[1m--- a/doc/src/sgml/func.sgml[m
[1m+++ b/doc/src/sgml/func.sgml[m
[36m@@ -15980,4 +15980,51 @@[m [mFOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();[m
         <xref linkend="SQL-CREATETRIGGER">.[m
     </para>[m
   </sect1>[m
[32m+[m
[32m+[m[32m  <sect1 id="functions-event-triggers">[m
[32m+[m[32m   <title>Event Trigger Functions</title>[m
[32m+[m
[32m+[m[32m   <indexterm>[m
[32m+[m[32m     <primary>pg_event_trigger_dropped_objects</primary>[m
[32m+[m[32m   </indexterm>[m
[32m+[m
[32m+[m[32m   <para>[m
[32m+[m[32m    Currently <productname>PostgreSQL</> provides one built-in event trigger[m
[32m+[m[32m    helper function, <function>pg_event_trigger_dropped_objects</>, which[m
[32m+[m[32m    lists all object dropped by the command in whose <literal>sql_drop</>[m
[32m+[m[32m    event it is called.[m
[32m+[m[32m   </para>[m
[32m+[m
[32m+[m[32m   <para>[m
[32m+[m[32m    The <function>pg_event_trigger_dropped_objects</> function can be used[m
[32m+[m[32m    in an event trigger like this:[m
[32m+[m[32m<programlisting>[m
[32m+[m[32mCREATE FUNCTION test_event_trigger_for_drops()[m
[32m+[m[32m        RETURNS event_trigger LANGUAGE plpgsql AS $$[m
[32m+[m[32mDECLARE[m
[32m+[m[32m    obj record;[m
[32m+[m[32mBEGIN[m
[32m+[m[32m    FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()[m
[32m+[m[32m    LOOP[m
[32m+[m[32m        RAISE NOTICE '% dropped object: % %.% %',[m
[32m+[m[32m                     tg_tag,[m
[32m+[m[32m                     obj.object_type,[m
[32m+[m[32m                     obj.schema_name,[m
[32m+[m[32m                     obj.object_name,[m
[32m+[m[32m                     obj.subobject_name;[m
[32m+[m[32m    END LOOP;[m
[32m+[m[32mEND[m
[32m+[m[32m$$;[m
[32m+[m[32mCREATE EVENT TRIGGER test_event_trigger_for_drops[m
[32m+[m[32m   ON ddl_command_end[m
[32m+[m[32m   EXECUTE PROCEDURE test_event_trigger_for_drops();[m
[32m+[m[32m</programlisting>[m
[32m+[m[32m    </para>[m
[32m+[m
[32m+[m[32m     <para>[m
[32m+[m[32m       For more information about event triggers,[m
[32m+[m[32m       see <xref linkend="event-triggers">.[m
[32m+[m[32m    </para>[m
[32m+[m[32m  </sect1>[m
[32m+[m
 </chapter>[m
[1mdiff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c[m
[1mindex ddf1990..286137c 100644[m
[1m--- a/src/backend/catalog/dependency.c[m
[1m+++ b/src/backend/catalog/dependency.c[m
[36m@@ -191,6 +191,44 @@[m [mstatic bool stack_address_present_add_flags(const ObjectAddress *object,[m
 [m
 [m
 /*[m
[32m+[m[32m * Go through the objects given running the final actions on them, and execute[m
[32m+[m[32m * the actual deletion.[m
[32m+[m[32m */[m
[32m+[m[32mstatic void[m
[32m+[m[32mdeleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel,[m
[32m+[m					[32mint flags)[m
[32m+[m[32m{[m
[32m+[m	[32mint		i;[m
[32m+[m
[32m+[m	[32m/*[m
[32m+[m	[32m * Keep track of objects for event triggers, if necessary.[m
[32m+[m	[32m */[m
[32m+[m	[32mif (trackDroppedObjectsNeeded())[m
[32m+[m	[32m{[m
[32m+[m		[32mfor (i = 0; i < targetObjects->numrefs; i++)[m
[32m+[m		[32m{[m
[32m+[m			[32mObjectAddress *thisobj = targetObjects->refs + i;[m
[32m+[m
[32m+[m			[32mif ((!(flags & PERFORM_DELETION_INTERNAL)) &&[m
[32m+[m				[32mEventTriggerSupportsObjectType(getObjectClass(thisobj)))[m
[32m+[m			[32m{[m
[32m+[m				[32mEventTriggerSQLDropAddObject(thisobj);[m
[32m+[m			[32m}[m
[32m+[m		[32m}[m
[32m+[m	[32m}[m
[32m+[m
[32m+[m	[32m/*[m
[32m+[m	[32m * Delete all the objects in the proper order.[m
[32m+[m	[32m */[m
[32m+[m	[32mfor (i = 0; i < targetObjects->numrefs; i++)[m
[32m+[m	[32m{[m
[32m+[m		[32mObjectAddress *thisobj = targetObjects->refs + i;[m
[32m+[m
[32m+[m		[32mdeleteOneObject(thisobj, depRel, flags);[m
[32m+[m	[32m}[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32m/*[m
  * performDeletion: attempt to drop the specified object.  If CASCADE[m
  * behavior is specified, also drop any dependent objects (recursively).[m
  * If RESTRICT behavior is specified, error out if there are any dependent[m
[36m@@ -215,7 +253,6 @@[m [mperformDeletion(const ObjectAddress *object,[m
 {[m
 	Relation	depRel;[m
 	ObjectAddresses *targetObjects;[m
[31m-	int			i;[m
 [m
 	/*[m
 	 * We save some cycles by opening pg_depend just once and passing the[m
[36m@@ -250,15 +287,8 @@[m [mperformDeletion(const ObjectAddress *object,[m
 						   NOTICE,[m
 						   object);[m
 [m
[31m-	/*[m
[31m-	 * Delete all the objects in the proper order.[m
[31m-	 */[m
[31m-	for (i = 0; i < targetObjects->numrefs; i++)[m
[31m-	{[m
[31m-		ObjectAddress *thisobj = targetObjects->refs + i;[m
[31m-[m
[31m-		deleteOneObject(thisobj, &depRel, flags);[m
[31m-	}[m
[32m+[m	[32m/* do the deed */[m
[32m+[m	[32mdeleteObjectsInList(targetObjects, &depRel, flags);[m
 [m
 	/* And clean up */[m
 	free_object_addresses(targetObjects);[m
[36m@@ -332,15 +362,8 @@[m [mperformMultipleDeletions(const ObjectAddresses *objects,[m
 						   NOTICE,[m
 						   (objects->numrefs == 1 ? objects->refs : NULL));[m
 [m
[31m-	/*[m
[31m-	 * Delete all the objects in the proper order.[m
[31m-	 */[m
[31m-	for (i = 0; i < targetObjects->numrefs; i++)[m
[31m-	{[m
[31m-		ObjectAddress *thisobj = targetObjects->refs + i;[m
[31m-[m
[31m-		deleteOneObject(thisobj, &depRel, flags);[m
[31m-	}[m
[32m+[m	[32m/* do the deed */[m
[32m+[m	[32mdeleteObjectsInList(targetObjects, &depRel, flags);[m
 [m
 	/* And clean up */[m
 	free_object_addresses(targetObjects);[m
[36m@@ -356,6 +379,10 @@[m [mperformMultipleDeletions(const ObjectAddresses *objects,[m
  * This is currently used only to clean out the contents of a schema[m
  * (namespace): the passed object is a namespace.  We normally want this[m
  * to be done silently, so there's an option to suppress NOTICE messages.[m
[32m+[m[32m *[m
[32m+[m[32m * Note we don't fire object drop event triggers here; it would be wrong to do[m
[32m+[m[32m * so for the current only use of this function, but if more callers are added[m
[32m+[m[32m * this might need to be reconsidered.[m
  */[m
 void[m
 deleteWhatDependsOn(const ObjectAddress *object,[m
[1mdiff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c[m
[1mindex fbe8f49..fa00c1e 100644[m
[1m--- a/src/backend/commands/event_trigger.c[m
[1m+++ b/src/backend/commands/event_trigger.c[m
[36m@@ -19,12 +19,14 @@[m
 #include "catalog/indexing.h"[m
 #include "catalog/objectaccess.h"[m
 #include "catalog/pg_event_trigger.h"[m
[32m+[m[32m#include "catalog/pg_namespace.h"[m
 #include "catalog/pg_proc.h"[m
 #include "catalog/pg_trigger.h"[m
 #include "catalog/pg_type.h"[m
 #include "commands/dbcommands.h"[m
 #include "commands/event_trigger.h"[m
 #include "commands/trigger.h"[m
[32m+[m[32m#include "funcapi.h"[m
 #include "parser/parse_func.h"[m
 #include "pgstat.h"[m
 #include "miscadmin.h"[m
[36m@@ -39,6 +41,16 @@[m
 #include "utils/syscache.h"[m
 #include "tcop/utility.h"[m
 [m
[32m+[m
[32m+[m[32mtypedef struct EventTriggerQueryState[m
[32m+[m[32m{[m
[32m+[m	[32mslist_head	SQLDropList;[m
[32m+[m	[32mMemoryContext	cxt;[m
[32m+[m	[32mstruct EventTriggerQueryState *previous;[m
[32m+[m[32m} EventTriggerQueryState;[m
[32m+[m
[32m+[m[32mEventTriggerQueryState	*currentEventTriggerState = NULL;[m
[32m+[m
 typedef struct[m
 {[m
 	const char	   *obtypename;[m
[36m@@ -89,6 +101,17 @@[m [mstatic event_trigger_support_data event_trigger_support[] = {[m
 	{ NULL, false }[m
 };[m
 [m
[32m+[m[32m/* Support for dropped objects */[m
[32m+[m[32mtypedef struct SQLDropObject[m
[32m+[m[32m{[m
[32m+[m	[32mObjectAddress	address;[m
[32m+[m	[32mchar		   *schemaname;[m
[32m+[m	[32mchar		   *objname;[m
[32m+[m	[32mchar		   *objidentity;[m
[32m+[m	[32mchar		   *objecttype;[m
[32m+[m	[32mslist_node		next;[m
[32m+[m[32m} SQLDropObject;[m
[32m+[m
 static void AlterEventTriggerOwner_internal(Relation rel,[m
 											HeapTuple tup,[m
 											Oid newOwnerId);[m
[36m@@ -127,7 +150,8 @@[m [mCreateEventTrigger(CreateEventTrigStmt *stmt)[m
 [m
 	/* Validate event name. */[m
 	if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&[m
[31m-		strcmp(stmt->eventname, "ddl_command_end") != 0)[m
[32m+[m		[32mstrcmp(stmt->eventname, "ddl_command_end") != 0 &&[m
[32m+[m		[32mstrcmp(stmt->eventname, "sql_drop") != 0)[m
 		ereport(ERROR,[m
 			(errcode(ERRCODE_SYNTAX_ERROR),[m
 			 errmsg("unrecognized event name \"%s\"",[m
[36m@@ -151,7 +175,10 @@[m [mCreateEventTrigger(CreateEventTrigStmt *stmt)[m
 	}[m
 [m
 	/* Validate tag list, if any. */[m
[31m-	if (strcmp(stmt->eventname, "ddl_command_start") == 0 && tags != NULL)[m
[32m+[m	[32mif ((strcmp(stmt->eventname, "ddl_command_start") == 0 ||[m
[32m+[m		[32m strcmp(stmt->eventname, "ddl_command_end") == 0 ||[m
[32m+[m		[32m strcmp(stmt->eventname, "sql_drop") == 0)[m
[32m+[m		[32m&& tags != NULL)[m
 		validate_ddl_tags("tag", tags);[m
 [m
 	/*[m
[36m@@ -220,7 +247,8 @@[m [mcheck_ddl_tag(const char *tag)[m
 		pg_strcasecmp(tag, "SELECT INTO") == 0 ||[m
 		pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||[m
 		pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||[m
[31m-		pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0)[m
[32m+[m		[32mpg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||[m
[32m+[m		[32mpg_strcasecmp(tag, "DROP OWNED") == 0)[m
 		return EVENT_TRIGGER_COMMAND_TAG_OK;[m
 [m
 	/*[m
[36m@@ -749,6 +777,91 @@[m [mEventTriggerDDLCommandEnd(Node *parsetree)[m
 }[m
 [m
 /*[m
[32m+[m[32m * Fire sql_drop triggers.[m
[32m+[m[32m */[m
[32m+[m[32mvoid[m
[32m+[m[32mEventTriggerSQLDrop(Node *parsetree)[m
[32m+[m[32m{[m
[32m+[m	[32mList	   *cachelist;[m
[32m+[m	[32mList	   *runlist = NIL;[m
[32m+[m	[32mListCell   *lc;[m
[32m+[m	[32mconst char *tag;[m
[32m+[m	[32mEventTriggerData	trigdata;[m
[32m+[m
[32m+[m	[32m/*[m
[32m+[m	[32m * See EventTriggerDDLCommandStart for a discussion about why event[m
[32m+[m	[32m * triggers are disabled in single user mode.[m
[32m+[m	[32m */[m
[32m+[m	[32mif (!IsUnderPostmaster)[m
[32m+[m		[32mreturn;[m
[32m+[m
[32m+[m	[32m/*[m
[32m+[m	[32m * See EventTriggerDDLCommandStart for a discussion about why this check is[m
[32m+[m	[32m * important.[m
[32m+[m	[32m *[m
[32m+[m	[32m */[m
[32m+[m[32m#ifdef USE_ASSERT_CHECKING[m
[32m+[m	[32mif (assert_enabled)[m
[32m+[m	[32m{[m
[32m+[m		[32mconst char *dbgtag;[m
[32m+[m
[32m+[m		[32mdbgtag = CreateCommandTag(parsetree);[m
[32m+[m		[32mif (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)[m
[32m+[m			[32melog(ERROR, "unexpected command tag \"%s\"", dbgtag);[m
[32m+[m	[32m}[m
[32m+[m[32m#endif[m
[32m+[m
[32m+[m	[32m/* Use current state to determine whether this trigger fires at all */[m
[32m+[m	[32mif (!currentEventTriggerState ||[m
[32m+[m		[32mslist_is_empty(&currentEventTriggerState->SQLDropList))[m
[32m+[m		[32mreturn;[m
[32m+[m
[32m+[m	[32m/* Use cache to find triggers for this event; fast exit if none. */[m
[32m+[m	[32mcachelist = EventCacheLookup(EVT_SQLDrop);[m
[32m+[m	[32mif (cachelist == NULL)[m
[32m+[m		[32mreturn;[m
[32m+[m
[32m+[m	[32m/* Get the command tag. */[m
[32m+[m	[32mtag = CreateCommandTag(parsetree);[m
[32m+[m
[32m+[m	[32m/*[m
[32m+[m	[32m * Filter list of event triggers by command tag, and copy them into[m
[32m+[m	[32m * our memory context.  Once we start running the command trigers, or[m
[32m+[m	[32m * indeed once we do anything at all that touches the catalogs, an[m
[32m+[m	[32m * invalidation might leave cachelist pointing at garbage, so we must[m
[32m+[m	[32m * do this before we can do much else.[m
[32m+[m	[32m */[m
[32m+[m	[32mforeach (lc, cachelist)[m
[32m+[m	[32m{[m
[32m+[m		[32mEventTriggerCacheItem  *item = lfirst(lc);[m
[32m+[m
[32m+[m		[32mif (filter_event_trigger(&tag, item))[m
[32m+[m		[32m{[m
[32m+[m			[32m/* We must plan to fire this trigger. */[m
[32m+[m			[32mrunlist = lappend_oid(runlist, item->fnoid);[m
[32m+[m		[32m}[m
[32m+[m	[32m}[m
[32m+[m
[32m+[m	[32m/* Construct event trigger data. */[m
[32m+[m	[32mtrigdata.type = T_EventTriggerData;[m
[32m+[m	[32mtrigdata.event = "sql_drop";[m
[32m+[m	[32mtrigdata.parsetree = parsetree;[m
[32m+[m	[32mtrigdata.tag = tag;[m
[32m+[m
[32m+[m	[32m/*[m
[32m+[m	[32m * Make sure anything the main command did will be visible to the[m
[32m+[m	[32m * event triggers.[m
[32m+[m	[32m */[m
[32m+[m	[32mCommandCounterIncrement();[m
[32m+[m
[32m+[m	[32m/* Run the triggers. */[m
[32m+[m	[32mEventTriggerInvoke(runlist, &trigdata);[m
[32m+[m
[32m+[m	[32m/* Cleanup. */[m
[32m+[m	[32mlist_free(runlist);[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32m/*[m
  * Invoke each event trigger in a list of event triggers.[m
  */[m
 static void[m
[36m@@ -832,3 +945,279 @@[m [mEventTriggerSupportsObjectType(ObjectType obtype)[m
 	}[m
 	return true;[m
 }[m
[32m+[m
[32m+[m[32m/*[m
[32m+[m[32m * Prepare event trigger state for a new complete query to run, if necessary;[m
[32m+[m[32m * returns whether this was done.  If it was, EventTriggerEndCompleteQuery must[m
[32m+[m[32m * be called when the query is done, regardless of whether it succeeds or fails[m
[32m+[m[32m * -- so use of a PG_TRY block is mandatory.[m
[32m+[m[32m */[m
[32m+[m[32mbool[m
[32m+[m[32mEventTriggerBeginCompleteQuery(void)[m
[32m+[m[32m{[m
[32m+[m	[32mEventTriggerQueryState *state;[m
[32m+[m	[32mMemoryContext	cxt;[m
[32m+[m
[32m+[m	[32m/*[m
[32m+[m	[32m * Currently, sql_drop events are the only reason to have event trigger[m
[32m+[m	[32m * state at all; so if there are none, don't install one.[m
[32m+[m	[32m */[m
[32m+[m	[32mif (!trackDroppedObjectsNeeded())[m
[32m+[m		[32mreturn false;[m
[32m+[m
[32m+[m	[32mcxt = AllocSetContextCreate(TopMemoryContext,[m
[32m+[m								[32m"event trigger state",[m
[32m+[m								[32mALLOCSET_DEFAULT_MINSIZE,[m
[32m+[m								[32mALLOCSET_DEFAULT_INITSIZE,[m
[32m+[m								[32mALLOCSET_DEFAULT_MAXSIZE);[m
[32m+[m	[32mstate = MemoryContextAlloc(cxt, sizeof(EventTriggerQueryState));[m
[32m+[m	[32mstate->cxt = cxt;[m
[32m+[m	[32mslist_init(&(state->SQLDropList));[m
[32m+[m
[32m+[m	[32mstate->previous = currentEventTriggerState;[m
[32m+[m	[32mcurrentEventTriggerState = state;[m
[32m+[m
[32m+[m	[32mreturn true;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32m/*[m
[32m+[m[32m * Query completed (or errored out) -- clean up local state, return to previous[m
[32m+[m[32m * one.[m
[32m+[m[32m *[m
[32m+[m[32m * Note: it's an error to call this routine if EventTriggerBeginCompleteQuery[m
[32m+[m[32m * returned false previously.[m
[32m+[m[32m *[m
[32m+[m[32m * Note: this might be called in the PG_CATCH block of a failing transaction,[m
[32m+[m[32m * so be wary of running anything unnecessary.  (In particular, it's probably[m
[32m+[m[32m * unwise to try to allocate memory.)[m
[32m+[m[32m */[m
[32m+[m[32mvoid[m
[32m+[m[32mEventTriggerEndCompleteQuery(void)[m
[32m+[m[32m{[m
[32m+[m	[32mEventTriggerQueryState *prevstate;[m
[32m+[m
[32m+[m	[32mprevstate = currentEventTriggerState->previous;[m
[32m+[m
[32m+[m	[32m/* this avoids the need for retail pfree of SQLDropList items: */[m
[32m+[m	[32mMemoryContextDelete(currentEventTriggerState->cxt);[m
[32m+[m
[32m+[m	[32mcurrentEventTriggerState = prevstate;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32m/*[m
[32m+[m[32m * Do we need to keep close track of objects being dropped?[m
[32m+[m[32m *[m
[32m+[m[32m * This is useful because there is a cost to running with them enabled.[m
[32m+[m[32m */[m
[32m+[m[32mbool[m
[32m+[m[32mtrackDroppedObjectsNeeded(void)[m
[32m+[m[32m{[m
[32m+[m	[32m/* true if any sql_drop event trigger exists */[m
[32m+[m	[32mreturn list_length(EventCacheLookup(EVT_SQLDrop)) > 0;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32m/*[m
[32m+[m[32m * Support for dropped objects information on event trigger functions.[m
[32m+[m[32m *[m
[32m+[m[32m * We keep the list of objects dropped by the current command in current[m
[32m+[m[32m * state's SQLDropList (comprising SQLDropObject items).  Each time a new[m
[32m+[m[32m * command is to start, a clean EventTriggerQueryState is created; commands[m
[32m+[m[32m * that drop objects do the dependency.c dance to drop objects, which[m
[32m+[m[32m * populates the current state's SQLDropList; when the event triggers are[m
[32m+[m[32m * invoked they can consume the list via pg_event_trigger_dropped_objects().[m
[32m+[m[32m * When the command finishes, the EventTriggerQueryState is cleared, and[m
[32m+[m[32m * the one from the previous command is restored (when no command is in[m
[32m+[m[32m * execution, the current state is NULL).[m
[32m+[m[32m *[m
[32m+[m[32m * All this lets us support the case that an event trigger function drops[m
[32m+[m[32m * objects "reentrantly".[m
[32m+[m[32m */[m
[32m+[m
[32m+[m[32m/*[m
[32m+[m[32m * Register one object as being dropped by the current command.[m
[32m+[m[32m */[m
[32m+[m[32mvoid[m
[32m+[m[32mEventTriggerSQLDropAddObject(ObjectAddress *object)[m
[32m+[m[32m{[m
[32m+[m	[32mSQLDropObject  *obj;[m
[32m+[m	[32mMemoryContext	oldcxt;[m
[32m+[m
[32m+[m	[32mif (!currentEventTriggerState)[m
[32m+[m		[32mreturn;[m
[32m+[m
[32m+[m	[32mAssert(EventTriggerSupportsObjectType(getObjectClass(object)));[m
[32m+[m
[32m+[m	[32m/* don't report temp schemas */[m
[32m+[m	[32mif (object->classId == NamespaceRelationId &&[m
[32m+[m		[32misAnyTempNamespace(object->objectId))[m
[32m+[m		[32mreturn;[m
[32m+[m
[32m+[m	[32moldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);[m
[32m+[m
[32m+[m	[32mobj = palloc0(sizeof(SQLDropObject));[m
[32m+[m	[32mobj->address = *object;[m
[32m+[m
[32m+[m	[32m/*[m
[32m+[m	[32m * Obtain schema names from the object's catalog tuple, if one exists;[m
[32m+[m	[32m * this lets us skip objects in temp schemas.  We trust that ObjectProperty[m
[32m+[m	[32m * contains all object classes that can be schema-qualified.[m
[32m+[m	[32m */[m
[32m+[m	[32mif (is_objectclass_supported(object->classId))[m
[32m+[m	[32m{[m
[32m+[m		[32mRelation	catalog;[m
[32m+[m		[32mHeapTuple	tuple;[m
[32m+[m
[32m+[m		[32mcatalog = heap_open(obj->address.classId, AccessShareLock);[m
[32m+[m		[32mtuple = get_catalog_object_by_oid(catalog, obj->address.objectId);[m
[32m+[m
[32m+[m		[32mif (tuple)[m
[32m+[m		[32m{[m
[32m+[m			[32mAttrNumber	attnum;[m
[32m+[m			[32mDatum		datum;[m
[32m+[m			[32mbool		isnull;[m
[32m+[m
[32m+[m			[32mattnum = get_object_attnum_namespace(obj->address.classId);[m
[32m+[m			[32mif (attnum != InvalidAttrNumber)[m
[32m+[m			[32m{[m
[32m+[m				[32mdatum = heap_getattr(tuple, attnum,[m
[32m+[m									[32m RelationGetDescr(catalog), &isnull);[m
[32m+[m				[32mif (!isnull)[m
[32m+[m				[32m{[m
[32m+[m					[32mOid		namespaceId;[m
[32m+[m
[32m+[m					[32mnamespaceId = DatumGetObjectId(datum);[m
[32m+[m					[32m/* Don't report objects in temp namespaces */[m
[32m+[m					[32mif (isAnyTempNamespace(namespaceId))[m
[32m+[m					[32m{[m
[32m+[m						[32mpfree(obj);[m
[32m+[m						[32mheap_close(catalog, AccessShareLock);[m
[32m+[m						[32mMemoryContextSwitchTo(oldcxt);[m
[32m+[m						[32mreturn;[m
[32m+[m					[32m}[m
[32m+[m
[32m+[m					[32mobj->schemaname = get_namespace_name(namespaceId);[m
[32m+[m				[32m}[m
[32m+[m			[32m}[m
[32m+[m
[32m+[m			[32mattnum = get_object_attnum_name(obj->address.classId);[m
[32m+[m			[32mif (attnum != InvalidAttrNumber)[m
[32m+[m			[32m{[m
[32m+[m				[32mdatum = heap_getattr(tuple, attnum,[m
[32m+[m									[32m RelationGetDescr(catalog), &isnull);[m
[32m+[m				[32mif (!isnull)[m
[32m+[m					[32mobj->objname = pstrdup(NameStr(*DatumGetName(datum)));[m
[32m+[m			[32m}[m
[32m+[m		[32m}[m
[32m+[m
[32m+[m		[32mheap_close(catalog, AccessShareLock);[m
[32m+[m	[32m}[m
[32m+[m
[32m+[m	[32m/* object identity */[m
[32m+[m	[32mobj->objidentity = getObjectIdentity(&obj->address);[m
[32m+[m
[32m+[m	[32m/* and object type, too */[m
[32m+[m	[32mobj->objecttype = getObjectTypeDescription(&obj->address);[m
[32m+[m
[32m+[m	[32mslist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);[m
[32m+[m
[32m+[m	[32mMemoryContextSwitchTo(oldcxt);[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32m/*[m
[32m+[m[32m * pg_event_trigger_dropped_objects[m
[32m+[m[32m *[m
[32m+[m[32m * Make the list of dropped objects available to the user function run by the[m
[32m+[m[32m * Event Trigger.[m
[32m+[m[32m */[m
[32m+[m[32mDatum[m
[32m+[m[32mpg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)[m
[32m+[m[32m{[m
[32m+[m	[32mReturnSetInfo	   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;[m
[32m+[m	[32mTupleDesc			tupdesc;[m
[32m+[m	[32mTuplestorestate	   *tupstore;[m
[32m+[m	[32mMemoryContext		per_query_ctx;[m
[32m+[m	[32mMemoryContext		oldcontext;[m
[32m+[m	[32mslist_iter			iter;[m
[32m+[m
[32m+[m	[32m/* XXX can this actually happen? */[m
[32m+[m	[32mif (!currentEventTriggerState)[m
[32m+[m		[32mereport(ERROR,[m
[32m+[m				[32m(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),[m
[32m+[m				[32m errmsg("%s can only be called when there's a command in execution",[m
[32m+[m						[32m"pg_event_trigger_dropped_objects()")));[m
[32m+[m
[32m+[m	[32m/* check to see if caller supports us returning a tuplestore */[m
[32m+[m	[32mif (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))[m
[32m+[m		[32mereport(ERROR,[m
[32m+[m				[32m(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),[m
[32m+[m				[32m errmsg("set-valued function called in context that cannot accept a set")));[m
[32m+[m	[32mif (!(rsinfo->allowedModes & SFRM_Materialize))[m
[32m+[m		[32mereport(ERROR,[m
[32m+[m				[32m(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),[m
[32m+[m				[32m errmsg("materialize mode required, but it is not allowed in this context")));[m
[32m+[m
[32m+[m	[32m/* Build a tuple descriptor for our result type */[m
[32m+[m	[32mif (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)[m
[32m+[m		[32melog(ERROR, "return type must be a row type");[m
[32m+[m
[32m+[m	[32m/* Build tuplestore to hold the result rows */[m
[32m+[m	[32mper_query_ctx = rsinfo->econtext->ecxt_per_query_memory;[m
[32m+[m	[32moldcontext = MemoryContextSwitchTo(per_query_ctx);[m
[32m+[m
[32m+[m	[32mtupstore = tuplestore_begin_heap(true, false, work_mem);[m
[32m+[m	[32mrsinfo->returnMode = SFRM_Materialize;[m
[32m+[m	[32mrsinfo->setResult = tupstore;[m
[32m+[m	[32mrsinfo->setDesc = tupdesc;[m
[32m+[m
[32m+[m	[32mMemoryContextSwitchTo(oldcontext);[m
[32m+[m
[32m+[m	[32mslist_foreach(iter, &(currentEventTriggerState->SQLDropList))[m
[32m+[m	[32m{[m
[32m+[m		[32mSQLDropObject *obj;[m
[32m+[m		[32mint			i = 0;[m
[32m+[m		[32mDatum		values[7];[m
[32m+[m		[32mbool		nulls[7];[m
[32m+[m
[32m+[m		[32mobj = slist_container(SQLDropObject, next, iter.cur);[m
[32m+[m
[32m+[m		[32mMemSet(values, 0, sizeof(values));[m
[32m+[m		[32mMemSet(nulls, 0, sizeof(nulls));[m
[32m+[m
[32m+[m		[32m/* classid */[m
[32m+[m		[32mvalues[i++] = ObjectIdGetDatum(obj->address.classId);[m
[32m+[m
[32m+[m		[32m/* objid */[m
[32m+[m		[32mvalues[i++] = ObjectIdGetDatum(obj->address.objectId);[m
[32m+[m
[32m+[m		[32m/* objsubid */[m
[32m+[m		[32mvalues[i++] = Int32GetDatum(obj->address.objectSubId);[m
[32m+[m
[32m+[m		[32m/* object_type */[m
[32m+[m		[32mvalues[i++] = CStringGetTextDatum(obj->objecttype);[m
[32m+[m
[32m+[m		[32m/* schema_name */[m
[32m+[m		[32mif (obj->schemaname)[m
[32m+[m			[32mvalues[i++] = CStringGetTextDatum(obj->schemaname);[m
[32m+[m		[32melse[m
[32m+[m			[32mnulls[i++] = true;[m
[32m+[m
[32m+[m		[32m/* object_name */[m
[32m+[m		[32mif (obj->objname)[m
[32m+[m			[32mvalues[i++] = CStringGetTextDatum(obj->objname);[m
[32m+[m		[32melse[m
[32m+[m			[32mnulls[i++] = true;[m
[32m+[m
[32m+[m		[32m/* object_identity */[m
[32m+[m		[32mif (obj->objidentity)[m
[32m+[m			[32mvalues[i++] = CStringGetTextDatum(obj->objidentity);[m
[32m+[m		[32melse[m
[32m+[m			[32mnulls[i++] = true;[m
[32m+[m
[32m+[m		[32mtuplestore_putvalues(tupstore, tupdesc, values, nulls);[m
[32m+[m	[32m}[m
[32m+[m
[32m+[m	[32m/* clean up and return the tuplestore */[m
[32m+[m	[32mtuplestore_donestoring(tupstore);[m
[32m+[m
[32m+[m	[32mreturn (Datum) 0;[m
[32m+[m[32m}[m
[1mdiff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y[m
[1mindex 0d82141..ed8502c 100644[m
[1m--- a/src/backend/parser/gram.y[m
[1m+++ b/src/backend/parser/gram.y[m
[36m@@ -4478,7 +4478,6 @@[m [mDropTrigStmt:[m
  *[m
  *		QUERIES :[m
  *				CREATE EVENT TRIGGER ...[m
[31m- *				DROP EVENT TRIGGER ...[m
  *				ALTER EVENT TRIGGER ...[m
  *[m
  *****************************************************************************/[m
[1mdiff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c[m
[1mindex a1c03f1..78765c2 100644[m
[1m--- a/src/backend/tcop/utility.c[m
[1m+++ b/src/backend/tcop/utility.c[m
[36m@@ -351,6 +351,7 @@[m [mProcessUtility(Node *parsetree,[m
 		fncall; \[m
         if (isCompleteQuery) \[m
         { \[m
[32m+[m			[32mEventTriggerSQLDrop(parsetree); \[m
 		    EventTriggerDDLCommandEnd(parsetree); \[m
 		} \[m
 	} while (0)[m
[36m@@ -366,10 +367,48 @@[m [mProcessUtility(Node *parsetree,[m
 		fncall; \[m
 		if (_supported) \[m
 		{ \[m
[32m+[m			[32mEventTriggerSQLDrop(parsetree); \[m
 			EventTriggerDDLCommandEnd(parsetree); \[m
 		} \[m
 	} while (0)[m
 [m
[32m+[m[32m/*[m
[32m+[m[32m * UTILITY_BEGIN_QUERY and UTILITY_END_QUERY are a pair of macros to enclose[m
[32m+[m[32m * execution of a single DDL command, to ensure the event trigger environment[m
[32m+[m[32m * is appropriately set up before starting, and tore down after completion or[m
[32m+[m[32m * error.[m
[32m+[m[32m */[m
[32m+[m[32m#define UTILITY_BEGIN_QUERY(isComplete) \[m
[32m+[m	[32mdo { \[m
[32m+[m		[32mbool		_needCleanup = false; \[m
[32m+[m		[32m\[m
[32m+[m		[32mif (isComplete) \[m
[32m+[m		[32m{ \[m
[32m+[m			[32m_needCleanup = EventTriggerBeginCompleteQuery(); \[m
[32m+[m		[32m} \[m
[32m+[m		[32m\[m
[32m+[m		[32mPG_TRY(); \[m
[32m+[m		[32m{ \[m
[32m+[m			[32m/* avoid empty statement when followed by a semicolon */ \[m
[32m+[m			[32m(void) 0[m
[32m+[m
[32m+[m[32m#define UTILITY_END_QUERY() \[m
[32m+[m		[32m} \[m
[32m+[m		[32mPG_CATCH(); \[m
[32m+[m		[32m{ \[m
[32m+[m			[32mif (_needCleanup) \[m
[32m+[m			[32m{ \[m
[32m+[m				[32mEventTriggerEndCompleteQuery(); \[m
[32m+[m			[32m} \[m
[32m+[m			[32mPG_RE_THROW(); \[m
[32m+[m		[32m} \[m
[32m+[m		[32mPG_END_TRY(); \[m
[32m+[m		[32mif (_needCleanup) \[m
[32m+[m		[32m{ \[m
[32m+[m			[32mEventTriggerEndCompleteQuery(); \[m
[32m+[m		[32m} \[m
[32m+[m	[32m} while (0)[m
[32m+[m
 void[m
 standard_ProcessUtility(Node *parsetree,[m
 						const char *queryString,[m
[36m@@ -386,6 +425,8 @@[m [mstandard_ProcessUtility(Node *parsetree,[m
 	if (completionTag)[m
 		completionTag[0] = '\0';[m
 [m
[32m+[m	[32mUTILITY_BEGIN_QUERY(isCompleteQuery);[m
[32m+[m
 	switch (nodeTag(parsetree))[m
 	{[m
 			/*[m
[36m@@ -615,7 +656,10 @@[m [mstandard_ProcessUtility(Node *parsetree,[m
 				}[m
 [m
 				if (isCompleteQuery)[m
[32m+[m				[32m{[m
[32m+[m					[32mEventTriggerSQLDrop(parsetree);[m
 					EventTriggerDDLCommandEnd(parsetree);[m
[32m+[m				[32m}[m
 			}[m
 			break;[m
 [m
[36m@@ -726,7 +770,10 @@[m [mstandard_ProcessUtility(Node *parsetree,[m
 [m
 				if (isCompleteQuery[m
 					&& EventTriggerSupportsObjectType(stmt->removeType))[m
[32m+[m				[32m{[m
[32m+[m					[32mEventTriggerSQLDrop(parsetree);[m
 					EventTriggerDDLCommandEnd(parsetree);[m
[32m+[m				[32m}[m
 [m
 				break;[m
 			}[m
[36m@@ -856,6 +903,12 @@[m [mstandard_ProcessUtility(Node *parsetree,[m
 					ereport(NOTICE,[m
 						  (errmsg("relation \"%s\" does not exist, skipping",[m
 								  atstmt->relation->relname)));[m
[32m+[m
[32m+[m				[32mif (isCompleteQuery)[m
[32m+[m				[32m{[m
[32m+[m					[32mEventTriggerSQLDrop(parsetree);[m
[32m+[m					[32mEventTriggerDDLCommandEnd(parsetree);[m
[32m+[m				[32m}[m
 			}[m
 			break;[m
 [m
[36m@@ -1248,8 +1301,9 @@[m [mstandard_ProcessUtility(Node *parsetree,[m
 			break;[m
 [m
 		case T_DropOwnedStmt:[m
[31m-			/* no event triggers for global objects */[m
[31m-			DropOwnedObjects((DropOwnedStmt *) parsetree);[m
[32m+[m			[32mInvokeDDLCommandEventTriggers([m
[32m+[m				[32mparsetree,[m
[32m+[m				[32mDropOwnedObjects((DropOwnedStmt *) parsetree));[m
 			break;[m
 [m
 		case T_ReassignOwnedStmt:[m
[36m@@ -1372,6 +1426,8 @@[m [mstandard_ProcessUtility(Node *parsetree,[m
 				 (int) nodeTag(parsetree));[m
 			break;[m
 	}[m
[32m+[m
[32m+[m	[32mUTILITY_END_QUERY();[m
 }[m
 [m
 /*[m
[1mdiff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c[m
[1mindex 94599aa..9e1706b 100644[m
[1m--- a/src/backend/utils/adt/regproc.c[m
[1m+++ b/src/backend/utils/adt/regproc.c[m
[36m@@ -345,7 +345,7 @@[m [mformat_procedure_internal(Oid procedure_oid, bool force_qualify)[m
 [m
 		/*[m
 		 * Would this proc be found (given the right args) by regprocedurein?[m
[31m-		 * If not, we need to qualify it.[m
[32m+[m		[32m * If not, we need to qualify it -- unless caller wants it bare.[m
 		 */[m
 		if (!force_qualify && FunctionIsVisible(procedure_oid))[m
 			nspname = NULL;[m
[1mdiff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c[m
[1mindex 34c6128..bbd3ae3 100644[m
[1m--- a/src/backend/utils/cache/evtcache.c[m
[1m+++ b/src/backend/utils/cache/evtcache.c[m
[36m@@ -169,6 +169,8 @@[m [mBuildEventTriggerCache(void)[m
 			event = EVT_DDLCommandStart;[m
 		else if (strcmp(evtevent, "ddl_command_end") == 0)[m
 			event = EVT_DDLCommandEnd;[m
[32m+[m		[32melse if (strcmp(evtevent, "sql_drop") == 0)[m
[32m+[m			[32mevent = EVT_SQLDrop;[m
 		else[m
 			continue;[m
 [m
[1mdiff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h[m
[1mindex 4aee002..86bfed4 100644[m
[1m--- a/src/include/catalog/pg_proc.h[m
[1m+++ b/src/include/catalog/pg_proc.h[m
[36m@@ -4693,6 +4693,9 @@[m [mDATA(insert OID = 3473 (  spg_range_quad_leaf_consistent	PGNSP PGUID 12 1 0 0 0[m
 DESCR("SP-GiST support for quad tree over range");[m
 [m
 [m
[32m+[m[32m/* event triggers */[m
[32m+[m[32mDATA(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,26,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_ ));[m
[32m+[m[32mDESCR("list objects dropped by the current command");[m
 /*[m
  * Symbolic values for provolatile column: these indicate whether the result[m
  * of a function is dependent *only* on the values of its explicit arguments,[m
[1mdiff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h[m
[1mindex 74c150b..b11fe6a 100644[m
[1m--- a/src/include/commands/event_trigger.h[m
[1m+++ b/src/include/commands/event_trigger.h[m
[36m@@ -13,7 +13,9 @@[m
 #ifndef EVENT_TRIGGER_H[m
 #define EVENT_TRIGGER_H[m
 [m
[32m+[m[32m#include "catalog/objectaddress.h"[m
 #include "catalog/pg_event_trigger.h"[m
[32m+[m[32m#include "lib/ilist.h"[m
 #include "nodes/parsenodes.h"[m
 [m
 typedef struct EventTriggerData[m
[36m@@ -42,5 +44,11 @@[m [mextern void AlterEventTriggerOwner_oid(Oid, Oid newOwnerId);[m
 extern bool EventTriggerSupportsObjectType(ObjectType obtype);[m
 extern void EventTriggerDDLCommandStart(Node *parsetree);[m
 extern void EventTriggerDDLCommandEnd(Node *parsetree);[m
[32m+[m[32mextern void EventTriggerSQLDrop(Node *parsetree);[m
[32m+[m
[32m+[m[32mextern bool EventTriggerBeginCompleteQuery(void);[m
[32m+[m[32mextern void EventTriggerEndCompleteQuery(void);[m
[32m+[m[32mextern bool trackDroppedObjectsNeeded(void);[m
[32m+[m[32mextern void EventTriggerSQLDropAddObject(ObjectAddress *object);[m
 [m
 #endif   /* EVENT_TRIGGER_H */[m
[1mdiff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h[m
[1mindex cd8ac94..e718765 100644[m
[1m--- a/src/include/utils/builtins.h[m
[1m+++ b/src/include/utils/builtins.h[m
[36m@@ -1151,6 +1151,9 @@[m [mextern Datum pg_identify_object(PG_FUNCTION_ARGS);[m
 /* commands/constraint.c */[m
 extern Datum unique_key_recheck(PG_FUNCTION_ARGS);[m
 [m
[32m+[m[32m/* commands/event_trigger.c */[m
[32m+[m[32mextern Datum pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS);[m
[32m+[m
 /* commands/extension.c */[m
 extern Datum pg_available_extensions(PG_FUNCTION_ARGS);[m
 extern Datum pg_available_extension_versions(PG_FUNCTION_ARGS);[m
[1mdiff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h[m
[1mindex c230995..945e5b5 100644[m
[1m--- a/src/include/utils/evtcache.h[m
[1m+++ b/src/include/utils/evtcache.h[m
[36m@@ -19,7 +19,8 @@[m
 typedef enum[m
 {[m
 	EVT_DDLCommandStart,[m
[31m-	EVT_DDLCommandEnd[m
[32m+[m	[32mEVT_DDLCommandEnd,[m
[32m+[m	[32mEVT_SQLDrop[m
 } EventTriggerEvent;[m
 [m
 typedef struct[m
[1mdiff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out[m
[1mindex bf020de..57c5ecf 100644[m
[1m--- a/src/test/regress/expected/event_trigger.out[m
[1m+++ b/src/test/regress/expected/event_trigger.out[m
[36m@@ -93,11 +93,123 @@[m [mERROR:  event trigger "regress_event_trigger" does not exist[m
 drop role regression_bob;[m
 ERROR:  role "regression_bob" cannot be dropped because some objects depend on it[m
 DETAIL:  owner of event trigger regress_event_trigger3[m
[32m+[m[32m-- cleanup before next test[m
 -- these are all OK; the second one should emit a NOTICE[m
 drop event trigger if exists regress_event_trigger2;[m
 drop event trigger if exists regress_event_trigger2;[m
 NOTICE:  event trigger "regress_event_trigger2" does not exist, skipping[m
 drop event trigger regress_event_trigger3;[m
 drop event trigger regress_event_trigger_end;[m
[31m-drop function test_event_trigger();[m
[31m-drop role regression_bob;[m
[32m+[m[32m-- test support for dropped objects[m
[32m+[m[32mCREATE SCHEMA schema_one authorization regression_bob;[m
[32m+[m[32mCREATE SCHEMA schema_two authorization regression_bob;[m
[32m+[m[32mCREATE SCHEMA audit_tbls authorization regression_bob;[m
[32m+[m[32mSET SESSION AUTHORIZATION regression_bob;[m
[32m+[m[32mCREATE TABLE schema_one.table_one(a int);[m
[32m+[m[32mCREATE TABLE schema_one."table two"(a int);[m
[32m+[m[32mCREATE TABLE schema_one.table_three(a int);[m
[32m+[m[32mCREATE TABLE audit_tbls.schema_one_table_two(the_value text);[m
[32m+[m[32mCREATE TABLE schema_two.table_two(a int);[m
[32m+[m[32mCREATE TABLE schema_two.table_three(a int, b text);[m
[32m+[m[32mCREATE TABLE audit_tbls.schema_two_table_three(the_value text);[m
[32m+[m[32mCREATE OR REPLACE FUNCTION schema_two.add(int, int) RETURNS int LANGUAGE plpgsql[m
[32m+[m[32m  CALLED ON NULL INPUT[m
[32m+[m[32m  AS $$ BEGIN RETURN coalesce($1,0) + coalesce($2,0); END; $$;[m
[32m+[m[32mCREATE AGGREGATE schema_two.newton[m
[32m+[m[32m  (BASETYPE = int, SFUNC = schema_two.add, STYPE = int);[m
[32m+[m[32mRESET SESSION AUTHORIZATION;[m
[32m+[m[32mCREATE TABLE dropped_objects ([m
[32m+[m	[32mtype text,[m
[32m+[m	[32mschema text,[m
[32m+[m	[32mobject text);[m
[32m+[m[32mCREATE OR REPLACE FUNCTION test_evtrig_dropped_objects() RETURNS event_trigger[m
[32m+[m[32mLANGUAGE plpgsql AS $$[m
[32m+[m[32mDECLARE[m
[32m+[m[32m    obj record;[m
[32m+[m[32mBEGIN[m
[32m+[m[32m    FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()[m
[32m+[m[32m    LOOP[m
[32m+[m[32m        IF obj.object_type = 'table' THEN[m
[32m+[m[32m                EXECUTE format('DROP TABLE IF EXISTS audit_tbls.%I',[m
[32m+[m					[32mformat('%s_%s', obj.schema_name, obj.object_name));[m
[32m+[m[32m        END IF;[m
[32m+[m
[32m+[m	[32mINSERT INTO dropped_objects[m
[32m+[m		[32m(type, schema, object) VALUES[m
[32m+[m		[32m(obj.object_type, obj.schema_name, obj.object_identity);[m
[32m+[m[32m    END LOOP;[m
[32m+[m[32mEND[m
[32m+[m[32m$$;[m
[32m+[m[32mCREATE EVENT TRIGGER regress_event_trigger_drop_objects ON sql_drop[m
[32m+[m	[32mWHEN TAG IN ('drop table', 'drop function', 'drop view',[m
[32m+[m		[32m'drop owned', 'drop schema', 'alter table')[m
[32m+[m	[32mEXECUTE PROCEDURE test_evtrig_dropped_objects();[m
[32m+[m[32mALTER TABLE schema_one.table_one DROP COLUMN a;[m
[32m+[m[32mDROP SCHEMA schema_one, schema_two CASCADE;[m
[32m+[m[32mNOTICE:  drop cascades to 7 other objects[m
[32m+[m[32mDETAIL:  drop cascades to table schema_two.table_two[m
[32m+[m[32mdrop cascades to table schema_two.table_three[m
[32m+[m[32mdrop cascades to function schema_two.add(integer,integer)[m
[32m+[m[32mdrop cascades to function schema_two.newton(integer)[m
[32m+[m[32mdrop cascades to table schema_one.table_one[m
[32m+[m[32mdrop cascades to table schema_one."table two"[m
[32m+[m[32mdrop cascades to table schema_one.table_three[m
[32m+[m[32mNOTICE:  table "schema_two_table_two" does not exist, skipping[m
[32m+[m[32mCONTEXT:  SQL statement "DROP TABLE IF EXISTS audit_tbls.schema_two_table_two"[m
[32m+[m[32mPL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement[m
[32m+[m[32mNOTICE:  table "audit_tbls_schema_two_table_three" does not exist, skipping[m
[32m+[m[32mCONTEXT:  SQL statement "DROP TABLE IF EXISTS audit_tbls.audit_tbls_schema_two_table_three"[m
[32m+[m[32mPL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement[m
[32m+[m[32mSQL statement "DROP TABLE IF EXISTS audit_tbls.schema_two_table_three"[m
[32m+[m[32mPL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement[m
[32m+[m[32mNOTICE:  table "schema_one_table_one" does not exist, skipping[m
[32m+[m[32mCONTEXT:  SQL statement "DROP TABLE IF EXISTS audit_tbls.schema_one_table_one"[m
[32m+[m[32mPL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement[m
[32m+[m[32mNOTICE:  table "schema_one_table two" does not exist, skipping[m
[32m+[m[32mCONTEXT:  SQL statement "DROP TABLE IF EXISTS audit_tbls."schema_one_table two""[m
[32m+[m[32mPL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement[m
[32m+[m[32mNOTICE:  table "schema_one_table_three" does not exist, skipping[m
[32m+[m[32mCONTEXT:  SQL statement "DROP TABLE IF EXISTS audit_tbls.schema_one_table_three"[m
[32m+[m[32mPL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement[m
[32m+[m[32mSELECT * FROM dropped_objects WHERE schema IS NULL OR schema <> 'pg_toast';[m
[32m+[m[32m     type     |   schema   |               object[m[41m                [m
[32m+[m[32m--------------+------------+-------------------------------------[m
[32m+[m[32m table column | schema_one | schema_one.table_one.a[m
[32m+[m[32m schema       |            | schema_two[m
[32m+[m[32m table        | schema_two | schema_two.table_two[m
[32m+[m[32m type         | schema_two | schema_two.table_two[m
[32m+[m[32m type         | schema_two | schema_two.table_two[][m
[32m+[m[32m table        | audit_tbls | audit_tbls.schema_two_table_three[m
[32m+[m[32m type         | audit_tbls | audit_tbls.schema_two_table_three[m
[32m+[m[32m type         | audit_tbls | audit_tbls.schema_two_table_three[][m
[32m+[m[32m table        | schema_two | schema_two.table_three[m
[32m+[m[32m type         | schema_two | schema_two.table_three[m
[32m+[m[32m type         | schema_two | schema_two.table_three[][m
[32m+[m[32m function     | schema_two | schema_two.add(integer,integer)[m
[32m+[m[32m aggregate    | schema_two | schema_two.newton(integer)[m
[32m+[m[32m schema       |            | schema_one[m
[32m+[m[32m table        | schema_one | schema_one.table_one[m
[32m+[m[32m type         | schema_one | schema_one.table_one[m
[32m+[m[32m type         | schema_one | schema_one.table_one[][m
[32m+[m[32m table        | schema_one | schema_one."table two"[m
[32m+[m[32m type         | schema_one | schema_one."table two"[m
[32m+[m[32m type         | schema_one | schema_one."table two"[][m
[32m+[m[32m table        | schema_one | schema_one.table_three[m
[32m+[m[32m type         | schema_one | schema_one.table_three[m
[32m+[m[32m type         | schema_one | schema_one.table_three[][m
[32m+[m[32m(23 rows)[m
[32m+[m
[32m+[m[32mDROP OWNED BY regression_bob;[m
[32m+[m[32mNOTICE:  table "audit_tbls_schema_one_table_two" does not exist, skipping[m
[32m+[m[32mCONTEXT:  SQL statement "DROP TABLE IF EXISTS audit_tbls.audit_tbls_schema_one_table_two"[m
[32m+[m[32mPL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement[m
[32m+[m[32mSELECT * FROM dropped_objects WHERE type = 'schema';[m
[32m+[m[32m  type  | schema |   object[m[41m   [m
[32m+[m[32m--------+--------+------------[m
[32m+[m[32m schema |        | schema_two[m
[32m+[m[32m schema |        | schema_one[m
[32m+[m[32m schema |        | audit_tbls[m
[32m+[m[32m(3 rows)[m
[32m+[m
[32m+[m[32mDROP ROLE regression_bob;[m
[32m+[m[32mDROP EVENT TRIGGER regress_event_trigger_drop_objects;[m
[1mdiff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql[m
[1mindex a07dcd7..8dd297a 100644[m
[1m--- a/src/test/regress/sql/event_trigger.sql[m
[1m+++ b/src/test/regress/sql/event_trigger.sql[m
[36m@@ -97,10 +97,73 @@[m [mdrop event trigger regress_event_trigger;[m
 -- should fail, regression_bob owns regress_event_trigger2/3[m
 drop role regression_bob;[m
 [m
[32m+[m[32m-- cleanup before next test[m
 -- these are all OK; the second one should emit a NOTICE[m
 drop event trigger if exists regress_event_trigger2;[m
 drop event trigger if exists regress_event_trigger2;[m
 drop event trigger regress_event_trigger3;[m
 drop event trigger regress_event_trigger_end;[m
[31m-drop function test_event_trigger();[m
[31m-drop role regression_bob;[m
[32m+[m
[32m+[m[32m-- test support for dropped objects[m
[32m+[m[32mCREATE SCHEMA schema_one authorization regression_bob;[m
[32m+[m[32mCREATE SCHEMA schema_two authorization regression_bob;[m
[32m+[m[32mCREATE SCHEMA audit_tbls authorization regression_bob;[m
[32m+[m[32mSET SESSION AUTHORIZATION regression_bob;[m
[32m+[m
[32m+[m[32mCREATE TABLE schema_one.table_one(a int);[m
[32m+[m[32mCREATE TABLE schema_one."table two"(a int);[m
[32m+[m[32mCREATE TABLE schema_one.table_three(a int);[m
[32m+[m[32mCREATE TABLE audit_tbls.schema_one_table_two(the_value text);[m
[32m+[m
[32m+[m[32mCREATE TABLE schema_two.table_two(a int);[m
[32m+[m[32mCREATE TABLE schema_two.table_three(a int, b text);[m
[32m+[m[32mCREATE TABLE audit_tbls.schema_two_table_three(the_value text);[m
[32m+[m
[32m+[m[32mCREATE OR REPLACE FUNCTION schema_two.add(int, int) RETURNS int LANGUAGE plpgsql[m
[32m+[m[32m  CALLED ON NULL INPUT[m
[32m+[m[32m  AS $$ BEGIN RETURN coalesce($1,0) + coalesce($2,0); END; $$;[m
[32m+[m[32mCREATE AGGREGATE schema_two.newton[m
[32m+[m[32m  (BASETYPE = int, SFUNC = schema_two.add, STYPE = int);[m
[32m+[m
[32m+[m[32mRESET SESSION AUTHORIZATION;[m
[32m+[m
[32m+[m[32mCREATE TABLE dropped_objects ([m
[32m+[m	[32mtype text,[m
[32m+[m	[32mschema text,[m
[32m+[m	[32mobject text);[m
[32m+[m
[32m+[m[32mCREATE OR REPLACE FUNCTION test_evtrig_dropped_objects() RETURNS event_trigger[m
[32m+[m[32mLANGUAGE plpgsql AS $$[m
[32m+[m[32mDECLARE[m
[32m+[m[32m    obj record;[m
[32m+[m[32mBEGIN[m
[32m+[m[32m    FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()[m
[32m+[m[32m    LOOP[m
[32m+[m[32m        IF obj.object_type = 'table' THEN[m
[32m+[m[32m                EXECUTE format('DROP TABLE IF EXISTS audit_tbls.%I',[m
[32m+[m					[32mformat('%s_%s', obj.schema_name, obj.object_name));[m
[32m+[m[32m        END IF;[m
[32m+[m
[32m+[m	[32mINSERT INTO dropped_objects[m
[32m+[m		[32m(type, schema, object) VALUES[m
[32m+[m		[32m(obj.object_type, obj.schema_name, obj.object_identity);[m
[32m+[m[32m    END LOOP;[m
[32m+[m[32mEND[m
[32m+[m[32m$$;[m
[32m+[m
[32m+[m[32mCREATE EVENT TRIGGER regress_event_trigger_drop_objects ON sql_drop[m
[32m+[m	[32mWHEN TAG IN ('drop table', 'drop function', 'drop view',[m
[32m+[m		[32m'drop owned', 'drop schema', 'alter table')[m
[32m+[m	[32mEXECUTE PROCEDURE test_evtrig_dropped_objects();[m
[32m+[m
[32m+[m[32mALTER TABLE schema_one.table_one DROP COLUMN a;[m
[32m+[m[32mDROP SCHEMA schema_one, schema_two CASCADE;[m
[32m+[m
[32m+[m[32mSELECT * FROM dropped_objects WHERE schema IS NULL OR schema <> 'pg_toast';[m
[32m+[m
[32m+[m[32mDROP OWNED BY regression_bob;[m
[32m+[m[32mSELECT * FROM dropped_objects WHERE type = 'schema';[m
[32m+[m
[32m+[m[32mDROP ROLE regression_bob;[m
[32m+[m
[32m+[m[32mDROP EVENT TRIGGER regress_event_trigger_drop_objects;[m
