(The changes in the regression test are bogus, BTW; I didn't care enough
to get them fixed before sorting out the rest of the mess.)

-- 
Álvaro Herrera                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
commit 89c8cbed0072ad4d921128b834fcb4f9e2eb4c33
Author: Alvaro Herrera <alvhe...@alvh.no-ip.org>
Date:   Mon Dec 22 18:32:43 2014 -0300

    array objname/objargs stuff

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 24c64b7..112b6a0 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17772,6 +17772,23 @@ FOR EACH ROW EXECUTE PROCEDURE suppress_redundant_updates_trigger();
          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 <function>pg_get_object_address()</function> to
+         recreate the object address in a remote server containing an
+         identically named object of the same kind.
+        </entry>
+       </row>
+       <row>
+        <entry><literal>address_args</literal></entry>
+        <entry><type>text[]</type></entry>
+        <entry>
+         Complement for <literal>address_names</literal> above.
+        </entry>
+       </row>
       </tbody>
      </tgroup>
     </informaltable>
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 85079d6..789af5f 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
@@ -2960,6 +2962,66 @@ pg_identify_object(PG_FUNCTION_ARGS)
 }
 
 /*
+ * 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.
  *
@@ -3215,7 +3277,7 @@ 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.
@@ -3223,14 +3285,42 @@ getProcedureTypeDescription(StringInfo buffer, Oid procid)
 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;
@@ -3238,17 +3328,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:
@@ -3271,6 +3371,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;
 			}
@@ -3291,6 +3397,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;
 			}
@@ -3311,19 +3419,35 @@ 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;
+					HeapTuple	domainTup;
+					Form_pg_type typ;
 
-					domain.classId = TypeRelationId;
-					domain.objectId = con->contypid;
-					domain.objectSubId = 0;
+					Assert(OidIsValid(con->contypid));
+					domainTup = SearchSysCache1(TYPEOID,
+												ObjectIdGetDatum(con->contypid));
+					if (!HeapTupleIsValid(domainTup))
+						elog(ERROR, "cache lookup failed for domain %u",
+							 con->contypid);
+					typ = (Form_pg_type) GETSTRUCT(domainTup);
 
 					appendStringInfo(&buffer, "%s on %s",
 									 quote_identifier(NameStr(con->conname)),
-									 getObjectIdentity(&domain));
+									 quote_qualified_identifier(get_namespace_name(typ->typnamespace),
+																NameStr(typ->typname)));
+
+					if (objname)
+					{
+						*objname = lappend(*objname, get_namespace_name(typ->typnamespace));
+						*objname = lappend(*objname, NameStr(typ->typname));
+						*objname = lappend(*objname, pstrdup(NameStr(con->conname)));
+					}
+					ReleaseSysCache(domainTup);
 				}
 
 				ReleaseSysCache(conTup);
@@ -3343,6 +3467,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;
 			}
@@ -3380,7 +3506,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);
@@ -3400,17 +3527,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:
@@ -3441,14 +3574,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:
@@ -3460,6 +3598,10 @@ getObjectIdentity(const ObjectAddress *object)
 				Form_pg_amop amopForm;
 				StringInfoData opfam;
 
+				/* no objname support here */
+				if (objname)
+					*objname = NIL;
+
 				amopDesc = heap_open(AccessMethodOperatorRelationId,
 									 AccessShareLock);
 
@@ -3480,7 +3622,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,
@@ -3504,6 +3646,10 @@ getObjectIdentity(const ObjectAddress *object)
 				Form_pg_amproc amprocForm;
 				StringInfoData opfam;
 
+				/* no objname support here */
+				if (objname)
+					*objname = NIL;
+
 				amprocDesc = heap_open(AccessMethodProcedureRelationId,
 									   AccessShareLock);
 
@@ -3524,7 +3670,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,
@@ -3557,7 +3703,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;
@@ -3581,7 +3729,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;
@@ -3605,7 +3755,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;
@@ -3621,6 +3773,8 @@ getObjectIdentity(const ObjectAddress *object)
 						 object->objectId);
 				appendStringInfoString(&buffer,
 									   quote_identifier(nspname));
+				if (objname)
+					*objname = list_make1(nspname);
 				break;
 			}
 
@@ -3640,6 +3794,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;
 			}
@@ -3660,6 +3817,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;
 			}
@@ -3680,7 +3840,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;
 			}
@@ -3701,6 +3863,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;
 			}
@@ -3710,6 +3875,8 @@ getObjectIdentity(const ObjectAddress *object)
 				char	   *username;
 
 				username = GetUserNameFromId(object->objectId);
+				if (objname)
+					*objname = list_make1(username);
 				appendStringInfoString(&buffer,
 									   quote_identifier(username));
 				break;
@@ -3723,6 +3890,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;
@@ -3736,6 +3905,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;
@@ -3747,6 +3918,8 @@ getObjectIdentity(const ObjectAddress *object)
 
 				fdw = GetForeignDataWrapper(object->objectId);
 				appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
