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