.. and here's the patch.

-- 
Álvaro Herrera                http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
*** a/src/backend/catalog/dependency.c
--- b/src/backend/catalog/dependency.c
***************
*** 67,72 ****
--- 67,73 ----
  #include "commands/trigger.h"
  #include "commands/typecmds.h"
  #include "foreign/foreign.h"
+ #include "funcapi.h"
  #include "miscadmin.h"
  #include "nodes/nodeFuncs.h"
  #include "parser/parsetree.h"
***************
*** 198,203 **** static bool stack_address_present_add_flags(const ObjectAddress *object,
--- 199,210 ----
  								ObjectAddressStack *stack);
  static void getRelationDescription(StringInfo buffer, Oid relid);
  static void getOpFamilyDescription(StringInfo buffer, Oid opfid);
+ 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);
  
  
  /*
***************
*** 2193,2199 **** getObjectClass(const ObjectAddress *object)
  	/* only pg_class entries can have nonzero objectSubId */
  	if (object->classId != RelationRelationId &&
  		object->objectSubId != 0)
! 		elog(ERROR, "invalid objectSubId 0 for object class %u",
  			 object->classId);
  
  	switch (object->classId)
--- 2200,2206 ----
  	/* only pg_class entries can have nonzero objectSubId */
  	if (object->classId != RelationRelationId &&
  		object->objectSubId != 0)
! 		elog(ERROR, "invalid non-zero objectSubId for object class %u",
  			 object->classId);
  
  	switch (object->classId)
***************
*** 3087,3093 **** pg_describe_object(PG_FUNCTION_ARGS)
  	Oid			classid = PG_GETARG_OID(0);
  	Oid			objid = PG_GETARG_OID(1);
  	int32		subobjid = PG_GETARG_INT32(2);
! 	char	   *description = NULL;
  	ObjectAddress address;
  
  	/* for "pinned" items in pg_depend, return null */
--- 3094,3100 ----
  	Oid			classid = PG_GETARG_OID(0);
  	Oid			objid = PG_GETARG_OID(1);
  	int32		subobjid = PG_GETARG_INT32(2);
! 	char	   *description;
  	ObjectAddress address;
  
  	/* for "pinned" items in pg_depend, return null */
***************
*** 3101,3103 **** pg_describe_object(PG_FUNCTION_ARGS)
--- 3108,4145 ----
  	description = getObjectDescription(&address);
  	PG_RETURN_TEXT_P(cstring_to_text(description));
  }
