diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 51df17248a..490d710643 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -13930,6 +13930,10 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); pg_describe_object + + pg_identify_object + + pg_get_constraintdef @@ -14029,6 +14033,11 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); text get description of a database object + + pg_identify_object(catalog_id oid, object_id oid, object_sub_id integer) + type text, schema text, name text, identity text + get identity of a database object + pg_get_constraintdef(constraint_oid) text @@ -14273,12 +14282,30 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); - pg_describe_object returns a description of a database + pg_describe_object returns a textual description of a database object specified by catalog OID, object OID and a (possibly zero) sub-object ID. + This description is intended to be human-readable, and might be translated, + depending on server configuration. This is useful to determine the identity of an object as stored in the pg_depend catalog. + + pg_identify_object returns a row containing enough information + to uniquely identify the database object specified by catalog OID, object OID and a + (possibly zero) sub-object ID. This information is intended to be machine-readable, + and is never translated. + type identifies the type of database object; + schema is the schema name that the object belongs in, or + NULL for object types that do not belong to schemas; + name is the name of the object, quoted if necessary, only + present if it can be used (alongside schema name, if pertinent) as an unique + identifier of the object, otherwise NULL; + identity is the complete object identity, with the precise format + depending on object type, and each part within the format being + schema-qualified and quoted as necessary. + + pg_typeof returns the OID of the data type of the value that is passed to it. This can be helpful for troubleshooting or diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index 7b8e024633..ddf199049e 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -15,12 +15,10 @@ #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" @@ -56,24 +54,18 @@ #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" @@ -196,8 +188,6 @@ static bool object_address_present_add_flags(const ObjectAddress *object, 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); /* @@ -2193,7 +2183,7 @@ 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", + elog(ERROR, "invalid non-zero objectSubId for object class %u", object->classId); switch (object->classId) @@ -2297,807 +2287,3 @@ getObjectClass(const ObjectAddress *object) 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)); -} diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 6f60d7cad1..48ef6bf0a4 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -20,8 +20,12 @@ #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" @@ -46,6 +50,7 @@ #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" @@ -54,6 +59,7 @@ #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" @@ -88,6 +94,10 @@ typedef struct 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[] = @@ -101,7 +111,8 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, InvalidAttrNumber, InvalidAttrNumber, - -1 + -1, + false }, { CollationRelationId, @@ -112,7 +123,8 @@ static ObjectPropertyType ObjectProperty[] = Anum_pg_collation_collnamespace, Anum_pg_collation_collowner, InvalidAttrNumber, - ACL_KIND_COLLATION + ACL_KIND_COLLATION, + true }, { ConstraintRelationId, @@ -123,7 +135,8 @@ static ObjectPropertyType ObjectProperty[] = Anum_pg_constraint_connamespace, InvalidAttrNumber, InvalidAttrNumber, - -1 + -1, + false }, { ConversionRelationId, @@ -134,7 +147,8 @@ static ObjectPropertyType ObjectProperty[] = Anum_pg_conversion_connamespace, Anum_pg_conversion_conowner, InvalidAttrNumber, - ACL_KIND_CONVERSION + ACL_KIND_CONVERSION, + true }, { DatabaseRelationId, @@ -145,7 +159,8 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, Anum_pg_database_datdba, Anum_pg_database_datacl, - ACL_KIND_DATABASE + ACL_KIND_DATABASE, + true }, { ExtensionRelationId, @@ -156,7 +171,8 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, /* extension doesn't belong to extnamespace */ Anum_pg_extension_extowner, InvalidAttrNumber, - ACL_KIND_EXTENSION + ACL_KIND_EXTENSION, + true }, { ForeignDataWrapperRelationId, @@ -167,7 +183,8 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, Anum_pg_foreign_data_wrapper_fdwowner, Anum_pg_foreign_data_wrapper_fdwacl, - ACL_KIND_FDW + ACL_KIND_FDW, + true }, { ForeignServerRelationId, @@ -178,7 +195,8 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, Anum_pg_foreign_server_srvowner, Anum_pg_foreign_server_srvacl, - ACL_KIND_FOREIGN_SERVER + ACL_KIND_FOREIGN_SERVER, + true }, { ProcedureRelationId, @@ -189,7 +207,8 @@ static ObjectPropertyType ObjectProperty[] = Anum_pg_proc_pronamespace, Anum_pg_proc_proowner, Anum_pg_proc_proacl, - ACL_KIND_PROC + ACL_KIND_PROC, + false }, { LanguageRelationId, @@ -200,7 +219,8 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, Anum_pg_language_lanowner, Anum_pg_language_lanacl, - ACL_KIND_LANGUAGE + ACL_KIND_LANGUAGE, + true }, { LargeObjectMetadataRelationId, @@ -211,7 +231,8 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, Anum_pg_largeobject_metadata_lomowner, Anum_pg_largeobject_metadata_lomacl, - ACL_KIND_LARGEOBJECT + ACL_KIND_LARGEOBJECT, + false }, { OperatorClassRelationId, @@ -222,7 +243,8 @@ static ObjectPropertyType ObjectProperty[] = Anum_pg_opclass_opcnamespace, Anum_pg_opclass_opcowner, InvalidAttrNumber, - ACL_KIND_OPCLASS + ACL_KIND_OPCLASS, + true }, { OperatorRelationId, @@ -233,7 +255,8 @@ static ObjectPropertyType ObjectProperty[] = Anum_pg_operator_oprnamespace, Anum_pg_operator_oprowner, InvalidAttrNumber, - ACL_KIND_OPER + ACL_KIND_OPER, + false }, { OperatorFamilyRelationId, @@ -244,7 +267,8 @@ static ObjectPropertyType ObjectProperty[] = Anum_pg_opfamily_opfnamespace, Anum_pg_opfamily_opfowner, InvalidAttrNumber, - ACL_KIND_OPFAMILY + ACL_KIND_OPFAMILY, + true }, { AuthIdRelationId, @@ -255,7 +279,8 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, InvalidAttrNumber, InvalidAttrNumber, - -1 + -1, + true }, { RewriteRelationId, @@ -266,7 +291,8 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, InvalidAttrNumber, InvalidAttrNumber, - -1 + -1, + false }, { NamespaceRelationId, @@ -277,7 +303,8 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, Anum_pg_namespace_nspowner, Anum_pg_namespace_nspacl, - ACL_KIND_NAMESPACE + ACL_KIND_NAMESPACE, + true }, { RelationRelationId, @@ -288,7 +315,8 @@ static ObjectPropertyType ObjectProperty[] = Anum_pg_class_relnamespace, Anum_pg_class_relowner, Anum_pg_class_relacl, - ACL_KIND_CLASS + ACL_KIND_CLASS, + true }, { TableSpaceRelationId, @@ -299,7 +327,8 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, Anum_pg_tablespace_spcowner, Anum_pg_tablespace_spcacl, - ACL_KIND_TABLESPACE + ACL_KIND_TABLESPACE, + true }, { TriggerRelationId, @@ -311,6 +340,7 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, InvalidAttrNumber, -1, + false }, { EventTriggerRelationId, @@ -322,6 +352,7 @@ static ObjectPropertyType ObjectProperty[] = Anum_pg_event_trigger_evtowner, InvalidAttrNumber, ACL_KIND_EVENT_TRIGGER, + true }, { TSConfigRelationId, @@ -332,7 +363,8 @@ static ObjectPropertyType ObjectProperty[] = Anum_pg_ts_config_cfgnamespace, Anum_pg_ts_config_cfgowner, InvalidAttrNumber, - ACL_KIND_TSCONFIGURATION + ACL_KIND_TSCONFIGURATION, + true }, { TSDictionaryRelationId, @@ -343,7 +375,8 @@ static ObjectPropertyType ObjectProperty[] = Anum_pg_ts_dict_dictnamespace, Anum_pg_ts_dict_dictowner, InvalidAttrNumber, - ACL_KIND_TSDICTIONARY + ACL_KIND_TSDICTIONARY, + true }, { TSParserRelationId, @@ -355,6 +388,7 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, InvalidAttrNumber, -1, + true }, { TSTemplateRelationId, @@ -366,6 +400,7 @@ static ObjectPropertyType ObjectProperty[] = InvalidAttrNumber, InvalidAttrNumber, -1, + true, }, { TypeRelationId, @@ -376,7 +411,8 @@ static ObjectPropertyType ObjectProperty[] = Anum_pg_type_typnamespace, Anum_pg_type_typowner, Anum_pg_type_typacl, - ACL_KIND_TYPE + ACL_KIND_TYPE, + true } }; @@ -396,6 +432,15 @@ static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname, 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. @@ -1344,6 +1389,32 @@ get_object_aclkind(Oid class_id) 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. */ @@ -1374,3 +1445,1919 @@ get_object_property_data(Oid 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); +} diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 2d2ab1bcfb..665b3804d5 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -752,58 +752,6 @@ 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 diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 9d07f30906..0d82141fef 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -5195,20 +5195,25 @@ 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] | + * 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] | * AGGREGATE (arg1, ...) | - * FUNCTION (arg1, arg2, ...) | - * OPERATOR (leftoperand_typ, rightoperand_typ) | - * TRIGGER ON | + * CAST ( AS ) | + * COLUMN . | * CONSTRAINT ON | - * RULE ON ] + * FUNCTION (arg1, arg2, ...) | + * LARGE OBJECT | + * OPERATOR (leftoperand_typ, rightoperand_typ) | + * OPERATOR CLASS USING | + * OPERATOR FAMILY USING | + * RULE ON | + * TRIGGER ON ] * IS 'text' * *****************************************************************************/ @@ -5332,38 +5337,6 @@ CommentStmt: n->comment = $7; $$ = (Node *) n; } - | COMMENT ON TEXT_P SEARCH PARSER any_name IS comment_text - { - CommentStmt *n = makeNode(CommentStmt); - n->objtype = OBJECT_TSPARSER; - n->objname = $6; - n->comment = $8; - $$ = (Node *) n; - } - | COMMENT ON TEXT_P SEARCH DICTIONARY any_name IS comment_text - { - CommentStmt *n = makeNode(CommentStmt); - n->objtype = OBJECT_TSDICTIONARY; - n->objname = $6; - n->comment = $8; - $$ = (Node *) n; - } - | COMMENT ON TEXT_P SEARCH TEMPLATE any_name IS comment_text - { - CommentStmt *n = makeNode(CommentStmt); - n->objtype = OBJECT_TSTEMPLATE; - n->objname = $6; - n->comment = $8; - $$ = (Node *) n; - } - | COMMENT ON TEXT_P SEARCH CONFIGURATION any_name IS comment_text - { - CommentStmt *n = makeNode(CommentStmt); - n->objtype = OBJECT_TSCONFIGURATION; - n->objname = $6; - n->comment = $8; - $$ = (Node *) n; - } ; comment_type: @@ -5386,6 +5359,10 @@ comment_type: | SERVER { $$ = OBJECT_FOREIGN_SERVER; } | FOREIGN DATA_P WRAPPER { $$ = OBJECT_FDW; } | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } + | TEXT_P SEARCH CONFIGURATION { $$ = OBJECT_TSCONFIGURATION; } + | TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; } + | TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; } + | TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; } ; comment_text: diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c index 99771ec53c..cd164c7e7e 100644 --- a/src/backend/utils/adt/format_type.c +++ b/src/backend/utils/adt/format_type.c @@ -29,7 +29,8 @@ #define MAX_INT32_LEN 11 static char *format_type_internal(Oid type_oid, int32 typemod, - bool typemod_given, bool allow_invalid); + 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,11 +78,11 @@ format_type(PG_FUNCTION_ARGS) type_oid = PG_GETARG_OID(0); if (PG_ARGISNULL(1)) - result = format_type_internal(type_oid, -1, false, true); + 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); + result = format_type_internal(type_oid, typemod, true, true, false); } PG_RETURN_TEXT_P(cstring_to_text(result)); @@ -96,7 +97,13 @@ format_type(PG_FUNCTION_ARGS) char * format_type_be(Oid type_oid) { - return format_type_internal(type_oid, -1, false, false); + 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,14 +112,13 @@ 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); + 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 typemod_given, bool allow_invalid, + bool force_qualify) { bool with_typemod = typemod_given && (typemod >= 0); HeapTuple tuple; @@ -300,7 +306,7 @@ format_type_internal(Oid type_oid, int32 typemod, char *nspname; char *typname; - if (TypeIsVisible(type_oid)) + if (!force_qualify && TypeIsVisible(type_oid)) nspname = NULL; else nspname = get_namespace_name(typeform->typnamespace); @@ -421,7 +427,7 @@ oidvectortypes(PG_FUNCTION_ARGS) for (num = 0; num < numargs; num++) { char *typename = format_type_internal(oidArray->values[num], -1, - false, true); + false, true, false); size_t slen = strlen(typename); if (left < (slen + 2)) diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index cb11bd0ade..94599aa44b 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -41,6 +41,8 @@ #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); @@ -303,6 +305,25 @@ regprocedurein(PG_FUNCTION_ARGS) */ 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,7 +347,7 @@ 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)) + if (!force_qualify && FunctionIsVisible(procedure_oid)) nspname = NULL; else nspname = get_namespace_name(procform->pronamespace); @@ -339,7 +360,10 @@ format_procedure(Oid procedure_oid) if (i > 0) appendStringInfoChar(&buf, ','); - appendStringInfoString(&buf, format_type_be(thisargtype)); + appendStringInfoString(&buf, + force_qualify ? + format_type_be_qualified(thisargtype) : + format_type_be(thisargtype)); } appendStringInfoChar(&buf, ')'); @@ -653,8 +677,8 @@ 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) +static char * +format_operator_internal(Oid operator_oid, bool force_qualify) { char *result; HeapTuple opertup; @@ -674,9 +698,9 @@ format_operator(Oid operator_oid) /* * Would this oper be found (given the right args) by regoperatorin? - * If not, we need to qualify it. + * If not, or if caller explicitely requests it, we need to qualify it. */ - if (!OperatorIsVisible(operator_oid)) + if (force_qualify || !OperatorIsVisible(operator_oid)) { nspname = get_namespace_name(operform->oprnamespace); appendStringInfo(&buf, "%s.", @@ -687,12 +711,16 @@ format_operator(Oid operator_oid) 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,6 +741,18 @@ format_operator(Oid operator_oid) 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)" */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index db0776e24d..cbc4673d1b 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201303141 +#define CATALOG_VERSION_NO 201303201 #endif diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 8e0837f7d6..3aefbb5e6a 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -176,9 +176,6 @@ extern void recordDependencyOnSingleRelExpr(const ObjectAddress *depender, extern ObjectClass getObjectClass(const ObjectAddress *object); -extern char *getObjectDescription(const ObjectAddress *object); -extern char *getObjectDescriptionOids(Oid classid, Oid objid); - extern ObjectAddresses *new_object_addresses(void); extern void add_exact_object_address(const ObjectAddress *object, diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h index ffaf4ea25a..2f8f58da9b 100644 --- a/src/include/catalog/objectaddress.h +++ b/src/include/catalog/objectaddress.h @@ -38,6 +38,7 @@ extern void check_object_ownership(Oid roleid, 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); @@ -46,5 +47,15 @@ extern AttrNumber get_object_attnum_namespace(Oid class_id); extern AttrNumber get_object_attnum_owner(Oid class_id); extern AttrNumber get_object_attnum_acl(Oid class_id); extern AclObjectKind get_object_aclkind(Oid class_id); +extern bool get_object_namensp_unique(Oid class_id); -#endif /* PARSE_OBJECT_H */ +extern HeapTuple get_catalog_object_by_oid(Relation catalog, + Oid objectId); + +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); + +#endif /* OBJECTADDRESS_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index c97056e167..4aee00233a 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -2917,6 +2917,9 @@ DESCR("view members of a multixactid"); 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_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index c0debe400c..cd8ac9462b 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -615,7 +615,9 @@ extern Datum regdictionarysend(PG_FUNCTION_ARGS); 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,6 +1029,7 @@ extern Datum pg_encoding_max_length_sql(PG_FUNCTION_ARGS); /* 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,6 +1146,7 @@ extern Datum pg_get_multixact_members(PG_FUNCTION_ARGS); /* 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);