Commit c10575ff authored by Robert Haas's avatar Robert Haas

Rewrite comment code for better modularity, and add necessary locking.

Review by Alvaro Herrera, KaiGai Kohei, and Tom Lane.
parent 73b3bd55
......@@ -2,7 +2,7 @@
#
# Makefile for backend/catalog
#
# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.78 2010/05/13 11:49:48 petere Exp $
# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.79 2010/08/27 11:47:41 rhaas Exp $
#
#-------------------------------------------------------------------------
......@@ -11,9 +11,10 @@ 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
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
......
/*-------------------------------------------------------------------------
*
* 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;
}
......@@ -7,7 +7,7 @@
* 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 $
*
*-------------------------------------------------------------------------
*/
......@@ -17,83 +17,30 @@
#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);
/*
......@@ -105,84 +52,175 @@ static void CommentTSConfiguration(List *qualname, char *comment);
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);
}
/*
......@@ -524,36 +562,17 @@ 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.
* 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:
......@@ -585,48 +604,15 @@ 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
* 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));
......@@ -645,613 +631,18 @@ 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).
* Check whether the user is allowed to comment on the specified cast.
*/
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);
......@@ -1263,9 +654,6 @@ 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()))
......@@ -1274,65 +662,4 @@ 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);
}
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.45 2010/04/05 01:09:53 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.46 2010/08/27 11:47:41 rhaas Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -15,6 +15,7 @@
#define DEPENDENCY_H
#include "nodes/parsenodes.h" /* for DropBehavior */
#include "catalog/objectaddress.h"
/*
......@@ -100,17 +101,6 @@ 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;
......
/*-------------------------------------------------------------------------
*
* 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: pgsql/src/include/catalog/objectaddress.h,v 1.1 2010/08/27 11:47:41 rhaas Exp $
*
*-------------------------------------------------------------------------
*/
#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 */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment