From 1aac2c852a2ccd817daf2dac99cf450e7822eb20 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Fri, 1 Mar 2002 22:45:19 +0000
Subject: [PATCH] User and database-specific session defaults for run-time
 configuration variables.  New commands ALTER DATABASE ... SET and ALTER USER
 ... SET.

---
 doc/src/sgml/catalogs.sgml           |  16 ++-
 doc/src/sgml/ref/allfiles.sgml       |   3 +-
 doc/src/sgml/ref/alter_database.sgml | 169 +++++++++++++++++++++++++++
 doc/src/sgml/ref/alter_user.sgml     |  53 ++++++++-
 doc/src/sgml/reference.sgml          |   3 +-
 doc/src/sgml/release.sgml            |   4 +-
 doc/src/sgml/runtime.sgml            |  11 +-
 src/backend/commands/dbcommands.c    |  79 ++++++++++++-
 src/backend/commands/user.c          |  89 +++++++++++++-
 src/backend/nodes/copyfuncs.c        |  36 +++++-
 src/backend/nodes/equalfuncs.c       |  34 +++++-
 src/backend/parser/gram.y            |  59 +++++++++-
 src/backend/tcop/postgres.c          |  12 +-
 src/backend/tcop/utility.c           |  10 +-
 src/backend/utils/init/miscinit.c    |  20 +++-
 src/backend/utils/init/postinit.c    |  26 ++++-
 src/backend/utils/misc/guc.c         | 156 ++++++++++++++++++++++++-
 src/bin/initdb/initdb.sh             |   5 +-
 src/include/catalog/catversion.h     |   4 +-
 src/include/catalog/pg_attribute.h   |   4 +-
 src/include/catalog/pg_class.h       |   6 +-
 src/include/catalog/pg_database.h    |   8 +-
 src/include/catalog/pg_shadow.h      |   8 +-
 src/include/commands/dbcommands.h    |   5 +-
 src/include/commands/user.h          |   3 +-
 src/include/nodes/nodes.h            |   4 +-
 src/include/nodes/parsenodes.h       |  24 +++-
 src/include/utils/guc.h              |   7 +-
 src/test/regress/expected/rules.out  |   2 +-
 29 files changed, 812 insertions(+), 48 deletions(-)
 create mode 100644 doc/src/sgml/ref/alter_database.sgml

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 20adc19bab8..aab0a906d9b 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,6 +1,6 @@
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
- $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.30 2002/02/18 23:10:59 petere Exp $
+ $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.31 2002/03/01 22:45:03 petere Exp $
  -->
 
 <chapter id="catalogs">
@@ -889,6 +889,13 @@
        or an absolute path, depending how it was entered.
       </entry>
      </row>
+
+     <row>
+      <entry>datconfig</entry>
+      <entry><type>text[]</type></entry>
+      <entry></entry>
+      <entry>Session defaults for run-time configuration variables</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
@@ -1980,6 +1987,13 @@
       <entry></entry>
       <entry>Account expiry time (only used for password authentication)</entry>
      </row>
+
+     <row>
+      <entry>useconfig</entry>
+      <entry><type>text[]</type></entry>
+      <entry></entry>
+      <entry>Session defaults for run-time configuration variables</entry>
+     </row>
     </tbody>
    </tgroup>
   </table>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index ef29c0e8b98..94f1226c24f 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.32 2002/01/15 05:05:49 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.33 2002/03/01 22:45:07 petere Exp $
 PostgreSQL documentation
 Complete list of usable sgml source files in this directory.
 -->
@@ -37,6 +37,7 @@ Complete list of usable sgml source files in this directory.
 
 <!-- SQL commands -->
 <!entity abort              system "abort.sgml">
+<!entity alterDatabase      system "alter_database.sgml">
 <!entity alterGroup         system "alter_group.sgml">
 <!entity alterTable         system "alter_table.sgml">
 <!entity alterUser          system "alter_user.sgml">
diff --git a/doc/src/sgml/ref/alter_database.sgml b/doc/src/sgml/ref/alter_database.sgml
new file mode 100644
index 00000000000..b1aa34262a1
--- /dev/null
+++ b/doc/src/sgml/ref/alter_database.sgml
@@ -0,0 +1,169 @@
+<!--
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_database.sgml,v 1.1 2002/03/01 22:45:07 petere Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-ALTERDATABASE">
+ <refmeta>
+  <refentrytitle id="sql-alterdatabase-title">ALTER DATABASE</refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>ALTER DATABASE</refname>
+  <refpurpose>change a database</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> SET <replaceable>variable</replaceable> { TO | = } { <replaceable>value</replaceable> | DEFAULT }
+ALTER DATABASE <replaceable class="PARAMETER">name</replaceable> RESET <replaceable>variable</replaceable>
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>ALTER DATABASE</command> is used to change the session
+   default of a run-time configuration variable for a
+   <productname>PostgreSQL</productname> database. Whenever a new
+   session is subsequently started, <literal>SET
+   <replaceable>variable</replaceable> TO
+   <replaceable>value</replaceable></literal> is effectively executed
+   before the start of the session.
+  </para>
+
+  <para>
+   Only a database owner can change the session defaults for a
+   database.  Superusers can change the session defaults of any
+   database.
+  </para>
+
+  <refsect2>
+   <title>Parameters</title>
+
+   <para>
+    <variablelist>
+     <varlistentry>
+      <term><replaceable class="PARAMETER">name</replaceable></term>
+      <listitem>
+       <para>
+	The name of the database whose session defaults are to be altered.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable>variable</replaceable></term>
+      <term><replaceable>value</replaceable></term>
+      <listitem>
+       <para>
+        Set the session default for this database of the specified
+        configuration variable to the given value.  If
+        <replaceable>value</replaceable> is <literal>DEFAULT</literal>
+        or, equivalently, <literal>RESET</literal> is used, the
+        database-specific variable setting is removed and the default
+        setting will be inherited in new sessions.  Use <literal>RESET
+        ALL</literal> to clear all settings.
+       </para>
+
+       <para>
+        See <xref linkend="sql-set" endterm="sql-set-title"> and the
+        <citetitle>Administrator's Guide</citetitle> for more
+        information about allowed variable names and values.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+  </refsect2>
+ </refsect1>
+
+ <refsect1>
+  <title>Diagnostics</title>
+
+  <para>
+   <variablelist>
+    <varlistentry>
+     <term><computeroutput>ALTER DATABASE</computeroutput></term>
+     <listitem>
+      <para>
+       Message returned if the alteration was successful.
+      </para>
+     </listitem>
+    </varlistentry>
+     
+    <varlistentry>
+     <term><computeroutput>ERROR:  database "dbname" does not exist</computeroutput></term>
+     <listitem>
+      <para>
+       Error message returned if the specified database is not known
+       to the system.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   Using <xref linkend="sql-alteruser" endterm="sql-alteruser-title">,
+   it is also possible to tie a session default to a specific user
+   rather than a database.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   To disable index scans by default in the database
+   <literal>test</literal>:
+
+<programlisting>
+ALTER DATABASE test SET enable_indexscan TO off;
+</programlisting>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+    
+  <para>
+   The <command>ALTER DATABASE</command> statement is a
+   <productname>PostgreSQL</productname> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-alteruser" endterm="sql-alteruser-title"></member>
+   <member><xref linkend="sql-createdatabase" endterm="sql-createdatabase-title"></member>
+   <member><xref linkend="sql-dropdatabase" endterm="sql-dropdatabase-title"></member>
+   <member><xref linkend="sql-set" endterm="sql-set-title"></member>
+  </simplelist>
+ </refsect1>
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:nil
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:"../reference.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:"/usr/lib/sgml/catalog"
+sgml-local-ecat-files:nil
+End:
+-->
diff --git a/doc/src/sgml/ref/alter_user.sgml b/doc/src/sgml/ref/alter_user.sgml
index 0156565eb50..3819bf8d964 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.19 2002/02/27 21:14:53 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_user.sgml,v 1.20 2002/03/01 22:45:07 petere Exp $
 PostgreSQL documentation
 -->
 
