diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 24c64b7187f2fa5f27612528608cb1eead714cff..53aeb12f9a51e53dcd950de198971ffa133ce7b8 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15307,14 +15307,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
     <primary>format_type</primary>
    </indexterm>
 
-   <indexterm>
-    <primary>pg_describe_object</primary>
-   </indexterm>
-
-   <indexterm>
-    <primary>pg_identify_object</primary>
-   </indexterm>
-
    <indexterm>
     <primary>pg_get_constraintdef</primary>
    </indexterm>
@@ -15429,16 +15421,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
        <entry><type>text</type></entry>
        <entry>get SQL name of a data type</entry>
       </row>
-      <row>
-       <entry><literal><function>pg_describe_object(<parameter>catalog_id</parameter>, <parameter>object_id</parameter>, <parameter>object_sub_id</parameter>)</function></literal></entry>
-       <entry><type>text</type></entry>
-       <entry>get description of a database object</entry>
-      </row>
-      <row>
-       <entry><literal><function>pg_identify_object(<parameter>catalog_id</parameter> <type>oid</>, <parameter>object_id</parameter> <type>oid</>, <parameter>object_sub_id</parameter> <type>integer</>)</function></literal></entry>
-       <entry><parameter>type</> <type>text</>, <parameter>schema</> <type>text</>, <parameter>name</> <type>text</>, <parameter>identity</> <type>text</></entry>
-       <entry>get identity of a database object</entry>
-      </row>
       <row>
        <entry><literal><function>pg_get_constraintdef(<parameter>constraint_oid</parameter>)</function></literal></entry>
        <entry><type>text</type></entry>
@@ -15707,31 +15689,6 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
    <structname>pg_class</> catalogs.
   </para>
 
-  <para>
-   <function>pg_describe_object</function> returns a textual description of a database
-   object specified by catalog OID, object OID and a (possibly zero) sub-object ID.
-   This description is intended to be human-readable, and might be translated,
-   depending on server configuration.
-   This is useful to determine the identity of an object as stored in the
-   <structname>pg_depend</structname> catalog.
-  </para>
-
-  <para>
-   <function>pg_identify_object</function> returns a row containing enough information
-   to uniquely identify the database object specified by catalog OID, object OID and a
-   (possibly zero) sub-object ID.  This information is intended to be machine-readable,
-   and is never translated.
-   <parameter>type</> identifies the type of database object;
-   <parameter>schema</> is the schema name that the object belongs in, or
-   <literal>NULL</> for object types that do not belong to schemas;
-   <parameter>name</> is the name of the object, quoted if necessary, only
-   present if it can be used (alongside schema name, if pertinent) as a unique
-   identifier of the object, otherwise <literal>NULL</>;
-   <parameter>identity</> is the complete object identity, with the precise format
-   depending on object type, and each part within the format being
-   schema-qualified and quoted as necessary.
-  </para>
-
   <para>
    <function>pg_typeof</function> returns the OID of the data type of the
    value that is passed to it.  This can be helpful for troubleshooting or
@@ -15790,6 +15747,112 @@ SELECT collation for ('foo' COLLATE "de_DE");
    the given name matches multiple objects).
   </para>
 