+ 
+ Datum
+ pg_identify_object(PG_FUNCTION_ARGS)
+ {
+ 	Oid			classid = PG_GETARG_OID(0);
+ 	Oid			objid = PG_GETARG_OID(1);
+ 	int32		subobjid = PG_GETARG_INT32(2);
+ 	Oid			schema_oid = InvalidOid;
+ 	const char *objname = NULL;
+ 	ObjectAddress address;
+ 	Datum		values[4];
+ 	bool		nulls[4];
+ 	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(4, false);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "type",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "schema",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 3, "name",
+ 					   TEXTOID, -1, 0);
+ 	TupleDescInitEntry(tupdesc, (AttrNumber) 4, "identity",
+ 					   TEXTOID, -1, 0);
+ 
+ 	tupdesc = BlessTupleDesc(tupdesc);
+ 
+ 	if (is_objectclass_supported(address.classId))
+ 	{
+ 		HeapTuple	objtup;
+ 		Relation	catalog = heap_open(address.classId, AccessShareLock);
+ 
+ 		objtup = get_catalog_object_by_oid(catalog, address.objectId);
+ 		if (objtup != NULL)
+ 		{
+ 			bool		isnull;
+ 			AttrNumber	nspAttnum;
+ 			AttrNumber	nameAttnum;
+ 
+ 			nspAttnum = get_object_attnum_namespace(address.classId);
+ 			if (nspAttnum != InvalidAttrNumber)
+ 			{
+ 				schema_oid = heap_getattr(objtup, nspAttnum,
+ 										  RelationGetDescr(catalog), &isnull);
+ 				if (isnull)
+ 					elog(ERROR, "invalid null namespace in object %u/%u/%d",
+ 						 address.classId, address.objectId, address.objectSubId);
+ 			}
+ 
+ 			nameAttnum = get_object_attnum_name(address.classId);
+ 			if (nameAttnum != InvalidAttrNumber)
+ 			{
+ 				Datum	nameDatum;
+ 
+ 				nameDatum = heap_getattr(objtup, nameAttnum,
+ 										 RelationGetDescr(catalog), &isnull);
+ 				if (isnull)
+ 					elog(ERROR, "invalid null name in object %u/%u/%d",
+ 						 address.classId, address.objectId, address.objectSubId);
+ 				objname = quote_identifier(NameStr(*(DatumGetName(nameDatum))));
+ 			}
+ 		}
+ 
+ 		heap_close(catalog, AccessShareLock);
+ 	}
+ 
+ 	/* object type */
+ 	values[0] = CStringGetTextDatum(getObjectTypeDescription(&address));
+ 	nulls[0] = false;
+ 
+ 	/* schema name */
+ 	if (OidIsValid(schema_oid))
+ 	{
+ 		const char	*schema = quote_identifier(get_namespace_name(schema_oid));
+ 
+ 		values[1] = CStringGetTextDatum(schema);
+ 		nulls[1] = false;
+ 	}
+ 	else
+ 		nulls[1] = true;
+ 
+ 	/* object name */
+ 	if (objname)
+ 	{
+ 		values[2] = CStringGetTextDatum(objname);
+ 		nulls[2] = false;
+ 	}
+ 	else
+ 		nulls[2] = true;
+ 
+ 	/* object identity */
+ 	values[3] = CStringGetTextDatum(getObjectIdentity(&address));
+ 	nulls[3] = 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.
+  */
+ char *
+ getObjectTypeDescription(const ObjectAddress *object)
+ {
+ 	StringInfoData buffer;
+ 
+ 	initStringInfo(&buffer);
+ 
+ 	switch (getObjectClass(object))
+ 	{
+ 		case OCLASS_CLASS:
+ 			getRelationTypeDescription(&buffer, object->objectId,
+ 									   object->objectSubId);
+ 			break;
+ 
+ 		case OCLASS_PROC:
+ 			getProcedureTypeDescription(&buffer, object->objectId);
+ 			break;
+ 
+ 		case OCLASS_TYPE:
+ 			appendStringInfo(&buffer, "type");
+ 			break;
+ 
+ 		case OCLASS_CAST:
+ 			appendStringInfo(&buffer, "cast");
+ 			break;
+ 
+ 		case OCLASS_COLLATION:
+ 			appendStringInfo(&buffer, "collation");
+ 			break;
+ 
+ 		case OCLASS_CONSTRAINT:
+ 			getConstraintTypeDescription(&buffer, object->objectId);
+ 			break;
+ 
+ 		case OCLASS_CONVERSION:
+ 			appendStringInfo(&buffer, "conversion");
+ 			break;
+ 
+ 		case OCLASS_DEFAULT:
+ 			appendStringInfo(&buffer, "default value");
+ 			break;
+ 
+ 		case OCLASS_LANGUAGE:
+ 			appendStringInfo(&buffer, "language");
+ 			break;
+ 
+ 		case OCLASS_LARGEOBJECT:
+ 			appendStringInfo(&buffer, "large object");
+ 			break;
+ 
+ 		case OCLASS_OPERATOR:
+ 			appendStringInfo(&buffer, "operator");
+ 			break;
+ 
+ 		case OCLASS_OPCLASS:
+ 			appendStringInfo(&buffer, "operator class");
+ 			break;
+ 
+ 		case OCLASS_OPFAMILY:
+ 			appendStringInfo(&buffer, "operator family");
+ 			break;
+ 
+ 		case OCLASS_AMOP:
+ 			appendStringInfo(&buffer, "operator of access method");
+ 			break;
+ 
+ 		case OCLASS_AMPROC:
+ 			appendStringInfo(&buffer, "function of access method");
+ 			break;
+ 
+ 		case OCLASS_REWRITE:
+ 			appendStringInfo(&buffer, "rule");
+ 			break;
+ 
+ 		case OCLASS_TRIGGER:
+ 			appendStringInfo(&buffer, "trigger");
+ 			break;
+ 
+ 		case OCLASS_SCHEMA:
+ 			appendStringInfo(&buffer, "schema");
+ 			break;
+ 
+ 		case OCLASS_TSPARSER:
+ 			appendStringInfo(&buffer, "text search parser");
+ 			break;
+ 
+ 		case OCLASS_TSDICT:
+ 			appendStringInfo(&buffer, "text search dictionary");
+ 			break;
+ 
+ 		case OCLASS_TSTEMPLATE:
+ 			appendStringInfo(&buffer, "text search template");
+ 			break;
+ 
+ 		case OCLASS_TSCONFIG:
+ 			appendStringInfo(&buffer, "text search configuration");
+ 			break;
+ 
+ 		case OCLASS_ROLE:
+ 			appendStringInfo(&buffer, "role");
+ 			break;
+ 
+ 		case OCLASS_DATABASE:
+ 			appendStringInfo(&buffer, "database");
+ 			break;
+ 
+ 		case OCLASS_TBLSPACE:
+ 			appendStringInfo(&buffer, "tablespace");
+ 			break;
+ 
+ 		case OCLASS_FDW:
+ 			appendStringInfo(&buffer, "foreign-data wrapper");
+ 			break;
+ 
+ 		case OCLASS_FOREIGN_SERVER:
+ 			appendStringInfo(&buffer, "server");
+ 			break;
+ 
+ 		case OCLASS_USER_MAPPING:
+ 			appendStringInfo(&buffer, "user mapping");
+ 			break;
+ 
+ 		case OCLASS_DEFACL:
+ 			appendStringInfo(&buffer, "default acl");
+ 			break;
+ 
+ 		case OCLASS_EXTENSION:
+ 			appendStringInfo(&buffer, "extension");
+ 			break;
+ 
+ 		case OCLASS_EVENT_TRIGGER:
+ 			appendStringInfo(&buffer, "event trigger");
+ 			break;
+ 
+ 		default:
+ 			appendStringInfo(&buffer, "unrecognized %u", object->classId);
+ 			break;
+ 	}
+ 
+ 	return buffer.data;
+ }
+ 
+ /*
+  * subroutine for getObjectTypeDescription: describe a relation type
+  */
+ static void
+ getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId)
+ {
+ 	HeapTuple	relTup;
+ 	Form_pg_class relForm;
+ 
+ 	relTup = SearchSysCache1(RELOID,
+ 							 ObjectIdGetDatum(relid));
+ 	if (!HeapTupleIsValid(relTup))
+ 		elog(ERROR, "cache lookup failed for relation %u", relid);
+ 	relForm = (Form_pg_class) GETSTRUCT(relTup);
+ 
+ 	switch (relForm->relkind)
+ 	{
+ 		case RELKIND_RELATION:
+ 			appendStringInfo(buffer, "table");
+ 			break;
+ 		case RELKIND_INDEX:
+ 			appendStringInfo(buffer, "index");
+ 			break;
+ 		case RELKIND_SEQUENCE:
+ 			appendStringInfo(buffer, "sequence");
+ 			break;
+ 		case RELKIND_TOASTVALUE:
+ 			appendStringInfo(buffer, "toast table");
+ 			break;
+ 		case RELKIND_VIEW:
+ 			appendStringInfo(buffer, "view");
+ 			break;
+ 		case RELKIND_MATVIEW:
+ 			appendStringInfo(buffer, "materialized view");
+ 			break;
+ 		case RELKIND_COMPOSITE_TYPE:
+ 			appendStringInfo(buffer, "composite type");
+ 			break;
+ 		case RELKIND_FOREIGN_TABLE:
+ 			appendStringInfo(buffer, "foreign table");
+ 			break;
+ 		default:
+ 			/* shouldn't get here */
+ 			appendStringInfo(buffer, "relation");
+ 			break;
+ 	}
+ 
+ 	if (objectSubId != 0)
+ 		appendStringInfo(buffer, " column");
+ 
+ 	ReleaseSysCache(relTup);
+ }
+ 
+ /*
+  * subroutine for getObjectTypeDescription: describe a constraint type
+  */
+ static void
+ getConstraintTypeDescription(StringInfo buffer, Oid constroid)
+ {
+ 	Relation	constrRel;
+ 	HeapTuple	constrTup;
+ 	Form_pg_constraint	constrForm;
+ 
+ 	constrRel = heap_open(ConstraintRelationId, AccessShareLock);
+ 	constrTup = get_catalog_object_by_oid(constrRel, constroid);
+ 	if (!HeapTupleIsValid(constrTup))
+ 		elog(ERROR, "cache lookup failed for constraint %u", constroid);
+ 
+ 	constrForm = (Form_pg_constraint) GETSTRUCT(constrTup);
+ 
+ 	if (OidIsValid(constrForm->conrelid))
+ 		appendStringInfoString(buffer, "table constraint");
+ 	else if (OidIsValid(constrForm->contypid))
+ 		appendStringInfoString(buffer, "domain constraint");
+ 	else
+ 		elog(ERROR, "invalid constraint %u", HeapTupleGetOid(constrTup));
+ 
+ 	heap_close(constrRel, AccessShareLock);
+ }
+ 
+ /*
+  * subroutine for getObjectTypeDescription: describe a procedure type
+  */
+ static void
+ getProcedureTypeDescription(StringInfo buffer, Oid procid)
+ {
+ 	HeapTuple	procTup;
+ 	Form_pg_proc procForm;
+ 
+ 	procTup = SearchSysCache1(PROCOID,
+ 							 ObjectIdGetDatum(procid));
+ 	if (!HeapTupleIsValid(procTup))
+ 		elog(ERROR, "cache lookup failed for procedure %u", procid);
+ 	procForm = (Form_pg_proc) GETSTRUCT(procTup);
+ 
+ 	if (procForm->proisagg)
+ 		appendStringInfo(buffer, "aggregate");
+ 	else
+ 		appendStringInfo(buffer, "function");
+ 
+ 	ReleaseSysCache(procTup);
+ }
+ 
+ /*
+  * Return a palloc'ed string that identifies an object.
+  *
+  * This is for machine consumption, so it's not translated.  All elements are
+  * schema-qualified when appropriate.
+  */
+ char *
+ getObjectIdentity(const ObjectAddress *object)
+ {
+ 	StringInfoData buffer;
+ 
+ 	initStringInfo(&buffer);
+ 
+ 	switch (getObjectClass(object))
+ 	{
+ 		case OCLASS_CLASS:
+ 			getRelationIdentity(&buffer, object->objectId);
+ 			if (object->objectSubId != 0)
+ 			{
+ 				char   *attr;
+ 
+ 				attr = get_relid_attribute_name(object->objectId,
+ 												object->objectSubId);
+ 				appendStringInfo(&buffer, ".%s", quote_identifier(attr));
+ 			}
+ 			break;
+ 
+ 		case OCLASS_PROC:
+ 			appendStringInfo(&buffer, "%s",
+ 							 format_procedure_qualified(object->objectId));
+ 			break;
+ 
+ 		case OCLASS_TYPE:
+ 			appendStringInfo(&buffer, "%s",
+ 							 format_type_be_qualified(object->objectId));
+ 			break;
+ 
+ 		case OCLASS_CAST:
+ 			{
+ 				Relation	castRel;
+ 				HeapTuple	tup;
+ 				Form_pg_cast castForm;
+ 
+ 				castRel = heap_open(CastRelationId, AccessShareLock);
+ 
+ 				tup = get_catalog_object_by_oid(castRel, object->objectId);
+ 
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "could not find tuple for cast %u",
+ 						 object->objectId);
+ 
+ 				castForm = (Form_pg_cast) GETSTRUCT(tup);
+ 
+ 				appendStringInfo(&buffer, "(%s AS %s)",
+ 								 format_type_be_qualified(castForm->castsource),
+ 								 format_type_be_qualified(castForm->casttarget));
+ 
+ 				heap_close(castRel, AccessShareLock);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_COLLATION:
+ 			{
+ 				HeapTuple	collTup;
+ 				Form_pg_collation coll;
+ 				char   *schema;
+ 
+ 				collTup = SearchSysCache1(COLLOID,
+ 										  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(collTup))
+ 					elog(ERROR, "cache lookup failed for collation %u",
+ 						 object->objectId);
+ 				coll = (Form_pg_collation) GETSTRUCT(collTup);
+ 				schema = get_namespace_name(coll->collnamespace);
+ 				appendStringInfoString(&buffer,
+ 									   quote_qualified_identifier(schema,
+ 																  NameStr(coll->collname)));
+ 				ReleaseSysCache(collTup);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_CONSTRAINT:
+ 			{
+ 				HeapTuple	conTup;
+ 				Form_pg_constraint con;
+ 
+ 				conTup = SearchSysCache1(CONSTROID,
+ 										 ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(conTup))
+ 					elog(ERROR, "cache lookup failed for constraint %u",
+ 						 object->objectId);
+ 				con = (Form_pg_constraint) GETSTRUCT(conTup);
+ 
+ 				if (OidIsValid(con->conrelid))
+ 				{
+ 					appendStringInfo(&buffer, "%s on ",
+ 									 quote_identifier(NameStr(con->conname)));
+ 					getRelationIdentity(&buffer, con->conrelid);
+ 				}
+ 				else
+ 				{
+ 					ObjectAddress	domain;
+ 
+ 					domain.classId = TypeRelationId;
+ 					domain.objectId = con->contypid;
+ 					domain.objectSubId = 0;
+ 
+ 					appendStringInfo(&buffer, "%s on %s",
+ 									 quote_identifier(NameStr(con->conname)),
+ 									 getObjectIdentity(&domain));
+ 				}
+ 
+ 				ReleaseSysCache(conTup);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_CONVERSION:
+ 			{
+ 				HeapTuple	conTup;
+ 				Form_pg_conversion conForm;
+ 
+ 				conTup = SearchSysCache1(CONVOID,
+ 										 ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(conTup))
+ 					elog(ERROR, "cache lookup failed for conversion %u",
+ 						 object->objectId);
+ 				conForm = (Form_pg_conversion) GETSTRUCT(conTup);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(NameStr(conForm->conname)));
+ 				ReleaseSysCache(conTup);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_DEFAULT:
+ 			{
+ 				Relation	attrdefDesc;
+ 				ScanKeyData skey[1];
+ 				SysScanDesc adscan;
+ 
+ 				HeapTuple	tup;
+ 				Form_pg_attrdef attrdef;
+ 				ObjectAddress colobject;
+ 
+ 				attrdefDesc = heap_open(AttrDefaultRelationId, AccessShareLock);
+ 
+ 				ScanKeyInit(&skey[0],
+ 							ObjectIdAttributeNumber,
+ 							BTEqualStrategyNumber, F_OIDEQ,
+ 							ObjectIdGetDatum(object->objectId));
+ 
+ 				adscan = systable_beginscan(attrdefDesc, AttrDefaultOidIndexId,
+ 											true, SnapshotNow, 1, skey);
+ 
+ 				tup = systable_getnext(adscan);
+ 
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "could not find tuple for attrdef %u",
+ 						 object->objectId);
+ 
+ 				attrdef = (Form_pg_attrdef) GETSTRUCT(tup);
+ 
+ 				colobject.classId = RelationRelationId;
+ 				colobject.objectId = attrdef->adrelid;
+ 				colobject.objectSubId = attrdef->adnum;
+ 
+ 				appendStringInfo(&buffer, "for %s",
+ 								 getObjectIdentity(&colobject));
+ 
+ 				systable_endscan(adscan);
+ 				heap_close(attrdefDesc, AccessShareLock);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_LANGUAGE:
+ 			{
+ 				HeapTuple	langTup;
+ 				Form_pg_language langForm;
+ 
+ 				langTup = SearchSysCache1(LANGOID,
+ 										  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(langTup))
+ 					elog(ERROR, "cache lookup failed for language %u",
+ 						 object->objectId);
+ 				langForm = (Form_pg_language) GETSTRUCT(langTup);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(NameStr(langForm->lanname)));
+ 				ReleaseSysCache(langTup);
+ 				break;
+ 			}
+ 		case OCLASS_LARGEOBJECT:
+ 			appendStringInfo(&buffer, "%u",
+ 							 object->objectId);
+ 			break;
+ 
+ 		case OCLASS_OPERATOR:
+ 			appendStringInfo(&buffer, "%s",
+ 							 format_operator_qualified(object->objectId));
+ 			break;
+ 
+ 		case OCLASS_OPCLASS:
+ 			{
+ 				HeapTuple	opcTup;
+ 				Form_pg_opclass opcForm;
+ 				HeapTuple	amTup;
+ 				Form_pg_am	amForm;
+ 				char	   *schema;
+ 
+ 				opcTup = SearchSysCache1(CLAOID,
+ 										 ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(opcTup))
+ 					elog(ERROR, "cache lookup failed for opclass %u",
+ 						 object->objectId);
+ 				opcForm = (Form_pg_opclass) GETSTRUCT(opcTup);
+ 				schema = get_namespace_name(opcForm->opcnamespace);
+ 
+ 				amTup = SearchSysCache1(AMOID,
+ 										ObjectIdGetDatum(opcForm->opcmethod));
+ 				if (!HeapTupleIsValid(amTup))
+ 					elog(ERROR, "cache lookup failed for access method %u",
+ 						 opcForm->opcmethod);
+ 				amForm = (Form_pg_am) GETSTRUCT(amTup);
+ 
+ 				appendStringInfo(&buffer,
+ 								 "%s",
+ 								 quote_qualified_identifier(schema,
+ 															NameStr(opcForm->opcname)));
+ 				appendStringInfo(&buffer, " for %s",
+ 								 quote_identifier(NameStr(amForm->amname)));
+ 
+ 				ReleaseSysCache(amTup);
+ 				ReleaseSysCache(opcTup);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_OPFAMILY:
+ 			getOpFamilyIdentity(&buffer, object->objectId);
+ 			break;
+ 
+ 		case OCLASS_AMOP:
+ 			{
+ 				Relation	amopDesc;
+ 				HeapTuple	tup;
+ 				Form_pg_amop amopForm;
+ 				StringInfoData opfam;
+ 
+ 				amopDesc = heap_open(AccessMethodOperatorRelationId,
+ 									 AccessShareLock);
+ 
+ 				tup = get_catalog_object_by_oid(amopDesc, object->objectId);
+ 
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "could not find tuple for amop entry %u",
+ 						 object->objectId);
+ 
+ 				amopForm = (Form_pg_amop) GETSTRUCT(tup);
+ 
+ 				initStringInfo(&opfam);
+ 				getOpFamilyIdentity(&opfam, amopForm->amopfamily);
+ 
+ 				/* XXX is this too verbose? */
+ 				appendStringInfo(&buffer, "%d (%s, %s) of %s: %s",
+ 								 amopForm->amopstrategy,
+ 								 format_type_be_qualified(amopForm->amoplefttype),
+ 								 format_type_be_qualified(amopForm->amoprighttype),
+ 								 opfam.data,
+ 								 format_operator_qualified(amopForm->amopopr));
+ 
+ 				pfree(opfam.data);
+ 
+ 				heap_close(amopDesc, AccessShareLock);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_AMPROC:
+ 			{
+ 				Relation	amprocDesc;
+ 				HeapTuple	tup;
+ 				Form_pg_amproc amprocForm;
+ 				StringInfoData opfam;
+ 
+ 				amprocDesc = heap_open(AccessMethodProcedureRelationId,
+ 									   AccessShareLock);
+ 
+ 				tup = get_catalog_object_by_oid(amprocDesc, object->objectId);
+ 
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "could not find tuple for amproc entry %u",
+ 						 object->objectId);
+ 
+ 				amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
+ 
+ 				initStringInfo(&opfam);
+ 				getOpFamilyIdentity(&opfam, amprocForm->amprocfamily);
+ 
+ 				/* XXX is this too verbose? */
+ 				appendStringInfo(&buffer, "%d (%s, %s) of %s: %s",
+ 								 amprocForm->amprocnum,
+ 								 format_type_be_qualified(amprocForm->amproclefttype),
+ 								 format_type_be_qualified(amprocForm->amprocrighttype),
+ 								 opfam.data,
+ 								 format_procedure_qualified(amprocForm->amproc));
+ 
+ 				pfree(opfam.data);
+ 
+ 				heap_close(amprocDesc, AccessShareLock);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_REWRITE:
+ 			{
+ 				Relation	ruleDesc;
+ 				HeapTuple	tup;
+ 				Form_pg_rewrite rule;
+ 
+ 				ruleDesc = heap_open(RewriteRelationId, AccessShareLock);
+ 
+ 				tup = get_catalog_object_by_oid(ruleDesc, object->objectId);
+ 
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "could not find tuple for rule %u",
+ 						 object->objectId);
+ 
+ 				rule = (Form_pg_rewrite) GETSTRUCT(tup);
+ 
+ 				appendStringInfo(&buffer, "%s on ",
+ 								 quote_identifier(NameStr(rule->rulename)));
+ 				getRelationIdentity(&buffer, rule->ev_class);
+ 
+ 				heap_close(ruleDesc, AccessShareLock);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_TRIGGER:
+ 			{
+ 				Relation	trigDesc;
+ 				HeapTuple	tup;
+ 				Form_pg_trigger trig;
+ 
+ 				trigDesc = heap_open(TriggerRelationId, AccessShareLock);
+ 
+ 				tup = get_catalog_object_by_oid(trigDesc, object->objectId);
+ 
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "could not find tuple for trigger %u",
+ 						 object->objectId);
+ 
+ 				trig = (Form_pg_trigger) GETSTRUCT(tup);
+ 
+ 				appendStringInfo(&buffer, "%s on ",
+ 								 quote_identifier(NameStr(trig->tgname)));
+ 				getRelationIdentity(&buffer, trig->tgrelid);
+ 
+ 				heap_close(trigDesc, AccessShareLock);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_SCHEMA:
+ 			{
+ 				char	   *nspname;
+ 
+ 				nspname = get_namespace_name(object->objectId);
+ 				if (!nspname)
+ 					elog(ERROR, "cache lookup failed for namespace %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(nspname));
+ 				break;
+ 			}
+ 
+ 		case OCLASS_TSPARSER:
+ 			{
+ 				HeapTuple	tup;
+ 				Form_pg_ts_parser	formParser;
+ 
+ 				tup = SearchSysCache1(TSPARSEROID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for text search parser %u",
+ 						 object->objectId);
+ 				formParser = (Form_pg_ts_parser) GETSTRUCT(tup);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(NameStr(formParser->prsname)));
+ 				ReleaseSysCache(tup);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_TSDICT:
+ 			{
+ 				HeapTuple	tup;
+ 				Form_pg_ts_dict		formDict;
+ 
+ 				tup = SearchSysCache1(TSDICTOID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for text search dictionary %u",
+ 						 object->objectId);
+ 				formDict = (Form_pg_ts_dict) GETSTRUCT(tup);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(NameStr(formDict->dictname)));
+ 				ReleaseSysCache(tup);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_TSTEMPLATE:
+ 			{
+ 				HeapTuple	tup;
+ 				Form_pg_ts_template formTmpl;
+ 
+ 				tup = SearchSysCache1(TSTEMPLATEOID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for text search template %u",
+ 						 object->objectId);
+ 				formTmpl = (Form_pg_ts_template) GETSTRUCT(tup);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(NameStr(formTmpl->tmplname)));
+ 				ReleaseSysCache(tup);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_TSCONFIG:
+ 			{
+ 				HeapTuple	tup;
+ 				Form_pg_ts_config formCfg;
+ 
+ 				tup = SearchSysCache1(TSCONFIGOID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for text search configuration %u",
+ 						 object->objectId);
+ 				formCfg = (Form_pg_ts_config) GETSTRUCT(tup);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(NameStr(formCfg->cfgname)));
+ 				ReleaseSysCache(tup);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_ROLE:
+ 			{
+ 				char   *username;
+ 
+ 				username = GetUserNameFromId(object->objectId);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(username));
+ 				break;
+ 			}
+ 
+ 		case OCLASS_DATABASE:
+ 			{
+ 				char	   *datname;
+ 
+ 				datname = get_database_name(object->objectId);
+ 				if (!datname)
+ 					elog(ERROR, "cache lookup failed for database %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(datname));
+ 				break;
+ 			}
+ 
+ 		case OCLASS_TBLSPACE:
+ 			{
+ 				char	   *tblspace;
+ 
+ 				tblspace = get_tablespace_name(object->objectId);
+ 				if (!tblspace)
+ 					elog(ERROR, "cache lookup failed for tablespace %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(tblspace));
+ 				break;
+ 			}
+ 
+ 		case OCLASS_FDW:
+ 			{
+ 				ForeignDataWrapper *fdw;
+ 
+ 				fdw = GetForeignDataWrapper(object->objectId);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(fdw->fdwname));
+ 				break;
+ 			}
+ 
+ 		case OCLASS_FOREIGN_SERVER:
+ 			{
+ 				ForeignServer *srv;
+ 
+ 				srv = GetForeignServer(object->objectId);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(srv->servername));
+ 				break;
+ 			}
+ 
+ 		case OCLASS_USER_MAPPING:
+ 			{
+ 				HeapTuple	tup;
+ 				Oid			useid;
+ 				const char *usename;
+ 
+ 				tup = SearchSysCache1(USERMAPPINGOID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for user mapping %u",
+ 						 object->objectId);
+ 
+ 				useid = ((Form_pg_user_mapping) GETSTRUCT(tup))->umuser;
+ 
+ 				ReleaseSysCache(tup);
+ 
+ 				if (OidIsValid(useid))
+ 					usename = quote_identifier(GetUserNameFromId(useid));
+ 				else
+ 					usename = "public";
+ 
+ 				appendStringInfo(&buffer, "%s", usename);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_DEFACL:
+ 			{
+ 				Relation	defaclrel;
+ 				ScanKeyData skey[1];
+ 				SysScanDesc rcscan;
+ 
+ 				HeapTuple	tup;
+ 				Form_pg_default_acl defacl;
+ 
+ 				defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
+ 
+ 				ScanKeyInit(&skey[0],
+ 							ObjectIdAttributeNumber,
+ 							BTEqualStrategyNumber, F_OIDEQ,
+ 							ObjectIdGetDatum(object->objectId));
+ 
+ 				rcscan = systable_beginscan(defaclrel, DefaultAclOidIndexId,
+ 											true, SnapshotNow, 1, skey);
+ 
+ 				tup = systable_getnext(rcscan);
+ 
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "could not find tuple for default ACL %u",
+ 						 object->objectId);
+ 
+ 				defacl = (Form_pg_default_acl) GETSTRUCT(tup);
+ 
+ 				appendStringInfo(&buffer,
+ 								 "for role %s",
+ 								 quote_identifier(GetUserNameFromId(defacl->defaclrole)));
+ 
+ 				if (OidIsValid(defacl->defaclnamespace))
+ 				{
+ 					char   *schema;
+ 
+ 					schema = get_namespace_name(defacl->defaclnamespace);
+ 					appendStringInfo(&buffer,
+ 									 " in schema %s",
+ 									 quote_identifier(schema));
+ 				}
+ 
+ 				switch (defacl->defaclobjtype)
+ 				{
+ 					case DEFACLOBJ_RELATION:
+ 						appendStringInfoString(&buffer,
+ 											   " on tables");
+ 						break;
+ 					case DEFACLOBJ_SEQUENCE:
+ 						appendStringInfoString(&buffer,
+ 											   " on sequences");
+ 						break;
+ 					case DEFACLOBJ_FUNCTION:
+ 						appendStringInfoString(&buffer,
+ 											   " on functions");
+ 						break;
+ 					case DEFACLOBJ_TYPE:
+ 						appendStringInfoString(&buffer,
+ 											   " on types");
+ 						break;
+ 				}
+ 
+ 				systable_endscan(rcscan);
+ 				heap_close(defaclrel, AccessShareLock);
+ 				break;
+ 			}
+ 
+ 		case OCLASS_EXTENSION:
+ 			{
+ 				char	   *extname;
+ 
+ 				extname = get_extension_name(object->objectId);
+ 				if (!extname)
+ 					elog(ERROR, "cache lookup failed for extension %u",
+ 						 object->objectId);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(extname));
+ 				break;
+ 			}
+ 
+ 		case OCLASS_EVENT_TRIGGER:
+ 			{
+ 				HeapTuple	tup;
+ 				Form_pg_event_trigger trigForm;
+ 
+ 				tup = SearchSysCache1(EVENTTRIGGEROID,
+ 									  ObjectIdGetDatum(object->objectId));
+ 				if (!HeapTupleIsValid(tup))
+ 					elog(ERROR, "cache lookup failed for event trigger %u",
+ 						 object->objectId);
+ 				trigForm = (Form_pg_event_trigger) GETSTRUCT(tup);
+ 				appendStringInfo(&buffer, "%s",
+ 								 quote_identifier(NameStr(trigForm->evtname)));
+ 				ReleaseSysCache(tup);
+ 				break;
+ 			}
+ 
+ 		default:
+ 			appendStringInfo(&buffer, "unrecognized object %u %u %d",
+ 							 object->classId,
+ 							 object->objectId,
+ 							 object->objectSubId);
+ 			break;
+ 	}
+ 
+ 	return buffer.data;
+ }
+ 
+ static void
+ getOpFamilyIdentity(StringInfo buffer, Oid opfid)
+ {
+ 	HeapTuple	opfTup;
+ 	Form_pg_opfamily opfForm;
+ 	HeapTuple	amTup;
+ 	Form_pg_am	amForm;
+ 	char	   *schema;
+ 
+ 	opfTup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfid));
+ 	if (!HeapTupleIsValid(opfTup))
+ 		elog(ERROR, "cache lookup failed for opfamily %u", opfid);
+ 	opfForm = (Form_pg_opfamily) GETSTRUCT(opfTup);
+ 
+ 	amTup = SearchSysCache1(AMOID, ObjectIdGetDatum(opfForm->opfmethod));
+ 	if (!HeapTupleIsValid(amTup))
+ 		elog(ERROR, "cache lookup failed for access method %u",
+ 			 opfForm->opfmethod);
+ 	amForm = (Form_pg_am) GETSTRUCT(amTup);
+ 
+ 	schema = get_namespace_name(opfForm->opfnamespace);
+ 	appendStringInfo(buffer, "%s for %s",
+ 					 quote_qualified_identifier(schema,
+ 												NameStr(opfForm->opfname)),
+ 					 NameStr(amForm->amname));
+ 
+ 	ReleaseSysCache(amTup);
+ 	ReleaseSysCache(opfTup);
+ }
+ 
+ /*
+  * Append the relation identity (quoted qualified name) to the given
+  * StringInfo.
+  */
+ static void
+ getRelationIdentity(StringInfo buffer, Oid relid)
+ {
+ 	HeapTuple	relTup;
+ 	Form_pg_class relForm;
+ 	char	   *schema;
+ 
+ 	relTup = SearchSysCache1(RELOID,
+ 							 ObjectIdGetDatum(relid));
+ 	if (!HeapTupleIsValid(relTup))
+ 		elog(ERROR, "cache lookup failed for relation %u", relid);
+ 	relForm = (Form_pg_class) GETSTRUCT(relTup);
+ 
+ 	schema = get_namespace_name(relForm->relnamespace);
+ 	appendStringInfo(buffer, "%s",
+ 					 quote_qualified_identifier(schema,
+ 												NameStr(relForm->relname)));
+ 
+ 	ReleaseSysCache(relTup);
+ }
*** a/src/backend/catalog/objectaddress.c
--- b/src/backend/catalog/objectaddress.c
***************
*** 1345,1350 **** get_object_aclkind(Oid class_id)
--- 1345,1368 ----
  }
  
  /*
+  * Return whether we have useful data for the given object class in the
+  * ObjectProperty table.
+  */
+ bool
+ is_objectclass_supported(Oid class_id)
+ {
+ 	int			index;
+ 
+ 	for (index = 0; index < lengthof(ObjectProperty); index++)
+ 	{
+ 		if (ObjectProperty[index].class_oid == class_id)
+ 			return true;
+ 	}
+ 
+ 	return false;
+ }
+ 
+ /*
   * Find ObjectProperty structure by class_id.
   */
  static ObjectPropertyType *