@@ -24,6 +24,9 @@ where <replaceable class="PARAMETER">option</replaceable> can be:
     | CREATEDB | NOCREATEDB
     | CREATEUSER | NOCREATEUSER 
     | VALID UNTIL '<replaceable class="PARAMETER">abstime</replaceable>'
+
+ALTER USER <replaceable class="PARAMETER">username</replaceable> SET <replaceable>variable</replaceable> { TO | = } { <replaceable>value</replaceable> | DEFAULT }
+ALTER USER <replaceable class="PARAMETER">username</replaceable> RESET <replaceable>variable</replaceable>
 </synopsis>
  </refsynopsisdiv>
 
@@ -37,9 +40,22 @@ where <replaceable class="PARAMETER">option</replaceable> can be:
   </para>
 
   <para>
-   Only a database superuser can change privileges and password
-   expiration with this command. Ordinary users can only change their
-   own password.
+   The first variant of this command in the synopsis changes certain
+   global user privileges and authentication settings.  (See below for
+   details.)  Only a database superuser can change privileges and
+   password expiration with this command.  Ordinary users can only
+   change their own password.
+  </para>
+
+  <para>
+   The second and the third variant change a user's session default of
+   a specified configuration variable.  Whenever the user subsequently
+   starts a new session, <literal>SET
+   <replaceable>variable</replaceable> TO
+   <replaceable>value</replaceable></literal> is effectively executed
+   before the start of the session.  Ordinary users can change their
+   own session defaults.  Superusers can change anyone's session
+   defaults.
   </para>
 
   <refsect2>
@@ -113,6 +129,28 @@ where <replaceable class="PARAMETER">option</replaceable> can be:
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry>
+      <term><replaceable>variable</replaceable></term>
+      <term><replaceable>value</replaceable></term>
+      <listitem>
+       <para>
+        Set this user's session default of the specified configuration
+        variable to the given value.  If
+        <replaceable>value</replaceable> is <literal>DEFAULT</literal>
+        or, equivalently, <literal>RESET</literal> is used, the
+        user-specific variable setting is removed and the user will
+        inherit the default setting in new sessions.  Use
+        <literal>RESET ALL</literal> to clear all settings.
+       </para>
+
+       <para>
+        See <xref linkend="sql-set" endterm="sql-set-title"> and the
+        <citetitle>Administrator's Guide</citetitle> for more
+        information about allowed variable names and values.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
    </para>
   </refsect2>
@@ -159,6 +197,12 @@ where <replaceable class="PARAMETER">option</replaceable> can be:
    Use <xref linkend="SQL-ALTERGROUP" endterm="SQL-ALTERGROUP-title">
    to do that.
   </para>
+
+  <para>
+   Using <xref linkend="sql-alterdatabase"
+   endterm="sql-alterdatabase-title">, it is also possible to tie a
+   session default to a specific database rather than a user.
+  </para>
  </refsect1>
 
  <refsect1>
@@ -214,6 +258,7 @@ ALTER USER miriam CREATEUSER CREATEDB;
   <simplelist type="inline">
    <member><xref linkend="sql-createuser" endterm="sql-createuser-title"></member>
    <member><xref linkend="sql-dropuser" endterm="sql-dropuser-title"></member>
+   <member><xref linkend="sql-set" endterm="sql-set-title"></member>
   </simplelist>
  </refsect1>
 </refentry>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index a5b8cfbf163..27f218d33fb 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -1,5 +1,5 @@
 <!-- reference.sgml
-$Header: /cvsroot/pgsql/doc/src/sgml/reference.sgml,v 1.20 2002/01/15 05:05:49 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/reference.sgml,v 1.21 2002/03/01 22:45:04 petere Exp $
 
 PostgreSQL Reference Manual
 -->
@@ -46,6 +46,7 @@ PostgreSQL Reference Manual
   </partintro>
 
    &abort;
+   &alterDatabase;
    &alterGroup;
    &alterTable;
    &alterUser;
diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml
index edcdc9405aa..3801b8d1eab 100644
--- a/doc/src/sgml/release.sgml
+++ b/doc/src/sgml/release.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.118 2002/02/24 20:20:19 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.119 2002/03/01 22:45:04 petere Exp $
 -->
 
 <appendix id="release">
@@ -28,6 +28,8 @@ Access privileges on functions
 Access privileges on procedural languages
 CREATE DATABASE has OWNER option so superuser can create DB for someone else
 Kerberos 5 support now works with Heimdal
+Database and user-specific session defaults of run-time configurations variables
+    (ALTER DATABASE ... SET and ALTER USER ... SET)
 ]]></literallayout>
 
  </sect1>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 2c0441fdcc9..3f46a3e9e4f 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.103 2002/01/20 22:19:56 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/runtime.sgml,v 1.104 2002/03/01 22:45:05 petere Exp $
 -->
 
 <Chapter Id="runtime">
@@ -537,12 +537,17 @@ env PGOPTIONS='-c geqo=off' psql
    </para>
 
    <para>
-    Finally, some options can be changed in individual SQL sessions
-    with the <command>SET</command> command, for example
+    Some options can be changed in individual SQL sessions with the
+    <command>SET</command> command, for example
 <screen>
 =&gt; <userinput>SET ENABLE_SEQSCAN TO OFF;</userinput>
 </screen>
     See the SQL command language reference for details on the syntax.
+    Furthermore, it is possible to assign a set of option settings to
+    a user or a database.  Whenever a session is started, the default
+    settings for the user and database involved are loaded.  The
+    commands <literal>ALTER DATABASE</literal> and <literal>ALTER
+    USER</literal>, respectively, are used to set this up.
    </para>
 
    <sect2 id="runtime-config-optimizer">
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 712df38ec51..3de94de9c6e 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.83 2002/02/24 20:20:19 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.84 2002/03/01 22:45:08 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,8 +32,10 @@
 #include "miscadmin.h"
 #include "storage/freespace.h"
 #include "storage/sinval.h"
