diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
index 71241c8..5263238 100644
--- a/doc/src/sgml/event-trigger.sgml
+++ b/doc/src/sgml/event-trigger.sgml
@@ -27,9 +27,10 @@
    <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>
@@ -46,6 +47,16 @@
    </para>
 
    <para>
+    The <literal>sql_drop</> event occurs just before
+    the <literal>ddl_command_end</> event trigger for a <literal>DROP</>
+    operation, once per object. Note that happens after the objects have
+    been deleted, so no catalog lookup is possible. To list all objects that
+    have been deleted, use the Set Returning
+    Function <literal>pg_event_trigger_dropped_objects()</> from
+    your <literal>ddl_command_end</> event trigger code.
+   </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,6 +110,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 +118,481 @@
         <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>-</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>-</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 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 92a79d3..19e1921 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15688,9 +15688,54 @@ FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();
       choose a trigger name that comes after the name of any other trigger
       you might have on the table.
     </para>
-    <para>
+     <para>
        For more information about creating triggers, see
         <xref linkend="SQL-CREATETRIGGER">.
     </para>
   </sect1>
+
+  <sect1 id="functions-trigger">
+   <title>Event Trigger Functions</title>
+
+   <indexterm>
+     <primary>pg_dropped_objects</primary>
+   </indexterm>
+
+   <para>
+    Currently <productname>PostgreSQL</> provides one built in event trigger
+    helper function, <function>pg_event_trigger_dropped_objects</>, which
+    will list all object dropped by a <listeral>DROP</> command. That
+    listing includes multiple targets of the command, as in <command>DROP
+    TABLE a, b, c;</command> and objects dropped because of
+    a <literal>CASCADE</> dependency.
+    </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_sql_drop()
+        returns event_trigger as $$
+DECLARE
+    obj record;
+BEGIN
+    RAISE NOTICE 'test_event_trigger: % %', tg_event, tg_tag;
+
+    FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects()
+    LOOP
+        RAISE NOTICE 'sql_drop event trigger: % % % %',
+                     obj.classId::regclass,
+                     obj.classId, obj.objid, obj.objsubid;
+    END LOOP;
+END
+$$ language plpgsql;
+</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 d203725..d670f5e 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -261,6 +261,44 @@ performDeletion(const ObjectAddress *object,
 						   object);
 
 	/*
+	 * When event triggers have been configured to run on drop events,
+	 * we run the triggers, then recompute the list of objects to delete; we
+	 * can then verify that the list of objects to delete after the triggers
+	 * are run is the same as the list of objects we determined at the
+	 * beggining.  If they differ, raise an error, aborting the transaction.
+	 * This is to ensure that the triggers don't try to modify what we're
+	 * dropping, because that could leave us with inconsistent catalogs.
+	 *
+	 * See performMultipleDeletion if you change this.
+	 */
+	if (AreThereDDLDropEventTriggers())
+	{
+		ObjectAddresses *recheckObjects;
+
+		/* Fire DDL_DROP event trigger here, using targetObjects as list */
+		EventTriggerDDLDrop(targetObjects);
+
+		CommandCounterIncrement();
+
+		recheckObjects = new_object_addresses();
+
+		findDependentObjects(object,
+							 DEPFLAG_ORIGINAL,
+							 NULL,		/* empty stack */
+							 recheckObjects,
+							 NULL,
+							 &depRel);
+
+		/* verify that the lists contain the same elements. */
+		if (!object_addresses_identical(targetObjects, recheckObjects))
+			ereport(ERROR,
+						(errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
+						 errmsg("event trigger function on ddl_drop event must not change list of objects to drop")));
+
+		free_object_addresses(recheckObjects);
+	}
+
+	/*
 	 * Delete all the objects in the proper order.
 	 */
 	for (i = 0; i < targetObjects->numrefs; i++)
@@ -343,6 +381,43 @@ performMultipleDeletions(const ObjectAddresses *objects,
 						   (objects->numrefs == 1 ? objects->refs : NULL));
 
 	/*
+	 * See comment in performDeletion about ddl_drop event triggers
+	 */
+	if (AreThereDDLDropEventTriggers())
+	{
+		ObjectAddresses *recheckObjects;
+
+		/* Fire DDL_DROP event trigger here, using targetObjects as list */
+		EventTriggerDDLDrop(targetObjects);
+
+		CommandCounterIncrement();
+
+		recheckObjects = new_object_addresses();
+
+		for (i = 0; i < objects->numrefs; i++)
+		{
+			const ObjectAddress *thisobj = objects->refs + i;
+
+			AcquireDeletionLock(thisobj, flags);
+
+			findDependentObjects(thisobj,
+								 DEPFLAG_ORIGINAL,
+								 NULL,		/* empty stack */
+								 recheckObjects,
+								 objects,
+								 &depRel);
+		}
+
+		/* verify that the lists contain the same elements. */
+		if (!object_addresses_identical(targetObjects, recheckObjects))
+			ereport(ERROR,
+						(errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
+						 errmsg("event trigger function on ddl_drop event must not change list of objects to drop")));
+
+		free_object_addresses(recheckObjects);
+	}
+
+	/*
 	 * Delete all the objects in the proper order.
 	 */
 	for (i = 0; i < targetObjects->numrefs; i++)
