#include "postgres.h"
 
 #include "access/htup_details.h"
-#include "access/sysattr.h"
 #include "access/xact.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
-#include "catalog/namespace.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_amop.h"
 #include "catalog/pg_amproc.h"
 #include "catalog/pg_type.h"
 #include "catalog/pg_user_mapping.h"
 #include "commands/comment.h"
-#include "commands/dbcommands.h"
 #include "commands/defrem.h"
 #include "commands/event_trigger.h"
 #include "commands/extension.h"
 #include "commands/proclang.h"
 #include "commands/schemacmds.h"
 #include "commands/seclabel.h"
-#include "commands/tablespace.h"
 #include "commands/trigger.h"
 #include "commands/typecmds.h"
-#include "foreign/foreign.h"
-#include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteRemove.h"
 #include "storage/lmgr.h"
-#include "utils/acl.h"
-#include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/guc.h"
 #include "utils/lsyscache.h"
 static bool stack_address_present_add_flags(const ObjectAddress *object,
                                int flags,
                                ObjectAddressStack *stack);
-static void getRelationDescription(StringInfo buffer, Oid relid);
-static void getOpFamilyDescription(StringInfo buffer, Oid opfid);
 
 
 /*
    /* only pg_class entries can have nonzero objectSubId */
    if (object->classId != RelationRelationId &&
        object->objectSubId != 0)
-       elog(ERROR, "invalid objectSubId 0 for object class %u",
+       elog(ERROR, "invalid non-zero objectSubId for object class %u",
             object->classId);
 
    switch (object->classId)
    elog(ERROR, "unrecognized object class: %u", object->classId);
    return OCLASS_CLASS;        /* keep compiler quiet */
 }
