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.
This commit is contained in:
Alvaro Herrera 2013-03-20 18:19:19 -03:00
parent a7921f71a3
commit f8348ea32e
12 changed files with 2141 additions and 955 deletions

View File

@ -13930,6 +13930,10 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
<primary>pg_describe_object</primary>
</indexterm>
<indexterm>
<primary>pg_identify_object</primary>
</indexterm>
<indexterm>
<primary>pg_get_constraintdef</primary>
</indexterm>
@ -14029,6 +14033,11 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
<entry><type>text</type></entry>
<entry>get description of a database object</entry>
</row>
<row>
<entry><literal><function>pg_identify_object(<parameter>catalog_id</parameter> <type>oid</>, <parameter>object_id</parameter> <type>oid</>, <parameter>object_sub_id</parameter> <type>integer</>)</function></literal></entry>
<entry><parameter>type</> <type>text</>, <parameter>schema</> <type>text</>, <parameter>name</> <type>text</>, <parameter>identity</> <type>text</></entry>
<entry>get identity of a database object</entry>
</row>
<row>
<entry><literal><function>pg_get_constraintdef(<parameter>constraint_oid</parameter>)</function></literal></entry>
<entry><type>text</type></entry>
@ -14273,12 +14282,30 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
</para>
<para>
<function>pg_describe_object</function> returns a description of a database
<function>pg_describe_object</function> 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
<structname>pg_depend</structname> catalog.
</para>
<para>
<function>pg_identify_object</function> 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.
<parameter>type</> identifies the type of database object;
<parameter>schema</> is the schema name that the object belongs in, or
<literal>NULL</> for object types that do not belong to schemas;
<parameter>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 <literal>NULL</>;
<parameter>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.
</para>
<para>
<function>pg_typeof</function> returns the OID of the data type of the
value that is passed to it. This can be helpful for troubleshooting or

View File

@ -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));
}

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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] <objname> |
* COMMENT ON [ [ CONVERSION | COLLATION | DATABASE | DOMAIN |
* EXTENSION | EVENT TRIGGER | FOREIGN DATA WRAPPER |
* FOREIGN TABLE | INDEX | [PROCEDURAL] LANGUAGE |
* MATERIALIZED VIEW | ROLE | SCHEMA | SEQUENCE |
* SERVER | TABLE | TABLESPACE |
* TEXT SEARCH CONFIGURATION | TEXT SEARCH DICTIONARY |
* TEXT SEARCH PARSER | TEXT SEARCH TEMPLATE | TYPE |
* VIEW] <objname> |
* AGGREGATE <aggname> (arg1, ...) |
* FUNCTION <funcname> (arg1, arg2, ...) |
* OPERATOR <op> (leftoperand_typ, rightoperand_typ) |
* TRIGGER <triggername> ON <relname> |
* CAST (<src type> AS <dst type>) |
* COLUMN <relname>.<colname> |
* CONSTRAINT <constraintname> ON <relname> |
* RULE <rulename> ON <relname> ]
* FUNCTION <funcname> (arg1, arg2, ...) |
* LARGE OBJECT <oid> |
* OPERATOR <op> (leftoperand_typ, rightoperand_typ) |
* OPERATOR CLASS <name> USING <access-method> |
* OPERATOR FAMILY <name> USING <access-method> |
* RULE <rulename> ON <relname> |
* TRIGGER <triggername> ON <relname> ]
* IS 'text'
*
*****************************************************************************/
@ -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:

View File

@ -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))

View File

@ -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)"
*/

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201303141
#define CATALOG_VERSION_NO 201303201
#endif

View File

@ -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,

View File

@ -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 */

View File

@ -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_ ));

View File

@ -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);