>From 5525571e41b91ff70c03b08ac914c020dc489e87 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Wed, 15 Oct 2014 18:03:22 -0300
Subject: [PATCH 3/3] add objname/objargs support

---
 doc/src/sgml/func.sgml               |  16 ++
 src/backend/catalog/objectaddress.c  | 315 ++++++++++++++++++++++++++++++++---
 src/backend/commands/alter.c         |   3 +-
 src/backend/commands/event_trigger.c |  57 ++++++-
 src/backend/commands/tablecmds.c     |   2 +-
 src/backend/parser/gram.y            |  16 +-
 src/backend/parser/parse_type.c      |  46 +++--
 src/backend/parser/parse_utilcmd.c   |   2 +-
 src/backend/tcop/utility.c           |   5 +-
 src/backend/utils/adt/regproc.c      |  60 +++++++
 src/include/catalog/objectaddress.h  |   3 +
 src/include/catalog/pg_proc.h        |   3 +-
 src/include/nodes/parsenodes.h       |   5 +-
 src/include/parser/parse_type.h      |   1 +
 src/include/utils/builtins.h         |   4 +
 15 files changed, 481 insertions(+), 57 deletions(-)

diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 45f3efa..dec2a67 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17625,6 +17625,22 @@ 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 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>
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index a443e84..4d0726c 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -431,6 +431,97 @@ static const ObjectPropertyType ObjectProperty[] =
 	}
 };
 
+/*
+ * 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.
+ *
+ * To ease maintenance, 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_TABCONSTRAINT },
+	{ "domain constraint", OBJECT_DOMCONSTRAINT },
+	/* 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 },		/* unmapped */
+	/* 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,
@@ -456,8 +547,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
@@ -539,11 +631,28 @@ get_object_address(ObjectType objtype, List *objname, List *objargs,
 				break;
 			case OBJECT_RULE:
 			case OBJECT_TRIGGER:
-			case OBJECT_CONSTRAINT:
+			case OBJECT_TABCONSTRAINT:
 			case OBJECT_POLICY:
 				address = get_object_address_relobject(objtype, objname,
 													   &relation, missing_ok);
 				break;
+			case OBJECT_DOMCONSTRAINT:
+				{
+					List		   *domname;
+					ObjectAddress	domaddr;
+					char		   *constrname;
+
+					domname = list_truncate(list_copy(objname), list_length(objname) - 1);
+					constrname = strVal(llast(objname));
+					domaddr = get_object_address_type(OBJECT_DOMAIN, domname, missing_ok);
+
+					address.classId = ConstraintRelationId;
+					address.objectId = get_domain_constraint_oid(domaddr.objectId,
+															  constrname, missing_ok);
+					address.objectSubId = 0;
+
+				}
+				break;
 			case OBJECT_DATABASE:
 			case OBJECT_EXTENSION:
 			case OBJECT_TABLESPACE:
@@ -943,7 +1052,7 @@ get_object_address_relobject(ObjectType objtype, List *objname,
 	const char *depname;
 
 	/* Extract name of dependent object. */
-	depname = strVal(lfirst(list_tail(objname)));
+	depname = strVal(llast(objname));
 
 	/* Separate relation name from dependent object name. */
 	nnames = list_length(objname);
@@ -999,7 +1108,7 @@ get_object_address_relobject(ObjectType objtype, List *objname,
 					get_trigger_oid(reloid, depname, missing_ok) : InvalidOid;
 				address.objectSubId = 0;
 				break;
-			case OBJECT_CONSTRAINT:
+			case OBJECT_TABCONSTRAINT:
 				address.classId = ConstraintRelationId;
 				address.objectId = relation ?
 					get_relation_constraint_oid(reloid, depname, missing_ok) :
@@ -1269,7 +1378,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
 		case OBJECT_RULE:
 		case OBJECT_TRIGGER:
 		case OBJECT_POLICY:
-		case OBJECT_CONSTRAINT:
+		case OBJECT_TABCONSTRAINT:
 			if (!pg_class_ownercheck(RelationGetRelid(relation), roleid))
 				aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
 							   RelationGetRelationName(relation));