@@ -366,6 +441,9 @@ 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.
+ *
+ * XXX this currently does not fire DDL_DROP triggers.  If more callers are
+ * added, this might need to be reconsidered.
  */
 void
 deleteWhatDependsOn(const ObjectAddress *object,
@@ -2175,6 +2253,76 @@ record_object_address_dependencies(const ObjectAddress *depender,
 							   behavior);
 }
 
+static ObjectAddresses *
+object_addresses_copy(const ObjectAddresses *src)
+{
+	ObjectAddresses *dst;
+
+	dst = palloc(sizeof(ObjectAddresses));
+
+	dst->numrefs = src->numrefs;
+	dst->maxrefs = src->numrefs;
+	dst->refs = (ObjectAddress *)
+		palloc(dst->maxrefs * sizeof(ObjectAddress));
+	dst->extras = NULL;
+
+	memcpy(dst->refs, src->refs, sizeof(ObjectAddress) * src->numrefs);
+
+	return dst;
+}
+
+/*
+ * Compare two ObjectAddresses arrays and determine whether they have exactly
+ * the same elements.
+ *
+ * We don't scribble on the const argument, but the second one might be sorted
+ * after this function is called.
+ */
+bool
+object_addresses_identical(const ObjectAddresses *a,
+						   ObjectAddresses *b)
+{
+	ObjectAddresses *a_copy;
+	int		i;
+
+	/* If they don't have the same number of elements, return quickly */
+	if (a->numrefs != b->numrefs)
+		return false;
+
+	/* prepare a copy we can scribble on */
+	a_copy = object_addresses_copy(a);
+
+	qsort((void *) b->refs, b->numrefs, sizeof(ObjectAddress),
+		  object_address_comparator);
+	qsort((void *) a_copy->refs, a_copy->numrefs, sizeof(ObjectAddress),
+		  object_address_comparator);
+
+	for (i = 0; i < a->numrefs; i++)
+	{
+		if (object_address_comparator((void *) (a_copy->refs + i),
+									  (void *) (b->refs + i)) != 0)
+		{
+			free_object_addresses(a_copy);
+			return false;
+		}
+	}
+
+	free_object_addresses(a_copy);
+	return true;
+}
+
+int
+get_object_addresses_numelements(const ObjectAddresses *addresses)
+{
+	return addresses->numrefs;
+}
+
+ObjectAddress *
+get_object_addresses_element(const ObjectAddresses *addresses, int i)
+{
+	return addresses->refs + i;
+}
+
 /*
  * Clean up when done with an ObjectAddresses array.
  */
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 18b3753..5697a1b 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -18,13 +18,18 @@
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
+#include "catalog/pg_authid.h"
+#include "catalog/pg_auth_members.h"
+#include "catalog/pg_database.h"
 #include "catalog/pg_event_trigger.h"
 #include "catalog/pg_proc.h"
+#include "catalog/pg_tablespace.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 "miscadmin.h"
@@ -39,6 +44,11 @@
 #include "utils/syscache.h"
 #include "tcop/utility.h"
 
