*** a/doc/src/sgml/event-trigger.sgml
--- b/doc/src/sgml/event-trigger.sgml
***************
*** 27,43 ****
     <para>
       An event trigger fires whenever the event with which it is associated
       occurs in the database in which it is defined. Currently, the only
!      supported events are <literal>ddl_command_start</>
!      and <literal>ddl_command_end</>. Support for additional events may be
!      added in future releases.
     </para>
  
     <para>
       The <literal>ddl_command_start</> event occurs just before the
       execution of a <literal>CREATE</>, <literal>ALTER</>, or <literal>DROP</>
       command.  As an exception, however, this event does not occur for
!      DDL commands targeting shared objects - databases, roles, and tablespaces
!      - or for command targeting event triggers themselves.  The event trigger
       mechanism does not support these object types.
       <literal>ddl_command_start</> also occurs just before the execution of a
       <literal>SELECT INTO</literal> command, since this is equivalent to
--- 27,45 ----
     <para>
       An event trigger fires whenever the event with which it is associated
       occurs in the database in which it is defined. Currently, the only
!      supported events are
!      <literal>ddl_command_start</>,
!      <literal>ddl_command_end</>
!      and <literal>sql_drop</>.
!      Support for additional events may be added in future releases.
     </para>
  
     <para>
       The <literal>ddl_command_start</> event occurs just before the
       execution of a <literal>CREATE</>, <literal>ALTER</>, or <literal>DROP</>
       command.  As an exception, however, this event does not occur for
!      DDL commands targeting shared objects &mdash; databases, roles, and tablespaces
!      &mdash; or for command targeting event triggers themselves.  The event trigger
       mechanism does not support these object types.
       <literal>ddl_command_start</> also occurs just before the execution of a
       <literal>SELECT INTO</literal> command, since this is equivalent to
***************
*** 46,51 ****
--- 48,64 ----
     </para>
  
     <para>
+     The <literal>sql_drop</> event occurs just before the
+     <literal>ddl_command_end</> event trigger for any operation that drops
+     database objects.  To list the objects that have been dropped, use the set
+     returning function <literal>pg_event_trigger_dropped_objects()</> from your
+     <literal>sql_drop</> event trigger code (see
+     <xref linkend="functions-event-triggers">). Note that
+     the trigger is executed after the objects have been deleted from the
+     system catalogs, so it's not possible to look them up anymore.
+    </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,
***************
*** 99,104 ****
--- 112,118 ----
          <entry>command tag</entry>
          <entry><literal>ddl_command_start</literal></entry>
          <entry><literal>ddl_command_end</literal></entry>
+         <entry><literal>sql_drop</literal></entry>
         </row>
        </thead>
        <tbody>
***************
*** 106,506 ****
--- 120,606 ----
          <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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
+        </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </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>
         </row>
        </tbody>
       </tgroup>
*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 15980,15983 **** FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();
--- 15980,16090 ----
          <xref linkend="SQL-CREATETRIGGER">.
      </para>
    </sect1>
+ 
+   <sect1 id="functions-event-triggers">
+    <title>Event Trigger Functions</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</>, which
+     lists all object dropped by the command in whose <literal>sql_drop</>
+     event it is called.  This function 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>classid</literal></entry>
+         <entry><type>Oid</type></entry>
+         <entry>OID of catalog the object belonged in</entry>
+        </row>
+        <row>
+         <entry><literal>objid</literal></entry>
+         <entry><type>Oid</type></entry>
+         <entry>OID the object had within the catalog</entry>
+        </row>
+        <row>
+         <entry><literal>objsubid</literal></entry>
+         <entry><type>int32</type></entry>
+         <entry>Object sub-id (e.g. attribute number for columns)</entry>
+        </row>
+        <row>
+         <entry><literal>object_type</literal></entry>
+         <entry><type>text</type></entry>
+         <entry>Type of the object</entry>
+        </row>
+        <row>
+         <entry><literal>schema_name</literal></entry>
+         <entry><type>text</type></entry>
+         <entry>
+          Name of the schema the object belonged in, if any.  No quoting
+          is applied.
+         </entry>
+        </row>
+        <row>
+         <entry><literal>object_name</literal></entry>
+         <entry><type>text</type></entry>
+         <entry>
+          Name of the object, if the combination of schema and name can be
+          used as an unique identifier for the object; otherwise <literal>NULL</>.
+          No quoting is applied, and name is never schema-qualified.
+         </entry>
+        </row>
+        <row>
+         <entry><literal>object_identity</literal></entry>
+         <entry><type>text</type></entry>
+         <entry>
+          Text rendering of the object identity, schema-qualified. Each and every
+          identifier present in the identity is quoted if necessary.
+         </entry>
+        </row>
+       </tbody>
+      </tgroup>
+     </informaltable>
+    </para>
+ 
+    <para>
+     The <function>pg_event_trigger_dropped_objects</> function can be used
+     in an event trigger like this:
+ <programlisting>
+ CREATE FUNCTION test_event_trigger_for_drops()
+         RETURNS event_trigger LANGUAGE plpgsql AS $$
+ DECLARE
+     obj record;
+ BEGIN
+     FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()
+     LOOP
+         RAISE NOTICE '% dropped object: % %.% %',
+                      tg_tag,
+                      obj.object_type,
+                      obj.schema_name,
+                      obj.object_name,
+                      obj.object_identity;
+     END LOOP;
+ END
+ $$;
+ CREATE EVENT TRIGGER test_event_trigger_for_drops
+    ON sql_drop
+    EXECUTE PROCEDURE test_event_trigger_for_drops();
+ </programlisting>
+     </para>
+ 
+      <para>
+        For more information about event triggers,
+        see <xref linkend="event-triggers">.
+     </para>
+   </sect1>
+ 
  </chapter>
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 191,196 **** static bool stack_address_present_add_flags(const ObjectAddress *object,
--- 191,234 ----
  
  
  /*
+  * Go through the objects given running the final actions on them, and execute
+  * the actual deletion.
+  */
+ static void
+ deleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel,
+ 					int flags)
+ {
+ 	int		i;
+ 
+ 	/*
+ 	 * Keep track of objects for event triggers, if necessary.
+ 	 */
+ 	if (trackDroppedObjectsNeeded())
+ 	{
+ 		for (i = 0; i < targetObjects->numrefs; i++)
+ 		{
+ 			ObjectAddress *thisobj = targetObjects->refs + i;
+ 
+ 			if ((!(flags & PERFORM_DELETION_INTERNAL)) &&
+ 				EventTriggerSupportsObjectType(getObjectClass(thisobj)))
+ 			{
+ 				EventTriggerSQLDropAddObject(thisobj);
+ 			}
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * Delete all the objects in the proper order.
+ 	 */
+ 	for (i = 0; i < targetObjects->numrefs; i++)
+ 	{
+ 		ObjectAddress *thisobj = targetObjects->refs + i;
+ 
+ 		deleteOneObject(thisobj, depRel, flags);
+ 	}
+ }
+ 
+ /*
   * performDeletion: attempt to drop the specified object.  If CASCADE
   * behavior is specified, also drop any dependent objects (recursively).
   * If RESTRICT behavior is specified, error out if there are any dependent
***************
*** 215,221 **** performDeletion(const ObjectAddress *object,
  {
  	Relation	depRel;
  	ObjectAddresses *targetObjects;
- 	int			i;
  
  	/*
  	 * We save some cycles by opening pg_depend just once and passing the
--- 253,258 ----
***************
*** 250,264 **** performDeletion(const ObjectAddress *object,
  						   NOTICE,
  						   object);
  
! 	/*
! 	 * Delete all the objects in the proper order.
! 	 */
! 	for (i = 0; i < targetObjects->numrefs; i++)
! 	{
! 		ObjectAddress *thisobj = targetObjects->refs + i;
! 
! 		deleteOneObject(thisobj, &depRel, flags);
! 	}
  
  	/* And clean up */
  	free_object_addresses(targetObjects);
--- 287,294 ----
  						   NOTICE,
  						   object);
  
! 	/* do the deed */
! 	deleteObjectsInList(targetObjects, &depRel, flags);
  
  	/* And clean up */
  	free_object_addresses(targetObjects);
***************
*** 332,346 **** performMultipleDeletions(const ObjectAddresses *objects,
  						   NOTICE,
  						   (objects->numrefs == 1 ? objects->refs : NULL));
  
! 	/*
! 	 * Delete all the objects in the proper order.
! 	 */
! 	for (i = 0; i < targetObjects->numrefs; i++)
! 	{
! 		ObjectAddress *thisobj = targetObjects->refs + i;
! 
! 		deleteOneObject(thisobj, &depRel, flags);
! 	}
  
  	/* And clean up */
  	free_object_addresses(targetObjects);
--- 362,369 ----
  						   NOTICE,
  						   (objects->numrefs == 1 ? objects->refs : NULL));
  
