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