+   <indexterm>
+    <primary>pg_describe_object</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>pg_identify_object</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>pg_identify_object_as_address</primary>
+   </indexterm>
+
+   <indexterm>
+    <primary>pg_get_object_address</primary>
+   </indexterm>
+
+  <para>
+   <xref linkend="functions-info-object-table"> lists functions related to
+   database object identification and addressing.
+  </para>
+
+   <table id="functions-info-object-table">
+    <title>Object Information and Addressing Functions</title>
+    <tgroup cols="3">
+     <thead>
+      <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+     </thead>
+
+     <tbody>
+      <row>
+       <entry><literal><function>pg_describe_object(<parameter>catalog_id</parameter>, <parameter>object_id</parameter>, <parameter>object_sub_id</parameter>)</function></literal></entry>
+       <entry><type>text</type></entry>
+       <entry>get description of a database object</entry>
+      </row>
+      <row>
+       <entry><literal><function>pg_identify_object(<parameter>catalog_id</parameter> <type>oid</>, <parameter>object_id</parameter> <type>oid</>, <parameter>object_sub_id</parameter> <type>integer</>)</function></literal></entry>
+       <entry><parameter>type</> <type>text</>, <parameter>schema</> <type>text</>, <parameter>name</> <type>text</>, <parameter>identity</> <type>text</></entry>
+       <entry>get identity of a database object</entry>
+      </row>
+      <row>
+       <entry><literal><function>pg_identify_object_as_address(<parameter>catalog_id</parameter> <type>oid</>, <parameter>object_id</parameter> <type>oid</>, <parameter>object_sub_id</parameter> <type>integer</>)</function></literal></entry>
+       <entry><parameter>type</> <type>text</>, <parameter>name</> <type>text[]</>, <parameter>args</> <type>text[]</></entry>
+       <entry>get external representation of a database object's address</entry>
+      </row>
+      <row>
+       <entry><literal><function>pg_get_object_address(<parameter>type</parameter> <type>text</>, <parameter>name</parameter> <type>text[]</>, <parameter>args</parameter> <type>text[]</>)</function></literal></entry>
+       <entry><parameter>catalog_id</> <type>oid</>, <parameter>object_id</> <type>oid</>, <parameter>object_sub_id</> <type>int32</></entry>
+       <entry>get address of a database object, from its external representation</entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+  <para>
+   <function>pg_describe_object</function> returns a textual description of a database
+   object specified by catalog OID, object OID and a (possibly zero) sub-object ID.
+   This description is intended to be human-readable, and might be translated,
+   depending on server configuration.
+   This is useful to determine the identity of an object as stored in the
+   <structname>pg_depend</structname> catalog.
+  </para>
+
+  <para>
+   <function>pg_identify_object</function> returns a row containing enough information
+   to uniquely identify the database object specified by catalog OID, object OID and a
+   (possibly zero) sub-object ID.  This information is intended to be machine-readable,
+   and is never translated.
+   <parameter>type</> identifies the type of database object;
+   <parameter>schema</> is the schema name that the object belongs in, or
+   <literal>NULL</> for object types that do not belong to schemas;
+   <parameter>name</> is the name of the object, quoted if necessary, only
+   present if it can be used (alongside schema name, if pertinent) as a unique
+   identifier of the object, otherwise <literal>NULL</>;
+   <parameter>identity</> is the complete object identity, with the precise format
+   depending on object type, and each part within the format being
+   schema-qualified and quoted as necessary.
+  </para>
+
+  <para>
+   <function>pg_identify_object_as_address</function> returns a row containing
+   enough information to uniquely identify the database object specified by
+   catalog OID, object OID and a (possibly zero) sub-object ID.  The returned
+   information is independent of the current server, that is, it could be used
+   to identify an identically named object in another server.
+   <parameter>type</> identifies the type of database object;
+   <parameter>name</> and <parameter>args</> are text arrays that together
+   form a reference to the object.  These three columns can be passed to
+   <function>pg_get_object_address</> to obtain the internal address
+   of the object.
+   This function is the inverse of <function>pg_get_object_address</function>.
+  </para>
+
+  <para>
+   <function>pg_get_object_address</function> returns a row containing enough
+   information to uniquely identify the database object specified by its
+   type and object name and argument arrays.  The returned values are the
+   ones that would be used in system catalogs such as <structname>pg_depend</>
+   and can be passed to other system functions such as
+   <function>pg_identify_object</> or <function>pg_describe_object</>.
+   <parameter>catalog_id</> is the OID of the system catalog containing the
+   object;
+   <parameter>object_id</> is the OID of the object itself, and
+   <parameter>object_sub_id</> is the object sub-ID, or zero if none.
+   This function is the inverse of <function>pg_identify_object_as_address</function>.
+  </para>
+
    <indexterm>
     <primary>col_description</primary>
    </indexterm>
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 9ca609d886881d93b8daac268699496b5e52a66b..cd763b3b91d03dc95865b6391c1b3d9870002d80 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -74,6 +74,7 @@
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
+#include "utils/memutils.h"
 #include "utils/syscache.h"
 #include "utils/tqual.h"
 
@@ -556,8 +557,9 @@ 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);
+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
@@ -2931,6 +2933,66 @@ pg_identify_object(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
 }
 
