diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml
index ae6092f1a2d66ec30f0eed621c13af06e3a3559d..3d4041ba8eead3f8ebc7c0959c6017fae037189a 100644
--- a/doc/src/sgml/ref/comment.sgml
+++ b/doc/src/sgml/ref/comment.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/comment.sgml,v 1.18 2002/04/23 02:07:15 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/comment.sgml,v 1.19 2002/05/13 17:45:30 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -23,13 +23,20 @@ PostgreSQL documentation
   <synopsis>
 COMMENT ON
 [
-  [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW ] <replaceable class="PARAMETER">object_name</replaceable> |
+  TABLE <replaceable class="PARAMETER">object_name</replaceable> |
   COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
   AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable>) |
-  FUNCTION <replaceable class="PARAMETER">func_name</replaceable> (<replaceable class="PARAMETER">arg1</replaceable>, <replaceable class="PARAMETER">arg2</replaceable>, ...) |
-  OPERATOR <replaceable class="PARAMETER">op</replaceable> (<replaceable class="PARAMETER">leftoperand_type</replaceable> <replaceable class="PARAMETER">rightoperand_type</replaceable>) |
+  DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
+  DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
+  FUNCTION <replaceable class="PARAMETER">func_name</replaceable> (<replaceable class="PARAMETER">arg1_type</replaceable>, <replaceable class="PARAMETER">arg2_type</replaceable>, ...) |
+  INDEX <replaceable class="PARAMETER">object_name</replaceable> |
+  OPERATOR <replaceable class="PARAMETER">op</replaceable> (<replaceable class="PARAMETER">leftoperand_type</replaceable>, <replaceable class="PARAMETER">rightoperand_type</replaceable>) |
   RULE <replaceable class="PARAMETER">rule_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> |
-  TRIGGER <replaceable class="PARAMETER">trigger_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable>
+  SCHEMA <replaceable class="PARAMETER">object_name</replaceable> |
+  SEQUENCE <replaceable class="PARAMETER">object_name</replaceable> |
+  TRIGGER <replaceable class="PARAMETER">trigger_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> |
+  TYPE <replaceable class="PARAMETER">object_name</replaceable> |
+  VIEW <replaceable class="PARAMETER">object_name</replaceable>
 ] IS <replaceable class="PARAMETER">'text'</replaceable>
   </synopsis>
 
@@ -49,8 +56,9 @@ COMMENT ON
       <listitem>
        <para>
 	The name of the object to be be commented.  Names of tables,
-	indexes, sequences, views, types, domains, functions, aggregates,
-	and operators may be schema-qualified.
+	aggregates, domains, functions, indexes, operators, sequences, types,
+	and views
+	may be schema-qualified.
        </para>
       </listitem>
      </varlistentry>
@@ -116,13 +124,15 @@ COMMENT
     Comments are automatically dropped when the object is dropped.
   </para>
 
+  <note>
   <para>
-    It should be noted that there is presently no security mechanism
+    There is presently no security mechanism
     for comments: any user connected to a database can see all the comments
     for objects in that database (although only superusers can change
     comments for objects that they don't own).  Therefore, don't put
     security-critical information in comments.
   </para>
+  </note>
  </refsect1>
 
  <refsect1 id="R1-SQL-COMMENT-2">
@@ -130,10 +140,16 @@ COMMENT
    Usage
   </title>
   <para>
-   Comment the table <literal>mytable</literal>:
+   Attach a comment to the table <literal>mytable</literal>:
 
    <programlisting>
-COMMENT ON mytable IS 'This is my table.';
+COMMENT ON TABLE mytable IS 'This is my table.';
+   </programlisting>
+
+   Remove it again:
+
+   <programlisting>
+COMMENT ON TABLE mytable IS NULL;
    </programlisting>
   </para>
 
@@ -141,19 +157,21 @@ COMMENT ON mytable IS 'This is my table.';
    Some more examples:
 
    <programlisting>
+COMMENT ON AGGREGATE my_aggregate (double precision) IS 'Computes sample variance';
+COMMENT ON COLUMN my_table.my_field IS 'Employee ID number';
 COMMENT ON DATABASE my_database IS 'Development Database';
 COMMENT ON DOMAIN my_domain IS 'Email Address Domain';
-COMMENT ON INDEX my_index IS 'Enforces uniqueness on employee id';
-COMMENT ON SEQUENCE my_sequence IS 'Used to generate primary keys';
-COMMENT ON TABLE my_table IS 'Employee Information';
-COMMENT ON TYPE my_type IS 'Complex Number support';
-COMMENT ON VIEW my_view IS 'View of departmental costs';
-COMMENT ON COLUMN my_table.my_field IS 'Employee ID number';
-COMMENT ON AGGREGATE my_aggregate (double precision) IS 'Computes sample variance';
 COMMENT ON FUNCTION my_function (timestamp) IS 'Returns Roman Numeral';
-COMMENT ON OPERATOR ^ (text, text) IS 'Performs intersection of two text';
+COMMENT ON INDEX my_index IS 'Enforces uniqueness on employee id';
+COMMENT ON OPERATOR ^ (text, text) IS 'Performs intersection of two texts';
+COMMENT ON OPERATOR ^ (NONE, text) IS 'This is a prefix operator on text';
 COMMENT ON RULE my_rule ON my_table IS 'Logs UPDATES of employee records';
+COMMENT ON SCHEMA my_schema IS 'Departmental data';
+COMMENT ON SEQUENCE my_sequence IS 'Used to generate primary keys';
+COMMENT ON TABLE my_schema.my_table IS 'Employee Information';
 COMMENT ON TRIGGER my_trigger ON my_table IS 'Used for R.I.';
+COMMENT ON TYPE complex IS 'Complex Number datatype';
+COMMENT ON VIEW my_view IS 'View of departmental costs';
    </programlisting>
   </para>
  </refsect1>
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index 0eb59b912f3daaeda9c8bb6b6ed62ef601562df3..1c12061ba553d297a2707391bc896da84f438294 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -7,7 +7,7 @@
  * Copyright (c) 1999-2001, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.45 2002/04/27 03:45:00 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.46 2002/05/13 17:45:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,6 +50,7 @@
 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);