+/* Globally visible state variables */
+bool EventTriggerDDLDropInProgress = false;
+ObjectAddresses *EventTriggerDDLDropList = NULL;
+bool isIgnoredDDLDropEvent = false;
+
 typedef struct
 {
 	const char	   *obtypename;
@@ -126,7 +136,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, "ddl_drop") != 0)
 		ereport(ERROR,
 			(errcode(ERRCODE_SYNTAX_ERROR),
 			 errmsg("unrecognized event name \"%s\"",
@@ -150,8 +161,17 @@ 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"))
+		&& tags != NULL)
+	{
 		validate_ddl_tags("tag", tags);
+	}
+	if ((strcmp(stmt->eventname, "ddl_drop") == 0) &&
+		tags != NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("DDL_DROP command triggers do not support tag filtering")));
 
 	/*
 	 * Give user a nice error message if an event trigger of the same name
@@ -742,6 +762,105 @@ EventTriggerDDLCommandEnd(Node *parsetree)
 }
 
 /*
+ * Return whether there are any DDL_DROP triggers installed.
+ *
+ * This is useful because there is a cost to running with them enabled.
+ */
+bool
+AreThereDDLDropEventTriggers(void)
+{
+	return list_length(EventCacheLookup(EVT_DDLDrop)) > 0;
+}
+
+/*
+ * Fire ddl_drop event triggers for the given dropped objects.
+ */
+void
+EventTriggerDDLDrop(ObjectAddresses *objects)
+{
+	List	   *cachelist;
+	List	   *runlist = NIL;
+	ListCell   *lc;
+	EventTriggerData	trigdata;
+	ObjectAddresses *save_EventTriggerDDLDropList;
+	bool		save_EventTriggerDDLDropInProgress;
+
+	/*
+	 * See EventTriggerDDLCommandStart for a discussion about why event
+	 * triggers are disabled in single user mode.
+	 */
+	if (!IsUnderPostmaster)
+		return;
+
+	/* these do not fire when running commands for unsupported object types */
+	if (isIgnoredDDLDropEvent)
+		return;
+
+	/*
+	 * Other command triggers have a consistency check for the tag here.
+	 * We don't do that because we don't have the parsetree, for one thing,
+	 * and we don't support filtering by tags for DDL_DROP events anyway.
+	 */
+
+	cachelist = EventCacheLookup(EVT_DDLDrop);
+	if (cachelist == NIL)
+		return;
+
+	/*
+	 * No tag filtering here; just copy the function OIDs into the new list.
+	 */
+	foreach(lc, cachelist)
+	{
+		EventTriggerCacheItem  *item = lfirst(lc);
+
+		runlist = lappend_oid(runlist, item->fnoid);
+	}
+
+	/* Construct event trigger data. */
+	trigdata.type = T_EventTriggerData;
+	trigdata.event = "ddl_drop";
+	trigdata.parsetree = NULL;	/* no parse tree here */
+	trigdata.tag = NULL;		/* no command tag, either */
+
+	/*
+	 * We purposefully do not call CommandCounterIncrement here.
+	 */
+
+	/*
+	 * Now run the triggers.  Store the list of dropped objects where the
+	 * triggers can see it, but save the existing list, so that we can restore
+	 * it afterwards, in case we've been called reentrantly.
+	 *
+	 * Use a PG_TRY block here to ensure we reset the global variables properly
+	 * even in case of an error.
+	 */
+	save_EventTriggerDDLDropInProgress = EventTriggerDDLDropInProgress;
+	save_EventTriggerDDLDropList = EventTriggerDDLDropList;
+
+	PG_TRY();
+	{
+		EventTriggerDDLDropInProgress = true;
+		EventTriggerDDLDropList = objects;
+
+		EventTriggerInvoke(runlist, &trigdata);
+	}
+	PG_CATCH();
+	{
+		EventTriggerDDLDropInProgress = save_EventTriggerDDLDropInProgress;
+		EventTriggerDDLDropList = save_EventTriggerDDLDropList;
+
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
+
+	EventTriggerDDLDropInProgress = save_EventTriggerDDLDropInProgress;
+	EventTriggerDDLDropList = save_EventTriggerDDLDropList;
+
+	/* Cleanup. */
+	list_free(runlist);
+}
+
+/*
  * Invoke each event trigger in a list of event triggers.
  */
 static void
@@ -806,6 +925,8 @@ EventTriggerInvoke(List *fn_oid_list, EventTriggerData *trigdata)
 
 /*
  * Do event triggers support this object type?
+ *
+ * Note: see also the exclusion list in pg_event_trigger_dropped_objects.
  */
 bool
 EventTriggerSupportsObjectType(ObjectType obtype)
@@ -825,3 +946,97 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 	}
 	return true;
 }
