diff --git a/doc/src/sgml/ref/alter_user.sgml b/doc/src/sgml/ref/alter_user.sgml
index 8579b4f09a1f4ff75ef5995bc027888dfc9c398c..534d2a7eb2dcd196f76c06d0148e3e9265a9ad6f 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.13 2000/07/22 04:30:26 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_user.sgml,v 1.14 2001/07/10 22:09:27 tgl Exp $
 Postgres documentation
 -->
 
@@ -20,13 +20,17 @@ Postgres documentation
  </refnamediv>
  <refsynopsisdiv>
   <refsynopsisdivinfo>
-   <date>1999-07-20</date>
+   <date>2001-07-10</date>
   </refsynopsisdivinfo>
   <synopsis>
-ALTER USER <replaceable class="PARAMETER">username</replaceable>
-    [ WITH PASSWORD '<replaceable class="PARAMETER">password</replaceable>' ]
-    [ CREATEDB | NOCREATEDB ] [ CREATEUSER | NOCREATEUSER ]
-    [ VALID UNTIL '<replaceable class="PARAMETER">abstime</replaceable>' ]
+ALTER USER <replaceable class="PARAMETER">username</replaceable> [ [ WITH ] <replaceable class="PARAMETER">option</replaceable> [ ... ] ]
+
+where <replaceable class="PARAMETER">option</replaceable> can be:
+
+	  PASSWORD '<replaceable class="PARAMETER">password</replaceable>' 
+        | CREATEDB | NOCREATEDB
+        | CREATEUSER | NOCREATEUSER 
+        | VALID UNTIL '<replaceable class="PARAMETER">abstime</replaceable>'
   </synopsis>
 
   <refsect2 id="R2-SQL-ALTERUSER-1">
@@ -138,10 +142,19 @@ ERROR:  ALTER USER: user "username" does not exist
   </title>
   <para>
    <command>ALTER USER</command> is used to change the attributes of a user's
-   <productname>Postgres</productname> account. Only a database superuser
+   <productname>Postgres</productname> account.  Attributes not mentioned
+   in the command retain their previous settings.
+  </para>
+  <para>
+   Only a database superuser
    can change privileges and password expiration with this command. Ordinary
    users can only change their own password.
   </para>
+  <para>
+   <command>ALTER USER</command> cannot change a user's group memberships.
+   Use <xref linkend="SQL-ALTERGROUP" endterm="SQL-ALTERGROUP-title">
+   to do that.
+  </para>
   <para>
    Use <xref linkend="SQL-CREATEUSER" endterm="SQL-CREATEUSER-title">
    to create a new user and <xref linkend="SQL-DROPUSER"
diff --git a/doc/src/sgml/ref/create_user.sgml b/doc/src/sgml/ref/create_user.sgml
index df98765c7f5daa0c2319ee9c0251ff842ff63649..8c97dbcf86795ff0ed2740887a051c1cb1277535 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.16 2000/10/12 22:08:42 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_user.sgml,v 1.17 2001/07/10 22:09:27 tgl Exp $
 Postgres documentation
 -->
 
@@ -20,16 +20,19 @@ Postgres documentation
  </refnamediv>
  <refsynopsisdiv>
   <refsynopsisdivinfo>
-   <date>1999-07-20</date>
+   <date>2001-07-10</date>
   </refsynopsisdivinfo>
   <synopsis>
-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>' ]
+CREATE USER <replaceable class="PARAMETER">username</replaceable> [ [ WITH ] <replaceable class="PARAMETER">option</replaceable> [ ... ] ]
+
+where <replaceable class="PARAMETER">option</replaceable> can be:
+    
+	  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>' 
   </synopsis>
   
   <refsect2 id="R2-SQL-CREATEUSER-1">
@@ -115,6 +118,7 @@ CREATE USER <replaceable class="PARAMETER">username</replaceable>
       <listitem>
        <para>
 	A name of a group into which to insert the user as a new member.
+	Multiple group names may be listed.
        </para>
       </listitem>
      </varlistentry>
@@ -164,7 +168,7 @@ CREATE USER <replaceable class="PARAMETER">username</replaceable>
    Description
   </title>
   <para>
-   CREATE USER will add a new user to an instance of 
+   <command>CREATE USER</command> will add a new user to an instance of 
    <productname>Postgres</productname>. Refer to the administrator's
    guide for information about managing users and authentication.
    You must be a database superuser to use this command.
@@ -173,7 +177,8 @@ CREATE USER <replaceable class="PARAMETER">username</replaceable>
    Use <xref linkend="SQL-ALTERUSER" endterm="SQL-ALTERUSER-title">
    to change a user's password and privileges, and <xref linkend="SQL-DROPUSER"
    endterm="SQL-DROPUSER-title"> to remove a user.
-   Use <command>ALTER GROUP</command> to add or remove the user from other groups.
+   Use <xref linkend="SQL-ALTERGROUP" endterm="SQL-ALTERGROUP-title">
+   to add or remove the user from other groups.
    <productname>Postgres</productname>
    comes with a script <xref linkend="APP-CREATEUSER"
    endterm="APP-CREATEUSER-title">
diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml
index 7ce61b72cc84a00dbc3ca45d1cad8cb3a3c8b6b1..e8374725b34bd3cc8e98d224ca092a866bd6f5ec 100644
--- a/doc/src/sgml/ref/vacuum.sgml
+++ b/doc/src/sgml/ref/vacuum.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/vacuum.sgml,v 1.16 2001/05/25 15:45:31 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/vacuum.sgml,v 1.17 2001/07/10 22:09:28 tgl Exp $
 Postgres documentation
 -->
 
@@ -20,16 +20,16 @@ Postgres documentation
  </refnamediv>
  <refsynopsisdiv>
   <refsynopsisdivinfo>
-   <date>2001-05-04</date>
+   <date>2001-07-10</date>
   </refsynopsisdivinfo>
   <synopsis>
-VACUUM [ VERBOSE ] [ <replaceable class="PARAMETER">table</replaceable> ]
-VACUUM [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table</replaceable> [ (<replaceable class="PARAMETER">column</replaceable> [, ...] ) ] ]
+VACUUM [ FULL ] [ VERBOSE ] [ <replaceable class="PARAMETER">table</replaceable> ]
+VACUUM [ FULL ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table</replaceable> [ (<replaceable class="PARAMETER">column</replaceable> [, ...] ) ] ]
   </synopsis>
 
   <refsect2 id="R2-SQL-VACUUM-1">
    <refsect2info>
-    <date>1998-10-04</date>
+    <date>2001-07-10</date>
    </refsect2info>
    <title>
     Inputs