+#include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/guc.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
@@ -287,6 +289,7 @@ createdb(const char *dbname, const char *dbowner,
 		DirectFunctionCall1(textin, CStringGetDatum(dbpath ? dbpath : ""));
 
 	memset(new_record_nulls, ' ', sizeof(new_record_nulls));
+	new_record_nulls[Anum_pg_database_datconfig - 1] = 'n';
 
 	tuple = heap_formtuple(pg_database_dsc, new_record, new_record_nulls);
 
@@ -446,6 +449,80 @@ dropdb(const char *dbname)
 
 
 
+/*
+ * ALTER DATABASE name SET ...
+ */
+void
+AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
+{
+	char	   *valuestr;
+	HeapTuple	tuple,
+				newtuple;
+	Relation	rel;
+	ScanKeyData	scankey;
+	HeapScanDesc scan;
+	Datum		repl_val[Natts_pg_database];
+	char		repl_null[Natts_pg_database];
+	char		repl_repl[Natts_pg_database];
+	int			i;
+
+	valuestr = (stmt->value
+				? ((A_Const *) lfirst(stmt->value))->val.val.str
+				: NULL);
+
+	rel = heap_openr(DatabaseRelationName, RowExclusiveLock);
+	ScanKeyEntryInitialize(&scankey, 0, Anum_pg_database_datname,
+						   F_NAMEEQ, NameGetDatum(stmt->dbname));
+	scan = heap_beginscan(rel, 0, SnapshotNow, 1, &scankey);
+	tuple = heap_getnext(scan, 0);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "database \"%s\" does not exist", stmt->dbname);
+
+	if (!(superuser()
+		  || ((Form_pg_database) GETSTRUCT(tuple))->datdba == GetUserId()))
+		elog(ERROR, "permission denied");
+
+	for (i = 0; i < Natts_pg_database; i++)
+		repl_repl[i] = ' ';
+
+	repl_repl[Anum_pg_database_datconfig-1] = 'r';
+	if (strcmp(stmt->variable, "all")==0 && stmt->value == NULL)
+		/* RESET ALL */
+		repl_null[Anum_pg_database_datconfig-1] = 'n';
+	else
+	{
+		Datum datum;
+		bool isnull;
+		ArrayType *a;
+
+		repl_null[Anum_pg_database_datconfig-1] = ' ';
+
+		datum = heap_getattr(tuple, Anum_pg_database_datconfig,
+							 RelationGetDescr(rel), &isnull);
+
+		if (valuestr)
+			a = GUCArrayAdd(isnull
+							? NULL
+							: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
+							stmt->variable, valuestr);
+		else
+			a = GUCArrayDelete(isnull
+							   ? NULL
+							   : (ArrayType *) pg_detoast_datum((struct varlena *)datum),
+							   stmt->variable);
+
+		repl_val[Anum_pg_database_datconfig-1] = PointerGetDatum(a);
+	}
+
+	newtuple = heap_modifytuple(tuple, rel, repl_val, repl_null, repl_repl);
+	simple_heap_update(rel, &tuple->t_self, newtuple);
+
+	heap_endscan(scan);
+	heap_close(rel, RowExclusiveLock);
+}
+
+
+
 /*
  * Helper functions
  */
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index a7ccd3768ff..a059207b0aa 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.90 2001/11/05 17:46:25 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.91 2002/03/01 22:45:08 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,7 @@
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/guc.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
@@ -406,6 +407,8 @@ CreateUser(CreateUserStmt *stmt)
 	new_record_nulls[Anum_pg_shadow_passwd - 1] = password ? ' ' : 'n';
 	new_record_nulls[Anum_pg_shadow_valuntil - 1] = validUntil ? ' ' : 'n';
 
+	new_record_nulls[Anum_pg_shadow_useconfig - 1] = 'n';
+
 	tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
 
 	/*
@@ -653,6 +656,11 @@ AlterUser(AlterUserStmt *stmt)
 		new_record_nulls[Anum_pg_shadow_valuntil - 1] = null ? 'n' : ' ';
 	}
 
+	/* leave useconfig as is */
+	new_record[Anum_pg_shadow_useconfig - 1] =
+		heap_getattr(tuple, Anum_pg_shadow_useconfig, pg_shadow_dsc, &null);
+	new_record_nulls[Anum_pg_shadow_useconfig - 1] = null ? 'n' : ' ';
+
 	new_tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
 	simple_heap_update(pg_shadow_rel, &tuple->t_self, new_tuple);
 
@@ -684,6 +692,85 @@ AlterUser(AlterUserStmt *stmt)
 
 
 
+/*
+ * ALTER USER ... SET
+ */
+void
+AlterUserSet(AlterUserSetStmt *stmt)
+{
+	char	   *valuestr;
+	HeapTuple	oldtuple,
+				newtuple;
+	Relation	rel;
+	Datum		repl_val[Natts_pg_shadow];
+	char		repl_null[Natts_pg_shadow];
+	char		repl_repl[Natts_pg_shadow];
+	int			i;
+
+	valuestr = (stmt->value
+				? ((A_Const *) lfirst(stmt->value))->val.val.str
+				: NULL);
+
+	rel = heap_openr(ShadowRelationName, RowExclusiveLock);
+	oldtuple = SearchSysCache(SHADOWNAME,
+							  PointerGetDatum(stmt->user),
+							  0, 0, 0);
+	if (!HeapTupleIsValid(oldtuple))
+		elog(ERROR, "user \"%s\" does not exist", stmt->user);
+
+	if (!(superuser()
+		  || ((Form_pg_shadow) GETSTRUCT(oldtuple))->usesysid == GetUserId()))
+		elog(ERROR, "permission denied");
+
+	for (i = 0; i < Natts_pg_shadow; i++)
+		repl_repl[i] = ' ';
+
+	repl_repl[Anum_pg_shadow_useconfig-1] = 'r';
+	if (strcmp(stmt->variable, "all")==0 && stmt->value == NULL)
+		/* RESET ALL */
+		repl_null[Anum_pg_shadow_useconfig-1] = 'n';
+	else
+	{
+		Datum datum;
+		bool isnull;
+		ArrayType *a;
+
+		repl_null[Anum_pg_shadow_useconfig-1] = ' ';
+
+		datum = SysCacheGetAttr(SHADOWNAME, oldtuple,
+								Anum_pg_shadow_useconfig, &isnull);
+
+		if (valuestr)
+			a = GUCArrayAdd(isnull
+							? NULL
+							: (ArrayType *) pg_detoast_datum((struct varlena *)datum),
+							stmt->variable, valuestr);
+		else
+			a = GUCArrayDelete(isnull
+							   ? NULL
+							   : (ArrayType *) pg_detoast_datum((struct varlena *)datum),
+							   stmt->variable);
+
+		repl_val[Anum_pg_shadow_useconfig-1] = PointerGetDatum(a);
+	}
+
+	newtuple = heap_modifytuple(oldtuple, rel, repl_val, repl_null, repl_repl);
+	simple_heap_update(rel, &oldtuple->t_self, newtuple);
+
+	{
+		Relation	idescs[Num_pg_shadow_indices];
+
+		CatalogOpenIndices(Num_pg_shadow_indices, Name_pg_shadow_indices, idescs);
+		CatalogIndexInsert(idescs, Num_pg_shadow_indices, rel, newtuple);
+		CatalogCloseIndices(Num_pg_shadow_indices, idescs);
+	}
+
+	ReleaseSysCache(oldtuple);
+	heap_close(rel, RowExclusiveLock);
+}
+
+
+
 /*
  * DROP USER
  */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 570fa285234..d3fe436ac77 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.164 2002/03/01 06:01:18 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.165 2002/03/01 22:45:11 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2245,6 +2245,20 @@ _copyCreatedbStmt(CreatedbStmt *from)
 	return newnode;
 }
 