@@ -99,6 +100,9 @@ CommentObject(CommentStmt *stmt)
 		case TRIGGER:
 			CommentTrigger(stmt->objname, stmt->comment);
 			break;
+		case SCHEMA:
+			CommentNamespace(stmt->objname, stmt->comment);
+			break;
 		default:
 			elog(ERROR, "An attempt was made to comment on a unknown type: %d",
 				 stmt->objtype);
@@ -332,22 +336,22 @@ CommentRelation(int objtype, List *relname, char *comment)
 	{
 		case INDEX:
 			if (relation->rd_rel->relkind != RELKIND_INDEX)
-				elog(ERROR, "relation '%s' is not an index",
+				elog(ERROR, "relation \"%s\" is not an index",
 					 RelationGetRelationName(relation));
 			break;
 		case TABLE:
 			if (relation->rd_rel->relkind != RELKIND_RELATION)
-				elog(ERROR, "relation '%s' is not a table",
+				elog(ERROR, "relation \"%s\" is not a table",
 					 RelationGetRelationName(relation));
 			break;
 		case VIEW:
 			if (relation->rd_rel->relkind != RELKIND_VIEW)
-				elog(ERROR, "relation '%s' is not a view",
+				elog(ERROR, "relation \"%s\" is not a view",
 					 RelationGetRelationName(relation));
 			break;
 		case SEQUENCE:
 			if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
-				elog(ERROR, "relation '%s' is not a sequence",
+				elog(ERROR, "relation \"%s\" is not a sequence",
 					 RelationGetRelationName(relation));
 			break;
 	}
@@ -400,7 +404,7 @@ CommentAttribute(List *qualname, char *comment)
 
 	attnum = get_attnum(RelationGetRelid(relation), attrname);
 	if (attnum == InvalidAttrNumber)
-		elog(ERROR, "'%s' is not an attribute of class '%s'",
+		elog(ERROR, "\"%s\" is not an attribute of class \"%s\"",
 			 attrname, RelationGetRelationName(relation));
 
 	/* Create the comment using the relation's oid */
@@ -451,13 +455,13 @@ CommentDatabase(List *qualname, char *comment)
 	/* Validate database exists, and fetch the db oid */
 
 	if (!HeapTupleIsValid(dbtuple))
-		elog(ERROR, "database '%s' does not exist", database);
+		elog(ERROR, "database \"%s\" does not exist", database);
 	oid = dbtuple->t_data->t_oid;
 
 	/* Allow if the user matches the database dba or is a superuser */
 
 	if (!(superuser() || is_dbadmin(oid)))
-		elog(ERROR, "you are not permitted to comment on database '%s'",
+		elog(ERROR, "you are not permitted to comment on database \"%s\"",
 			 database);
 
 	/* Create the comments with the pg_database oid */
@@ -470,6 +474,51 @@ CommentDatabase(List *qualname, char *comment)
 	heap_close(pg_database, AccessShareLock);
 }
 
+/*
+ * 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 succesful, 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;
+	Oid			classoid;
+	HeapTuple	tp;
+	char	   *namespace;
+
+	if (length(qualname) != 1)
+		elog(ERROR, "CommentSchema: schema name may not be qualified");
+	namespace = strVal(lfirst(qualname));
+
+	tp = SearchSysCache(NAMESPACENAME,
+						CStringGetDatum(namespace),
+						0, 0, 0);
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "CommentSchema: Schema \"%s\" could not be found",
+			 namespace);
+
+	oid = tp->t_data->t_oid;
+
+	/* Check object security */
+	if (!pg_namespace_ownercheck(oid, GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, namespace);
+
+	/* pg_namespace doesn't have a hard-coded OID, so must look it up */
+	classoid = get_relname_relid(NamespaceRelationName, PG_CATALOG_NAMESPACE);
+	Assert(OidIsValid(classoid));
+
+	/* Call CreateComments() to create/drop the comments */
+	CreateComments(oid, classoid, 0, comment);
+
+	/* Cleanup */
+	ReleaseSysCache(tp);
+}
+
 /*
  * CommentRule --
  *
@@ -528,12 +577,12 @@ CommentRule(List *qualname, char *comment)
 		}
 		else
 		{
-			elog(ERROR, "rule '%s' does not exist", rulename);
+			elog(ERROR, "rule \"%s\" does not exist", rulename);
 			reloid = ruleoid = 0; /* keep compiler quiet */
 		}
 
 		if (HeapTupleIsValid(tuple = heap_getnext(scanDesc, 0)))
-			elog(ERROR, "There are multiple rules '%s'"
+			elog(ERROR, "There are multiple rules \"%s\""
 				 "\n\tPlease specify a relation name as well as a rule name",
 				 rulename);
 
@@ -561,7 +610,7 @@ CommentRule(List *qualname, char *comment)
 							   PointerGetDatum(rulename),
 							   0, 0);
 		if (!HeapTupleIsValid(tuple))
-			elog(ERROR, "rule '%s' does not exist", rulename);
+			elog(ERROR, "rule \"%s\" does not exist", rulename);
 		Assert(reloid == ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class);
 		ruleoid = tuple->t_data->t_oid;
 		ReleaseSysCache(tuple);
@@ -574,7 +623,6 @@ CommentRule(List *qualname, char *comment)
 		aclcheck_error(aclcheck, rulename);
 
 	/* pg_rewrite doesn't have a hard-coded OID, so must look it up */
-
 	classoid = get_relname_relid(RewriteRelationName, PG_CATALOG_NAMESPACE);
 	Assert(OidIsValid(classoid));
 
@@ -689,12 +737,7 @@ CommentProc(List *function, List *arguments, char *comment)
  * 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.
- *
- * NOTE: we actually attach the comment to the procedure that underlies
- * the operator.  This is a feature, not a bug: we want the same comment
- * to be visible for both operator and function.
+ * object.
  */
 static void
 CommentOperator(List *opername, List *arguments, char *comment)
@@ -702,27 +745,22 @@ CommentOperator(List *opername, List *arguments, char *comment)
 	TypeName   *typenode1 = (TypeName *) lfirst(arguments);
 	TypeName   *typenode2 = (TypeName *) lsecond(arguments);
 	Oid			oid;
+	Oid			classoid;
 
 	/* Look up the operator */
-
 	oid = LookupOperNameTypeNames(opername, typenode1, typenode2,
 								  "CommentOperator");
 
 	/* Valid user's ability to comment on this operator */
-
 	if (!pg_oper_ownercheck(oid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, NameListToString(opername));
 
-	/* Get the procedure associated with the operator */
-
-	oid = get_opcode(oid);
-	if (oid == InvalidOid)
-		elog(ERROR, "operator '%s' does not have an underlying function",
-			 NameListToString(opername));
+	/* pg_operator doesn't have a hard-coded OID, so must look it up */
+	classoid = get_relname_relid(OperatorRelationName, PG_CATALOG_NAMESPACE);
+	Assert(OidIsValid(classoid));
 
 	/* Call CreateComments() to create/drop the comments */
-
-	CreateComments(oid, RelOid_pg_proc, 0, comment);
+	CreateComments(oid, classoid, 0, comment);
 }
 
 /*
@@ -784,7 +822,7 @@ CommentTrigger(List *qualname, char *comment)
 	/* If no trigger exists for the relation specified, notify user */
 
 	if (!HeapTupleIsValid(triggertuple))
-		elog(ERROR, "trigger '%s' for relation '%s' does not exist",
+		elog(ERROR, "trigger \"%s\" for relation \"%s\" does not exist",
 			 trigname, RelationGetRelationName(relation));
 
 	oid = triggertuple->t_data->t_oid;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6b7469f2bd71fc47a5c7912e7dd018b712ad4921..bd3a7a0832b4844b3835c89f41c499709af9ca40 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.314 2002/05/12 20:10:04 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.315 2002/05/13 17:45:30 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -2278,6 +2278,7 @@ CommentStmt:	COMMENT ON comment_type any_name IS comment_text
 
 comment_type:	COLUMN { $$ = COLUMN; }
 		| DATABASE { $$ = DATABASE; }
+		| SCHEMA { $$ = SCHEMA; }
 		| INDEX { $$ = INDEX; }
 		| SEQUENCE { $$ = SEQUENCE; }
 		| TABLE { $$ = TABLE; }
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index c3f89cb85dfe300e5ee339b680cc75929f8c47ef..f3b8f8a19b1b418d8164f12272d8f9c1d15f1aac 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -22,7 +22,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.259 2002/05/10 22:36:26 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.260 2002/05/13 17:45:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2686,7 +2686,6 @@ dumpNamespaces(Archive *fout, NamespaceInfo *nsinfo, int numNamespaces)
 					 nsinfo[i].usename, "SCHEMA", NULL,
 					 q->data, delq->data, NULL, NULL, NULL);
 
-#ifdef NOTYET					/* suppress till COMMENT ON SCHEMA works */
 		/*** Dump Schema Comments ***/
 		resetPQExpBuffer(q);
 		appendPQExpBuffer(q, "SCHEMA %s",
@@ -2694,7 +2693,6 @@ dumpNamespaces(Archive *fout, NamespaceInfo *nsinfo, int numNamespaces)
 		dumpComment(fout, q->data,
 					NULL, nsinfo[i].usename,
 					nsinfo[i].oid, "pg_namespace", 0, NULL);
-#endif
 	}
 
 	destroyPQExpBuffer(q);
@@ -3396,7 +3394,7 @@ dumpOneFunc(Archive *fout, FuncInfo *finfo)
 	/*** Dump Function Comments ***/
 
 	resetPQExpBuffer(q);
-	appendPQExpBuffer(q, "FUNCTION %s ", fn->data);
+	appendPQExpBuffer(q, "FUNCTION %s", fn->data);
 	dumpComment(fout, q->data,
 				finfo->pronamespace->nspname, finfo->usename,
 				finfo->oid, "pg_proc", 0, NULL);
@@ -3655,11 +3653,13 @@ dumpOneOpr(Archive *fout, OprInfo *oprinfo,
 				 q->data, delq->data,
 				 NULL, NULL, NULL);
 
-	/*
-	 * Note: no need to dump operator comment; we expect that the comment
-	 * is attached to the underlying function instead.  (If the function
-	 * isn't getting dumped ... you lose.)
-	 */
+	/*** Dump Operator Comments ***/
+
+	resetPQExpBuffer(q);
+	appendPQExpBuffer(q, "OPERATOR %s", oprid->data);
+	dumpComment(fout, q->data,
+				oprinfo->oprnamespace->nspname, oprinfo->usename,
+				oprinfo->oid, "pg_operator", 0, NULL);
 
 	PQclear(res);
 
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 2f163f63762cb877be2610ca005853a8582c03c6..297875e0becaab36eaca1e780a432bfc8c96dd78 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -3,7 +3,7 @@
  *
  * Copyright 2000 by PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.53 2002/04/25 02:56:56 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.54 2002/05/13 17:45:30 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "describe.h"
@@ -210,7 +210,8 @@ describeOperators(const char *name)
 			 "  CASE WHEN o.oprkind='l' THEN NULL ELSE format_type(o.oprleft, NULL) END AS \"%s\",\n"
 			 "  CASE WHEN o.oprkind='r' THEN NULL ELSE format_type(o.oprright, NULL) END AS \"%s\",\n"
 			 "  format_type(o.oprresult, NULL) AS \"%s\",\n"
-			 "  obj_description(o.oprcode, 'pg_proc') AS \"%s\"\n"
+			 "  coalesce(obj_description(o.oid, 'pg_operator'),"
+			 "           obj_description(o.oprcode, 'pg_proc')) AS \"%s\"\n"
 			 "FROM pg_operator o\n",
 			 _("Name"), _("Left arg type"), _("Right arg type"),
 			 _("Result type"), _("Description"));
@@ -359,10 +360,9 @@ objectDescription(const char *object)
 			 "  FROM pg_proc p\n"
 		"  WHERE (p.pronargs = 0 or oidvectortypes(p.proargtypes) <> '') AND NOT p.proisagg\n"
 
-	/* Operator descriptions (must get comment via associated function) */
+	/* Operator descriptions (only if operator has its own comment) */
 			 "UNION ALL\n"
-			 "  SELECT CAST(o.oprcode AS oid) as oid,\n"
-			 "  (SELECT oid FROM pg_class WHERE relname = 'pg_proc') as tableoid,\n"
+			 "  SELECT o.oid as oid, o.tableoid as tableoid,\n"
 	  "  CAST(o.oprname AS text) as name, CAST('%s' AS text) as object\n"
 			 "  FROM pg_operator o\n"