*** a/doc/src/sgml/func.sgml
--- b/doc/src/sgml/func.sgml
***************
*** 17538,17543 **** FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();
--- 17538,17556 ----
          <entry>Object sub-id (e.g. attribute number for columns)</entry>
         </row>
         <row>
+         <entry><literal>original</literal></entry>
+         <entry><type>bool</type></entry>
+         <entry>Flag used to identify the root object of the deletion</entry>
+        </row>
+        <row>
+         <entry></literal>normal</literal></entry>
+         <entry><type>bool</type></entry>
+         <entry>
+          Flag indicating that there's a normal dependency relationship
+          in the dependency graph leading to this object
+         </entry>
+        </row>
+        <row>
          <entry><literal>object_type</literal></entry>
          <entry><type>text</type></entry>
          <entry>Type of the object</entry>
***************
*** 17567,17572 **** FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();
--- 17580,17601 ----
           identifier present in the identity is quoted if necessary.
          </entry>
         </row>
+        <row>
+         <entry><literal>address_names</literal></entry>
+         <entry><type>text[]</type></entry>
+         <entry>
+          An array that, together with <literal>address_args</literal>,
+          can be used by the C-language function getObjectAddress() to
+          recreate the object address in a remote server containing a similar object.
+         <entry>
+        </row>
+        <row>
+         <entry><literal>address_args</literal></entry>
+         <entry><type>text[]</type></entry>
+         <entry>
+          See <literal>address_names</literal> above.
+         </entry>
+        </row>
        </tbody>
       </tgroup>
      </informaltable>
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 203,218 **** deleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel,
  	/*
  	 * 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)) &&
! 				EventTriggerSupportsObjectClass(getObjectClass(thisobj)))
  			{
! 				EventTriggerSQLDropAddObject(thisobj);
  			}
  		}
  	}
--- 203,227 ----
  	/*
  	 * Keep track of objects for event triggers, if necessary.
  	 */
! 	if (trackDroppedObjectsNeeded() && !(flags & PERFORM_DELETION_INTERNAL))
  	{
  		for (i = 0; i < targetObjects->numrefs; i++)
  		{
! 			const ObjectAddress *thisobj = &targetObjects->refs[i];
! 			const ObjectAddressExtra *extra = &targetObjects->extras[i];
! 			bool	original = false;
! 			bool	normal = false;
! 
! 			if (extra->flags & DEPFLAG_ORIGINAL)
! 				original = true;
! 			if (extra->flags & DEPFLAG_NORMAL)
! 				normal = true;
! 			if (extra->flags & DEPFLAG_REVERSE)
! 				normal = true;
! 
! 			if (EventTriggerSupportsObjectClass(getObjectClass(thisobj)))
  			{
! 				EventTriggerSQLDropAddObject(thisobj, original, normal);
  			}
  		}
  	}
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
***************
*** 417,422 **** static const ObjectPropertyType ObjectProperty[] =
--- 417,513 ----
  	}
  };
  
