diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 9ebd6fa3a2dc69f6532f6f88ac4a9b8805b7006c..eb80acbd008396e13b71ef3d97d2112a04505e47 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.95 2002/04/04 04:25:45 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.96 2002/04/18 21:16:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -153,7 +153,7 @@ write_group_file(Relation urel, Relation grel)
 		datum = heap_getattr(tuple, Anum_pg_group_groname, dsc, &isnull);
 		if (isnull)
 			continue;			/* ignore NULL groupnames */
-		groname = (char *) DatumGetName(datum);
+		groname = NameStr(*DatumGetName(datum));
 
 		grolist_datum = heap_getattr(tuple, Anum_pg_group_grolist, dsc, &isnull);
 		/* Ignore NULL group lists */
@@ -293,7 +293,7 @@ write_user_file(Relation urel)
 		datum = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &isnull);
 		if (isnull)
 			continue;			/* ignore NULL usernames */
-		usename = (char *) DatumGetName(datum);
+		usename = NameStr(*DatumGetName(datum));
 
 		datum = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &isnull);
 
@@ -498,6 +498,10 @@ CreateUser(CreateUserStmt *stmt)
 	if (!superuser())
 		elog(ERROR, "CREATE USER: permission denied");
 
+	if (strcmp(stmt->user, "public") == 0)
+		elog(ERROR, "CREATE USER: user name \"%s\" is reserved",
+			 stmt->user);
+
 	/*
 	 * Scan the pg_shadow relation to be certain the user or id doesn't
 	 * already exist.  Note we secure exclusive lock, because we also need
@@ -518,7 +522,7 @@ CreateUser(CreateUserStmt *stmt)
 		datum = heap_getattr(tuple, Anum_pg_shadow_usename,
 							 pg_shadow_dsc, &null);
 		Assert(!null);
-		user_exists = (strcmp((char *) DatumGetName(datum), stmt->user) == 0);
+		user_exists = (strcmp(NameStr(*DatumGetName(datum)), stmt->user) == 0);
 
 		datum = heap_getattr(tuple, Anum_pg_shadow_usesysid,
 							 pg_shadow_dsc, &null);
@@ -1027,7 +1031,7 @@ DropUser(DropUserStmt *stmt)
 			datum = heap_getattr(tmp_tuple, Anum_pg_database_datname,
 								 pg_dsc, &null);
 			Assert(!null);
-			dbname = (char *) DatumGetName(datum);
+			dbname = NameStr(*DatumGetName(datum));
 			elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s",
 				 user, dbname,
 				 (length(stmt->users) > 1) ? " (no users removed)" : "");
@@ -1186,6 +1190,10 @@ CreateGroup(CreateGroupStmt *stmt)
 	if (!superuser())
 		elog(ERROR, "CREATE GROUP: permission denied");
 
+	if (strcmp(stmt->name, "public") == 0)
+		elog(ERROR, "CREATE GROUP: group name \"%s\" is reserved",
+			 stmt->name);
+
 	pg_group_rel = heap_openr(GroupRelationName, ExclusiveLock);
 	pg_group_dsc = RelationGetDescr(pg_group_rel);
 
@@ -1200,7 +1208,7 @@ CreateGroup(CreateGroupStmt *stmt)
 		datum = heap_getattr(tuple, Anum_pg_group_groname,
 							 pg_group_dsc, &null);
 		Assert(!null);
-		group_exists = (strcmp((char *) DatumGetName(datum), stmt->name) == 0);
+		group_exists = (strcmp(NameStr(*DatumGetName(datum)), stmt->name) == 0);
 
 		datum = heap_getattr(tuple, Anum_pg_group_grosysid,
 							 pg_group_dsc, &null);
@@ -1597,7 +1605,7 @@ DropGroup(DropGroupStmt *stmt)
 
 		datum = heap_getattr(tuple, Anum_pg_group_groname,
 							 pg_group_dsc, &null);
-		if (!null && strcmp((char *) DatumGetName(datum), stmt->name) == 0)
+		if (!null && strcmp(NameStr(*DatumGetName(datum)), stmt->name) == 0)
 		{
 			gro_exists = true;
 			simple_heap_delete(pg_group_rel, &tuple->t_self);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index e4ec05abf2464961bb96294b51cbde7e72ee73b7..1f03ce42dbe4508bd1ba7d9419fe004b2c999ffc 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.304 2002/04/18 20:01:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.305 2002/04/18 21:16:16 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -330,7 +330,7 @@ static bool set_name_needs_quotes(const char *name);
 		MATCH, MINUTE_P, MONTH_P, NAMES,
 		NATIONAL, NATURAL, NCHAR, NEXT, NO, NOT, NULLIF, NULL_P, NUMERIC,
 		OF, OLD, ON, ONLY, OPTION, OR, ORDER, OUTER_P, OVERLAPS,
-		PARTIAL, POSITION, PRECISION, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE, PUBLIC,
+		PARTIAL, POSITION, PRECISION, PRIMARY, PRIOR, PRIVILEGES, PROCEDURE,
 		READ, REFERENCES, RELATIVE, REVOKE, RIGHT, ROLLBACK,
 		SCHEMA, SCROLL, SECOND_P, SELECT, SESSION, SESSION_USER, SET, SOME, SUBSTRING,
 		TABLE, TEMPORARY, THEN, TIME, TIMESTAMP,
@@ -2532,25 +2532,26 @@ grantee_list: grantee					{ $$ = makeList1($1); }
 		| grantee_list ',' grantee		{ $$ = lappend($1, $3); }
 		;
 
-grantee:  PUBLIC
+grantee:  ColId
 				{
 					PrivGrantee *n = makeNode(PrivGrantee);
-					n->username = NULL;
+					/* This hack lets us avoid reserving PUBLIC as a keyword */
+					if (strcmp($1, "public") == 0)
+						n->username = NULL;
+					else
+						n->username = $1;
 					n->groupname = NULL;
 					$$ = (Node *)n;
 				}
 		| GROUP ColId
 				{
 					PrivGrantee *n = makeNode(PrivGrantee);
+					/* Treat GROUP PUBLIC as a synonym for PUBLIC */
+					if (strcmp($2, "public") == 0)
+						n->groupname = NULL;
+					else
+						n->groupname = $2;
 					n->username = NULL;
-					n->groupname = $2;
-					$$ = (Node *)n;
-				}
-		| ColId
-				{
-					PrivGrantee *n = makeNode(PrivGrantee);
-					n->username = $1;
-					n->groupname = NULL;
 					$$ = (Node *)n;
 				}
 		;
