diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index bd0a8668d0fe97a0f126672736ea2c64df6f7d07..eb934f6d3d94b98b6aec1697b156d99b9567e9a4 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.104 1999/10/15 01:49:39 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.105 1999/10/26 03:12:33 momjian Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -45,6 +45,7 @@
 #include "catalog/pg_proc.h"
 #include "catalog/pg_relcheck.h"
 #include "catalog/pg_type.h"
+#include "commands/comment.h"
 #include "commands/trigger.h"
 #include "optimizer/clauses.h"
 #include "optimizer/planmain.h"
@@ -1276,146 +1277,18 @@ DeleteAttributeTuples(Relation rel)
 								   Int16GetDatum(attnum),
 														   0, 0)))
 		{
-		      DeleteComments(tup->t_data->t_oid);
-		      heap_delete(pg_attribute_desc, &tup->t_self, NULL);
-		      pfree(tup);
-		}
-	}
-
-	heap_close(pg_attribute_desc, RowExclusiveLock);
-}
-
-/* ----------------------------------------------------------
- *  CreateComments
- * 
- *  This routine is handed the oid and the command associated
- *  with that id and will insert, update, or delete (if the 
- *  comment is an empty string or a NULL pointer) the associated
- *  comment from the system cataloge, pg_description. 
- *
- * ----------------------------------------------------------
- */
-
-void
-CreateComments(Oid oid, char *comment)
-{
+		  
+		  /*** Delete any comments associated with this attribute ***/
 
-  Relation description;
-  TupleDesc tupDesc;
-  HeapScanDesc scan;
-  ScanKeyData entry;
-  HeapTuple desctuple, searchtuple;
-  Datum values[Natts_pg_description];
-  char nulls[Natts_pg_description];
-  char replaces[Natts_pg_description];
-  bool modified = false;
-  int i;
-
-  /*** Open pg_description, form a new tuple, if necessary ***/
-
-  description = heap_openr(DescriptionRelationName, RowExclusiveLock);
-  tupDesc = description->rd_att;
-  if ((comment != NULL) && (strlen(comment) > 0)) {
-    for (i = 0; i < Natts_pg_description; i++) {
-      nulls[i] = ' ';
-      replaces[i] = 'r';
-      values[i] = (Datum) NULL;
-    }
-    i = 0;
-    values[i++] = ObjectIdGetDatum(oid);
-    values[i++] = (Datum) fmgr(F_TEXTIN, comment);
-  }
-
-  /*** Now, open pg_description and attempt to find the old tuple ***/
-  
-  ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_description_objoid, F_OIDEQ,
-			 ObjectIdGetDatum(oid));
-  scan = heap_beginscan(description, false, SnapshotNow, 1, &entry);
-  searchtuple = heap_getnext(scan, 0);
-
-  /*** If a previous tuple exists, either delete it or prepare a replacement ***/
-
-  if (HeapTupleIsValid(searchtuple)) {
-        
-    /*** If the comment is blank, call heap_delete, else heap_replace ***/
-
-    if ((comment == NULL) || (strlen(comment) == 0)) {
-      heap_delete(description, &searchtuple->t_self, NULL);
-    } else {
-      desctuple = heap_modifytuple(searchtuple, description, values, nulls, replaces);      
-      setheapoverride(true);
-      heap_replace(description, &searchtuple->t_self, desctuple, NULL);
-      setheapoverride(false);
-      modified = TRUE;
-    }
-
-  } else {    
-    desctuple = heap_formtuple(tupDesc, values, nulls);
-    heap_insert(description, desctuple);
-    modified = TRUE;
-  }
-
-  /*** Complete the scan, update indices, if necessary ***/
-
-  heap_endscan(scan);
-  
-  if (modified) {
-    if (RelationGetForm(description)->relhasindex) {
-      Relation idescs[Num_pg_description_indices];
-      
-      CatalogOpenIndices(Num_pg_description_indices, Name_pg_description_indices, idescs);
-      CatalogIndexInsert(idescs, Num_pg_description_indices, description, desctuple);
-      CatalogCloseIndices(Num_pg_description_indices, idescs);
-    }
-    pfree(desctuple);
-
-  }
-
-  heap_close(description, RowExclusiveLock);
+		  DeleteComments(tup->t_data->t_oid);
 
-}
-    
-/* --------------------------------
- *  DeleteComments
- *
- *  This routine is used to purge any comments 
- *  associated with the Oid handed to this routine,
- *  regardless of the actual object type. It is
- *  called, for example, when a relation is destroyed.
- * --------------------------------
- */
+		  heap_delete(pg_attribute_desc, &tup->t_self, NULL);
+		  pfree(tup);
 
-void 
-DeleteComments(Oid oid) 
-{
+		}
+	}
 
-  Relation description;
-  TupleDesc tupDesc;
-  ScanKeyData entry;
-  HeapScanDesc scan;
-  HeapTuple searchtuple;
-
-  description = heap_openr(DescriptionRelationName, RowExclusiveLock);
-  tupDesc = description->rd_att;
-
-  /*** Now, open pg_description and attempt to find the old tuple ***/
-  
-  ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_description_objoid, F_OIDEQ,
-			 ObjectIdGetDatum(oid));
-  scan = heap_beginscan(description, false, SnapshotNow, 1, &entry);
-  searchtuple = heap_getnext(scan, 0);
-
-  /*** If a previous tuple exists, delete it ***/
-
-  if (HeapTupleIsValid(searchtuple)) {    
-    heap_delete(description, &searchtuple->t_self, NULL);
-  } 
-  
-  /*** Complete the scan, update indices, if necessary ***/
-
-  heap_endscan(scan);
-  heap_close(description, RowExclusiveLock);
-  
+	heap_close(pg_attribute_desc, RowExclusiveLock);
 }
 
 /* --------------------------------
@@ -1529,6 +1402,7 @@ DeleteTypeTuple(Relation rel)
 	 *	we release the read lock on pg_type.  -mer 13 Aug 1991
 	 * ----------------
 	 */
+	
 	heap_delete(pg_type_desc, &tup->t_self, NULL);
 
 	heap_endscan(pg_type_scan);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index f8c4dac95e77278290f56b13ecf300cc03dcf43b..9e7850f8f637a9254a896815b7a27dcbccc1ffef 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.91 1999/09/24 00:24:11 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.92 1999/10/26 03:12:33 momjian Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -32,6 +32,7 @@
 #include "catalog/pg_index.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
+#include "commands/comment.h"
 #include "executor/executor.h"
 #include "miscadmin.h"
 #include "optimizer/clauses.h"