+ /*
+  * This struct maps the object types as returned by getObjectTypeDescription
+  * into ObjType enum values.  Note that some enum values can be obtained by
+  * different names, and that some string object types do not have corresponding
+  * values in the enum.  The user of this map must be careful to test for
+  * invalid values being returned.
+  *
+  * This follows the order of getObjectTypeDescription.
+  */
+ static const struct object_type_map
+ {
+ 	const char *tm_name;
+ 	ObjectType	tm_type;
+ }
+ ObjectTypeMap[] =
+ {
+ 	/* OCLASS_CLASS */
+ 	{ "table", OBJECT_TABLE },
+ 	{ "index", OBJECT_INDEX },
+ 	{ "sequence", OBJECT_SEQUENCE },
+ 	{ "toast table", -1 },		/* unmapped */
+ 	{ "view", OBJECT_VIEW },
+ 	{ "materialized view", OBJECT_MATVIEW },
+ 	{ "composite type", OBJECT_COMPOSITE },
+ 	{ "foreign table", OBJECT_FOREIGN_TABLE },
+ 	{ "table column", OBJECT_COLUMN },
+ 	/* OCLASS_PROC */
+ 	{ "aggregate", OBJECT_AGGREGATE },
+ 	{ "function", OBJECT_FUNCTION },
+ 	/* OCLASS_TYPE */
+ 	{ "type", OBJECT_TYPE },
+ 	/* OCLASS_CAST */
+ 	{ "cast", OBJECT_CAST },
+ 	/* OCLASS_COLLATION */
+ 	{ "collation", OBJECT_COLLATION },
+ 	/* OCLASS_CONSTRAINT */
+ 	{ "table constraint", OBJECT_CONSTRAINT },
+ 	{ "domain constraint", OBJECT_CONSTRAINT },
+ 	/* OCLASS_CONVERSION */
+ 	{ "conversion", OBJECT_CONVERSION },
+ 	/* OCLASS_DEFAULT */
+ 	{ "default value", OBJECT_DEFAULT },
+ 	/* OCLASS_LANGUAGE */
+ 	{ "language", OBJECT_LANGUAGE },
+ 	/* OCLASS_LARGEOBJECT */
+ 	{ "large object", OBJECT_LARGEOBJECT },
+ 	/* OCLASS_OPERATOR */
+ 	{ "operator", OBJECT_OPERATOR },
+ 	/* OCLASS_OPCLASS */
+ 	{ "operator class", OBJECT_OPCLASS },
+ 	/* OCLASS_OPFAMILY */
+ 	{ "operator family", OBJECT_OPFAMILY },
+ 	/* OCLASS_AMOP */
+ 	{ "operator of access method", -1 },	/* unmapped */
+ 	/* OCLASS_AMPROC */
+ 	{ "function of access method", -1 },	/* unmapped */
+ 	/* OCLASS_REWRITE */
+ 	{ "rule", OBJECT_RULE },
+ 	/* OCLASS_TRIGGER */
+ 	{ "trigger", OBJECT_TRIGGER },
+ 	/* OCLASS_SCHEMA */
+ 	{ "schema", OBJECT_SCHEMA },
+ 	/* OCLASS_TSPARSER */
+ 	{ "text search parser", OBJECT_TSPARSER },
+ 	/* OCLASS_TSDICT */
+ 	{ "text search dictionary", OBJECT_TSDICTIONARY },
+ 	/* OCLASS_TSTEMPLATE */
+ 	{ "text search template", OBJECT_TSTEMPLATE },
+ 	/* OCLASS_TSCONFIG */
+ 	{ "text search configuration", OBJECT_TSCONFIGURATION },
+ 	/* OCLASS_ROLE */
+ 	{ "role", OBJECT_ROLE },
+ 	/* OCLASS_DATABASE */
+ 	{ "database", OBJECT_DATABASE },
+ 	/* OCLASS_TBLSPACE */
+ 	{ "tablespace", OBJECT_TABLESPACE },
+ 	/* OCLASS_FDW */
+ 	{ "foreign-data wrapper", OBJECT_FDW },
+ 	/* OCLASS_FOREIGN_SERVER */
+ 	{ "server", OBJECT_FOREIGN_SERVER },
+ 	/* OCLASS_USER_MAPPING */
+ 	{ "user mapping", OBJECT_USER_MAPPING },
+ 	/* OCLASS_DEFACL */
+ 	{ "default acl", -1 },			/* FIXME */
+ 	/* OCLASS_EXTENSION */
+ 	{ "extension", OBJECT_EXTENSION },
+ 	/* OCLASS_EVENT_TRIGGER */
+ 	{ "event trigger", OBJECT_EVENT_TRIGGER }
+ };
+ 
+ 
  static ObjectAddress get_object_address_unqualified(ObjectType objtype,
  							   List *qualname, bool missing_ok);
  static ObjectAddress get_relation_by_qualified_name(ObjectType objtype,
***************
*** 439,446 **** static void getRelationTypeDescription(StringInfo buffer, Oid relid,
  						   int32 objectSubId);
  static void getProcedureTypeDescription(StringInfo buffer, Oid procid);
  static void getConstraintTypeDescription(StringInfo buffer, Oid constroid);
! static void getOpFamilyIdentity(StringInfo buffer, Oid opfid);
! static void getRelationIdentity(StringInfo buffer, Oid relid);
  
  /*
   * Translate an object name and arguments (as passed by the parser) to an
--- 530,538 ----
  						   int32 objectSubId);
  static void getProcedureTypeDescription(StringInfo buffer, Oid procid);
  static void getConstraintTypeDescription(StringInfo buffer, Oid constroid);
! static void getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname,
! 					List **objargs);
! static void getRelationIdentity(StringInfo buffer, Oid relid, List **objname);
  
  /*
   * Translate an object name and arguments (as passed by the parser) to an
***************
*** 1347,1352 **** get_object_namespace(const ObjectAddress *address)
--- 1439,1472 ----
  }
  
  /*
+  * Return ObjectType for the given object type as given by
+  * getObjectTypeDescription; if no valid ObjectType code exists, but it's a
+  * possible output type from getObjectTypeDescription, return -1.
+  * Otherwise, an error is thrown.
+  */
+ int
+ unstringify_objtype(const char *objtype)
+ {
+ 	ObjectType	type;
+ 	int			i;
+ 
+ 	for (i = 0; i < lengthof(ObjectTypeMap); i++)
+ 	{
+ 		if (strcmp(ObjectTypeMap[i].tm_name, objtype) == 0)
+ 		{
+ 			type = ObjectTypeMap[i].tm_type;
+ 			break;
+ 		}
+ 	}
+ 	if (i >= lengthof(ObjectTypeMap))
+ 		ereport(ERROR,
+ 			   (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				errmsg("unrecognized object type \"%s\"", objtype)));
+ 
+ 	return type;
+ }
+ 
+ /*
   * Interfaces to reference fields of ObjectPropertyType
   */
  Oid
***************
*** 2442,2447 **** pg_identify_object(PG_FUNCTION_ARGS)
--- 2562,2569 ----
  /*
   * Return a palloc'ed string that describes the type of object that the
   * passed address is for.
+  *
+  * Keep ObjectTypeMap in sync with this.
   */
  char *
  getObjectTypeDescription(const ObjectAddress *object)
***************
*** 2689,2695 **** getProcedureTypeDescription(StringInfo buffer, Oid procid)
  }
  
  /*
!  * Return a palloc'ed string that identifies an object.
   *
   * This is for machine consumption, so it's not translated.  All elements are
   * schema-qualified when appropriate.
--- 2811,2817 ----
  }
  
  /*
!  * Obtain a given object's identity, as a palloc'ed string.
   *
   * This is for machine consumption, so it's not translated.  All elements are
   * schema-qualified when appropriate.
***************
*** 2697,2710 **** getProcedureTypeDescription(StringInfo buffer, Oid procid)
  char *
  getObjectIdentity(const ObjectAddress *object)
  {
  	StringInfoData buffer;
  
  	initStringInfo(&buffer);
  
  	switch (getObjectClass(object))
  	{
  		case OCLASS_CLASS:
! 			getRelationIdentity(&buffer, object->objectId);
  			if (object->objectSubId != 0)
  			{
  				char	   *attr;
--- 2819,2855 ----
  char *
  getObjectIdentity(const ObjectAddress *object)
  {
+ 	return getObjectIdentityParts(object, NULL, NULL);
+ }
+ 
+ /*
+  * As above, but more detailed.
+  *
+  * There are two sets of return values: the identity itself as a palloc'd
+  * string is returned.  objname and objargs, if not NULL, are output parameters
+  * that receive lists of strings that are useful to give back to
+  * get_object_address() to reconstruct the ObjectAddress.
+  */
+ char *
+ getObjectIdentityParts(const ObjectAddress *object,
+ 					   List **objname, List **objargs)
+ {
  	StringInfoData buffer;
  
  	initStringInfo(&buffer);
  
+ 	/*
+ 	 * Make sure that both objname and objargs were passed, or none was.
+ 	 * Initialize objargs to empty list, which is the most common case.
+ 	 */
+ 	Assert(PointerIsValid(objname) == PointerIsValid(objargs));
+ 	if (objargs)
+ 		*objargs = NIL;
+ 
  	switch (getObjectClass(object))
  	{
  		case OCLASS_CLASS:
! 			getRelationIdentity(&buffer, object->objectId, objname);
  			if (object->objectSubId != 0)
  			{
  				char	   *attr;
***************
*** 2712,2728 **** getObjectIdentity(const ObjectAddress *object)
  				attr = get_relid_attribute_name(object->objectId,
  												object->objectSubId);
  				appendStringInfo(&buffer, ".%s", quote_identifier(attr));
  			}
  			break;
  
  		case OCLASS_PROC:
  			appendStringInfoString(&buffer,
  							   format_procedure_qualified(object->objectId));
  			break;
  
  		case OCLASS_TYPE:
! 			appendStringInfoString(&buffer,
! 								 format_type_be_qualified(object->objectId));
  			break;
  
  		case OCLASS_CAST:
--- 2857,2883 ----
  				attr = get_relid_attribute_name(object->objectId,
  												object->objectSubId);
  				appendStringInfo(&buffer, ".%s", quote_identifier(attr));
+ 				if (objname)
+ 					*objname = lappend(*objname, attr);
  			}
  			break;
  
  		case OCLASS_PROC:
  			appendStringInfoString(&buffer,
  							   format_procedure_qualified(object->objectId));
+ 			if (objname)
+ 				format_procedure_parts(object->objectId, objname, objargs);
  			break;
  
  		case OCLASS_TYPE:
! 			{
! 				char *typeout;
! 
! 				typeout = format_type_be_qualified(object->objectId);
! 				appendStringInfoString(&buffer, typeout);
! 				if (objname)
! 					*objname = list_make1(typeout);
! 			}
  			break;
  
  		case OCLASS_CAST:
***************
*** 2745,2750 **** getObjectIdentity(const ObjectAddress *object)
--- 2900,2909 ----
  							  format_type_be_qualified(castForm->castsource),
  							 format_type_be_qualified(castForm->casttarget));
  