@@ -6112,7 +6113,7 @@ unreserved_keyword:
 		| STATISTICS					{ $$ = "statistics"; }
 		| STDIN							{ $$ = "stdin"; }
 		| STDOUT						{ $$ = "stdout"; }
-        | STORAGE                       { $$ = "storage"; }
+		| STORAGE						{ $$ = "storage"; }
 		| SYSID							{ $$ = "sysid"; }
 		| TEMP							{ $$ = "temp"; }
 		| TEMPLATE						{ $$ = "template"; }
@@ -6205,7 +6206,6 @@ func_name_keyword:
 		| NOTNULL						{ $$ = "notnull"; }
 		| OUTER_P						{ $$ = "outer"; }
 		| OVERLAPS						{ $$ = "overlaps"; }
-		| PUBLIC						{ $$ = "public"; }
 		| RIGHT							{ $$ = "right"; }
 		| VERBOSE						{ $$ = "verbose"; }
 		;
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 61e8744968efa273d159e783bd85411e5d3a3d77..ab08ae04f7efea4ca234b5cface58681271712ea 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.104 2002/03/19 02:18:19 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.105 2002/04/18 21:16:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -211,7 +211,6 @@ static ScanKeyword ScanKeywords[] = {
 	{"privileges", PRIVILEGES},
 	{"procedural", PROCEDURAL},
 	{"procedure", PROCEDURE},
-	{"public", PUBLIC},
 	{"read", READ},
 	{"references", REFERENCES},
 	{"reindex", REINDEX},