@@ -1127,6 +1128,13 @@ index_destroy(Oid indexId)
 	if (IsTransactionBlock() && ! userindexRelation->rd_myxactonly)
 		elog(NOTICE, "Caution: DROP INDEX cannot be rolled back, so don't abort now");
 
+	/* ----------------
+	 * fix DESCRIPTION relation
+	 * ----------------
+	 */
+
+	DeleteComments(indexId);
+	
 	/* ----------------
 	 * fix RELATION relation
 	 * ----------------
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 3b4d3a8d3179fae6c14ce581dc0893460f49b465..fa1c4495e59d974ade045332d391c03946c3f49f 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -4,7 +4,7 @@
 #    Makefile for commands
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.18 1999/02/27 21:42:33 tgl Exp $
+#    $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.19 1999/10/26 03:12:34 momjian Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -17,7 +17,7 @@ ifdef MULTIBYTE
 CFLAGS+= $(MBFLAGS)
 endif
 
-OBJS = async.o creatinh.o command.o copy.o indexcmds.o define.o \
+OBJS = async.o creatinh.o command.o comment.o copy.o indexcmds.o define.o \
        remove.o rename.o vacuum.o view.o cluster.o \
        explain.o sequence.o trigger.o user.o proclang.o \
        dbcommands.o variable.o
@@ -27,6 +27,12 @@ all: SUBSYS.o
 SUBSYS.o: $(OBJS)
 	$(LD) -r -o SUBSYS.o $(OBJS)
 
+# The following declares a hard-coded dependency on parse.h since, 
+# if compiled without make dep, comment.c would get compiled before 
+# the parser. 
+
+comment.o: ../parse.h
+
 depend dep:
 	$(CC) -MM $(CFLAGS) *.c >depend
 
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
new file mode 100644
index 0000000000000000000000000000000000000000..c2dacaa8483107591e770844f3f24b287b5bbae2
--- /dev/null
+++ b/src/backend/commands/comment.c
@@ -0,0 +1,796 @@
+/*-------------------------------------------------------------------------
+ *
+ * comment.c
+ *	  
+ * PostgreSQL object comments utility code.
+ *
+ * Copyright (c) 1999, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "catalog/catname.h"
+#include "catalog/indexing.h"
+#include "catalog/heap.h"
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_description.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_rewrite.h"
+#include "catalog/pg_shadow.h"
+#include "catalog/pg_trigger.h"
+#include "catalog/pg_type.h"
+#include "commands/comment.h"
+#include "miscadmin.h"
+#include "rewrite/rewriteRemove.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/syscache.h"
+
+#include "../backend/parser/parse.h"
+
+/*------------------------------------------------------------------
+ * Static Function Prototypes --
+ * 
+ * The following protoypes 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, char *relation, char *comment);
+static void CommentAttribute(char *relation, char *attrib, char *comment);
+static void CommentDatabase(char *database, char *comment);
+static void CommentRewrite(char *rule, char *comment);
+static void CommentType(char *type, char *comment);
+static void CommentAggregate(char *aggregate, char *aggtype, char *comment);
+static void CommentProc(char *function, List *arguments, char *comment);
+static void CommentOperator(char *opname, List *arguments, char *comment);
+static void CommentTrigger(char *trigger, char *relation, char *comments);
+
+/*------------------------------------------------------------------
+ * CommentObject --
+ *
+ * This routine is used to add the associated comment into 
+ * pg_description for the object specified by the paramters handed 
+ * to this routine. If the routine cannot determine an Oid to 
+ * associated with the parameters handed to this routine, an 
+ * error is thrown. Otherwise the comment is added to pg_description
+ * by calling the CreateComments() routine. If the comments were
+ * empty, CreateComments() will drop any comments associated with
+ * the object.
+ *------------------------------------------------------------------
+*/
+
+void CommentObject(int objtype, char *objname, char *objproperty, 
+		   List *objlist, char *comment) {
+  
+  switch (objtype) {
+  case (INDEX):
+  case (SEQUENCE):
+  case (TABLE):
+  case (VIEW):
+    CommentRelation(objtype, objname, comment);
+    break;
+  case (COLUMN):
+    CommentAttribute(objname, objproperty, comment);
+    break;
+  case (DATABASE):
+    CommentDatabase(objname, comment);
+    break;
+  case (RULE):
+    CommentRewrite(objname, comment);
+    break;
+  case (TYPE_P):
+    CommentType(objname, comment);
+    break;
+  case (AGGREGATE):
+    CommentAggregate(objname, objproperty, comment);
+    break;
+  case (FUNCTION):
+    CommentProc(objname, objlist, comment);
+    break;
+  case (OPERATOR):
+    CommentOperator(objname, objlist, comment);
+    break;
+  case (TRIGGER):
+    CommentTrigger(objname, objproperty, comment);
+    break;
+  default:
+    elog(ERROR, "An attempt was made to comment on a unkown type: %i",
+	 objtype);    
+  }
+
+}
+
+/*------------------------------------------------------------------
+ * CreateComments --
+ * 
+ * This routine is handed the oid and the command associated
+ * with that id and will insert, update, or delete (if the 
+ * comment is an empty string or a NULL pointer) the associated
+ * comment from the system cataloge, pg_description. 
+ *
+ *------------------------------------------------------------------
+ */
+
+void CreateComments(Oid oid, char *comment) {
+
+  Relation description;
+  TupleDesc tupDesc;
+  HeapScanDesc scan;
+  ScanKeyData entry;
+  HeapTuple desctuple = NULL, searchtuple;
+  Datum values[Natts_pg_description];
+  char nulls[Natts_pg_description];
+  char replaces[Natts_pg_description];
+  bool modified = false;
+  int i;
+
+  /*** Open pg_description, form a new tuple, if necessary ***/
+
+  description = heap_openr(DescriptionRelationName, RowExclusiveLock);
+  tupDesc = description->rd_att;
+  if ((comment != NULL) && (strlen(comment) > 0)) {
+    for (i = 0; i < Natts_pg_description; i++) {
+      nulls[i] = ' ';
+      replaces[i] = 'r';
+      values[i] = (Datum) NULL;
+    }
+    i = 0;
+    values[i++] = ObjectIdGetDatum(oid);
+    values[i++] = (Datum) fmgr(F_TEXTIN, comment);
+  }
+
+  /*** Now, open pg_description and attempt to find the old tuple ***/
+  
+  ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_description_objoid, F_OIDEQ,
+			 ObjectIdGetDatum(oid));
+  scan = heap_beginscan(description, false, SnapshotNow, 1, &entry);
+  searchtuple = heap_getnext(scan, 0);
+
+  /*** If a previous tuple exists, either delete or prep replacement ***/
+
+  if (HeapTupleIsValid(searchtuple)) {
+        
+    /*** If the comment is blank, call heap_delete, else heap_replace ***/
+
+    if ((comment == NULL) || (strlen(comment) == 0)) {
+      heap_delete(description, &searchtuple->t_self, NULL);
+    } else {
+      desctuple = heap_modifytuple(searchtuple, description, values, 
+				   nulls, replaces);      
+      setheapoverride(true);
+      heap_replace(description, &searchtuple->t_self, desctuple, NULL);
+      setheapoverride(false);
+      modified = TRUE;
+    }
+
+  } else {    
+    desctuple = heap_formtuple(tupDesc, values, nulls);
+    heap_insert(description, desctuple);
+    modified = TRUE;
+  }
+
+  /*** Complete the scan, update indices, if necessary ***/
+
+  heap_endscan(scan);
+  
+  if (modified) {
+    if (RelationGetForm(description)->relhasindex) {
+      Relation idescs[Num_pg_description_indices];
+      
+      CatalogOpenIndices(Num_pg_description_indices, 
+			 Name_pg_description_indices, idescs);
+      CatalogIndexInsert(idescs, Num_pg_description_indices, description, 
+			 desctuple);
+      CatalogCloseIndices(Num_pg_description_indices, idescs);
+    }
+    pfree(desctuple);
+
+  }
+
+  heap_close(description, RowExclusiveLock);
+
+}
+
+/*------------------------------------------------------------------    
+ * DeleteComments --
+ *
+ * This routine is used to purge any comments 
+ * associated with the Oid handed to this routine,
+ * regardless of the actual object type. It is
+ * called, for example, when a relation is destroyed.
+ *------------------------------------------------------------------
+ */
+
+void DeleteComments(Oid oid) {
+
+  Relation description;
+  TupleDesc tupDesc;
+  ScanKeyData entry;
+  HeapScanDesc scan;
+  HeapTuple searchtuple;
+
+  description = heap_openr(DescriptionRelationName, RowExclusiveLock);
+  tupDesc = description->rd_att;
+
+  /*** Now, open pg_description and attempt to find the old tuple ***/
+  
+  ScanKeyEntryInitialize(&entry, 0x0, Anum_pg_description_objoid, F_OIDEQ,
+			 ObjectIdGetDatum(oid));
+  scan = heap_beginscan(description, false, SnapshotNow, 1, &entry);
+  searchtuple = heap_getnext(scan, 0);
+
+  /*** If a previous tuple exists, delete it ***/
+
+  if (HeapTupleIsValid(searchtuple)) {    
+    heap_delete(description, &searchtuple->t_self, NULL);
+  } 
+  
+  /*** Complete the scan, update indices, if necessary ***/
+
+  heap_endscan(scan);
+  heap_close(description, RowExclusiveLock);
+  
+}
+    
+/*------------------------------------------------------------------
+ * 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.
+ *------------------------------------------------------------------
+*/
+
+void CommentRelation(int reltype, char *relname, char *comment) {
+
+  HeapTuple reltuple;
+  Oid oid;
+  char relkind;
+
+  /*** First, check object security ***/
+
+  #ifndef NO_SECURITY
+  if (!pg_ownercheck(GetPgUserName(), relname, RELNAME)) {
+    elog(ERROR, "you are not permitted to comment on class '%s'", relname);
+  }
+  #endif
+
+  /*** Now, attempt to find the oid in the cached version of pg_class ***/
+
+  reltuple = SearchSysCacheTuple(RELNAME, PointerGetDatum(relname),
+				 0, 0, 0);
+  if (!HeapTupleIsValid(reltuple)) {
+    elog(ERROR, "relation '%s' does not exist", relname);
+  }
+
+  oid = reltuple->t_data->t_oid;
+
+  /*** Next, verify that the relation type matches the intent ***/
+
+  relkind = ((Form_pg_class) GETSTRUCT(reltuple))->relkind;
+
+  switch (reltype) {
+  case (INDEX):
+    if (relkind != 'i') {
+      elog(ERROR, "relation '%s' is not an index", relname);
+    }
+    break;
+  case (TABLE):
+    if (relkind != 'r') {
+      elog(ERROR, "relation '%s' is not a table", relname);
+    }
+    break;
+  case (VIEW):
+    if (relkind != 'r') {
+      elog(ERROR, "relation '%s' is not a view", relname);
+    }
+    break;
+  case (SEQUENCE):
+    if (relkind != 'S') {
+      elog(ERROR, "relation '%s' is not a sequence", relname);
+    }
+    break;
+  }
+    
+  /*** Create the comments using the tuple's oid ***/
+
+  CreateComments(oid, comment);  
+
+}
+
+/*------------------------------------------------------------------
+ * 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 fetch the oid of the associated
+ * attribute. If successful, a comment is added/dropped, else an
+ * elog() exception is thrown.  The parameters are the relation
+ * and attribute names, and the comments 
+ *------------------------------------------------------------------
+*/
+
+void CommentAttribute(char *relname, char *attrname, char *comment) {
+
+  Relation relation;
+  HeapTuple attrtuple;
+  Oid oid;
+
+  /*** First, check object security ***/
+
+  #ifndef NO_SECURITY
+  if (!pg_ownercheck(GetPgUserName(), relname, RELNAME)) {
+    elog(ERROR, "you are not permitted to comment on class '%s\'", relname);
+  }
+  #endif
+  
+  /*** Now, fetch the attribute oid from the system cache ***/
+
+  relation = heap_openr(relname, AccessShareLock);  
+  attrtuple = SearchSysCacheTuple(ATTNAME, ObjectIdGetDatum(relation->rd_id),
+				  PointerGetDatum(attrname), 0, 0);
+  if (!HeapTupleIsValid(attrtuple)) {
+    elog(ERROR, "'%s' is not an attribute of class '%s'",
+	 attrname, relname);
+  }
+  oid = attrtuple->t_data->t_oid;
+  
+  /*** Call CreateComments() to create/drop the comments ***/
+
+  CreateComments(oid, comment);
+
+  /*** Now, close the heap relation and return ***/
+
+  heap_close(relation, AccessShareLock); 
+
+}
+
+/*------------------------------------------------------------------
+ * 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 succesful, will then 
+ * attempt to find the oid of the database specified. Once found, 
+ * a comment is added/dropped using the CreateComments() routine. 
+ *------------------------------------------------------------------
+*/
+
+void CommentDatabase(char *database, char *comment) {
+
+  Relation pg_database;
+  HeapTuple dbtuple, usertuple;
+  ScanKeyData entry;
+  HeapScanDesc scan;
+  Oid oid;
+  bool superuser;
+  int4 dba, userid;
+  char *username;  
+
+  /*** First find the tuple in pg_database for the database ***/
+
+  pg_database = heap_openr(DatabaseRelationName, AccessShareLock);
+  ScanKeyEntryInitialize(&entry, 0, Anum_pg_database_datname,
+			 F_NAMEEQ, NameGetDatum(database));
+  scan = heap_beginscan(pg_database, 0, SnapshotNow, 1, &entry);
+  dbtuple = heap_getnext(scan, 0);
+  
+  /*** Validate database exists, and fetch the dba id and oid ***/
+
+  if (!HeapTupleIsValid(dbtuple)) {
+    elog(ERROR, "database '%s' does not exist", database);
+  }
+  dba = ((Form_pg_database) GETSTRUCT(dbtuple))->datdba;
+  oid = dbtuple->t_data->t_oid;
+  
+  /*** Now, fetch user information ***/
+  
+  username = GetPgUserName();
+  usertuple = SearchSysCacheTuple(USENAME, PointerGetDatum(username),
+				  0, 0, 0);
+  if (!HeapTupleIsValid(usertuple)) {
+    elog(ERROR, "current user '%s' does not exist", username);
+  }
+  userid = ((Form_pg_shadow) GETSTRUCT(usertuple))->usesysid;
+  superuser = ((Form_pg_shadow) GETSTRUCT(usertuple))->usesuper;
+  
+  /*** Allow if the userid matches the database dba or is a superuser ***/
+  
+  #ifndef NO_SECURITY
+  if (!(superuser || (userid == dba))) {
+    elog(ERROR, "you are not permitted to comment on database '%s'",
+	 database);
+  }
+  #endif
+
+  /*** Create the comments with the pg_database oid ***/
+  
+  CreateComments(oid, comment);
+  
+  /*** Complete the scan and close any opened relations ***/
+  
+  heap_endscan(scan);
+  heap_close(pg_database, AccessShareLock);
+
+}
+
+/*------------------------------------------------------------------
+ * CommentRewrite --
+ *
+ * This routine is used to add/drop any user-comments a user might
+ * have regarding a specified RULE. The rule is specified by name
+ * and, if found, and the user has appropriate permissions, a 
+ * comment will be added/dropped using the CreateComments() routine.
+ *------------------------------------------------------------------
+*/
+
+void CommentRewrite(char *rule, char *comment) {
+
+  HeapTuple rewritetuple;
+  Oid oid;
+  char *user, *relation;
+  int aclcheck;
+
+  /*** First, validate user ***/
+  
+  #ifndef NO_SECURITY  
+  user = GetPgUserName();
+  relation = RewriteGetRuleEventRel(rule);
+  aclcheck = pg_aclcheck(relation, user, ACL_RU);
+  if (aclcheck != ACLCHECK_OK) {
+    elog(ERROR, "you are not permitted to comment on rule '%s'",
+	 rule);
+  }
+  #endif
+
+  /*** Next, find the rule's oid ***/
+  
+  rewritetuple = SearchSysCacheTuple(REWRITENAME, PointerGetDatum(rule),
+				     0, 0, 0);
+  if (!HeapTupleIsValid(rewritetuple)) {
+    elog(ERROR, "rule '%s' does not exist", rule);
+  }
+
+  oid = rewritetuple->t_data->t_oid;
+  
+  /*** Call CreateComments() to create/drop the comments ***/
+
+  CreateComments(oid, comment);
+
+}
+
+/*------------------------------------------------------------------
+ * 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 paramters to this routine.
+ *------------------------------------------------------------------
+*/
+
+void CommentType(char *type, char *comment) {
+
+  HeapTuple typetuple;
+  Oid oid;
+  char *user;
+
+  /*** First, validate user ***/
+  
+  #ifndef NO_SECURITY  
+  user = GetPgUserName();
+  if (!pg_ownercheck(user, type, TYPNAME)) {    
+    elog(ERROR, "you are not permitted to comment on type '%s'",
+	 type);
+  }
+  #endif
+
+  /*** Next, find the type's oid ***/
+  
+  typetuple = SearchSysCacheTuple(TYPNAME, PointerGetDatum(type),
+				  0, 0, 0);
+  if (!HeapTupleIsValid(typetuple)) {
+    elog(ERROR, "type '%s' does not exist", type);
+  }
+
+  oid = typetuple->t_data->t_oid;
+  
+  /*** Call CreateComments() to create/drop the comments ***/
+
+  CreateComments(oid, 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, which, with the comments are 
+ * the three parameters handed to this routine.
+ *------------------------------------------------------------------
+*/
+
+void CommentAggregate(char *aggregate, char *argument, char *comment) {
+  
+  HeapTuple aggtuple;
+  Oid baseoid, oid;
+  bool defined;
+  char *user;
+  
+  /*** First, attempt to determine the base aggregate oid ***/
+
+  if (argument) {
+    baseoid = TypeGet(argument, &defined);
+    if (!OidIsValid(baseoid)) {
+      elog(ERROR, "aggregate type '%s' does not exist", argument);
+    }
+  } else {
+    baseoid = 0;
+  }
+  
+  /*** Next, validate the user's attempt to comment ***/
+
+  #ifndef NO_SECURITY
+  user = GetPgUserName();
+  if (!pg_aggr_ownercheck(user, aggregate, baseoid)) {
+    if (argument) {
+      elog(ERROR, "you are not permitted to comment on aggregate '%s' %s '%s'",
+	   aggregate, "with type", argument);
+    } else {
+      elog(ERROR, "you are not permitted to comment on aggregate '%s'",
+	   aggregate);
+    }
+  }
+  #endif
+
+  /*** Now, attempt to find the actual tuple in pg_aggregate ***/
+
+  aggtuple = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggregate),
+				 ObjectIdGetDatum(baseoid), 0, 0);
+  if (!HeapTupleIsValid(aggtuple)) {
+    if (argument) {
+      elog(ERROR, "aggregate type '%s' does not exist for aggregate '%s'",
+	   argument, aggregate);
+    } else {
+      elog(ERROR, "aggregate '%s' does not exist", aggregate);
+    }
+  }
+  
+  oid = aggtuple->t_data->t_oid;
+  
+  /*** Call CreateComments() to create/drop the comments ***/
+
+  CreateComments(oid, 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.
+ *------------------------------------------------------------------
+*/
+
+void CommentProc(char *function, List *arguments, char *comment) {
+
+  HeapTuple argtuple, functuple;
+  Oid oid, argoids[8];
+  char *user, *argument;
+  int i, argcount;
+
+  /*** First, initialize function's argument list with their type oids ***/
+
+  argcount = length(arguments);
+  if (argcount > 0) {
+    MemSet(argoids, 0, 8 * sizeof(Oid));
+    for (i = 0; i < argcount; i++) {
+      argument = strVal(lfirst(arguments));
+      arguments = lnext(arguments);
+      if (strcmp(argument, "opaque") == 0) {
+	argoids[i] = 0;
+      } else {
+	argtuple = SearchSysCacheTuple(TYPNAME, PointerGetDatum(argument),
+				       0, 0, 0);
+	if (!HeapTupleIsValid(argtuple)) {
+	  elog(ERROR, "function argument type '%s' does not exist",
+	       argument);
+	}
+	argoids[i] = argtuple->t_data->t_oid;
+      }
+    }
+  }
+
+  /*** Now, validate the user's ability to comment on this function ***/
+
+  #ifndef NO_SECURITY
+  user = GetPgUserName();
+  if (!pg_func_ownercheck(user, function, argcount, argoids)) {
+    elog(ERROR, "you are not permitted to comment on function '%s'",
+	 function);
+  }
+  #endif
+
+  /*** Now, find the corresponding oid for this procedure ***/
+
+  functuple = SearchSysCacheTuple(PRONAME, PointerGetDatum(function),
+				  Int32GetDatum(argcount),
+				  PointerGetDatum(argoids), 0);
+
+  /*** Deallocate our argument oids and check the function tuple ***/
+  
+  if (!HeapTupleIsValid(functuple)) {
+    elog(ERROR, "function '%s' with the supplied %s does not exist",
+	 function, "argument list");
+  }
+    
+  oid = functuple->t_data->t_oid;
+  
+  /*** Call CreateComments() to create/drop the comments ***/
+
+  CreateComments(oid, 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. If the comments string is empty, the associated comment 
+ * is dropped.
+ *------------------------------------------------------------------
+*/
+
+void CommentOperator(char *opername, List *arguments, char *comment) {
+
+  HeapTuple optuple;
+  Oid oid, leftoid = InvalidOid, rightoid = InvalidOid;
+  bool defined;
+  char oprtype = 0, *user, *lefttype = NULL, *righttype = NULL;
+
+  /*** Initialize our left and right argument types ***/
+
+  if (lfirst(arguments) != NULL) {
+    lefttype = strVal(lfirst(arguments));
+  }  
+  if (lsecond(arguments) != NULL) {
+    righttype = strVal(lsecond(arguments));
+  }
+
+  /*** Attempt to fetch the left oid, if specified ***/
+
+  if (lefttype != NULL) {
+    leftoid = TypeGet(lefttype, &defined);
+    if (!OidIsValid(leftoid)) {
+      elog(ERROR, "left type '%s' does not exist", lefttype);
+    }
+  }
+  
+  /*** Attempt to fetch the right oid, if specified ***/
+  
+  if (righttype != NULL) {
+    rightoid = TypeGet(righttype, &defined);
+    if (!OidIsValid(rightoid)) {
+      elog(ERROR, "right type '%s' does not exist", righttype);
+    }
+  }
+
+  /*** Determine operator type ***/
+
+  if (OidIsValid(leftoid) && (OidIsValid(rightoid))) oprtype = 'b';
+  else if (OidIsValid(leftoid)) oprtype = 'l';
+  else if (OidIsValid(rightoid)) oprtype = 'r';
+  else elog(ERROR, "operator '%s' is of an illegal type'", opername);
+  
+  /*** Attempt to fetch the operator oid ***/
+
+  optuple = SearchSysCacheTupleCopy(OPRNAME, PointerGetDatum(opername),
+				    ObjectIdGetDatum(leftoid),
+				    ObjectIdGetDatum(rightoid),
+				    CharGetDatum(oprtype));
+  if (!HeapTupleIsValid(optuple)) {
+    elog(ERROR, "operator '%s' does not exist", opername);
+  }
+
+  oid = optuple->t_data->t_oid;  
+  
+  /*** Valid user's ability to comment on this operator ***/
+
+  #ifndef NO_SECURITY
+  user = GetPgUserName();
+  if (!pg_ownercheck(user, (char *) ObjectIdGetDatum(oid), OPROID)) {
+    elog(ERROR, "you are not permitted to comment on operator '%s'",
+	 opername);
+  }
+  #endif
+
+  /*** Call CreateComments() to create/drop the comments ***/
+
+  CreateComments(oid, 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, the relation name, and the comments
+ * to add/drop.
+ *------------------------------------------------------------------
+*/
+
+void CommentTrigger(char *trigger, char *relname, char *comment) {
+
+  Form_pg_trigger data;
+  Relation pg_trigger, relation;
+  HeapTuple triggertuple;
+  HeapScanDesc scan;
+  ScanKeyData entry;
+  Oid oid = InvalidOid;
+  char *user;
+  
+  /*** First, validate the user's action ***/	
+
+  #ifndef NO_SECURITY
+  user = GetPgUserName();
+  if (!pg_ownercheck(user, relname, RELNAME)) {
+    elog(ERROR, "you are not permitted to comment on trigger '%s' %s '%s'",
+	 trigger, "defined for relation", relname);
+  }
+  #endif
+
+  /*** Now, fetch the trigger oid from pg_trigger  ***/
+
+  relation = heap_openr(relname, AccessShareLock);  
+  pg_trigger = heap_openr(TriggerRelationName, AccessShareLock);
+  ScanKeyEntryInitialize(&entry, 0, Anum_pg_trigger_tgrelid,
+			 F_OIDEQ, RelationGetRelid(relation));  
+  scan = heap_beginscan(pg_trigger, 0, SnapshotNow, 1, &entry);
+  triggertuple = heap_getnext(scan, 0);
+  while (HeapTupleIsValid(triggertuple)) {
+    data = (Form_pg_trigger) GETSTRUCT(triggertuple);
+    if (namestrcmp(&(data->tgname), trigger) == 0) {
+      oid = triggertuple->t_data->t_oid;
+      break;
+    }
+    triggertuple = heap_getnext(scan, 0);
+  }
+
+  /*** If no trigger exists for the relation specified, notify user ***/
+
+  if (oid == InvalidOid) {
+    elog(ERROR, "trigger '%s' defined for relation '%s' does not exist",
+	 trigger, relname);
+  }
+
+  /*** Create the comments with the pg_trigger oid ***/
+  
+  CreateComments(oid, comment);
+  
+  /*** Complete the scan and close any opened relations ***/
+  
+  heap_endscan(scan);
+  heap_close(pg_trigger, AccessShareLock);
+  heap_close(relation, AccessShareLock);
+
+}
diff --git a/src/backend/commands/creatinh.c b/src/backend/commands/creatinh.c
index 9463d55a6a16be2bb64081d660bf97a5cdc90ed0..f07586dac0acaef18f8365eb03ee654ba7d11915 100644
--- a/src/backend/commands/creatinh.c
+++ b/src/backend/commands/creatinh.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.49 1999/10/15 01:49:39 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.50 1999/10/26 03:12:34 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -234,52 +234,6 @@ TruncateRelation(char *name)
 	heap_truncate(name);
 }
 
-/*------------------------------------------------------------------
- * CommentRelation --
- *                Adds a comment to pg_description for the associated
- *                relation or relation attribute.
- *
- * Note:           
- *                The comment is dropped on the relation or attribute if
- *                the comment is an empty string.
- *------------------------------------------------------------------
- */
-void 
-CommentRelation(char *relname, char *attrname, char *comments) 
-{
-
-  Relation relation;
-  HeapTuple attrtuple;
-  Oid oid;
-
-  /*** First ensure relname is valid ***/
-
-  relation = heap_openr(relname, AccessShareLock);
-  
-  /*** Now, if an attribute was specified, fetch its oid, else use relation's oid ***/
-  
-  if (attrname != NULL) {
-    attrtuple = SearchSysCacheTuple(ATTNAME, ObjectIdGetDatum(relation->rd_id),
-				    PointerGetDatum(attrname), 0, 0);
-    if (!HeapTupleIsValid(attrtuple)) {
-      elog(ERROR, "CommentRelation: attribute \"%s\" is not an attribute of relation \"%s\"",
-	   attrname, relname);
-    }
-    oid = attrtuple->t_data->t_oid;
-  } else {
-    oid = RelationGetRelid(relation);
-  }
-  
-  /*** Call CreateComments() to create/drop the comments ***/
-
-  CreateComments(oid, comments);
-
-  /*** Now, close the heap relation ***/
-
-  heap_close(relation, AccessShareLock); 
-
-}
-
 /*
  * MergeAttributes
  *		Returns new schema given initial schema and supers.
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 75a917092a002054e3b3dca073bfe8e55b34f449..fc3bff2e4f4bc888db906e86c8ee639dd9fd09dc 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.42 1999/10/25 03:07:43 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.43 1999/10/26 03:12:34 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,6 +20,7 @@
 #include "catalog/catname.h"
 #include "catalog/pg_database.h"
 #include "catalog/pg_shadow.h"
+#include "commands/comment.h"
 #include "commands/dbcommands.h"
 #include "miscadmin.h"
 #include "storage/sinval.h"
@@ -149,6 +150,10 @@ destroydb(char *dbname, CommandDest dest)
 			 dbname, db_id);
 	}
 
+	/*** Delete any comments associated with the database ***/
+	
+	DeleteComments(db_id);
+
 	/*
 	 * Houston, we have launch commit...
 	 *
diff --git a/src/backend/commands/remove.c b/src/backend/commands/remove.c
index a73964cb02c3b1908afa842afdb31865b667c13b..67cd5c8c2abbd610bb61212b9d2ecb15607211c6 100644
--- a/src/backend/commands/remove.c
+++ b/src/backend/commands/remove.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.37 1999/09/18 19:06:40 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.38 1999/10/26 03:12:34 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,6 +18,7 @@
 #include "catalog/pg_language.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
+#include "commands/comment.h"
 #include "commands/defrem.h"
 #include "miscadmin.h"
 #include "parser/parse_func.h"
@@ -93,7 +94,14 @@ RemoveOperator(char *operatorName,		/* operator name */
 			elog(ERROR, "RemoveOperator: operator '%s': permission denied",
 				 operatorName);
 #endif
+
+
+		/*** Delete any comments associated with this operator ***/
+
+		DeleteComments(tup->t_data->t_oid);
+
 		heap_delete(relation, &tup->t_self, NULL);
+
 	}
 	else
 	{
@@ -147,8 +155,17 @@ SingleOpOperatorRemove(Oid typeOid)
 	{
 		key[0].sk_attno = attnums[i];
 		scan = heap_beginscan(rel, 0, SnapshotNow, 1, key);
-		while (HeapTupleIsValid(tup = heap_getnext(scan, 0)))
-			heap_delete(rel, &tup->t_self, NULL);
+		while (HeapTupleIsValid(tup = heap_getnext(scan, 0))) {
+
+		  /*** This is apparently a routine not in use, but remove ***/
+		  /*** any comments anyways ***/
+
+		  DeleteComments(tup->t_data->t_oid);
+
+		  heap_delete(rel, &tup->t_self, NULL);
+		  
+		}
+
 		heap_endscan(scan);
 	}
 	heap_close(rel, RowExclusiveLock);
@@ -259,6 +276,11 @@ RemoveType(char *typeName)		/* type name to be removed */
 	}
 
 	typeOid = tup->t_data->t_oid;
+
+	/*** Delete any comments associated with this type ***/
+
+	DeleteComments(typeOid);
+
 	heap_delete(relation, &tup->t_self, NULL);
 
 	/* Now, Delete the "array of" that type */
@@ -347,6 +369,10 @@ RemoveFunction(char *functionName,		/* function name to be removed */
 		elog(ERROR, "RemoveFunction: function \"%s\" is built-in", functionName);
 	}
 
+	/*** Delete any comments associated with this function ***/
+
+	DeleteComments(tup->t_data->t_oid);
+
 	heap_delete(relation, &tup->t_self, NULL);
 
 	heap_close(relation, RowExclusiveLock);
@@ -418,6 +444,11 @@ RemoveAggregate(char *aggName, char *aggType)
 				 aggName);
 		}
 	}
+	
+	/*** Remove any comments related to this aggregate ***/
+
+	DeleteComments(tup->t_data->t_oid);
+
 	heap_delete(relation, &tup->t_self, NULL);
 
 	heap_close(relation, RowExclusiveLock);
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 17a26c3c8e937b70d409158aa093c0995974c307..1bec6aa7626e91d660ac27498e3e17e0e9445e88 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -16,6 +16,7 @@
 #include "catalog/pg_language.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_trigger.h"
+#include "commands/comment.h"
 #include "commands/trigger.h"
 #include "executor/executor.h"
 #include "miscadmin.h"
@@ -297,8 +298,14 @@ DropTrigger(DropTrigStmt *stmt)
 
 		if (namestrcmp(&(pg_trigger->tgname), stmt->trigname) == 0)
 		{
-			heap_delete(tgrel, &tuple->t_self, NULL);
-			tgfound++;
+
+		  /*** Delete any comments associated with this trigger ***/
+
+		  DeleteComments(tuple->t_data->t_oid);
+
+		  heap_delete(tgrel, &tuple->t_self, NULL);
+		  tgfound++;
+		  
 		}
 		else
 			found++;
@@ -355,8 +362,15 @@ RelationRemoveTriggers(Relation rel)
 
 	tgscan = heap_beginscan(tgrel, 0, SnapshotNow, 1, &key);
 
-	while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0)))
-		heap_delete(tgrel, &tup->t_self, NULL);
+	while (HeapTupleIsValid(tup = heap_getnext(tgscan, 0))) {
+
+	  /*** Delete any comments associated with this trigger ***/
+
+	  DeleteComments(tup->t_data->t_oid);
+	  
+	  heap_delete(tgrel, &tup->t_self, NULL);
+
+	}
 
 	heap_endscan(tgscan);
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 16a4efa2baa49fdeb6babe428db091e2cda13e76..a4a0c721ea318ae884ae6aeb682a7a3d7ab50d40 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.109 1999/10/15 01:49:41 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.110 1999/10/26 03:12:34 momjian Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -196,7 +196,8 @@ Oid	param_type(int t); /* used in parse_expr.c */
 %type <boolean> opt_cursor
 
 %type <ival>	copy_dirn, def_type, opt_direction, remove_type,