+static AlterDatabaseSetStmt *
+_copyAlterDatabaseSetStmt(AlterDatabaseSetStmt *from)
+{
+	AlterDatabaseSetStmt *newnode = makeNode(AlterDatabaseSetStmt);
+
+	if (from->dbname)
+		newnode->dbname = pstrdup(from->dbname);
+	if (from->variable)
+		newnode->variable = pstrdup(from->variable);
+	Node_Copy(from, newnode, value);
+
+	return newnode;
+}
+
 static DropdbStmt *
 _copyDropdbStmt(DropdbStmt *from)
 {
@@ -2427,6 +2441,20 @@ _copyAlterUserStmt(AlterUserStmt *from)
 	return newnode;
 }
 
+static AlterUserSetStmt *
+_copyAlterUserSetStmt(AlterUserSetStmt *from)
+{
+	AlterUserSetStmt *newnode = makeNode(AlterUserSetStmt);
+
+	if (from->user)
+		newnode->user = pstrdup(from->user);
+	if (from->variable)
+		newnode->user = pstrdup(from->variable);
+	Node_Copy(from, newnode, value);
+
+	return newnode;
+}
+
 static DropUserStmt *
 _copyDropUserStmt(DropUserStmt *from)
 {
@@ -2845,6 +2873,9 @@ copyObject(void *from)
 		case T_CreatedbStmt:
 			retval = _copyCreatedbStmt(from);
 			break;
+		case T_AlterDatabaseSetStmt:
+			retval = _copyAlterDatabaseSetStmt(from);
+			break;
 		case T_DropdbStmt:
 			retval = _copyDropdbStmt(from);
 			break;
@@ -2884,6 +2915,9 @@ copyObject(void *from)
 		case T_AlterUserStmt:
 			retval = _copyAlterUserStmt(from);
 			break;
+		case T_AlterUserSetStmt:
+			retval = _copyAlterUserSetStmt(from);
+			break;
 		case T_DropUserStmt:
 			retval = _copyDropUserStmt(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 408c14bb7e4..46cf4497923 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.111 2002/02/26 22:47:05 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.112 2002/03/01 22:45:12 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1112,6 +1112,19 @@ _equalCreatedbStmt(CreatedbStmt *a, CreatedbStmt *b)
 	return true;
 }
 
+static bool
+_equalAlterDatabaseSetStmt(AlterDatabaseSetStmt *a, AlterDatabaseSetStmt *b)
+{
+	if (!equalstr(a->dbname, b->dbname))
+		return false;
+	if (!equalstr(a->variable, b->variable))
+		return false;
+	if (!equal(a->value, b->value))
+		return false;
+
+	return true;
+}
+
 static bool
 _equalDropdbStmt(DropdbStmt *a, DropdbStmt *b)
 {
@@ -1289,6 +1302,19 @@ _equalAlterUserStmt(AlterUserStmt *a, AlterUserStmt *b)
 	return true;
 }
 
+static bool
+_equalAlterUserSetStmt(AlterUserSetStmt *a, AlterUserSetStmt *b)
+{
+	if (!equalstr(a->user, b->user))
+		return false;
+	if (!equalstr(a->variable, b->variable))
+		return false;
+	if (!equal(a->value, b->value))
+		return false;
+
+	return true;
+}
+
 static bool
 _equalDropUserStmt(DropUserStmt *a, DropUserStmt *b)
 {
@@ -1988,6 +2014,9 @@ equal(void *a, void *b)
 		case T_CreatedbStmt:
 			retval = _equalCreatedbStmt(a, b);
 			break;
+		case T_AlterDatabaseSetStmt:
+			retval = _equalAlterDatabaseSetStmt(a, b);
+			break;
 		case T_DropdbStmt:
 			retval = _equalDropdbStmt(a, b);
 			break;
@@ -2027,6 +2056,9 @@ equal(void *a, void *b)
 		case T_AlterUserStmt:
 			retval = _equalAlterUserStmt(a, b);
 			break;
+		case T_AlterUserSetStmt:
+			retval = _equalAlterUserSetStmt(a, b);
+			break;
 		case T_DropUserStmt:
 			retval = _equalDropUserStmt(a, b);
 			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index f522769704c..841d136f47b 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.281 2002/02/25 03:37:14 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.282 2002/03/01 22:45:12 petere Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -130,8 +130,8 @@ static void doNegateFloat(Value *v);
 }
 
 %type <node>	stmt,
-		AlterGroupStmt, AlterSchemaStmt, AlterTableStmt, AlterUserStmt,
-		AnalyzeStmt,
+		AlterDatabaseSetStmt, AlterGroupStmt, AlterSchemaStmt, AlterTableStmt,
+		AlterUserStmt, AlterUserSetStmt, AnalyzeStmt,
 		ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt,
 		CopyStmt, CreateAsStmt, CreateGroupStmt, CreatePLangStmt,
 		CreateSchemaStmt, CreateSeqStmt, CreateStmt, CreateTrigStmt,
@@ -436,10 +436,12 @@ stmtmulti:  stmtmulti ';' stmt
 				}
 		;
 
-stmt :	AlterSchemaStmt
-		| AlterTableStmt
+stmt : AlterDatabaseSetStmt
 		| AlterGroupStmt
+		| AlterSchemaStmt
+		| AlterTableStmt
 		| AlterUserStmt
+		| AlterUserSetStmt
 		| ClosePortalStmt
 		| CopyStmt
 		| CreateStmt
@@ -539,6 +541,26 @@ AlterUserStmt:  ALTER USER UserId OptUserList
 				 }
 		;
 
+
+AlterUserSetStmt: ALTER USER UserId VariableSetStmt
+				{
+					AlterUserSetStmt *n = makeNode(AlterUserSetStmt);
+					n->user = $3;
+					n->variable = ((VariableSetStmt *)$4)->name;
+					n->value = ((VariableSetStmt *)$4)->args;
+					$$ = (Node *)n;
+				}
+				| ALTER USER UserId VariableResetStmt
+				{
+					AlterUserSetStmt *n = makeNode(AlterUserSetStmt);
+					n->user = $3;
+					n->variable = ((VariableResetStmt *)$4)->name;
+					n->value = NULL;
+					$$ = (Node *)n;
+				}
+		;
+
+
 /*****************************************************************************
  *
  * Drop a postgresql DBMS user
@@ -3163,6 +3185,33 @@ opt_equal: '='								{ $$ = TRUE; }
 		| /*EMPTY*/							{ $$ = FALSE; }
 		;
 