+ 				if (objname)
+ 					*objname = list_make2(format_type_be_qualified(castForm->castsource),
+ 										  format_type_be_qualified(castForm->casttarget));
+ 
  				heap_close(castRel, AccessShareLock);
  				break;
  			}
***************
*** 2765,2770 **** getObjectIdentity(const ObjectAddress *object)
--- 2924,2931 ----
  				appendStringInfoString(&buffer,
  									   quote_qualified_identifier(schema,
  												   NameStr(coll->collname)));
+ 				if (objname)
+ 					*objname = list_make2(schema, NameStr(coll->collname));
  				ReleaseSysCache(collTup);
  				break;
  			}
***************
*** 2785,2791 **** getObjectIdentity(const ObjectAddress *object)
  				{
  					appendStringInfo(&buffer, "%s on ",
  									 quote_identifier(NameStr(con->conname)));
! 					getRelationIdentity(&buffer, con->conrelid);
  				}
  				else
  				{
--- 2946,2954 ----
  				{
  					appendStringInfo(&buffer, "%s on ",
  									 quote_identifier(NameStr(con->conname)));
! 					getRelationIdentity(&buffer, con->conrelid, objname);
! 					if (objname)
! 						*objname = lappend(*objname, pstrdup(NameStr(con->conname)));
  				}
  				else
  				{
***************
*** 2798,2803 **** getObjectIdentity(const ObjectAddress *object)
--- 2961,2968 ----
  					appendStringInfo(&buffer, "%s on %s",
  									 quote_identifier(NameStr(con->conname)),
  									 getObjectIdentity(&domain));
+ 
+ 					/* FIXME missing objname/objargs */
  				}
  
  				ReleaseSysCache(conTup);
***************
*** 2817,2822 **** getObjectIdentity(const ObjectAddress *object)
--- 2982,2989 ----
  				conForm = (Form_pg_conversion) GETSTRUCT(conTup);
  				appendStringInfoString(&buffer,
  								quote_identifier(NameStr(conForm->conname)));
+ 				if (objname)
+ 					*objname = list_make1(pstrdup(NameStr(conForm->conname)));
  				ReleaseSysCache(conTup);
  				break;
  			}
***************
*** 2856,2861 **** getObjectIdentity(const ObjectAddress *object)
--- 3023,3030 ----
  				appendStringInfo(&buffer, "for %s",
  								 getObjectIdentity(&colobject));
  
+ 				/* XXX no objname/objargs here */
+ 
  				systable_endscan(adscan);
  				heap_close(attrdefDesc, AccessShareLock);
  				break;
***************
*** 2874,2890 **** getObjectIdentity(const ObjectAddress *object)
--- 3043,3065 ----
  				langForm = (Form_pg_language) GETSTRUCT(langTup);
  				appendStringInfoString(&buffer,
  							   quote_identifier(NameStr(langForm->lanname)));
+ 				if (objname)
+ 					*objname = list_make1(pstrdup(NameStr(langForm->lanname)));
  				ReleaseSysCache(langTup);
  				break;
  			}
  		case OCLASS_LARGEOBJECT:
  			appendStringInfo(&buffer, "%u",
  							 object->objectId);
