diff --git a/doc/src/sgml/event-trigger.sgml b/doc/src/sgml/event-trigger.sgml
index 71241c8..bcb8bee 100644
--- a/doc/src/sgml/event-trigger.sgml
+++ b/doc/src/sgml/event-trigger.sgml
@@ -27,9 +27,9 @@
    <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</>. Support
+     for additional events may be added in future releases.
    </para>
 
    <para>
@@ -46,6 +46,14 @@
    </para>
 
    <para>
+    To list all objects that have been deleted as part of executing a
+    command, use the Set Returning
+    Function <literal>pg_event_trigger_dropped_objects()</> from
+    your <literal>ddl_command_end</> event trigger code. Note that happens
+    after the objects have been deleted, so no catalog lookup is possible.
+   </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,
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 92a79d3..687dd94 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15688,9 +15688,55 @@ 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 '% dropped object: % % % %',
+                     tg_tag,
+                     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/access/transam/xact.c b/src/backend/access/transam/xact.c
index 81d2687..ab0f13c 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -30,6 +30,7 @@
 #include "catalog/namespace.h"
 #include "catalog/storage.h"
 #include "commands/async.h"
+#include "commands/event_trigger.h"
 #include "commands/tablecmds.h"
 #include "commands/trigger.h"
 #include "executor/spi.h"
@@ -1955,6 +1956,7 @@ CommitTransaction(void)
 	AtEOXact_HashTables(true);
 	AtEOXact_PgStat(true);
 	AtEOXact_Snapshot(true);
+	AtEOXact_EventTrigger(true);
 	pgstat_report_xact_timestamp(0);
 
 	CurrentResourceOwner = NULL;
@@ -2208,6 +2210,7 @@ PrepareTransaction(void)
 	AtEOXact_HashTables(true);
 	/* don't call AtEOXact_PgStat here */
 	AtEOXact_Snapshot(true);
+	AtEOXact_EventTrigger(true);
 
 	CurrentResourceOwner = NULL;
 	ResourceOwnerDelete(TopTransactionResourceOwner);
@@ -2382,6 +2385,7 @@ CleanupTransaction(void)
 	 */
 	AtCleanup_Portals();		/* now safe to release portal memory */
 	AtEOXact_Snapshot(false);	/* and release the transaction's snapshots */
+	AtEOXact_EventTrigger(false); /* and reset Event Trigger internal state */
 
 	CurrentResourceOwner = NULL;	/* and resource owner */
 	if (TopTransactionResourceOwner)
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index d203725..0543076 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -347,7 +347,15 @@ performMultipleDeletions(const ObjectAddresses *objects,
 	 */
 	for (i = 0; i < targetObjects->numrefs; i++)
 	{
-		ObjectAddress *thisobj = targetObjects->refs + i;
+		ObjectAddress  *thisobj;
+
+		thisobj = targetObjects->refs + i;
+
+		if (EventTriggerSQLDropInProgress &&
+			EventTriggerSupportsObjectType(getObjectClass(thisobj)))
+		{
+			add_exact_object_address(thisobj, EventTriggerSQLDropList);
+		}
 
 		deleteOneObject(thisobj, &depRel, flags);
 	}
@@ -2175,6 +2183,18 @@ record_object_address_dependencies(const ObjectAddress *depender,
 							   behavior);
 }
 
+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..9ed5715 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -25,6 +25,7 @@
 #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 +40,10 @@
 #include "utils/syscache.h"
 #include "tcop/utility.h"
 
+/* Globally visible state variables */
+bool EventTriggerSQLDropInProgress = false;
+ObjectAddresses *EventTriggerSQLDropList = NULL;
+
 typedef struct
 {
 	const char	   *obtypename;
@@ -150,8 +155,12 @@ 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)
+		&& tags != NULL)
+	{
 		validate_ddl_tags("tag", tags);
+	}
 
 	/*
 	 * Give user a nice error message if an event trigger of the same name
@@ -739,6 +748,14 @@ EventTriggerDDLCommandEnd(Node *parsetree)
 
 	/* Cleanup. */
 	list_free(runlist);