-				opt_column, event
+		opt_column, event, comment_type, comment_cl,
+		comment_ag, comment_fn, comment_op, comment_tg
 
 %type <ival>	fetch_how_many
 
@@ -1542,28 +1543,101 @@ TruncateStmt:  TRUNCATE TABLE relation_name
 
 /*****************************************************************************
  *
- *             QUERY:
- *                     comment on [ table <relname> | column <relname>.<attribute> ]
- *                                is 'text'
+ *  The COMMENT ON statement can take different forms based upon the type of
+ *  the object associated with the comment. The form of the statement is:
+ *
+ *  COMMENT ON [ [ DATABASE | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ] 
+ *               <objname> | AGGREGATE <aggname> <aggtype> | FUNCTION 
+ *		 <funcname> (arg1, arg2, ...) | OPERATOR <op> 
+ *		 (leftoperand_typ rightoperand_typ) | TRIGGER <triggername> ON
+ *		 <relname> ] IS 'text'
  *
  *****************************************************************************/
  
-CommentStmt:	COMMENT ON COLUMN relation_name '.' attr_name IS Sconst
+CommentStmt:	COMMENT ON comment_type name IS Sconst
+			{
+				CommentStmt *n = makeNode(CommentStmt);
+				n->objtype = $3;
+				n->objname = $4;
+				n->objproperty = NULL;
+				n->objlist = NULL;
+				n->comment = $6;
+				$$ = (Node *) n;
+			}
+		| COMMENT ON comment_cl relation_name '.' attr_name IS Sconst
 			{
 				CommentStmt *n = makeNode(CommentStmt);
-				n->relname = $4;
-				n->attrname = $6;
+				n->objtype = $3;
+				n->objname = $4;
+				n->objproperty = $6;
+				n->objlist = NULL;
 				n->comment = $8;
 				$$ = (Node *) n;
 			}
