From eebfb9baa517b2680cf963b74e4c6a9fb12a1df2 Mon Sep 17 00:00:00 2001
From: Bruce Momjian <bruce@momjian.us>
Date: Tue, 30 Nov 1999 03:57:29 +0000
Subject: [PATCH] create/alter user extension
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This one should work much better than the one I sent in previously. The
functionality is the same, but the patch was missing one file resulting
in
the compilation failing. The docs also received a minor fix.

Peter Eisentraut                  Sernanders väg 10:115
---
 doc/src/sgml/ref/alter_user.sgml  |  31 ++++++--
 doc/src/sgml/ref/create_user.sgml |  74 +++++++++---------
 src/backend/commands/user.c       | 124 ++++++++++++++++++++----------
 src/backend/parser/gram.y         |  82 +++++++++++++++-----
 src/backend/parser/keywords.c     |   3 +-
 src/include/nodes/parsenodes.h    |   3 +-
 6 files changed, 210 insertions(+), 107 deletions(-)

diff --git a/doc/src/sgml/ref/alter_user.sgml b/doc/src/sgml/ref/alter_user.sgml
index 686684632a5..75bcca86546 100644
--- a/doc/src/sgml/ref/alter_user.sgml
+++ b/doc/src/sgml/ref/alter_user.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_user.sgml,v 1.8 1999/07/22 15:09:06 thomas Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_user.sgml,v 1.9 1999/11/30 03:57:22 momjian Exp $
 Postgres documentation
 -->
 
@@ -23,7 +23,10 @@ Postgres documentation
    <date>1999-07-20</date>
   </refsynopsisdivinfo>
   <synopsis>
-ALTER USER <replaceable class="PARAMETER">username</replaceable> [ WITH PASSWORD <replaceable class="PARAMETER">password</replaceable> ]
+ALTER USER <replaceable class="PARAMETER">username</replaceable>
+    [ WITH
+     [ SYSID <replaceable class="PARAMETER">uid</replaceable> ]
+     [ PASSWORD <replaceable class="PARAMETER">password</replaceable> ] ]
     [ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]
     [ IN GROUP <replaceable class="PARAMETER">groupname</replaceable> [, ...] ]
     [ VALID UNTIL '<replaceable class="PARAMETER">abstime</replaceable>' ]