-
-/*
- * getObjectDescription: build an object description for messages
- *
- * The result is a palloc'd string.
- */
-char *
-getObjectDescription(const ObjectAddress *object)
-{
-   StringInfoData buffer;
-
-   initStringInfo(&buffer);
-
-   switch (getObjectClass(object))
-   {
-       case OCLASS_CLASS:
-           getRelationDescription(&buffer, object->objectId);
-           if (object->objectSubId != 0)
-               appendStringInfo(&buffer, _(" column %s"),
-                                get_relid_attribute_name(object->objectId,
-                                                      object->objectSubId));
-           break;
-
-       case OCLASS_PROC:
-           appendStringInfo(&buffer, _("function %s"),
-                            format_procedure(object->objectId));
-           break;
-
-       case OCLASS_TYPE:
-           appendStringInfo(&buffer, _("type %s"),
-                            format_type_be(object->objectId));
-           break;
-
-       case OCLASS_CAST:
-           {
-               Relation    castDesc;
-               ScanKeyData skey[1];
-               SysScanDesc rcscan;
-               HeapTuple   tup;
-               Form_pg_cast castForm;
-
-               castDesc = heap_open(CastRelationId, AccessShareLock);
-
-               ScanKeyInit(&skey[0],
-                           ObjectIdAttributeNumber,
-                           BTEqualStrategyNumber, F_OIDEQ,
-                           ObjectIdGetDatum(object->objectId));
-
-               rcscan = systable_beginscan(castDesc, CastOidIndexId, true,
-                                           SnapshotNow, 1, skey);
-
-               tup = systable_getnext(rcscan);
-
-               if (!HeapTupleIsValid(tup))
-                   elog(ERROR, "could not find tuple for cast %u",
-                        object->objectId);
-
-               castForm = (Form_pg_cast) GETSTRUCT(tup);
-
-               appendStringInfo(&buffer, _("cast from %s to %s"),
-                                format_type_be(castForm->castsource),
-                                format_type_be(castForm->casttarget));
-
-               systable_endscan(rcscan);
-               heap_close(castDesc, AccessShareLock);
-               break;
-           }
-
-       case OCLASS_COLLATION:
-           {
-               HeapTuple   collTup;
-               Form_pg_collation coll;
-
-               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);
-               appendStringInfo(&buffer, _("collation %s"),
-                                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))
-               {
-                   StringInfoData rel;
-
-                   initStringInfo(&rel);
-                   getRelationDescription(&rel, con->conrelid);
-                   appendStringInfo(&buffer, _("constraint %s on %s"),
-                                    NameStr(con->conname), rel.data);
-                   pfree(rel.data);
-               }
-               else
-               {
-                   appendStringInfo(&buffer, _("constraint %s"),
-                                    NameStr(con->conname));
-               }
-
-               ReleaseSysCache(conTup);
-               break;
-           }
-
-       case OCLASS_CONVERSION:
-           {
-               HeapTuple   conTup;
-
-               conTup = SearchSysCache1(CONVOID,
-                                        ObjectIdGetDatum(object->objectId));
-               if (!HeapTupleIsValid(conTup))
-                   elog(ERROR, "cache lookup failed for conversion %u",
-                        object->objectId);
-               appendStringInfo(&buffer, _("conversion %s"),
-                NameStr(((Form_pg_conversion) GETSTRUCT(conTup))->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, _("default for %s"),
-                                getObjectDescription(&colobject));
-
-               systable_endscan(adscan);
-               heap_close(attrdefDesc, AccessShareLock);
-               break;
-           }
-
-       case OCLASS_LANGUAGE:
-           {
-               HeapTuple   langTup;
-
-               langTup = SearchSysCache1(LANGOID,
-                                         ObjectIdGetDatum(object->objectId));
-               if (!HeapTupleIsValid(langTup))
-                   elog(ERROR, "cache lookup failed for language %u",
-                        object->objectId);
-               appendStringInfo(&buffer, _("language %s"),
-                 NameStr(((Form_pg_language) GETSTRUCT(langTup))->lanname));
-               ReleaseSysCache(langTup);
-               break;
-           }
-       case OCLASS_LARGEOBJECT:
-           appendStringInfo(&buffer, _("large object %u"),
-                            object->objectId);
-           break;
-
-       case OCLASS_OPERATOR:
-           appendStringInfo(&buffer, _("operator %s"),
-                            format_operator(object->objectId));
-           break;
-
-       case OCLASS_OPCLASS:
-           {
-               HeapTuple   opcTup;
-               Form_pg_opclass opcForm;
-               HeapTuple   amTup;
-               Form_pg_am  amForm;
-               char       *nspname;
-
-               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);
-
-               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);
-
-               /* Qualify the name if not visible in search path */
-               if (OpclassIsVisible(object->objectId))
-                   nspname = NULL;
-               else
-                   nspname = get_namespace_name(opcForm->opcnamespace);
-
-               appendStringInfo(&buffer, _("operator class %s for access method %s"),
-                                quote_qualified_identifier(nspname,
-                                                 NameStr(opcForm->opcname)),
-                                NameStr(amForm->amname));
-
-               ReleaseSysCache(amTup);
-               ReleaseSysCache(opcTup);
-               break;
-           }
-
-       case OCLASS_OPFAMILY:
-           getOpFamilyDescription(&buffer, object->objectId);
-           break;
-
-       case OCLASS_AMOP:
-           {
-               Relation    amopDesc;
-               ScanKeyData skey[1];
-               SysScanDesc amscan;
-               HeapTuple   tup;
-               Form_pg_amop amopForm;
-               StringInfoData opfam;
-
-               amopDesc = heap_open(AccessMethodOperatorRelationId,
-                                    AccessShareLock);
-
-               ScanKeyInit(&skey[0],
-                           ObjectIdAttributeNumber,
-                           BTEqualStrategyNumber, F_OIDEQ,
-                           ObjectIdGetDatum(object->objectId));
-
-               amscan = systable_beginscan(amopDesc, AccessMethodOperatorOidIndexId, true,
-                                           SnapshotNow, 1, skey);
-
-               tup = systable_getnext(amscan);
-
-               if (!HeapTupleIsValid(tup))
-                   elog(ERROR, "could not find tuple for amop entry %u",
-                        object->objectId);
-
-               amopForm = (Form_pg_amop) GETSTRUCT(tup);
-
-               initStringInfo(&opfam);
-               getOpFamilyDescription(&opfam, amopForm->amopfamily);
-
-               /*------
-                  translator: %d is the operator strategy (a number), the
-                  first two %s's are data type names, the third %s is the
-                  description of the operator family, and the last %s is the
-                  textual form of the operator with arguments.  */
-               appendStringInfo(&buffer, _("operator %d (%s, %s) of %s: %s"),
-                                amopForm->amopstrategy,
-                                format_type_be(amopForm->amoplefttype),
-                                format_type_be(amopForm->amoprighttype),
-                                opfam.data,
-                                format_operator(amopForm->amopopr));
-
-               pfree(opfam.data);
-
-               systable_endscan(amscan);
-               heap_close(amopDesc, AccessShareLock);
-               break;
-           }
-
-       case OCLASS_AMPROC:
-           {
-               Relation    amprocDesc;
-               ScanKeyData skey[1];
-               SysScanDesc amscan;
-               HeapTuple   tup;
-               Form_pg_amproc amprocForm;
-               StringInfoData opfam;
-
-               amprocDesc = heap_open(AccessMethodProcedureRelationId,
-                                      AccessShareLock);
-
-               ScanKeyInit(&skey[0],
-                           ObjectIdAttributeNumber,
-                           BTEqualStrategyNumber, F_OIDEQ,
-                           ObjectIdGetDatum(object->objectId));
-
-               amscan = systable_beginscan(amprocDesc, AccessMethodProcedureOidIndexId, true,
-                                           SnapshotNow, 1, skey);
-
-               tup = systable_getnext(amscan);
-
-               if (!HeapTupleIsValid(tup))
-                   elog(ERROR, "could not find tuple for amproc entry %u",
-                        object->objectId);
-
-               amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
-
-               initStringInfo(&opfam);
-               getOpFamilyDescription(&opfam, amprocForm->amprocfamily);
-
-               /*------
-                  translator: %d is the function number, the first two %s's
-                  are data type names, the third %s is the description of the
-                  operator family, and the last %s is the textual form of the
-                  function with arguments.  */
-               appendStringInfo(&buffer, _("function %d (%s, %s) of %s: %s"),
-                                amprocForm->amprocnum,
-                                format_type_be(amprocForm->amproclefttype),
-                                format_type_be(amprocForm->amprocrighttype),
-                                opfam.data,
-                                format_procedure(amprocForm->amproc));
-
-               pfree(opfam.data);
-
-               systable_endscan(amscan);
-               heap_close(amprocDesc, AccessShareLock);
-               break;
-           }
-
-       case OCLASS_REWRITE:
-           {
-               Relation    ruleDesc;
-               ScanKeyData skey[1];
-               SysScanDesc rcscan;
-               HeapTuple   tup;
-               Form_pg_rewrite rule;
-
-               ruleDesc = heap_open(RewriteRelationId, AccessShareLock);
-
-               ScanKeyInit(&skey[0],
-                           ObjectIdAttributeNumber,
-                           BTEqualStrategyNumber, F_OIDEQ,
-                           ObjectIdGetDatum(object->objectId));
-
-               rcscan = systable_beginscan(ruleDesc, RewriteOidIndexId, true,
-                                           SnapshotNow, 1, skey);
-
-               tup = systable_getnext(rcscan);
-
-               if (!HeapTupleIsValid(tup))
-                   elog(ERROR, "could not find tuple for rule %u",
-                        object->objectId);
-
-               rule = (Form_pg_rewrite) GETSTRUCT(tup);
-
-               appendStringInfo(&buffer, _("rule %s on "),
-                                NameStr(rule->rulename));
-               getRelationDescription(&buffer, rule->ev_class);
-
-               systable_endscan(rcscan);
-               heap_close(ruleDesc, AccessShareLock);
-               break;
-           }
-
-       case OCLASS_TRIGGER:
-           {
-               Relation    trigDesc;
-               ScanKeyData skey[1];
-               SysScanDesc tgscan;
-               HeapTuple   tup;
-               Form_pg_trigger trig;
-
-               trigDesc = heap_open(TriggerRelationId, AccessShareLock);
-
-               ScanKeyInit(&skey[0],
-                           ObjectIdAttributeNumber,
-                           BTEqualStrategyNumber, F_OIDEQ,
-                           ObjectIdGetDatum(object->objectId));
-
-               tgscan = systable_beginscan(trigDesc, TriggerOidIndexId, true,
-                                           SnapshotNow, 1, skey);
-
-               tup = systable_getnext(tgscan);
-
-               if (!HeapTupleIsValid(tup))
-                   elog(ERROR, "could not find tuple for trigger %u",
-                        object->objectId);
-
-               trig = (Form_pg_trigger) GETSTRUCT(tup);
-
-               appendStringInfo(&buffer, _("trigger %s on "),
-                                NameStr(trig->tgname));
-               getRelationDescription(&buffer, trig->tgrelid);
-
-               systable_endscan(tgscan);
-               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, _("schema %s"), nspname);
-               break;
-           }
-
-       case OCLASS_TSPARSER:
-           {
-               HeapTuple   tup;
-
-               tup = SearchSysCache1(TSPARSEROID,
-                                     ObjectIdGetDatum(object->objectId));
-               if (!HeapTupleIsValid(tup))
-                   elog(ERROR, "cache lookup failed for text search parser %u",
-                        object->objectId);
-               appendStringInfo(&buffer, _("text search parser %s"),
-                    NameStr(((Form_pg_ts_parser) GETSTRUCT(tup))->prsname));
-               ReleaseSysCache(tup);
-               break;
-           }
-
-       case OCLASS_TSDICT:
-           {
-               HeapTuple   tup;
-
-               tup = SearchSysCache1(TSDICTOID,
-                                     ObjectIdGetDatum(object->objectId));
-               if (!HeapTupleIsValid(tup))
-                   elog(ERROR, "cache lookup failed for text search dictionary %u",
-                        object->objectId);
-               appendStringInfo(&buffer, _("text search dictionary %s"),
-                     NameStr(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname));
-               ReleaseSysCache(tup);
-               break;
-           }
-
-       case OCLASS_TSTEMPLATE:
-           {
-               HeapTuple   tup;
-
-               tup = SearchSysCache1(TSTEMPLATEOID,
-                                     ObjectIdGetDatum(object->objectId));
-               if (!HeapTupleIsValid(tup))
-                   elog(ERROR, "cache lookup failed for text search template %u",
-                        object->objectId);
-               appendStringInfo(&buffer, _("text search template %s"),
-                 NameStr(((Form_pg_ts_template) GETSTRUCT(tup))->tmplname));
-               ReleaseSysCache(tup);
-               break;
-           }
-
-       case OCLASS_TSCONFIG:
-           {
-               HeapTuple   tup;
-
-               tup = SearchSysCache1(TSCONFIGOID,
-                                     ObjectIdGetDatum(object->objectId));
-               if (!HeapTupleIsValid(tup))
-                   elog(ERROR, "cache lookup failed for text search configuration %u",
-                        object->objectId);
-               appendStringInfo(&buffer, _("text search configuration %s"),
-                    NameStr(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname));
-               ReleaseSysCache(tup);
-               break;
-           }
-
-       case OCLASS_ROLE:
-           {
-               appendStringInfo(&buffer, _("role %s"),
-                                GetUserNameFromId(object->objectId));
-               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, _("database %s"), 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, _("tablespace %s"), tblspace);
-               break;
-           }
-
-       case OCLASS_FDW:
-           {
-               ForeignDataWrapper *fdw;
-
-               fdw = GetForeignDataWrapper(object->objectId);
-               appendStringInfo(&buffer, _("foreign-data wrapper %s"), fdw->fdwname);
-               break;
-           }
-
-       case OCLASS_FOREIGN_SERVER:
-           {
-               ForeignServer *srv;
-
-               srv = GetForeignServer(object->objectId);
-               appendStringInfo(&buffer, _("server %s"), srv->servername);
-               break;
-           }
-
-       case OCLASS_USER_MAPPING:
-           {
-               HeapTuple   tup;
-               Oid         useid;
-               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 = GetUserNameFromId(useid);
-               else
-                   usename = "public";
-
-               appendStringInfo(&buffer, _("user mapping for %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);
-
-               switch (defacl->defaclobjtype)
-               {
-                   case DEFACLOBJ_RELATION:
-                       appendStringInfo(&buffer,
-                                        _("default privileges on new relations belonging to role %s"),
-                                     GetUserNameFromId(defacl->defaclrole));
-                       break;
-                   case DEFACLOBJ_SEQUENCE:
-                       appendStringInfo(&buffer,
-                                        _("default privileges on new sequences belonging to role %s"),
-                                     GetUserNameFromId(defacl->defaclrole));
-                       break;
-                   case DEFACLOBJ_FUNCTION:
-                       appendStringInfo(&buffer,
-                                        _("default privileges on new functions belonging to role %s"),
-                                     GetUserNameFromId(defacl->defaclrole));
-                       break;
-                   case DEFACLOBJ_TYPE:
-                       appendStringInfo(&buffer,
-                                        _("default privileges on new types belonging to role %s"),
-                                     GetUserNameFromId(defacl->defaclrole));
-                       break;
-                   default:
-                       /* shouldn't get here */
-                       appendStringInfo(&buffer,
-                               _("default privileges belonging to role %s"),
-                                     GetUserNameFromId(defacl->defaclrole));
-                       break;
-               }
-
-               if (OidIsValid(defacl->defaclnamespace))
-               {
-                   appendStringInfo(&buffer,
-                                    _(" in schema %s"),
-                               get_namespace_name(defacl->defaclnamespace));
-               }
-
-               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, _("extension %s"), extname);
-               break;
-           }
-
-        case OCLASS_EVENT_TRIGGER:
-           {
-               HeapTuple   tup;
-
-               tup = SearchSysCache1(EVENTTRIGGEROID,
-                                     ObjectIdGetDatum(object->objectId));
-               if (!HeapTupleIsValid(tup))
-                   elog(ERROR, "cache lookup failed for event trigger %u",
-                        object->objectId);
-               appendStringInfo(&buffer, _("event trigger %s"),
-                    NameStr(((Form_pg_event_trigger) GETSTRUCT(tup))->evtname));
-               ReleaseSysCache(tup);
-               break;
-           }
-
-       default:
-           appendStringInfo(&buffer, "unrecognized object %u %u %d",
-                            object->classId,
-                            object->objectId,
-                            object->objectSubId);
-           break;
-   }
-
-   return buffer.data;
-}
-
-/*
- * getObjectDescriptionOids: as above, except the object is specified by Oids
- */
-char *
-getObjectDescriptionOids(Oid classid, Oid objid)
-{
-   ObjectAddress address;
-
-   address.classId = classid;
-   address.objectId = objid;
-   address.objectSubId = 0;
-
-   return getObjectDescription(&address);
-}
-
-/*
- * subroutine for getObjectDescription: describe a relation
- */
-static void
-getRelationDescription(StringInfo buffer, Oid relid)
-{
-   HeapTuple   relTup;
-   Form_pg_class relForm;
-   char       *nspname;
-   char       *relname;
-
-   relTup = SearchSysCache1(RELOID,
-                            ObjectIdGetDatum(relid));
-   if (!HeapTupleIsValid(relTup))
-       elog(ERROR, "cache lookup failed for relation %u", relid);
-   relForm = (Form_pg_class) GETSTRUCT(relTup);
-
-   /* Qualify the name if not visible in search path */
-   if (RelationIsVisible(relid))
-       nspname = NULL;
-   else
-       nspname = get_namespace_name(relForm->relnamespace);
-
-   relname = quote_qualified_identifier(nspname, NameStr(relForm->relname));
-
-   switch (relForm->relkind)
-   {
-       case RELKIND_RELATION:
-           appendStringInfo(buffer, _("table %s"),
-                            relname);
-           break;
-       case RELKIND_INDEX:
-           appendStringInfo(buffer, _("index %s"),
-                            relname);
-           break;
-       case RELKIND_SEQUENCE:
-           appendStringInfo(buffer, _("sequence %s"),
-                            relname);
-           break;
-       case RELKIND_TOASTVALUE:
-           appendStringInfo(buffer, _("toast table %s"),
-                            relname);
-           break;
-       case RELKIND_VIEW:
-           appendStringInfo(buffer, _("view %s"),
-                            relname);
-           break;
-       case RELKIND_MATVIEW:
-           appendStringInfo(buffer, _("materialized view %s"),
-                            relname);
-           break;
-       case RELKIND_COMPOSITE_TYPE:
-           appendStringInfo(buffer, _("composite type %s"),
-                            relname);
-           break;
-       case RELKIND_FOREIGN_TABLE:
-           appendStringInfo(buffer, _("foreign table %s"),
-                            relname);
-           break;
-       default:
-           /* shouldn't get here */
-           appendStringInfo(buffer, _("relation %s"),
-                            relname);
-           break;
-   }
-
-   ReleaseSysCache(relTup);
-}
-
-/*
- * subroutine for getObjectDescription: describe an operator family
- */
-static void
-getOpFamilyDescription(StringInfo buffer, Oid opfid)
-{
-   HeapTuple   opfTup;
-   Form_pg_opfamily opfForm;
-   HeapTuple   amTup;
-   Form_pg_am  amForm;
-   char       *nspname;
-
-   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);
-
-   /* Qualify the name if not visible in search path */
-   if (OpfamilyIsVisible(opfid))
-       nspname = NULL;
-   else
-       nspname = get_namespace_name(opfForm->opfnamespace);
-
-   appendStringInfo(buffer, _("operator family %s for access method %s"),
-                    quote_qualified_identifier(nspname,
-                                               NameStr(opfForm->opfname)),
-                    NameStr(amForm->amname));
-
-   ReleaseSysCache(amTup);
-   ReleaseSysCache(opfTup);
-}
-
-/*
- * SQL-level callable version of getObjectDescription
- */
-Datum
-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 */
-   if (!OidIsValid(classid) && !OidIsValid(objid))
-       PG_RETURN_NULL();
-
-   address.classId = classid;
-   address.objectId = objid;
-   address.objectSubId = subobjid;
-
-   description = getObjectDescription(&address);
-   PG_RETURN_TEXT_P(cstring_to_text(description));
-}
 
 #include "catalog/catalog.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaddress.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_amproc.h"