*** a/src/backend/commands/alter.c
--- b/src/backend/commands/alter.c
***************
*** 753,810 **** ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
  }
  
  /*
-  * Return a copy of the tuple for the object with the given object OID, from
-  * the given catalog (which must have been opened by the caller and suitably
-  * locked).  NULL is returned if the OID is not found.
-  *
-  * We try a syscache first, if available.
-  *
-  * XXX this function seems general in possible usage.  Given sufficient callers
-  * elsewhere, we should consider moving it to a more appropriate place.
-  */
- static HeapTuple
- get_catalog_object_by_oid(Relation catalog, Oid objectId)
- {
- 	HeapTuple	tuple;
- 	Oid			classId = RelationGetRelid(catalog);
- 	int			oidCacheId = get_object_catcache_oid(classId);
- 
- 	if (oidCacheId > 0)
- 	{
- 		tuple = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objectId));
- 		if (!HeapTupleIsValid(tuple))  /* should not happen */
- 			return NULL;
- 	}
- 	else
- 	{
- 		Oid			oidIndexId = get_object_oid_index(classId);
- 		SysScanDesc	scan;
- 		ScanKeyData	skey;
- 
- 		Assert(OidIsValid(oidIndexId));
- 
- 		ScanKeyInit(&skey,
- 					ObjectIdAttributeNumber,
- 					BTEqualStrategyNumber, F_OIDEQ,
- 					ObjectIdGetDatum(objectId));
- 
- 		scan = systable_beginscan(catalog, oidIndexId, true,
- 								  SnapshotNow, 1, &skey);
- 		tuple = systable_getnext(scan);
- 		if (!HeapTupleIsValid(tuple))
- 		{
- 			systable_endscan(scan);
- 			return NULL;
- 		}
- 		tuple = heap_copytuple(tuple);
- 
- 		systable_endscan(scan);
- 	}
- 
- 	return tuple;
- }
- 
- /*
   * Generic function to change the ownership of a given object, for simple
   * cases (won't work for tables, nor other cases where we need to do more than
   * change the ownership column of a single catalog entry).
--- 753,758 ----
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 5195,5214 **** opt_restart_seqs:
   *	The COMMENT ON statement can take different forms based upon the type of
   *	the object associated with the comment. The form of the statement is:
   *
!  *	COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW |
!  *				   COLLATION | CONVERSION | LANGUAGE | OPERATOR CLASS |
!  *				   LARGE OBJECT | CAST | COLUMN | SCHEMA | TABLESPACE |
!  *				   EXTENSION | ROLE | TEXT SEARCH PARSER |
!  *				   TEXT SEARCH DICTIONARY | TEXT SEARCH TEMPLATE |
!  *				   TEXT SEARCH CONFIGURATION | FOREIGN TABLE |
!  *				   FOREIGN DATA WRAPPER | SERVER | EVENT TRIGGER |
!  *				   MATERIALIZED VIEW] <objname> |
   *				 AGGREGATE <aggname> (arg1, ...) |
   *				 FUNCTION <funcname> (arg1, arg2, ...) |
   *				 OPERATOR <op> (leftoperand_typ, rightoperand_typ) |
   *				 TRIGGER <triggername> ON <relname> |
-  *				 CONSTRAINT <constraintname> ON <relname> |
-  *				 RULE <rulename> ON <relname> ]
   *			   IS 'text'
   *
   *****************************************************************************/