+
+/*
+ * 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;
+	int					 i;
+
+	/*
+	 * This function is meant to be called from within any Event Trigger in
+	 * order to get the list of objects dropped, if any.
+	 */
+	if (!EventTriggerDDLDropInProgress)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("%s can only be called from an 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);
+
+	for (i = 0;
+		 i < get_object_addresses_numelements(EventTriggerDDLDropList);
+		 i++)
+	{
+		ObjectAddress *object;
+		Datum		values[3];
+		bool		nulls[3];
+
+		object = get_object_addresses_element(EventTriggerDDLDropList, i);
+
+		/*
+		 * Skip objects which are not supposed to fire event triggers.  This
+		 * list must match EventTriggerSupportsObjectType.
+		 */
+		if (object->classId == EventTriggerRelationId ||
+			object->classId == DatabaseRelationId ||
+			object->classId == TableSpaceRelationId ||
+			object->classId == AuthIdRelationId ||
+			object->classId == AuthMemRelationId)
+			continue;
+
+		/* Emit result row */
+		memset(values, 0, sizeof(values));
+		memset(nulls, 0, sizeof(nulls));
+
+		/* classid */
+		values[0] = ObjectIdGetDatum(object->classId);
+
+		/* objid */
+		values[1] = ObjectIdGetDatum(object->objectId);
+
+		/* objsubid */
+		values[2] = Int32GetDatum(object->objectSubId);
+
+		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+	}
+
+	/* clean up and return the tuplestore */
+	tuplestore_donestoring(tupstore);
+
+	return (Datum) 0;
+}
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 8904c6f..66bce04 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -697,29 +697,46 @@ standard_ProcessUtility(Node *parsetree,
 		case T_DropStmt:
 			{
 				DropStmt   *stmt = (DropStmt *) parsetree;
+				bool		save_isIgnoredDDLDropEvent;
+
 
 				if (isCompleteQuery
 					&& EventTriggerSupportsObjectType(stmt->removeType))
 					EventTriggerDDLCommandStart(parsetree);
 
-				switch (stmt->removeType)
+				save_isIgnoredDDLDropEvent = isIgnoredDDLDropEvent;
+				isIgnoredDDLDropEvent =
+					!EventTriggerSupportsObjectType(stmt->removeType);
+
+				PG_TRY();
 				{
-					case OBJECT_INDEX:
-						if (stmt->concurrent)
-							PreventTransactionChain(isTopLevel,
-													"DROP INDEX CONCURRENTLY");
-						/* fall through */
+					switch (stmt->removeType)
+					{
+						case OBJECT_INDEX:
+							if (stmt->concurrent)
+								PreventTransactionChain(isTopLevel,
+														"DROP INDEX CONCURRENTLY");
+							/* fall through */
+
+						case OBJECT_TABLE:
+						case OBJECT_SEQUENCE:
+						case OBJECT_VIEW:
+						case OBJECT_FOREIGN_TABLE:
+							RemoveRelations((DropStmt *) parsetree);
+							break;
+						default:
+							RemoveObjects((DropStmt *) parsetree);
+							break;
+					}
+				}
+				PG_CATCH();
+				{
+					isIgnoredDDLDropEvent = save_isIgnoredDDLDropEvent;
 
-					case OBJECT_TABLE:
-					case OBJECT_SEQUENCE:
-					case OBJECT_VIEW:
-					case OBJECT_FOREIGN_TABLE:
-						RemoveRelations((DropStmt *) parsetree);
-						break;
-					default:
-						RemoveObjects((DropStmt *) parsetree);
-						break;
+					PG_RE_THROW();
 				}
+				PG_END_TRY();
+				isIgnoredDDLDropEvent = save_isIgnoredDDLDropEvent;
 
 				if (isCompleteQuery
 					&& EventTriggerSupportsObjectType(stmt->removeType))
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index 34c6128..0fec1b1 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -68,7 +68,7 @@ EventCacheLookup(EventTriggerEvent event)
 	if (EventTriggerCacheState != ETCS_VALID)
 		BuildEventTriggerCache();
 	entry = hash_search(EventTriggerCache, &event, HASH_FIND, NULL);
-	return entry != NULL ? entry->triggerlist : NULL;
+	return entry != NULL ? entry->triggerlist : NIL;
 }
 
 /*
@@ -169,6 +169,8 @@ BuildEventTriggerCache(void)
 			event = EVT_DDLCommandStart;
 		else if (strcmp(evtevent, "ddl_command_end") == 0)
 			event = EVT_DDLCommandEnd;
+		else if (strcmp(evtevent, "ddl_drop") == 0)
+			event = EVT_DDLDrop;
 		else
 			continue;
 
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 8e0837f..c1fbe05 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -191,6 +191,14 @@ extern void record_object_address_dependencies(const ObjectAddress *depender,
 								   ObjectAddresses *referenced,
 								   DependencyType behavior);
 
+extern int get_object_addresses_numelements(const ObjectAddresses *addresses);
+
+extern bool object_addresses_identical(const ObjectAddresses *a,
+						   ObjectAddresses *b);
+
+extern ObjectAddress *get_object_addresses_element(const ObjectAddresses *addresses,
+						   int i);
+
 extern void free_object_addresses(ObjectAddresses *addrs);
 
 /* in pg_depend.c */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index d9f50d2..372e50a 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4679,7 +4679,9 @@ DESCR("SP-GiST support for quad tree over range");
 DATA(insert OID = 3473 (  spg_range_quad_leaf_consistent	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "2281 2281" _null_ _null_ _null_ _null_  spg_range_quad_leaf_consistent _null_ _null_ _null_ ));
 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}" "{o,o,o}" "{classid, objid, objsubid}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