+ 			if (objname)
+ 				*objname = list_make1(psprintf("%u", object->objectId));
  			break;
  
  		case OCLASS_OPERATOR:
  			appendStringInfoString(&buffer,
  								format_operator_qualified(object->objectId));
+ 			if (objname)
+ 				format_operator_parts(object->objectId, objname, objargs);
  			break;
  
  		case OCLASS_OPCLASS:
***************
*** 2915,2928 **** getObjectIdentity(const ObjectAddress *object)
  												 NameStr(opcForm->opcname)));
  				appendStringInfo(&buffer, " for %s",
  								 quote_identifier(NameStr(amForm->amname)));
! 
  				ReleaseSysCache(amTup);
  				ReleaseSysCache(opcTup);
  				break;
  			}
  
  		case OCLASS_OPFAMILY:
! 			getOpFamilyIdentity(&buffer, object->objectId);
  			break;
  
  		case OCLASS_AMOP:
--- 3090,3108 ----
  												 NameStr(opcForm->opcname)));
  				appendStringInfo(&buffer, " for %s",
  								 quote_identifier(NameStr(amForm->amname)));
! 				if (objname)
! 				{
! 					*objname = list_make2(pstrdup(schema),
! 										  pstrdup(NameStr(opcForm->opcname)));
! 					*objargs = list_make1(pstrdup(NameStr(amForm->amname)));
! 				}
  				ReleaseSysCache(amTup);
  				ReleaseSysCache(opcTup);
  				break;
  			}
  
  		case OCLASS_OPFAMILY:
! 			getOpFamilyIdentity(&buffer, object->objectId, objname, objargs);
  			break;
  
  		case OCLASS_AMOP:
***************
*** 2934,2939 **** getObjectIdentity(const ObjectAddress *object)
--- 3114,3123 ----
  				Form_pg_amop amopForm;
  				StringInfoData opfam;
  
+ 				/* no objname support here */
+ 				if (objname)
+ 					*objname = NIL;
+ 
  				amopDesc = heap_open(AccessMethodOperatorRelationId,
  									 AccessShareLock);
  
***************
*** 2954,2960 **** getObjectIdentity(const ObjectAddress *object)
  				amopForm = (Form_pg_amop) GETSTRUCT(tup);
  
  				initStringInfo(&opfam);
! 				getOpFamilyIdentity(&opfam, amopForm->amopfamily);
  
  				appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
  								 amopForm->amopstrategy,
--- 3138,3144 ----
  				amopForm = (Form_pg_amop) GETSTRUCT(tup);
  
  				initStringInfo(&opfam);
! 				getOpFamilyIdentity(&opfam, amopForm->amopfamily, NULL, NULL);
  
  				appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
  								 amopForm->amopstrategy,
***************
*** 2978,2983 **** getObjectIdentity(const ObjectAddress *object)
--- 3162,3171 ----
  				Form_pg_amproc amprocForm;
  				StringInfoData opfam;
  
+ 				/* no objname support here */
+ 				if (objname)
+ 					*objname = NIL;
+ 
  				amprocDesc = heap_open(AccessMethodProcedureRelationId,
  									   AccessShareLock);
  
***************
*** 2998,3004 **** getObjectIdentity(const ObjectAddress *object)
  				amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
  
  				initStringInfo(&opfam);
! 				getOpFamilyIdentity(&opfam, amprocForm->amprocfamily);
  
  				appendStringInfo(&buffer, "function %d (%s, %s) of %s",
  								 amprocForm->amprocnum,
--- 3186,3192 ----
  				amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
  
  				initStringInfo(&opfam);
! 				getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, NULL, NULL);
  
  				appendStringInfo(&buffer, "function %d (%s, %s) of %s",
  								 amprocForm->amprocnum,
***************
*** 3031,3037 **** getObjectIdentity(const ObjectAddress *object)
  
  				appendStringInfo(&buffer, "%s on ",
  								 quote_identifier(NameStr(rule->rulename)));
! 				getRelationIdentity(&buffer, rule->ev_class);
  
  				heap_close(ruleDesc, AccessShareLock);
  				break;
--- 3219,3227 ----
  
  				appendStringInfo(&buffer, "%s on ",
  								 quote_identifier(NameStr(rule->rulename)));
! 				getRelationIdentity(&buffer, rule->ev_class, objname);
! 				if (objname)
! 					*objname = lappend(*objname, NameStr(rule->rulename));
  
  				heap_close(ruleDesc, AccessShareLock);
  				break;
***************
*** 3055,3061 **** getObjectIdentity(const ObjectAddress *object)
  
  				appendStringInfo(&buffer, "%s on ",
  								 quote_identifier(NameStr(trig->tgname)));
! 				getRelationIdentity(&buffer, trig->tgrelid);
  
  				heap_close(trigDesc, AccessShareLock);
  				break;
--- 3245,3253 ----
  
  				appendStringInfo(&buffer, "%s on ",
  								 quote_identifier(NameStr(trig->tgname)));
! 				getRelationIdentity(&buffer, trig->tgrelid, objname);
! 				if (objname)
! 					*objname = lappend(*objname, NameStr(trig->tgname));
  
  				heap_close(trigDesc, AccessShareLock);
  				break;
***************
*** 3071,3076 **** getObjectIdentity(const ObjectAddress *object)
--- 3263,3270 ----
  						 object->objectId);
  				appendStringInfoString(&buffer,
  									   quote_identifier(nspname));
+ 				if (objname)
+ 					*objname = list_make1(nspname);
  				break;
  			}
  
***************
*** 3090,3095 **** getObjectIdentity(const ObjectAddress *object)
--- 3284,3292 ----
  				appendStringInfoString(&buffer,
  									   quote_qualified_identifier(schema,
  											  NameStr(formParser->prsname)));