+/*
+ * SQL-level callable function to obtain object type + identity
+ */
+Datum
+pg_identify_object_as_address(PG_FUNCTION_ARGS)
+{
+	Oid			classid = PG_GETARG_OID(0);
+	Oid			objid = PG_GETARG_OID(1);
+	int32		subobjid = PG_GETARG_INT32(2);
+	ObjectAddress address;
+	char	   *identity;
+	List	   *names;
+	List	   *args;
+	Datum		values[3];
+	bool		nulls[3];
+	TupleDesc	tupdesc;
+	HeapTuple	htup;
+
+	address.classId = classid;
+	address.objectId = objid;
+	address.objectSubId = subobjid;
+
+	/*
+	 * Construct a tuple descriptor for the result row.  This must match this
+	 * function's pg_proc entry!
+	 */
+	tupdesc = CreateTemplateTupleDesc(3, false);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "type",
+					   TEXTOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "object_names",
+					   TEXTARRAYOID, -1, 0);
+	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "object_args",
+					   TEXTARRAYOID, -1, 0);
+
+	tupdesc = BlessTupleDesc(tupdesc);
+
+	/* object type */
+	values[0] = CStringGetTextDatum(getObjectTypeDescription(&address));
+	nulls[0] = false;
+
+	/* object identity */
+	identity = getObjectIdentityParts(&address, &names, &args);
+	pfree(identity);
+
+	/* object_names */
+	values[1] = PointerGetDatum(strlist_to_textarray(names));
+	nulls[1] = false;
+
+	/* object_args */
+	if (args)
+		values[2] = PointerGetDatum(strlist_to_textarray(args));
+	else
+		values[2] = PointerGetDatum(construct_empty_array(TEXTOID));
+	nulls[2] = false;
+
+	htup = heap_form_tuple(tupdesc, values, nulls);
+
+	PG_RETURN_DATUM(HeapTupleGetDatum(htup));
+}
+
 /*
  * Return a palloc'ed string that describes the type of object that the
  * passed address is for.
@@ -3187,22 +3249,50 @@ getProcedureTypeDescription(StringInfo buffer, Oid procid)
 }
 
 /*
- * Return a palloc'ed string that identifies an object.
+ * 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.
  */
 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 C-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; and
+	 * initialize them to empty lists.  For objname this is useless because it
+	 * will be initialized in all cases inside the switch; but we do it anyway
+	 * so that we can test below that no branch leaves it unset.
+	 */
+	Assert(PointerIsValid(objname) == PointerIsValid(objargs));
+	if (objname)
+	{
+		*objname = NIL;
+		*objargs = NIL;
+	}
+
 	switch (getObjectClass(object))
 	{
 		case OCLASS_CLASS:
-			getRelationIdentity(&buffer, object->objectId);
+			getRelationIdentity(&buffer, object->objectId, objname);
 			if (object->objectSubId != 0)
 			{
 				char	   *attr;
@@ -3210,17 +3300,27 @@ getObjectIdentity(const ObjectAddress *object)
 				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:
-			appendStringInfoString(&buffer,
-								 format_type_be_qualified(object->objectId));
+			{
+				char *typeout;
+
+				typeout = format_type_be_qualified(object->objectId);
+				appendStringInfoString(&buffer, typeout);
+				if (objname)
+					*objname = list_make1(typeout);
+			}
 			break;
 
 		case OCLASS_CAST:
@@ -3243,6 +3343,12 @@ getObjectIdentity(const ObjectAddress *object)
 							  format_type_be_qualified(castForm->castsource),
 							 format_type_be_qualified(castForm->casttarget));
 
+				if (objname)
+				{
+					*objname = list_make1(format_type_be_qualified(castForm->castsource));
+					*objargs = list_make1(format_type_be_qualified(castForm->casttarget));
+				}
+
 				heap_close(castRel, AccessShareLock);
 				break;
 			}
@@ -3263,6 +3369,8 @@ getObjectIdentity(const ObjectAddress *object)
 				appendStringInfoString(&buffer,
 									   quote_qualified_identifier(schema,
 												   NameStr(coll->collname)));
+				if (objname)
+					*objname = list_make2(schema, NameStr(coll->collname));
 				ReleaseSysCache(collTup);
 				break;
 			}
@@ -3283,19 +3391,25 @@ getObjectIdentity(const ObjectAddress *object)
 				{
 					appendStringInfo(&buffer, "%s on ",
 									 quote_identifier(NameStr(con->conname)));
-					getRelationIdentity(&buffer, con->conrelid);
+					getRelationIdentity(&buffer, con->conrelid, objname);
+					if (objname)
+						*objname = lappend(*objname, pstrdup(NameStr(con->conname)));
 				}
 				else
 				{
 					ObjectAddress domain;
 
+					Assert(OidIsValid(con->contypid));
 					domain.classId = TypeRelationId;
 					domain.objectId = con->contypid;
 					domain.objectSubId = 0;
 
 					appendStringInfo(&buffer, "%s on %s",
 									 quote_identifier(NameStr(con->conname)),
-									 getObjectIdentity(&domain));
+									 getObjectIdentityParts(&domain, objname, objargs));
+
+					if (objname)
+						*objargs = lappend(*objargs, pstrdup(NameStr(con->conname)));
 				}
 
 				ReleaseSysCache(conTup);
@@ -3315,6 +3429,8 @@ getObjectIdentity(const ObjectAddress *object)
 				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;
 			}