+DESCR("list an extension's version update paths");
 /*
  * 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 74c150b..200d649 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -13,9 +13,29 @@
 #ifndef EVENT_TRIGGER_H
 #define EVENT_TRIGGER_H
 
+#include "catalog/dependency.h"
+#include "catalog/objectaddress.h"
 #include "catalog/pg_event_trigger.h"
 #include "nodes/parsenodes.h"
 
+/*
+ * EventTriggerDDLDropList is set to the objects to delete when a DDLDrop
+ * event happens (basically any command that drops objects).  Note that all
+ * object types are here, not only those supported as
+ * EventTriggerSupportsObjectType().  FIXME we need to filter this somehow.
+ *
+ * EventTriggerDDLDropInProgress will be set in a DDLDrop event.  That's used
+ * to allow access to the DDLDropList.
+ *
+ * isIgnoredDDLDropEvent is set when running DROP commands that affect
+ * unsupported object types.  ddl_drop triggers must not be called when it
+ * is set.
+ */
+extern bool EventTriggerDDLDropInProgress;
+extern ObjectAddresses *EventTriggerDDLDropList;
+extern bool isIgnoredDDLDropEvent;
+
+
 typedef struct EventTriggerData
 {
 	NodeTag		type;
@@ -42,5 +62,7 @@ extern void AlterEventTriggerOwner_oid(Oid, Oid newOwnerId);
 extern bool EventTriggerSupportsObjectType(ObjectType obtype);
 extern void EventTriggerDDLCommandStart(Node *parsetree);
 extern void EventTriggerDDLCommandEnd(Node *parsetree);
+extern bool AreThereDDLDropEventTriggers(void);
+extern void EventTriggerDDLDrop(ObjectAddresses *objects);
 
 #endif   /* EVENT_TRIGGER_H */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 533539c..d51b829 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1146,6 +1146,9 @@ extern Datum pg_describe_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 c230995..d8ce1ec 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_DDLDrop
 } EventTriggerEvent;
 
 typedef struct
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 70e67d9..772c830 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -803,9 +803,18 @@ plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata)
 	var->freeval = true;
 
 	var = (PLpgSQL_var *) (estate.datums[func->tg_tag_varno]);