+ 				if (objname)
+ 					*objname = list_make2(schema,
+ 										  pstrdup(NameStr(formParser->prsname)));
  				ReleaseSysCache(tup);
  				break;
  			}
***************
*** 3110,3115 **** getObjectIdentity(const ObjectAddress *object)
--- 3307,3315 ----
  				appendStringInfoString(&buffer,
  									   quote_qualified_identifier(schema,
  											   NameStr(formDict->dictname)));
+ 				if (objname)
+ 					*objname = list_make2(schema,
+ 										  pstrdup(NameStr(formDict->dictname)));
  				ReleaseSysCache(tup);
  				break;
  			}
***************
*** 3130,3136 **** getObjectIdentity(const ObjectAddress *object)
  				appendStringInfoString(&buffer,
  									   quote_qualified_identifier(schema,
  											   NameStr(formTmpl->tmplname)));
! 				pfree(schema);
  				ReleaseSysCache(tup);
  				break;
  			}
--- 3330,3338 ----
  				appendStringInfoString(&buffer,
  									   quote_qualified_identifier(schema,
  											   NameStr(formTmpl->tmplname)));
! 				if (objname)
! 					*objname = list_make2(schema,
! 										  pstrdup(NameStr(formTmpl->tmplname)));
  				ReleaseSysCache(tup);
  				break;
  			}
***************
*** 3151,3156 **** getObjectIdentity(const ObjectAddress *object)
--- 3353,3361 ----
  				appendStringInfoString(&buffer,
  									   quote_qualified_identifier(schema,
  												 NameStr(formCfg->cfgname)));
+ 				if (objname)
+ 					*objname = list_make2(schema,
+ 										  pstrdup(NameStr(formCfg->cfgname)));
  				ReleaseSysCache(tup);
  				break;
  			}
***************
*** 3159,3164 **** getObjectIdentity(const ObjectAddress *object)
--- 3364,3372 ----
  			{
  				char	   *username;
  
+ 				/* no objname support here */
+ 				Assert(objname == NULL);
+ 
  				username = GetUserNameFromId(object->objectId);
  				appendStringInfoString(&buffer,
  									   quote_identifier(username));
***************
*** 3169,3174 **** getObjectIdentity(const ObjectAddress *object)
--- 3377,3385 ----
  			{
  				char	   *datname;
  
+ 				/* no objname support here */
+ 				Assert(objname == NULL);
+ 
  				datname = get_database_name(object->objectId);
  				if (!datname)
  					elog(ERROR, "cache lookup failed for database %u",
***************
*** 3182,3187 **** getObjectIdentity(const ObjectAddress *object)
--- 3393,3401 ----
  			{
  				char	   *tblspace;
  
+ 				/* no objname support here */
+ 				Assert(objname == NULL);
+ 
  				tblspace = get_tablespace_name(object->objectId);
  				if (!tblspace)
  					elog(ERROR, "cache lookup failed for tablespace %u",
***************
*** 3197,3202 **** getObjectIdentity(const ObjectAddress *object)
--- 3411,3418 ----
  
  				fdw = GetForeignDataWrapper(object->objectId);
  				appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
+ 				if (objname)
+ 					*objname = list_make1(pstrdup(fdw->fdwname));
  				break;
  			}
  
***************
*** 3207,3212 **** getObjectIdentity(const ObjectAddress *object)
--- 3423,3430 ----
  				srv = GetForeignServer(object->objectId);
  				appendStringInfoString(&buffer,
  									   quote_identifier(srv->servername));
+ 				if (objname)
+ 					*objname = list_make1(pstrdup(srv->servername));
  				break;
  			}
  
***************
*** 3216,3221 **** getObjectIdentity(const ObjectAddress *object)
--- 3434,3441 ----
  				Oid			useid;
  				const char *usename;
  
+ 				/* XXX get_object_address doesn't seem to support this */
+ 
  				tup = SearchSysCache1(USERMAPPINGOID,
  									  ObjectIdGetDatum(object->objectId));
  				if (!HeapTupleIsValid(tup))
***************
*** 3240,3249 **** getObjectIdentity(const ObjectAddress *object)
  				Relation	defaclrel;
  				ScanKeyData skey[1];
  				SysScanDesc rcscan;
- 
  				HeapTuple	tup;
  				Form_pg_default_acl defacl;
  
  				defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
  
  				ScanKeyInit(&skey[0],
--- 3460,3470 ----
  				Relation	defaclrel;
  				ScanKeyData skey[1];
  				SysScanDesc rcscan;
  				HeapTuple	tup;
  				Form_pg_default_acl defacl;
  
+ 				/* XXX get_object_address doesn't seem to support this */
+ 
  				defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
  
  				ScanKeyInit(&skey[0],
***************
*** 3310,3315 **** getObjectIdentity(const ObjectAddress *object)
--- 3531,3538 ----
  					elog(ERROR, "cache lookup failed for extension %u",
  						 object->objectId);
  				appendStringInfoString(&buffer, quote_identifier(extname));
+ 				if (objname)
+ 					*objname = list_make1(extname);
  				break;
  			}
  
***************
*** 3318,3323 **** getObjectIdentity(const ObjectAddress *object)
--- 3541,3549 ----
  				HeapTuple	tup;
  				Form_pg_event_trigger trigForm;
  
+ 				/* no objname support here */
+ 				Assert(objname == NULL);
+ 
  				tup = SearchSysCache1(EVENTTRIGGEROID,
  									  ObjectIdGetDatum(object->objectId));
  				if (!HeapTupleIsValid(tup))
***************
*** 3342,3348 **** getObjectIdentity(const ObjectAddress *object)
  }
  
  static void
! getOpFamilyIdentity(StringInfo buffer, Oid opfid)
  {
  	HeapTuple	opfTup;
  	Form_pg_opfamily opfForm;
--- 3568,3574 ----
  }
  
  static void
! getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, List **objargs)
  {
  	HeapTuple	opfTup;
  	Form_pg_opfamily opfForm;
***************
*** 3367,3372 **** getOpFamilyIdentity(StringInfo buffer, Oid opfid)
--- 3593,3605 ----
  												NameStr(opfForm->opfname)),
  					 NameStr(amForm->amname));
  
+ 	if (objname)
+ 	{
+ 		*objname = list_make2(pstrdup(schema),
+ 							  pstrdup(NameStr(opfForm->opfname)));
+ 		*objargs = list_make1(pstrdup(NameStr(amForm->amname)));
+ 	}
+ 
  	ReleaseSysCache(amTup);
  	ReleaseSysCache(opfTup);
  }