! 	/* do the deed */
! 	deleteObjectsInList(targetObjects, &depRel, flags);
  
  	/* And clean up */
  	free_object_addresses(targetObjects);
***************
*** 356,361 **** performMultipleDeletions(const ObjectAddresses *objects,
--- 379,388 ----
   * This is currently used only to clean out the contents of a schema
   * (namespace): the passed object is a namespace.  We normally want this
   * to be done silently, so there's an option to suppress NOTICE messages.
+  *
+  * Note we don't fire object drop event triggers here; it would be wrong to do
+  * so for the current only use of this function, but if more callers are added
+  * this might need to be reconsidered.
   */
  void
  deleteWhatDependsOn(const ObjectAddress *object,
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
***************
*** 19,32 ****
--- 19,35 ----
  #include "catalog/indexing.h"
  #include "catalog/objectaccess.h"
  #include "catalog/pg_event_trigger.h"
+ #include "catalog/pg_namespace.h"
  #include "catalog/pg_proc.h"
  #include "catalog/pg_trigger.h"
  #include "catalog/pg_type.h"
  #include "commands/dbcommands.h"
  #include "commands/event_trigger.h"
  #include "commands/trigger.h"
+ #include "funcapi.h"
  #include "parser/parse_func.h"
  #include "pgstat.h"
+ #include "lib/ilist.h"
  #include "miscadmin.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
***************
*** 39,44 ****
--- 42,57 ----
  #include "utils/syscache.h"
  #include "tcop/utility.h"
  
+ 
+ typedef struct EventTriggerQueryState
+ {
+ 	slist_head	SQLDropList;
+ 	MemoryContext	cxt;
+ 	struct EventTriggerQueryState *previous;
+ } EventTriggerQueryState;
+ 
+ EventTriggerQueryState	*currentEventTriggerState = NULL;
+ 
  typedef struct
  {
  	const char	   *obtypename;
***************
*** 89,94 **** static event_trigger_support_data event_trigger_support[] = {
--- 102,118 ----
  	{ NULL, false }
  };
  
+ /* Support for dropped objects */
+ typedef struct SQLDropObject
+ {
+ 	ObjectAddress	address;
+ 	const char	   *schemaname;
+ 	const char	   *objname;
+ 	const char	   *objidentity;
+ 	const char	   *objecttype;
+ 	slist_node		next;
+ } SQLDropObject;
+ 
  static void AlterEventTriggerOwner_internal(Relation rel,
  											HeapTuple tup,
  											Oid newOwnerId);
***************
*** 127,133 **** CreateEventTrigger(CreateEventTrigStmt *stmt)
  
  	/* Validate event name. */
  	if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
! 		strcmp(stmt->eventname, "ddl_command_end") != 0)
  		ereport(ERROR,
  			(errcode(ERRCODE_SYNTAX_ERROR),
  			 errmsg("unrecognized event name \"%s\"",
--- 151,158 ----
  
  	/* Validate event name. */
  	if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
! 		strcmp(stmt->eventname, "ddl_command_end") != 0 &&
! 		strcmp(stmt->eventname, "sql_drop") != 0)
  		ereport(ERROR,
  			(errcode(ERRCODE_SYNTAX_ERROR),
  			 errmsg("unrecognized event name \"%s\"",
***************
*** 151,157 **** CreateEventTrigger(CreateEventTrigStmt *stmt)
  	}
  
  	/* Validate tag list, if any. */
! 	if (strcmp(stmt->eventname, "ddl_command_start") == 0 && tags != NULL)
  		validate_ddl_tags("tag", tags);
  
  	/*
--- 176,185 ----
  	}
  
  	/* Validate tag list, if any. */
! 	if ((strcmp(stmt->eventname, "ddl_command_start") == 0 ||
! 		 strcmp(stmt->eventname, "ddl_command_end") == 0 ||
! 		 strcmp(stmt->eventname, "sql_drop") == 0)
! 		&& tags != NULL)
  		validate_ddl_tags("tag", tags);
  
  	/*
***************
*** 220,226 **** check_ddl_tag(const char *tag)
  		pg_strcasecmp(tag, "SELECT INTO") == 0 ||
  		pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
  		pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
! 		pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0)
  		return EVENT_TRIGGER_COMMAND_TAG_OK;
  
  	/*
--- 248,255 ----
  		pg_strcasecmp(tag, "SELECT INTO") == 0 ||
  		pg_strcasecmp(tag, "REFRESH MATERIALIZED VIEW") == 0 ||
  		pg_strcasecmp(tag, "ALTER DEFAULT PRIVILEGES") == 0 ||
! 		pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
! 		pg_strcasecmp(tag, "DROP OWNED") == 0)
  		return EVENT_TRIGGER_COMMAND_TAG_OK;
  
  	/*
***************
*** 568,602 **** filter_event_trigger(const char **tag, EventTriggerCacheItem  *item)
  }
  
  /*
!  * Fire ddl_command_start triggers.
   */
! void
! EventTriggerDDLCommandStart(Node *parsetree)
  {
  	List	   *cachelist;
- 	List	   *runlist = NIL;
  	ListCell   *lc;
! 	const char *tag;
! 	EventTriggerData	trigdata;
! 
! 	/*
! 	 * Event Triggers are completely disabled in standalone mode.  There are
! 	 * (at least) two reasons for this:
! 	 *
! 	 * 1. A sufficiently broken event trigger might not only render the
! 	 * database unusable, but prevent disabling itself to fix the situation.
! 	 * In this scenario, restarting in standalone mode provides an escape
! 	 * hatch.
! 	 *
! 	 * 2. BuildEventTriggerCache relies on systable_beginscan_ordered, and
! 	 * therefore will malfunction if pg_event_trigger's indexes are damaged.
! 	 * To allow recovery from a damaged index, we need some operating mode
! 	 * wherein event triggers are disabled.  (Or we could implement
! 	 * heapscan-and-sort logic for that case, but having disaster recovery
! 	 * scenarios depend on code that's otherwise untested isn't appetizing.)
! 	 */
! 	if (!IsUnderPostmaster)
! 		return;
  
  	/*
  	 * We want the list of command tags for which this procedure is actually
--- 597,615 ----
  }
  
  /*
!  * Setup for running triggers for the given event.  Return value is an OID list
!  * of functions to run; if there are any, trigdata is filled with an
!  * appropriate EventTriggerData for them to receive.
   */
! static List *
! EventTriggerCommonSetup(Node *parsetree,
! 						EventTriggerEvent event, const char *eventstr,
! 						EventTriggerData *trigdata)
  {
+ 	const char *tag;
  	List	   *cachelist;
  	ListCell   *lc;
! 	List	   *runlist = NIL;
  
  	/*
  	 * We want the list of command tags for which this procedure is actually
***************
*** 624,632 **** EventTriggerDDLCommandStart(Node *parsetree)
  #endif
  
  	/* Use cache to find triggers for this event; fast exit if none. */
! 	cachelist = EventCacheLookup(EVT_DDLCommandStart);
! 	if (cachelist == NULL)
! 		return;
  
  	/* Get the command tag. */
  	tag = CreateCommandTag(parsetree);
--- 637,645 ----
  #endif
  
  	/* Use cache to find triggers for this event; fast exit if none. */
! 	cachelist = EventCacheLookup(event);
! 	if (cachelist == NIL)
! 		return NIL;
  
  	/* Get the command tag. */
  	tag = CreateCommandTag(parsetree);
***************
*** 649,659 **** EventTriggerDDLCommandStart(Node *parsetree)
  		}
  	}
  
! 	/* Construct event trigger data. */
! 	trigdata.type = T_EventTriggerData;
! 	trigdata.event = "ddl_command_start";
! 	trigdata.parsetree = parsetree;
! 	trigdata.tag = tag;
  
  	/* Run the triggers. */
  	EventTriggerInvoke(runlist, &trigdata);
--- 662,712 ----
  		}
  	}
  
! 	/* don't spend any more time on this if no functions to run */
! 	if (runlist == NIL)
! 		return NIL;
! 
! 	trigdata->type = T_EventTriggerData;
! 	trigdata->event = eventstr;
! 	trigdata->parsetree = parsetree;
! 	trigdata->tag = tag;
! 
! 	return runlist;
! }
! 
! /*
!  * Fire ddl_command_start triggers.
!  */
! void
! EventTriggerDDLCommandStart(Node *parsetree)
! {
! 	List	   *runlist;
! 	EventTriggerData	trigdata;
! 
! 	/*
! 	 * Event Triggers are completely disabled in standalone mode.  There are
! 	 * (at least) two reasons for this:
! 	 *
! 	 * 1. A sufficiently broken event trigger might not only render the
! 	 * database unusable, but prevent disabling itself to fix the situation.
! 	 * In this scenario, restarting in standalone mode provides an escape
! 	 * hatch.
! 	 *
! 	 * 2. BuildEventTriggerCache relies on systable_beginscan_ordered, and
! 	 * therefore will malfunction if pg_event_trigger's indexes are damaged.
! 	 * To allow recovery from a damaged index, we need some operating mode
! 	 * wherein event triggers are disabled.  (Or we could implement
! 	 * heapscan-and-sort logic for that case, but having disaster recovery
! 	 * scenarios depend on code that's otherwise untested isn't appetizing.)
! 	 */
! 	if (!IsUnderPostmaster)
! 		return;
! 
! 	runlist = EventTriggerCommonSetup(parsetree,
! 									  EVT_DDLCommandStart, "ddl_command_start",
! 									  &trigdata);
! 	if (runlist == NIL)
! 		return;
  
  	/* Run the triggers. */
  	EventTriggerInvoke(runlist, &trigdata);
***************
*** 674,683 **** EventTriggerDDLCommandStart(Node *parsetree)
  void
  EventTriggerDDLCommandEnd(Node *parsetree)
  {
! 	List	   *cachelist;
! 	List	   *runlist = NIL;
! 	ListCell   *lc;
! 	const char *tag;
  	EventTriggerData	trigdata;
  
  	/*
--- 727,733 ----
  void
  EventTriggerDDLCommandEnd(Node *parsetree)
  {
! 	List	   *runlist;
  	EventTriggerData	trigdata;
  
  	/*
***************
*** 687,739 **** EventTriggerDDLCommandEnd(Node *parsetree)
  	if (!IsUnderPostmaster)
  		return;
  
  	/*
! 	 * See EventTriggerDDLCommandStart for a discussion about why this check is
! 	 * important.
! 	 *
  	 */
! #ifdef USE_ASSERT_CHECKING
! 	if (assert_enabled)
! 	{
! 		const char *dbgtag;
  
! 		dbgtag = CreateCommandTag(parsetree);
! 		if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
! 			elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
! 	}
! #endif
  
! 	/* Use cache to find triggers for this event; fast exit if none. */
! 	cachelist = EventCacheLookup(EVT_DDLCommandEnd);
! 	if (cachelist == NULL)
! 		return;
  
! 	/* Get the command tag. */
! 	tag = CreateCommandTag(parsetree);
  
  	/*
! 	 * Filter list of event triggers by command tag, and copy them into
! 	 * our memory context.  Once we start running the command trigers, or
! 	 * indeed once we do anything at all that touches the catalogs, an
! 	 * invalidation might leave cachelist pointing at garbage, so we must
! 	 * do this before we can do much else.
  	 */
! 	foreach (lc, cachelist)
! 	{
! 		EventTriggerCacheItem  *item = lfirst(lc);
  
! 		if (filter_event_trigger(&tag, item))
! 		{
! 			/* We must plan to fire this trigger. */
! 			runlist = lappend_oid(runlist, item->fnoid);
! 		}
! 	}
  
! 	/* Construct event trigger data. */
! 	trigdata.type = T_EventTriggerData;
! 	trigdata.event = "ddl_command_end";
! 	trigdata.parsetree = parsetree;
! 	trigdata.tag = tag;
  
  	/*
  	 * Make sure anything the main command did will be visible to the
--- 737,797 ----
  	if (!IsUnderPostmaster)
  		return;
  
+ 	runlist = EventTriggerCommonSetup(parsetree,
+ 									  EVT_DDLCommandEnd, "ddl_command_end",
+ 									  &trigdata);
+ 	if (runlist == NIL)
+ 		return;
+ 
  	/*
! 	 * Make sure anything the main command did will be visible to the
! 	 * event triggers.
  	 */
! 	CommandCounterIncrement();
  
! 	/* Run the triggers. */
! 	EventTriggerInvoke(runlist, &trigdata);
  
! 	/* Cleanup. */
! 	list_free(runlist);
! }
  
! /*
!  * Fire sql_drop triggers.
!  */
! void
! EventTriggerSQLDrop(Node *parsetree)
! {
! 	List	   *runlist;
! 	EventTriggerData	trigdata;
  
  	/*
! 	 * See EventTriggerDDLCommandStart for a discussion about why event
! 	 * triggers are disabled in single user mode.
  	 */
! 	if (!IsUnderPostmaster)
! 		return;
  
! 	/*
! 	 * Use current state to determine whether this event fires at all.  If there
! 	 * are no triggers for the sql_drop event, then we don't have anything to do
! 	 * here.  Note that dropped object collection is disabled if this is the case,
! 	 * so even if we were to try to run, the list would be empty.
! 	 */
! 	if (!currentEventTriggerState ||
! 		slist_is_empty(&currentEventTriggerState->SQLDropList))
! 		return;
  
! 	runlist = EventTriggerCommonSetup(parsetree,
! 									  EVT_SQLDrop, "sql_drop",
! 									  &trigdata);
! 	/*
! 	 * Nothing to do if run list is empty.  Note this shouldn't happen, because
! 	 * if there are no sql_drop events, then objects-to-drop wouldn't have been
! 	 * collected in the first place and we would have quitted above.
! 	 */
! 	if (runlist == NIL)
! 		return;
  
  	/*
  	 * Make sure anything the main command did will be visible to the
***************
*** 832,834 **** EventTriggerSupportsObjectType(ObjectType obtype)
--- 890,1172 ----
  	}
  	return true;
  }
+ 
+ /*
+  * Prepare event trigger state for a new complete query to run, if necessary;
+  * returns whether this was done.  If it was, EventTriggerEndCompleteQuery must
+  * be called when the query is done, regardless of whether it succeeds or fails
+  * -- so use of a PG_TRY block is mandatory.
+  */
+ bool
+ EventTriggerBeginCompleteQuery(void)
+ {
+ 	EventTriggerQueryState *state;
+ 	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.
+ 	 */
+ 	if (!trackDroppedObjectsNeeded())
+ 		return false;
+ 
+ 	cxt = AllocSetContextCreate(TopMemoryContext,
+ 								"event trigger state",
+ 								ALLOCSET_DEFAULT_MINSIZE,
+ 								ALLOCSET_DEFAULT_INITSIZE,
+ 								ALLOCSET_DEFAULT_MAXSIZE);
+ 	state = MemoryContextAlloc(cxt, sizeof(EventTriggerQueryState));
+ 	state->cxt = cxt;
+ 	slist_init(&(state->SQLDropList));
+ 
+ 	state->previous = currentEventTriggerState;
+ 	currentEventTriggerState = state;
+ 
+ 	return true;
+ }
+ 
+ /*
+  * Query completed (or errored out) -- clean up local state, return to previous
+  * one.
+  *
+  * Note: it's an error to call this routine if EventTriggerBeginCompleteQuery
+  * returned false previously.
+  *
+  * Note: this might be called in the PG_CATCH block of a failing transaction,
+  * so be wary of running anything unnecessary.  (In particular, it's probably
+  * unwise to try to allocate memory.)
+  */
+ void
+ EventTriggerEndCompleteQuery(void)
+ {
+ 	EventTriggerQueryState *prevstate;
+ 
+ 	prevstate = currentEventTriggerState->previous;
+ 
+ 	/* this avoids the need for retail pfree of SQLDropList items: */
+ 	MemoryContextDelete(currentEventTriggerState->cxt);
+ 
+ 	currentEventTriggerState = prevstate;
+ }
+ 
+ /*
+  * Do we need to keep close track of objects being dropped?
+  *
+  * This is useful because there is a cost to running with them enabled.
+  */
+ bool
+ trackDroppedObjectsNeeded(void)
+ {
+ 	/* true if any sql_drop event trigger exists */
+ 	return list_length(EventCacheLookup(EVT_SQLDrop)) > 0;
+ }
+ 
+ /*
+  * Support for dropped objects information on event trigger functions.
+  *
+  * We keep the list of objects dropped by the current command in current
+  * state's SQLDropList (comprising SQLDropObject items).  Each time a new
+  * command is to start, a clean EventTriggerQueryState is created; commands
+  * that drop objects do the dependency.c dance to drop objects, which
+  * populates the current state's SQLDropList; when the event triggers are
+  * invoked they can consume the list via pg_event_trigger_dropped_objects().
+  * When the command finishes, the EventTriggerQueryState is cleared, and
+  * the one from the previous command is restored (when no command is in
+  * execution, the current state is NULL).
+  *
+  * All this lets us support the case that an event trigger function drops
+  * objects "reentrantly".
+  */
+ 
+ /*
+  * Register one object as being dropped by the current command.
+  */
+ void
+ EventTriggerSQLDropAddObject(ObjectAddress *object)
+ {
+ 	SQLDropObject  *obj;
+ 	MemoryContext	oldcxt;
+ 
+ 	if (!currentEventTriggerState)
+ 		return;
+ 
+ 	Assert(EventTriggerSupportsObjectType(getObjectClass(object)));
+ 
+ 	/* don't report temp schemas */
+ 	if (object->classId == NamespaceRelationId &&
+ 		isAnyTempNamespace(object->objectId))
+ 		return;
+ 
+ 	oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
+ 
+ 	obj = palloc0(sizeof(SQLDropObject));
+ 	obj->address = *object;
+ 
+ 	/*
+ 	 * Obtain schema names from the object's catalog tuple, if one exists;
+ 	 * this lets us skip objects in temp schemas.  We trust that ObjectProperty
+ 	 * contains all object classes that can be schema-qualified.
+ 	 */
+ 	if (is_objectclass_supported(object->classId))
+ 	{
+ 		Relation	catalog;
+ 		HeapTuple	tuple;
+ 
+ 		catalog = heap_open(obj->address.classId, AccessShareLock);
+ 		tuple = get_catalog_object_by_oid(catalog, obj->address.objectId);
+ 
+ 		if (tuple)
+ 		{
+ 			AttrNumber	attnum;
+ 			Datum		datum;
+ 			bool		isnull;
+ 
+ 			attnum = get_object_attnum_namespace(obj->address.classId);
+ 			if (attnum != InvalidAttrNumber)
+ 			{
+ 				datum = heap_getattr(tuple, attnum,
+ 									 RelationGetDescr(catalog), &isnull);
+ 				if (!isnull)
+ 				{
+ 					Oid		namespaceId;
+ 
+ 					namespaceId = DatumGetObjectId(datum);
+ 					/* Don't report objects in temp namespaces */
+ 					if (isAnyTempNamespace(namespaceId))
+ 					{
+ 						pfree(obj);
+ 						heap_close(catalog, AccessShareLock);
+ 						MemoryContextSwitchTo(oldcxt);
+ 						return;
+ 					}
+ 
+ 					obj->schemaname = get_namespace_name(namespaceId);
+ 				}
+ 			}
+ 
+ 			if (get_object_namensp_unique(obj->address.classId) &&
+ 				obj->address.objectSubId == 0)
+ 			{
+ 				attnum = get_object_attnum_name(obj->address.classId);
+ 				if (attnum != InvalidAttrNumber)
+ 				{
+ 					datum = heap_getattr(tuple, attnum,
+ 										 RelationGetDescr(catalog), &isnull);
+ 					if (!isnull)
+ 						obj->objname = pstrdup(NameStr(*DatumGetName(datum)));
+ 				}
+ 			}
+ 		}
+ 
+ 		heap_close(catalog, AccessShareLock);
+ 	}
+ 
+ 	/* object identity */
+ 	obj->objidentity = getObjectIdentity(&obj->address);
+ 
+ 	/* and object type, too */
+ 	obj->objecttype = getObjectTypeDescription(&obj->address);
+ 
+ 	slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
+ 
+ 	MemoryContextSwitchTo(oldcxt);
+ }
+ 
+ /*
+  * pg_event_trigger_dropped_objects
+  *
+  * Make the list of dropped objects available to the user function run by the
+  * Event Trigger.
+  */
+ Datum
+ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
+ {
+ 	ReturnSetInfo	   *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ 	TupleDesc			tupdesc;
+ 	Tuplestorestate	   *tupstore;
+ 	MemoryContext		per_query_ctx;
+ 	MemoryContext		oldcontext;
+ 	slist_iter			iter;
+ 
+ 	/* XXX can this actually happen? */
+ 	if (!currentEventTriggerState)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("%s can only be called when there's a command in execution",
+ 						"pg_event_trigger_dropped_objects()")));
+ 
+ 	/* check to see if caller supports us returning a tuplestore */
+ 	if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("set-valued function called in context that cannot accept a set")));
+ 	if (!(rsinfo->allowedModes & SFRM_Materialize))
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("materialize mode required, but it is not allowed in this context")));
+ 
+ 	/* Build a tuple descriptor for our result type */
+ 	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ 		elog(ERROR, "return type must be a row type");
+ 
+ 	/* Build tuplestore to hold the result rows */
+ 	per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ 	oldcontext = MemoryContextSwitchTo(per_query_ctx);
+ 
+ 	tupstore = tuplestore_begin_heap(true, false, work_mem);
+ 	rsinfo->returnMode = SFRM_Materialize;
+ 	rsinfo->setResult = tupstore;
+ 	rsinfo->setDesc = tupdesc;
+ 
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	slist_foreach(iter, &(currentEventTriggerState->SQLDropList))
+ 	{
+ 		SQLDropObject *obj;
+ 		int			i = 0;
+ 		Datum		values[7];
+ 		bool		nulls[7];
+ 
+ 		obj = slist_container(SQLDropObject, next, iter.cur);
+ 
+ 		MemSet(values, 0, sizeof(values));
+ 		MemSet(nulls, 0, sizeof(nulls));
+ 
+ 		/* classid */
+ 		values[i++] = ObjectIdGetDatum(obj->address.classId);
+ 
+ 		/* objid */
+ 		values[i++] = ObjectIdGetDatum(obj->address.objectId);
+ 
+ 		/* objsubid */
+ 		values[i++] = Int32GetDatum(obj->address.objectSubId);
+ 
+ 		/* object_type */
+ 		values[i++] = CStringGetTextDatum(obj->objecttype);
+ 
+ 		/* schema_name */
+ 		if (obj->schemaname)
+ 			values[i++] = CStringGetTextDatum(obj->schemaname);
+ 		else
+ 			nulls[i++] = true;
+ 
+ 		/* object_name */
+ 		if (obj->objname)
+ 			values[i++] = CStringGetTextDatum(obj->objname);
+ 		else
+ 			nulls[i++] = true;
+ 
+ 		/* object_identity */
+ 		if (obj->objidentity)
+ 			values[i++] = CStringGetTextDatum(obj->objidentity);
+ 		else
+ 			nulls[i++] = true;
+ 
+ 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+ 	}
+ 
+ 	/* clean up and return the tuplestore */
+ 	tuplestore_donestoring(tupstore);
+ 
+ 	return (Datum) 0;
+ }
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 4478,4484 **** DropTrigStmt:
   *
   *		QUERIES :
   *				CREATE EVENT TRIGGER ...