@@ -3352,7 +3468,8 @@ getObjectIdentity(const ObjectAddress *object)
 				colobject.objectSubId = attrdef->adnum;
 
 				appendStringInfo(&buffer, "for %s",
-								 getObjectIdentity(&colobject));
+								 getObjectIdentityParts(&colobject,
+														objname, objargs));
 
 				systable_endscan(adscan);
 				heap_close(attrdefDesc, AccessShareLock);
@@ -3372,17 +3489,23 @@ getObjectIdentity(const ObjectAddress *object)
 				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:
@@ -3413,14 +3536,19 @@ getObjectIdentity(const ObjectAddress *object)
 												 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);
+			getOpFamilyIdentity(&buffer, object->objectId, objname, objargs);
 			break;
 
 		case OCLASS_AMOP:
@@ -3432,6 +3560,10 @@ getObjectIdentity(const ObjectAddress *object)
 				Form_pg_amop amopForm;
 				StringInfoData opfam;
 
+				/* no objname support here */
+				if (objname)
+					*objname = NIL;
+
 				amopDesc = heap_open(AccessMethodOperatorRelationId,
 									 AccessShareLock);
 
@@ -3452,7 +3584,7 @@ getObjectIdentity(const ObjectAddress *object)
 				amopForm = (Form_pg_amop) GETSTRUCT(tup);
 
 				initStringInfo(&opfam);
-				getOpFamilyIdentity(&opfam, amopForm->amopfamily);
+				getOpFamilyIdentity(&opfam, amopForm->amopfamily, NULL, NULL);
 
 				appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
 								 amopForm->amopstrategy,
@@ -3476,6 +3608,10 @@ getObjectIdentity(const ObjectAddress *object)
 				Form_pg_amproc amprocForm;
 				StringInfoData opfam;
 
+				/* no objname support here */
+				if (objname)
+					*objname = NIL;
+
 				amprocDesc = heap_open(AccessMethodProcedureRelationId,
 									   AccessShareLock);
 
@@ -3496,7 +3632,7 @@ getObjectIdentity(const ObjectAddress *object)
 				amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
 
 				initStringInfo(&opfam);
-				getOpFamilyIdentity(&opfam, amprocForm->amprocfamily);
+				getOpFamilyIdentity(&opfam, amprocForm->amprocfamily, NULL, NULL);
 
 				appendStringInfo(&buffer, "function %d (%s, %s) of %s",
 								 amprocForm->amprocnum,
@@ -3529,7 +3665,9 @@ getObjectIdentity(const ObjectAddress *object)
 
 				appendStringInfo(&buffer, "%s on ",
 								 quote_identifier(NameStr(rule->rulename)));
-				getRelationIdentity(&buffer, rule->ev_class);
+				getRelationIdentity(&buffer, rule->ev_class, objname);
+				if (objname)
+					*objname = lappend(*objname, NameStr(rule->rulename));
 
 				heap_close(ruleDesc, AccessShareLock);
 				break;
@@ -3553,7 +3691,9 @@ getObjectIdentity(const ObjectAddress *object)
 
 				appendStringInfo(&buffer, "%s on ",
 								 quote_identifier(NameStr(trig->tgname)));
-				getRelationIdentity(&buffer, trig->tgrelid);
+				getRelationIdentity(&buffer, trig->tgrelid, objname);
+				if (objname)
+					*objname = lappend(*objname, NameStr(trig->tgname));
 
 				heap_close(trigDesc, AccessShareLock);
 				break;
@@ -3577,7 +3717,9 @@ getObjectIdentity(const ObjectAddress *object)
 
 				appendStringInfo(&buffer, "%s on ",
 								 quote_identifier(NameStr(policy->polname)));
-				getRelationIdentity(&buffer, policy->polrelid);
+				getRelationIdentity(&buffer, policy->polrelid, objname);
+				if (objname)
+					*objname = lappend(*objname, NameStr(policy->polname));
 
 				heap_close(polDesc, AccessShareLock);
 				break;
@@ -3593,6 +3735,8 @@ getObjectIdentity(const ObjectAddress *object)
 						 object->objectId);
 				appendStringInfoString(&buffer,
 									   quote_identifier(nspname));
+				if (objname)
+					*objname = list_make1(nspname);
 				break;
 			}
 
@@ -3612,6 +3756,9 @@ getObjectIdentity(const ObjectAddress *object)
 				appendStringInfoString(&buffer,
 									   quote_qualified_identifier(schema,
 											  NameStr(formParser->prsname)));
+				if (objname)
+					*objname = list_make2(schema,
+										  pstrdup(NameStr(formParser->prsname)));
 				ReleaseSysCache(tup);
 				break;
 			}
@@ -3632,6 +3779,9 @@ getObjectIdentity(const ObjectAddress *object)
 				appendStringInfoString(&buffer,
 									   quote_qualified_identifier(schema,
 											   NameStr(formDict->dictname)));
