diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
index 71241c822450e5199fb2e3a730925a7ef9aa57b0..36928413d15c5011e32b802bf299283904f82f4d 100644
--- a/doc/src/sgml/event-trigger.sgml
+++ b/doc/src/sgml/event-trigger.sgml
@@ -27,17 +27,19 @@
    <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.
+     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 - databases, roles, and tablespaces
-     - or for command targeting event triggers themselves.  The event trigger
+     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
@@ -45,6 +47,17 @@
      event occurs just after the execution of this same set of commands.
    </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
@@ -99,6 +112,7 @@
         <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,401 +120,487 @@
         <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>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 490d7106435510f3c87771d53c62ee9bbfd54719..db4e33f871deda7fdfb8b8d323d92305de7680ac 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15980,4 +15980,117 @@ FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();
         <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</>.
+   </para>
+
+   <para>
+    <function>pg_event_trigger_dropped_objects</> returns a list of all object
+    dropped by the command in whose <literal>sql_drop</> event it is called.
+    If called in any other context,
+    <function>pg_event_trigger_dropped_objects</> raises an error.
+    <function>pg_event_trigger_dropped_objects</> 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; otherwise <literal>NULL</>.
+         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>
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index ddf199049ef48032beff595b57dc7ba98c072a12..286137c2513a47124846fc05ffc791857b0ac6de 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -190,6 +190,44 @@ static bool stack_address_present_add_flags(const ObjectAddress *object,
 								ObjectAddressStack *stack);
 
 
+/*
+ * 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).
@@ -215,7 +253,6 @@ performDeletion(const ObjectAddress *object,
 {
 	Relation	depRel;
 	ObjectAddresses *targetObjects;
-	int			i;
 
 	/*
 	 * We save some cycles by opening pg_depend just once and passing the
@@ -250,15 +287,8 @@ 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);
-	}
+	/* do the deed */
+	deleteObjectsInList(targetObjects, &depRel, flags);
 
 	/* And clean up */
 	free_object_addresses(targetObjects);
@@ -332,15 +362,8 @@ 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);
-	}
+	/* do the deed */
+	deleteObjectsInList(targetObjects, &depRel, flags);
 
 	/* And clean up */
 	free_object_addresses(targetObjects);