-  *				DROP EVENT TRIGGER ...
   *				ALTER EVENT TRIGGER ...
   *
   *****************************************************************************/
--- 4478,4483 ----
*** a/src/backend/tcop/utility.c
--- b/src/backend/tcop/utility.c
***************
*** 346,357 **** ProcessUtility(Node *parsetree,
  	do { \
  	    if (isCompleteQuery) \
          { \
! 		    EventTriggerDDLCommandStart(parsetree); \
  		} \
  		fncall; \
          if (isCompleteQuery) \
          { \
! 		    EventTriggerDDLCommandEnd(parsetree); \
  		} \
  	} while (0)
  
--- 346,358 ----
  	do { \
  	    if (isCompleteQuery) \
          { \
! 			EventTriggerDDLCommandStart(parsetree); \
  		} \
  		fncall; \
          if (isCompleteQuery) \
          { \
! 			EventTriggerSQLDrop(parsetree); \
! 			EventTriggerDDLCommandEnd(parsetree); \
  		} \
  	} while (0)
  
***************
*** 366,375 **** ProcessUtility(Node *parsetree,
--- 367,414 ----
  		fncall; \
  		if (_supported) \
  		{ \
+ 			EventTriggerSQLDrop(parsetree); \
  			EventTriggerDDLCommandEnd(parsetree); \
  		} \
  	} while (0)
  
+ /*
+  * UTILITY_BEGIN_QUERY and UTILITY_END_QUERY are a pair of macros to enclose
+  * execution of a single DDL command, to ensure the event trigger environment
+  * is appropriately set up before starting, and tore down after completion or
+  * error.
+  */
+ #define UTILITY_BEGIN_QUERY(isComplete) \
+ 	do { \
+ 		bool		_needCleanup = false; \
+ 		\
+ 		if (isComplete) \
+ 		{ \
+ 			_needCleanup = EventTriggerBeginCompleteQuery(); \
+ 		} \
+ 		\
+ 		PG_TRY(); \
+ 		{ \
+ 			/* avoid empty statement when followed by a semicolon */ \
+ 			(void) 0
+ 
+ #define UTILITY_END_QUERY() \
+ 		} \
+ 		PG_CATCH(); \
+ 		{ \
+ 			if (_needCleanup) \
+ 			{ \
+ 				EventTriggerEndCompleteQuery(); \
+ 			} \
+ 			PG_RE_THROW(); \
+ 		} \
+ 		PG_END_TRY(); \
+ 		if (_needCleanup) \
+ 		{ \
+ 			EventTriggerEndCompleteQuery(); \
+ 		} \
+ 	} while (0)
+ 
  void
  standard_ProcessUtility(Node *parsetree,
  						const char *queryString,
***************
*** 386,391 **** standard_ProcessUtility(Node *parsetree,
--- 425,432 ----
  	if (completionTag)
  		completionTag[0] = '\0';
  
+ 	UTILITY_BEGIN_QUERY(isCompleteQuery);
+ 
  	switch (nodeTag(parsetree))
  	{
  			/*
***************
*** 615,621 **** standard_ProcessUtility(Node *parsetree,
--- 656,665 ----
  				}
  
  				if (isCompleteQuery)
+ 				{
+ 					EventTriggerSQLDrop(parsetree);
  					EventTriggerDDLCommandEnd(parsetree);
+ 				}
  			}
  			break;
  
***************
*** 726,732 **** standard_ProcessUtility(Node *parsetree,
--- 770,779 ----
  
  				if (isCompleteQuery
  					&& EventTriggerSupportsObjectType(stmt->removeType))
+ 				{
+ 					EventTriggerSQLDrop(parsetree);
  					EventTriggerDDLCommandEnd(parsetree);
+ 				}
  
  				break;
  			}
***************
*** 856,861 **** standard_ProcessUtility(Node *parsetree,
--- 903,914 ----
  					ereport(NOTICE,
  						  (errmsg("relation \"%s\" does not exist, skipping",
  								  atstmt->relation->relname)));
+ 
+ 				if (isCompleteQuery)
+ 				{
+ 					EventTriggerSQLDrop(parsetree);
+ 					EventTriggerDDLCommandEnd(parsetree);
+ 				}
  			}
  			break;
  
***************
*** 1248,1255 **** standard_ProcessUtility(Node *parsetree,
  			break;
  
  		case T_DropOwnedStmt:
! 			/* no event triggers for global objects */
! 			DropOwnedObjects((DropOwnedStmt *) parsetree);
  			break;
  
  		case T_ReassignOwnedStmt:
--- 1301,1309 ----
  			break;
  
  		case T_DropOwnedStmt:
! 			InvokeDDLCommandEventTriggers(
! 				parsetree,
! 				DropOwnedObjects((DropOwnedStmt *) parsetree));
  			break;
  
  		case T_ReassignOwnedStmt:
***************
*** 1372,1377 **** standard_ProcessUtility(Node *parsetree,
--- 1426,1433 ----
  				 (int) nodeTag(parsetree));
  			break;
  	}
+ 
+ 	UTILITY_END_QUERY();
  }
  
  /*
*** a/src/backend/utils/adt/regproc.c
--- b/src/backend/utils/adt/regproc.c
***************
*** 345,351 **** format_procedure_internal(Oid procedure_oid, bool force_qualify)
  
  		/*
  		 * Would this proc be found (given the right args) by regprocedurein?
! 		 * If not, we need to qualify it.
  		 */
  		if (!force_qualify && FunctionIsVisible(procedure_oid))
  			nspname = NULL;
--- 345,351 ----
  
  		/*
  		 * Would this proc be found (given the right args) by regprocedurein?
! 		 * If not, or if caller requests it, we need to qualify it.
  		 */
  		if (!force_qualify && FunctionIsVisible(procedure_oid))
  			nspname = NULL;
*** a/src/backend/utils/cache/evtcache.c
--- b/src/backend/utils/cache/evtcache.c
***************
*** 169,174 **** BuildEventTriggerCache(void)
--- 169,176 ----
  			event = EVT_DDLCommandStart;
  		else if (strcmp(evtevent, "ddl_command_end") == 0)
  			event = EVT_DDLCommandEnd;
+ 		else if (strcmp(evtevent, "sql_drop") == 0)
+ 			event = EVT_SQLDrop;
  		else
  			continue;
  
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 4693,4698 **** DATA(insert OID = 3473 (  spg_range_quad_leaf_consistent	PGNSP PGUID 12 1 0 0 0
--- 4693,4701 ----
  DESCR("SP-GiST support for quad tree over range");
  
  
+ /* 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");
  /*
   * Symbolic values for provolatile column: these indicate whether the result
   * of a function is dependent *only* on the values of its explicit arguments,
*** a/src/include/commands/event_trigger.h
--- b/src/include/commands/event_trigger.h
***************
*** 19,25 ****
  typedef struct EventTriggerData
  {
  	NodeTag		type;
! 	char	   *event;				/* event name */
  	Node	   *parsetree;			/* parse tree */
  	const char *tag;				/* command tag */
  } EventTriggerData;
--- 19,25 ----
  typedef struct EventTriggerData
  {
  	NodeTag		type;
! 	const char *event;				/* event name */
  	Node	   *parsetree;			/* parse tree */
  	const char *tag;				/* command tag */
  } EventTriggerData;
***************
*** 42,46 **** extern void AlterEventTriggerOwner_oid(Oid, Oid newOwnerId);
--- 42,52 ----
  extern bool EventTriggerSupportsObjectType(ObjectType obtype);
  extern void EventTriggerDDLCommandStart(Node *parsetree);
  extern void EventTriggerDDLCommandEnd(Node *parsetree);
+ extern void EventTriggerSQLDrop(Node *parsetree);
+ 
+ extern bool EventTriggerBeginCompleteQuery(void);
+ extern void EventTriggerEndCompleteQuery(void);
+ extern bool trackDroppedObjectsNeeded(void);
+ extern void EventTriggerSQLDropAddObject(ObjectAddress *object);
  
  #endif   /* EVENT_TRIGGER_H */
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 1151,1156 **** extern Datum pg_identify_object(PG_FUNCTION_ARGS);
--- 1151,1159 ----
  /* commands/constraint.c */
  extern Datum unique_key_recheck(PG_FUNCTION_ARGS);
  
+ /* commands/event_trigger.c */
+ extern Datum pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS);
+ 
  /* commands/extension.c */
  extern Datum pg_available_extensions(PG_FUNCTION_ARGS);
  extern Datum pg_available_extension_versions(PG_FUNCTION_ARGS);
*** a/src/include/utils/evtcache.h
--- b/src/include/utils/evtcache.h
***************
*** 19,25 ****
  typedef enum
  {
  	EVT_DDLCommandStart,
! 	EVT_DDLCommandEnd
  } EventTriggerEvent;
  
  typedef struct
--- 19,26 ----
  typedef enum
  {
  	EVT_DDLCommandStart,
! 	EVT_DDLCommandEnd,
! 	EVT_SQLDrop
  } EventTriggerEvent;
  
  typedef struct
*** a/src/test/regress/expected/event_trigger.out
--- b/src/test/regress/expected/event_trigger.out
***************
*** 93,103 **** ERROR:  event trigger "regress_event_trigger" does not exist
  drop role regression_bob;
  ERROR:  role "regression_bob" cannot be dropped because some objects depend on it
  DETAIL:  owner of event trigger regress_event_trigger3
  -- these are all OK; the second one should emit a NOTICE
  drop event trigger if exists regress_event_trigger2;
  drop event trigger if exists regress_event_trigger2;
  NOTICE:  event trigger "regress_event_trigger2" does not exist, skipping
  drop event trigger regress_event_trigger3;
  drop event trigger regress_event_trigger_end;
! drop function test_event_trigger();
! drop role regression_bob;
--- 93,215 ----
  drop role regression_bob;
  ERROR:  role "regression_bob" cannot be dropped because some objects depend on it
  DETAIL:  owner of event trigger regress_event_trigger3
+ -- cleanup before next test
  -- these are all OK; the second one should emit a NOTICE
  drop event trigger if exists regress_event_trigger2;
  drop event trigger if exists regress_event_trigger2;
  NOTICE:  event trigger "regress_event_trigger2" does not exist, skipping
  drop event trigger regress_event_trigger3;
  drop event trigger regress_event_trigger_end;
! -- test support for dropped objects
! CREATE SCHEMA schema_one authorization regression_bob;
! CREATE SCHEMA schema_two authorization regression_bob;
! CREATE SCHEMA audit_tbls authorization regression_bob;
! SET SESSION AUTHORIZATION regression_bob;
! CREATE TABLE schema_one.table_one(a int);
! CREATE TABLE schema_one."table two"(a int);
! CREATE TABLE schema_one.table_three(a int);
! CREATE TABLE audit_tbls.schema_one_table_two(the_value text);
! CREATE TABLE schema_two.table_two(a int);
! CREATE TABLE schema_two.table_three(a int, b text);
! CREATE TABLE audit_tbls.schema_two_table_three(the_value text);
! CREATE OR REPLACE FUNCTION schema_two.add(int, int) RETURNS int LANGUAGE plpgsql
!   CALLED ON NULL INPUT
!   AS $$ BEGIN RETURN coalesce($1,0) + coalesce($2,0); END; $$;
! CREATE AGGREGATE schema_two.newton
!   (BASETYPE = int, SFUNC = schema_two.add, STYPE = int);
! RESET SESSION AUTHORIZATION;
! CREATE TABLE dropped_objects (
! 	type text,
! 	schema text,
! 	object text);
! CREATE OR REPLACE FUNCTION test_evtrig_dropped_objects() RETURNS event_trigger
! LANGUAGE plpgsql AS $$
! DECLARE
!     obj record;
! BEGIN
!     FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()
!     LOOP
!         IF obj.object_type = 'table' THEN
!                 EXECUTE format('DROP TABLE IF EXISTS audit_tbls.%I',
! 					format('%s_%s', obj.schema_name, obj.object_name));
!         END IF;
! 
! 	INSERT INTO dropped_objects
! 		(type, schema, object) VALUES
! 		(obj.object_type, obj.schema_name, obj.object_identity);
!     END LOOP;
! END
! $$;
! CREATE EVENT TRIGGER regress_event_trigger_drop_objects ON sql_drop
! 	WHEN TAG IN ('drop table', 'drop function', 'drop view',
! 		'drop owned', 'drop schema', 'alter table')
! 	EXECUTE PROCEDURE test_evtrig_dropped_objects();
! ALTER TABLE schema_one.table_one DROP COLUMN a;
! DROP SCHEMA schema_one, schema_two CASCADE;
! NOTICE:  drop cascades to 7 other objects
! DETAIL:  drop cascades to table schema_two.table_two
! drop cascades to table schema_two.table_three
! drop cascades to function schema_two.add(integer,integer)
! drop cascades to function schema_two.newton(integer)
! drop cascades to table schema_one.table_one
! drop cascades to table schema_one."table two"
! drop cascades to table schema_one.table_three
! NOTICE:  table "schema_two_table_two" does not exist, skipping
! CONTEXT:  SQL statement "DROP TABLE IF EXISTS audit_tbls.schema_two_table_two"
! PL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement
! NOTICE:  table "audit_tbls_schema_two_table_three" does not exist, skipping
! CONTEXT:  SQL statement "DROP TABLE IF EXISTS audit_tbls.audit_tbls_schema_two_table_three"
! PL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement
! SQL statement "DROP TABLE IF EXISTS audit_tbls.schema_two_table_three"
! PL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement
! NOTICE:  table "schema_one_table_one" does not exist, skipping
! CONTEXT:  SQL statement "DROP TABLE IF EXISTS audit_tbls.schema_one_table_one"
! PL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement
! NOTICE:  table "schema_one_table two" does not exist, skipping
! CONTEXT:  SQL statement "DROP TABLE IF EXISTS audit_tbls."schema_one_table two""
! PL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement
! NOTICE:  table "schema_one_table_three" does not exist, skipping
! CONTEXT:  SQL statement "DROP TABLE IF EXISTS audit_tbls.schema_one_table_three"
! PL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement
! SELECT * FROM dropped_objects WHERE schema IS NULL OR schema <> 'pg_toast';
!      type     |   schema   |               object                
! --------------+------------+-------------------------------------
!  table column | schema_one | schema_one.table_one.a
!  schema       |            | schema_two
!  table        | schema_two | schema_two.table_two
!  type         | schema_two | schema_two.table_two
!  type         | schema_two | schema_two.table_two[]
!  table        | audit_tbls | audit_tbls.schema_two_table_three
!  type         | audit_tbls | audit_tbls.schema_two_table_three
!  type         | audit_tbls | audit_tbls.schema_two_table_three[]
!  table        | schema_two | schema_two.table_three
!  type         | schema_two | schema_two.table_three
!  type         | schema_two | schema_two.table_three[]
!  function     | schema_two | schema_two.add(integer,integer)
!  aggregate    | schema_two | schema_two.newton(integer)
!  schema       |            | schema_one
!  table        | schema_one | schema_one.table_one
!  type         | schema_one | schema_one.table_one
!  type         | schema_one | schema_one.table_one[]
!  table        | schema_one | schema_one."table two"
!  type         | schema_one | schema_one."table two"
!  type         | schema_one | schema_one."table two"[]
!  table        | schema_one | schema_one.table_three
!  type         | schema_one | schema_one.table_three
!  type         | schema_one | schema_one.table_three[]
! (23 rows)
! 
! DROP OWNED BY regression_bob;
! NOTICE:  table "audit_tbls_schema_one_table_two" does not exist, skipping
! CONTEXT:  SQL statement "DROP TABLE IF EXISTS audit_tbls.audit_tbls_schema_one_table_two"
! PL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement
! SELECT * FROM dropped_objects WHERE type = 'schema';
!   type  | schema |   object   
! --------+--------+------------
!  schema |        | schema_two
!  schema |        | schema_one
!  schema |        | audit_tbls
! (3 rows)
! 
! DROP ROLE regression_bob;
! DROP EVENT TRIGGER regress_event_trigger_drop_objects;
*** a/src/test/regress/sql/event_trigger.sql
--- b/src/test/regress/sql/event_trigger.sql
***************
*** 97,106 **** drop event trigger regress_event_trigger;
  -- should fail, regression_bob owns regress_event_trigger2/3
  drop role regression_bob;
  
  -- these are all OK; the second one should emit a NOTICE
  drop event trigger if exists regress_event_trigger2;
  drop event trigger if exists regress_event_trigger2;
  drop event trigger regress_event_trigger3;
  drop event trigger regress_event_trigger_end;
! drop function test_event_trigger();
! drop role regression_bob;
--- 97,169 ----
  -- should fail, regression_bob owns regress_event_trigger2/3
  drop role regression_bob;
  
+ -- cleanup before next test
  -- these are all OK; the second one should emit a NOTICE
  drop event trigger if exists regress_event_trigger2;
  drop event trigger if exists regress_event_trigger2;
  drop event trigger regress_event_trigger3;
  drop event trigger regress_event_trigger_end;
! 
! -- test support for dropped objects
! CREATE SCHEMA schema_one authorization regression_bob;
! CREATE SCHEMA schema_two authorization regression_bob;
! CREATE SCHEMA audit_tbls authorization regression_bob;
! SET SESSION AUTHORIZATION regression_bob;
! 
! CREATE TABLE schema_one.table_one(a int);
! CREATE TABLE schema_one."table two"(a int);
! CREATE TABLE schema_one.table_three(a int);
! CREATE TABLE audit_tbls.schema_one_table_two(the_value text);
! 
! CREATE TABLE schema_two.table_two(a int);
! CREATE TABLE schema_two.table_three(a int, b text);
! CREATE TABLE audit_tbls.schema_two_table_three(the_value text);
! 
! CREATE OR REPLACE FUNCTION schema_two.add(int, int) RETURNS int LANGUAGE plpgsql
!   CALLED ON NULL INPUT
!   AS $$ BEGIN RETURN coalesce($1,0) + coalesce($2,0); END; $$;
! CREATE AGGREGATE schema_two.newton
!   (BASETYPE = int, SFUNC = schema_two.add, STYPE = int);
! 
! RESET SESSION AUTHORIZATION;
! 
! CREATE TABLE dropped_objects (
! 	type text,
! 	schema text,
! 	object text);
! 
! CREATE OR REPLACE FUNCTION test_evtrig_dropped_objects() RETURNS event_trigger
! LANGUAGE plpgsql AS $$
! DECLARE
!     obj record;
! BEGIN
!     FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()
!     LOOP
!         IF obj.object_type = 'table' THEN
!                 EXECUTE format('DROP TABLE IF EXISTS audit_tbls.%I',
! 					format('%s_%s', obj.schema_name, obj.object_name));
!         END IF;
! 
! 	INSERT INTO dropped_objects
! 		(type, schema, object) VALUES
! 		(obj.object_type, obj.schema_name, obj.object_identity);
!     END LOOP;
! END
! $$;
! 
! CREATE EVENT TRIGGER regress_event_trigger_drop_objects ON sql_drop
! 	WHEN TAG IN ('drop table', 'drop function', 'drop view',
! 		'drop owned', 'drop schema', 'alter table')
! 	EXECUTE PROCEDURE test_evtrig_dropped_objects();
! 
! ALTER TABLE schema_one.table_one DROP COLUMN a;
! DROP SCHEMA schema_one, schema_two CASCADE;
! 
! SELECT * FROM dropped_objects WHERE schema IS NULL OR schema <> 'pg_toast';
! 
! DROP OWNED BY regression_bob;
! SELECT * FROM dropped_objects WHERE type = 'schema';
! 
! DROP ROLE regression_bob;
! 
! DROP EVENT TRIGGER regress_event_trigger_drop_objects;