-		| COMMENT ON TABLE relation_name IS Sconst
-		        {
+		| COMMENT ON comment_ag name aggr_argtype IS Sconst
+			{
 				CommentStmt *n = makeNode(CommentStmt);
-				n->relname = $4;
-				n->attrname = NULL;
-				n->comment = $6;
+				n->objtype = $3;
+				n->objname = $4;
+				n->objproperty = $5;
+				n->objlist = NULL;
+				n->comment = $7;
+				$$ = (Node *) n;
+			}
+		| COMMENT ON comment_fn func_name func_args IS Sconst
+			{
+				CommentStmt *n = makeNode(CommentStmt);
+				n->objtype = $3;
+				n->objname = $4;
+				n->objproperty = NULL;
+				n->objlist = $5;
+				n->comment = $7;
+				$$ = (Node *) n;
+			}
+		| COMMENT ON comment_op all_Op '(' oper_argtypes ')' IS Sconst
+			{
+				CommentStmt *n = makeNode(CommentStmt);
+				n->objtype = $3;
+				n->objname = $4;
+				n->objproperty = NULL;
+				n->objlist = $6;
+				n->comment = $9;
 				$$ = (Node *) n;
 			}
+		| COMMENT ON comment_tg name ON relation_name IS Sconst
+			{
+				CommentStmt *n = makeNode(CommentStmt);
+				n->objtype = $3;
+				n->objname = $4;
+				n->objproperty = $6;
+				n->objlist = NULL;
+				n->comment = $8;
+				$$ = (Node *) n;
+			}
+		;
+
+comment_type:	DATABASE { $$ = DATABASE; }
+		| INDEX { $$ = INDEX; }
+		| RULE { $$ = RULE; }
+		| SEQUENCE { $$ = SEQUENCE; }
+		| TABLE { $$ = TABLE; }
+		| TYPE_P { $$ = TYPE_P; }
+		| VIEW { $$ = VIEW; }
+		;		
+
+comment_cl:	COLUMN { $$ = COLUMN; }
+		;
+
+comment_ag:	AGGREGATE { $$ = AGGREGATE; }
+		;
+
+comment_fn:	FUNCTION { $$ = FUNCTION; }
+		;
+
+comment_op:	OPERATOR { $$ = OPERATOR; }
+		;
+
+comment_tg:	TRIGGER { $$ = TRIGGER; }
 		;
 
 /*****************************************************************************
diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c
index 50e6c62c027beac0af47d36a97574e7bcfeb955f..c751550e494cd62a907485fa2bf09b63c1e1247b 100644
--- a/src/backend/rewrite/rewriteRemove.c
+++ b/src/backend/rewrite/rewriteRemove.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.29 1999/09/18 19:07:19 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteRemove.c,v 1.30 1999/10/26 03:12:35 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,6 +18,7 @@
 #include "access/heapam.h"
 #include "catalog/catname.h"
 #include "catalog/pg_rewrite.h"
+#include "commands/comment.h"
 #include "rewrite/rewriteRemove.h"
 #include "rewrite/rewriteSupport.h"
 #include "utils/syscache.h"
@@ -119,6 +120,13 @@ RemoveRewriteRule(char *ruleName)
 	 */
 	prs2_deleteFromRelation(eventRelationOid, ruleId);
 
+	/*
+	 * Delete any comments associated with this rule
+	 *
+	 */	 
+	 
+	DeleteComments(ruleId);
+
 	/*
 	 * Now delete the tuple...
 	 */
@@ -158,8 +166,15 @@ RelationRemoveRules(Oid relid)
 	scanDesc = heap_beginscan(RewriteRelation,
 							  0, SnapshotNow, 1, &scanKeyData);
 
-	while (HeapTupleIsValid(tuple = heap_getnext(scanDesc, 0)))
-		heap_delete(RewriteRelation, &tuple->t_self, NULL);
+	while (HeapTupleIsValid(tuple = heap_getnext(scanDesc, 0))) {
+
+	  /*** Delete any comments associated with this relation ***/
+
+	  DeleteComments(tuple->t_data->t_oid);
+	   
+	  heap_delete(RewriteRelation, &tuple->t_self, NULL);
+
+	}
 
 	heap_endscan(scanDesc);
 	heap_close(RewriteRelation, RowExclusiveLock);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index ec617d753692251b224e358014d3e504aab31ee0..6035c89b2eaf729a7336717219ad34b39fe5e9d5 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.70 1999/10/15 01:49:43 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.71 1999/10/26 03:12:36 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,6 +21,7 @@
 #include "commands/async.h"
 #include "commands/cluster.h"
 #include "commands/command.h"
+#include "commands/comment.h"
 #include "commands/copy.h"
 #include "commands/creatinh.h"
 #include "commands/dbcommands.h"
@@ -38,7 +39,6 @@
 #include "rewrite/rewriteRemove.h"
 #include "tcop/utility.h"
 #include "utils/acl.h"
-#include "utils/acl.h"
 #include "utils/ps_status.h"
 #include "utils/syscache.h"
 
@@ -242,14 +242,9 @@ ProcessUtility(Node *parsetree,
 	    
 	    PS_SET_STATUS(commandTag = "COMMENT");
 	    CHECK_IF_ABORTED();
-
-#ifndef NO_SECURITY
-	    if (!pg_ownercheck(userName, statement->relname, RELNAME))
-	      elog(ERROR, "you do not own class \"%s\"", statement->relname);
-#endif
-
-	    CommentRelation(statement->relname, statement->attrname, 
-			    statement->comment);
+	    CommentObject(statement->objtype, statement->objname,
+			  statement->objproperty, statement->objlist,
+			  statement->comment);
 	  }
 	  break;
 	    
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 4720013160486ad5f28023fac2d2cd19e817d8f7..1cb86b501c7584a4f50768b89a1187f0541ae271 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: heap.h,v 1.23 1999/10/15 01:49:44 momjian Exp $
+ * $Id: heap.h,v 1.24 1999/10/26 03:12:37 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,23 +21,20 @@ typedef struct RawColumnDefault
 	Node	   *raw_default;	/* default value (untransformed parse tree) */
 } RawColumnDefault;
 