***************
*** 3376,3382 **** getOpFamilyIdentity(StringInfo buffer, Oid opfid)
   * StringInfo.
   */
  static void
! getRelationIdentity(StringInfo buffer, Oid relid)
  {
  	HeapTuple	relTup;
  	Form_pg_class relForm;
--- 3609,3615 ----
   * StringInfo.
   */
  static void
! getRelationIdentity(StringInfo buffer, Oid relid, List **objname)
  {
  	HeapTuple	relTup;
  	Form_pg_class relForm;
***************
*** 3392,3397 **** getRelationIdentity(StringInfo buffer, Oid relid)
--- 3625,3632 ----
  	appendStringInfoString(buffer,
  						   quote_qualified_identifier(schema,
  												 NameStr(relForm->relname)));
+ 	if (objname)
+ 		*objname = list_make2(schema, pstrdup(NameStr(relForm->relname)));
  
  	ReleaseSysCache(relTup);
  }
*** a/src/backend/commands/event_trigger.c
--- b/src/backend/commands/event_trigger.c
***************
*** 111,116 **** typedef struct SQLDropObject
--- 111,121 ----
  	const char *objname;
  	const char *objidentity;
  	const char *objecttype;
+ 	List	   *addrnames;
+ 	List	   *addrargs;
+ 	ObjectAddress dependee;
+ 	bool		original;
+ 	bool		normal;
  	slist_node	next;
  } SQLDropObject;
  
***************
*** 920,928 **** EventTriggerSupportsObjectType(ObjectType obtype)
--- 925,935 ----
  		case OBJECT_ATTRIBUTE:
  		case OBJECT_CAST:
  		case OBJECT_COLUMN:
+ 		case OBJECT_COMPOSITE:
  		case OBJECT_CONSTRAINT:
  		case OBJECT_COLLATION:
  		case OBJECT_CONVERSION:
+ 		case OBJECT_DEFAULT:
  		case OBJECT_DOMAIN:
  		case OBJECT_EXTENSION:
  		case OBJECT_FDW:
***************
*** 946,951 **** EventTriggerSupportsObjectType(ObjectType obtype)
--- 953,959 ----
  		case OBJECT_TSPARSER:
  		case OBJECT_TSTEMPLATE:
  		case OBJECT_TYPE:
+ 		case OBJECT_USER_MAPPING:
  		case OBJECT_VIEW:
  			return true;
  	}
***************
*** 1102,1108 **** trackDroppedObjectsNeeded(void)
   * Register one object as being dropped by the current command.
   */
  void
! EventTriggerSQLDropAddObject(ObjectAddress *object)
  {
  	SQLDropObject *obj;
  	MemoryContext oldcxt;
--- 1110,1116 ----
   * Register one object as being dropped by the current command.
   */
  void
! EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool normal)
  {
  	SQLDropObject *obj;
  	MemoryContext oldcxt;
***************
*** 1121,1126 **** EventTriggerSQLDropAddObject(ObjectAddress *object)
--- 1129,1136 ----
  
  	obj = palloc0(sizeof(SQLDropObject));
  	obj->address = *object;
+ 	obj->original = original;
+ 	obj->normal = normal;
  
  	/*
  	 * Obtain schema names from the object's catalog tuple, if one exists;
***************
*** 1182,1191 **** EventTriggerSQLDropAddObject(ObjectAddress *object)
  		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);
--- 1192,1202 ----
  		heap_close(catalog, AccessShareLock);
  	}
  
! 	/* object identity, objname and objargs */
! 	obj->objidentity =
! 		getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs);
  
! 	/* object type */
  	obj->objecttype = getObjectTypeDescription(&obj->address);
  
  	slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
