From f8348ea32ec8d713cd6e5d5e16f15edef22c4d03 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Wed, 20 Mar 2013 18:19:19 -0300 Subject: [PATCH] Allow extracting machine-readable object identity Introduce pg_identify_object(oid,oid,int4), which is similar in spirit to pg_describe_object but instead produces a row of machine-readable information to uniquely identify the given object, without resorting to OIDs or other internal representation. This is intended to be used in the event trigger implementation, to report objects being operated on; but it has usefulness of its own. Catalog version bumped because of the new function. --- doc/src/sgml/func.sgml | 29 +- src/backend/catalog/dependency.c | 816 +---------- src/backend/catalog/objectaddress.c | 2031 ++++++++++++++++++++++++++- src/backend/commands/alter.c | 52 - src/backend/parser/gram.y | 65 +- src/backend/utils/adt/format_type.c | 26 +- src/backend/utils/adt/regproc.c | 52 +- src/include/catalog/catversion.h | 2 +- src/include/catalog/dependency.h | 3 - src/include/catalog/objectaddress.h | 13 +- src/include/catalog/pg_proc.h | 3 + src/include/utils/builtins.h | 4 + 12 files changed, 2141 insertions(+), 955 deletions(-) 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);