@@ -37,6 +37,15 @@ VACUUM [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table</replaceable>
 
    <para>
     <variablelist>
+     <varlistentry>
+      <term>FULL</term>
+      <listitem>
+       <para>
+        Selects <quote>full</quote> vacuum, which may reclaim more space,
+	but takes much longer and exclusively locks the table.
+       </para>
+      </listitem>
+     </varlistentry>
      <varlistentry>
       <term>VERBOSE</term>
       <listitem>
@@ -58,7 +67,8 @@ VACUUM [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table</replaceable>
       <term><replaceable class="PARAMETER">table</replaceable></term>
       <listitem>
        <para>
-	The name of a specific table to vacuum. Defaults to all tables.
+	The name of a specific table to vacuum. Defaults to all tables
+	in the current database.
        </para>
       </listitem>
      </varlistentry>
@@ -138,7 +148,7 @@ NOTICE:  Index <replaceable class="PARAMETER">index</replaceable>: Pages 28;
 
  <refsect1 id="R1-SQL-VACUUM-1">
   <refsect1info>
-   <date>1998-10-04</date>
+   <date>2001-07-10</date>
   </refsect1info>
   <title>
    Description
@@ -158,6 +168,16 @@ NOTICE:  Index <replaceable class="PARAMETER">index</replaceable>: Pages 28;
    only that table.
   </para>
 
+  <para>
+   Plain <command>VACUUM</command> simply reclaims space and makes it
+   available for re-use.  This form of the command can operate in parallel
+   with normal reading and writing of the table.  <command>VACUUM
+   FULL</command> does more extensive processing, including moving of tuples
+   across blocks to try to compact the table to the minimum number of disk
+   blocks.  This is much slower and requires an exclusive lock on each table
+   while it is being processed.
+  </para>
+
   <para>
    <command>VACUUM ANALYZE</command> performs a <command>VACUUM</command>
    and then an <command>ANALYZE</command> for each selected table.  This
@@ -168,7 +188,7 @@ NOTICE:  Index <replaceable class="PARAMETER">index</replaceable>: Pages 28;
 
   <refsect2 id="R2-SQL-VACUUM-3">
    <refsect2info>
-    <date>1998-10-04</date>
+    <date>2001-07-10</date>
    </refsect2info>
    <title>
     Notes
@@ -176,8 +196,8 @@ NOTICE:  Index <replaceable class="PARAMETER">index</replaceable>: Pages 28;
 
    <para>
     We recommend that active production databases be
-    <command>VACUUM</command>-ed nightly, in order to remove
-    expired rows. After copying a large table into
+    <command>VACUUM</command>-ed frequently (at least nightly), in order to
+    remove expired rows. After copying a large table into
     <productname>Postgres</productname> or after deleting a large number
     of records, it may be a good idea to issue a <command>VACUUM
     ANALYZE</command> command for the affected table. This will update the
@@ -187,6 +207,14 @@ NOTICE:  Index <replaceable class="PARAMETER">index</replaceable>: Pages 28;
     choices in planning user queries.
    </para>
 
+   <para>
+    The <option>FULL</option> option is not recommended for routine use,
+    but may be useful in special cases.  An example is when you have deleted
+    most of the rows in a table and would like the table to physically shrink
+    to occupy less disk space.  <command>VACUUM FULL</command> will usually
+    shrink the table more than a plain <command>VACUUM</command> would.
+   </para>
+
   </refsect2>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/vacuumdb.sgml b/doc/src/sgml/ref/vacuumdb.sgml
index 1efa7fce93bd47ce101ea43af7017f37272ff705..141d2dcd57bfac2d00567422ed42d390397583a5 100644
--- a/doc/src/sgml/ref/vacuumdb.sgml
+++ b/doc/src/sgml/ref/vacuumdb.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/vacuumdb.sgml,v 1.16 2001/03/17 16:27:31 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/vacuumdb.sgml,v 1.17 2001/07/10 22:09:28 tgl Exp $
 Postgres documentation
 -->
 
@@ -24,8 +24,9 @@ Postgres documentation
    <command>vacuumdb</command>
    <arg rep="repeat"><replaceable>connection-options</replaceable></arg>
    <arg><arg>-d</arg> <replaceable>dbname</replaceable></arg>
-   <group><arg>--analyze</arg><arg>-z</arg></group>
+   <group><arg>--full</arg><arg>-f</arg></group>
    <group><arg>--verbose</arg><arg>-v</arg></group>
+   <group><arg>--analyze</arg><arg>-z</arg></group>
    <arg>--table '<replaceable>table</replaceable>
     <arg>( <replaceable class="parameter">column</replaceable> [,...] )</arg>'
    </arg>
@@ -33,8 +34,9 @@ Postgres documentation
    <command>vacuumdb</command>
    <arg rep="repeat"><replaceable>connection-options</replaceable></arg>
    <group><arg>--all</arg><arg>-a</arg></group>
-   <group><arg>--analyze</arg><arg>-z</arg></group>
+   <group><arg>--full</arg><arg>-f</arg></group>
    <group><arg>--verbose</arg><arg>-v</arg></group>
+   <group><arg>--analyze</arg><arg>-z</arg></group>
   </cmdsynopsis>
 
   <refsect2 id="R2-APP-VACUUMDB-1">
@@ -56,21 +58,21 @@ Postgres documentation
      </varlistentry>
 
      <varlistentry>
-      <term>-z</term>
-      <term>--analyze</term>
+      <term>-a</term>
+      <term>--alldb</term>
       <listitem>
        <para>
-	Calculate statistics on the database for use by the optimizer.
+	Vacuum all databases.
        </para>
       </listitem>
      </varlistentry>
 
      <varlistentry>
-      <term>-a</term>
-      <term>--alldb</term>
+      <term>-f</term>
+      <term>--full</term>
       <listitem>
        <para>
-	Vacuum all databases.
+        Perform <quote>full</quote> vacuuming.
        </para>
       </listitem>
      </varlistentry>
@@ -85,6 +87,16 @@ Postgres documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term>-z</term>
+      <term>--analyze</term>
+      <listitem>
+       <para>
+	Calculate statistics for use by the optimizer.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term>-t <replaceable class="parameter">table</replaceable> [ (<replaceable class="parameter">column</replaceable> [,...]) ]</term>
       <term>--table <replaceable class="parameter">table</replaceable> [ (<replaceable class="parameter">column</replaceable> [,...]) ]</term>
@@ -257,7 +269,7 @@ Postgres documentation
 
   <informalexample>
    <para>
-    To analyze for the optimzer a database named
+    To clean and analyze for the optimizer a database named
     <literal>bigdb</literal>:
 <screen>
 <prompt>$ </prompt><userinput>vacuumdb --analyze bigdb</userinput>
@@ -267,9 +279,10 @@ Postgres documentation
 
   <informalexample>
    <para>
-    To analyze a single column <literal>bar</literal> in table
+    To clean a single table
     <literal>foo</literal> in a database named
-    <literal>xyzzy</literal> for the optimizer:
+    <literal>xyzzy</literal>, and analyze a single column
+    <literal>bar</literal> of the table for the optimizer:
 <screen>
 <prompt>$ </prompt><userinput>vacuumdb --analyze --verbose --table 'foo(bar)' xyzzy</userinput>
 </screen>
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 3d003616c09bb2ab137a9044b4b1e07f91de9a1c..e840b9109f8921e607f3f49424bd2a6229a0434a 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.77 2001/06/14 01:09:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.78 2001/07/10 22:09:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -197,14 +197,80 @@ CreateUser(CreateUserStmt *stmt)
 	char		new_record_nulls[Natts_pg_shadow];
 	bool		user_exists = false,
 				sysid_exists = false,
-				havesysid;
+				havesysid = false;
 	int			max_id = -1;
-	List	   *item;
-
-	havesysid = stmt->sysid > 0;
+	List	   *item, *option;
+	char	   *password = NULL;	/* PostgreSQL user password */
+	int			sysid = 0;			/* PgSQL system id (valid if havesysid) */
+	bool		createdb = false;	/* Can the user create databases? */
+	bool		createuser = false;	/* Can this user create users? */
+	List	   *groupElts = NIL;	/* The groups the user is a member of */
+	char	   *validUntil = NULL;	/* The time the login is valid until */
+    DefElem    *dpassword = NULL;
+    DefElem    *dsysid = NULL;
+    DefElem    *dcreatedb = NULL;
+    DefElem    *dcreateuser = NULL;
+    DefElem    *dgroupElts = NULL;
+    DefElem    *dvalidUntil = NULL;
+
+	/* Extract options from the statement node tree */
+	foreach(option, stmt->options)
+	{
+		DefElem *defel = (DefElem *) lfirst(option);
+
+        if (strcasecmp(defel->defname, "password") == 0) {
+            if (dpassword)
+                elog(ERROR, "CREATE USER: conflicting options");
+            dpassword = defel;
+        }
+        else if (strcasecmp(defel->defname, "sysid") == 0) {
+            if (dsysid)
+                elog(ERROR, "CREATE USER: conflicting options");
+            dsysid = defel;
+        }
+        else if (strcasecmp(defel->defname, "createdb") == 0) {
+            if (dcreatedb)
+                elog(ERROR, "CREATE USER: conflicting options");
+            dcreatedb = defel;
+        }
+        else if (strcasecmp(defel->defname, "createuser") == 0) {
+            if (dcreateuser)
+                elog(ERROR, "CREATE USER: conflicting options");
+            dcreateuser = defel;
+        } 
+        else if (strcasecmp(defel->defname, "groupElts") == 0) {
+            if (dgroupElts)
+                elog(ERROR, "CREATE USER: conflicting options");
+            dgroupElts = defel;
+        }
+        else if (strcasecmp(defel->defname, "validUntil") == 0) {
+            if (dvalidUntil)
+                elog(ERROR, "CREATE USER: conflicting options");
+            dvalidUntil = defel;
+        }
+        else 
+            elog(ERROR,"CREATE USER: option \"%s\" not recognized",
+                 defel->defname);
+    }
+
+    if (dcreatedb)
+		createdb = intVal(dcreatedb->arg) != 0;
+    if (dcreateuser)
+		createuser = intVal(dcreateuser->arg) != 0;
+    if (dsysid)
+	{
+		sysid = intVal(dsysid->arg);
+		havesysid = true;
+	}
+    if (dvalidUntil)
+		validUntil = strVal(dvalidUntil->arg);
+    if (dpassword)
+		password = strVal(dpassword->arg);
+    if (dgroupElts)
+		groupElts = (List *) dgroupElts->arg;
 
 	/* Check some permissions first */
-	if (stmt->password)
+	if (password)
 		CheckPgUserAclNotNull();
 
 	if (!superuser())
@@ -235,7 +301,7 @@ CreateUser(CreateUserStmt *stmt)
 							 pg_shadow_dsc, &null);
 		Assert(!null);
 		if (havesysid)			/* customized id wanted */
-			sysid_exists = (DatumGetInt32(datum) == stmt->sysid);
+			sysid_exists = (DatumGetInt32(datum) == sysid);
 		else
 		{
 			/* pick 1 + max */
@@ -249,30 +315,33 @@ CreateUser(CreateUserStmt *stmt)
 		elog(ERROR, "CREATE USER: user name \"%s\" already exists",
 			 stmt->user);
 	if (sysid_exists)
-		elog(ERROR, "CREATE USER: sysid %d is already assigned",
-			 stmt->sysid);
+		elog(ERROR, "CREATE USER: sysid %d is already assigned", sysid);
+
+	/* If no sysid given, use max existing id + 1 */
+	if (! havesysid)
+		sysid = max_id + 1;
 
 	/*
 	 * Build a tuple to insert
 	 */
-	new_record[Anum_pg_shadow_usename - 1] = DirectFunctionCall1(namein,
-											CStringGetDatum(stmt->user));
-	new_record[Anum_pg_shadow_usesysid - 1] = Int32GetDatum(havesysid ? stmt->sysid : max_id + 1);
+	new_record[Anum_pg_shadow_usename - 1] =
+		DirectFunctionCall1(namein, CStringGetDatum(stmt->user));
+	new_record[Anum_pg_shadow_usesysid - 1] = Int32GetDatum(sysid);
 
-	AssertState(BoolIsValid(stmt->createdb));
-	new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(stmt->createdb);
+	AssertState(BoolIsValid(createdb));
+	new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(createdb);
 	new_record[Anum_pg_shadow_usetrace - 1] = BoolGetDatum(false);
-	AssertState(BoolIsValid(stmt->createuser));
-	new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(stmt->createuser);
+	AssertState(BoolIsValid(createuser));
+	new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(createuser);
 	/* superuser gets catupd right by default */
-	new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(stmt->createuser);
+	new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(createuser);
 
-	if (stmt->password)
+	if (password)
 		new_record[Anum_pg_shadow_passwd - 1] =
-			DirectFunctionCall1(textin, CStringGetDatum(stmt->password));
-	if (stmt->validUntil)
+			DirectFunctionCall1(textin, CStringGetDatum(password));
+	if (validUntil)
 		new_record[Anum_pg_shadow_valuntil - 1] =
-			DirectFunctionCall1(nabstimein, CStringGetDatum(stmt->validUntil));
+			DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil));
 
 	new_record_nulls[Anum_pg_shadow_usename - 1] = ' ';
 	new_record_nulls[Anum_pg_shadow_usesysid - 1] = ' ';