***************
*** 1248,1255 **** pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
  	{
  		SQLDropObject *obj;
  		int			i = 0;
! 		Datum		values[7];
! 		bool		nulls[7];
  
  		obj = slist_container(SQLDropObject, next, iter.cur);
  
--- 1259,1266 ----
  	{
  		SQLDropObject *obj;
  		int			i = 0;
! 		Datum		values[11];
! 		bool		nulls[11];
  
  		obj = slist_container(SQLDropObject, next, iter.cur);
  
***************
*** 1265,1270 **** pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
--- 1276,1287 ----
  		/* objsubid */
  		values[i++] = Int32GetDatum(obj->address.objectSubId);
  
+ 		/* original */
+ 		values[i++] = BoolGetDatum(obj->original);
+ 
+ 		/* normal */
+ 		values[i++] = BoolGetDatum(obj->normal);
+ 
  		/* object_type */
  		values[i++] = CStringGetTextDatum(obj->objecttype);
  
***************
*** 1286,1291 **** pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
--- 1303,1356 ----
  		else
  			nulls[i++] = true;
  
+ 		/* address_names */
+ 		if (obj->addrnames)
+ 		{
+ 			ArrayType *arr;
+ 			Datum	*datums;
+ 			int		j = 0;
+ 			ListCell *cell;
+ 
+ 			datums = palloc(sizeof(text *) * list_length(obj->addrnames));
+ 			foreach(cell, obj->addrnames)
+ 			{
+ 				char   *name = lfirst(cell);
+ 
+ 				datums[j++] = CStringGetTextDatum(name);
+ 			}
+ 
+ 			arr = construct_array(datums, list_length(obj->addrnames),
+ 								  TEXTOID, -1, false, 'i');
+ 
+ 			values[i++] = PointerGetDatum(arr);
+ 		}
+ 		else
+ 			nulls[i++] = true;
+ 
+ 		/* address_args */
+ 		/* FIXME duplicated code block ... */
+ 		if (obj->addrargs)
+ 		{
+ 			ArrayType *arr;
+ 			Datum   *datums;
+ 			int		j = 0;
+ 			ListCell *cell;
+ 
+ 			datums = palloc(sizeof(text *) * list_length(obj->addrargs));
+ 			foreach(cell, obj->addrargs)
+ 			{
+ 				char	*arg = lfirst(cell);
+ 
+ 				datums[j++] = CStringGetTextDatum(arg);
+ 			}
+ 
+ 			arr = construct_array(datums, list_length(obj->addrargs),
+ 								  TEXTOID, -1, false, 'i');
+ 			values[i++] = PointerGetDatum(arr);
+ 		}
+ 		else
+ 			nulls[i++] = true;
+ 
  		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
  	}
  
*** a/src/backend/parser/parse_type.c
--- b/src/backend/parser/parse_type.c
***************
*** 705,717 **** pts_error_callback(void *arg)
  /*
   * Given a string that is supposed to be a SQL-compatible type declaration,
   * such as "int4" or "integer" or "character varying(32)", parse
!  * the string and convert it to a type OID and type modifier.
!  * If missing_ok is true, InvalidOid is returned rather than raising an error
!  * when the type name is not found.
   */
! void
! parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
! 				bool missing_ok)
  {
  	StringInfoData buf;
  	List	   *raw_parsetree_list;
--- 705,715 ----
  /*
   * Given a string that is supposed to be a SQL-compatible type declaration,
   * such as "int4" or "integer" or "character varying(32)", parse
!  * the string and return the result as a TypeName.
!  * If the string cannot be parsed as a type, an error is raised.
   */
! TypeName *
! typeStringToTypeName(const char *str)
  {
  	StringInfoData buf;
  	List	   *raw_parsetree_list;
***************
*** 720,726 **** parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
  	TypeCast   *typecast;
  	TypeName   *typeName;
  	ErrorContextCallback ptserrcontext;
- 	Type		tup;
  
  	/* make sure we give useful error for empty input */
  	if (strspn(str, " \t\n\r\f") == strlen(str))
--- 718,723 ----
***************
*** 779,784 **** parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
--- 776,782 ----
  		typecast->arg == NULL ||
  		!IsA(typecast->arg, A_Const))
  		goto fail;
+ 
  	typeName = typecast->typeName;
  	if (typeName == NULL ||
  		!IsA(typeName, TypeName))
***************
*** 786,791 **** parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
--- 784,814 ----
  	if (typeName->setof)
  		goto fail;
  
+ 	pfree(buf.data);
+ 
+ 	return typeName;
+ 
+ fail:
+ 	ereport(ERROR,
+ 			(errcode(ERRCODE_SYNTAX_ERROR),
+ 			 errmsg("invalid type name \"%s\"", str)));
+ }
+ 
+ /*
+  * Given a string that is supposed to be a SQL-compatible type declaration,
+  * such as "int4" or "integer" or "character varying(32)", parse
+  * the string and convert it to a type OID and type modifier.
+  * If missing_ok is true, InvalidOid is returned rather than raising an error
+  * when the type name is not found.
+  */
+ void
+ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, bool missing_ok)
+ {
+ 	TypeName   *typeName;
+ 	Type		tup;
+ 
+ 	typeName = typeStringToTypeName(str);
+ 
  	tup = LookupTypeName(NULL, typeName, typmod_p, missing_ok);
  	if (tup == NULL)
  	{
***************
*** 808,820 **** parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
  		*typeid_p = HeapTupleGetOid(tup);
  		ReleaseSysCache(tup);
  	}
- 
- 	pfree(buf.data);
- 
- 	return;
- 
- fail:
- 	ereport(ERROR,
- 			(errcode(ERRCODE_SYNTAX_ERROR),
- 			 errmsg("invalid type name \"%s\"", str)));
  }
--- 831,834 ----
*** a/src/backend/utils/adt/regproc.c
--- b/src/backend/utils/adt/regproc.c
***************
*** 439,444 **** format_procedure_internal(Oid procedure_oid, bool force_qualify)
--- 439,479 ----
  }
  
  /*
+  * Output a objname/objargs representation for the procedure with the
+  * given OID.  If it doesn't exist, an error is thrown.
+  *
+  * This can be used to feed get_object_address.
+  */
+ void
+ format_procedure_parts(Oid procedure_oid, List **objnames, List **objargs)
+ {
+ 	HeapTuple	proctup;
+ 	Form_pg_proc procform;
+ 	int			nargs;
+ 	int			i;
+ 
+ 	proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid));
+ 
+ 	if (!HeapTupleIsValid(proctup))
+ 		elog(ERROR, "cache lookup failed for procedure with OID %u", procedure_oid);
+ 
+ 	procform = (Form_pg_proc) GETSTRUCT(proctup);
+ 	nargs = procform->pronargs;
+ 
+ 	*objnames = list_make2(get_namespace_name(procform->pronamespace),
+ 						   pstrdup(NameStr(procform->proname)));
+ 	*objargs = NIL;
+ 	for (i = 0; i < nargs; i++)
+ 	{
+ 		Oid		thisargtype = procform->proargtypes.values[i];
+ 
+ 		*objargs = lappend(*objargs, format_type_be_qualified(thisargtype));
+ 	}
+ 
+ 	ReleaseSysCache(proctup);
+ }
+ 
+ /*
   * regprocedureout		- converts proc OID to "pro_name(args)"
   */
  Datum