+#include "catalog/pg_attrdef.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_cast.h"
+#include "catalog/pg_default_acl.h"
 #include "catalog/pg_event_trigger.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_ts_parser.h"
 #include "catalog/pg_ts_template.h"
 #include "catalog/pg_type.h"
+#include "catalog/pg_user_mapping.h"
 #include "commands/dbcommands.h"
 #include "commands/defrem.h"
 #include "commands/event_trigger.h"
 #include "commands/tablespace.h"
 #include "commands/trigger.h"
 #include "foreign/foreign.h"
+#include "funcapi.h"
 #include "libpq/be-fsstubs.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
    AttrNumber  attnum_owner;   /* attnum of owner field */
    AttrNumber  attnum_acl;     /* attnum of acl field */
    AclObjectKind acl_kind;     /* ACL_KIND_* of this object type */
+   bool        is_nsp_name_unique; /* can the nsp/name combination (or name
+                                    * alone, if there's no namespace) be
+                                    * considered an unique identifier for an
+                                    * object of this class? */
 } ObjectPropertyType;
 
 static ObjectPropertyType ObjectProperty[] =
        InvalidAttrNumber,
        InvalidAttrNumber,
        InvalidAttrNumber,
-       -1
+       -1,
+       false
    },
    {
        CollationRelationId,
        Anum_pg_collation_collnamespace,
        Anum_pg_collation_collowner,
        InvalidAttrNumber,
-       ACL_KIND_COLLATION
+       ACL_KIND_COLLATION,
+       true
    },
    {
        ConstraintRelationId,
        Anum_pg_constraint_connamespace,
        InvalidAttrNumber,
        InvalidAttrNumber,
-       -1
+       -1,
+       false
    },
    {
        ConversionRelationId,
        Anum_pg_conversion_connamespace,
        Anum_pg_conversion_conowner,
        InvalidAttrNumber,
-       ACL_KIND_CONVERSION
+       ACL_KIND_CONVERSION,
+       true
    },
    {
        DatabaseRelationId,
        InvalidAttrNumber,
        Anum_pg_database_datdba,
        Anum_pg_database_datacl,
-       ACL_KIND_DATABASE
+       ACL_KIND_DATABASE,
+       true
    },
    {
        ExtensionRelationId,
        InvalidAttrNumber,      /* extension doesn't belong to extnamespace */
        Anum_pg_extension_extowner,
        InvalidAttrNumber,
-       ACL_KIND_EXTENSION
+       ACL_KIND_EXTENSION,
+       true
    },
    {
        ForeignDataWrapperRelationId,
        InvalidAttrNumber,
        Anum_pg_foreign_data_wrapper_fdwowner,
        Anum_pg_foreign_data_wrapper_fdwacl,
-       ACL_KIND_FDW
+       ACL_KIND_FDW,
+       true
    },
    {
        ForeignServerRelationId,
        InvalidAttrNumber,
        Anum_pg_foreign_server_srvowner,
        Anum_pg_foreign_server_srvacl,
-       ACL_KIND_FOREIGN_SERVER
+       ACL_KIND_FOREIGN_SERVER,
+       true
    },
    {
        ProcedureRelationId,
        Anum_pg_proc_pronamespace,
        Anum_pg_proc_proowner,
        Anum_pg_proc_proacl,
-       ACL_KIND_PROC
+       ACL_KIND_PROC,
+       false
    },
    {
        LanguageRelationId,
        InvalidAttrNumber,
        Anum_pg_language_lanowner,
        Anum_pg_language_lanacl,
-       ACL_KIND_LANGUAGE
+       ACL_KIND_LANGUAGE,
+       true
    },
    {
        LargeObjectMetadataRelationId,
        InvalidAttrNumber,
        Anum_pg_largeobject_metadata_lomowner,
        Anum_pg_largeobject_metadata_lomacl,
-       ACL_KIND_LARGEOBJECT
+       ACL_KIND_LARGEOBJECT,
+       false
    },
    {
        OperatorClassRelationId,
        Anum_pg_opclass_opcnamespace,
        Anum_pg_opclass_opcowner,
        InvalidAttrNumber,
-       ACL_KIND_OPCLASS
+       ACL_KIND_OPCLASS,
+       true
    },
    {
        OperatorRelationId,
        Anum_pg_operator_oprnamespace,
        Anum_pg_operator_oprowner,
        InvalidAttrNumber,
-       ACL_KIND_OPER
+       ACL_KIND_OPER,
+       false
    },
    {
        OperatorFamilyRelationId,
        Anum_pg_opfamily_opfnamespace,
        Anum_pg_opfamily_opfowner,
        InvalidAttrNumber,
-       ACL_KIND_OPFAMILY
+       ACL_KIND_OPFAMILY,
+       true
    },
    {
        AuthIdRelationId,
        InvalidAttrNumber,
        InvalidAttrNumber,
        InvalidAttrNumber,
-       -1
+       -1,
+       true
    },
    {
        RewriteRelationId,
        InvalidAttrNumber,
        InvalidAttrNumber,
        InvalidAttrNumber,
-       -1
+       -1,
+       false
    },
    {
        NamespaceRelationId,
        InvalidAttrNumber,
        Anum_pg_namespace_nspowner,
        Anum_pg_namespace_nspacl,
-       ACL_KIND_NAMESPACE
+       ACL_KIND_NAMESPACE,
+       true
    },
    {
        RelationRelationId,
        Anum_pg_class_relnamespace,
        Anum_pg_class_relowner,
        Anum_pg_class_relacl,
-       ACL_KIND_CLASS
+       ACL_KIND_CLASS,
+       true
    },
    {
        TableSpaceRelationId,
        InvalidAttrNumber,
        Anum_pg_tablespace_spcowner,
        Anum_pg_tablespace_spcacl,
-       ACL_KIND_TABLESPACE
+       ACL_KIND_TABLESPACE,
+       true
    },
    {
        TriggerRelationId,
        InvalidAttrNumber,
        InvalidAttrNumber,
        -1,
+       false
    },
    {
        EventTriggerRelationId,
        Anum_pg_event_trigger_evtowner,
        InvalidAttrNumber,
        ACL_KIND_EVENT_TRIGGER,
+       true
    },
    {
        TSConfigRelationId,
        Anum_pg_ts_config_cfgnamespace,
        Anum_pg_ts_config_cfgowner,
        InvalidAttrNumber,
-       ACL_KIND_TSCONFIGURATION
+       ACL_KIND_TSCONFIGURATION,
+       true
    },
    {
        TSDictionaryRelationId,
        Anum_pg_ts_dict_dictnamespace,
        Anum_pg_ts_dict_dictowner,
        InvalidAttrNumber,
-       ACL_KIND_TSDICTIONARY
+       ACL_KIND_TSDICTIONARY,
+       true
    },
    {
        TSParserRelationId,
        InvalidAttrNumber,
        InvalidAttrNumber,
        -1,
+       true
    },
    {
        TSTemplateRelationId,
        InvalidAttrNumber,
        InvalidAttrNumber,
        -1,
+       true,
    },
    {
        TypeRelationId,
        Anum_pg_type_typnamespace,
        Anum_pg_type_typowner,
        Anum_pg_type_typacl,
-       ACL_KIND_TYPE
+       ACL_KIND_TYPE,
+       true
    }
 };
 
                        List *objargs, bool missing_ok);
 static ObjectPropertyType *get_object_property_data(Oid class_id);
 