@@ -62,6 +65,22 @@ ALTER USER <replaceable class="PARAMETER">username</replaceable> [ WITH PASSWORD
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><replaceable class="parameter">uid</replaceable></term>
+      <listitem>
+       <para>
+        The new <productname>PostgreSQL</productname> user id of the user.
+        Since this number is used as a key into the
+        <literal>pg_shadow</literal>/<literal>pg_user</literal> table
+        throughout the system catalogs, it is not recommended that you change
+        it unless the user in question does not own anything at all and/or
+        you really know what you are doing. Note that it is not necessary that
+        database and <acronym>UNIX</acronym> user ids match, but some people
+        choose to keep the numbers the same.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><replaceable class="PARAMETER"> groupname </replaceable></term>
       <listitem>
@@ -130,9 +149,7 @@ ERROR: alterUser: user "username" does not exist
   <para>
    <command>ALTER USER</command> is used to change the attributes of a user's
    <productname>Postgres</productname> account.
-   Please note that it is not possible
-   to alter a user's "<literal>usesysid</literal>" via the alter user
-   statement. Also, it is only possible for the
+   Also, it is only possible for the
    <productname>Postgres</productname>
    user or any user with read and modify permissions on
    <literal>pg_shadow</literal> to alter user passwords.
@@ -161,9 +178,7 @@ ERROR: alterUser: user "username" does not exist
     to create or remove a user account.
    </para>
    <para>
-    In the current release (v6.5), the IN GROUP clause is parsed
-    but has no affect.  When it is fully implemented, it is
-    intended to modify the pg_group relation.
+    The IN GROUP clause is not yet implemented.
    </para>
   </refsect2>
  </refsect1>
diff --git a/doc/src/sgml/ref/create_user.sgml b/doc/src/sgml/ref/create_user.sgml
index b7a55d05d48..9eccd3f7130 100644
--- a/doc/src/sgml/ref/create_user.sgml
+++ b/doc/src/sgml/ref/create_user.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_user.sgml,v 1.9 1999/10/07 16:40:36 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_user.sgml,v 1.10 1999/11/30 03:57:23 momjian Exp $
 Postgres documentation
 -->
 
@@ -23,8 +23,10 @@ Postgres documentation
    <date>1999-07-20</date>
   </refsynopsisdivinfo>
   <synopsis>
-CREATE USER<replaceable class="PARAMETER"> username</replaceable>
-    [ WITH PASSWORD <replaceable class="PARAMETER">password</replaceable> ]
+CREATE USER <replaceable class="PARAMETER">username</replaceable>
+    [ WITH
+     [ SYSID <replaceable class="PARAMETER">uid</replaceable> ]
+     [ PASSWORD <replaceable class="PARAMETER">password</replaceable> ] ]
     [ CREATEDB   | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]
     [ IN GROUP     <replaceable class="PARAMETER">groupname</replaceable> [, ...] ]
     [ VALID UNTIL  '<replaceable class="PARAMETER">abstime</replaceable>' ]
@@ -49,11 +51,28 @@ CREATE USER<replaceable class="PARAMETER"> username</replaceable>
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><replaceable class="parameter">uid</replaceable></term>
+      <listitem>
+       <para>
+        The <literal>SYSID</literal> clause can be used to choose
+        the <productname>PostgreSQL</productname> user id of the user
+        that is being created. It is not at all necessary that those
+        match the <acronym>UNIX</acronym> user ids, but some people
+        choose to keep the numbers the same.
+       </para>
+       <para>
+        If this is not specified, the highest assigned user id plus one
+        will be used as default.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><replaceable class="parameter">password</replaceable></term>
       <listitem>
        <para>
-	The WITH PASSWORD clause sets the user's password within
+	The PASSWORD clause sets the user's password within
 	the "<filename>pg_shadow</filename>" table. For this reason,
 	<filename>"pg_shadow</filename>" is no
 	longer accessible to the instance of
@@ -173,30 +192,9 @@ CREATE USER
   </title>
   <para>
    CREATE USER will add a new user to an instance of 
-   <productname>Postgres</productname>.
+   <productname>PostgreSQL</productname>.
   </para>
 
-  <para>
-   The new user will be given a <filename>usesysid</filename> of:
-
-   <programlisting>
-SELECT MAX(usesysid) + 1 FROM pg_shadow;
-   </programlisting>
-
-   This means that 
-   <productname>Postgres</productname> users' <filename>usesysid</filename>s will not
-   correspond to their operating
-   system(OS) user ids. The exception to this rule is
-   the <literal>postgres</literal> superuser, whose OS user id
-   is used as the
-   <filename>usesysid</filename> during the initdb process. 
-   If you still want the
-   OS user id and the <filename>usesysid</filename> to match
-   for any given user,
-   use the <application>createuser</application> script provided with
-   the <productname>Postgres</productname> distribution.
-  </para>
-  
   <refsect2 id="R2-SQL-CREATEUSER-3">
    <refsect2info>
     <date>1998-09-21</date>
@@ -216,19 +214,17 @@ SELECT MAX(usesysid) + 1 FROM pg_shadow;
     Refer to the <filename>pg_shadow</filename> table for further information.
    </para>
    <programlisting>
-   Table    = pg_shadow
-   +--------------------------+--------------------------+-------+
-   |          Field           |          Type            | Length|
-   +--------------------------+--------------------------+-------+
-   | usename                  | name                     |    32 |
-   | usesysid                 | int4                     |     4 |
-   | usecreatedb              | bool                     |     1 |
-   | usetrace                 | bool                     |     1 |
-   | usesuper                 | bool                     |     1 |
-   | usecatupd                | bool                     |     1 |
-   | passwd                   | text                     |   var |
-   | valuntil                 | abstime                  |     4 |
-   +--------------------------+--------------------------+-------+
+      Table "pg_shadow"
+  Attribute  |  Type   | Extra
+-------------+---------+-------
+ usename     | name    |
+ usesysid    | int4    |
+ usecreatedb | bool    |
+ usetrace    | bool    |
+ usesuper    | bool    |
+ usecatupd   | bool    |
+ passwd      | text    |
+ valuntil    | abstime |
    </programlisting>
   </refsect2>
  </refsect1>
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 27a11cbcaf1..044779b7ee2 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: user.c,v 1.38 1999/11/24 16:52:32 momjian Exp $
+ * $Id: user.c,v 1.39 1999/11/30 03:57:23 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -105,14 +105,15 @@ DefineUser(CreateUserStmt *stmt, CommandDest dest)
 	TupleDesc	pg_shadow_dsc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
-	Datum		datum;
-	bool		exists = false,
-				n,
+	bool		user_exists = false,
+                sysid_exists = false,
 				inblock,
+                havesysid,
 				havepassword,
 				havevaluntil;
 	int			max_id = -1;
 
+    havesysid    = stmt->sysid >= 0;
 	havepassword = stmt->password && stmt->password[0];
 	havevaluntil = stmt->validUntil && stmt->validUntil[0];
 
@@ -129,13 +130,13 @@ DefineUser(CreateUserStmt *stmt, CommandDest dest)
 	if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR | ACL_AP) != ACLCHECK_OK)
 	{
 		UserAbortTransactionBlock();
-		elog(ERROR, "defineUser: user \"%s\" does not have SELECT and INSERT privilege for \"%s\"",
+		elog(ERROR, "DefineUser: user \"%s\" does not have SELECT and INSERT privilege for \"%s\"",
 			 pg_shadow, ShadowRelationName);
 		return;
 	}
 
 	/*
-	 * Scan the pg_shadow relation to be certain the user doesn't already
+	 * 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 to be
 	 * sure of what the next usesysid should be, and we need to protect
 	 * our update of the flat password file.
@@ -144,25 +145,33 @@ DefineUser(CreateUserStmt *stmt, CommandDest dest)
 	pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
 
 	scan = heap_beginscan(pg_shadow_rel, false, SnapshotNow, 0, NULL);
-	while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+	while (!user_exists && !sysid_exists && HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
 	{
-		datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_shadow_dsc, &n);
-
-		if (!exists && !strncmp((char *) datum, stmt->user, strlen(stmt->user)))
-			exists = true;
-
-		datum = heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &n);
-		if ((int) datum > max_id)
-			max_id = (int) datum;
+        Datum		datum;
+        bool        null;
+
+		datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_shadow_dsc, &null);
+		user_exists = datum && !null && (strcmp((char *) datum, stmt->user) == 0);
+
+		datum = heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null);
+        if (havesysid) /* customized id wanted */
+            sysid_exists = datum && !null && ((int)datum == stmt->sysid);
+        else /* pick 1 + max */
+        {
+            if ((int) datum > max_id)
+                max_id = (int) datum;
+        }
 	}
 	heap_endscan(scan);
 