@@ -282,8 +351,8 @@ CreateUser(CreateUserStmt *stmt)
 	new_record_nulls[Anum_pg_shadow_usesuper - 1] = ' ';
 	new_record_nulls[Anum_pg_shadow_usecatupd - 1] = ' ';
 
-	new_record_nulls[Anum_pg_shadow_passwd - 1] = stmt->password ? ' ' : 'n';
-	new_record_nulls[Anum_pg_shadow_valuntil - 1] = stmt->validUntil ? ' ' : 'n';
+	new_record_nulls[Anum_pg_shadow_passwd - 1] = password ? ' ' : 'n';
+	new_record_nulls[Anum_pg_shadow_valuntil - 1] = validUntil ? ' ' : 'n';
 
 	tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
 
@@ -310,15 +379,14 @@ CreateUser(CreateUserStmt *stmt)
 	 * Add the user to the groups specified. We'll just call the below
 	 * AlterGroup for this.
 	 */
-	foreach(item, stmt->groupElts)
+	foreach(item, groupElts)
 	{
 		AlterGroupStmt ags;
 
 		ags.name = strVal(lfirst(item));		/* the group name to add
 												 * this in */
 		ags.action = +1;
-		ags.listUsers = makeList1(makeInteger(havesysid ?
-											  stmt->sysid : max_id + 1));
+		ags.listUsers = makeList1(makeInteger(sysid));
 		AlterGroup(&ags, "CREATE USER");
 	}
 
@@ -348,21 +416,69 @@ AlterUser(AlterUserStmt *stmt)
 	HeapTuple	tuple,
 				new_tuple;
 	bool		null;