+
+/*****************************************************************************
+ *
+ *		ALTER DATABASE
+ *
+ *
+ *****************************************************************************/
+
+AlterDatabaseSetStmt: ALTER DATABASE database_name VariableSetStmt
+				{
+					AlterDatabaseSetStmt *n = makeNode(AlterDatabaseSetStmt);
+					n->dbname = $3;
+					n->variable = ((VariableSetStmt *)$4)->name;
+					n->value = ((VariableSetStmt *)$4)->args;
+					$$ = (Node *)n;
+				}
+				| ALTER DATABASE database_name VariableResetStmt
+				{
+					AlterDatabaseSetStmt *n = makeNode(AlterDatabaseSetStmt);
+					n->dbname = $3;
+					n->variable = ((VariableResetStmt *)$4)->name;
+					n->value = NULL;
+					$$ = (Node *)n;
+				}
+		;
+
+
 /*****************************************************************************
  *
  *		DROP DATABASE
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index e688c414830..fad79146cc8 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.250 2002/02/27 23:16:07 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.251 2002/03/01 22:45:13 petere Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -1707,7 +1707,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 	if (!IsUnderPostmaster)
 	{
 		puts("\nPOSTGRES backend interactive interface ");
-		puts("$Revision: 1.250 $ $Date: 2002/02/27 23:16:07 $\n");
+		puts("$Revision: 1.251 $ $Date: 2002/03/01 22:45:13 $\n");
 	}
 
 	/*
@@ -2265,6 +2265,10 @@ CreateCommandTag(Node *parsetree)
 			tag = "CREATE DATABASE";
 			break;
 
+		case T_AlterDatabaseSetStmt:
+			tag = "ALTER DATABASE";
+			break;
+
 		case T_DropdbStmt:
 			tag = "DROP DATABASE";
 			break;
@@ -2342,6 +2346,10 @@ CreateCommandTag(Node *parsetree)
 			tag = "ALTER USER";
 			break;
 
+		case T_AlterUserSetStmt:
+			tag = "ALTER USER";
+			break;
+
 		case T_DropUserStmt:
 			tag = "DROP USER";
 			break;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index a6a8b561e02..4e2b89508e7 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.127 2002/02/26 22:47:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.128 2002/03/01 22:45:14 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -600,6 +600,10 @@ ProcessUtility(Node *parsetree,
 			}
 			break;
 
+		case T_AlterDatabaseSetStmt:
+			AlterDatabaseSet((AlterDatabaseSetStmt *)parsetree);
+			break;
+
 		case T_DropdbStmt:
 			{
 				DropdbStmt *stmt = (DropdbStmt *) parsetree;
@@ -748,6 +752,10 @@ ProcessUtility(Node *parsetree,
 			AlterUser((AlterUserStmt *) parsetree);
 			break;
 
+		case T_AlterUserSetStmt:
+			AlterUserSet((AlterUserSetStmt *) parsetree);
+			break;
+
 		case T_DropUserStmt:
 			DropUser((DropUserStmt *) parsetree);
 			break;
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 9705dda664c..0ccbe753d06 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.82 2002/01/09 19:13:41 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.83 2002/03/01 22:45:15 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,6 +31,7 @@
 #include "libpq/libpq-be.h"
 #include "miscadmin.h"
 #include "utils/builtins.h"
+#include "utils/guc.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
@@ -437,6 +438,8 @@ void
 InitializeSessionUserId(const char *username)
 {
 	HeapTuple	userTup;
+	Datum		datum;
+	bool		isnull;
 
 	/*
 	 * Don't do scans if we're bootstrapping, none of the system catalogs
@@ -457,6 +460,21 @@ InitializeSessionUserId(const char *username)
 
 	AuthenticatedUserIsSuperuser = ((Form_pg_shadow) GETSTRUCT(userTup))->usesuper;
 
+	/*
+	 * Set up user-specific configuration variables.  This is a good
+	 * place to do it so we don't have to read pg_shadow twice during
+	 * session startup.
+	 */
+	datum = SysCacheGetAttr(SHADOWNAME, userTup,
+							Anum_pg_shadow_useconfig, &isnull);
+	if (!isnull)
+	{
+		ArrayType *a;
+
+		a = (ArrayType *) pg_detoast_datum((struct varlena *)datum);
+		ProcessGUCArray(a, PGC_S_USER);
+	}
+
 	ReleaseSysCache(userTup);
 }
 
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 222ab6d54ab..858b6e649c7 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.98 2002/02/19 20:11:18 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.99 2002/03/01 22:45:15 petere Exp $
  *
  *
  *-------------------------------------------------------------------------
@@ -35,6 +35,7 @@
 #include "storage/sinval.h"
 #include "storage/smgr.h"
 #include "utils/fmgroids.h"
+#include "utils/guc.h"
 #include "utils/portal.h"
 #include "utils/relcache.h"
 #include "utils/syscache.h"
@@ -70,6 +71,10 @@ static bool ThereIsAtLeastOneUser(void);
  *
  * This is also a handy place to fetch the database encoding info out
  * of pg_database, if we are in MULTIBYTE mode.
+ *
+ * To avoid having to read pg_database more times than necessary
+ * during session startup, this place is also fitting to set up any
+ * database-specific configuration variables.
  * --------------------------------
  */
 static void
@@ -132,6 +137,25 @@ ReverifyMyDatabase(const char *name)
 			 dbform->encoding);
 #endif
 
+	/*
+	 * Set up datbase-specific configuration variables.
+	 */
+	if (IsUnderPostmaster)
+	{
+		Datum		datum;
+		bool		isnull;
+
+		datum = heap_getattr(tup, Anum_pg_database_datconfig,
+							 RelationGetDescr(pgdbrel), &isnull);
+		if (!isnull)
+		{
+			ArrayType *a;
+
+			a = (ArrayType *) pg_detoast_datum((struct varlena *)datum);
+			ProcessGUCArray(a, PGC_S_DATABASE);
+		}
+	}
+
 	heap_endscan(pgdbscan);
 	heap_close(pgdbrel, AccessShareLock);
 }
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6e112caa43c..f140d871f0c 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -4,7 +4,7 @@
  * Support for grand unified configuration scheme, including SET
  * command, configuration file, and command line options.
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.59 2002/02/23 01:31:36 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.60 2002/03/01 22:45:16 petere Exp $
  *
  * Copyright 2000 by PostgreSQL Global Development Group
  * Written by Peter Eisentraut <peter_e@gmx.net>.
@@ -36,6 +36,8 @@
 #include "storage/lock.h"
 #include "storage/proc.h"
 #include "tcop/tcopprot.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
 #include "utils/datetime.h"
 #include "pgstat.h"
 
@@ -89,6 +91,7 @@ bool		Password_encryption = false;
 #define PG_KRB_SRVTAB ""
 #endif
 