-	if (exists)
+	if (user_exists || sysid_exists)
 	{
 		heap_close(pg_shadow_rel, AccessExclusiveLock);
 		UserAbortTransactionBlock();
-		elog(ERROR,
-		 "defineUser: user \"%s\" has already been created", stmt->user);
+        if (user_exists)
+            elog(ERROR, "DefineUser: user name \"%s\" already exists", stmt->user);
+        else
+            elog(ERROR, "DefineUser: sysid %d is already assigned", stmt->sysid);
 		return;
 	}
 
@@ -184,7 +193,7 @@ DefineUser(CreateUserStmt *stmt, CommandDest dest)
 			 "values('%s',%d,'%c','f','%c','%c',%s%s%s,%s%s%s)",
 			 ShadowRelationName,
 			 stmt->user,
-			 max_id + 1,
+			 havesysid ? stmt->sysid : max_id + 1,
 			 (stmt->createdb && *stmt->createdb) ? 't' : 'f',
 			 (stmt->createuser && *stmt->createuser) ? 't' : 'f',
 			 ((stmt->createdb && *stmt->createdb) ||
@@ -234,6 +243,7 @@ AlterUser(AlterUserStmt *stmt, CommandDest dest)
 	TupleDesc	pg_shadow_dsc;
 	HeapTuple	tuple;
 	bool		inblock;
+    bool        comma = false;
 
 	if (stmt->password)
 		CheckPgUserAclNotNull();
@@ -248,7 +258,7 @@ AlterUser(AlterUserStmt *stmt, CommandDest dest)
 	if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
 	{
 		UserAbortTransactionBlock();
-		elog(ERROR, "alterUser: user \"%s\" does not have SELECT and UPDATE privilege for \"%s\"",
+		elog(ERROR, "AlterUser: user \"%s\" does not have SELECT and UPDATE privilege for \"%s\"",
 			 pg_shadow, ShadowRelationName);
 		return;
 	}
@@ -268,43 +278,79 @@ AlterUser(AlterUserStmt *stmt, CommandDest dest)
 	{
 		heap_close(pg_shadow_rel, AccessExclusiveLock);
 		UserAbortTransactionBlock();
-		elog(ERROR, "alterUser: user \"%s\" does not exist", stmt->user);
+		elog(ERROR, "AlterUser: user \"%s\" does not exist", stmt->user);
 	}
 
+    /* look for duplicate sysid */
+	tuple = SearchSysCacheTuple(USESYSID,
+								Int32GetDatum(stmt->sysid),
+								0, 0, 0);
+    if (HeapTupleIsValid(tuple))
+    {
+        Datum datum;
+        bool null;
+
+		datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_shadow_dsc, &null);
+        if (datum && !null && strcmp((char *) datum, stmt->user) != 0)
+        {
+            heap_close(pg_shadow_rel, AccessExclusiveLock);
+            UserAbortTransactionBlock();
+            elog(ERROR, "AlterUser: sysid %d is already assigned", stmt->sysid);
+        }
+    }
+
+
 	/*
 	 * Create the update statement to modify the user.
 	 *
 	 * XXX see diatribe in preceding routine.  This code is just as bogus.
 	 */
-	snprintf(sql, SQL_LENGTH, "update %s set", ShadowRelationName);
+	snprintf(sql, SQL_LENGTH, "update %s set ", ShadowRelationName);
 
 	if (stmt->password)
+    {
 		snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
-				 " passwd = '%s'", stmt->password);
+				 "passwd = '%s'", stmt->password);
+        comma = true;
+    }
+
+    if (stmt->sysid>=0)
+    {
+        if (comma)
+            strcat(sql, ", ");
+        snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
+                 "usesysid = %d", stmt->sysid);
+        comma = true;
+    }
 
 	if (stmt->createdb)