@@ -1282,6 +1391,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
 		case OBJECT_TYPE:
 		case OBJECT_DOMAIN:
 		case OBJECT_ATTRIBUTE:
+		case OBJECT_DOMCONSTRAINT:
 			if (!pg_type_ownercheck(address.objectId, roleid))
 				aclcheck_error_type(ACLCHECK_NOT_OWNER, address.objectId);
 			break;
@@ -1461,6 +1571,34 @@ get_object_namespace(const ObjectAddress *address)
 }
 
 /*
+ * 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
@@ -2591,6 +2729,8 @@ pg_identify_object(PG_FUNCTION_ARGS)
 /*
  * 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)
@@ -2838,7 +2978,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.
@@ -2846,14 +2986,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 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 Assert() 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;
@@ -2861,17 +3029,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:
@@ -2894,6 +3072,10 @@ getObjectIdentity(const ObjectAddress *object)
 							  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;
 			}
@@ -2914,6 +3096,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;
 			}
@@ -2934,7 +3118,9 @@ 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
 				{
@@ -2946,7 +3132,10 @@ getObjectIdentity(const ObjectAddress *object)
 
 					appendStringInfo(&buffer, "%s on %s",
 									 quote_identifier(NameStr(con->conname)),
-									 getObjectIdentity(&domain));
+									 getObjectIdentityParts(&domain, objname,
+															objargs));
+					if (objname)
+						*objname = lappend(*objname, pstrdup(NameStr(con->conname)));
 				}
 
 				ReleaseSysCache(conTup);
@@ -2966,6 +3155,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;
 			}
@@ -3003,7 +3194,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);
@@ -3023,17 +3215,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:
@@ -3064,14 +3262,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:
@@ -3083,6 +3286,10 @@ getObjectIdentity(const ObjectAddress *object)
 				Form_pg_amop amopForm;
 				StringInfoData opfam;
 
+				/* no objname support here */
+				if (objname)
+					*objname = NIL;
+
 				amopDesc = heap_open(AccessMethodOperatorRelationId,
 									 AccessShareLock);
 
@@ -3103,7 +3310,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,
@@ -3127,6 +3334,10 @@ getObjectIdentity(const ObjectAddress *object)
 				Form_pg_amproc amprocForm;
 				StringInfoData opfam;
 
+				/* no objname support here */
+				if (objname)
+					*objname = NIL;
+
 				amprocDesc = heap_open(AccessMethodProcedureRelationId,
 									   AccessShareLock);
 
@@ -3147,7 +3358,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,
@@ -3180,7 +3391,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;
@@ -3204,7 +3417,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;
@@ -3220,6 +3435,8 @@ getObjectIdentity(const ObjectAddress *object)
 						 object->objectId);
 				appendStringInfoString(&buffer,
 									   quote_identifier(nspname));
+				if (objname)
+					*objname = list_make1(nspname);
 				break;
 			}
 
@@ -3239,6 +3456,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;
 			}
@@ -3259,6 +3479,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;
 			}
@@ -3279,7 +3502,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;
 			}
@@ -3300,6 +3525,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;
 			}
@@ -3308,6 +3536,9 @@ getObjectIdentity(const ObjectAddress *object)
 			{
 				char	   *username;
 
+				/* no objname support here */
+				Assert(objname == NULL);
+
 				username = GetUserNameFromId(object->objectId);
 				appendStringInfoString(&buffer,
 									   quote_identifier(username));
@@ -3318,6 +3549,9 @@ getObjectIdentity(const ObjectAddress *object)
 			{
 				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",
@@ -3331,6 +3565,9 @@ getObjectIdentity(const ObjectAddress *object)
 			{
 				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",
@@ -3346,6 +3583,8 @@ getObjectIdentity(const ObjectAddress *object)
 
 				fdw = GetForeignDataWrapper(object->objectId);
 				appendStringInfoString(&buffer, quote_identifier(fdw->fdwname));
+				if (objname)
+					*objname = list_make1(pstrdup(fdw->fdwname));
 				break;
 			}
 
@@ -3356,6 +3595,8 @@ getObjectIdentity(const ObjectAddress *object)
 				srv = GetForeignServer(object->objectId);
 				appendStringInfoString(&buffer,
 									   quote_identifier(srv->servername));
+				if (objname)
+					*objname = list_make1(pstrdup(srv->servername));
 				break;
 			}
 
@@ -3365,6 +3606,8 @@ getObjectIdentity(const ObjectAddress *object)
 				Oid			useid;
 				const char *usename;
 
+				/* XXX get_object_address doesn't seem to support this */
+
 				tup = SearchSysCache1(USERMAPPINGOID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
@@ -3389,10 +3632,15 @@ getObjectIdentity(const ObjectAddress *object)
 				Relation	defaclrel;
 				ScanKeyData skey[1];
 				SysScanDesc rcscan;
-
 				HeapTuple	tup;
 				Form_pg_default_acl defacl;
 
+				/*
+				 * There is no valid representation for default ACL objects for
+				 * get_object_address; disallow callers from asking for it.
+				 */
+				Assert(!objname);
+
 				defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
 
 				ScanKeyInit(&skey[0],
@@ -3459,6 +3707,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;
 			}
 
@@ -3467,6 +3717,9 @@ getObjectIdentity(const ObjectAddress *object)
 				HeapTuple	tup;
 				Form_pg_event_trigger trigForm;
 
+				/* no objname support here */
+				Assert(objname == NULL);
+
 				tup = SearchSysCache1(EVENTTRIGGEROID,
 									  ObjectIdGetDatum(object->objectId));
 				if (!HeapTupleIsValid(tup))
@@ -3487,11 +3740,18 @@ 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.
+	 */
+	Assert(!objname || *objname != NIL);
+
 	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;
@@ -3516,6 +3776,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);
 }
@@ -3525,7 +3792,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;
@@ -3541,6 +3808,8 @@ 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);
 }
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index c9a9baf..e7f4ef3 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -305,7 +305,8 @@ ExecRenameStmt(RenameStmt *stmt)
 {
 	switch (stmt->renameType)
 	{
-		case OBJECT_CONSTRAINT:
+		case OBJECT_TABCONSTRAINT:
+		case OBJECT_DOMCONSTRAINT:
 			return RenameConstraint(stmt);
 
 		case OBJECT_DATABASE:
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 6cb067e..bb55bbc 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -112,6 +112,8 @@ typedef struct SQLDropObject
 	const char *objname;
 	const char *objidentity;
 	const char *objecttype;
+	List	   *addrnames;
+	List	   *addrargs;
 	bool		original;
 	bool		normal;
 	slist_node	next;
@@ -923,11 +925,12 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		case OBJECT_ATTRIBUTE:
 		case OBJECT_CAST:
 		case OBJECT_COLUMN:
-		case OBJECT_CONSTRAINT:
+		case OBJECT_COMPOSITE:
 		case OBJECT_COLLATION:
 		case OBJECT_CONVERSION:
 		case OBJECT_DEFAULT:
 		case OBJECT_DOMAIN:
+		case OBJECT_DOMCONSTRAINT:
 		case OBJECT_EXTENSION:
 		case OBJECT_FDW:
 		case OBJECT_FOREIGN_SERVER:
@@ -944,6 +947,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		case OBJECT_RULE:
 		case OBJECT_SCHEMA:
 		case OBJECT_SEQUENCE:
+		case OBJECT_TABCONSTRAINT:
 		case OBJECT_TABLE:
 		case OBJECT_TRIGGER:
 		case OBJECT_TSCONFIGURATION:
@@ -951,6 +955,7 @@ EventTriggerSupportsObjectType(ObjectType obtype)
 		case OBJECT_TSPARSER:
 		case OBJECT_TSTEMPLATE:
 		case OBJECT_TYPE:
+		case OBJECT_USER_MAPPING:
 		case OBJECT_VIEW:
 			return true;
 	}
@@ -1190,10 +1195,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);
@@ -1202,6 +1208,33 @@ EventTriggerSQLDropAddObject(const ObjectAddress *object, bool original, bool no
 }
 
 /*
+ * helper for pg_event_trigger_dropped_object
+ *
+ * Make an array of text Datum out of a list of C strings.
+ */
+static Datum
+strlist_to_textarray(List *list)
+{
+	ArrayType *arr;
+	Datum	*datums;
+	int		j = 0;
+	ListCell *cell;
+
+	datums = palloc(sizeof(text *) * list_length(list));
+	foreach(cell, list)
+	{
+		char   *name = lfirst(cell);
+
+		datums[j++] = CStringGetTextDatum(name);
+	}
+
+	arr = construct_array(datums, list_length(list),
+						  TEXTOID, -1, false, 'i');
+
+	return PointerGetDatum(arr);
+}
+
+/*
  * pg_event_trigger_dropped_objects
  *
  * Make the list of dropped objects available to the user function run by the
@@ -1256,8 +1289,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);
 
@@ -1300,6 +1333,18 @@ pg_event_trigger_dropped_objects(PG_FUNCTION_ARGS)
 		else
 			nulls[i++] = true;
 
+		/* address_names */
+		if (obj->addrnames)
+			values[i++] = strlist_to_textarray(obj->addrnames);
+		else
+			nulls[i++] = true;
+
+		/* address_args */
+		if (obj->addrargs)
+			values[i++] = strlist_to_textarray(obj->addrargs);
+		else
+			nulls[i++] = true;
+
 		tuplestore_putvalues(tupstore, tupdesc, values, nulls);
 	}
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index ecdff1e..c6e06f0 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -2457,7 +2457,7 @@ RenameConstraint(RenameStmt *stmt)
 	Oid			relid = InvalidOid;
 	Oid			typid = InvalidOid;
 