+				if (objname)
+					*objname = list_make2(schema,
+										  pstrdup(NameStr(formDict->dictname)));
 				ReleaseSysCache(tup);
 				break;
 			}
@@ -3652,7 +3802,9 @@ getObjectIdentity(const ObjectAddress *object)
 				appendStringInfoString(&buffer,
 									   quote_qualified_identifier(schema,
 											   NameStr(formTmpl->tmplname)));
-				pfree(schema);
+				if (objname)
+					*objname = list_make2(schema,
+										  pstrdup(NameStr(formTmpl->tmplname)));
 				ReleaseSysCache(tup);
 				break;
 			}
@@ -3673,6 +3825,9 @@ getObjectIdentity(const ObjectAddress *object)
 				appendStringInfoString(&buffer,
 									   quote_qualified_identifier(schema,
 												 NameStr(formCfg->cfgname)));
+				if (objname)
+					*objname = list_make2(schema,
+										  pstrdup(NameStr(formCfg->cfgname)));
 				ReleaseSysCache(tup);
 				break;
 			}
@@ -3682,6 +3837,8 @@ getObjectIdentity(const ObjectAddress *object)
 				char	   *username;
 
 				username = GetUserNameFromId(object->objectId);
+				if (objname)
+					*objname = list_make1(username);
 				appendStringInfoString(&buffer,
 									   quote_identifier(username));
 				break;
@@ -3695,6 +3852,8 @@ getObjectIdentity(const ObjectAddress *object)
 				if (!datname)
 					elog(ERROR, "cache lookup failed for database %u",
 						 object->objectId);
+				if (objname)
+					*objname = list_make1(datname);
 				appendStringInfoString(&buffer,
 									   quote_identifier(datname));
 				break;
@@ -3708,6 +3867,8 @@ getObjectIdentity(const ObjectAddress *object)
 				if (!tblspace)
 					elog(ERROR, "cache lookup failed for tablespace %u",
 						 object->objectId);
+				if (objname)
+					*objname = list_make1(tblspace);
 				appendStringInfoString(&buffer,
 									   quote_identifier(tblspace));
 				break;
@@ -3719,6 +3880,8 @@ getObjectIdentity(const ObjectAddress *object)
 
 				fdw = GetForeignDataWrapper(object->objectId);
 				appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
+				if (objname)
+					*objname = list_make1(pstrdup(fdw->fdwname));
 				break;
 			}
 
@@ -3729,6 +3892,8 @@ getObjectIdentity(const ObjectAddress *object)
 				srv = GetForeignServer(object->objectId);
 				appendStringInfoString(&buffer,
 									   quote_identifier(srv->servername));
+				if (objname)
+					*objname = list_make1(pstrdup(srv->servername));
 				break;
 			}
 
@@ -3738,6 +3903,10 @@ getObjectIdentity(const ObjectAddress *object)
 				Oid			useid;
 				const char *usename;
 
+				/* no objname support */
+				if (objname)
+					*objname = NIL;
+
 				tup = SearchSysCache1(USERMAPPINGOID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
@@ -3762,10 +3931,13 @@ getObjectIdentity(const ObjectAddress *object)
 				Relation	defaclrel;
 				ScanKeyData skey[1];
 				SysScanDesc rcscan;
-
 				HeapTuple	tup;
 				Form_pg_default_acl defacl;
 
+				/* no objname support */
+				if (objname)
+					*objname = NIL;
+
 				defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
 
 				ScanKeyInit(&skey[0],
@@ -3832,6 +4004,8 @@ getObjectIdentity(const ObjectAddress *object)
 					elog(ERROR, "cache lookup failed for extension %u",
 						 object->objectId);
 				appendStringInfoString(&buffer, quote_identifier(extname));
+				if (objname)
+					*objname = list_make1(extname);
 				break;
 			}
 
@@ -3840,6 +4014,10 @@ getObjectIdentity(const ObjectAddress *object)
 				HeapTuple	tup;
 				Form_pg_event_trigger trigForm;
 
+				/* no objname support here */
+				if (objname)
+					*objname = NIL;
+
 				tup = SearchSysCache1(EVENTTRIGGEROID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
@@ -3860,11 +4038,21 @@ getObjectIdentity(const ObjectAddress *object)
 			break;
 	}
 
+	/*
+	 * If a get_object_address representation was requested, make sure we are
+	 * providing one.  We don't check for objargs, because many of the cases
+	 * above leave it as NIL.
+	 */
+	if (objname && *objname == NIL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("requested object address for object type that cannot support it")));
+
 	return buffer.data;
 }
 
 static void
