]> granicus.if.org Git - postgresql/commitdiff
Rewrite comment code for better modularity, and add necessary locking.
authorRobert Haas <rhaas@postgresql.org>
Fri, 27 Aug 2010 11:47:41 +0000 (11:47 +0000)
committerRobert Haas <rhaas@postgresql.org>
Fri, 27 Aug 2010 11:47:41 +0000 (11:47 +0000)
Review by Alvaro Herrera, KaiGai Kohei, and Tom Lane.

src/backend/catalog/Makefile
src/backend/catalog/objectaddress.c [new file with mode: 0644]
src/backend/commands/comment.c
src/include/catalog/dependency.h
src/include/catalog/objectaddress.h [new file with mode: 0644]

index a97003943ed5600b62f55af5921633cbdb839e58..0a1546bfa7b19725b69322d0281839eecec6e293 100644 (file)
@@ -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
 
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
new file mode 100644 (file)
index 0000000..9eec92f
--- /dev/null
@@ -0,0 +1,654 @@
+/*-------------------------------------------------------------------------
+ *
+ * objectaddress.c
+ *       functions for working with ObjectAddresses
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       $PostgreSQL: 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;   
+}
index 6653495176cfedaaff3e0c850f7402ad03e39070..4a801858b6b475a75daeb1e3404e376bfb15b6af 100644 (file)
@@ -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 $
  *
  *-------------------------------------------------------------------------
  */
 #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.
- *
+ * 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);
@@ -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);
 }
index d8a0e0aa639e60b4901cc3cf532c81981704f196..93c2073d26cbfde48c4e3da4f14d7bffef9da324 100644 (file)
@@ -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;
 
diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h
new file mode 100644 (file)
index 0000000..780d915
--- /dev/null
@@ -0,0 +1,33 @@
+/*-------------------------------------------------------------------------
+ *
+ * objectaddress.h
+ *       functions for working with object addresses
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: 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 */