--- 5195,5219 ----
   *	The COMMENT ON statement can take different forms based upon the type of
   *	the object associated with the comment. The form of the statement is:
   *
!  *	COMMENT ON [ [ CONVERSION | COLLATION | DATABASE | DOMAIN |
!  *                 EXTENSION | EVENT TRIGGER | FOREIGN DATA WRAPPER |
!  *                 FOREIGN TABLE | INDEX | [PROCEDURAL] LANGUAGE |
!  *                 MATERIALIZED VIEW | ROLE | SCHEMA | SEQUENCE |
!  *                 SERVER | TABLE | TABLESPACE |
!  *                 TEXT SEARCH CONFIGURATION | TEXT SEARCH DICTIONARY |
!  *                 TEXT SEARCH PARSER | TEXT SEARCH TEMPLATE | TYPE |
!  *                 VIEW] <objname>] |
   *				 AGGREGATE <aggname> (arg1, ...) |
+  *				 CAST (<src type> AS <dst type>) |
+  *               COLUMN <relname>.<colname> |
+  *               CONSTRAINT <constraintname> ON <relname> |
   *				 FUNCTION <funcname> (arg1, arg2, ...) |
+  *				 LARGE OBJECT <oid> |
   *				 OPERATOR <op> (leftoperand_typ, rightoperand_typ) |