+
+	if (EventTriggerSQLDropInProgress)
+	{
+		free_object_addresses(EventTriggerSQLDropList);
+
+		EventTriggerSQLDropInProgress = false;
+		EventTriggerSQLDropList = NULL;
+	}
 }
 
 /*
@@ -825,3 +842,107 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 	}
 	return true;
 }
+
+/*
+ * SQL DROP event support functions
+ */
+void
+EventTriggerInitDropList(void)
+{
+	EventTriggerSQLDropInProgress = true;
+	EventTriggerSQLDropList = new_object_addresses();
+}
+
+/*
+ * AtEOXact_EventTrigger
+ *		Event Trigger's cleanup function for end of transaction
+ */
+void
+AtEOXact_EventTrigger(bool isCommit)
+{
+	/* even on success we want to reset EventTriggerSQLDropInProgress */
+	EventTriggerSQLDropInProgress = false;
+}
+
+/*
+ * 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 an event trigger in
+	 * order to get the list of objects dropped, if any.
+	 */
+	if (!EventTriggerSQLDropInProgress)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("pg_dropped_objects() can only be called from an event trigger function")));
+
+	/* 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(EventTriggerSQLDropList); i++)
+	{
+		ObjectAddress *object;
+		Datum		values[3];
+		bool		nulls[3];
+
+		/* Emit result row */
+		object = get_object_addresses_element(EventTriggerSQLDropList, i);
+
+		MemSet(values, 0, sizeof(values));
+		MemSet(nulls, 0, sizeof(nulls));
+
+		/* classid */
+		values[0] = ObjectIdGetDatum(object->classId);
+
+		/* objid */
+		values[1] = ObjectIdGetDatum(object->objectId);
+
+		/* objsubid */
+		if (OidIsValid(object->objectSubId))
+			values[2] = ObjectIdGetDatum(object->objectSubId);
+		else
+			nulls[2] = true;
+
+		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..7ed05d3 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -698,18 +698,33 @@ standard_ProcessUtility(Node *parsetree,
 			{
 				DropStmt   *stmt = (DropStmt *) parsetree;
 
-				if (isCompleteQuery
-					&& EventTriggerSupportsObjectType(stmt->removeType))
+				/*
+				 * don't run any event trigger when we require not to have open
+				 * a transaction
+				 */
+				if (stmt->removeType == OBJECT_INDEX && stmt->concurrent)
+					PreventTransactionChain(isTopLevel,
+											"DROP INDEX CONCURRENTLY");
+
+				if (isCompleteQuery &&
+					EventTriggerSupportsObjectType(stmt->removeType))
+				{
 					EventTriggerDDLCommandStart(parsetree);
 
+					/*
+					 * cater with multiple targets and cascading drops.
+					 *
+					 * Initialize that after having called the
+					 * ddl_command_start triggers so that
+					 * EventTriggerSQLDropInProgress is still false there, as
+					 * that protects pg_dropped_objects() calls.
+					 */
+					EventTriggerInitDropList();
+				}
+
 				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:
@@ -723,8 +738,9 @@ standard_ProcessUtility(Node *parsetree,
 
 				if (isCompleteQuery
 					&& EventTriggerSupportsObjectType(stmt->removeType))
+				{
 					EventTriggerDDLCommandEnd(parsetree);
-
+				}
 				break;
 			}
 
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 8e0837f..846726c 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -191,6 +191,11 @@ extern void record_object_address_dependencies(const ObjectAddress *depender,
 								   ObjectAddresses *referenced,
 								   DependencyType behavior);
 
+extern int get_object_addresses_numelements(const ObjectAddresses *addresses);
+
+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 028e168..3981513 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,26}" "{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..7ed92b0 100644
--- a/src/include/commands/event_trigger.h
+++ b/src/include/commands/event_trigger.h
@@ -13,9 +13,23 @@
 #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"
 
+/*
+ * Global objects that we need to keep track of for benefits of Event Triggers.
+ *
+ * The EventTriggerSQLDropList is a list of ObjectAddress filled in from
+ * dependency.c doDeletion() function. Only objects that are supported as in
+ * EventTriggerSupportsObjectType() get appended here. ProcessUtility is
+ * responsible for resetting this list to NIL at the beginning of any DROP
+ * operation.
+ */
+extern bool EventTriggerSQLDropInProgress;
+extern ObjectAddresses *EventTriggerSQLDropList;
+
 typedef struct EventTriggerData
 {
 	NodeTag		type;
@@ -43,4 +57,11 @@ extern bool EventTriggerSupportsObjectType(ObjectType obtype);
 extern void EventTriggerDDLCommandStart(Node *parsetree);
 extern void EventTriggerDDLCommandEnd(Node *parsetree);
 
+extern void EventTriggerInitDropList(void);
+extern List *EventTriggerAppendToDropList(ObjectAddress *object);
+extern void EventTriggerSQLDrop(Node *parsetree);
+
+extern void AtEOXact_EventTrigger(bool isCommit);
+
+
 #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/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index bf020de..75d4ee7 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -93,11 +93,74 @@ ERROR:  event trigger "regress_event_trigger" does not exist
 drop role regression_bob;
 ERROR:  role "regression_bob" cannot be dropped because some objects depend on it
 DETAIL:  owner of event trigger regress_event_trigger3
--- these are all OK; the second one should emit a NOTICE
+-- now test pg_event_trigger_dropped_objects()
+create function test_event_trigger_dropped_objects() 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
+        -- we can't output the full data that we have here because the OID
+        -- would change each time we run the regression tests.
+        --
+        -- obj.classId, obj.objid, obj.objsubid;
+        RAISE NOTICE '% dropped object: %', tg_tag, obj.classId::regclass;
+    END LOOP;
+END
+$$ language plpgsql;
+NOTICE:  test_event_trigger: ddl_command_start CREATE FUNCTION
+NOTICE:  test_event_trigger: ddl_command_end CREATE FUNCTION
+-- OK
+create event trigger regress_event_trigger_drop_objects on ddl_command_end
+                when tag in ('drop table', 'drop function', 'drop view')
+   execute procedure test_event_trigger_dropped_objects();
+-- a simple enough test: cascade
+create table evt_a(id serial);
+NOTICE:  test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE:  test_event_trigger: ddl_command_end CREATE TABLE
+create view evt_a_v as select id from evt_a;
+NOTICE:  test_event_trigger: ddl_command_end CREATE VIEW
+drop table evt_a cascade;
+NOTICE:  drop cascades to view evt_a_v
+NOTICE:  test_event_trigger: ddl_command_end DROP TABLE
+NOTICE:  DROP TABLE dropped object: pg_type
+NOTICE:  DROP TABLE dropped object: pg_type
+NOTICE:  DROP TABLE dropped object: pg_rewrite
+NOTICE:  DROP TABLE dropped object: pg_type
+NOTICE:  DROP TABLE dropped object: pg_type
+NOTICE:  DROP TABLE dropped object: pg_class
+NOTICE:  DROP TABLE dropped object: pg_type
+NOTICE:  DROP TABLE dropped object: pg_class
+NOTICE:  DROP TABLE dropped object: pg_class
+NOTICE:  test_event_trigger: ddl_command_end DROP TABLE
+-- another test with multiple targets
+create table evt_a(id serial);
+NOTICE:  test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE:  test_event_trigger: ddl_command_end CREATE TABLE
+create table evt_b(id serial);
+NOTICE:  test_event_trigger: ddl_command_start CREATE TABLE
+NOTICE:  test_event_trigger: ddl_command_end CREATE TABLE
+drop table evt_a, evt_b;
+NOTICE:  test_event_trigger: ddl_command_end DROP TABLE
+NOTICE:  DROP TABLE dropped object: pg_type
+NOTICE:  DROP TABLE dropped object: pg_type
+NOTICE:  DROP TABLE dropped object: pg_type
+NOTICE:  DROP TABLE dropped object: pg_class
+NOTICE:  DROP TABLE dropped object: pg_class
+NOTICE:  DROP TABLE dropped object: pg_type
+NOTICE:  DROP TABLE dropped object: pg_type
+NOTICE:  DROP TABLE dropped object: pg_type
+NOTICE:  DROP TABLE dropped object: pg_class
+NOTICE:  DROP TABLE dropped object: pg_class
+NOTICE:  test_event_trigger: ddl_command_end DROP TABLE
+-- these are all OK; the third one should emit a NOTICE
+drop event trigger if exists regress_event_trigger_drop_objects;
 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 function test_event_trigger_dropped_objects();
 drop role regression_bob;
diff --git a/src/test/regress/sql/event_trigger.sql b/src/test/regress/sql/event_trigger.sql
index a07dcd7..4d92071 100644
--- a/src/test/regress/sql/event_trigger.sql
+++ b/src/test/regress/sql/event_trigger.sql
@@ -97,10 +97,45 @@ drop event trigger regress_event_trigger;
 -- should fail, regression_bob owns regress_event_trigger2/3
 drop role regression_bob;
 
--- these are all OK; the second one should emit a NOTICE
+-- now test pg_event_trigger_dropped_objects()
+create function test_event_trigger_dropped_objects() 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
+        -- we can't output the full data that we have here because the OID
+        -- would change each time we run the regression tests.
+        --
+        -- obj.classId, obj.objid, obj.objsubid;
+        RAISE NOTICE '% dropped object: %', tg_tag, obj.classId::regclass;
+    END LOOP;
+END
+$$ language plpgsql;
+
+-- OK
+create event trigger regress_event_trigger_drop_objects on ddl_command_end
+                when tag in ('drop table', 'drop function', 'drop view')
+   execute procedure test_event_trigger_dropped_objects();
+
+-- a simple enough test: cascade
+create table evt_a(id serial);
+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;
+
+-- these are all OK; the third one should emit a NOTICE
+drop event trigger if exists regress_event_trigger_drop_objects;
 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 function test_event_trigger_dropped_objects();
+
 drop role regression_bob;