-	var->value = CStringGetTextDatum(trigdata->tag);
-	var->isnull = false;
-	var->freeval = true;
+	if (trigdata->tag)
+	{
+		var->value = CStringGetTextDatum(trigdata->tag);
+		var->isnull = false;
+		var->freeval = true;
+	}
+	else
+	{
+		var->value = (Datum) 0;
+		var->isnull = true;
+		var->freeval = false;
+	}
 
 	/*
 	 * Let the instrumentation plugin peek at this function
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index bf020de..38e2e42 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -99,5 +99,205 @@ 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;
+-- now test the ddl_drop event trigger
+CREATE FUNCTION test_evtrig_dropped_objs() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+DECLARE
+obj record;
+col record;
+schema oid;
+catalog text;
+colname text;
+BEGIN
+	RAISE NOTICE 'function test_evtrig_dropped_objs starting: event %', tg_event;
+
+	FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() ORDER BY 1, 2
+	LOOP
+		-- skip reporting for objects in pg_toast; they contain OID references
+		-- in their names
+		IF obj.classid::regclass = 'pg_catalog.pg_class'::regclass THEN
+			catalog = 'pg_class';
+			colname = 'relnamespace';
+		END IF;
+		IF obj.classid::regclass = 'pg_catalog.pg_index'::regclass THEN
+			catalog = 'pg_index';
+			colname = 'indnamespace';
+		END IF;
+		IF obj.classid::regclass = 'pg_catalog.pg_type'::regclass THEN
+			catalog = 'pg_type';
+			colname = 'typnamespace';
+		END IF;
+		IF catalog IS NOT NULL THEN
+			EXECUTE format('SELECT %s FROM %s WHERE oid = %s',
+				colname, catalog, obj.objid)
+			INTO schema;
+			IF schema = 99 OR schema = pg_my_temp_schema() THEN CONTINUE; END IF;
+		END IF;
+
+		RAISE NOTICE 'object: %', pg_describe_object(obj.classid, obj.objid, obj.objsubid);
+
+		END LOOP;
+	RAISE NOTICE 'function test_evtrig_dropped_objs done';
+	END
+$$;
+-- OK
+create event trigger regress_event_trigger_drop_objects on ddl_drop
+   execute procedure test_evtrig_dropped_objs();
+-- a simple enough test: cascade
+create table evt_a(id serial primary key, value text);
+create view evt_a_v as select id from evt_a;
+drop table evt_a cascade;
+NOTICE:  drop cascades to view evt_a_v
+NOTICE:  function test_evtrig_dropped_objs starting: event ddl_drop
+NOTICE:  object: type evt_a_id_seq
+NOTICE:  object: type evt_a[]
+NOTICE:  object: type evt_a
+NOTICE:  object: type evt_a_v[]
+NOTICE:  object: type evt_a_v
+NOTICE:  object: sequence evt_a_id_seq
+NOTICE:  object: table evt_a
+NOTICE:  object: index evt_a_pkey
+NOTICE:  object: view evt_a_v
+NOTICE:  object: default for table evt_a column id
+NOTICE:  object: constraint evt_a_pkey on table evt_a
+NOTICE:  object: rule _RETURN on view evt_a_v
+NOTICE:  function test_evtrig_dropped_objs done
+-- another test with multiple targets
+create table evt_a(id serial);
+create table evt_b(id serial);
+drop table evt_a, evt_b;
+NOTICE:  function test_evtrig_dropped_objs starting: event ddl_drop
+NOTICE:  object: type evt_a_id_seq
+NOTICE:  object: type evt_a[]
+NOTICE:  object: type evt_a
+NOTICE:  object: type evt_b_id_seq
+NOTICE:  object: type evt_b[]
+NOTICE:  object: type evt_b
+NOTICE:  object: sequence evt_a_id_seq
+NOTICE:  object: table evt_a
+NOTICE:  object: sequence evt_b_id_seq
+NOTICE:  object: table evt_b
+NOTICE:  object: default for table evt_a column id
+NOTICE:  object: default for table evt_b column id
+NOTICE:  function test_evtrig_dropped_objs done
+-- sneaky: modify the table being dropped in the event trigger.
+create function test_evtrig_dropped_objs_sneaky() returns event_trigger
+language plpgsql as $$
+DECLARE
+obj record;
+BEGIN
+	RAISE NOTICE 'test_evtrig_dropped_objs_sneaky: %', tg_event;
+
+	FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() ORDER BY 1, 2
+	LOOP
+		IF obj.objid::regclass = 'evt_b'::regclass THEN
+			ALTER TABLE evt_a ADD COLUMN bar text;
+		END IF;
+	END LOOP;
+	END
+$$;
+create event trigger regress_event_trigger_drop_objects_2 on ddl_drop
+   execute procedure test_evtrig_dropped_objs_sneaky();
+create table evt_a (a serial primary key, b int, c text);
+create table evt_b (a serial primary key, b int, c text);
+-- drop column also runs a trigger.
+alter table evt_a drop column b;
+NOTICE:  function test_evtrig_dropped_objs starting: event ddl_drop
+NOTICE:  object: table evt_a column b
+NOTICE:  function test_evtrig_dropped_objs done
+NOTICE:  test_evtrig_dropped_objs_sneaky: ddl_drop
+-- this should work.
+drop table evt_a, evt_b;
+NOTICE:  function test_evtrig_dropped_objs starting: event ddl_drop
+NOTICE:  object: type evt_a_a_seq
+NOTICE:  object: type evt_a[]
+NOTICE:  object: type evt_a
+NOTICE:  object: type evt_b_a_seq
+NOTICE:  object: type evt_b[]
+NOTICE:  object: type evt_b
+NOTICE:  object: sequence evt_a_a_seq
+NOTICE:  object: table evt_a
+NOTICE:  object: index evt_a_pkey
+NOTICE:  object: sequence evt_b_a_seq
+NOTICE:  object: table evt_b
+NOTICE:  object: index evt_b_pkey
+NOTICE:  object: default for table evt_a column a
+NOTICE:  object: default for table evt_b column a
+NOTICE:  object: constraint evt_a_pkey on table evt_a
+NOTICE:  object: constraint evt_b_pkey on table evt_b
+NOTICE:  function test_evtrig_dropped_objs done
+NOTICE:  test_evtrig_dropped_objs_sneaky: ddl_drop
+drop event trigger regress_event_trigger_drop_objects_2;
+-- sneakier: add a default value to the new column, too
+create function test_evtrig_dropped_objs_sneakier() returns event_trigger
+language plpgsql as $$
+DECLARE
+obj record;
+col record;
+BEGIN
+	RAISE NOTICE 'test_evtrig_dropped_objs_sneakier: %', tg_event;
+
+	FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() ORDER BY 1, 2
+	LOOP
+		IF obj.objid::regclass = 'evt_a'::regclass THEN
+			ALTER TABLE evt_a ADD COLUMN bar text DEFAULT 'broke ya';
+		END IF;
+	END LOOP;
+	END
+$$;
+create event trigger regress_event_trigger_drop_objects_3 on ddl_drop
+   execute procedure test_evtrig_dropped_objs_sneakier();
+create table evt_a (a serial primary key, b int, c text);
+-- this should fail: refuse to operate when catalogs were changed by the
+-- trigger ... but the actual error we get is that we cannot run the ALTER
+-- TABLE because the table "is being accessed by other queries in this
+-- session", which is correct.
+alter table evt_a drop column b;
+NOTICE:  function test_evtrig_dropped_objs starting: event ddl_drop
+NOTICE:  object: table evt_a column b
+NOTICE:  function test_evtrig_dropped_objs done
+NOTICE:  test_evtrig_dropped_objs_sneakier: ddl_drop
+ERROR:  cannot ALTER TABLE "evt_a" because it is being used by active queries in this session
+CONTEXT:  SQL statement "ALTER TABLE evt_a ADD COLUMN bar text DEFAULT 'broke ya'"
+PL/pgSQL function test_evtrig_dropped_objs_sneakier() line 11 at SQL statement
+-- this should really fail because of our refusal to operate when catalogs were
+-- changed by the trigger
+drop table evt_a;
+NOTICE:  function test_evtrig_dropped_objs starting: event ddl_drop
+NOTICE:  object: type evt_a_a_seq
+NOTICE:  object: type evt_a[]
+NOTICE:  object: type evt_a
+NOTICE:  object: sequence evt_a_a_seq
+NOTICE:  object: table evt_a
+NOTICE:  object: index evt_a_pkey
+NOTICE:  object: default for table evt_a column a
+NOTICE:  object: constraint evt_a_pkey on table evt_a
+NOTICE:  function test_evtrig_dropped_objs done
+NOTICE:  test_evtrig_dropped_objs_sneakier: ddl_drop
+NOTICE:  function test_evtrig_dropped_objs starting: event ddl_drop
+CONTEXT:  SQL statement "ALTER TABLE evt_a ADD COLUMN bar text DEFAULT 'broke ya'"
+PL/pgSQL function test_evtrig_dropped_objs_sneakier() line 11 at SQL statement
+NOTICE:  object: type pg_temp_206879[]
+CONTEXT:  SQL statement "ALTER TABLE evt_a ADD COLUMN bar text DEFAULT 'broke ya'"
+PL/pgSQL function test_evtrig_dropped_objs_sneakier() line 11 at SQL statement
+NOTICE:  object: type pg_temp_206879
+CONTEXT:  SQL statement "ALTER TABLE evt_a ADD COLUMN bar text DEFAULT 'broke ya'"
+PL/pgSQL function test_evtrig_dropped_objs_sneakier() line 11 at SQL statement
+NOTICE:  object: table pg_temp_206879
+CONTEXT:  SQL statement "ALTER TABLE evt_a ADD COLUMN bar text DEFAULT 'broke ya'"
+PL/pgSQL function test_evtrig_dropped_objs_sneakier() line 11 at SQL statement
+NOTICE:  function test_evtrig_dropped_objs done
+CONTEXT:  SQL statement "ALTER TABLE evt_a ADD COLUMN bar text DEFAULT 'broke ya'"
+PL/pgSQL function test_evtrig_dropped_objs_sneakier() line 11 at SQL statement
+NOTICE:  test_evtrig_dropped_objs_sneakier: ddl_drop
+CONTEXT:  SQL statement "ALTER TABLE evt_a ADD COLUMN bar text DEFAULT 'broke ya'"
+PL/pgSQL function test_evtrig_dropped_objs_sneakier() line 11 at SQL statement
+ERROR:  event trigger function on ddl_drop event must not change list of objects to drop
+-- cleanup the ddl_drop tests
+drop event trigger if exists regress_event_trigger_drop_objects;
+drop event trigger if exists regress_event_trigger_drop_objects_2;
+NOTICE:  event trigger "regress_event_trigger_drop_objects_2" does not exist, skipping
+drop event trigger if exists regress_event_trigger_drop_objects_3;
 drop function test_event_trigger();
+drop function test_evtrig_dropped_objs();
 drop role regression_bob;
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index a07dcd7..01ee307 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -102,5 +102,125 @@ 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;
+
+-- now test the ddl_drop event trigger
+CREATE FUNCTION test_evtrig_dropped_objs() RETURNS event_trigger
+LANGUAGE plpgsql AS $$
+DECLARE
+obj record;
+col record;
+schema oid;
+catalog text;
+colname text;
+BEGIN
+	RAISE NOTICE 'function test_evtrig_dropped_objs starting: event %', tg_event;
+
+	FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() ORDER BY 1, 2
+	LOOP
+		-- skip reporting for objects in pg_toast; they contain OID references
+		-- in their names
+		IF obj.classid::regclass = 'pg_catalog.pg_class'::regclass THEN
+			catalog = 'pg_class';
+			colname = 'relnamespace';
+		END IF;
+		IF obj.classid::regclass = 'pg_catalog.pg_index'::regclass THEN
+			catalog = 'pg_index';
+			colname = 'indnamespace';
+		END IF;
+		IF obj.classid::regclass = 'pg_catalog.pg_type'::regclass THEN
+			catalog = 'pg_type';
+			colname = 'typnamespace';
+		END IF;
+		IF catalog IS NOT NULL THEN
+			EXECUTE format('SELECT %s FROM %s WHERE oid = %s',
+				colname, catalog, obj.objid)
+			INTO schema;
+			IF schema = 99 OR schema = pg_my_temp_schema() THEN CONTINUE; END IF;
+		END IF;
+
+		RAISE NOTICE 'object: %', pg_describe_object(obj.classid, obj.objid, obj.objsubid);
+
+		END LOOP;
+	RAISE NOTICE 'function test_evtrig_dropped_objs done';
+	END
+$$;
+
+-- OK
+create event trigger regress_event_trigger_drop_objects on ddl_drop
+   execute procedure test_evtrig_dropped_objs();
+
+-- a simple enough test: cascade
+create table evt_a(id serial primary key, value text);
+create view evt_a_v as select id from evt_a;
+drop table evt_a cascade;
+
+-- another test with multiple targets
+create table evt_a(id serial);
+create table evt_b(id serial);
+drop table evt_a, evt_b;
+
+-- sneaky: modify the table being dropped in the event trigger.
+create function test_evtrig_dropped_objs_sneaky() returns event_trigger
+language plpgsql as $$
+DECLARE
+obj record;
+BEGIN
+	RAISE NOTICE 'test_evtrig_dropped_objs_sneaky: %', tg_event;
+
+	FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() ORDER BY 1, 2
+	LOOP
+		IF obj.objid::regclass = 'evt_b'::regclass THEN
+			ALTER TABLE evt_a ADD COLUMN bar text;
+		END IF;
+	END LOOP;
+	END
+$$;
+create event trigger regress_event_trigger_drop_objects_2 on ddl_drop
+   execute procedure test_evtrig_dropped_objs_sneaky();
+
+create table evt_a (a serial primary key, b int, c text);
+create table evt_b (a serial primary key, b int, c text);
+-- drop column also runs a trigger.
+alter table evt_a drop column b;
+-- this should work.
+drop table evt_a, evt_b;
+drop event trigger regress_event_trigger_drop_objects_2;
+
+-- sneakier: add a default value to the new column, too
+create function test_evtrig_dropped_objs_sneakier() returns event_trigger
+language plpgsql as $$
+DECLARE
+obj record;
+col record;
+BEGIN
+	RAISE NOTICE 'test_evtrig_dropped_objs_sneakier: %', tg_event;
+
+	FOR obj IN SELECT * FROM pg_event_trigger_dropped_objects() ORDER BY 1, 2
+	LOOP
+		IF obj.objid::regclass = 'evt_a'::regclass THEN
+			ALTER TABLE evt_a ADD COLUMN bar text DEFAULT 'broke ya';
+		END IF;
+	END LOOP;
+	END
+$$;
+create event trigger regress_event_trigger_drop_objects_3 on ddl_drop
+   execute procedure test_evtrig_dropped_objs_sneakier();
+
+create table evt_a (a serial primary key, b int, c text);
+-- this should fail: refuse to operate when catalogs were changed by the
+-- trigger ... but the actual error we get is that we cannot run the ALTER
+-- TABLE because the table "is being accessed by other queries in this
+-- session", which is correct.
+alter table evt_a drop column b;
+-- this should really fail because of our refusal to operate when catalogs were
+-- changed by the trigger
+drop table evt_a;
+
+-- cleanup the ddl_drop tests
+drop event trigger if exists regress_event_trigger_drop_objects;
+drop event trigger if exists regress_event_trigger_drop_objects_2;
+drop event trigger if exists regress_event_trigger_drop_objects_3;
 drop function test_event_trigger();
+drop function test_evtrig_dropped_objs();
+
 drop role regression_bob;