+				if (objname)
+					*objname = list_make1(pstrdup(fdw->fdwname));
 				break;
 			}
 
@@ -3757,6 +3930,8 @@ getObjectIdentity(const ObjectAddress *object)
 				srv = GetForeignServer(object->objectId);
 				appendStringInfoString(&buffer,
 									   quote_identifier(srv->servername));
+				if (objname)
+					*objname = list_make1(pstrdup(srv->servername));
 				break;
 			}
 
@@ -3766,6 +3941,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))
@@ -3790,10 +3969,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],
@@ -3860,6 +4042,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;
 			}
 
@@ -3868,6 +4052,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))
@@ -3888,11 +4076,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;
@@ -3917,6 +4115,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);
 }
@@ -3926,7 +4131,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;
@@ -3942,6 +4147,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/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 34dd3c0..33f9db4 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -117,6 +117,8 @@ typedef struct SQLDropObject
 	const char *objname;
 	const char *objidentity;
 	const char *objecttype;
+	List	   *addrnames;
+	List	   *addrargs;
 	bool		original;
 	bool		normal;
 	slist_node	next;
@@ -1324,10 +1326,11 @@ EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool no
 		heap_close(catalog, AccessShareLock);
 	}
 
-	/* object identity */
-	obj->objidentity = getObjectIdentity(&obj->address);
+	/* object identity, objname and objargs */
+	obj->objidentity =
+		getObjectIdentityParts(&obj->address, &obj->addrnames, &obj->addrargs);
 
-	/* and object type, too */
+	/* object type */
 	obj->objecttype = getObjectTypeDescription(&obj->address);
 
 	slist_push_head(&(currentEventTriggerState->SQLDropList), &obj->next);