+	List       *option;
+	char	   *password = NULL;	/* PostgreSQL user password */
+	int			createdb = -1;		/* Can the user create databases? */
+	int			createuser = -1;	/* Can this user create users? */
+	char	   *validUntil = NULL;	/* The time the login is valid until */
+	DefElem    *dpassword = NULL;
+	DefElem    *dcreatedb = NULL;
+	DefElem    *dcreateuser = NULL;
+	DefElem    *dvalidUntil = NULL;
+
+	/* Extract options from the statement node tree */
+	foreach(option,stmt->options)
+	{
+		DefElem *defel = (DefElem *) lfirst(option);
 
-	if (stmt->password)
+		if (strcasecmp(defel->defname, "password") == 0) {
+			if (dpassword)
+				elog(ERROR, "ALTER USER: conflicting options");
+			dpassword = defel;
+		}
+		else if (strcasecmp(defel->defname, "createdb") == 0) {
+			if (dcreatedb)
+				elog(ERROR, "ALTER USER: conflicting options");
+			dcreatedb = defel;
+		}
+		else if (strcasecmp(defel->defname, "createuser") == 0) {
+			if (dcreateuser)
+				elog(ERROR, "ALTER USER: conflicting options");
+			dcreateuser = defel;
+		} 
+		else if (strcasecmp(defel->defname, "validUntil") == 0) {
+			if (dvalidUntil)
+				elog(ERROR, "ALTER USER: conflicting options");
+			dvalidUntil = defel;
+		}
+		else 
+			elog(ERROR,"ALTER USER: option \"%s\" not recognized",
+				 defel->defname);
+	}
+ 
+	if (dcreatedb)
+		createdb = intVal(dcreatedb->arg);
+	if (dcreateuser)
+		createuser = intVal(dcreateuser->arg);
+	if (dvalidUntil)
+		validUntil = strVal(dvalidUntil->arg);
+	if (dpassword)
+		password = strVal(dpassword->arg);
+ 
+ 	if (password)
 		CheckPgUserAclNotNull();
 
 	/* must be superuser or just want to change your own password */
 	if (!superuser() &&
-		!(stmt->createdb == 0 &&
-		  stmt->createuser == 0 &&
-		  !stmt->validUntil &&
-		  stmt->password &&
+		!(createdb < 0 &&
+		  createuser < 0 &&
+		  !validUntil &&
+		  password &&
 		  strcmp(GetUserName(GetUserId()), stmt->user) == 0))
 		elog(ERROR, "ALTER USER: permission denied");
 
 	/* changes to the flat password file cannot be rolled back */
-	if (IsTransactionBlock() && stmt->password)
+	if (IsTransactionBlock() && password)
 		elog(NOTICE, "ALTER USER: password changes cannot be rolled back");
 
 	/*
@@ -391,7 +507,7 @@ AlterUser(AlterUserStmt *stmt)
 	new_record_nulls[Anum_pg_shadow_usesysid - 1] = null ? 'n' : ' ';
 
 	/* createdb */
-	if (stmt->createdb == 0)
+	if (createdb < 0)
 	{
 		/* don't change */
 		new_record[Anum_pg_shadow_usecreatedb - 1] = heap_getattr(tuple, Anum_pg_shadow_usecreatedb, pg_shadow_dsc, &null);
@@ -399,7 +515,7 @@ AlterUser(AlterUserStmt *stmt)
 	}
 	else
 	{
-		new_record[Anum_pg_shadow_usecreatedb - 1] = (Datum) (stmt->createdb > 0 ? true : false);
+		new_record[Anum_pg_shadow_usecreatedb - 1] = BoolGetDatum(createdb > 0);
 		new_record_nulls[Anum_pg_shadow_usecreatedb - 1] = ' ';
 	}
 
@@ -408,7 +524,7 @@ AlterUser(AlterUserStmt *stmt)
 	new_record_nulls[Anum_pg_shadow_usetrace - 1] = null ? 'n' : ' ';
 
 	/* createuser (superuser) */
-	if (stmt->createuser == 0)
+	if (createuser < 0)
 	{
 		/* don't change */
 		new_record[Anum_pg_shadow_usesuper - 1] = heap_getattr(tuple, Anum_pg_shadow_usesuper, pg_shadow_dsc, &null);
@@ -416,14 +532,14 @@ AlterUser(AlterUserStmt *stmt)
 	}
 	else
 	{
-		new_record[Anum_pg_shadow_usesuper - 1] = (Datum) (stmt->createuser > 0 ? true : false);
+		new_record[Anum_pg_shadow_usesuper - 1] = BoolGetDatum(createuser > 0);
 		new_record_nulls[Anum_pg_shadow_usesuper - 1] = ' ';
 	}
 
 	/* catupd - set to false if someone's superuser priv is being yanked */
-	if (stmt->createuser < 0)
+	if (createuser == 0)
 	{
-		new_record[Anum_pg_shadow_usecatupd - 1] = (Datum) (false);
+		new_record[Anum_pg_shadow_usecatupd - 1] = BoolGetDatum(false);
 		new_record_nulls[Anum_pg_shadow_usecatupd - 1] = ' ';
 	}
 	else
@@ -434,10 +550,10 @@ AlterUser(AlterUserStmt *stmt)
 	}
 
 	/* password */
-	if (stmt->password)
+	if (password)
 	{
 		new_record[Anum_pg_shadow_passwd - 1] =
-			DirectFunctionCall1(textin, CStringGetDatum(stmt->password));
+			DirectFunctionCall1(textin, CStringGetDatum(password));
 		new_record_nulls[Anum_pg_shadow_passwd - 1] = ' ';
 	}
 	else
@@ -449,10 +565,10 @@ AlterUser(AlterUserStmt *stmt)
 	}
 
 	/* valid until */
-	if (stmt->validUntil)
+	if (validUntil)
 	{
 		new_record[Anum_pg_shadow_valuntil - 1] =
-			DirectFunctionCall1(nabstimein, CStringGetDatum(stmt->validUntil));
+			DirectFunctionCall1(nabstimein, CStringGetDatum(validUntil));
 		new_record_nulls[Anum_pg_shadow_valuntil - 1] = ' ';
 	}
 	else
@@ -761,9 +877,10 @@ CreateGroup(CreateGroupStmt *stmt)
 	else
 		max_id++;
 
-	new_record[Anum_pg_group_groname - 1] = (Datum) (stmt->name);
-	new_record[Anum_pg_group_grosysid - 1] = (Datum) (max_id);
-	new_record[Anum_pg_group_grolist - 1] = (Datum) userarray;
+	new_record[Anum_pg_group_groname - 1] =
+		DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
+	new_record[Anum_pg_group_grosysid - 1] = Int32GetDatum(max_id);
+	new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(userarray);
 
 	new_record_nulls[Anum_pg_group_groname - 1] = ' ';
 	new_record_nulls[Anum_pg_group_grosysid - 1] = ' ';