-extern Oid	RelnameFindRelid(char *relname);
+extern Oid RelnameFindRelid(char *relname);
 extern Relation heap_create(char *relname, TupleDesc att,
-			bool isnoname, bool istemp);
+			    bool isnoname, bool istemp);
 
-extern Oid heap_create_with_catalog(char *relname,
-						 TupleDesc tupdesc, char relkind, bool istemp);
+extern Oid heap_create_with_catalog(char *relname, TupleDesc tupdesc, 
+				    char relkind, bool istemp);
 
 extern void heap_destroy_with_catalog(char *relname);
 extern void heap_truncate(char *relname);
 extern void heap_destroy(Relation rel);
 
-extern void CreateComments(Oid object, char *comments);
-extern void DeleteComments(Oid object);
-
 extern void AddRelationRawConstraints(Relation rel,
-									  List *rawColDefaults,
-									  List *rawConstraints);
+				      List *rawColDefaults,
+				      List *rawConstraints);
 
 extern void InitNoNameRelList(void);
 extern void DestroyNoNameRels(void);
diff --git a/src/include/commands/comment.h b/src/include/commands/comment.h
new file mode 100644
index 0000000000000000000000000000000000000000..93ed200a74a887540cb526e47bac32bde81f1900
--- /dev/null
+++ b/src/include/commands/comment.h
@@ -0,0 +1,31 @@
+/*-------------------------------------------------------------------------
+ *
+ * comment.h
+ *	  
+ * Prototypes for functions in commands/comment.c
+ *
+ * Copyright (c) 1999, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef COMMENT_H
+#define COMMENT_H
+
+/*------------------------------------------------------------------
+ * Function Prototypes --
+ * 
+ * The following protoypes define the public functions of the comment
+ * related routines. CreateComments() is used to create/drop a comment
+ * for any object with a valid oid. DeleteComments() deletes, if any, 
+ * the comments associated with the object. CommentObject() is used to 
+ * create comments to be identified by the specific type.
+ *------------------------------------------------------------------
+ */
+
+void CreateComments(Oid oid, char *comment);
+void DeleteComments(Oid oid);
+void CommentObject(int objtype, char *objname, char *objproperty, 
+		   List *objlist, char *comment);
+
+#endif	 /* COMMENT_H */
diff --git a/src/include/commands/creatinh.h b/src/include/commands/creatinh.h
index 11d5fdb7a0d41602c310cf8ff043f8ee6947dba0..8bfe142a1f499e74b7938b87c3feb85c16fdc310 100644
--- a/src/include/commands/creatinh.h
+++ b/src/include/commands/creatinh.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: creatinh.h,v 1.11 1999/10/15 01:49:46 momjian Exp $
+ * $Id: creatinh.h,v 1.12 1999/10/26 03:12:38 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,6 +18,5 @@
 extern void DefineRelation(CreateStmt *stmt, char relkind);
 extern void RemoveRelation(char *name);
 extern void TruncateRelation(char *name);
-extern void CommentRelation(char *name, char *attr, char *comment);
 
 #endif	 /* CREATINH_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 208b31d740da7ac5b67b28b8be0d33f098514903..aac3ef7de536145ee05cefb9acab2d2a514c8311 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.85 1999/10/15 01:49:47 momjian Exp $
+ * $Id: parsenodes.h,v 1.86 1999/10/26 03:12:39 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -316,10 +316,12 @@ typedef struct TruncateStmt
  */
 typedef struct CommentStmt
 {
-  NodeTag         type;
-  char *relname;                       /* relation to create/drop comment */
-  char *attrname;                      /* attribute to comment on */
-  char *comment;                       /* the actual comment */
+  NodeTag type;
+  int objtype;                         /* Object's type */
+  char *objname;                       /* Name of the object */
+  char *objproperty;                   /* Property Id (such as column) */
+  List *objlist;                       /* Arguments for VAL objects */
+  char *comment;                       /* The comment to insert */
 } CommentStmt;
       
 /* ----------------------