+  *               OPERATOR CLASS <name> USING <access-method> |
+  *               OPERATOR FAMILY <name> USING <access-method> |
+  *				 RULE <rulename> ON <relname> |
   *				 TRIGGER <triggername> ON <relname> |
   *			   IS 'text'
   *
   *****************************************************************************/
*** a/src/backend/utils/adt/format_type.c
--- b/src/backend/utils/adt/format_type.c
***************
*** 29,35 ****
  #define MAX_INT32_LEN 11
  
  static char *format_type_internal(Oid type_oid, int32 typemod,
! 					 bool typemod_given, bool allow_invalid);
  static char *printTypmod(const char *typname, int32 typmod, Oid typmodout);
  static char *
  psnprintf(size_t len, const char *fmt,...)
--- 29,36 ----
  #define MAX_INT32_LEN 11
  
  static char *format_type_internal(Oid type_oid, int32 typemod,
! 					 bool typemod_given, bool allow_invalid,
! 					 bool force_qualify);
  static char *printTypmod(const char *typname, int32 typmod, Oid typmodout);
  static char *
  psnprintf(size_t len, const char *fmt,...)
***************
*** 77,87 **** format_type(PG_FUNCTION_ARGS)
  	type_oid = PG_GETARG_OID(0);
  
  	if (PG_ARGISNULL(1))
