--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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: pgsql/src/backend/catalog/objectaddress.c,v 1.1 2010/08/27 11:47:41 rhaas Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#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;
+}
* Copyright (c) 1996-2010, PostgreSQL Global Development Group
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.117 2010/08/05 15:25:35 rhaas Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.118 2010/08/27 11:47:41 rhaas Exp $
*
*-------------------------------------------------------------------------
*/
#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/objectaddress.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.
+ * 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 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);
+static void CheckRelationComment(int objtype, Relation relation);
+static void CheckAttributeComment(Relation relation);
+static void CheckCastComment(List *qualname, List *arguments);
/*
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:
- CommentRelation(stmt->objtype, stmt->objname, stmt->comment);
+ CheckRelationComment(stmt->objtype, relation);
break;
case OBJECT_COLUMN:
- CommentAttribute(stmt->objname, stmt->comment);
+ CheckAttributeComment(relation);
break;
case OBJECT_DATABASE:
- CommentDatabase(stmt->objname, stmt->comment);
- break;
- case OBJECT_RULE:
- CommentRule(stmt->objname, stmt->comment);
+ if (!pg_database_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
+ strVal(linitial(stmt->objname)));
break;
case OBJECT_TYPE:
- CommentType(stmt->objname, stmt->comment);
+ if (!pg_type_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ format_type_be(address.objectId));
break;
case OBJECT_AGGREGATE:
- CommentAggregate(stmt->objname, stmt->objargs, stmt->comment);
- break;
case OBJECT_FUNCTION:
- CommentProc(stmt->objname, stmt->objargs, stmt->comment);
+ if (!pg_proc_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+ NameListToString(stmt->objname));
break;
case OBJECT_OPERATOR:
- CommentOperator(stmt->objname, stmt->objargs, stmt->comment);
+ 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:
- CommentTrigger(stmt->objname, stmt->comment);
+ case OBJECT_CONSTRAINT:
+ if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ RelationGetRelationName(relation));
break;
case OBJECT_SCHEMA:
- CommentNamespace(stmt->objname, stmt->comment);
- break;
- case OBJECT_CONSTRAINT:
- CommentConstraint(stmt->objname, stmt->comment);
+ if (!pg_namespace_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
+ strVal(linitial(stmt->objname)));
break;
case OBJECT_CONVERSION:
- CommentConversion(stmt->objname, stmt->comment);
+ if (!pg_conversion_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
+ NameListToString(stmt->objname));
break;
case OBJECT_LANGUAGE:
- CommentLanguage(stmt->objname, stmt->comment);
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to comment on procedural language")));
break;
case OBJECT_OPCLASS:
- CommentOpClass(stmt->objname, stmt->objargs, stmt->comment);
+ if (!pg_opclass_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
+ NameListToString(stmt->objname));
break;
case OBJECT_OPFAMILY:
- CommentOpFamily(stmt->objname, stmt->objargs, stmt->comment);
+ if (!pg_opfamily_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
+ NameListToString(stmt->objname));
break;
case OBJECT_LARGEOBJECT:
- CommentLargeObject(stmt->objname, stmt->comment);
+ 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:
- CommentCast(stmt->objname, stmt->objargs, stmt->comment);
+ CheckCastComment(stmt->objname, stmt->objargs);
break;
case OBJECT_TABLESPACE:
- CommentTablespace(stmt->objname, stmt->comment);
+ if (!pg_tablespace_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE,
+ strVal(linitial(stmt->objname)));
break;
case OBJECT_ROLE:
- CommentRole(stmt->objname, stmt->comment);
+ 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:
- CommentTSParser(stmt->objname, stmt->comment);
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to comment on text search parser")));
break;
case OBJECT_TSDICTIONARY:
- CommentTSDictionary(stmt->objname, stmt->comment);
+ if (!pg_ts_dict_ownercheck(address.objectId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
+ NameListToString(stmt->objname));
break;
case OBJECT_TSTEMPLATE:
- CommentTSTemplate(stmt->objname, stmt->comment);
+ if (!superuser())
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to comment on text search template")));
break;
case OBJECT_TSCONFIGURATION:
- CommentTSConfiguration(stmt->objname, stmt->comment);
+ 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);
}
/*
}
/*
- * 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.
+ * Check whether the user is allowed to comment on this relation.
*/
static void
-CommentRelation(int objtype, List *relname, char *comment)
+CheckRelationComment(int objtype, Relation relation)
{
- 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:
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
+ * Check whether the user is allowed to comment on an attribute of the
+ * specified relation.
*/
static void
-CommentAttribute(List *qualname, char *comment)
+CheckAttributeComment(Relation relation)
{
- 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));
(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.
- *
+ * Check whether the user is allowed to comment on the specified cast.
*/
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)
+CheckCastComment(List *qualname, List *arguments)
{
TypeName *sourcetype;
TypeName *targettype;
Oid sourcetypeid;
Oid targettypeid;
- Oid castOid;
Assert(list_length(qualname) == 1);
sourcetype = (TypeName *) linitial(qualname);
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()))
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);
}