-getOpFamilyIdentity(StringInfo buffer, Oid opfid)
+getOpFamilyIdentity(StringInfo buffer, Oid opfid, List **objname, List **objargs)
 {
 	HeapTuple	opfTup;
 	Form_pg_opfamily opfForm;
@@ -3889,6 +4077,13 @@ getOpFamilyIdentity(StringInfo buffer, Oid opfid)
 												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);
 }
@@ -3898,7 +4093,7 @@ getOpFamilyIdentity(StringInfo buffer, Oid opfid)
  * StringInfo.
  */
 static void
-getRelationIdentity(StringInfo buffer, Oid relid)
+getRelationIdentity(StringInfo buffer, Oid relid, List **objname)
 {
 	HeapTuple	relTup;
 	Form_pg_class relForm;
@@ -3914,6 +4109,45 @@ getRelationIdentity(StringInfo buffer, Oid relid)
 	appendStringInfoString(buffer,
 						   quote_qualified_identifier(schema,
 												 NameStr(relForm->relname)));
+	if (objname)
+		*objname = list_make2(schema, pstrdup(NameStr(relForm->relname)));
 
 	ReleaseSysCache(relTup);
 }
+
+/*
+ * Auxiliary function to return a TEXT array out of a list of C-strings.
+ */
+ArrayType *
+strlist_to_textarray(List *list)
+{
+	ArrayType *arr;
+	Datum	*datums;
+	int		j = 0;
+	ListCell *cell;
+	MemoryContext memcxt;
+	MemoryContext oldcxt;
+
+	memcxt = AllocSetContextCreate(CurrentMemoryContext,
+								   "strlist to array",
+								   ALLOCSET_DEFAULT_MINSIZE,
+								   ALLOCSET_DEFAULT_INITSIZE,
+								   ALLOCSET_DEFAULT_MAXSIZE);
+	oldcxt = MemoryContextSwitchTo(memcxt);
+
+	datums = palloc(sizeof(text *) * list_length(list));
+	foreach(cell, list)
+	{
+		char   *name = lfirst(cell);
+
+		datums[j++] = CStringGetTextDatum(name);
+	}
+
+	MemoryContextSwitchTo(oldcxt);
+
+	arr = construct_array(datums, list_length(list),
+						  TEXTOID, -1, false, 'i');
+	MemoryContextDelete(memcxt);
+
+	return arr;
+}
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index c0314ee53227c39a6b3ac9b4b5d14b69203f2371..8cda52ba8cb342af268732452766a405f953cd9e 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -438,6 +438,41 @@ format_procedure_internal(Oid procedure_oid, bool force_qualify)
 	return result;
 }
 
+/*
+ * 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)"
  */
@@ -875,6 +910,31 @@ format_operator_qualified(Oid operator_oid)
 	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)"
  */
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 042ecef8029fd1be937f77539aa43e1509ec3e76..b4c08136f621cf79d6de9f700dcf72f084cdf092 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201412234
+#define CATALOG_VERSION_NO	201412301
 
 #endif
diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h
index d885692a43a8944f99afb1888eaf2bc691bbb971..27cae445a1a3da408ddddf3ede109ecdf3b31339 100644
--- a/src/include/catalog/objectaddress.h
+++ b/src/include/catalog/objectaddress.h
@@ -58,5 +58,8 @@ extern char *getObjectDescriptionOids(Oid classid, Oid objid);
 extern int read_objtype_from_string(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);
+extern ArrayType *strlist_to_textarray(List *list);
 
 #endif   /* OBJECTADDRESS_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 484b853a10e38de949581859bd21f125e92a4009..5c10d96ce293c72e99396cebde63fbc3574fb12c 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3036,6 +3036,9 @@ DESCR("get identification of SQL object");
 DATA(insert OID = 3839 (  pg_identify_object		PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 2249 "26 26 23" "{26,26,23,25,25,25,25}" "{i,i,i,o,o,o,o}" "{classid,objid,subobjid,type,schema,name,identity}" _null_ pg_identify_object _null_ _null_ _null_ ));
 DESCR("get machine-parseable identification of SQL object");
 
+DATA(insert OID = 3382 (  pg_identify_object_as_address	PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 2249 "26 26 23" "{26,26,23,25,1009,1009}" "{i,i,i,o,o,o}" "{classid,objid,subobjid,type,object_names,object_args}" _null_ pg_identify_object_as_address _null_ _null_ _null_ ));
+DESCR("get identification of SQL object for pg_get_object_address()");
+
 DATA(insert OID = 3954 (  pg_get_object_address    PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 2249 "25 1009 1009" "{25,1009,1009,26,26,23}" "{i,i,i,o,o,o}" "{type,name,args,classid,objid,subobjid}" _null_ pg_get_object_address _null_ _null_ _null_ ));
 DESCR("get OID-based object address from name/args arrays");
 
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 7c4d29145e9d2d2c4953572ba2daf7e15e0ec06d..e05ffb28ca4caf79a53d4e0674613b1c129e8fcf 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -642,8 +642,12 @@ extern Datum text_regclass(PG_FUNCTION_ARGS);
 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);
