*** a/src/backend/catalog/Makefile --- b/src/backend/catalog/Makefile *************** *** 11,19 **** top_builddir = ../../.. include $(top_builddir)/src/Makefile.global OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ ! pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \ ! pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \ ! pg_db_role_setting.o pg_shdepend.o pg_type.o storage.o toasting.o BKIFILES = postgres.bki postgres.description postgres.shdescription --- 11,20 ---- include $(top_builddir)/src/Makefile.global OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ ! objectaddress.o pg_aggregate.o pg_constraint.o pg_conversion.o \ ! pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \ ! pg_operator.o pg_proc.o pg_db_role_setting.o pg_shdepend.o pg_type.o \ ! storage.o toasting.o BKIFILES = postgres.bki postgres.description postgres.shdescription *** /dev/null --- b/src/backend/catalog/objectaddress.c *************** *** 0 **** --- 1,654 ---- + /*------------------------------------------------------------------------- + * + * objectaddress.c + * functions for working with ObjectAddresses + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ + + #include "postgres.h" + + #include "access/heapam.h" + #include "access/sysattr.h" + #include "catalog/catalog.h" + #include "catalog/dependency.h" + #include "catalog/indexing.h" + #include "catalog/namespace.h" + #include "catalog/objectaddress.h" + #include "catalog/pg_authid.h" + #include "catalog/pg_cast.h" + #include "catalog/pg_class.h" + #include "catalog/pg_constraint.h" + #include "catalog/pg_conversion.h" + #include "catalog/pg_database.h" + #include "catalog/pg_language.h" + #include "catalog/pg_largeobject.h" + #include "catalog/pg_largeobject_metadata.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_opclass.h" + #include "catalog/pg_opfamily.h" + #include "catalog/pg_operator.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_rewrite.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_trigger.h" + #include "catalog/pg_ts_config.h" + #include "catalog/pg_ts_dict.h" + #include "catalog/pg_ts_parser.h" + #include "catalog/pg_ts_template.h" + #include "catalog/pg_type.h" + #include "commands/dbcommands.h" + #include "commands/defrem.h" + #include "commands/proclang.h" + #include "commands/tablespace.h" + #include "commands/trigger.h" + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parse_type.h" + #include "rewrite/rewriteSupport.h" + #include "storage/lmgr.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" + #include "utils/syscache.h" + #include "utils/rel.h" + #include "utils/tqual.h" + + static ObjectAddress get_object_address_unqualified(ObjectType objtype, + List *qualname); + static Relation get_relation_by_qualified_name(ObjectType objtype, + List *objname, LOCKMODE lockmode); + static ObjectAddress get_object_address_relobject(ObjectType objtype, + List *objname, Relation *relp); + static ObjectAddress get_object_address_attribute(ObjectType objtype, + List *objname, Relation *relp, LOCKMODE lockmode); + static ObjectAddress get_object_address_opcf(ObjectType objtype, List *objname, + List *objargs); + static bool object_exists(ObjectAddress address); + + /* + * Translate an object name and arguments (as passed by the parser) to an + * ObjectAddress. + * + * The returned object will be locked using the specified lockmode. If a + * sub-object is looked up, the parent object will be locked instead. + * + * If the object is a relation or a child object of a relation (e.g. an + * attribute or contraint), the relation is also opened and *relp receives + * the open relcache entry pointer; otherwise, *relp is set to NULL. This + * is a bit grotty but it makes life simpler, since the caller will + * typically need the relcache entry too. Caller must close the relcache + * entry when done with it. The relation is locked with the specified lockmode + * if the target object is the relation itself or an attribute, but for other + * child objects, only AccessShareLock is acquired on the relation. + * + * We don't currently provide a function to release the locks acquired here; + * typically, the lock must be held until commit to guard against a concurrent + * drop operation. + */ + ObjectAddress + get_object_address(ObjectType objtype, List *objname, List *objargs, + Relation *relp, LOCKMODE lockmode) + { + ObjectAddress address; + Relation relation = NULL; + + /* Some kind of lock must be taken. */ + Assert(lockmode != NoLock); + + switch (objtype) + { + case OBJECT_INDEX: + case OBJECT_SEQUENCE: + case OBJECT_TABLE: + case OBJECT_VIEW: + relation = + get_relation_by_qualified_name(objtype, objname, lockmode); + address.classId = RelationRelationId; + address.objectId = RelationGetRelid(relation); + address.objectSubId = 0; + break; + case OBJECT_COLUMN: + address = + get_object_address_attribute(objtype, objname, &relation, + lockmode); + break; + case OBJECT_RULE: + case OBJECT_TRIGGER: + case OBJECT_CONSTRAINT: + address = get_object_address_relobject(objtype, objname, &relation); + break; + case OBJECT_DATABASE: + case OBJECT_TABLESPACE: + case OBJECT_ROLE: + case OBJECT_SCHEMA: + case OBJECT_LANGUAGE: + address = get_object_address_unqualified(objtype, objname); + break; + case OBJECT_TYPE: + address.classId = TypeRelationId; + address.objectId = + typenameTypeId(NULL, makeTypeNameFromNameList(objname), NULL); + address.objectSubId = 0; + break; + case OBJECT_AGGREGATE: + address.classId = ProcedureRelationId; + address.objectId = LookupAggNameTypeNames(objname, objargs, false); + address.objectSubId = 0; + break; + case OBJECT_FUNCTION: + address.classId = ProcedureRelationId; + address.objectId = LookupFuncNameTypeNames(objname, objargs, false); + address.objectSubId = 0; + break; + case OBJECT_OPERATOR: + Assert(list_length(objargs) == 2); + address.classId = OperatorRelationId; + address.objectId = + LookupOperNameTypeNames(NULL, objname, + (TypeName *) linitial(objargs), + (TypeName *) lsecond(objargs), + false, -1); + address.objectSubId = 0; + break; + case OBJECT_CONVERSION: + address.classId = ConversionRelationId; + address.objectId = get_conversion_oid(objname, false); + address.objectSubId = 0; + break; + case OBJECT_OPCLASS: + case OBJECT_OPFAMILY: + address = get_object_address_opcf(objtype, objname, objargs); + break; + case OBJECT_LARGEOBJECT: + Assert(list_length(objname) == 1); + address.classId = LargeObjectRelationId; + address.objectId = oidparse(linitial(objname)); + address.objectSubId = 0; + if (!LargeObjectExists(address.objectId)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("large object %u does not exist", + address.objectId))); + break; + case OBJECT_CAST: + { + TypeName *sourcetype = (TypeName *) linitial(objname); + TypeName *targettype = (TypeName *) linitial(objargs); + Oid sourcetypeid = typenameTypeId(NULL, sourcetype, NULL); + Oid targettypeid = typenameTypeId(NULL, targettype, NULL); + + address.classId = CastRelationId; + address.objectId = + get_cast_oid(sourcetypeid, targettypeid, false); + address.objectSubId = 0; + } + break; + case OBJECT_TSPARSER: + address.classId = TSParserRelationId; + address.objectId = get_ts_parser_oid(objname, false); + address.objectSubId = 0; + break; + case OBJECT_TSDICTIONARY: + address.classId = TSDictionaryRelationId; + address.objectId = get_ts_dict_oid(objname, false); + address.objectSubId = 0; + break; + case OBJECT_TSTEMPLATE: + address.classId = TSTemplateRelationId; + address.objectId = get_ts_template_oid(objname, false); + address.objectSubId = 0; + break; + case OBJECT_TSCONFIGURATION: + address.classId = TSConfigRelationId; + address.objectId = get_ts_config_oid(objname, false); + address.objectSubId = 0; + break; + default: + elog(ERROR, "unrecognized objtype: %d", (int) objtype); + } + + /* + * If we're dealing with a relation or attribute, then the relation is + * already locked. If we're dealing with any other type of object, we need + * to lock it and then verify that it still exists. + */ + if (address.classId != RelationRelationId) + { + if (IsSharedRelation(address.classId)) + LockSharedObject(address.classId, address.objectId, 0, lockmode); + else + LockDatabaseObject(address.classId, address.objectId, 0, lockmode); + /* Did it go away while we were waiting for the lock? */ + if (!object_exists(address)) + elog(ERROR, "cache lookup failed for class %u object %u subobj %d", + address.classId, address.objectId, address.objectSubId); + } + + /* Return the object address and the relation. */ + *relp = relation; + return address; + } + + /* + * Find an ObjectAddress for a type of object that is identified by an + * unqualified name. + */ + static ObjectAddress + get_object_address_unqualified(ObjectType objtype, List *qualname) + { + const char *name; + ObjectAddress address; + + /* + * The types of names handled by this function are not permitted to be + * schema-qualified or catalog-qualified. + */ + if (list_length(qualname) != 1) + { + const char *msg; + + switch (objtype) + { + case OBJECT_DATABASE: + msg = gettext_noop("database name cannot be qualified"); + break; + case OBJECT_TABLESPACE: + msg = gettext_noop("tablespace name cannot be qualified"); + break; + case OBJECT_ROLE: + msg = gettext_noop("role name cannot be qualified"); + break; + case OBJECT_SCHEMA: + msg = gettext_noop("schema name cannot be qualified"); + break; + case OBJECT_LANGUAGE: + msg = gettext_noop("language name cannot be qualified"); + break; + default: + elog(ERROR, "unrecognized objtype: %d", (int) objtype); + msg = NULL; /* placate compiler */ + } + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("%s", _(msg)))); + } + + /* Format is valid, extract the actual name. */ + name = strVal(linitial(qualname)); + + /* Translate name to OID. */ + switch (objtype) + { + case OBJECT_DATABASE: + address.classId = DatabaseRelationId; + address.objectId = get_database_oid(name, false); + address.objectSubId = 0; + break; + case OBJECT_TABLESPACE: + address.classId = TableSpaceRelationId; + address.objectId = get_tablespace_oid(name, false); + address.objectSubId = 0; + break; + case OBJECT_ROLE: + address.classId = AuthIdRelationId; + address.objectId = get_role_oid(name, false); + address.objectSubId = 0; + break; + case OBJECT_SCHEMA: + address.classId = NamespaceRelationId; + address.objectId = get_namespace_oid(name, false); + address.objectSubId = 0; + break; + case OBJECT_LANGUAGE: + address.classId = LanguageRelationId; + address.objectId = get_language_oid(name, false); + address.objectSubId = 0; + break; + default: + elog(ERROR, "unrecognized objtype: %d", (int) objtype); + /* placate compiler, which doesn't know elog won't return */ + address.classId = InvalidOid; + address.objectId = InvalidOid; + address.objectSubId = 0; + } + + return address; + } + + /* + * Locate a relation by qualified name. + */ + static Relation + get_relation_by_qualified_name(ObjectType objtype, List *objname, + LOCKMODE lockmode) + { + Relation relation; + + relation = relation_openrv(makeRangeVarFromNameList(objname), lockmode); + switch (objtype) + { + case OBJECT_INDEX: + if (relation->rd_rel->relkind != RELKIND_INDEX) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not an index", + RelationGetRelationName(relation)))); + break; + case OBJECT_SEQUENCE: + if (relation->rd_rel->relkind != RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", + RelationGetRelationName(relation)))); + break; + case OBJECT_TABLE: + if (relation->rd_rel->relkind != RELKIND_RELATION) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table", + RelationGetRelationName(relation)))); + break; + case OBJECT_VIEW: + if (relation->rd_rel->relkind != RELKIND_VIEW) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a view", + RelationGetRelationName(relation)))); + break; + default: + elog(ERROR, "unrecognized objtype: %d", (int) objtype); + break; + } + + return relation; + } + + /* + * Find object address for an object that is attached to a relation. + * + * Note that we take only an AccessShareLock on the relation. We need not + * pass down the LOCKMODE from get_object_address(), because that is the lock + * mode for the object itself, not the relation to which it is attached. + */ + static ObjectAddress + get_object_address_relobject(ObjectType objtype, List *objname, Relation *relp) + { + ObjectAddress address; + Relation relation = NULL; + int nnames; + const char *depname; + + /* Extract name of dependent object. */ + depname = strVal(lfirst(list_tail(objname))); + + /* Separate relation name from dependent object name. */ + nnames = list_length(objname); + if (nnames < 2) + { + Oid reloid; + + /* + * For compatibility with very old releases, we sometimes allow users + * to attempt to specify a rule without mentioning the relation name. + * If there's only rule by that name in the entire database, this will + * work. But objects other than rules don't get this special + * treatment. + */ + if (objtype != OBJECT_RULE) + elog(ERROR, "must specify relation and object name"); + address.classId = RewriteRelationId; + address.objectId = get_rewrite_oid_without_relid(depname, &reloid); + address.objectSubId = 0; + relation = heap_open(reloid, AccessShareLock); + } + else + { + List *relname; + Oid reloid; + + /* Extract relation name and open relation. */ + relname = list_truncate(list_copy(objname), nnames - 1); + relation = heap_openrv(makeRangeVarFromNameList(relname), + AccessShareLock); + reloid = RelationGetRelid(relation); + + switch (objtype) + { + case OBJECT_RULE: + address.classId = RewriteRelationId; + address.objectId = get_rewrite_oid(reloid, depname, false); + address.objectSubId = 0; + break; + case OBJECT_TRIGGER: + address.classId = TriggerRelationId; + address.objectId = get_trigger_oid(reloid, depname, false); + address.objectSubId = 0; + break; + case OBJECT_CONSTRAINT: + address.classId = ConstraintRelationId; + address.objectId = get_constraint_oid(reloid, depname, false); + address.objectSubId = 0; + break; + default: + elog(ERROR, "unrecognized objtype: %d", (int) objtype); + /* placate compiler, which doesn't know elog won't return */ + address.classId = InvalidOid; + address.objectId = InvalidOid; + address.objectSubId = 0; + } + } + + /* Done. */ + *relp = relation; + return address; + } + + /* + * Find the ObjectAddress for an attribute. + */ + static ObjectAddress + get_object_address_attribute(ObjectType objtype, List *objname, + Relation *relp, LOCKMODE lockmode) + { + ObjectAddress address; + List *relname; + Oid reloid; + Relation relation; + const char *attname; + + /* Extract relation name and open relation. */ + attname = strVal(lfirst(list_tail(objname))); + relname = list_truncate(list_copy(objname), list_length(objname) - 1); + relation = heap_openrv(makeRangeVarFromNameList(relname), lockmode); + reloid = RelationGetRelid(relation); + + /* Look up attribute and construct return value. */ + address.classId = RelationRelationId; + address.objectId = reloid; + address.objectSubId = get_attnum(reloid, attname); + if (address.objectSubId == InvalidAttrNumber) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + attname, RelationGetRelationName(relation)))); + + *relp = relation; + return address; + } + + /* + * Find the ObjectAddress for an opclass or opfamily. + */ + static ObjectAddress + get_object_address_opcf(ObjectType objtype, List *objname, List *objargs) + { + Oid amoid; + ObjectAddress address; + + Assert(list_length(objargs) == 1); + amoid = get_am_oid(strVal(linitial(objargs)), false); + + switch (objtype) + { + case OBJECT_OPCLASS: + address.classId = OperatorClassRelationId; + address.objectId = get_opclass_oid(amoid, objname, false); + address.objectSubId = 0; + break; + case OBJECT_OPFAMILY: + address.classId = OperatorFamilyRelationId; + address.objectId = get_opfamily_oid(amoid, objname, false); + address.objectSubId = 0; + break; + default: + elog(ERROR, "unrecognized objtype: %d", (int) objtype); + /* placate compiler, which doesn't know elog won't return */ + address.classId = InvalidOid; + address.objectId = InvalidOid; + address.objectSubId = 0; + } + + return address; + } + + /* + * Test whether an object exists. + */ + static bool + object_exists(ObjectAddress address) + { + int cache = -1; + Oid indexoid = InvalidOid; + Relation rel; + ScanKeyData skey[1]; + SysScanDesc sd; + bool found; + + /* Sub-objects require special treatment. */ + if (address.objectSubId != 0) + { + HeapTuple atttup; + + /* Currently, attributes are the only sub-objects. */ + Assert(address.classId == RelationRelationId); + atttup = SearchSysCache2(ATTNUM, ObjectIdGetDatum(address.objectId), + Int16GetDatum(address.objectSubId)); + if (!HeapTupleIsValid(atttup)) + found = false; + else + { + found = ((Form_pg_attribute) GETSTRUCT(atttup))->attisdropped; + ReleaseSysCache(atttup); + } + return found; + } + + /* + * For object types that have a relevant syscache, we use it; for + * everything else, we'll have to do an index-scan. This switch + * sets either the cache to be used for the syscache lookup, or the + * index to be used for the index scan. + */ + switch (address.classId) + { + case RelationRelationId: + cache = RELOID; + break; + case RewriteRelationId: + indexoid = RewriteOidIndexId; + break; + case TriggerRelationId: + indexoid = TriggerOidIndexId; + break; + case ConstraintRelationId: + cache = CONSTROID; + break; + case DatabaseRelationId: + cache = DATABASEOID; + break; + case TableSpaceRelationId: + cache = TABLESPACEOID; + break; + case AuthIdRelationId: + cache = AUTHOID; + break; + case NamespaceRelationId: + cache = NAMESPACEOID; + break; + case LanguageRelationId: + cache = LANGOID; + break; + case TypeRelationId: + cache = TYPEOID; + break; + case ProcedureRelationId: + cache = PROCOID; + break; + case OperatorRelationId: + cache = OPEROID; + break; + case ConversionRelationId: + cache = CONVOID; + break; + case OperatorClassRelationId: + cache = CLAOID; + break; + case OperatorFamilyRelationId: + cache = OPFAMILYOID; + break; + case LargeObjectRelationId: + /* + * Weird backward compatibility hack: ObjectAddress notation uses + * LargeObjectRelationId for large objects, but since PostgreSQL + * 9.0, the relevant catalog is actually + * LargeObjectMetadataRelationId. + */ + address.classId = LargeObjectMetadataRelationId; + indexoid = LargeObjectMetadataOidIndexId; + break; + case CastRelationId: + indexoid = CastOidIndexId; + break; + case TSParserRelationId: + cache = TSPARSEROID; + break; + case TSDictionaryRelationId: + cache = TSDICTOID; + break; + case TSTemplateRelationId: + cache = TSTEMPLATEOID; + break; + case TSConfigRelationId: + cache = TSCONFIGOID; + break; + default: + elog(ERROR, "unrecognized classid: %u", address.classId); + } + + /* Found a syscache? */ + if (cache != -1) + return SearchSysCacheExists1(cache, ObjectIdGetDatum(address.objectId)); + + /* No syscache, so examine the table directly. */ + Assert(OidIsValid(indexoid)); + ScanKeyInit(&skey[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(address.objectId)); + rel = heap_open(address.classId, AccessShareLock); + sd = systable_beginscan(rel, indexoid, true, SnapshotNow, 1, skey); + found = HeapTupleIsValid(systable_getnext(sd)); + systable_endscan(sd); + heap_close(rel, AccessShareLock); + return found; + } *** a/src/backend/commands/comment.c --- b/src/backend/commands/comment.c *************** *** 17,99 **** #include "access/genam.h" #include "access/heapam.h" #include "catalog/indexing.h" ! #include "catalog/pg_authid.h" ! #include "catalog/pg_cast.h" ! #include "catalog/pg_constraint.h" ! #include "catalog/pg_conversion.h" ! #include "catalog/pg_database.h" #include "catalog/pg_description.h" - #include "catalog/pg_language.h" - #include "catalog/pg_largeobject.h" - #include "catalog/pg_largeobject_metadata.h" - #include "catalog/pg_namespace.h" - #include "catalog/pg_opclass.h" - #include "catalog/pg_operator.h" - #include "catalog/pg_opfamily.h" - #include "catalog/pg_proc.h" - #include "catalog/pg_rewrite.h" #include "catalog/pg_shdescription.h" - #include "catalog/pg_tablespace.h" - #include "catalog/pg_trigger.h" - #include "catalog/pg_ts_config.h" - #include "catalog/pg_ts_dict.h" - #include "catalog/pg_ts_parser.h" - #include "catalog/pg_ts_template.h" - #include "catalog/pg_type.h" #include "commands/comment.h" #include "commands/dbcommands.h" - #include "commands/defrem.h" - #include "commands/proclang.h" - #include "commands/tablespace.h" - #include "commands/trigger.h" #include "libpq/be-fsstubs.h" #include "miscadmin.h" - #include "nodes/makefuncs.h" #include "parser/parse_func.h" - #include "parser/parse_oper.h" #include "parser/parse_type.h" - #include "rewrite/rewriteSupport.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" - #include "utils/lsyscache.h" #include "utils/rel.h" - #include "utils/syscache.h" #include "utils/tqual.h" - /* ! * Static Function Prototypes -- ! * ! * The following prototypes are declared static so as not to conflict ! * with any other routines outside this module. These routines are ! * called by the public function CommentObject() routine to create ! * the appropriate comment for the specific object type. */ ! ! static void CommentRelation(int objtype, List *relname, char *comment); ! static void CommentAttribute(List *qualname, char *comment); ! static void CommentDatabase(List *qualname, char *comment); ! static void CommentNamespace(List *qualname, char *comment); ! static void CommentRule(List *qualname, char *comment); ! static void CommentType(List *typename, char *comment); ! static void CommentAggregate(List *aggregate, List *arguments, char *comment); ! static void CommentProc(List *function, List *arguments, char *comment); ! static void CommentOperator(List *opername, List *arguments, char *comment); ! static void CommentTrigger(List *qualname, char *comment); ! static void CommentConstraint(List *qualname, char *comment); ! static void CommentConversion(List *qualname, char *comment); ! static void CommentLanguage(List *qualname, char *comment); ! static void CommentOpClass(List *qualname, List *arguments, char *comment); ! static void CommentOpFamily(List *qualname, List *arguments, char *comment); ! static void CommentLargeObject(List *qualname, char *comment); ! static void CommentCast(List *qualname, List *arguments, char *comment); ! static void CommentTablespace(List *qualname, char *comment); ! static void CommentRole(List *qualname, char *comment); ! static void CommentTSParser(List *qualname, char *comment); ! static void CommentTSDictionary(List *qualname, char *comment); ! static void CommentTSTemplate(List *qualname, char *comment); ! static void CommentTSConfiguration(List *qualname, char *comment); /* --- 17,46 ---- #include "access/genam.h" #include "access/heapam.h" #include "catalog/indexing.h" ! #include "catalog/objectaddress.h" #include "catalog/pg_description.h" #include "catalog/pg_shdescription.h" #include "commands/comment.h" #include "commands/dbcommands.h" #include "libpq/be-fsstubs.h" #include "miscadmin.h" #include "parser/parse_func.h" #include "parser/parse_type.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/rel.h" #include "utils/tqual.h" /* ! * For most object types, the permissions-checking logic is simple enough ! * that it makes sense to just include it in CommentObject(). However, a few ! * object types require something more complex; for those, we define helper ! * functions. */ ! static void CheckRelationComment(int objtype, Relation relation); ! static void CheckAttributeComment(Relation relation); ! static void CheckCastComment(List *qualname, List *arguments); /* *************** *** 105,188 **** static void CommentTSConfiguration(List *qualname, char *comment); void CommentObject(CommentStmt *stmt) { switch (stmt->objtype) { case OBJECT_INDEX: case OBJECT_SEQUENCE: case OBJECT_TABLE: case OBJECT_VIEW: ! CommentRelation(stmt->objtype, stmt->objname, stmt->comment); break; case OBJECT_COLUMN: ! CommentAttribute(stmt->objname, stmt->comment); break; case OBJECT_DATABASE: ! CommentDatabase(stmt->objname, stmt->comment); ! break; ! case OBJECT_RULE: ! CommentRule(stmt->objname, stmt->comment); break; case OBJECT_TYPE: ! CommentType(stmt->objname, stmt->comment); break; case OBJECT_AGGREGATE: - CommentAggregate(stmt->objname, stmt->objargs, stmt->comment); - break; case OBJECT_FUNCTION: ! CommentProc(stmt->objname, stmt->objargs, stmt->comment); break; case OBJECT_OPERATOR: ! CommentOperator(stmt->objname, stmt->objargs, stmt->comment); break; case OBJECT_TRIGGER: ! CommentTrigger(stmt->objname, stmt->comment); break; case OBJECT_SCHEMA: ! CommentNamespace(stmt->objname, stmt->comment); ! break; ! case OBJECT_CONSTRAINT: ! CommentConstraint(stmt->objname, stmt->comment); break; case OBJECT_CONVERSION: ! CommentConversion(stmt->objname, stmt->comment); break; case OBJECT_LANGUAGE: ! CommentLanguage(stmt->objname, stmt->comment); break; case OBJECT_OPCLASS: ! CommentOpClass(stmt->objname, stmt->objargs, stmt->comment); break; case OBJECT_OPFAMILY: ! CommentOpFamily(stmt->objname, stmt->objargs, stmt->comment); break; case OBJECT_LARGEOBJECT: ! CommentLargeObject(stmt->objname, stmt->comment); break; case OBJECT_CAST: ! CommentCast(stmt->objname, stmt->objargs, stmt->comment); break; case OBJECT_TABLESPACE: ! CommentTablespace(stmt->objname, stmt->comment); break; case OBJECT_ROLE: ! CommentRole(stmt->objname, stmt->comment); break; case OBJECT_TSPARSER: ! CommentTSParser(stmt->objname, stmt->comment); break; case OBJECT_TSDICTIONARY: ! CommentTSDictionary(stmt->objname, stmt->comment); break; case OBJECT_TSTEMPLATE: ! CommentTSTemplate(stmt->objname, stmt->comment); break; case OBJECT_TSCONFIGURATION: ! CommentTSConfiguration(stmt->objname, stmt->comment); break; default: elog(ERROR, "unrecognized object type: %d", (int) stmt->objtype); } } /* --- 52,226 ---- void CommentObject(CommentStmt *stmt) { + ObjectAddress address; + Relation relation; + + /* + * When loading a dump, we may see a COMMENT ON DATABASE for the old name + * of the database. Erroring out would prevent pg_restore from completing + * (which is really pg_restore's fault, but for now we will work around + * the problem here). Consensus is that the best fix is to treat wrong + * database name as a WARNING not an ERROR; hence, the following special + * case. (If the length of stmt->objname is not 1, get_object_address will + * throw an error below; that's OK.) + */ + if (stmt->objtype == OBJECT_DATABASE && list_length(stmt->objname) == 1) + { + char *database = strVal(linitial(stmt->objname)); + if (!OidIsValid(get_database_oid(database, true))) + { + ereport(WARNING, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("database \"%s\" does not exist", database))); + return; + } + } + + /* + * Translate the parser representation which identifies this object into + * an ObjectAddress. get_object_address() will throw an error if the + * object does not exist, and will also acquire a lock on the target + * to guard against concurrent DROP operations. + */ + address = get_object_address(stmt->objtype, stmt->objname, stmt->objargs, + &relation, ShareUpdateExclusiveLock); + + /* Privilege and integrity checks. */ switch (stmt->objtype) { case OBJECT_INDEX: case OBJECT_SEQUENCE: case OBJECT_TABLE: case OBJECT_VIEW: ! CheckRelationComment(stmt->objtype, relation); break; case OBJECT_COLUMN: ! CheckAttributeComment(relation); break; case OBJECT_DATABASE: ! if (!pg_database_ownercheck(address.objectId, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, ! strVal(linitial(stmt->objname))); break; case OBJECT_TYPE: ! if (!pg_type_ownercheck(address.objectId, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, ! format_type_be(address.objectId)); break; case OBJECT_AGGREGATE: case OBJECT_FUNCTION: ! if (!pg_proc_ownercheck(address.objectId, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, ! NameListToString(stmt->objname)); break; case OBJECT_OPERATOR: ! if (!pg_oper_ownercheck(address.objectId, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, ! NameListToString(stmt->objname)); break; + case OBJECT_RULE: case OBJECT_TRIGGER: ! case OBJECT_CONSTRAINT: ! if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, ! RelationGetRelationName(relation)); break; case OBJECT_SCHEMA: ! if (!pg_namespace_ownercheck(address.objectId, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, ! strVal(linitial(stmt->objname))); break; case OBJECT_CONVERSION: ! if (!pg_conversion_ownercheck(address.objectId, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, ! NameListToString(stmt->objname)); break; case OBJECT_LANGUAGE: ! if (!superuser()) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("must be superuser to comment on procedural language"))); break; case OBJECT_OPCLASS: ! if (!pg_opclass_ownercheck(address.objectId, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, ! NameListToString(stmt->objname)); break; case OBJECT_OPFAMILY: ! if (!pg_opfamily_ownercheck(address.objectId, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, ! NameListToString(stmt->objname)); break; case OBJECT_LARGEOBJECT: ! if (!lo_compat_privileges && ! !pg_largeobject_ownercheck(address.objectId, GetUserId())) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("must be owner of large object %u", ! address.objectId))); break; case OBJECT_CAST: ! CheckCastComment(stmt->objname, stmt->objargs); break; case OBJECT_TABLESPACE: ! if (!pg_tablespace_ownercheck(address.objectId, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, ! strVal(linitial(stmt->objname))); break; case OBJECT_ROLE: ! if (!has_privs_of_role(GetUserId(), address.objectId)) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("must be member of role \"%s\" to comment upon it", ! strVal(linitial(stmt->objname))))); break; case OBJECT_TSPARSER: ! if (!superuser()) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("must be superuser to comment on text search parser"))); break; case OBJECT_TSDICTIONARY: ! if (!pg_ts_dict_ownercheck(address.objectId, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, ! NameListToString(stmt->objname)); break; case OBJECT_TSTEMPLATE: ! if (!superuser()) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("must be superuser to comment on text search template"))); break; case OBJECT_TSCONFIGURATION: ! if (!pg_ts_config_ownercheck(address.objectId, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, ! NameListToString(stmt->objname)); break; default: elog(ERROR, "unrecognized object type: %d", (int) stmt->objtype); } + + /* + * Databases, tablespaces, and roles are cluster-wide objects, so any + * comments on those objects are recorded in the shared pg_shdescription + * catalog. Comments on all other objects are recorded in pg_description. + */ + if (stmt->objtype == OBJECT_DATABASE || stmt->objtype == OBJECT_TABLESPACE + || stmt->objtype == OBJECT_ROLE) + CreateSharedComments(address.objectId, address.classId, stmt->comment); + else + CreateComments(address.objectId, address.classId, address.objectSubId, + stmt->comment); + + /* + * If get_object_address() opened the relation for us, we close it to keep + * the reference count correct - but we retain any locks acquired by + * get_object_address() until commit time, to guard against concurrent + * activity. + */ + if (relation != NULL) + relation_close(relation, NoLock); } /* *************** *** 524,559 **** GetComment(Oid oid, Oid classoid, int32 subid) } /* ! * CommentRelation -- ! * ! * This routine is used to add/drop a comment from a relation, where ! * a relation is a TABLE, SEQUENCE, VIEW or INDEX. The routine simply ! * finds the relation name by searching the system cache, locating ! * the appropriate tuple, and inserting a comment using that ! * tuple's oid. Its parameters are the relation name and comments. */ static void ! CommentRelation(int objtype, List *relname, char *comment) { - Relation relation; - RangeVar *tgtrel; - - tgtrel = makeRangeVarFromNameList(relname); - - /* - * Open the relation. We do this mainly to acquire a lock that ensures no - * one else drops the relation before we commit. (If they did, they'd - * fail to remove the entry we are about to make in pg_description.) - */ - relation = relation_openrv(tgtrel, AccessShareLock); - /* Check object security */ if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); /* Next, verify that the relation type matches the intent */ - switch (objtype) { case OBJECT_INDEX: --- 562,578 ---- } /* ! * Check whether the user is allowed to comment on this relation. */ static void ! CheckRelationComment(int objtype, Relation relation) { /* Check object security */ if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); /* Next, verify that the relation type matches the intent */ switch (objtype) { case OBJECT_INDEX: *************** *** 585,632 **** CommentRelation(int objtype, List *relname, char *comment) RelationGetRelationName(relation)))); break; } - - /* Create the comment using the relation's oid */ - CreateComments(RelationGetRelid(relation), RelationRelationId, - 0, comment); - - /* Done, but hold lock until commit */ - relation_close(relation, NoLock); } /* ! * CommentAttribute -- ! * ! * This routine is used to add/drop a comment from an attribute ! * such as a table's column. The routine will check security ! * restrictions and then attempt to look up the specified ! * attribute. If successful, a comment is added/dropped, else an ! * ereport() exception is thrown. The parameters are the relation ! * and attribute names, and the comment */ static void ! CommentAttribute(List *qualname, char *comment) { - int nnames; - List *relname; - char *attrname; - RangeVar *rel; - Relation relation; - AttrNumber attnum; - - /* Separate relname and attr name */ - nnames = list_length(qualname); - if (nnames < 2) /* parser messed up */ - elog(ERROR, "must specify relation and attribute"); - relname = list_truncate(list_copy(qualname), nnames - 1); - attrname = strVal(lfirst(list_tail(qualname))); - - /* Open the containing relation to ensure it won't go away meanwhile */ - rel = makeRangeVarFromNameList(relname); - relation = relation_openrv(rel, AccessShareLock); - - /* Check object security */ - if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); --- 604,618 ---- RelationGetRelationName(relation)))); break; } } /* ! * Check whether the user is allowed to comment on an attribute of the ! * specified relation. */ static void ! CheckAttributeComment(Relation relation) { if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); *************** *** 645,1257 **** CommentAttribute(List *qualname, char *comment) (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table, view, or composite type", RelationGetRelationName(relation)))); - - /* Now, fetch the attribute number from the system cache */ - - attnum = get_attnum(RelationGetRelid(relation), attrname); - if (attnum == InvalidAttrNumber) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - attrname, RelationGetRelationName(relation)))); - - /* Create the comment using the relation's oid */ - CreateComments(RelationGetRelid(relation), RelationRelationId, - (int32) attnum, comment); - - /* Done, but hold lock until commit */ - - relation_close(relation, NoLock); - } - - /* - * CommentDatabase -- - * - * This routine is used to add/drop any user-comments a user might - * have regarding the specified database. The routine will check - * security for owner permissions, and, if successful, will then - * attempt to find the oid of the database specified. Once found, - * a comment is added/dropped using the CreateSharedComments() routine. - */ - static void - CommentDatabase(List *qualname, char *comment) - { - char *database; - Oid oid; - - if (list_length(qualname) != 1) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("database name cannot be qualified"))); - database = strVal(linitial(qualname)); - - /* - * When loading a dump, we may see a COMMENT ON DATABASE for the old name - * of the database. Erroring out would prevent pg_restore from completing - * (which is really pg_restore's fault, but for now we will work around - * the problem here). Consensus is that the best fix is to treat wrong - * database name as a WARNING not an ERROR (thus, we tell get_database_oid - * to ignore the error so that we can handle it differently here). - */ - oid = get_database_oid(database, true); - if (!OidIsValid(oid)) - { - ereport(WARNING, - (errcode(ERRCODE_UNDEFINED_DATABASE), - errmsg("database \"%s\" does not exist", database))); - return; - } - - /* Check object security */ - if (!pg_database_ownercheck(oid, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - database); - - /* Call CreateSharedComments() to create/drop the comments */ - CreateSharedComments(oid, DatabaseRelationId, comment); } /* ! * CommentTablespace -- ! * ! * This routine is used to add/drop any user-comments a user might ! * have regarding a tablespace. The tablepace is specified by name ! * and, if found, and the user has appropriate permissions, a ! * comment will be added/dropped using the CreateSharedComments() routine. ! * */ static void ! CommentTablespace(List *qualname, char *comment) ! { ! char *tablespace; ! Oid oid; ! ! if (list_length(qualname) != 1) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("tablespace name cannot be qualified"))); ! tablespace = strVal(linitial(qualname)); ! ! oid = get_tablespace_oid(tablespace, false); ! ! /* Check object security */ ! if (!pg_tablespace_ownercheck(oid, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, tablespace); ! ! /* Call CreateSharedComments() to create/drop the comments */ ! CreateSharedComments(oid, TableSpaceRelationId, comment); ! } ! ! /* ! * CommentRole -- ! * ! * This routine is used to add/drop any user-comments a user might ! * have regarding a role. The role is specified by name ! * and, if found, and the user has appropriate permissions, a ! * comment will be added/dropped using the CreateSharedComments() routine. ! */ ! static void ! CommentRole(List *qualname, char *comment) ! { ! char *role; ! Oid oid; ! ! if (list_length(qualname) != 1) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("role name cannot be qualified"))); ! role = strVal(linitial(qualname)); ! ! oid = get_role_oid(role, false); ! ! /* Check object security */ ! if (!has_privs_of_role(GetUserId(), oid)) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("must be member of role \"%s\" to comment upon it", role))); ! ! /* Call CreateSharedComments() to create/drop the comments */ ! CreateSharedComments(oid, AuthIdRelationId, comment); ! } ! ! /* ! * CommentNamespace -- ! * ! * This routine is used to add/drop any user-comments a user might ! * have regarding the specified namespace. The routine will check ! * security for owner permissions, and, if successful, will then ! * attempt to find the oid of the namespace specified. Once found, ! * a comment is added/dropped using the CreateComments() routine. ! */ ! static void ! CommentNamespace(List *qualname, char *comment) ! { ! Oid oid; ! char *namespace; ! ! if (list_length(qualname) != 1) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("schema name cannot be qualified"))); ! namespace = strVal(linitial(qualname)); ! ! oid = get_namespace_oid(namespace, false); ! ! /* Check object security */ ! if (!pg_namespace_ownercheck(oid, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, ! namespace); ! ! /* Call CreateComments() to create/drop the comments */ ! CreateComments(oid, NamespaceRelationId, 0, comment); ! } ! ! /* ! * CommentRule -- ! * ! * This routine is used to add/drop any user-comments a user might ! * have regarding a specified RULE. The rule for commenting is determined by ! * both its name and the relation to which it refers. The arguments to this ! * function are the rule name and relation name (merged into a qualified ! * name), and the comment to add/drop. ! * ! * Before PG 7.3, rules had unique names across the whole database, and so ! * the syntax was just COMMENT ON RULE rulename, with no relation name. ! * For purposes of backwards compatibility, we support that as long as there ! * is only one rule by the specified name in the database. ! */ ! static void ! CommentRule(List *qualname, char *comment) ! { ! int nnames; ! List *relname; ! char *rulename; ! RangeVar *rel; ! Relation relation; ! Oid reloid; ! Oid ruleoid; ! ! /* Separate relname and trig name */ ! nnames = list_length(qualname); ! if (nnames == 1) ! { ! rulename = strVal(linitial(qualname)); ! ruleoid = get_rewrite_oid_without_relid(rulename, &reloid); ! ! /* Open the owning relation to ensure it won't go away meanwhile */ ! relation = heap_open(reloid, AccessShareLock); ! } ! else ! { ! /* New-style: rule and relname both provided */ ! Assert(nnames >= 2); ! relname = list_truncate(list_copy(qualname), nnames - 1); ! rulename = strVal(lfirst(list_tail(qualname))); ! ! /* Open the owning relation to ensure it won't go away meanwhile */ ! rel = makeRangeVarFromNameList(relname); ! relation = heap_openrv(rel, AccessShareLock); ! reloid = RelationGetRelid(relation); ! ! /* Find the rule's pg_rewrite tuple, get its OID */ ! ruleoid = get_rewrite_oid(reloid, rulename, false); ! } ! ! /* Check object security */ ! if (!pg_class_ownercheck(reloid, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, ! get_rel_name(reloid)); ! ! /* Call CreateComments() to create/drop the comments */ ! CreateComments(ruleoid, RewriteRelationId, 0, comment); ! ! heap_close(relation, NoLock); ! } ! ! /* ! * CommentType -- ! * ! * This routine is used to add/drop any user-comments a user might ! * have regarding a TYPE. The type is specified by name ! * and, if found, and the user has appropriate permissions, a ! * comment will be added/dropped using the CreateComments() routine. ! * The type's name and the comments are the parameters to this routine. ! */ ! static void ! CommentType(List *typename, char *comment) ! { ! TypeName *tname; ! Oid oid; ! ! /* XXX a bit of a crock; should accept TypeName in COMMENT syntax */ ! tname = makeTypeNameFromNameList(typename); ! ! /* Find the type's oid */ ! ! oid = typenameTypeId(NULL, tname, NULL); ! ! /* Check object security */ ! ! if (!pg_type_ownercheck(oid, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, ! format_type_be(oid)); ! ! /* Call CreateComments() to create/drop the comments */ ! CreateComments(oid, TypeRelationId, 0, comment); ! } ! ! /* ! * CommentAggregate -- ! * ! * This routine is used to allow a user to provide comments on an ! * aggregate function. The aggregate function is determined by both ! * its name and its argument type(s). ! */ ! static void ! CommentAggregate(List *aggregate, List *arguments, char *comment) ! { ! Oid oid; ! ! /* Look up function and make sure it's an aggregate */ ! oid = LookupAggNameTypeNames(aggregate, arguments, false); ! ! /* Next, validate the user's attempt to comment */ ! if (!pg_proc_ownercheck(oid, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, ! NameListToString(aggregate)); ! ! /* Call CreateComments() to create/drop the comments */ ! CreateComments(oid, ProcedureRelationId, 0, comment); ! } ! ! /* ! * CommentProc -- ! * ! * This routine is used to allow a user to provide comments on an ! * procedure (function). The procedure is determined by both ! * its name and its argument list. The argument list is expected to ! * be a series of parsed nodes pointed to by a List object. If the ! * comments string is empty, the associated comment is dropped. ! */ ! static void ! CommentProc(List *function, List *arguments, char *comment) ! { ! Oid oid; ! ! /* Look up the procedure */ ! ! oid = LookupFuncNameTypeNames(function, arguments, false); ! ! /* Now, validate the user's ability to comment on this function */ ! ! if (!pg_proc_ownercheck(oid, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, ! NameListToString(function)); ! ! /* Call CreateComments() to create/drop the comments */ ! CreateComments(oid, ProcedureRelationId, 0, comment); ! } ! ! /* ! * CommentOperator -- ! * ! * This routine is used to allow a user to provide comments on an ! * operator. The operator for commenting is determined by both ! * its name and its argument list which defines the left and right ! * hand types the operator will operate on. The argument list is ! * expected to be a couple of parse nodes pointed to be a List ! * object. ! */ ! static void ! CommentOperator(List *opername, List *arguments, char *comment) ! { ! TypeName *typenode1 = (TypeName *) linitial(arguments); ! TypeName *typenode2 = (TypeName *) lsecond(arguments); ! Oid oid; ! ! /* Look up the operator */ ! oid = LookupOperNameTypeNames(NULL, opername, ! typenode1, typenode2, ! false, -1); ! ! /* Check user's privilege to comment on this operator */ ! if (!pg_oper_ownercheck(oid, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, ! NameListToString(opername)); ! ! /* Call CreateComments() to create/drop the comments */ ! CreateComments(oid, OperatorRelationId, 0, comment); ! } ! ! /* ! * CommentTrigger -- ! * ! * This routine is used to allow a user to provide comments on a ! * trigger event. The trigger for commenting is determined by both ! * its name and the relation to which it refers. The arguments to this ! * function are the trigger name and relation name (merged into a qualified ! * name), and the comment to add/drop. ! */ ! static void ! CommentTrigger(List *qualname, char *comment) ! { ! int nnames; ! List *relname; ! char *trigname; ! RangeVar *rel; ! Relation relation; ! Oid oid; ! ! /* Separate relname and trig name */ ! nnames = list_length(qualname); ! if (nnames < 2) /* parser messed up */ ! elog(ERROR, "must specify relation and trigger"); ! relname = list_truncate(list_copy(qualname), nnames - 1); ! trigname = strVal(lfirst(list_tail(qualname))); ! ! /* Open the owning relation to ensure it won't go away meanwhile */ ! rel = makeRangeVarFromNameList(relname); ! relation = heap_openrv(rel, AccessShareLock); ! ! /* Check object security */ ! if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, ! RelationGetRelationName(relation)); ! ! oid = get_trigger_oid(RelationGetRelid(relation), trigname, false); ! ! /* Call CreateComments() to create/drop the comments */ ! CreateComments(oid, TriggerRelationId, 0, comment); ! ! /* Done, but hold lock on relation */ ! heap_close(relation, NoLock); ! } ! ! ! /* ! * CommentConstraint -- ! * ! * Enable commenting on constraints held within the pg_constraint ! * table. A qualified name is required as constraint names are ! * unique per relation. ! */ ! static void ! CommentConstraint(List *qualname, char *comment) ! { ! int nnames; ! List *relName; ! char *conName; ! RangeVar *rel; ! Relation relation; ! Oid conOid; ! ! /* Separate relname and constraint name */ ! nnames = list_length(qualname); ! if (nnames < 2) /* parser messed up */ ! elog(ERROR, "must specify relation and constraint"); ! relName = list_truncate(list_copy(qualname), nnames - 1); ! conName = strVal(lfirst(list_tail(qualname))); ! ! /* Open the owning relation to ensure it won't go away meanwhile */ ! rel = makeRangeVarFromNameList(relName); ! relation = heap_openrv(rel, AccessShareLock); ! ! /* Check object security */ ! ! if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, ! RelationGetRelationName(relation)); ! ! conOid = get_constraint_oid(RelationGetRelid(relation), conName, false); ! ! /* Call CreateComments() to create/drop the comments */ ! CreateComments(conOid, ConstraintRelationId, 0, comment); ! ! /* Done, but hold lock on relation */ ! heap_close(relation, NoLock); ! } ! ! /* ! * CommentConversion -- ! * ! * This routine is used to add/drop any user-comments a user might ! * have regarding a CONVERSION. The conversion is specified by name ! * and, if found, and the user has appropriate permissions, a ! * comment will be added/dropped using the CreateComments() routine. ! * The conversion's name and the comment are the parameters to this routine. ! */ ! static void ! CommentConversion(List *qualname, char *comment) ! { ! Oid conversionOid; ! ! conversionOid = get_conversion_oid(qualname, false); ! ! /* Check object security */ ! if (!pg_conversion_ownercheck(conversionOid, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, ! NameListToString(qualname)); ! ! /* Call CreateComments() to create/drop the comments */ ! CreateComments(conversionOid, ConversionRelationId, 0, comment); ! } ! ! /* ! * CommentLanguage -- ! * ! * This routine is used to add/drop any user-comments a user might ! * have regarding a LANGUAGE. The language is specified by name ! * and, if found, and the user has appropriate permissions, a ! * comment will be added/dropped using the CreateComments() routine. ! * The language's name and the comment are the parameters to this routine. ! */ ! static void ! CommentLanguage(List *qualname, char *comment) ! { ! Oid oid; ! char *language; ! ! if (list_length(qualname) != 1) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("language name cannot be qualified"))); ! language = strVal(linitial(qualname)); ! ! oid = get_language_oid(language, false); ! ! /* Check object security */ ! if (!superuser()) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("must be superuser to comment on procedural language"))); ! ! /* Call CreateComments() to create/drop the comments */ ! CreateComments(oid, LanguageRelationId, 0, comment); ! } ! ! /* ! * CommentOpClass -- ! * ! * This routine is used to allow a user to provide comments on an ! * operator class. The operator class for commenting is determined by both ! * its name and its argument list which defines the index method ! * the operator class is used for. The argument list is expected to contain ! * a single name (represented as a string Value node). ! */ ! static void ! CommentOpClass(List *qualname, List *arguments, char *comment) ! { ! char *amname; ! Oid amID; ! Oid opcID; ! ! Assert(list_length(arguments) == 1); ! amname = strVal(linitial(arguments)); ! ! /* ! * Get the operator class OID. ! */ ! amID = get_am_oid(amname, false); ! opcID = get_opclass_oid(amID, qualname, false); ! ! /* Permission check: must own opclass */ ! if (!pg_opclass_ownercheck(opcID, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, ! NameListToString(qualname)); ! ! /* Call CreateComments() to create/drop the comments */ ! CreateComments(opcID, OperatorClassRelationId, 0, comment); ! } ! ! /* ! * CommentOpFamily -- ! * ! * This routine is used to allow a user to provide comments on an ! * operator family. The operator family for commenting is determined by both ! * its name and its argument list which defines the index method ! * the operator family is used for. The argument list is expected to contain ! * a single name (represented as a string Value node). ! */ ! static void ! CommentOpFamily(List *qualname, List *arguments, char *comment) ! { ! char *amname; ! Oid amID; ! Oid opfID; ! ! Assert(list_length(arguments) == 1); ! amname = strVal(linitial(arguments)); ! ! /* Get the opfamily OID. */ ! amID = get_am_oid(amname, false); ! opfID = get_opfamily_oid(amID, qualname, false); ! ! /* Permission check: must own opfamily */ ! if (!pg_opfamily_ownercheck(opfID, GetUserId())) ! aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, ! NameListToString(qualname)); ! ! /* Call CreateComments() to create/drop the comments */ ! CreateComments(opfID, OperatorFamilyRelationId, 0, comment); ! } ! ! /* ! * CommentLargeObject -- ! * ! * This routine is used to add/drop any user-comments a user might ! * have regarding a LARGE OBJECT. The large object is specified by OID ! * and, if found, and the user has appropriate permissions, a ! * comment will be added/dropped using the CreateComments() routine. ! * The large object's OID and the comment are the parameters to this routine. ! */ ! static void ! CommentLargeObject(List *qualname, char *comment) ! { ! Oid loid; ! ! Assert(list_length(qualname) == 1); ! loid = oidparse((Node *) linitial(qualname)); ! ! /* check that the large object exists */ ! if (!LargeObjectExists(loid)) ! ereport(ERROR, ! (errcode(ERRCODE_UNDEFINED_OBJECT), ! errmsg("large object %u does not exist", loid))); ! ! /* Permission checks */ ! if (!lo_compat_privileges && ! !pg_largeobject_ownercheck(loid, GetUserId())) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("must be owner of large object %u", loid))); ! ! /* ! * Call CreateComments() to create/drop the comments ! * ! * See the comment in the inv_create() which describes the reason why ! * LargeObjectRelationId is used instead of LargeObjectMetadataRelationId. ! */ ! CreateComments(loid, LargeObjectRelationId, 0, comment); ! } ! ! /* ! * CommentCast -- ! * ! * This routine is used to add/drop any user-comments a user might ! * have regarding a CAST. The cast is specified by source and destination types ! * and, if found, and the user has appropriate permissions, a ! * comment will be added/dropped using the CreateComments() routine. ! * The cast's source type is passed as the "name", the destination type ! * as the "arguments". ! */ ! static void ! CommentCast(List *qualname, List *arguments, char *comment) { TypeName *sourcetype; TypeName *targettype; Oid sourcetypeid; Oid targettypeid; - Oid castOid; Assert(list_length(qualname) == 1); sourcetype = (TypeName *) linitial(qualname); --- 631,648 ---- (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table, view, or composite type", RelationGetRelationName(relation)))); } /* ! * Check whether the user is allowed to comment on the specified cast. */ static void ! CheckCastComment(List *qualname, List *arguments) { TypeName *sourcetype; TypeName *targettype; Oid sourcetypeid; Oid targettypeid; Assert(list_length(qualname) == 1); sourcetype = (TypeName *) linitial(qualname); *************** *** 1263,1271 **** CommentCast(List *qualname, List *arguments, char *comment) sourcetypeid = typenameTypeId(NULL, sourcetype, NULL); targettypeid = typenameTypeId(NULL, targettype, NULL); - /* Get the OID of the cast */ - castOid = get_cast_oid(sourcetypeid, targettypeid, false); - /* Permission check */ if (!pg_type_ownercheck(sourcetypeid, GetUserId()) && !pg_type_ownercheck(targettypeid, GetUserId())) --- 654,659 ---- *************** *** 1274,1338 **** CommentCast(List *qualname, List *arguments, char *comment) errmsg("must be owner of type %s or type %s", format_type_be(sourcetypeid), format_type_be(targettypeid)))); - - /* Call CreateComments() to create/drop the comments */ - CreateComments(castOid, CastRelationId, 0, comment); - } - - static void - CommentTSParser(List *qualname, char *comment) - { - Oid prsId; - - prsId = get_ts_parser_oid(qualname, false); - - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to comment on text search parser"))); - - CreateComments(prsId, TSParserRelationId, 0, comment); - } - - static void - CommentTSDictionary(List *qualname, char *comment) - { - Oid dictId; - - dictId = get_ts_dict_oid(qualname, false); - - if (!pg_ts_dict_ownercheck(dictId, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, - NameListToString(qualname)); - - CreateComments(dictId, TSDictionaryRelationId, 0, comment); - } - - static void - CommentTSTemplate(List *qualname, char *comment) - { - Oid tmplId; - - tmplId = get_ts_template_oid(qualname, false); - - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to comment on text search template"))); - - CreateComments(tmplId, TSTemplateRelationId, 0, comment); - } - - static void - CommentTSConfiguration(List *qualname, char *comment) - { - Oid cfgId; - - cfgId = get_ts_config_oid(qualname, false); - - if (!pg_ts_config_ownercheck(cfgId, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, - NameListToString(qualname)); - - CreateComments(cfgId, TSConfigRelationId, 0, comment); } --- 662,665 ---- *** a/src/include/catalog/dependency.h --- b/src/include/catalog/dependency.h *************** *** 15,20 **** --- 15,21 ---- #define DEPENDENCY_H #include "nodes/parsenodes.h" /* for DropBehavior */ + #include "catalog/objectaddress.h" /* *************** *** 100,116 **** typedef enum SharedDependencyType SHARED_DEPENDENCY_INVALID = 0 } SharedDependencyType; - - /* - * The two objects related by a dependency are identified by ObjectAddresses. - */ - typedef struct ObjectAddress - { - Oid classId; /* Class Id from pg_class */ - Oid objectId; /* OID of the object */ - int32 objectSubId; /* Subitem within object (eg column), or 0 */ - } ObjectAddress; - /* expansible list of ObjectAddresses (private in dependency.c) */ typedef struct ObjectAddresses ObjectAddresses; --- 101,106 ---- *** /dev/null --- b/src/include/catalog/objectaddress.h *************** *** 0 **** --- 1,33 ---- + /*------------------------------------------------------------------------- + * + * objectaddress.h + * functions for working with object addresses + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ + #ifndef OBJECTADDRESS_H + #define OBJECTADDRESS_H + + #include "nodes/parsenodes.h" + #include "storage/lock.h" + #include "utils/rel.h" + + /* + * An ObjectAddress represents a database object of any type. + */ + typedef struct ObjectAddress + { + Oid classId; /* Class Id from pg_class */ + Oid objectId; /* OID of the object */ + int32 objectSubId; /* Subitem within object (eg column), or 0 */ + } ObjectAddress; + + ObjectAddress get_object_address(ObjectType objtype, List *objname, + List *objargs, Relation *relp, LOCKMODE lockmode); + + #endif /* PARSE_OBJECT_H */