+static bool guc_session_init = false; /* XXX mildly bogus */
 
 /*
  * Declarations for GUC tables
@@ -882,7 +885,12 @@ set_config_option(const char *name, const char *value,
 	int			elevel;
 	bool		makeDefault;
 
-	elevel = (context == PGC_SIGHUP) ? DEBUG : ERROR;
+	if (context == PGC_SIGHUP)
+		elevel = DEBUG;
+	else if (guc_session_init)
+		elevel = NOTICE;
+	else
+		elevel = ERROR;
 
 	type = find_option(name, &record);
 	if (type == PGC_NONE)
@@ -1362,3 +1370,147 @@ assign_defaultxactisolevel(const char *value)
 	else
 		elog(ERROR, "bogus transaction isolation level");
 }
+
+
+
+void
+ProcessGUCArray(ArrayType *array, GucSource source)
+{
+	int		i;
+
+	Assert(array);
+
+	for (i = 1; i <= ARR_DIMS(array)[0]; i++)
+	{
+		Datum		d;
+		bool		isnull;
+		char	   *s;
+		char	   *name;
+		char	   *value;
+
+		d = array_ref(array, 1, &i,
+					  false /*notbyvalue*/,
+					  -1 /*varlenelem*/,
+					  -1 /*varlenarray*/,
+					  &isnull);
+
+		if (isnull)
+			continue;
+
+		s = DatumGetCString(DirectFunctionCall1(textout, d));
+		ParseLongOption(s, &name, &value);
+		if (!value)
+		{
+		    elog(NOTICE, "cannot to parse setting \"%s\"", name);
+		    continue;
+		}
+
+		/* prevent errors from incorrect options */
+		guc_session_init = true;
+		
+		SetConfigOption(name, value, PGC_SUSET, source);
+
+		guc_session_init = false;
+	}
+}
+
+
+
+ArrayType *
+GUCArrayAdd(ArrayType *array, const char *name, const char *value)
+{
+	Datum		datum;
+	char	   *newval;
+	ArrayType  *a;
+
+	Assert(name);
+	Assert(value);
+
+	/* test if the option is valid */
+	set_config_option(name, value,
+					  superuser() ? PGC_SUSET : PGC_USERSET,
+					  false, PGC_S_INFINITY);
+
+	newval = palloc(strlen(name) + 1 + strlen(value) + 1);
+	sprintf(newval, "%s=%s", name, value);
+	datum = DirectFunctionCall1(textin, CStringGetDatum(newval));
+	
+	if (array)
+	{
+		int		index;
+		bool	isnull;
+		int		i;
+
+		index = ARR_DIMS(array)[0] + 1;	/* add after end */
+
+		for (i = 1; i <= ARR_DIMS(array)[0]; i++)
+		{
+			Datum		d;
+			char	   *current;
+
+			d = array_ref(array, 1, &i,
+						  false /*notbyvalue*/,
+						  -1 /*varlenelem*/,
+						  -1 /*varlenarray*/,
+						  &isnull);
+			current = DatumGetCString(DirectFunctionCall1(textout, d));
+			if (strncmp(current, newval, strlen(name) + 1)==0)
+			{
+				index = i;
+				break;
+			}
+		}
+
+		isnull = false;
+		a = array_set(array, 1, &index, datum, false/*notbyval*/, -1, -1, &isnull);
+	}
+	else
+		a = construct_array(&datum, 1, false, -1, 'i');
+
+	return a;
+}
+
+
+
+ArrayType *
+GUCArrayDelete(ArrayType *array, const char *name)
+{
+	ArrayType *newarray;
+	int i;
+	int index;
+
+	Assert(name);
+	Assert(array);
+
+	/* test if the option is valid */
+	set_config_option(name, NULL,
+					  superuser() ? PGC_SUSET : PGC_USERSET,
+					  false, PGC_S_INFINITY);
+
+	newarray = construct_array(NULL, 0, false, -1, 'i');
+	index = 1;
+
+	for (i = 1; i <= ARR_DIMS(array)[0]; i++)
+	{
+		Datum		d;
+		char	   *val;
+		bool		isnull;
+
+		d = array_ref(array, 1, &i,
+					  false /*notbyvalue*/,
+					  -1 /*varlenelem*/,
+					  -1 /*varlenarray*/,
+					  &isnull);
+		val = DatumGetCString(DirectFunctionCall1(textout, d));
+
+		if (strncmp(val, name, strlen(name))==0
+			&& val[strlen(name)] == '=')
+			continue;
+
+		isnull = false;
+		newarray = array_set(newarray, 1, &index, d, false/*notbyval*/, -1, -1, &isnull);
+		index++;
+	}
+
+	return newarray;
+}
diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh
index c75d44c6a30..a3b283ab906 100644
--- a/src/bin/initdb/initdb.sh
+++ b/src/bin/initdb/initdb.sh
@@ -27,7 +27,7 @@
 # Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
 # Portions Copyright (c) 1994, Regents of the University of California
 #
-# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.143 2002/02/18 23:11:28 petere Exp $
+# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.144 2002/03/01 22:45:16 petere Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -579,7 +579,8 @@ CREATE VIEW pg_user AS \
         usesuper, \
         usecatupd, \
         '********'::text as passwd, \
-        valuntil \
+        valuntil, \
+        useconfig \
     FROM pg_shadow;
 
 CREATE VIEW pg_rules AS \
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 447469072b3..f579d0267c3 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.104 2002/02/18 23:11:32 petere Exp $
+ * $Id: catversion.h,v 1.105 2002/03/01 22:45:16 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200202181
+#define CATALOG_VERSION_NO	200203011
 
 #endif
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index f8c68f3ce69..43f806188e6 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_attribute.h,v 1.80 2002/02/18 23:11:33 petere Exp $
+ * $Id: pg_attribute.h,v 1.81 2002/03/01 22:45:16 petere Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -281,6 +281,7 @@ DATA(insert ( 1262 datvacuumxid		28 0  4   7 0 -1 -1 t p f i f f));
 DATA(insert ( 1262 datfrozenxid		28 0  4   8 0 -1 -1 t p f i f f));
 /* do not mark datpath as toastable; GetRawDatabaseInfo won't cope */
 DATA(insert ( 1262 datpath			25 0 -1   9 0 -1 -1 f p f i f f));
+DATA(insert ( 1262 datconfig	  1009 0 -1  10 0 -1 -1 f x f i f f));
 DATA(insert ( 1262 ctid				27 0  6  -1 0 -1 -1 f p f i f f));
 DATA(insert ( 1262 oid				26 0  4  -2 0 -1 -1 t p f i f f));
 DATA(insert ( 1262 xmin				28 0  4  -3 0 -1 -1 t p f i f f));
@@ -351,6 +352,7 @@ DATA(insert ( 1260 usesuper			16	0	1	5 0 -1 -1 t p f c f f));
 DATA(insert ( 1260 usecatupd		16	0	1	6 0 -1 -1 t p f c f f));
 DATA(insert ( 1260 passwd			25	0  -1	7 0 -1 -1 f x f i f f));
 DATA(insert ( 1260 valuntil			702 0	4	8 0 -1 -1 t p f i f f));