@@ -1390,8 +1393,8 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
 	{
 		SQLDropObject *obj;
 		int			i = 0;
-		Datum		values[9];
-		bool		nulls[9];
+		Datum		values[11];
+		bool		nulls[11];
 
 		obj = slist_container(SQLDropObject, next, iter.cur);
 
@@ -1434,6 +1437,18 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
 		else
 			nulls[i++] = true;
 
+		/* address_names */
+		if (obj->addrnames)
+			values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrnames));
+		else
+			nulls[i++] = true;
+
+		/* address_args */
+		if (obj->addrargs)
+			values[i++] = PointerGetDatum(strlist_to_textarray(obj->addrargs));
+		else
+			nulls[i++] = true;
+
 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
 	}
 
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index c0314ee..8cda52b 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -439,6 +439,41 @@ format_procedure_internal(Oid procedure_oid, bool force_qualify)
 }
 
 /*
+ * 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,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/objectaddress.h b/src/include/catalog/objectaddress.h
index d885692..27cae44 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 484b853..54d1f2e 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");
 
@@ -5078,7 +5081,8 @@ 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,16,16,25,25,25,25}" "{o,o,o,o,o,o,o,o,o}" "{classid, objid, objsubid, original, normal, object_type, schema_name, object_name, object_identity}" _null_ pg_event_trigger_dropped_objects _null_ _null_ _null_ ));
+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");
 DATA(insert OID = 4566 (  pg_event_trigger_table_rewrite_oid	PGNSP PGUID 12 1 0 0 0 f f f f t f s 0 0 26 "" "{26}" "{o}" "{oid}" _null_ pg_event_trigger_table_rewrite_oid _null_ _null_ _null_ ));
 DESCR("return Oid of the table getting rewritten");
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 7c4d291..e05ffb2 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 ca9a6d6..baa94a8 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -339,46 +339,47 @@ WITH objects (type, name, args) AS (VALUES
 				-- event trigger
 				('policy', '{addr_nsp, gentable, genpol}', '{}')
         )
-SELECT (pg_identify_object(classid, objid, subobjid)).*
+SELECT (pg_identify_object(classid, objid, subobjid)).*,
+       pg_identify_object_as_address(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
+           type            |   schema   |       name        |                               identity                               |                             pg_identify_object_as_address                              
+---------------------------+------------+-------------------+----------------------------------------------------------------------+----------------------------------------------------------------------------------------
+ type                      | pg_catalog | _int4             | integer[]                                                            | (type,{integer[]},{})
+ type                      | addr_nsp   | gencomptype       | addr_nsp.gencomptype                                                 | (type,{addr_nsp.gencomptype},{})
+ type                      | addr_nsp   | genenum           | addr_nsp.genenum                                                     | (type,{addr_nsp.genenum},{})
+ type                      | addr_nsp   | gendomain         | addr_nsp.gendomain                                                   | (type,{addr_nsp.gendomain},{})
+ function                  | pg_catalog |                   | pg_catalog.pg_identify_object(pg_catalog.oid,pg_catalog.oid,integer) | (function,"{pg_catalog,pg_identify_object}","{pg_catalog.oid,pg_catalog.oid,integer}")
+ aggregate                 | addr_nsp   |                   | addr_nsp.genaggr(integer)                                            | (aggregate,"{addr_nsp,genaggr}",{integer})
+ sequence                  | addr_nsp   | gentable_a_seq    | addr_nsp.gentable_a_seq                                              | (sequence,"{addr_nsp,gentable_a_seq}",{})
+ table                     | addr_nsp   | gentable          | addr_nsp.gentable                                                    | (table,"{addr_nsp,gentable}",{})
+ table column              | addr_nsp   | gentable          | addr_nsp.gentable.b                                                  | ("table column","{addr_nsp,gentable,b}",{})
+ index                     | addr_nsp   | gentable_pkey     | addr_nsp.gentable_pkey                                               | (index,"{addr_nsp,gentable_pkey}",{})
+ view                      | addr_nsp   | genview           | addr_nsp.genview                                                     | (view,"{addr_nsp,genview}",{})
+ materialized view         | addr_nsp   | genmatview        | addr_nsp.genmatview                                                  | ("materialized view","{addr_nsp,genmatview}",{})
+ foreign table column      | addr_nsp   | genftable         | addr_nsp.genftable.a                                                 | ("foreign table column","{addr_nsp,genftable,a}",{})
+ foreign table             | addr_nsp   | genftable         | addr_nsp.genftable                                                   | ("foreign table","{addr_nsp,genftable}",{})
+ role                      |            | regtest_addr_user | regtest_addr_user                                                    | (role,{regtest_addr_user},{})
+ server                    |            | addr_fserv        | addr_fserv                                                           | (server,{addr_fserv},{})
+ foreign-data wrapper      |            | addr_fdw          | addr_fdw                                                             | ("foreign-data wrapper",{addr_fdw},{})
+ default value             |            |                   | for addr_nsp.gentable.b                                              | ("default value","{addr_nsp,gentable,b}",{})
+ cast                      |            |                   | (bigint AS integer)                                                  | (cast,{bigint},{integer})
+ table constraint          | addr_nsp   |                   | a_chk on addr_nsp.gentable                                           | ("table constraint","{addr_nsp,gentable,a_chk}",{})
+ domain constraint         | addr_nsp   |                   | domconstr on addr_nsp.gendomain                                      | ("domain constraint","{addr_nsp.gendomain,domconstr}",{})
+ conversion                | pg_catalog | ascii_to_mic      | ascii_to_mic                                                         | (conversion,{ascii_to_mic},{})
+ language                  |            | plpgsql           | plpgsql                                                              | (language,{plpgsql},{})
+ schema                    |            | addr_nsp          | addr_nsp                                                             | (schema,{addr_nsp},{})
+ operator class            | pg_catalog | int4_ops          | pg_catalog.int4_ops for btree                                        | ("operator class","{pg_catalog,int4_ops}",{btree})
+ operator                  | pg_catalog |                   | pg_catalog.+(integer,integer)                                        | (operator,"{pg_catalog,+}","{integer,integer}")
+ rule                      |            |                   | "_RETURN" on addr_nsp.genview                                        | (rule,"{addr_nsp,genview,_RETURN}",{})
+ trigger                   |            |                   | t on addr_nsp.gentable                                               | (trigger,"{addr_nsp,gentable,t}",{})
+ operator family           | pg_catalog | integer_ops       | pg_catalog.integer_ops for btree                                     | ("operator family","{pg_catalog,integer_ops}",{btree})
+ policy                    |            |                   | genpol on addr_nsp.gentable                                          | (policy,"{addr_nsp,gentable,genpol}",{})
+ collation                 | pg_catalog | "default"         | pg_catalog."default"                                                 | (collation,"{pg_catalog,default}",{})
+ text search dictionary    | addr_nsp   | addr_ts_dict      | addr_nsp.addr_ts_dict                                                | ("text search dictionary","{addr_nsp,addr_ts_dict}",{})
+ text search parser        | addr_nsp   | addr_ts_prs       | addr_nsp.addr_ts_prs                                                 | ("text search parser","{addr_nsp,addr_ts_prs}",{})
+ text search configuration | addr_nsp   | addr_ts_conf      | addr_nsp.addr_ts_conf                                                | ("text search configuration","{addr_nsp,addr_ts_conf}",{})
+ text search template      | addr_nsp   | addr_ts_temp      | addr_nsp.addr_ts_temp                                                | ("text search template","{addr_nsp,addr_ts_temp}",{})
 (35 rows)
 
 ---
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index e5209b9..84032fe 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -159,7 +159,8 @@ WITH objects (type, name, args) AS (VALUES
 				-- event trigger
 				('policy', '{addr_nsp, gentable, genpol}', '{}')
         )
-SELECT (pg_identify_object(classid, objid, subobjid)).*
+SELECT (pg_identify_object(classid, objid, subobjid)).*,
+       pg_identify_object_as_address(classid, objid, subobjid)
   FROM objects, pg_get_object_address(type, name, args)
 ORDER BY classid, objid;
 
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to