@@ -356,6 +379,10 @@ performMultipleDeletions(const ObjectAddresses *objects,
  * 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,
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index fbe8f49a9e71c2fea6b4915ac2f7f1977747c588..ed5240d63b0192ddb27dca7343dd8e98ba324245 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -19,14 +19,17 @@
 #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,6 +42,17 @@
 #include "utils/syscache.h"
 #include "tcop/utility.h"
 
+
+typedef struct EventTriggerQueryState
+{
+	slist_head	SQLDropList;
+	bool		in_sql_drop;
+	MemoryContext	cxt;
+	struct EventTriggerQueryState *previous;
+} EventTriggerQueryState;
+
+EventTriggerQueryState	*currentEventTriggerState = NULL;
+
 typedef struct
 {
 	const char	   *obtypename;
@@ -89,6 +103,17 @@ static event_trigger_support_data event_trigger_support[] = {
 	{ 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,7 +152,8 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
 
 	/* Validate event name. */
 	if (strcmp(stmt->eventname, "ddl_command_start") != 0 &&
-		strcmp(stmt->eventname, "ddl_command_end") != 0)
+		strcmp(stmt->eventname, "ddl_command_end") != 0 &&
+		strcmp(stmt->eventname, "sql_drop") != 0)
 		ereport(ERROR,
 			(errcode(ERRCODE_SYNTAX_ERROR),
 			 errmsg("unrecognized event name \"%s\"",
@@ -151,7 +177,10 @@ CreateEventTrigger(CreateEventTrigStmt *stmt)
 	}
 
 	/* Validate tag list, if any. */
-	if (strcmp(stmt->eventname, "ddl_command_start") == 0 && tags != NULL)
+	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,7 +249,8 @@ 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)
+		pg_strcasecmp(tag, "ALTER LARGE OBJECT") == 0 ||
+		pg_strcasecmp(tag, "DROP OWNED") == 0)
 		return EVENT_TRIGGER_COMMAND_TAG_OK;
 
 	/*
@@ -568,35 +598,19 @@ filter_event_trigger(const char **tag, EventTriggerCacheItem  *item)
 }
 
 /*
- * Fire ddl_command_start triggers.
+ * 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.
  */
-void
-EventTriggerDDLCommandStart(Node *parsetree)
+static List *
+EventTriggerCommonSetup(Node *parsetree,
+						EventTriggerEvent event, const char *eventstr,
+						EventTriggerData *trigdata)
 {
+	const char *tag;
 	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;
+	List	   *runlist = NIL;
 
 	/*
 	 * We want the list of command tags for which this procedure is actually
@@ -624,9 +638,9 @@ EventTriggerDDLCommandStart(Node *parsetree)
 #endif
 
 	/* Use cache to find triggers for this event; fast exit if none. */
-	cachelist = EventCacheLookup(EVT_DDLCommandStart);
-	if (cachelist == NULL)
-		return;
+	cachelist = EventCacheLookup(event);
+	if (cachelist == NIL)
+		return NIL;
 
 	/* Get the command tag. */
 	tag = CreateCommandTag(parsetree);
@@ -649,11 +663,51 @@ EventTriggerDDLCommandStart(Node *parsetree)
 		}
 	}
 
-	/* Construct event trigger data. */
-	trigdata.type = T_EventTriggerData;
-	trigdata.event = "ddl_command_start";
-	trigdata.parsetree = parsetree;
-	trigdata.tag = tag;
+	/* 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,10 +728,7 @@ EventTriggerDDLCommandStart(Node *parsetree)
 void
 EventTriggerDDLCommandEnd(Node *parsetree)
 {
-	List	   *cachelist;
-	List	   *runlist = NIL;
-	ListCell   *lc;
-	const char *tag;
+	List	   *runlist;
 	EventTriggerData	trigdata;
 
 	/*
@@ -687,53 +738,61 @@ EventTriggerDDLCommandEnd(Node *parsetree)
 	if (!IsUnderPostmaster)
 		return;
 
+	runlist = EventTriggerCommonSetup(parsetree,
+									  EVT_DDLCommandEnd, "ddl_command_end",
+									  &trigdata);
+	if (runlist == NIL)
+		return;
+
 	/*
-	 * See EventTriggerDDLCommandStart for a discussion about why this check is
-	 * important.
-	 *
+	 * Make sure anything the main command did will be visible to the
+	 * event triggers.
 	 */
-#ifdef USE_ASSERT_CHECKING
-	if (assert_enabled)
-	{
-		const char *dbgtag;
+	CommandCounterIncrement();
 
-		dbgtag = CreateCommandTag(parsetree);
-		if (check_ddl_tag(dbgtag) != EVENT_TRIGGER_COMMAND_TAG_OK)
-			elog(ERROR, "unexpected command tag \"%s\"", dbgtag);
-	}
-#endif
+	/* Run the triggers. */
+	EventTriggerInvoke(runlist, &trigdata);
 
-	/* Use cache to find triggers for this event; fast exit if none. */
-	cachelist = EventCacheLookup(EVT_DDLCommandEnd);
-	if (cachelist == NULL)
-		return;
+	/* Cleanup. */
+	list_free(runlist);
+}
 
-	/* Get the command tag. */
-	tag = CreateCommandTag(parsetree);
+/*
+ * Fire sql_drop triggers.
+ */
+void
+EventTriggerSQLDrop(Node *parsetree)
+{
+	List	   *runlist;
+	EventTriggerData	trigdata;
 
 	/*
-	 * 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.
+	 * See EventTriggerDDLCommandStart for a discussion about why event
+	 * triggers are disabled in single user mode.
 	 */
-	foreach (lc, cachelist)
-	{
-		EventTriggerCacheItem  *item = lfirst(lc);
+	if (!IsUnderPostmaster)
+		return;
 
-		if (filter_event_trigger(&tag, item))
-		{
-			/* We must plan to fire this trigger. */
-			runlist = lappend_oid(runlist, item->fnoid);
-		}
-	}
+	/*
+	 * 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;
 
-	/* Construct event trigger data. */
-	trigdata.type = T_EventTriggerData;
-	trigdata.event = "ddl_command_end";
-	trigdata.parsetree = parsetree;
-	trigdata.tag = tag;
+	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
@@ -741,8 +800,27 @@ EventTriggerDDLCommandEnd(Node *parsetree)
 	 */
 	CommandCounterIncrement();
 
+	/*
+	 * Make sure pg_event_trigger_dropped_objects only works when running these
+	 * triggers.  Use PG_TRY to ensure in_sql_drop is reset even when one
+	 * trigger fails.  (This is perhaps not necessary, as the currentState
+	 * variable will be removed shortly by our caller, but it seems better to
+	 * play safe.)
+	 */
+	currentEventTriggerState->in_sql_drop = true;
+
 	/* Run the triggers. */
-	EventTriggerInvoke(runlist, &trigdata);
+	PG_TRY();
+	{
+		EventTriggerInvoke(runlist, &trigdata);
+	}
+	PG_CATCH();
+	{
+		currentEventTriggerState->in_sql_drop = false;
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
+	currentEventTriggerState->in_sql_drop = false;
 
 	/* Cleanup. */
 	list_free(runlist);
@@ -832,3 +910,287 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 	}
 	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->in_sql_drop = false;
+
+	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;
+
+	/*
+	 * Protect this function from being called out of context
+	 */
+	if (!currentEventTriggerState ||
+		!currentEventTriggerState->in_sql_drop)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("%s can only be called in a sql_drop event trigger function",
+						"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;
+}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 0d82141fef6cb9ae697ae0833febff15bf9236c9..ed8502c6e4ef32971998fe2ba3cf26fe466773fb 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4478,7 +4478,6 @@ DropTrigStmt:
  *
  *		QUERIES :
  *				CREATE EVENT TRIGGER ...
- *				DROP EVENT TRIGGER ...
  *				ALTER EVENT TRIGGER ...
  *
  *****************************************************************************/
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index a1c03f1f76386ec098784fe36c92214950004dc4..77b4e5368e7bab91690b8a668e3300aa333a539d 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -346,12 +346,13 @@ ProcessUtility(Node *parsetree,
 	do { \
 	    if (isCompleteQuery) \
         { \
-		    EventTriggerDDLCommandStart(parsetree); \
+			EventTriggerDDLCommandStart(parsetree); \
 		} \
 		fncall; \
         if (isCompleteQuery) \
         { \
-		    EventTriggerDDLCommandEnd(parsetree); \
+			EventTriggerSQLDrop(parsetree); \
+			EventTriggerDDLCommandEnd(parsetree); \
 		} \
 	} while (0)
 
@@ -366,10 +367,48 @@ ProcessUtility(Node *parsetree,
 		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,6 +425,8 @@ standard_ProcessUtility(Node *parsetree,
 	if (completionTag)
 		completionTag[0] = '\0';
 
+	UTILITY_BEGIN_QUERY(isCompleteQuery);
+
 	switch (nodeTag(parsetree))
 	{
 			/*
@@ -615,7 +656,10 @@ standard_ProcessUtility(Node *parsetree,
 				}
 
 				if (isCompleteQuery)
+				{
+					EventTriggerSQLDrop(parsetree);
 					EventTriggerDDLCommandEnd(parsetree);
+				}
 			}
 			break;
 
@@ -726,7 +770,10 @@ standard_ProcessUtility(Node *parsetree,
 
 				if (isCompleteQuery
 					&& EventTriggerSupportsObjectType(stmt->removeType))
+				{
+					EventTriggerSQLDrop(parsetree);
 					EventTriggerDDLCommandEnd(parsetree);
+				}
 
 				break;
 			}
@@ -856,6 +903,12 @@ standard_ProcessUtility(Node *parsetree,
 					ereport(NOTICE,
 						  (errmsg("relation \"%s\" does not exist, skipping",
 								  atstmt->relation->relname)));
+
+				if (isCompleteQuery)
+				{
+					EventTriggerSQLDrop(parsetree);
+					EventTriggerDDLCommandEnd(parsetree);
+				}
 			}
 			break;
 
@@ -1248,8 +1301,9 @@ standard_ProcessUtility(Node *parsetree,
 			break;
 
 		case T_DropOwnedStmt:
-			/* no event triggers for global objects */
-			DropOwnedObjects((DropOwnedStmt *) parsetree);
+			InvokeDDLCommandEventTriggers(
+				parsetree,
+				DropOwnedObjects((DropOwnedStmt *) parsetree));
 			break;
 
 		case T_ReassignOwnedStmt:
@@ -1372,6 +1426,8 @@ standard_ProcessUtility(Node *parsetree,
 				 (int) nodeTag(parsetree));
 			break;
 	}
+
+	UTILITY_END_QUERY();
 }
 
 /*
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index 94599aa44ba73367c3660f38ad30f648bcd0028d..700247e4741a7d42a8bf878323772fe70b6d48b6 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -345,7 +345,7 @@ 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 not, or if caller requests it, we need to qualify it.
 		 */
 		if (!force_qualify && FunctionIsVisible(procedure_oid))
 			nspname = NULL;
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 34c61280c27c08e0f82a0395d9f71d10aab4623b..bbd3ae369d3c005dcf595ad97e1bce3ee156b73a 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -169,6 +169,8 @@ BuildEventTriggerCache(void)
 			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;
 
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index cbc4673d1b497db7c7488eb8f6ddda38f2b521fa..0eb8eefb8ff03a17ad03ecfb53fe3c9f4c68da93 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201303201
+#define CATALOG_VERSION_NO	201303271
 
 #endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 4aee00233a9b06adea1976f107fb20d4e1a64de2..151987ec1d318a1be8260792e1fc06157fe65c65 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4693,6 +4693,9 @@ DATA(insert OID = 3473 (  spg_range_quad_leaf_consistent	PGNSP PGUID 12 1 0 0 0
 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,
diff --git a/src/include/commands/event_trigger.h b/src/include/commands/event_trigger.h
index 74c150bd0846f30f7755529b8fb55522569cd656..8ea99c19c998f28d554a0d241329ec3b8949ba97 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -13,13 +13,14 @@
 #ifndef EVENT_TRIGGER_H
 #define EVENT_TRIGGER_H
 
+#include "catalog/objectaddress.h"
 #include "catalog/pg_event_trigger.h"
 #include "nodes/parsenodes.h"
 
 typedef struct EventTriggerData
 {
 	NodeTag		type;
-	char	   *event;				/* event name */
+	const char *event;				/* event name */
 	Node	   *parsetree;			/* parse tree */
 	const char *tag;				/* command tag */
 } EventTriggerData;
@@ -42,5 +43,11 @@ extern void AlterEventTriggerOwner_oid(Oid, Oid newOwnerId);
 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 */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index cd8ac9462b53ef3f9c4bd9fe7469ad24a11f8e13..e71876502e1e6e56aab6066ee7388383ce9c0a59 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1151,6 +1151,9 @@ extern Datum pg_identify_object(PG_FUNCTION_ARGS);
 /* 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);
diff --git a/src/include/utils/evtcache.h b/src/include/utils/evtcache.h
index c230995212dc1ff0b40f8c769048734d8b5ca31d..945e5b53cb811cc207a2073ba065d018ef0ea0d4 100644
--- a/src/include/utils/evtcache.h
+++ b/src/include/utils/evtcache.h
@@ -19,7 +19,8 @@
 typedef enum
 {
 	EVT_DDLCommandStart,
-	EVT_DDLCommandEnd
+	EVT_DDLCommandEnd,
+	EVT_SQLDrop
 } EventTriggerEvent;
 
 typedef struct
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index bf020de16f698607bfdc0a227bfea15b810fbaea..060cd722bef034726205db0b6bdcf72fc71c2f71 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -93,11 +93,204 @@ 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
+-- 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;
-drop function test_event_trigger();
-drop role regression_bob;
+-- 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 undroppable_objs (
+	object_type text,
+	object_identity text
+);
+INSERT INTO undroppable_objs VALUES
+('table', 'schema_one.table_three'),
+('table', 'audit_tbls.schema_two_table_three');
+CREATE TABLE dropped_objects (
+	type text,
+	schema text,
+	object text
+);
+-- This tests errors raised within event triggers; the one in audit_tbls
+-- uses 2nd-level recursive invocation via test_evtrig_dropped_objects().
+CREATE OR REPLACE FUNCTION undroppable() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+DECLARE
+	obj record;
+BEGIN
+	PERFORM 1 FROM pg_tables WHERE tablename = 'undroppable_objs';
+	IF NOT FOUND THEN
+		RAISE NOTICE 'table undroppable_objs not found, skipping';
+		RETURN;
+	END IF;
+	FOR obj IN
+		SELECT * FROM pg_event_trigger_dropped_objects() JOIN
+			undroppable_objs USING (object_type, object_identity)
+	LOOP
+		RAISE EXCEPTION 'object % of type % cannot be dropped',
+			obj.object_identity, obj.object_type;
+	END LOOP;
+END;
+$$;
+CREATE EVENT TRIGGER undroppable ON sql_drop
+	EXECUTE PROCEDURE undroppable();
+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
+ERROR:  object audit_tbls.schema_two_table_three of type table cannot be dropped
+CONTEXT:  SQL statement "DROP TABLE IF EXISTS audit_tbls.schema_two_table_three"
+PL/pgSQL function test_evtrig_dropped_objects() line 8 at EXECUTE statement
+DELETE FROM undroppable_objs WHERE object_identity = 'audit_tbls.schema_two_table_three';
+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
+ERROR:  object schema_one.table_three of type table cannot be dropped
+DELETE FROM undroppable_objs WHERE object_identity = 'schema_one.table_three';
+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;
+DROP EVENT TRIGGER undroppable;
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index a07dcd7554623458dfac0227c77706ace7e82d62..11d2ce5c133dd99e9b17c0eedd17eb14fcc792cf 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -97,10 +97,112 @@ drop event trigger regress_event_trigger;
 -- 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;
-drop function test_event_trigger();
-drop role regression_bob;
+
+-- 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 undroppable_objs (
+	object_type text,
+	object_identity text
+);
+INSERT INTO undroppable_objs VALUES
+('table', 'schema_one.table_three'),
+('table', 'audit_tbls.schema_two_table_three');
+
+CREATE TABLE dropped_objects (
+	type text,
+	schema text,
+	object text
+);
+
+-- This tests errors raised within event triggers; the one in audit_tbls
+-- uses 2nd-level recursive invocation via test_evtrig_dropped_objects().
+CREATE OR REPLACE FUNCTION undroppable() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+DECLARE
+	obj record;
+BEGIN
+	PERFORM 1 FROM pg_tables WHERE tablename = 'undroppable_objs';
+	IF NOT FOUND THEN
+		RAISE NOTICE 'table undroppable_objs not found, skipping';
+		RETURN;
+	END IF;
+	FOR obj IN
+		SELECT * FROM pg_event_trigger_dropped_objects() JOIN
+			undroppable_objs USING (object_type, object_identity)
+	LOOP
+		RAISE EXCEPTION 'object % of type % cannot be dropped',
+			obj.object_identity, obj.object_type;
+	END LOOP;
+END;
+$$;
+
+CREATE EVENT TRIGGER undroppable ON sql_drop
+	EXECUTE PROCEDURE undroppable();
+
+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;
+DELETE FROM undroppable_objs WHERE object_identity = 'audit_tbls.schema_two_table_three';
+DROP SCHEMA schema_one, schema_two CASCADE;
+DELETE FROM undroppable_objs WHERE object_identity = 'schema_one.table_three';
+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;
+DROP EVENT TRIGGER undroppable;