-	{
+    {
+        if (comma)
+            strcat(sql, ", ");
 		snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
-				 "%s usecreatedb='%s'",
-				 stmt->password ? "," : "",
-				 *stmt->createdb ? "t" : "f");
-	}
+				 "usecreatedb='%c'",
+				 *stmt->createdb ? 't' : 'f');
+        comma = true;
+    }
 
 	if (stmt->createuser)
-	{
+    {
+        if (comma)
+            strcat(sql, ", ");
 		snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
-				 "%s usesuper='%s'",
-				 (stmt->password || stmt->createdb) ? "," : "",
-				 *stmt->createuser ? "t" : "f");
-	}
+				 "usesuper='%c'",
+				 *stmt->createuser ? 't' : 'f');
+        comma = true;
+    }
 
 	if (stmt->validUntil)
-	{
+    {
+        if (comma)
+            strcat(sql, ", ");
 		snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
-				 "%s valuntil='%s'",
-		(stmt->password || stmt->createdb || stmt->createuser) ? "," : "",
+				 "valuntil='%s'",
 				 stmt->validUntil);
-	}
+    }
 
 	snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
 			 " where usename = '%s'",
@@ -362,7 +408,7 @@ RemoveUser(char *user, CommandDest dest)
 	if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
 	{
 		UserAbortTransactionBlock();
-		elog(ERROR, "removeUser: user \"%s\" does not have SELECT and DELETE privilege for \"%s\"",
+		elog(ERROR, "RemoveUser: user \"%s\" does not have SELECT and DELETE privilege for \"%s\"",
 			 pg_shadow, ShadowRelationName);
 	}
 
@@ -381,7 +427,7 @@ RemoveUser(char *user, CommandDest dest)
 	{
 		heap_close(pg_shadow_rel, AccessExclusiveLock);
 		UserAbortTransactionBlock();
-		elog(ERROR, "removeUser: user \"%s\" does not exist", user);
+		elog(ERROR, "RemoveUser: user \"%s\" does not exist", user);
 	}
 
 	usesysid = (int32) heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_dsc, &n);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5a291dc867f..c3d72896f65 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.115 1999/11/20 21:39:36 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.116 1999/11/30 03:57:24 momjian Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -137,6 +137,7 @@ static Node *doNegate(Node *n);
 
 %type <pboolean> user_createdb_clause, user_createuser_clause
 %type <str>		user_passwd_clause