-	if (stmt->relationType == OBJECT_DOMAIN)
+	if (stmt->renameType == OBJECT_DOMCONSTRAINT)
 	{
 		Relation	rel;
 		HeapTuple	tup;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index c98c27a..8ea581e 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -5611,12 +5611,21 @@ CommentStmt:
 			| COMMENT ON CONSTRAINT name ON any_name IS comment_text
 				{
 					CommentStmt *n = makeNode(CommentStmt);
-					n->objtype = OBJECT_CONSTRAINT;
+					n->objtype = OBJECT_TABCONSTRAINT;
 					n->objname = lappend($6, makeString($4));
 					n->objargs = NIL;
 					n->comment = $8;
 					$$ = (Node *) n;
 				}
+			| COMMENT ON CONSTRAINT name ON DOMAIN_P any_name IS comment_text
+				{
+					CommentStmt *n = makeNode(CommentStmt);
+					n->objtype = OBJECT_DOMCONSTRAINT;
+					n->objname = lappend($7, makeString($4));
+					n->objargs = NIL;
+					n->comment = $9;
+					$$ = (Node *) n;
+				}
 			| COMMENT ON RULE name ON any_name IS comment_text
 				{
 					CommentStmt *n = makeNode(CommentStmt);
@@ -7301,8 +7310,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
 			| ALTER DOMAIN_P any_name RENAME CONSTRAINT name TO name
 				{
 					RenameStmt *n = makeNode(RenameStmt);
-					n->renameType = OBJECT_CONSTRAINT;
-					n->relationType = OBJECT_DOMAIN;
+					n->renameType = OBJECT_DOMCONSTRAINT;
 					n->object = $3;
 					n->subname = $6;
 					n->newname = $8;
@@ -7570,7 +7578,7 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name
 			| ALTER TABLE relation_expr RENAME CONSTRAINT name TO name
 				{
 					RenameStmt *n = makeNode(RenameStmt);
-					n->renameType = OBJECT_CONSTRAINT;
+					n->renameType = OBJECT_TABCONSTRAINT;
 					n->relationType = OBJECT_TABLE;
 					n->relation = $3;
 					n->subname = $6;
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index d0803df..299cc53 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -705,13 +705,11 @@ 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.
+ * the string and return the result as a TypeName.
+ * If the string cannot be parsed as a type, an error is raised.
  */
-void
-parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
-				bool missing_ok)
+TypeName *
+typeStringToTypeName(const char *str)
 {
 	StringInfoData buf;
 	List	   *raw_parsetree_list;
@@ -720,7 +718,6 @@ 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))
@@ -779,6 +776,7 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
 		typecast->arg == NULL ||
 		!IsA(typecast->arg, A_Const))
 		goto fail;
+
 	typeName = typecast->typeName;
 	if (typeName == NULL ||
 		!IsA(typeName, TypeName))
@@ -786,6 +784,31 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
 	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,13 +831,4 @@ 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)));
 }
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 7c1939f..04c8595 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -871,7 +871,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
 			{
 				CommentStmt *stmt = makeNode(CommentStmt);
 
-				stmt->objtype = OBJECT_CONSTRAINT;
+				stmt->objtype = OBJECT_TABCONSTRAINT;
 				stmt->objname = list_make3(makeString(cxt->relation->schemaname),
 										   makeString(cxt->relation->relname),
 										   makeString(n->conname));
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4a2a339..2b93015 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1587,9 +1587,6 @@ AlterObjectTypeCommandTag(ObjectType objtype)
 		case OBJECT_COLUMN:
 			tag = "ALTER TABLE";
 			break;
-		case OBJECT_CONSTRAINT:
-			tag = "ALTER TABLE";
-			break;
 		case OBJECT_CONVERSION:
 			tag = "ALTER CONVERSION";
 			break;
@@ -1597,6 +1594,7 @@ AlterObjectTypeCommandTag(ObjectType objtype)
 			tag = "ALTER DATABASE";
 			break;
 		case OBJECT_DOMAIN:
+		case OBJECT_DOMCONSTRAINT:
 			tag = "ALTER DOMAIN";
 			break;
 		case OBJECT_EXTENSION:
@@ -1648,6 +1646,7 @@ AlterObjectTypeCommandTag(ObjectType objtype)
 			tag = "ALTER SEQUENCE";
 			break;
 		case OBJECT_TABLE:
+		case OBJECT_TABCONSTRAINT:
 			tag = "ALTER TABLE";
 			break;
 		case OBJECT_TABLESPACE:
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 2a9431d..997a156 100644
--- a/src/include/catalog/objectaddress.h
+++ b/src/include/catalog/objectaddress.h
@@ -55,7 +55,10 @@ extern HeapTuple get_catalog_object_by_oid(Relation catalog,
 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 */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c4e932f..a5c8b05 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4988,7 +4988,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");
 
 /* generic transition functions for ordered-set aggregates */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 5a092d8..54f6082 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1208,12 +1208,13 @@ typedef enum ObjectType
 	OBJECT_ATTRIBUTE,			/* type's attribute, when distinct from column */
 	OBJECT_CAST,
 	OBJECT_COLUMN,
-	OBJECT_CONSTRAINT,
+	OBJECT_COMPOSITE,
 	OBJECT_COLLATION,
 	OBJECT_CONVERSION,
 	OBJECT_DATABASE,
 	OBJECT_DEFAULT,
 	OBJECT_DOMAIN,
+	OBJECT_DOMCONSTRAINT,
 	OBJECT_EVENT_TRIGGER,
 	OBJECT_EXTENSION,
 	OBJECT_FDW,
@@ -1232,6 +1233,7 @@ typedef enum ObjectType
 	OBJECT_RULE,
 	OBJECT_SCHEMA,
 	OBJECT_SEQUENCE,
+	OBJECT_TABCONSTRAINT,
 	OBJECT_TABLE,
 	OBJECT_TABLESPACE,
 	OBJECT_TRIGGER,
@@ -1240,6 +1242,7 @@ typedef enum ObjectType
 	OBJECT_TSPARSER,
 	OBJECT_TSTEMPLATE,
 	OBJECT_TYPE,
+	OBJECT_USER_MAPPING,
 	OBJECT_VIEW
 } ObjectType;
 
diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h
index fa9cc59..152bdbb 100644
--- a/src/include/parser/parse_type.h
+++ b/src/include/parser/parse_type.h
@@ -47,6 +47,7 @@ extern Datum stringTypeDatum(Type tp, char *string, int32 atttypmod);
 
 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)
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index fb1b4a4..c4c8473 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);
-- 
1.9.1