! 		result = format_type_internal(type_oid, -1, false, true);
  	else
  	{
  		typemod = PG_GETARG_INT32(1);
! 		result = format_type_internal(type_oid, typemod, true, true);
  	}
  
  	PG_RETURN_TEXT_P(cstring_to_text(result));
--- 78,88 ----
  	type_oid = PG_GETARG_OID(0);
  
  	if (PG_ARGISNULL(1))
! 		result = format_type_internal(type_oid, -1, false, true, false);
  	else
  	{
  		typemod = PG_GETARG_INT32(1);
! 		result = format_type_internal(type_oid, typemod, true, true, false);
  	}
  
  	PG_RETURN_TEXT_P(cstring_to_text(result));
***************
*** 96,102 **** format_type(PG_FUNCTION_ARGS)
  char *
  format_type_be(Oid type_oid)
  {
! 	return format_type_internal(type_oid, -1, false, false);
  }
  
  /*
--- 97,109 ----
  char *
  format_type_be(Oid type_oid)
  {
! 	return format_type_internal(type_oid, -1, false, false, false);
! }
! 
! char *
! format_type_be_qualified(Oid type_oid)
! {
! 	return format_type_internal(type_oid, -1, false, false, true);
  }
  
  /*
***************
*** 105,118 **** format_type_be(Oid type_oid)
  char *
  format_type_with_typemod(Oid type_oid, int32 typemod)
  {
! 	return format_type_internal(type_oid, typemod, true, false);
  }
  
- 
- 
  static char *
  format_type_internal(Oid type_oid, int32 typemod,
! 					 bool typemod_given, bool allow_invalid)
  {
  	bool		with_typemod = typemod_given && (typemod >= 0);
  	HeapTuple	tuple;
--- 112,124 ----
  char *
  format_type_with_typemod(Oid type_oid, int32 typemod)
  {
! 	return format_type_internal(type_oid, typemod, true, false, false);
  }
  
  static char *
  format_type_internal(Oid type_oid, int32 typemod,
! 					 bool typemod_given, bool allow_invalid,
! 					 bool force_qualify)
  {
  	bool		with_typemod = typemod_given && (typemod >= 0);
  	HeapTuple	tuple;
***************
*** 300,306 **** format_type_internal(Oid type_oid, int32 typemod,
  		char	   *nspname;
  		char	   *typname;
  
! 		if (TypeIsVisible(type_oid))
  			nspname = NULL;
  		else
  			nspname = get_namespace_name(typeform->typnamespace);
--- 306,312 ----
  		char	   *nspname;
  		char	   *typname;
  
! 		if (!force_qualify && TypeIsVisible(type_oid))
  			nspname = NULL;
  		else
  			nspname = get_namespace_name(typeform->typnamespace);
***************
*** 421,427 **** oidvectortypes(PG_FUNCTION_ARGS)
  	for (num = 0; num < numargs; num++)
  	{
  		char	   *typename = format_type_internal(oidArray->values[num], -1,
! 													false, true);
  		size_t		slen = strlen(typename);
  
  		if (left < (slen + 2))
--- 427,433 ----
  	for (num = 0; num < numargs; num++)
  	{
  		char	   *typename = format_type_internal(oidArray->values[num], -1,
! 													false, true, false);
  		size_t		slen = strlen(typename);
  
  		if (left < (slen + 2))
*** a/src/backend/utils/adt/regproc.c
--- b/src/backend/utils/adt/regproc.c
***************
*** 41,46 ****
--- 41,48 ----
  #include "utils/syscache.h"
  #include "utils/tqual.h"
  
+ static char *format_operator_internal(Oid operator_oid, bool force_qualify);
+ static char *format_procedure_internal(Oid procedure_oid, bool force_qualify);
  static void parseNameAndArgTypes(const char *string, bool allowNone,
  					 List **names, int *nargs, Oid *argtypes);
  
***************
*** 304,309 **** regprocedurein(PG_FUNCTION_ARGS)
--- 306,330 ----
  char *
  format_procedure(Oid procedure_oid)
  {
+ 	return format_procedure_internal(procedure_oid, false);
+ }
+ 
+ char *
+ format_procedure_qualified(Oid procedure_oid)
+ {
+ 	return format_procedure_internal(procedure_oid, true);
+ }
+ 
+ /*
+  * Routine to produce regprocedure names; see format_procedure above.
+  *
+  * force_qualify says whether to schema-qualify; if true, the name is always
+  * qualified regardless of search_path visibility.  Otherwise the name is only
+  * qualified if the function is not in path.
+  */
+ static char *
+ format_procedure_internal(Oid procedure_oid, bool force_qualify)
+ {
  	char	   *result;
  	HeapTuple	proctup;
  
***************
*** 326,332 **** format_procedure(Oid procedure_oid)
  		 * Would this proc be found (given the right args) by regprocedurein?
  		 * If not, we need to qualify it.
  		 */
! 		if (FunctionIsVisible(procedure_oid))
  			nspname = NULL;
  		else
  			nspname = get_namespace_name(procform->pronamespace);
--- 347,353 ----
  		 * Would this proc be found (given the right args) by regprocedurein?
  		 * If not, we need to qualify it.
  		 */
! 		if (!force_qualify && FunctionIsVisible(procedure_oid))
  			nspname = NULL;
  		else
  			nspname = get_namespace_name(procform->pronamespace);
***************
*** 339,345 **** format_procedure(Oid procedure_oid)
  
  			if (i > 0)
  				appendStringInfoChar(&buf, ',');
! 			appendStringInfoString(&buf, format_type_be(thisargtype));
  		}
  		appendStringInfoChar(&buf, ')');
  