+DATA(insert ( 1260 useconfig	  1009  0  -1   9 0 -1 -1 f x f i f f));
 DATA(insert ( 1260 ctid				27 0  6  -1 0 -1 -1 f p f i f f));
 /* no OIDs in pg_shadow */
 DATA(insert ( 1260 xmin				28 0  4  -3 0 -1 -1 t p f i f f));
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index c37c5ec8f4f..2a4bc84a363 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_class.h,v 1.58 2002/02/18 23:11:34 petere Exp $
+ * $Id: pg_class.h,v 1.59 2002/03/01 22:45:17 petere Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -140,11 +140,11 @@ DATA(insert OID = 1255 (  pg_proc		81	PGUID 0 1255 0 0 0 0 f f r 18 0 0 0 0 0 t
 DESCR("");
 DATA(insert OID = 1259 (  pg_class		83	PGUID 0 1259 0 0 0 0 f f r 23 0 0 0 0 0 t f f f _null_ ));
 DESCR("");
-DATA(insert OID = 1260 (  pg_shadow		86	PGUID 0 1260 0 0 0 0 f t r 8  0 0 0 0 0 f f f f _null_ ));
+DATA(insert OID = 1260 (  pg_shadow		86	PGUID 0 1260 0 0 0 0 f t r 9  0 0 0 0 0 f f f f _null_ ));
 DESCR("");
 DATA(insert OID = 1261 (  pg_group		87	PGUID 0 1261 0 0 0 0 f t r 3  0 0 0 0 0 f f f f _null_ ));
 DESCR("");
-DATA(insert OID = 1262 (  pg_database	88	PGUID 0 1262 0 0 0 0 f t r 9  0 0 0 0 0 t f f f _null_ ));
+DATA(insert OID = 1262 (  pg_database	88	PGUID 0 1262 0 0 0 0 f t r 10  0 0 0 0 0 t f f f _null_ ));
 DESCR("");
 DATA(insert OID = 376  (  pg_xactlock	0	PGUID 0    0 0 0 0 0 f t s 1  0 0 0 0 0 f f f f _null_ ));
 DESCR("");
diff --git a/src/include/catalog/pg_database.h b/src/include/catalog/pg_database.h
index 34df08595ef..5a04e2917ab 100644
--- a/src/include/catalog/pg_database.h
+++ b/src/include/catalog/pg_database.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_database.h,v 1.21 2001/11/05 17:46:32 momjian Exp $
+ * $Id: pg_database.h,v 1.22 2002/03/01 22:45:17 petere Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -42,6 +42,7 @@ CATALOG(pg_database) BOOTSTRAP
 	TransactionId datvacuumxid; /* all XIDs before this are vacuumed */
 	TransactionId datfrozenxid; /* all XIDs before this are frozen */
 	text		datpath;		/* VARIABLE LENGTH FIELD */
+	text		datconfig[1];		/* database-specific GUC */
 } FormData_pg_database;
 
 /* ----------------
@@ -55,7 +56,7 @@ typedef FormData_pg_database *Form_pg_database;
  *		compiler constants for pg_database
  * ----------------
  */
-#define Natts_pg_database				9
+#define Natts_pg_database				10
 #define Anum_pg_database_datname		1
 #define Anum_pg_database_datdba			2
 #define Anum_pg_database_encoding		3
@@ -65,8 +66,9 @@ typedef FormData_pg_database *Form_pg_database;
 #define Anum_pg_database_datvacuumxid	7
 #define Anum_pg_database_datfrozenxid	8
 #define Anum_pg_database_datpath		9
+#define Anum_pg_database_datconfig		10
 
-DATA(insert OID = 1 (  template1 PGUID ENCODING t t 0 0 0 "" ));
+DATA(insert OID = 1 (  template1 PGUID ENCODING t t 0 0 0 "" _null_ ));
 DESCR("Default template database");
 
 #define TemplateDbOid			1
diff --git a/src/include/catalog/pg_shadow.h b/src/include/catalog/pg_shadow.h
index 00ccaeb85f3..2e8d5b771c5 100644
--- a/src/include/catalog/pg_shadow.h
+++ b/src/include/catalog/pg_shadow.h
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_shadow.h,v 1.17 2001/11/05 17:46:32 momjian Exp $
+ * $Id: pg_shadow.h,v 1.18 2002/03/01 22:45:17 petere Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -39,6 +39,7 @@ CATALOG(pg_shadow) BOOTSTRAP BKI_WITHOUT_OIDS
 	bool		usecatupd;
 	text		passwd;
 	int4		valuntil;
+	text		useconfig[1];
 } FormData_pg_shadow;
 
 /* ----------------
@@ -52,7 +53,7 @@ typedef FormData_pg_shadow *Form_pg_shadow;
  *		compiler constants for pg_shadow
  * ----------------
  */
-#define Natts_pg_shadow				8
+#define Natts_pg_shadow				9
 #define Anum_pg_shadow_usename			1
 #define Anum_pg_shadow_usesysid			2
 #define Anum_pg_shadow_usecreatedb		3
@@ -61,6 +62,7 @@ typedef FormData_pg_shadow *Form_pg_shadow;
 #define Anum_pg_shadow_usecatupd		6
 #define Anum_pg_shadow_passwd			7
 #define Anum_pg_shadow_valuntil			8
+#define Anum_pg_shadow_useconfig		9
 
 /* ----------------
  *		initial contents of pg_shadow
@@ -69,7 +71,7 @@ typedef FormData_pg_shadow *Form_pg_shadow;
  * user choices.
  * ----------------
  */
-DATA(insert ( "POSTGRES" PGUID t t t t _null_ _null_ ));
+DATA(insert ( "POSTGRES" PGUID t t t t _null_ _null_ _null_ ));
 
 #define BOOTSTRAP_USESYSID 1
 
diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h
index 0636130c2e1..32c823e152c 100644
--- a/src/include/commands/dbcommands.h
+++ b/src/include/commands/dbcommands.h
@@ -7,16 +7,19 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: dbcommands.h,v 1.20 2002/02/24 20:20:21 tgl Exp $
+ * $Id: dbcommands.h,v 1.21 2002/03/01 22:45:17 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef DBCOMMANDS_H
 #define DBCOMMANDS_H
 
+#include <nodes/parsenodes.h>
+
 extern void createdb(const char *dbname, const char *dbowner,
 					 const char *dbpath, const char *dbtemplate,
 					 int encoding);
 extern void dropdb(const char *dbname);
+extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
 
 #endif   /* DBCOMMANDS_H */
diff --git a/src/include/commands/user.h b/src/include/commands/user.h
index 3a56547a3fa..351c2d6ef6d 100644
--- a/src/include/commands/user.h
+++ b/src/include/commands/user.h
@@ -3,7 +3,7 @@
  * user.h
  *
  *
- * $Id: user.h,v 1.16 2001/11/05 17:46:33 momjian Exp $
+ * $Id: user.h,v 1.17 2002/03/01 22:45:17 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -14,6 +14,7 @@
 
 extern void CreateUser(CreateUserStmt *stmt);
 extern void AlterUser(AlterUserStmt *stmt);