@@ -832,7 +949,7 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
 								 * create user */
 	{
 		Datum		new_record[Natts_pg_group];
-		char		new_record_nulls[Natts_pg_group] = {' ', ' ', ' '};
+		char		new_record_nulls[Natts_pg_group];
 		ArrayType  *newarray,
 				   *oldarray;
 		List	   *newlist = NULL,
@@ -914,9 +1031,13 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
 		/*
 		 * Form a tuple with the new array and write it back.
 		 */
-		new_record[Anum_pg_group_groname - 1] = (Datum) (stmt->name);
+		new_record[Anum_pg_group_groname - 1] =
+			DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
+		new_record_nulls[Anum_pg_group_groname - 1] = ' ';
 		new_record[Anum_pg_group_grosysid - 1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
+		new_record_nulls[Anum_pg_group_grosysid - 1] = null ? 'n' : ' ';
 		new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray);
+		new_record_nulls[Anum_pg_group_grolist - 1] = newarray ? ' ' : 'n';
 
 		tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
 		simple_heap_update(pg_group_rel, &group_tuple->t_self, tuple);
@@ -950,7 +1071,7 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
 		{
 			HeapTuple	tuple;
 			Datum		new_record[Natts_pg_group];
-			char		new_record_nulls[Natts_pg_group] = {' ', ' ', ' '};
+			char		new_record_nulls[Natts_pg_group];
 			ArrayType  *oldarray,
 					   *newarray;
 			List	   *newlist = NULL,
@@ -1014,9 +1135,13 @@ AlterGroup(AlterGroupStmt *stmt, const char *tag)
 			/*
 			 * Insert the new tuple with the updated user list
 			 */
-			new_record[Anum_pg_group_groname - 1] = (Datum) (stmt->name);
+			new_record[Anum_pg_group_groname - 1] =
+				DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
+			new_record_nulls[Anum_pg_group_groname - 1] = ' ';
 			new_record[Anum_pg_group_grosysid - 1] = heap_getattr(group_tuple, Anum_pg_group_grosysid, pg_group_dsc, &null);
+			new_record_nulls[Anum_pg_group_grosysid - 1] = null ? 'n' : ' ';
 			new_record[Anum_pg_group_grolist - 1] = PointerGetDatum(newarray);
+			new_record_nulls[Anum_pg_group_grolist - 1] = newarray ? ' ' : 'n';
 
 			tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
 			simple_heap_update(pg_group_rel, &group_tuple->t_self, tuple);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6cf5b35d26664fddc6a9551986740ed4d39dcfb0..1768b881750a31c2a27950e6c81209809d547f9e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.145 2001/06/19 22:39:11 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.146 2001/07/10 22:09:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2258,6 +2258,7 @@ _copyVacuumStmt(VacuumStmt *from)
 	VacuumStmt *newnode = makeNode(VacuumStmt);
 
 	newnode->vacuum = from->vacuum;
+	newnode->full = from->full;
 	newnode->analyze = from->analyze;
 	newnode->verbose = from->verbose;
 	if (from->vacrel)
@@ -2404,14 +2405,7 @@ _copyCreateUserStmt(CreateUserStmt *from)
 
 	if (from->user)
 		newnode->user = pstrdup(from->user);
-	if (from->password)
-		newnode->password = pstrdup(from->password);
-	newnode->sysid = from->sysid;
-	newnode->createdb = from->createdb;
-	newnode->createuser = from->createuser;
-	Node_Copy(from, newnode, groupElts);
-	if (from->validUntil)
-		newnode->validUntil = pstrdup(from->validUntil);
+	Node_Copy(from, newnode, options);
 
 	return newnode;
 }
@@ -2423,12 +2417,7 @@ _copyAlterUserStmt(AlterUserStmt *from)
 
 	if (from->user)
 		newnode->user = pstrdup(from->user);
-	if (from->password)
-		newnode->password = pstrdup(from->password);
-	newnode->createdb = from->createdb;
-	newnode->createuser = from->createuser;
-	if (from->validUntil)
-		newnode->validUntil = pstrdup(from->validUntil);
+	Node_Copy(from, newnode, options);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index b12b4c29127e664840f6d1e57a87a71de71d1e4d..b6a34e5f2f5fc27584ce66c6e12039018a1af291 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -20,7 +20,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.93 2001/06/19 22:39:11 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.94 2001/07/10 22:09:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1125,6 +1125,8 @@ _equalVacuumStmt(VacuumStmt *a, VacuumStmt *b)
 {
 	if (a->vacuum != b->vacuum)
 		return false;
+	if (a->full != b->full)
+		return false;
 	if (a->analyze != b->analyze)
 		return false;
 	if (a->verbose != b->verbose)
@@ -1265,17 +1267,7 @@ _equalCreateUserStmt(CreateUserStmt *a, CreateUserStmt *b)
 {
 	if (!equalstr(a->user, b->user))
 		return false;
-	if (!equalstr(a->password, b->password))
-		return false;
-	if (a->sysid != b->sysid)
-		return false;
-	if (a->createdb != b->createdb)
-		return false;
-	if (a->createuser != b->createuser)
-		return false;
-	if (!equal(a->groupElts, b->groupElts))
-		return false;
-	if (!equalstr(a->validUntil, b->validUntil))
+	if (!equal(a->options, b->options))
 		return false;
 
 	return true;
@@ -1286,13 +1278,7 @@ _equalAlterUserStmt(AlterUserStmt *a, AlterUserStmt *b)
 {
 	if (!equalstr(a->user, b->user))
 		return false;
-	if (!equalstr(a->password, b->password))
-		return false;
-	if (a->createdb != b->createdb)
-		return false;
-	if (a->createuser != b->createuser)
-		return false;
-	if (!equalstr(a->validUntil, b->validUntil))
+	if (!equal(a->options, b->options))
 		return false;
 
 	return true;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7e6f782984e53657fe8af0b2c478e64f71639407..20784ac1705f2d4add06ce800bca0364ac77071e 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.234 2001/07/09 22:18:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.235 2001/07/10 22:09:28 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -155,11 +155,10 @@ static void doNegateFloat(Value *v);
 %type <ival>	opt_lock, lock_type
 %type <boolean>	opt_force
 
-%type <ival>    user_createdb_clause, user_createuser_clause
-%type <str>		user_passwd_clause
-%type <ival>            sysid_clause
-%type <str>		user_valid_clause
-%type <list>	user_list, user_group_clause, users_in_new_group_clause
+%type <list>	user_list, users_in_new_group_clause
+
+%type <list>	OptUserList
+%type <defelt>	OptUserElem
 
 %type <boolean>	TriggerActionTime, TriggerForSpec, PLangTrusted, opt_procedural
 
@@ -212,7 +211,8 @@ static void doNegateFloat(Value *v);
 %type <node>	substr_from, substr_for
 
 %type <boolean>	opt_binary, opt_using, opt_instead, opt_cursor
-%type <boolean>	opt_with_copy, index_opt_unique, opt_verbose, analyze_keyword
+%type <boolean>	opt_with_copy, index_opt_unique, opt_verbose, opt_full
+%type <boolean>	analyze_keyword
 
 %type <ival>	copy_dirn, direction, reindex_type, drop_type,
 		opt_column, event, comment_type, comment_cl,
@@ -488,32 +488,18 @@ stmt :	AlterSchemaStmt
  *
  *****************************************************************************/
 
-CreateUserStmt:  CREATE USER UserId
-                 user_createdb_clause user_createuser_clause user_group_clause
-                 user_valid_clause
+CreateUserStmt:  CREATE USER UserId OptUserList 
 				{
 					CreateUserStmt *n = makeNode(CreateUserStmt);
 					n->user = $3;
-                    n->sysid = -1;
-					n->password = NULL;
-					n->createdb = $4 == +1 ? TRUE : FALSE;
-					n->createuser = $5 == +1 ? TRUE : FALSE;
-					n->groupElts = $6;
-					n->validUntil = $7;
+					n->options = $4;
 					$$ = (Node *)n;
 				}
-                | CREATE USER UserId WITH sysid_clause user_passwd_clause
-                user_createdb_clause user_createuser_clause user_group_clause
-                user_valid_clause
+                | CREATE USER UserId WITH OptUserList
                {
 					CreateUserStmt *n = makeNode(CreateUserStmt);
 					n->user = $3;
-                    n->sysid = $5;
-					n->password = $6;
-					n->createdb = $7 == +1 ? TRUE : FALSE;
-					n->createuser = $8 == +1 ? TRUE : FALSE;
-					n->groupElts = $9;
-					n->validUntil = $10;
+					n->options = $5;
 					$$ = (Node *)n;
                }                   
 		;
@@ -525,27 +511,18 @@ CreateUserStmt:  CREATE USER UserId
  *
  *****************************************************************************/
 
-AlterUserStmt:  ALTER USER UserId user_createdb_clause
-				user_createuser_clause user_valid_clause
+AlterUserStmt:  ALTER USER UserId OptUserList
 				{
 					AlterUserStmt *n = makeNode(AlterUserStmt);
 					n->user = $3;
-					n->password = NULL;
-					n->createdb = $4;
-					n->createuser = $5;
-					n->validUntil = $6;
+					n->options = $4;
 					$$ = (Node *)n;
 				}
-			| ALTER USER UserId WITH PASSWORD Sconst
-			  user_createdb_clause
-			  user_createuser_clause user_valid_clause
+			| ALTER USER UserId WITH OptUserList
 				{
 					AlterUserStmt *n = makeNode(AlterUserStmt);
 					n->user = $3;
-					n->password = $6;
-					n->createdb = $7;
-					n->createuser = $8;
-					n->validUntil = $9;
+					n->options = $5;
 					$$ = (Node *)n;
 				}
 		;
@@ -565,28 +542,62 @@ DropUserStmt:  DROP USER user_list
 				}
 		;
 
-user_passwd_clause:  PASSWORD Sconst			{ $$ = $2; }
-			| /*EMPTY*/							{ $$ = NULL; }
-		;
-
-sysid_clause: SYSID Iconst
-				{
-					if ($2 <= 0)
-						elog(ERROR, "sysid must be positive");
-					$$ = $2;
+/*
+ * Options for CREATE USER and ALTER USER
+ */
+OptUserList: OptUserList OptUserElem		{ $$ = lappend($1, $2); }
+			| /* EMPTY */					{ $$ = NIL; }
+		;
+
+OptUserElem:  PASSWORD Sconst
+                { 
+				  $$ = makeNode(DefElem);
+				  $$->defname = "password";
+				  $$->arg = (Node *)makeString($2);
+				}
+              | SYSID Iconst
+				{
+				  $$ = makeNode(DefElem);
+				  $$->defname = "sysid";
+				  $$->arg = (Node *)makeInteger($2);
+				}
+              | CREATEDB
+                { 
+				  $$ = makeNode(DefElem);
+				  $$->defname = "createdb";
+				  $$->arg = (Node *)makeInteger(TRUE);
+				}
+              | NOCREATEDB
+                { 
+				  $$ = makeNode(DefElem);
+				  $$->defname = "createdb";
+				  $$->arg = (Node *)makeInteger(FALSE);
+				}
+              | CREATEUSER
+                { 
+				  $$ = makeNode(DefElem);
+				  $$->defname = "createuser";
+				  $$->arg = (Node *)makeInteger(TRUE);
+				}
+              | NOCREATEUSER
+                { 
+				  $$ = makeNode(DefElem);
+				  $$->defname = "createuser";
+				  $$->arg = (Node *)makeInteger(FALSE);
+				}
+              | IN GROUP user_list
+                { 
+				  $$ = makeNode(DefElem);
+				  $$->defname = "groupElts";
+				  $$->arg = (Node *)$3;
+				}
+              | VALID UNTIL Sconst
+                { 
+				  $$ = makeNode(DefElem);
+				  $$->defname = "validUntil";
+				  $$->arg = (Node *)makeString($3);
 				}
-			| /*EMPTY*/							{ $$ = -1; }
-		;
-
-user_createdb_clause:  CREATEDB					{ $$ = +1; }
-			| NOCREATEDB						{ $$ = -1; }
-			| /*EMPTY*/							{ $$ = 0; }
-		;
-
-user_createuser_clause:  CREATEUSER				{ $$ = +1; }
-			| NOCREATEUSER						{ $$ = -1; }
-			| /*EMPTY*/							{ $$ = 0; }
-		;
+        ;
 
 user_list:  user_list ',' UserId
 				{
@@ -598,13 +609,6 @@ user_list:  user_list ',' UserId
 				}
 		;
 
-user_group_clause:  IN GROUP user_list			{ $$ = $3; }
-			| /*EMPTY*/							{ $$ = NULL; }
-		;
-
-user_valid_clause:  VALID UNTIL SCONST			{ $$ = $3; }
-			| /*EMPTY*/							{ $$ = NULL; }
-		;
 
 
 /*****************************************************************************
@@ -619,21 +623,29 @@ CreateGroupStmt:  CREATE GROUP UserId
 					CreateGroupStmt *n = makeNode(CreateGroupStmt);
 					n->name = $3;
 					n->sysid = -1;
-					n->initUsers = NULL;
+					n->initUsers = NIL;
 					$$ = (Node *)n;
 				}
-			| CREATE GROUP UserId WITH sysid_clause users_in_new_group_clause
+			| CREATE GROUP UserId WITH users_in_new_group_clause
 				{
 					CreateGroupStmt *n = makeNode(CreateGroupStmt);
 					n->name = $3;
-					n->sysid = $5;
-					n->initUsers = $6;
+					n->sysid = -1;
+					n->initUsers = $5;
+					$$ = (Node *)n;
+				}
+			| CREATE GROUP UserId WITH SYSID Iconst users_in_new_group_clause
+				{
+					CreateGroupStmt *n = makeNode(CreateGroupStmt);
+					n->name = $3;
+					n->sysid = $6;
+					n->initUsers = $7;
 					$$ = (Node *)n;
 				}
 		;
 
 users_in_new_group_clause:  USER user_list		{ $$ = $2; }
-			| /* EMPTY */						{ $$ = NULL; }
+			| /* EMPTY */						{ $$ = NIL; }
 		;                         
 
 /*****************************************************************************
@@ -3073,31 +3085,34 @@ ClusterStmt:  CLUSTER index_name ON relation_name
  *
  *****************************************************************************/
 
-VacuumStmt:  VACUUM opt_verbose
+VacuumStmt:  VACUUM opt_full opt_verbose
 				{
 					VacuumStmt *n = makeNode(VacuumStmt);
 					n->vacuum = true;
 					n->analyze = false;
-					n->verbose = $2;
+					n->full = $2;
+					n->verbose = $3;
 					n->vacrel = NULL;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
 				}
-		| VACUUM opt_verbose relation_name
+		| VACUUM opt_full opt_verbose relation_name
 				{
 					VacuumStmt *n = makeNode(VacuumStmt);
 					n->vacuum = true;
 					n->analyze = false;
-					n->verbose = $2;
-					n->vacrel = $3;
+					n->full = $2;
+					n->verbose = $3;
+					n->vacrel = $4;
 					n->va_cols = NIL;
 					$$ = (Node *)n;
 				}
-		| VACUUM opt_verbose AnalyzeStmt
+		| VACUUM opt_full opt_verbose AnalyzeStmt
 				{
-					VacuumStmt *n = (VacuumStmt *) $3;
+					VacuumStmt *n = (VacuumStmt *) $4;
 					n->vacuum = true;
-					n->verbose |= $2;
+					n->full = $2;
+					n->verbose |= $3;
 					$$ = (Node *)n;
 				}
 		;
@@ -3107,6 +3122,7 @@ AnalyzeStmt:  analyze_keyword opt_verbose
 					VacuumStmt *n = makeNode(VacuumStmt);
 					n->vacuum = false;
 					n->analyze = true;
+					n->full = false;
 					n->verbose = $2;
 					n->vacrel = NULL;
 					n->va_cols = NIL;
@@ -3117,6 +3133,7 @@ AnalyzeStmt:  analyze_keyword opt_verbose
 					VacuumStmt *n = makeNode(VacuumStmt);
 					n->vacuum = false;
 					n->analyze = true;
+					n->full = false;
 					n->verbose = $2;
 					n->vacrel = $3;
 					n->va_cols = $4;
@@ -3132,6 +3149,10 @@ opt_verbose:  VERBOSE							{ $$ = TRUE; }
 		| /*EMPTY*/								{ $$ = FALSE; }
 		;
 
+opt_full:  FULL									{ $$ = TRUE; }
+		| /*EMPTY*/								{ $$ = FALSE; }
+		;
+
 opt_name_list:  '(' name_list ')'				{ $$ = $2; }
 		| /*EMPTY*/								{ $$ = NIL; }
 		;
diff --git a/src/bin/scripts/vacuumdb b/src/bin/scripts/vacuumdb
index 214c995ee25e73339d7fa76d512c3ba35591a6d1..4245279275cecdf7791c5d0ed294d72dc7370c3b 100644
--- a/src/bin/scripts/vacuumdb
+++ b/src/bin/scripts/vacuumdb
@@ -12,7 +12,7 @@
 #
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/bin/scripts/Attic/vacuumdb,v 1.16 2001/02/18 18:34:02 momjian Exp $
+#    $Header: /cvsroot/pgsql/src/bin/scripts/Attic/vacuumdb,v 1.17 2001/07/10 22:09:29 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -20,6 +20,7 @@ CMDNAME=`basename $0`
 PATHNAME=`echo $0 | sed "s,$CMDNAME\$,,"`
 
 PSQLOPT=
+full=
 verbose=
 analyze=
 table=
@@ -97,6 +98,9 @@ do
         --table=*)
                 table=`echo $1 | sed 's/^--table=//'`
                 ;;
+	--full|-f)
+		full="FULL"
+		;;
 	--verbose|-v)
 		verbose="VERBOSE"
 		;;
@@ -126,9 +130,10 @@ if [ "$usage" ]; then
 	echo "  -W, --password                  Prompt for password"
 	echo "  -d, --dbname=DBNAME             Database to vacuum"
 	echo "  -a, --all                       Vacuum all databases"
-	echo "  -z, --analyze                   Update optimizer hints"
 	echo "  -t, --table='TABLE[(columns)]'  Vacuum specific table only"
+	echo "  -f, --full                      Do full vacuuming"
 	echo "  -v, --verbose                   Write a lot of output"
+	echo "  -z, --analyze                   Update optimizer hints"
 	echo "  -e, --echo                      Show the command being sent to the backend"
         echo "  -q, --quiet                     Don't write any output"
         echo
@@ -154,7 +159,7 @@ fi
 for db in $dbname
 do
         [ "$alldb" -a "$quiet" -ne 1 ] && echo "Vacuuming $db"
-	${PATHNAME}psql $PSQLOPT $ECHOOPT -c "VACUUM $verbose $analyze $table" -d $db
+	${PATHNAME}psql $PSQLOPT $ECHOOPT -c "VACUUM $full $verbose $analyze $table" -d $db
 	if [ $? -ne 0 ]; then
 	    echo "$CMDNAME: vacuum $table $db failed" 1>&2
 	    exit 1
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 42b72e8074afb37dcbc23d395f2bfd344ae02ed3..0e3bd2e4608070bce50a55e9d624cef1b26fb3e3 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.133 2001/06/23 00:07:34 momjian Exp $
+ * $Id: parsenodes.h,v 1.134 2001/07/10 22:09:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -339,23 +339,15 @@ typedef struct DropPLangStmt
 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 */
-	char	   *validUntil;		/* The time the login is valid until  */
+	char	   *user;			/* PostgreSQL user login name */
+	List	   *options;		/* List of DefElem nodes */
 } CreateUserStmt;
 
 typedef struct AlterUserStmt
 {
 	NodeTag		type;
-	char	   *user;			/* PostgreSQL user login			  */
-	char	   *password;		/* PostgreSQL user password			  */
-	int			createdb;		/* Can the user create databases?	  */
-	int			createuser;		/* Can this user create users?		  */
-	char	   *validUntil;		/* The time the login is valid until  */
+	char	   *user;			/* PostgreSQL user login name */
+	List	   *options;		/* List of DefElem nodes */
 } AlterUserStmt;
 
 typedef struct DropUserStmt