@@ -1194,6 +1198,7 @@ extern Datum pg_last_committed_xact(PG_FUNCTION_ARGS);
 /* catalogs/dependency.c */
 extern Datum pg_describe_object(PG_FUNCTION_ARGS);
 extern Datum pg_identify_object(PG_FUNCTION_ARGS);
+extern Datum pg_identify_object_as_address(PG_FUNCTION_ARGS);
 
 /* catalog/objectaddress.c */
 extern Datum pg_get_object_address(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 87c08daba81fe18ea3167128ca5089d271e28598..8e11b427590c7675f8c7c02f935edce3eff82a1d 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -339,46 +339,51 @@ WITH objects (type, name, args) AS (VALUES
 				-- event trigger
 				('policy', '{addr_nsp, gentable, genpol}', '{}')
         )
-SELECT (pg_identify_object(classid, objid, subobjid)).*
-  FROM objects, pg_get_object_address(type, name, args)
-ORDER BY classid, objid;
-           type            |   schema   |       name        |                               identity                               
----------------------------+------------+-------------------+----------------------------------------------------------------------
- type                      | pg_catalog | _int4             | integer[]
- type                      | addr_nsp   | gencomptype       | addr_nsp.gencomptype
- type                      | addr_nsp   | genenum           | addr_nsp.genenum
- type                      | addr_nsp   | gendomain         | addr_nsp.gendomain
- function                  | pg_catalog |                   | pg_catalog.pg_identify_object(pg_catalog.oid,pg_catalog.oid,integer)
- aggregate                 | addr_nsp   |                   | addr_nsp.genaggr(integer)
- sequence                  | addr_nsp   | gentable_a_seq    | addr_nsp.gentable_a_seq
- table                     | addr_nsp   | gentable          | addr_nsp.gentable
- table column              | addr_nsp   | gentable          | addr_nsp.gentable.b
- index                     | addr_nsp   | gentable_pkey     | addr_nsp.gentable_pkey
- view                      | addr_nsp   | genview           | addr_nsp.genview
- materialized view         | addr_nsp   | genmatview        | addr_nsp.genmatview
- foreign table column      | addr_nsp   | genftable         | addr_nsp.genftable.a
- foreign table             | addr_nsp   | genftable         | addr_nsp.genftable
- role                      |            | regtest_addr_user | regtest_addr_user
- server                    |            | addr_fserv        | addr_fserv
- foreign-data wrapper      |            | addr_fdw          | addr_fdw
- default value             |            |                   | for addr_nsp.gentable.b
- cast                      |            |                   | (bigint AS integer)
- table constraint          | addr_nsp   |                   | a_chk on addr_nsp.gentable
- domain constraint         | addr_nsp   |                   | domconstr on addr_nsp.gendomain
- conversion                | pg_catalog | ascii_to_mic      | ascii_to_mic
- language                  |            | plpgsql           | plpgsql
- schema                    |            | addr_nsp          | addr_nsp
- operator class            | pg_catalog | int4_ops          | pg_catalog.int4_ops for btree
- operator                  | pg_catalog |                   | pg_catalog.+(integer,integer)
- rule                      |            |                   | "_RETURN" on addr_nsp.genview
- trigger                   |            |                   | t on addr_nsp.gentable
- operator family           | pg_catalog | integer_ops       | pg_catalog.integer_ops for btree
- policy                    |            |                   | genpol on addr_nsp.gentable
- collation                 | pg_catalog | "default"         | pg_catalog."default"
- text search dictionary    | addr_nsp   | addr_ts_dict      | addr_nsp.addr_ts_dict
- text search parser        | addr_nsp   | addr_ts_prs       | addr_nsp.addr_ts_prs
- text search configuration | addr_nsp   | addr_ts_conf      | addr_nsp.addr_ts_conf
- text search template      | addr_nsp   | addr_ts_temp      | addr_nsp.addr_ts_temp
+SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
+	-- test roundtrip through pg_identify_object_as_address
+	ROW(pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)) =
+	ROW(pg_identify_object(addr2.classid, addr2.objid, addr2.subobjid))
+	  FROM objects, pg_get_object_address(type, name, args) addr1,
+			pg_identify_object_as_address(classid, objid, subobjid) ioa(typ,nms,args),
+			pg_get_object_address(typ, nms, ioa.args) as addr2
+	ORDER BY addr1.classid, addr1.objid;
+           type            |   schema   |       name        |                               identity                               | ?column? 
+---------------------------+------------+-------------------+----------------------------------------------------------------------+----------
+ type                      | pg_catalog | _int4             | integer[]                                                            | t
+ type                      | addr_nsp   | gencomptype       | addr_nsp.gencomptype                                                 | t
+ type                      | addr_nsp   | genenum           | addr_nsp.genenum                                                     | t
+ type                      | addr_nsp   | gendomain         | addr_nsp.gendomain                                                   | t
+ function                  | pg_catalog |                   | pg_catalog.pg_identify_object(pg_catalog.oid,pg_catalog.oid,integer) | t
+ aggregate                 | addr_nsp   |                   | addr_nsp.genaggr(integer)                                            | t
+ sequence                  | addr_nsp   | gentable_a_seq    | addr_nsp.gentable_a_seq                                              | t
+ table                     | addr_nsp   | gentable          | addr_nsp.gentable                                                    | t
+ table column              | addr_nsp   | gentable          | addr_nsp.gentable.b                                                  | t
+ index                     | addr_nsp   | gentable_pkey     | addr_nsp.gentable_pkey                                               | t
+ view                      | addr_nsp   | genview           | addr_nsp.genview                                                     | t
+ materialized view         | addr_nsp   | genmatview        | addr_nsp.genmatview                                                  | t
+ foreign table column      | addr_nsp   | genftable         | addr_nsp.genftable.a                                                 | t
+ foreign table             | addr_nsp   | genftable         | addr_nsp.genftable                                                   | t
+ role                      |            | regtest_addr_user | regtest_addr_user                                                    | t
+ server                    |            | addr_fserv        | addr_fserv                                                           | t
+ foreign-data wrapper      |            | addr_fdw          | addr_fdw                                                             | t
+ default value             |            |                   | for addr_nsp.gentable.b                                              | t
+ cast                      |            |                   | (bigint AS integer)                                                  | t
+ table constraint          | addr_nsp   |                   | a_chk on addr_nsp.gentable                                           | t
+ domain constraint         | addr_nsp   |                   | domconstr on addr_nsp.gendomain                                      | t
+ conversion                | pg_catalog | ascii_to_mic      | ascii_to_mic                                                         | t
+ language                  |            | plpgsql           | plpgsql                                                              | t
+ schema                    |            | addr_nsp          | addr_nsp                                                             | t
+ operator class            | pg_catalog | int4_ops          | pg_catalog.int4_ops for btree                                        | t
+ operator                  | pg_catalog |                   | pg_catalog.+(integer,integer)                                        | t
+ rule                      |            |                   | "_RETURN" on addr_nsp.genview                                        | t
+ trigger                   |            |                   | t on addr_nsp.gentable                                               | t
+ operator family           | pg_catalog | integer_ops       | pg_catalog.integer_ops for btree                                     | t
+ policy                    |            |                   | genpol on addr_nsp.gentable                                          | t
+ collation                 | pg_catalog | "default"         | pg_catalog."default"                                                 | t
+ text search dictionary    | addr_nsp   | addr_ts_dict      | addr_nsp.addr_ts_dict                                                | t
+ text search parser        | addr_nsp   | addr_ts_prs       | addr_nsp.addr_ts_prs                                                 | t
+ text search configuration | addr_nsp   | addr_ts_conf      | addr_nsp.addr_ts_conf                                                | t
+ text search template      | addr_nsp   | addr_ts_temp      | addr_nsp.addr_ts_temp                                                | t
 (35 rows)
 
 ---
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index dc55895d9325f7095049fa37b3ffdd2928c1a301..9fc27d8f6e61b6cd9ec9324d9bc75ef0fa353bcd 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -159,9 +159,14 @@ WITH objects (type, name, args) AS (VALUES
 				-- event trigger
 				('policy', '{addr_nsp, gentable, genpol}', '{}')
         )
-SELECT (pg_identify_object(classid, objid, subobjid)).*
-  FROM objects, pg_get_object_address(type, name, args)
-ORDER BY classid, objid;
+SELECT (pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)).*,
+	-- test roundtrip through pg_identify_object_as_address
+	ROW(pg_identify_object(addr1.classid, addr1.objid, addr1.subobjid)) =
+	ROW(pg_identify_object(addr2.classid, addr2.objid, addr2.subobjid))
+	  FROM objects, pg_get_object_address(type, name, args) addr1,
+			pg_identify_object_as_address(classid, objid, subobjid) ioa(typ,nms,args),
+			pg_get_object_address(typ, nms, ioa.args) as addr2
+	ORDER BY addr1.classid, addr1.objid;
 
 ---
 --- Cleanup resources