+extern void AlterUserSet(AlterUserSetStmt *stmt);
 extern void DropUser(DropUserStmt *stmt);
 
 extern void CreateGroup(CreateGroupStmt *stmt);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 1d5cf2dceab..96a89d9df56 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.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: nodes.h,v 1.97 2002/02/18 23:11:41 petere Exp $
+ * $Id: nodes.h,v 1.98 2002/03/01 22:45:17 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -194,6 +194,8 @@ typedef enum NodeTag
 	T_DropGroupStmt,
 	T_ReindexStmt,
 	T_CheckPointStmt,
+	T_AlterDatabaseSetStmt,
+	T_AlterUserSetStmt,
 
 	T_A_Expr = 700,
 	T_Attr,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index bfcbc91cd43..564985ae0e3 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.154 2002/02/26 22:47:10 tgl Exp $
+ * $Id: parsenodes.h,v 1.155 2002/03/01 22:45:18 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -359,7 +359,7 @@ typedef struct DropPLangStmt
 
 
 /* ----------------------
- *				Create/Alter/Drop User Statements
+ *	Create/Alter/Drop User Statements
  * ----------------------
  */
 typedef struct CreateUserStmt
@@ -376,6 +376,14 @@ typedef struct AlterUserStmt
 	List	   *options;		/* List of DefElem nodes */
 } AlterUserStmt;
 
+typedef struct AlterUserSetStmt
+{
+	NodeTag		type;
+	char	   *user;
+	char	   *variable;
+	List	   *value;
+} AlterUserSetStmt;
+
 typedef struct DropUserStmt
 {
 	NodeTag		type;
@@ -687,6 +695,18 @@ typedef struct CreatedbStmt
 	int			encoding;		/* MULTIBYTE encoding (-1 = use default) */
 } CreatedbStmt;
 
+/* ----------------------
+ *	Alter Database
+ * ----------------------
+ */
+typedef struct AlterDatabaseSetStmt
+{
+	NodeTag		type;
+	char	   *dbname;
+	char	   *variable;
+	List	   *value;
+} AlterDatabaseSetStmt;
+
 /* ----------------------
  *		Dropdb Statement
  * ----------------------
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index f7361dff47e..d58b9ef66ff 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -4,11 +4,13 @@
  * External declarations pertaining to backend/utils/misc/guc.c and
  * backend/utils/misc/guc-file.l
  *
- * $Id: guc.h,v 1.14 2002/02/23 01:31:37 petere Exp $
+ * $Id: guc.h,v 1.15 2002/03/01 22:45:18 petere Exp $
  */
 #ifndef GUC_H
 #define GUC_H
 
+#include "utils/array.h"
+
 /*
  * Certain options can only be set at certain times. The rules are
  * like this:
@@ -74,6 +76,9 @@ extern bool set_config_option(const char *name, const char *value,
 				  GucContext context, bool DoIt, GucSource source);
 extern void ShowAllGUCConfig(void);
 
+extern void ProcessGUCArray(ArrayType *array, GucSource source);
+extern ArrayType *GUCArrayAdd(ArrayType *array, const char *name, const char *value);
+extern ArrayType *GUCArrayDelete(ArrayType *array, const char *name);
 
 extern bool Debug_print_query;
 extern bool Debug_print_plan;
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index af00a05a339..4f54ed3a7cc 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1288,7 +1288,7 @@ SELECT viewname, definition FROM pg_views ORDER BY viewname;
  pg_statio_user_tables    | SELECT pg_statio_all_tables.relid, pg_statio_all_tables.relname, pg_statio_all_tables.heap_blks_read, pg_statio_all_tables.heap_blks_hit, pg_statio_all_tables.idx_blks_read, pg_statio_all_tables.idx_blks_hit, pg_statio_all_tables.toast_blks_read, pg_statio_all_tables.toast_blks_hit, pg_statio_all_tables.tidx_blks_read, pg_statio_all_tables.tidx_blks_hit FROM pg_statio_all_tables WHERE (pg_statio_all_tables.relname !~ '^pg_'::text);
  pg_stats                 | SELECT c.relname AS tablename, a.attname, s.stanullfrac AS null_frac, s.stawidth AS avg_width, s.stadistinct AS n_distinct, CASE WHEN (1 = s.stakind1) THEN s.stavalues1 WHEN (1 = s.stakind2) THEN s.stavalues2 WHEN (1 = s.stakind3) THEN s.stavalues3 WHEN (1 = s.stakind4) THEN s.stavalues4 ELSE NULL::"_text" END AS most_common_vals, CASE WHEN (1 = s.stakind1) THEN s.stanumbers1 WHEN (1 = s.stakind2) THEN s.stanumbers2 WHEN (1 = s.stakind3) THEN s.stanumbers3 WHEN (1 = s.stakind4) THEN s.stanumbers4 ELSE NULL::"_float4" END AS most_common_freqs, CASE WHEN (2 = s.stakind1) THEN s.stavalues1 WHEN (2 = s.stakind2) THEN s.stavalues2 WHEN (2 = s.stakind3) THEN s.stavalues3 WHEN (2 = s.stakind4) THEN s.stavalues4 ELSE NULL::"_text" END AS histogram_bounds, CASE WHEN (3 = s.stakind1) THEN s.stanumbers1[1] WHEN (3 = s.stakind2) THEN s.stanumbers2[1] WHEN (3 = s.stakind3) THEN s.stanumbers3[1] WHEN (3 = s.stakind4) THEN s.stanumbers4[1] ELSE NULL::float4 END AS correlation FROM pg_class c, pg_attribute a, pg_statistic s WHERE ((((c.oid = s.starelid) AND (c.oid = a.attrelid)) AND (a.attnum = s.staattnum)) AND has_table_privilege(c.oid, 'select'::text));
  pg_tables                | SELECT c.relname AS tablename, pg_get_userbyid(c.relowner) AS tableowner, c.relhasindex AS hasindexes, c.relhasrules AS hasrules, (c.reltriggers > 0) AS hastriggers FROM pg_class c WHERE ((c.relkind = 'r'::"char") OR (c.relkind = 's'::"char"));
- pg_user                  | SELECT pg_shadow.usename, pg_shadow.usesysid, pg_shadow.usecreatedb, pg_shadow.usetrace, pg_shadow.usesuper, pg_shadow.usecatupd, '********'::text AS passwd, pg_shadow.valuntil FROM pg_shadow;
+ pg_user                  | SELECT pg_shadow.usename, pg_shadow.usesysid, pg_shadow.usecreatedb, pg_shadow.usetrace, pg_shadow.usesuper, pg_shadow.usecatupd, '********'::text AS passwd, pg_shadow.valuntil, pg_shadow.useconfig FROM pg_shadow;
  pg_views                 | SELECT c.relname AS viewname, pg_get_userbyid(c.relowner) AS viewowner, pg_get_viewdef(c.relname) AS definition FROM pg_class c WHERE (c.relkind = 'v'::"char");
  rtest_v1                 | SELECT rtest_t1.a, rtest_t1.b FROM rtest_t1;
  rtest_vcomp              | SELECT x.part, (x.size * y.factor) AS size_in_cm FROM rtest_comp x, rtest_unitfact y WHERE (x.unit = y.unit);
-- 
GitLab