--- 360,369 ----
  
  			if (i > 0)
  				appendStringInfoChar(&buf, ',');
! 			appendStringInfoString(&buf,
! 								   force_qualify ?
! 								   format_type_be_qualified(thisargtype) :
! 								   format_type_be(thisargtype));
  		}
  		appendStringInfoChar(&buf, ')');
  
***************
*** 653,660 **** regoperatorin(PG_FUNCTION_ARGS)
   * This exports the useful functionality of regoperatorout for use
   * in other backend modules.  The result is a palloc'd string.
   */
! char *
! format_operator(Oid operator_oid)
  {
  	char	   *result;
  	HeapTuple	opertup;
--- 677,684 ----
   * This exports the useful functionality of regoperatorout for use
   * in other backend modules.  The result is a palloc'd string.
   */
! static char *
! format_operator_internal(Oid operator_oid, bool force_qualify)
  {
  	char	   *result;
  	HeapTuple	opertup;
***************
*** 674,682 **** format_operator(Oid operator_oid)
  
  		/*
  		 * Would this oper be found (given the right args) by regoperatorin?
! 		 * If not, we need to qualify it.
  		 */
! 		if (!OperatorIsVisible(operator_oid))
  		{
  			nspname = get_namespace_name(operform->oprnamespace);
  			appendStringInfo(&buf, "%s.",
--- 698,706 ----
  
  		/*
  		 * Would this oper be found (given the right args) by regoperatorin?
! 		 * If not, or if caller explicitely requests it, we need to qualify it.
  		 */
! 		if (force_qualify || !OperatorIsVisible(operator_oid))
  		{
  			nspname = get_namespace_name(operform->oprnamespace);
  			appendStringInfo(&buf, "%s.",
***************
*** 687,698 **** format_operator(Oid operator_oid)
--- 711,726 ----
  
  		if (operform->oprleft)
  			appendStringInfo(&buf, "%s,",
+ 							 force_qualify ?
+ 							 format_type_be_qualified(operform->oprleft) :
  							 format_type_be(operform->oprleft));
  		else
  			appendStringInfo(&buf, "NONE,");
  
  		if (operform->oprright)
  			appendStringInfo(&buf, "%s)",
+ 							 force_qualify ?
+ 							 format_type_be_qualified(operform->oprright) :
  							 format_type_be(operform->oprright));
  		else
  			appendStringInfo(&buf, "NONE)");
***************
*** 713,718 **** format_operator(Oid operator_oid)
--- 741,758 ----
  	return result;
  }
  
+ char *
+ format_operator(Oid operator_oid)
+ {
+ 	return format_operator_internal(operator_oid, false);
+ }
+ 
+ char *
+ format_operator_qualified(Oid operator_oid)
+ {
+ 	return format_operator_internal(operator_oid, true);
+ }
+ 
  /*
   * regoperatorout		- converts operator OID to "opr_name(args)"
   */
*** a/src/backend/utils/cache/lsyscache.c
--- b/src/backend/utils/cache/lsyscache.c
***************
*** 18,23 ****
--- 18,24 ----
  #include "access/hash.h"
  #include "access/htup_details.h"
  #include "access/nbtree.h"
+ #include "access/sysattr.h"
  #include "bootstrap/bootstrap.h"
  #include "catalog/pg_amop.h"
  #include "catalog/pg_amproc.h"
***************
*** 40,45 ****
--- 41,47 ----
  #include "utils/lsyscache.h"
  #include "utils/rel.h"
  #include "utils/syscache.h"
+ #include "utils/tqual.h"
  #include "utils/typcache.h"
  
  /* Hook for plugins to get control in get_attavgwidth() */
***************
*** 2926,2928 **** get_range_subtype(Oid rangeOid)
--- 2928,2981 ----
  	else
  		return InvalidOid;
  }
+ 
+ /*				------------- GENERIC --------------				 */
+ 
+ /*
+  * Return a copy of the tuple for the object with the given object OID, from
+  * the given catalog (which must have been opened by the caller and suitably
+  * locked).  NULL is returned if the OID is not found.
+  *
+  * We try a syscache first, if available.
+  */
+ HeapTuple
+ get_catalog_object_by_oid(Relation catalog, Oid objectId)
+ {
+ 	HeapTuple	tuple;
+ 	Oid			classId = RelationGetRelid(catalog);
+ 	int			oidCacheId = get_object_catcache_oid(classId);
+ 
+ 	if (oidCacheId > 0)
+ 	{
+ 		tuple = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objectId));
+ 		if (!HeapTupleIsValid(tuple))  /* should not happen */
+ 			return NULL;
+ 	}
+ 	else
+ 	{
+ 		Oid			oidIndexId = get_object_oid_index(classId);
+ 		SysScanDesc	scan;
+ 		ScanKeyData	skey;
+ 
+ 		Assert(OidIsValid(oidIndexId));
+ 
+ 		ScanKeyInit(&skey,
+ 					ObjectIdAttributeNumber,
+ 					BTEqualStrategyNumber, F_OIDEQ,
+ 					ObjectIdGetDatum(objectId));
+ 
+ 		scan = systable_beginscan(catalog, oidIndexId, true,
+ 								  SnapshotNow, 1, &skey);
+ 		tuple = systable_getnext(scan);
+ 		if (!HeapTupleIsValid(tuple))
+ 		{
+ 			systable_endscan(scan);
+ 			return NULL;
+ 		}
+ 		tuple = heap_copytuple(tuple);
+ 
+ 		systable_endscan(scan);
+ 	}
+ 
+ 	return tuple;
+ }
*** a/src/include/catalog/dependency.h
--- b/src/include/catalog/dependency.h
***************
*** 179,184 **** extern ObjectClass getObjectClass(const ObjectAddress *object);
--- 179,187 ----
  extern char *getObjectDescription(const ObjectAddress *object);
  extern char *getObjectDescriptionOids(Oid classid, Oid objid);
  
+ extern char *getObjectTypeDescription(const ObjectAddress *object);
+ extern char *getObjectIdentity(const ObjectAddress *address);
+ 
  extern ObjectAddresses *new_object_addresses(void);
  
  extern void add_exact_object_address(const ObjectAddress *object,
*** a/src/include/catalog/objectaddress.h
--- b/src/include/catalog/objectaddress.h
***************
*** 38,43 **** extern void check_object_ownership(Oid roleid,
--- 38,44 ----
  
  extern Oid	get_object_namespace(const ObjectAddress *address);
  
+ extern bool				is_objectclass_supported(Oid class_id);
  extern Oid				get_object_oid_index(Oid class_id);
  extern int				get_object_catcache_oid(Oid class_id);
  extern int				get_object_catcache_name(Oid class_id);
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 2917,2922 **** DESCR("view members of a multixactid");
--- 2917,2925 ----
  DATA(insert OID = 3537 (  pg_describe_object		PGNSP PGUID 12 1 0 0 0 f f f f t f s 3 0 25 "26 26 23" _null_ _null_ _null_ _null_ pg_describe_object _null_ _null_ _null_ ));
  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,23,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 = 2079 (  pg_table_is_visible		PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_table_is_visible _null_ _null_ _null_ ));
  DESCR("is table visible in search path?");
  DATA(insert OID = 2080 (  pg_type_is_visible		PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_type_is_visible _null_ _null_ _null_ ));
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 615,621 **** extern Datum regdictionarysend(PG_FUNCTION_ARGS);
--- 615,623 ----
  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 char *format_operator(Oid operator_oid);
+ extern char *format_operator_qualified(Oid operator_oid);
  
  /* rowtypes.c */
  extern Datum record_in(PG_FUNCTION_ARGS);
***************
*** 1027,1032 **** extern Datum pg_encoding_max_length_sql(PG_FUNCTION_ARGS);
--- 1029,1035 ----
  /* format_type.c */
  extern Datum format_type(PG_FUNCTION_ARGS);
  extern char *format_type_be(Oid type_oid);
+ extern char *format_type_be_qualified(Oid type_oid);
  extern char *format_type_with_typemod(Oid type_oid, int32 typemod);
  extern Datum oidvectortypes(PG_FUNCTION_ARGS);
  extern int32 type_maximum_size(Oid type_oid, int32 typemod);
***************
*** 1143,1148 **** extern Datum pg_get_multixact_members(PG_FUNCTION_ARGS);
--- 1146,1152 ----
  
  /* catalogs/dependency.c */
  extern Datum pg_describe_object(PG_FUNCTION_ARGS);
+ extern Datum pg_identify_object(PG_FUNCTION_ARGS);
  
  /* commands/constraint.c */
  extern Datum unique_key_recheck(PG_FUNCTION_ARGS);
*** a/src/include/utils/lsyscache.h
--- b/src/include/utils/lsyscache.h
***************
*** 16,21 ****
--- 16,22 ----
  #include "access/attnum.h"
  #include "access/htup.h"
  #include "nodes/pg_list.h"
+ #include "utils/relcache.h"
  
  /* Result list element for get_op_btree_interpretation */
  typedef struct OpBtreeInterpretation
***************
*** 152,157 **** extern void free_attstatsslot(Oid atttype,
--- 153,159 ----
  				  float4 *numbers, int nnumbers);
  extern char *get_namespace_name(Oid nspid);
  extern Oid	get_range_subtype(Oid rangeOid);
+ extern HeapTuple get_catalog_object_by_oid(Relation catalog, Oid objectId);
  
  #define type_is_array(typid)  (get_element_type(typid) != InvalidOid)
  /* type_is_array_domain accepts both plain arrays and domains over arrays */
-- 
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