+%type <ival>            sysid_clause
 %type <str>		user_valid_clause
 %type <list>	user_group_list, user_group_clause
 
@@ -295,6 +296,7 @@ static Node *doNegate(Node *n);
 		VALUES, VARCHAR, VARYING, VIEW,
 		WHEN, WHERE, WITH, WORK, YEAR_P, ZONE
 
+
 /* Keywords (in SQL3 reserved words) */
 %token	DEFERRABLE, DEFERRED,
 		IMMEDIATE, INITIALLY,
@@ -323,7 +325,7 @@ static Node *doNegate(Node *n);
 		NEW, NOCREATEDB, NOCREATEUSER, NONE, NOTHING, NOTIFY, NOTNULL,
 		OFFSET, OIDS, OPERATOR, PASSWORD, PROCEDURAL,
 		RENAME, RESET, RETURNS, ROW, RULE,
-		SEQUENCE, SERIAL, SETOF, SHARE, SHOW, START, STATEMENT, STDIN, STDOUT,
+		SEQUENCE, SERIAL, SETOF, SHARE, SHOW, START, STATEMENT, STDIN, STDOUT, SYSID,
 		TRUNCATE, TRUSTED, 
 		UNLISTEN, UNTIL, VACUUM, VALID, VERBOSE, VERSION
 
@@ -443,18 +445,34 @@ stmt :	  AddAttrStmt
  *
  *****************************************************************************/
 
-CreateUserStmt:  CREATE USER UserId user_passwd_clause user_createdb_clause
-			user_createuser_clause user_group_clause user_valid_clause
+CreateUserStmt:  CREATE USER UserId
+                 user_createdb_clause user_createuser_clause user_group_clause
+                 user_valid_clause
 				{
 					CreateUserStmt *n = makeNode(CreateUserStmt);
 					n->user = $3;
-					n->password = $4;
-					n->createdb = $5;
-					n->createuser = $6;
-					n->groupElts = $7;
-					n->validUntil = $8;
+                    n->sysid = -1;
+					n->password = NULL;
+					n->createdb = $4;
+					n->createuser = $5;
+					n->groupElts = $6;
+					n->validUntil = $7;
 					$$ = (Node *)n;
 				}
+                | CREATE USER UserId WITH sysid_clause user_passwd_clause
+                user_createdb_clause user_createuser_clause user_group_clause
+                user_valid_clause
+               {
+					CreateUserStmt *n = makeNode(CreateUserStmt);
+					n->user = $3;
+                    n->sysid = $5;
+					n->password = $6;
+					n->createdb = $7;
+					n->createuser = $8;
+					n->groupElts = $9;
+					n->validUntil = $10;
+					$$ = (Node *)n;
+               }                   
 		;
 
 /*****************************************************************************
@@ -464,16 +482,31 @@ CreateUserStmt:  CREATE USER UserId user_passwd_clause user_createdb_clause
  *
  *****************************************************************************/
 
