*** 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/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_object.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,225 ----
  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.
+ 	 */
+ 	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:
--- 561,577 ----
  }
  
  /*
!  * 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));
--- 603,617 ----
  								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);
--- 630,647 ----
  				(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()))
--- 653,658 ----
***************
*** 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);
  }
--- 661,664 ----
*** 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_object.o parse_oper.o parse_param.o \
! 	  parse_relation.o parse_target.o parse_type.o parse_utilcmd.o scansup.o
  
  FLEXFLAGS = -CF
  
*** /dev/null
--- b/src/backend/parser/parse_object.c
***************
*** 0 ****
--- 1,644 ----
+ /*-------------------------------------------------------------------------
+  *
+  * parse_object.c
+  *	  transform parser representations of arbitrary objects into
+  *    object addresses
+  *
+  * 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/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_object.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);
+ 
+ /*
+  * 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;	
+ }
+ 
+ /*
+  * 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.
+  *
+  * 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;
+ }
*** /dev/null
--- b/src/include/parser/parse_object.h
***************
*** 0 ****
--- 1,24 ----
+ /*-------------------------------------------------------------------------
+  *
+  * parse_object.h
+  *	  transform parser representations of arbitrary objects into object
+  *    addresses
+  *
+  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * $PostgreSQL$
+  *
+  *-------------------------------------------------------------------------
+  */
+ #ifndef PARSE_OBJECT_H
+ #define PARSE_OBJECT_H
+ 
+ #include "catalog/dependency.h"
+ #include "nodes/pg_list.h"
+ #include "storage/lock.h"
+ 
+ ObjectAddress get_object_address(ObjectType objtype, List *objname,
+ 				   List *objargs, Relation *relp, LOCKMODE lockmode);
+ 
+ #endif   /* PARSE_OBJECT_H */