@@ -715,6 +707,7 @@ typedef struct VacuumStmt
 {
 	NodeTag		type;
 	bool		vacuum;			/* do VACUUM step */
+	bool		full;			/* do FULL (non-concurrent) vacuum */
 	bool		analyze;		/* do ANALYZE step */
 	bool		verbose;		/* print progress info */
 	char	   *vacrel;			/* name of single table to process, or NULL */
diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y
index e0626025d8f825355e00b213d06b9aae7a6967e1..6b93e767e3e7da96319bec01fb34544b1db7e5df 100644
--- a/src/interfaces/ecpg/preproc/preproc.y
+++ b/src/interfaces/ecpg/preproc/preproc.y
@@ -301,7 +301,8 @@ make_name(void)
 %type  <str>    NotifyStmt columnElem copy_dirn UnlistenStmt copy_null
 %type  <str>    copy_delimiter ListenStmt CopyStmt copy_file_name opt_binary
 %type  <str>    opt_with_copy FetchStmt direction fetch_how_many from_in
-%type  <str>    ClosePortalStmt DropStmt VacuumStmt AnalyzeStmt opt_verbose func_arg
+%type  <str>    ClosePortalStmt DropStmt VacuumStmt AnalyzeStmt opt_verbose
+%type  <str>    opt_full func_arg
 %type  <str>    analyze_keyword opt_name_list ExplainStmt index_params
 %type  <str>    index_list func_index index_elem opt_class access_method_clause
 %type  <str>    index_opt_unique IndexStmt func_return ConstInterval
@@ -309,14 +310,13 @@ make_name(void)
 %type  <str>    def_elem def_list definition DefineStmt select_with_parens
 %type  <str>    opt_instead event event_object RuleActionList opt_using
 %type  <str>	RuleActionStmtOrEmpty RuleActionMulti func_as reindex_type
-%type  <str>    RuleStmt opt_column opt_name oper_argtypes sysid_clause
+%type  <str>    RuleStmt opt_column opt_name oper_argtypes
 %type  <str>    MathOp RemoveFuncStmt aggr_argtype for_update_clause
 %type  <str>    RemoveAggrStmt ExtendStmt opt_procedural select_no_parens
-%type  <str>    RemoveOperStmt RenameStmt all_Op user_valid_clause
+%type  <str>    RemoveOperStmt RenameStmt all_Op
 %type  <str>    VariableSetStmt var_value zone_value VariableShowStmt
 %type  <str>    VariableResetStmt AlterTableStmt DropUserStmt from_list
-%type  <str>    user_passwd_clause user_createdb_clause opt_trans
-%type  <str>    user_createuser_clause user_list user_group_clause
+%type  <str>    opt_trans user_list OptUserList OptUserElem
 %type  <str>    CreateUserStmt AlterUserStmt CreateSeqStmt OptSeqList
 %type  <str>    OptSeqElem TriggerForSpec TriggerForOpt TriggerForType
 %type  <str>	DropTrigStmt TriggerOneEvent TriggerEvents RuleActionStmt
@@ -593,17 +593,13 @@ stmt:  AlterSchemaStmt 			{ output_statement($1, 0, NULL, connection); }
  *
  *****************************************************************************/
 
-CreateUserStmt: CREATE USER UserId
-		user_createdb_clause user_createuser_clause user_group_clause
-		user_valid_clause
-				{
-					$$ = cat_str(6, make_str("create user"), $3, $4, $5, $6, $7);
-				}
-		| CREATE USER UserId WITH sysid_clause user_passwd_clause
-		user_createdb_clause user_createuser_clause user_group_clause
-		user_valid_clause
+CreateUserStmt: CREATE USER UserId OptUserList
+		{
+			$$ = cat_str(3, make_str("create user"), $3, $4);
+		}
+              | CREATE USER UserId WITH OptUserList
 		{
-					$$ = cat_str(9, make_str("create user"), $3, make_str("with"), $5, $6, $7, $8, $9, $10);
+			$$ = cat_str(4, make_str("create user"), $3, make_str("with"), $5);
 		}
 		;
 
@@ -614,17 +610,14 @@ CreateUserStmt: CREATE USER UserId
  *
  *****************************************************************************/
 
-AlterUserStmt:  ALTER USER UserId user_createdb_clause
-				user_createuser_clause user_valid_clause
-				{
-					$$ = cat_str(5, make_str("alter user"), $3, $4, $5, $6);
-				}
-			| ALTER USER UserId WITH PASSWORD StringConst
-				user_createdb_clause
-				user_createuser_clause user_valid_clause
-				{
-					$$ = cat_str(7, make_str("alter user"), $3, make_str("with password"), $6, $7, $8, $9);
-				}
+AlterUserStmt: ALTER USER UserId OptUserList
+		{
+			$$ = cat_str(3, make_str("alter user"), $3, $4);
+		}
+              | ALTER USER UserId WITH OptUserList
+		{
+			$$ = cat_str(4, make_str("alter user"), $3, make_str("with"), $5);
+		}
 		;
 
 /*****************************************************************************
@@ -640,38 +633,46 @@ DropUserStmt:  DROP USER user_list
 				}
 		;
 
-user_passwd_clause:  PASSWORD StringConst	{ $$ = cat2_str(make_str("password") , $2); }
-			| /*EMPTY*/	{ $$ = EMPTY; }
+/*
+ * Options for CREATE USER and ALTER USER
+ */
+OptUserList: OptUserList OptUserElem		{ $$ = cat2_str($1, $2); }
+			| /* EMPTY */					{ $$ = EMPTY; }
 		;
 
-sysid_clause:	SYSID PosIntConst	{ if (atoi($2) <= 0)
-						mmerror(ET_ERROR, "sysid must be positive");
-
-					  $$ = cat2_str(make_str("sysid"), $2); }
-			| /*EMPTY*/     { $$ = EMPTY; }
-                ;
-
-user_createdb_clause:  CREATEDB
+OptUserElem:  PASSWORD Sconst
+                { 
+					$$ = cat2_str(make_str("password"), $2);
+				}
+              | SYSID Iconst
 				{
+					$$ = cat2_str(make_str("sysid"), $2);
+				}
+              | CREATEDB
+                { 
 					$$ = make_str("createdb");
 				}
-			| NOCREATEDB
-				{
+              | NOCREATEDB
+                { 
 					$$ = make_str("nocreatedb");
 				}
-			| /*EMPTY*/		{ $$ = EMPTY; }
-		;
-
-user_createuser_clause:  CREATEUSER
-				{
+              | CREATEUSER
+                { 
 					$$ = make_str("createuser");
 				}
-			| NOCREATEUSER
-				{
+              | NOCREATEUSER
+                { 
 					$$ = make_str("nocreateuser");
 				}
-			| /*EMPTY*/		{ $$ = NULL; }
-		;
+              | IN GROUP user_list
+                { 
+					$$ = cat2_str(make_str("in group"), $3); 
+				}
+              | VALID UNTIL Sconst
+                { 
+					$$ = cat2_str(make_str("valid until"), $3); 
+				}
+        ;
 
 user_list:  user_list ',' UserId
 				{
@@ -683,17 +684,6 @@ user_list:  user_list ',' UserId
 				}
 		;
 
-user_group_clause:  IN GROUP user_list
-			{
-				$$ = cat2_str(make_str("in group"), $3); 
-			}
-			| /*EMPTY*/		{ $$ = EMPTY; }
-		;
-
-user_valid_clause:  VALID UNTIL StringConst			{ $$ = cat2_str(make_str("valid until"), $3); }
-			| /*EMPTY*/			{ $$ = EMPTY; }
-		;
-
 
 /*****************************************************************************
  *
@@ -702,14 +692,18 @@ user_valid_clause:  VALID UNTIL StringConst			{ $$ = cat2_str(make_str("valid un
  *
  ****************************************************************************/
 CreateGroupStmt: CREATE GROUP UserId
-                 {
-			$$ = cat2_str(make_str("create group"), $3);
-		 }
-               | CREATE GROUP UserId WITH sysid_clause users_in_new_group_clause
-                 {
-			$$ = cat_str(5, make_str("create group"), $3, make_str("with"), $5, $6);
-                 }
-                ;
+				{
+					$$ = cat2_str(make_str("create group"), $3);
+				}
+			| CREATE GROUP UserId WITH users_in_new_group_clause
+				{
+					$$ = cat_str(4, make_str("create group"), $3, make_str("with"), $5);
+				}
+			| CREATE GROUP UserId WITH SYSID Iconst users_in_new_group_clause
+				{
+					$$ = cat_str(5, make_str("create group"), $3, make_str("with sysid"), $6, $7);
+				}
+			;
 
 users_in_new_group_clause:  USER user_list   { $$ = cat2_str(make_str("user"), $2); }
                             | /* EMPTY */          { $$ = EMPTY; }
@@ -2289,17 +2283,17 @@ ClusterStmt:  CLUSTER index_name ON relation_name
  *
  *****************************************************************************/
 
-VacuumStmt:  VACUUM opt_verbose
+VacuumStmt:  VACUUM opt_full opt_verbose
 				{
-					$$ = cat_str(2, make_str("vacuum"), $2);
+					$$ = cat_str(3, make_str("vacuum"), $2, $3);
 				}
-		| VACUUM opt_verbose relation_name
+		| VACUUM opt_full opt_verbose relation_name
 				{
-					$$ = cat_str(3, make_str("vacuum"), $2, $3);
+					$$ = cat_str(4, make_str("vacuum"), $2, $3, $4);
 				}
-		| VACUUM opt_verbose AnalyzeStmt
+		| VACUUM opt_full opt_verbose AnalyzeStmt
 				{
-					$$ = cat_str(3, make_str("vacuum"), $2, $3);
+					$$ = cat_str(4, make_str("vacuum"), $2, $3, $4);
 				}
 		;
 
@@ -2318,7 +2312,11 @@ analyze_keyword:  ANALYZE					{ $$ = make_str("analyze"); }
 		;
 
 opt_verbose:  VERBOSE					{ $$ = make_str("verbose"); }
-		| /*EMPTY*/				{ $$ = EMPTY; }
+		| /*EMPTY*/						{ $$ = EMPTY; }
+		;
+
+opt_full:  FULL							{ $$ = make_str("full"); }
+		| /*EMPTY*/						{ $$ = EMPTY; }
 		;
 
 opt_name_list:  '(' name_list ')'		{ $$ = cat_str(3, make_str("("), $2, make_str(")")); }