-AlterUserStmt:  ALTER USER UserId user_passwd_clause user_createdb_clause
-			user_createuser_clause user_group_clause user_valid_clause
+AlterUserStmt:  ALTER USER UserId user_createdb_clause
+                user_createuser_clause user_group_clause user_valid_clause
 				{
 					AlterUserStmt *n = makeNode(AlterUserStmt);
 					n->user = $3;
-					n->password = $4;
-					n->createdb = $5;
-					n->createuser = $6;
-					n->groupElts = $7;
-					n->validUntil = $8;
+                    n->sysid = -1;
+					n->password = NULL;
+					n->createdb = $4;
+					n->createuser = $5;
+					n->groupElts = $6;
+					n->validUntil = $7;
+					$$ = (Node *)n;
+				}
+              | ALTER USER UserId WITH sysid_clause user_passwd_clause
+                user_createdb_clause
+                user_createuser_clause user_group_clause user_valid_clause
+				{
+					AlterUserStmt *n = makeNode(AlterUserStmt);
+					n->user = $3;
+                    n->sysid = $5;
+					n->password = $6;
+					n->createdb = $7;
+					n->createuser = $8;
+					n->groupElts = $9;
+					n->validUntil = $10;
 					$$ = (Node *)n;
 				}
 		;
@@ -493,10 +526,14 @@ DropUserStmt:  DROP USER UserId
 				}
 		;
 
-user_passwd_clause:  WITH PASSWORD UserId		{ $$ = $3; }
-			| /*EMPTY*/							{ $$ = NULL; }
+user_passwd_clause:  PASSWORD UserId		        { $$ = $2; }
+                        | /*EMPTY*/					{ $$ = NULL; }
 		;
 
+sysid_clause: SYSID Iconst                              { $$ = $2; }
+                        | /*EMPTY*/                     { $$ = -1; }
+        ;
+
 user_createdb_clause:  CREATEDB
 				{
 					bool*  b;
@@ -537,7 +574,13 @@ user_group_list:  user_group_list ',' UserId
 				}
 		;
 
-user_group_clause:  IN GROUP user_group_list	{ $$ = $3; }
+user_group_clause:  IN GROUP user_group_list
+                                {
+                                        /* the backend doesn't actually process this,
+                                         * so an error message is probably fairer */
+                                        yyerror("IN GROUP is not implemented");
+                                        /* $$ = $3; */
+                                }
 			| /*EMPTY*/							{ $$ = NULL; }
 		;
 
@@ -4764,6 +4807,7 @@ ColId:  IDENT							{ $$ = $1; }
 		| STATEMENT						{ $$ = "statement"; }
 		| STDIN							{ $$ = "stdin"; }
 		| STDOUT						{ $$ = "stdout"; }
+                | SYSID                                                 { $$ = "sysid"; }
 		| TIME							{ $$ = "time"; }
 		| TIMESTAMP						{ $$ = "timestamp"; }
 		| TIMEZONE_HOUR					{ $$ = "timezone_hour"; }
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index 392e7509c8e..3cb4e2787d5 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.63 1999/10/15 01:49:41 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.64 1999/11/30 03:57:26 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -219,6 +219,7 @@ static ScanKeyword ScanKeywords[] = {
 	{"stdin", STDIN},
 	{"stdout", STDOUT},
 	{"substring", SUBSTRING},
+    {"sysid", SYSID},
 	{"table", TABLE},
 	{"temp", TEMP},
 	{"temporary", TEMPORARY},
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index aac3ef7de53..04c8c9b5182 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.86 1999/10/26 03:12:39 momjian Exp $
+ * $Id: parsenodes.h,v 1.87 1999/11/30 03:57:29 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -237,6 +237,7 @@ typedef struct CreateUserStmt
 	NodeTag		type;
 	char	   *user;			/* PostgreSQL user login			  */
 	char	   *password;		/* PostgreSQL user password			  */
+    int         sysid;          /* PgSQL system id (-1 if don't care) */
 	bool	   *createdb;		/* Can the user create databases?	  */
 	bool	   *createuser;		/* Can this user create users?		  */
 	List	   *groupElts;		/* The groups the user is a member of */
-- 
GitLab