+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);
+
 /*
  * Translate an object name and arguments (as passed by the parser) to an
  * ObjectAddress.
    return prop->acl_kind;
 }
 
+bool
+get_object_namensp_unique(Oid class_id)
+{
+   ObjectPropertyType *prop = get_object_property_data(class_id);
+
+   return prop->is_nsp_name_unique;
+}
+
+/*
+ * 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.
  */
 
    return NULL; /* keep MSC compiler happy */
 }
+
+/*
+ * 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;
+}
+
+/*
+ * getObjectDescription: build an object description for messages
+ *
+ * The result is a palloc'd string.
+ */
+char *
+getObjectDescription(const ObjectAddress *object)
+{
+   StringInfoData buffer;
+
+   initStringInfo(&buffer);
+
+   switch (getObjectClass(object))
+   {
+       case OCLASS_CLASS:
+           getRelationDescription(&buffer, object->objectId);
+           if (object->objectSubId != 0)
+               appendStringInfo(&buffer, _(" column %s"),
+                                get_relid_attribute_name(object->objectId,
+                                                      object->objectSubId));
+           break;
+
+       case OCLASS_PROC:
+           appendStringInfo(&buffer, _("function %s"),
+                            format_procedure(object->objectId));
+           break;
+
+       case OCLASS_TYPE:
+           appendStringInfo(&buffer, _("type %s"),
+                            format_type_be(object->objectId));
+           break;
+
+       case OCLASS_CAST:
+           {
+               Relation    castDesc;
+               ScanKeyData skey[1];
+               SysScanDesc rcscan;
+               HeapTuple   tup;
+               Form_pg_cast castForm;
+
+               castDesc = heap_open(CastRelationId, AccessShareLock);
+
+               ScanKeyInit(&skey[0],
+                           ObjectIdAttributeNumber,
+                           BTEqualStrategyNumber, F_OIDEQ,
+                           ObjectIdGetDatum(object->objectId));
+
+               rcscan = systable_beginscan(castDesc, CastOidIndexId, true,
+                                           SnapshotNow, 1, skey);
+
+               tup = systable_getnext(rcscan);
+
+               if (!HeapTupleIsValid(tup))
+                   elog(ERROR, "could not find tuple for cast %u",
+                        object->objectId);
+
+               castForm = (Form_pg_cast) GETSTRUCT(tup);
+
+               appendStringInfo(&buffer, _("cast from %s to %s"),
+                                format_type_be(castForm->castsource),
+                                format_type_be(castForm->casttarget));
+
+               systable_endscan(rcscan);
+               heap_close(castDesc, AccessShareLock);
+               break;
+           }
+
+       case OCLASS_COLLATION:
+           {
+               HeapTuple   collTup;
+               Form_pg_collation coll;
+
+               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);
+               appendStringInfo(&buffer, _("collation %s"),
+                                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))
+               {
+                   StringInfoData rel;
+
+                   initStringInfo(&rel);
+                   getRelationDescription(&rel, con->conrelid);
+                   appendStringInfo(&buffer, _("constraint %s on %s"),
+                                    NameStr(con->conname), rel.data);
+                   pfree(rel.data);
+               }
+               else
+               {
+                   appendStringInfo(&buffer, _("constraint %s"),
+                                    NameStr(con->conname));
+               }
+
+               ReleaseSysCache(conTup);
+               break;
+           }
+
+       case OCLASS_CONVERSION:
+           {
+               HeapTuple   conTup;
+
+               conTup = SearchSysCache1(CONVOID,
+                                        ObjectIdGetDatum(object->objectId));
+               if (!HeapTupleIsValid(conTup))
+                   elog(ERROR, "cache lookup failed for conversion %u",
+                        object->objectId);
+               appendStringInfo(&buffer, _("conversion %s"),
+                NameStr(((Form_pg_conversion) GETSTRUCT(conTup))->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, _("default for %s"),
+                                getObjectDescription(&colobject));
+
+               systable_endscan(adscan);
+               heap_close(attrdefDesc, AccessShareLock);
+               break;
+           }
+
+       case OCLASS_LANGUAGE:
+           {
+               HeapTuple   langTup;
+
+               langTup = SearchSysCache1(LANGOID,
+                                         ObjectIdGetDatum(object->objectId));
+               if (!HeapTupleIsValid(langTup))
+                   elog(ERROR, "cache lookup failed for language %u",
+                        object->objectId);
+               appendStringInfo(&buffer, _("language %s"),
+                 NameStr(((Form_pg_language) GETSTRUCT(langTup))->lanname));
+               ReleaseSysCache(langTup);
+               break;
+           }
+       case OCLASS_LARGEOBJECT:
+           appendStringInfo(&buffer, _("large object %u"),
+                            object->objectId);
+           break;
+
+       case OCLASS_OPERATOR:
+           appendStringInfo(&buffer, _("operator %s"),
+                            format_operator(object->objectId));
+           break;
+
+       case OCLASS_OPCLASS:
+           {
+               HeapTuple   opcTup;
+               Form_pg_opclass opcForm;
+               HeapTuple   amTup;
+               Form_pg_am  amForm;
+               char       *nspname;
+
+               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);
+
+               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);
+
+               /* Qualify the name if not visible in search path */
+               if (OpclassIsVisible(object->objectId))
+                   nspname = NULL;
+               else
+                   nspname = get_namespace_name(opcForm->opcnamespace);
+
+               appendStringInfo(&buffer, _("operator class %s for access method %s"),
+                                quote_qualified_identifier(nspname,
+                                                 NameStr(opcForm->opcname)),
+                                NameStr(amForm->amname));
+
+               ReleaseSysCache(amTup);
+               ReleaseSysCache(opcTup);
+               break;
+           }
+
+       case OCLASS_OPFAMILY:
+           getOpFamilyDescription(&buffer, object->objectId);
+           break;
+
+       case OCLASS_AMOP:
+           {
+               Relation    amopDesc;
+               HeapTuple   tup;
+               ScanKeyData skey[1];
+               SysScanDesc amscan;
+               Form_pg_amop amopForm;
+               StringInfoData opfam;
+
+               amopDesc = heap_open(AccessMethodOperatorRelationId,
+                                    AccessShareLock);
+
+               ScanKeyInit(&skey[0],
+                           ObjectIdAttributeNumber,
+                           BTEqualStrategyNumber, F_OIDEQ,
+                           ObjectIdGetDatum(object->objectId));
+
+               amscan = systable_beginscan(amopDesc, AccessMethodOperatorOidIndexId, true,
+                                           SnapshotNow, 1, skey);
+
+               tup = systable_getnext(amscan);
+
+               if (!HeapTupleIsValid(tup))
+                   elog(ERROR, "could not find tuple for amop entry %u",
+                        object->objectId);
+
+               amopForm = (Form_pg_amop) GETSTRUCT(tup);
+
+               initStringInfo(&opfam);
+               getOpFamilyDescription(&opfam, amopForm->amopfamily);
+
+               /*------
+                  translator: %d is the operator strategy (a number), the
+                  first two %s's are data type names, the third %s is the
+                  description of the operator family, and the last %s is the
+                  textual form of the operator with arguments.  */
+               appendStringInfo(&buffer, _("operator %d (%s, %s) of %s: %s"),
+                                amopForm->amopstrategy,
+                                format_type_be(amopForm->amoplefttype),
+                                format_type_be(amopForm->amoprighttype),
+                                opfam.data,
+                                format_operator(amopForm->amopopr));
+
+               pfree(opfam.data);
+
+               systable_endscan(amscan);
+               heap_close(amopDesc, AccessShareLock);
+               break;
+           }
+
+       case OCLASS_AMPROC:
+           {
+               Relation    amprocDesc;
+               ScanKeyData skey[1];
+               SysScanDesc amscan;
+               HeapTuple   tup;
+               Form_pg_amproc amprocForm;
+               StringInfoData opfam;
+
+               amprocDesc = heap_open(AccessMethodProcedureRelationId,
+                                      AccessShareLock);
+
+               ScanKeyInit(&skey[0],
+                           ObjectIdAttributeNumber,
+                           BTEqualStrategyNumber, F_OIDEQ,
+                           ObjectIdGetDatum(object->objectId));
+
+               amscan = systable_beginscan(amprocDesc, AccessMethodProcedureOidIndexId, true,
+                                           SnapshotNow, 1, skey);
+
+               tup = systable_getnext(amscan);
+
+               if (!HeapTupleIsValid(tup))
+                   elog(ERROR, "could not find tuple for amproc entry %u",
+                        object->objectId);
+
+               amprocForm = (Form_pg_amproc) GETSTRUCT(tup);
+
+               initStringInfo(&opfam);
+               getOpFamilyDescription(&opfam, amprocForm->amprocfamily);
+
+               /*------
+                  translator: %d is the function number, the first two %s's
+                  are data type names, the third %s is the description of the
+                  operator family, and the last %s is the textual form of the
+                  function with arguments.  */
+               appendStringInfo(&buffer, _("function %d (%s, %s) of %s: %s"),
+                                amprocForm->amprocnum,
+                                format_type_be(amprocForm->amproclefttype),
+                                format_type_be(amprocForm->amprocrighttype),
+                                opfam.data,
+                                format_procedure(amprocForm->amproc));
+
+               pfree(opfam.data);
+
+               systable_endscan(amscan);
+               heap_close(amprocDesc, AccessShareLock);
+               break;
+           }
+
+       case OCLASS_REWRITE:
+           {
+               Relation    ruleDesc;
+               ScanKeyData skey[1];
+               SysScanDesc rcscan;
+               HeapTuple   tup;
+               Form_pg_rewrite rule;
+
+               ruleDesc = heap_open(RewriteRelationId, AccessShareLock);
+
+               ScanKeyInit(&skey[0],
+                           ObjectIdAttributeNumber,
+                           BTEqualStrategyNumber, F_OIDEQ,
+                           ObjectIdGetDatum(object->objectId));
+
+               rcscan = systable_beginscan(ruleDesc, RewriteOidIndexId, true,
+                                           SnapshotNow, 1, skey);
+
+               tup = systable_getnext(rcscan);
+
+               if (!HeapTupleIsValid(tup))
+                   elog(ERROR, "could not find tuple for rule %u",
+                        object->objectId);
+
+               rule = (Form_pg_rewrite) GETSTRUCT(tup);
+
+               appendStringInfo(&buffer, _("rule %s on "),
+                                NameStr(rule->rulename));
+               getRelationDescription(&buffer, rule->ev_class);
+
+               systable_endscan(rcscan);
+               heap_close(ruleDesc, AccessShareLock);
+               break;
+           }
+
+       case OCLASS_TRIGGER:
+           {
+               Relation    trigDesc;
+               ScanKeyData skey[1];
+               SysScanDesc tgscan;
+               HeapTuple   tup;
+               Form_pg_trigger trig;
+
+               trigDesc = heap_open(TriggerRelationId, AccessShareLock);
+
+               ScanKeyInit(&skey[0],
+                           ObjectIdAttributeNumber,
+                           BTEqualStrategyNumber, F_OIDEQ,
+                           ObjectIdGetDatum(object->objectId));
+
+               tgscan = systable_beginscan(trigDesc, TriggerOidIndexId, true,
+                                           SnapshotNow, 1, skey);
+
+               tup = systable_getnext(tgscan);
+
+               if (!HeapTupleIsValid(tup))
+                   elog(ERROR, "could not find tuple for trigger %u",
+                        object->objectId);
+
+               trig = (Form_pg_trigger) GETSTRUCT(tup);
+
+               appendStringInfo(&buffer, _("trigger %s on "),
+                                NameStr(trig->tgname));
+               getRelationDescription(&buffer, trig->tgrelid);
+
+               systable_endscan(tgscan);
+               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, _("schema %s"), nspname);
+               break;
+           }
+
+       case OCLASS_TSPARSER:
+           {
+               HeapTuple   tup;
+
+               tup = SearchSysCache1(TSPARSEROID,
+                                     ObjectIdGetDatum(object->objectId));
+               if (!HeapTupleIsValid(tup))
+                   elog(ERROR, "cache lookup failed for text search parser %u",
+                        object->objectId);
+               appendStringInfo(&buffer, _("text search parser %s"),
+                    NameStr(((Form_pg_ts_parser) GETSTRUCT(tup))->prsname));
+               ReleaseSysCache(tup);
+               break;
+           }
+
+       case OCLASS_TSDICT:
+           {
+               HeapTuple   tup;
+
+               tup = SearchSysCache1(TSDICTOID,
+                                     ObjectIdGetDatum(object->objectId));
+               if (!HeapTupleIsValid(tup))
+                   elog(ERROR, "cache lookup failed for text search dictionary %u",
+                        object->objectId);
+               appendStringInfo(&buffer, _("text search dictionary %s"),
+                     NameStr(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname));
+               ReleaseSysCache(tup);
+               break;
+           }
+
+       case OCLASS_TSTEMPLATE:
+           {
+               HeapTuple   tup;
+
+               tup = SearchSysCache1(TSTEMPLATEOID,
+                                     ObjectIdGetDatum(object->objectId));
+               if (!HeapTupleIsValid(tup))
+                   elog(ERROR, "cache lookup failed for text search template %u",
+                        object->objectId);
+               appendStringInfo(&buffer, _("text search template %s"),
+                 NameStr(((Form_pg_ts_template) GETSTRUCT(tup))->tmplname));
+               ReleaseSysCache(tup);
+               break;
+           }
+
+       case OCLASS_TSCONFIG:
+           {
+               HeapTuple   tup;
+
+               tup = SearchSysCache1(TSCONFIGOID,
+                                     ObjectIdGetDatum(object->objectId));
+               if (!HeapTupleIsValid(tup))
+                   elog(ERROR, "cache lookup failed for text search configuration %u",
+                        object->objectId);
+               appendStringInfo(&buffer, _("text search configuration %s"),
+                    NameStr(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname));
+               ReleaseSysCache(tup);
+               break;
+           }
+
+       case OCLASS_ROLE:
+           {
+               appendStringInfo(&buffer, _("role %s"),
+                                GetUserNameFromId(object->objectId));
+               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, _("database %s"), 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, _("tablespace %s"), tblspace);
+               break;
+           }
+
+       case OCLASS_FDW:
+           {
+               ForeignDataWrapper *fdw;
+
+               fdw = GetForeignDataWrapper(object->objectId);
+               appendStringInfo(&buffer, _("foreign-data wrapper %s"), fdw->fdwname);
+               break;
+           }
+
+       case OCLASS_FOREIGN_SERVER:
+           {
+               ForeignServer *srv;
+
+               srv = GetForeignServer(object->objectId);
+               appendStringInfo(&buffer, _("server %s"), srv->servername);
+               break;
+           }
+
+       case OCLASS_USER_MAPPING:
+           {
+               HeapTuple   tup;
+               Oid         useid;
+               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 = GetUserNameFromId(useid);
+               else
+                   usename = "public";
+
+               appendStringInfo(&buffer, _("user mapping for %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);
+
+               switch (defacl->defaclobjtype)
+               {
+                   case DEFACLOBJ_RELATION:
+                       appendStringInfo(&buffer,
+                                        _("default privileges on new relations belonging to role %s"),
+                                     GetUserNameFromId(defacl->defaclrole));
+                       break;
+                   case DEFACLOBJ_SEQUENCE:
+                       appendStringInfo(&buffer,
+                                        _("default privileges on new sequences belonging to role %s"),
+                                     GetUserNameFromId(defacl->defaclrole));
+                       break;
+                   case DEFACLOBJ_FUNCTION:
+                       appendStringInfo(&buffer,
+                                        _("default privileges on new functions belonging to role %s"),
+                                     GetUserNameFromId(defacl->defaclrole));
+                       break;
+                   case DEFACLOBJ_TYPE:
+                       appendStringInfo(&buffer,
+                                        _("default privileges on new types belonging to role %s"),
+                                     GetUserNameFromId(defacl->defaclrole));
+                       break;
+                   default:
+                       /* shouldn't get here */
+                       appendStringInfo(&buffer,
+                               _("default privileges belonging to role %s"),
+                                     GetUserNameFromId(defacl->defaclrole));
+                       break;
+               }
+
+               if (OidIsValid(defacl->defaclnamespace))
+               {
+                   appendStringInfo(&buffer,
+                                    _(" in schema %s"),
+                               get_namespace_name(defacl->defaclnamespace));
+               }
+
+               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, _("extension %s"), extname);
+               break;
+           }
+
+        case OCLASS_EVENT_TRIGGER:
+           {
+               HeapTuple   tup;
+
+               tup = SearchSysCache1(EVENTTRIGGEROID,
+                                     ObjectIdGetDatum(object->objectId));
+               if (!HeapTupleIsValid(tup))
+                   elog(ERROR, "cache lookup failed for event trigger %u",
+                        object->objectId);
+               appendStringInfo(&buffer, _("event trigger %s"),
+                    NameStr(((Form_pg_event_trigger) GETSTRUCT(tup))->evtname));
+               ReleaseSysCache(tup);
+               break;
+           }
+
+       default:
+           appendStringInfo(&buffer, "unrecognized object %u %u %d",
+                            object->classId,
+                            object->objectId,
+                            object->objectSubId);
+           break;
+   }
+
+   return buffer.data;
+}
+
+/*
+ * getObjectDescriptionOids: as above, except the object is specified by Oids
+ */
+char *
+getObjectDescriptionOids(Oid classid, Oid objid)
+{
+   ObjectAddress address;
+
+   address.classId = classid;
+   address.objectId = objid;
+   address.objectSubId = 0;
+
+   return getObjectDescription(&address);
+}
+
+/*
+ * subroutine for getObjectDescription: describe a relation
+ */
+static void
+getRelationDescription(StringInfo buffer, Oid relid)
+{
+   HeapTuple   relTup;
+   Form_pg_class relForm;
+   char       *nspname;
+   char       *relname;
+
+   relTup = SearchSysCache1(RELOID,
+                            ObjectIdGetDatum(relid));
+   if (!HeapTupleIsValid(relTup))
+       elog(ERROR, "cache lookup failed for relation %u", relid);
+   relForm = (Form_pg_class) GETSTRUCT(relTup);
+
+   /* Qualify the name if not visible in search path */
+   if (RelationIsVisible(relid))
+       nspname = NULL;
+   else
+       nspname = get_namespace_name(relForm->relnamespace);
+
+   relname = quote_qualified_identifier(nspname, NameStr(relForm->relname));
+
+   switch (relForm->relkind)
+   {
+       case RELKIND_RELATION:
+           appendStringInfo(buffer, _("table %s"),
+                            relname);
+           break;
+       case RELKIND_INDEX:
+           appendStringInfo(buffer, _("index %s"),
+                            relname);
+           break;
+       case RELKIND_SEQUENCE:
+           appendStringInfo(buffer, _("sequence %s"),
+                            relname);
+           break;
+       case RELKIND_TOASTVALUE:
+           appendStringInfo(buffer, _("toast table %s"),
+                            relname);
+           break;
+       case RELKIND_VIEW:
+           appendStringInfo(buffer, _("view %s"),
+                            relname);
+           break;
+       case RELKIND_MATVIEW:
+           appendStringInfo(buffer, _("materialized view %s"),
+                            relname);
+           break;
+       case RELKIND_COMPOSITE_TYPE:
+           appendStringInfo(buffer, _("composite type %s"),
+                            relname);
+           break;
+       case RELKIND_FOREIGN_TABLE:
+           appendStringInfo(buffer, _("foreign table %s"),
+                            relname);
+           break;
+       default:
+           /* shouldn't get here */
+           appendStringInfo(buffer, _("relation %s"),
+                            relname);
+           break;
+   }
+
+   ReleaseSysCache(relTup);
+}
+
+/*
+ * subroutine for getObjectDescription: describe an operator family
+ */
+static void
+getOpFamilyDescription(StringInfo buffer, Oid opfid)
+{
+   HeapTuple   opfTup;
+   Form_pg_opfamily opfForm;
+   HeapTuple   amTup;
+   Form_pg_am  amForm;
+   char       *nspname;
+
+   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);
+
+   /* Qualify the name if not visible in search path */
+   if (OpfamilyIsVisible(opfid))
+       nspname = NULL;
+   else
+       nspname = get_namespace_name(opfForm->opfnamespace);
+
+   appendStringInfo(buffer, _("operator family %s for access method %s"),
+                    quote_qualified_identifier(nspname,
+                                               NameStr(opfForm->opfname)),
+                    NameStr(amForm->amname));
+
+   ReleaseSysCache(amTup);
+   ReleaseSysCache(opfTup);
+}
+
+/*
+ * SQL-level callable version of getObjectDescription
+ */
+Datum
+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;
+   ObjectAddress address;
+
+   /* for "pinned" items in pg_depend, return null */
+   if (!OidIsValid(classid) && !OidIsValid(objid))
+       PG_RETURN_NULL();
+
+   address.classId = classid;
+   address.objectId = objid;
+   address.objectSubId = subobjid;
+
+   description = getObjectDescription(&address);
+   PG_RETURN_TEXT_P(cstring_to_text(description));
+}
+
+/*
+ * SQL-level callable function to obtain object type + identity
+ */
+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);
+           }
+
+           /*
+            * We only return the object name if it can be used (together
+            * with the schema name, if any) as an unique identifier.
+            */
+           if (get_object_namensp_unique(address.classId))
+           {
+               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;
+               ScanKeyData skey[1];
+               SysScanDesc amscan;
+               Form_pg_amop amopForm;
+               StringInfoData opfam;
+
+               amopDesc = heap_open(AccessMethodOperatorRelationId,
+                                    AccessShareLock);
+
+               ScanKeyInit(&skey[0],
+                           ObjectIdAttributeNumber,
+                           BTEqualStrategyNumber, F_OIDEQ,
+                           ObjectIdGetDatum(object->objectId));
+
+               amscan = systable_beginscan(amopDesc, AccessMethodOperatorOidIndexId, true,
+                                           SnapshotNow, 1, skey);
+
+               tup = systable_getnext(amscan);
+
+               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);
+
+               appendStringInfo(&buffer, "operator %d (%s, %s) of %s",
+                                amopForm->amopstrategy,
+                                format_type_be_qualified(amopForm->amoplefttype),
+                                format_type_be_qualified(amopForm->amoprighttype),
+                                opfam.data);
+
+               pfree(opfam.data);
+
+               systable_endscan(amscan);
+               heap_close(amopDesc, AccessShareLock);
+               break;
+           }
+
+       case OCLASS_AMPROC:
+           {
+               Relation    amprocDesc;
+               ScanKeyData skey[1];
+               SysScanDesc amscan;
+               HeapTuple   tup;
+               Form_pg_amproc amprocForm;
+               StringInfoData opfam;
+
+               amprocDesc = heap_open(AccessMethodProcedureRelationId,
+                                      AccessShareLock);
+
+               ScanKeyInit(&skey[0],
+                           ObjectIdAttributeNumber,
+                           BTEqualStrategyNumber, F_OIDEQ,
+                           ObjectIdGetDatum(object->objectId));
+
+               amscan = systable_beginscan(amprocDesc, AccessMethodProcedureOidIndexId, true,
+                                           SnapshotNow, 1, skey);
+
+               tup = systable_getnext(amscan);
+
+               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);
+
+               appendStringInfo(&buffer, "function %d (%s, %s) of %s",
+                                amprocForm->amprocnum,
+                                format_type_be_qualified(amprocForm->amproclefttype),
+                                format_type_be_qualified(amprocForm->amprocrighttype),
+                                opfam.data);
+
+               pfree(opfam.data);
+
+               systable_endscan(amscan);
+               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);
+}