***************
*** 875,880 **** format_operator_qualified(Oid operator_oid)
--- 910,940 ----
  	return format_operator_internal(operator_oid, true);
  }
  
+ void
+ format_operator_parts(Oid operator_oid, List **objnames, List **objargs)
+ {
+ 	HeapTuple	opertup;
+ 	Form_pg_operator oprForm;
+ 
+ 	opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operator_oid));
+ 	if (!HeapTupleIsValid(opertup))
+ 		elog(ERROR, "cache lookup failed for operator with OID %u",
+ 			 operator_oid);
+ 
+ 	oprForm = (Form_pg_operator) GETSTRUCT(opertup);
+ 	*objnames = list_make2(get_namespace_name(oprForm->oprnamespace),
+ 						   pstrdup(NameStr(oprForm->oprname)));
+ 	*objargs = NIL;
+ 	if (oprForm->oprleft)
+ 		*objargs = lappend(*objargs,
+ 						   format_type_be_qualified(oprForm->oprleft));
+ 	if (oprForm->oprright)
+ 		*objargs = lappend(*objargs,
+ 						   format_type_be_qualified(oprForm->oprright));
+ 
+ 	ReleaseSysCache(opertup);
+ }
+ 
  /*
   * regoperatorout		- converts operator OID to "opr_name(args)"
   */
*** a/src/include/catalog/objectaddress.h
--- b/src/include/catalog/objectaddress.h
***************
*** 55,61 **** extern HeapTuple get_catalog_object_by_oid(Relation catalog,
--- 55,64 ----
  extern char *getObjectDescription(const ObjectAddress *object);
  extern char *getObjectDescriptionOids(Oid classid, Oid objid);
  
+ extern int unstringify_objtype(const char *objtype);
  extern char *getObjectTypeDescription(const ObjectAddress *object);
  extern char *getObjectIdentity(const ObjectAddress *address);
+ extern char *getObjectIdentityParts(const ObjectAddress *address,
+ 					   List **objname, List **objargs);
  
  #endif   /* OBJECTADDRESS_H */
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 4978,4984 **** DATA(insert OID = 3785 (  pg_logical_slot_peek_binary_changes PGNSP PGUID 12 100
  DESCR("peek at binary changes from replication slot");
  
  /* 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");
  
  /* generic transition functions for ordered-set aggregates */
--- 4978,4984 ----
  DESCR("peek at binary changes from replication slot");
  
  /* 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,16,16,25,25,25,25,1009,1009}" "{o,o,o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, object_type, schema_name, object_name, object_identity, address_names, address_args}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
  DESCR("list objects dropped by the current command");
  
  /* generic transition functions for ordered-set aggregates */
*** a/src/include/commands/event_trigger.h
--- b/src/include/commands/event_trigger.h
***************
*** 50,55 **** 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 */
--- 50,56 ----
  extern bool EventTriggerBeginCompleteQuery(void);
  extern void EventTriggerEndCompleteQuery(void);
  extern bool trackDroppedObjectsNeeded(void);
! extern void EventTriggerSQLDropAddObject(const ObjectAddress *object,
! 							 bool original, bool normal);
  
  #endif   /* EVENT_TRIGGER_H */
*** a/src/include/nodes/parsenodes.h
--- b/src/include/nodes/parsenodes.h
***************
*** 1192,1201 **** typedef enum ObjectType
--- 1192,1203 ----
  	OBJECT_ATTRIBUTE,			/* type's attribute, when distinct from column */
  	OBJECT_CAST,
  	OBJECT_COLUMN,
+ 	OBJECT_COMPOSITE,
  	OBJECT_CONSTRAINT,
  	OBJECT_COLLATION,
  	OBJECT_CONVERSION,
  	OBJECT_DATABASE,
+ 	OBJECT_DEFAULT,
  	OBJECT_DOMAIN,
  	OBJECT_EVENT_TRIGGER,
  	OBJECT_EXTENSION,
***************
*** 1222,1227 **** typedef enum ObjectType
--- 1224,1230 ----
  	OBJECT_TSPARSER,
  	OBJECT_TSTEMPLATE,
  	OBJECT_TYPE,
+ 	OBJECT_USER_MAPPING,
  	OBJECT_VIEW
  } ObjectType;
  
*** a/src/include/parser/parse_type.h
--- b/src/include/parser/parse_type.h
***************
*** 47,52 **** extern Datum stringTypeDatum(Type tp, char *string, int32 atttypmod);
--- 47,53 ----
  
  extern Oid	typeidTypeRelid(Oid type_id);
  
+ extern TypeName *typeStringToTypeName(const char *str);
  extern void parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, bool missing_ok);
  
  #define ISCOMPLEX(typeid) (typeidTypeRelid(typeid) != InvalidOid)
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 641,648 **** extern Datum text_regclass(PG_FUNCTION_ARGS);
--- 641,652 ----
  extern List *stringToQualifiedNameList(const char *string);
  extern char *format_procedure(Oid procedure_oid);
  extern char *format_procedure_qualified(Oid procedure_oid);
+ extern void format_procedure_parts(Oid operator_oid, List **objnames,
+ 					  List **objargs);
  extern char *format_operator(Oid operator_oid);
  extern char *format_operator_qualified(Oid operator_oid);
+ extern void format_operator_parts(Oid operator_oid, List **objnames,
+ 					  List **objargs);
  
  /* rowtypes.c */
  extern Datum record_in(PG_FUNCTION_ARGS);
