]> granicus.if.org Git - postgresql/commitdiff
* User management commands no longer user pg_exec_query_dest -> more robust
authorPeter Eisentraut <peter_e@gmx.net>
Fri, 14 Jan 2000 22:11:38 +0000 (22:11 +0000)
committerPeter Eisentraut <peter_e@gmx.net>
Fri, 14 Jan 2000 22:11:38 +0000 (22:11 +0000)
* Let unprivileged users change their own passwords.

* The password is now an Sconst in the parser, which better reflects its text datatype and also
forces users to quote them.

* If your password is NULL you won't be written to the password file, meaning you can't connect
until you have a password set up (if you use password authentication).

* When you drop a user that owns a database you get an error. The database is not gone.

18 files changed:
doc/src/sgml/Makefile
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/alter_group.sgml [new file with mode: 0644]
doc/src/sgml/ref/alter_user.sgml
doc/src/sgml/ref/commands.sgml
doc/src/sgml/ref/create_group.sgml [new file with mode: 0644]
doc/src/sgml/ref/create_user.sgml
doc/src/sgml/ref/drop_group.sgml [new file with mode: 0644]
doc/src/sgml/ref/drop_user.sgml
src/backend/commands/copy.c
src/backend/commands/user.c
src/backend/parser/gram.y
src/backend/tcop/utility.c
src/backend/utils/misc/superuser.c
src/bin/scripts/createuser
src/include/commands/copy.h
src/include/commands/user.h
src/include/nodes/parsenodes.h

index 59e8431a666c27daaf40f4f6aa6a9f1f4bfdb28e..864d4621c24d1637fa5a91f5951e09afdc980865 100644 (file)
@@ -8,7 +8,7 @@
 #
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/doc/src/sgml/Makefile,v 1.12 1999/12/05 20:21:59 momjian Exp $
+#    $Header: /cvsroot/pgsql/doc/src/sgml/Makefile,v 1.13 2000/01/14 22:11:31 petere Exp $
 #
 #----------------------------------------------------------------------------
 
@@ -85,15 +85,17 @@ APPLICATIONS= createdb.sgml createuser.sgml \
        psql-ref.sgml \
        vacuumdb.sgml
 
-COMMANDS= abort.sgml alter_table.sgml alter_user.sgml \
+COMMANDS= abort.sgml alter_group.sgml alter_table.sgml alter_user.sgml \
        begin.sgml \
        close.sgml cluster.sgml commit.sgml copy.sgml \
-       create_aggregate.sgml create_database.sgml create_function.sgml create_index.sgml \
+       create_aggregate.sgml create_database.sgml create_function.sgml create_group.sgml \
+       create_index.sgml \
        create_language.sgml create_operator.sgml create_rule.sgml create_sequence.sgml \
        create_table.sgml create_table_as.sgml create_trigger.sgml create_type.sgml \
        create_user.sgml create_view.sgml \
        declare.sgml delete.sgml \
-       drop_aggregate.sgml drop_database.sgml drop_function.sgml drop_index.sgml \
+       drop_aggregate.sgml drop_database.sgml drop_function.sgml drop_group.sgml \
+       drop_index.sgml \
        drop_language.sgml drop_operator.sgml drop_rule.sgml drop_sequence.sgml \
        drop_table.sgml drop_trigger.sgml drop_type.sgml drop_user.sgml drop_view.sgml \
        explain.sgml fetch.sgml grant.sgml \
index dc04300f2a223ac78140b34464f3af9b0dc7fb64..09516b166b0c793f5b5114c18f3e1d713f0c7b53 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.14 1999/12/05 20:02:42 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.15 2000/01/14 22:11:32 petere Exp $
 Postgres documentation
 Complete list of usable sgml source files in this directory.
 -->
@@ -40,6 +40,7 @@ Complete list of usable sgml source files in this directory.
 
 <!-- these are in the "commands" reference chapter -->
 <!entity abort              system "abort.sgml">
+<!entity alterGroup         system "alter_group.sgml">
 <!entity alterTable         system "alter_table.sgml">
 <!entity alterUser          system "alter_user.sgml">
 <!entity begin              system "begin.sgml">
@@ -50,6 +51,7 @@ Complete list of usable sgml source files in this directory.
 <!entity createAggregate    system "create_aggregate.sgml">
 <!entity createDatabase     system "create_database.sgml">
 <!entity createFunction     system "create_function.sgml">
+<!entity createGroup        system "create_group.sgml">
 <!entity createIndex        system "create_index.sgml">
 <!entity createLanguage     system "create_language.sgml">
 <!entity createOperator     system "create_operator.sgml">
@@ -66,6 +68,7 @@ Complete list of usable sgml source files in this directory.
 <!entity dropAggregate      system "drop_aggregate.sgml">
 <!entity dropDatabase       system "drop_database.sgml">
 <!entity dropFunction       system "drop_function.sgml">
+<!entity dropGroup          system "drop_group.sgml">
 <!entity dropIndex          system "drop_index.sgml">
 <!entity dropLanguage       system "drop_language.sgml">
 <!entity dropOperator       system "drop_operator.sgml">
diff --git a/doc/src/sgml/ref/alter_group.sgml b/doc/src/sgml/ref/alter_group.sgml
new file mode 100644 (file)
index 0000000..debbe97
--- /dev/null
@@ -0,0 +1,162 @@
+<!--
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_group.sgml,v 1.1 2000/01/14 22:11:32 petere Exp $
+Postgres documentation
+-->
+
+<refentry id="SQL-ALTERGROUP">
+ <refmeta>
+  <refentrytitle id="SQL-ALTERGROUP-title">
+   ALTER GROUP
+  </refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+ <refnamediv>
+  <refname>
+   ALTER GROUP
+  </refname>
+  <refpurpose>
+   Add users to a group, remove users from a group
+  </refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+  <refsynopsisdivinfo>
+   <date>2000-01-14</date>
+  </refsynopsisdivinfo>
+  <synopsis>
+ALTER GROUP <replaceable class="PARAMETER">name</replaceable> ADD USER <replaceable class="PARAMETER">username</replaceable> [, ... ]
+ALTER GROUP <replaceable class="PARAMETER">name</replaceable> DROP USER <replaceable class="PARAMETER">username</replaceable> [, ... ]
+  </synopsis>
+
+  <refsect2 id="R2-SQL-ALTERGROUP-1">
+   <refsect2info>
+    <date>2000-01-14</date>
+   </refsect2info>
+   <title>
+    Inputs
+   </title>
+
+   <para>
+    <variablelist>
+     <varlistentry>
+      <term><replaceable class="PARAMETER">name</replaceable></term>
+      <listitem>
+       <para>
+       The name of the group to modify.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="PARAMETER">username</replaceable></term>
+      <listitem>
+       <para>
+        Users which are to be added or removed from the group. The user
+        names must exist.
+       </para>
+      </listitem>
+     </varlistentry>
+
+    </variablelist>
+   </para>
+  </refsect2>
+    
+  <refsect2 id="R2-SQL-ALTERGROUP-2">
+   <refsect2info>
+    <date>2000-01-14</date>
+   </refsect2info>
+   <title>
+    Outputs
+   </title>
+   <para>
+    <variablelist>
+     <varlistentry>
+      <term><computeroutput>ALTER GROUP</computeroutput></term>
+      <listitem>
+       <para>
+       Message returned if the alteration was successful.
+       </para>
+      </listitem>
+     </varlistentry>
+     
+    </variablelist>
+   </para>
+  </refsect2>
+ </refsynopsisdiv>
+
+ <refsect1 id="R1-SQL-ALTERGROUP-1">
+  <refsect1info>
+   <date>2000-01-14</date>
+  </refsect1info>
+  <title>
+   Description
+  </title>
+  <para>
+   <command>ALTER GROUP</command> is used to change add users to a group or
+   remove them from a group. Only database superusers can use this command.
+   Adding a user to a group does not create the user. Similarly, removing
+   a user from a group does not drop the user itself.
+  </para>
+  <para>
+   Use <xref linkend="SQL-CREATEGROUP" endterm="SQL-CREATEGROUP-title">
+   to create a new group and <xref linkend="SQL-DROPGROUP"
+   endterm="SQL-DROPGROUP-title"> to remove a group.
+  </para>
+ </refsect1>
+
+ <refsect1 id="R1-SQL-ALTERGROUP-2">
+  <title>
+   Usage
+  </title>
+  <para>
+   Add users to a group:
+
+<programlisting>
+ALTER GROUP staff ADD USER karl, john
+</programlisting>
+
+   Remove a user from a group
+
+<programlisting>
+ALTER GROUP workers DROP USER beth
+</programlisting>
+
+  </para>
+ </refsect1>
+
+ <refsect1 id="R1-SQL-ALTERGROUP-3">
+  <title>
+   Compatibility
+  </title>
+    
+  <refsect2 id="R2-SQL-ALTERGROUP-4">
+   <refsect2info>
+    <date>2000-01-14</date>
+   </refsect2info>
+   <title>
+    SQL92
+   </title>
+   <para>
+    There is no <command>ALTER GROUP</command> statement in
+    <acronym>SQL92</acronym>. The concept of roles is
+    similar.
+   </para>
+  </refsect2>
+ </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:
+-->
index 75bcca865462fb03a4ac3f0e3b16aa651ebf5e58..f3ce32bb16feb7ae9aea87993182b146206f5b88 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_user.sgml,v 1.9 1999/11/30 03:57:22 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/alter_user.sgml,v 1.10 2000/01/14 22:11:32 petere Exp $
 Postgres documentation
 -->
 
@@ -24,11 +24,8 @@ Postgres documentation
   </refsynopsisdivinfo>
   <synopsis>
 ALTER USER <replaceable class="PARAMETER">username</replaceable>
-    [ WITH
-     [ SYSID <replaceable class="PARAMETER">uid</replaceable> ]
-     [ PASSWORD <replaceable class="PARAMETER">password</replaceable> ] ]
+    [ WITH 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>
 
@@ -40,24 +37,19 @@ ALTER USER <replaceable class="PARAMETER">username</replaceable>
     Inputs
    </title>
 
-   <para>
-    Refer to <command>CREATE USER</command> for a detailed description of each
-    clause.
-   </para>
-
    <para>
     <variablelist>
      <varlistentry>
-      <term><replaceable class="PARAMETER"> username </replaceable></term>
+      <term><replaceable class="PARAMETER">username</replaceable></term>
       <listitem>
        <para>
-       The Postgres account name of the user whose details are to be altered.
+       The name of the user whose details are to be altered.
        </para>
       </listitem>
      </varlistentry>
 
      <varlistentry>
-      <term><replaceable class="PARAMETER"> password </replaceable></term>
+      <term><replaceable class="PARAMETER">password</replaceable></term>
       <listitem>
        <para>
        The new password to be used for this account.
@@ -66,36 +58,36 @@ ALTER USER <replaceable class="PARAMETER">username</replaceable>
      </varlistentry>
 
      <varlistentry>
-      <term><replaceable class="parameter">uid</replaceable></term>
+      <term>CREATEDB</term>
+      <term>NOCREATEDB</term>
       <listitem>
-       <para>
-        The new <productname>PostgreSQL</productname> user id of the user.
-        Since this number is used as a key into the
-        <literal>pg_shadow</literal>/<literal>pg_user</literal> table
-        throughout the system catalogs, it is not recommended that you change
-        it unless the user in question does not own anything at all and/or
-        you really know what you are doing. Note that it is not necessary that
-        database and <acronym>UNIX</acronym> user ids match, but some people
-        choose to keep the numbers the same.
+       <para> 
+       These clauses define a user's ability to create databases.
+       If CREATEDB is specified, the user being defined will
+       be allowed to create his own databases. Using NOCREATEDB
+       will deny a user the ability to create databases.
        </para>
       </listitem>
      </varlistentry>
 
      <varlistentry>
-      <term><replaceable class="PARAMETER"> groupname </replaceable></term>
+      <term>CREATEUSER</term>
+      <term>NOCREATEUSER</term>
       <listitem>
        <para>
-       The name of an access group into which this account is to be put.
+       These clauses determine whether a user will be permitted to
+       create new users himself. This option will also make the user
+        a superuser who can override all access restrictions.
        </para>
       </listitem>
      </varlistentry>
 
      <varlistentry>
-      <term><replaceable class="PARAMETER"> abstime </replaceable></term>
+      <term><replaceable class="PARAMETER">abstime</replaceable></term>
       <listitem>
        <para>
        The date (and, optionally, the time)
-       at which this user's access is to be terminated.
+       at which this user's password is to expire.
        </para>
       </listitem>
      </varlistentry>
@@ -113,9 +105,7 @@ ALTER USER <replaceable class="PARAMETER">username</replaceable>
    <para>
     <variablelist>
      <varlistentry>
-      <term><computeroutput>
-ALTER USER
-       </computeroutput></term>
+      <term><computeroutput>ALTER USER</computeroutput></term>
       <listitem>
        <para>
        Message returned if the alteration was successful.
@@ -125,7 +115,7 @@ ALTER USER
      
      <varlistentry>
       <term><computeroutput>
-ERROR: alterUser: user "username" does not exist
+ERROR:  ALTER USER: user "username" does not exist
        </computeroutput></term>
       <listitem>
        <para>
@@ -148,39 +138,15 @@ ERROR: alterUser: 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.
-   Also, it is only possible for the
-   <productname>Postgres</productname>
-   user or any user with read and modify permissions on
-   <literal>pg_shadow</literal> to alter user passwords.
+   <productname>PostgreSQL</productname> account. Only a database superuser
+   can change privileges and password expiration with this command. Ordinary
+   users can only change their own password.
   </para>
-
   <para>
-   If any of the clauses of the alter user statement are
-   omitted, the corresponding value in the <literal>pg_shadow</literal> table
-   is left unchanged.
+   Use <xref linkend="SQL-CREATEUSER" endterm="SQL-CREATEUSER-title">
+   to create a new user and <xref linkend="SQL-DROPUSER"
+   endterm="SQL-DROPUSER-title"> to remove a user.
   </para>
-    
-  <refsect2 id="R2-SQL-ALTERUSER-3">
-   <refsect2info>
-    <date>1998-09-08</date>
-   </refsect2info>
-   <title>
-    Notes
-   </title>
-   <para>
-    <command>ALTER USER</command>
-    is a <productname>Postgres</productname>
-    language extension.
-   </para>
-   <para>
-    Refer to <command>CREATE/DROP USER</command>
-    to create or remove a user account.
-   </para>
-   <para>
-    The IN GROUP clause is not yet implemented.
-   </para>
-  </refsect2>
  </refsect1>
 
  <refsect1 id="R1-SQL-ALTERUSER-2">
@@ -190,34 +156,29 @@ ERROR: alterUser: user "username" does not exist
   <para>
    Change a user password:
 
-  <programlisting>
-ALTER USER davide WITH PASSWORD hu8jmn3;
-  </programlisting>
+<programlisting>
+ALTER USER davide WITH PASSWORD 'hu8jmn3';
+</programlisting>
 
    Change a user's valid until date
 
-   <programlisting>
+<programlisting>
 ALTER USER manuel VALID UNTIL 'Jan 31 2030';
-   </programlisting>
+</programlisting>
 
    Change a user's valid until date, specifying that his
    authorisation should expire at midday on 4th May 1998 using
    the time zone which is one hour ahead of UTC
-   <programlisting>
+<programlisting>
 ALTER USER chris VALID UNTIL 'May 4 12:00:00 1998 +1';
-   </programlisting>
+</programlisting>
 
    Give a user the ability to create other users and new databases.
 
-   <programlisting>
+<programlisting>
 ALTER USER miriam CREATEUSER CREATEDB;
-   </programlisting>
-
-   Place a user in two groups
+</programlisting>
 
-   <programlisting>
-ALTER USER miriam IN GROUP sales, payroll;
-   </programlisting>
   </para>
  </refsect1>
 
index a8f32017393797d481968f39f21a0afb6779ed42..8516904dbf1ecdd239c8ea17a98bde9ebdd26f23 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/Attic/commands.sgml,v 1.21 1999/12/05 20:02:42 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/Attic/commands.sgml,v 1.22 2000/01/14 22:11:32 petere Exp $
 Postgres documentation
 -->
 
@@ -14,6 +14,7 @@ Postgres documentation
   </abstract>
 
    &abort;
+   &alterGroup;
    &alterTable;
    &alterUser;
    &begin;
@@ -24,6 +25,7 @@ Postgres documentation
    &createAggregate;
    &createDatabase;
    &createFunction;
+   &createGroup;
    &createIndex;
    &createLanguage;
    &createOperator;
@@ -40,6 +42,7 @@ Postgres documentation
    &dropAggregate;
    &dropDatabase;
    &dropFunction;
+   &dropGroup;
    &dropIndex;
    &dropLanguage;
    &dropOperator;
diff --git a/doc/src/sgml/ref/create_group.sgml b/doc/src/sgml/ref/create_group.sgml
new file mode 100644 (file)
index 0000000..5202372
--- /dev/null
@@ -0,0 +1,176 @@
+<!--
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_group.sgml,v 1.1 2000/01/14 22:11:32 petere Exp $
+Postgres documentation
+-->
+
+<refentry id="SQL-CREATEGROUP">
+ <refmeta>
+  <refentrytitle id="sql-creategroup-title">
+   CREATE GROUP
+  </refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+ <refnamediv>
+  <refname>
+   CREATE GROUP
+  </refname>
+  <refpurpose>
+   Creates a new group
+  </refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+  <refsynopsisdivinfo>
+   <date>2000-01-14</date>
+  </refsynopsisdivinfo>
+  <synopsis>
+CREATE GROUP <replaceable class="PARAMETER">name</replaceable>
+    [ WITH 
+     [ SYSID <replaceable class="PARAMETER">gid</replaceable> ]
+     [ USER  <replaceable class="PARAMETER">username</replaceable> [, ...] ] ]
+  </synopsis>
+  
+  <refsect2 id="R2-SQL-CREATEGROUP-1">
+   <refsect2info>
+    <date>2000-01-14</date>
+   </refsect2info>
+   <title>
+    Inputs
+   </title>
+   <para>
+
+    <variablelist>
+     <varlistentry>
+      <term><replaceable class="parameter">name</replaceable></term>
+      <listitem>
+       <para>
+       The name of the group.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="parameter">gid</replaceable></term>
+      <listitem>
+       <para>
+        The <literal>SYSID</literal> clause can be used to choose
+        the <productname>PostgreSQL</productname> group id of the new
+        group. It is not necessary to do so, however.
+       </para>
+       <para>
+        If this is not specified, the highest assigned group id plus one,
+        starting at 1, will be used as default.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><replaceable class="parameter">username</replaceable></term>
+      <listitem>
+       <para>
+        A list of users to include in the group. The users must already exist.
+       </para>
+      </listitem>
+     </varlistentry>
+
+    </variablelist>
+   </para>
+  </refsect2>
+  
+  <refsect2 id="R2-SQL-CREATEGROUP-2">
+   <refsect2info>
+    <date>2000-01-14</date>
+   </refsect2info>
+   <title>
+    Outputs
+   </title>
+
+   <para>
+    <variablelist>
+     <varlistentry>
+      <term><computeroutput>CREATE GROUP</computeroutput></term>
+      <listitem>
+       <para>
+       Message returned if the command completes successfully.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+  </refsect2>
+ </refsynopsisdiv>
+
+ <refsect1 id="R1-SQL-CREATEGROUP-1">
+  <refsect1info>
+   <date>2000-01-14</date>
+  </refsect1info>
+  <title>
+   Description
+  </title>
+  <para>
+   CREATE GROUP will create a new group in the database installation.
+   Refer to the adminstrator's guide for information about using groups
+   for authentication.
+   You must be a database superuser to use this command.
+  </para>
+  <para>
+   Use <xref linkend="SQL-ALTERGROUP" endterm="SQL-ALTERGROUP-title">
+   to change a group's membership, and <xref linkend="SQL-DROPGROUP"
+   endterm="SQL-DROPGROUP-title"> to remove a group.
+  </para>  
+ </refsect1> 
+ <refsect1 id="R1-SQL-CREATEGROUP-2">
+  <title>
+   Usage
+  </title>
+  <para>
+   Create an empty group:
+<programlisting>
+CREATE GROUP staff
+</programlisting>
+  </para>
+
+  <para>
+   Create a group with members:
+<programlisting>
+CREATE GROUP marketing WITH USER jonathan, david
+</programlisting>
+  </para>
+ </refsect1>
+ <refsect1 id="R1-SQL-CREATEGROUP-3">
+  <title>
+   Compatibility
+  </title>
+  
+  <refsect2 id="R2-SQL-CREATEGROUP-4">
+   <refsect2info>
+    <date>2000-01-14</date>
+   </refsect2info>
+   <title>
+    SQL92
+   </title>
+
+   <para>
+    There is no <command>CREATE GROUP</command> statement in SQL92.
+    Roles are similar in concept to groups.
+   </para>
+  </refsect2>
+ </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:
+-->
index 2f5d1d7feea7b63540ef39a94c90f25ffd44f032..6994837751d4112b16ea86d050fd1d795de3bfe7 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_user.sgml,v 1.12 1999/12/04 05:03:49 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_user.sgml,v 1.13 2000/01/14 22:11:32 petere Exp $
 Postgres documentation
 -->
 
@@ -15,7 +15,7 @@ Postgres documentation
    CREATE USER
   </refname>
   <refpurpose>
-   Creates account information for a new user
+   Creates a new database user
   </refpurpose>
  </refnamediv>
  <refsynopsisdiv>
@@ -26,7 +26,7 @@ Postgres documentation
 CREATE USER <replaceable class="PARAMETER">username</replaceable>
     [ WITH
      [ SYSID <replaceable class="PARAMETER">uid</replaceable> ]
-     [ PASSWORD <replaceable class="PARAMETER">password</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>' ]
@@ -61,13 +61,6 @@ CREATE USER <replaceable class="PARAMETER">username</replaceable>
         match the <acronym>UNIX</acronym> user ids, but some people
         choose to keep the numbers the same.
        </para>
-       <para>
-       If you still want the
-       OS user id and the <filename>usesysid</filename> to match
-       for any given user,
-       use the <application>createuser</application> script provided with
-       the <productname>Postgres</productname> distribution.
-       </para>
        <para>
         If this is not specified, the highest assigned user id plus one
         will be used as default.
@@ -79,30 +72,11 @@ CREATE USER <replaceable class="PARAMETER">username</replaceable>
       <term><replaceable class="parameter">password</replaceable></term>
       <listitem>
        <para>
-       The PASSWORD clause sets the user's password within
-       the "<filename>pg_shadow</filename>" table. For this reason,
-       <filename>"pg_shadow</filename>" is no
-       longer accessible to the instance of
-       <productname>Postgres</productname> that the
-       <productname>Postgres</productname>
-       user's password is initially set to NULL.
-       </para>
-       <para>
-       When a
-       user's password in the "<filename>pg_shadow</filename>"
-       table is NULL, user
-       authentication proceeds as it historically has (HBA,
-       PG_PASSWORD, etc). However, if a password is set for a
-       user, a new authentication system supplants any other
-       configured for the <productname>Postgres</productname>
-       instance, and the password
-       stored in the "<filename>pg_shadow</filename>" table is used
-       for authentication.
-       For more details on how this authentication system
-       functions see pg_crypt(3). If the WITH PASSWORD clause is
-       omitted, the user's password is set to the empty
-       string which equates to a NULL value in the authentication
-       system mentioned above.
+        Sets the user's password. If you do not plan to use password
+        authentication you can omit this option, otherwise the user
+        won't be able to connect to a password-authenticated server.
+        See pg_hba.conf(5) or the administrator's guide for details on
+        how to set up authentication mechanisms.
        </para>
       </listitem>
      </varlistentry>
@@ -127,8 +101,8 @@ CREATE USER <replaceable class="PARAMETER">username</replaceable>
       <listitem>
        <para>
        These clauses determine whether a user will be permitted to
-       create new
-       users in an instance of <productname>Postgres</productname>.
+       create new users himself. This option will also make the user
+        a superuser who can override all access restrictions.
        Omitting this clause will set the user's value of this
        attribute to be NOCREATEUSER.
        </para>
@@ -149,15 +123,8 @@ CREATE USER <replaceable class="PARAMETER">username</replaceable>
       <listitem>
        <para>
        The VALID UNTIL clause sets an absolute time after which the
-       user's <productname>Postgres</productname>
-       login is no longer valid. Please note that
-       if a user does not have a password defined in the
-       "<filename>pg_shadow</filename>"
-       table, the valid until date will not be checked
-       during user authentication. If this clause is omitted,
-       a NULL value is stored in "<filename>pg_shadow</filename>" 
-       for this attribute,
-       and the login will be valid for all time.
+       user's password is no longer valid. 
+       If this clause is omitted the login will be valid for all time.
        </para>
       </listitem>
      </varlistentry>
@@ -176,9 +143,7 @@ CREATE USER <replaceable class="PARAMETER">username</replaceable>
    <para>
     <variablelist>
      <varlistentry>
-      <term><computeroutput>
-CREATE USER
-       </computeroutput></term>
+      <term><computeroutput>CREATE USER</computeroutput></term>
       <listitem>
        <para>
        Message returned if the command completes successfully.
@@ -199,61 +164,38 @@ CREATE USER
   </title>
   <para>
    CREATE USER will add a new user to an instance of 
-   <productname>PostgreSQL</productname>.
+   <productname>PostgreSQL</productname>. Refer to the adminstrator's
+   guide for information about managing users and authentication.
+   You must be a database superuser to use this command.
   </para>
-
-  <refsect2 id="R2-SQL-CREATEUSER-3">
-   <refsect2info>
-    <date>1998-09-21</date>
-   </refsect2info>
-   <title>
-    Notes
-   </title>
-   <para>
-    <command>CREATE USER</command> statement is a
-    <productname>Postgres</productname> language extension.
-   </para>
-   <para>
-    Use <command>DROP USER</command> or <command>ALTER USER</command>
-    statements to remove or modify a user account.
-   </para>
-   <para>
-    Refer to the <filename>pg_shadow</filename> table for further information.
-   </para>
-   <programlisting>
-      Table "pg_shadow"
-  Attribute  |  Type   | Extra
--------------+---------+-------
- usename     | name    |
- usesysid    | int4    |
- usecreatedb | bool    |
- usetrace    | bool    |
- usesuper    | bool    |
- usecatupd   | bool    |
- passwd      | text    |
- valuntil    | abstime |
-   </programlisting>
-  </refsect2>
- </refsect1>
-  
+  <para>
+   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.
+   <productname>PostgreSQL</productname>
+   comes with a script <xref linkend="APP-CREATEUSER"
+   endterm="APP-CREATEUSER-title">
+   which has the same functionality as this command (in fact, it calls this command)
+   but can be run from the command shell.
+  </para>  
+ </refsect1> 
  <refsect1 id="R1-SQL-CREATEUSER-2">
   <title>
    Usage
   </title>
   <para>
    Create a user with no password:
-
-   <programlisting>
+<programlisting>
 CREATE USER jonathan
-   </programlisting>
+</programlisting>
   </para>
 
   <para>
    Create a user with a password:
-
-   <programlisting>
-CREATE USER davide WITH PASSWORD "jw8s0F4"
-   </programlisting>
+<programlisting>
+CREATE USER davide WITH PASSWORD 'jw8s0F4'
+</programlisting>
   </para>
 
   <para>
@@ -261,17 +203,16 @@ CREATE USER davide WITH PASSWORD "jw8s0F4"
    Note that after one second has ticked in 2002, the account is not
    valid:
 
-   <programlisting>
-CREATE USER miriam WITH PASSWORD "jw8s0F4" VALID UNTIL 'Jan 1 2002'
-   </programlisting>
+<programlisting>
+CREATE USER miriam WITH PASSWORD 'jw8s0F4' VALID UNTIL 'Jan 1 2002'
+</programlisting>
   </para>
 
   <para> 
    Create an account where the user can create databases:
-
-   <programlisting>
-CREATE USER manuel WITH PASSWORD "jw8s0F4" CREATEDB
-   </programlisting>
+<programlisting>
+CREATE USER manuel WITH PASSWORD 'jw8s0F4' CREATEDB
+</programlisting>
   </para>
  </refsect1>
  
diff --git a/doc/src/sgml/ref/drop_group.sgml b/doc/src/sgml/ref/drop_group.sgml
new file mode 100644 (file)
index 0000000..0d7db1e
--- /dev/null
@@ -0,0 +1,138 @@
+<!--
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_group.sgml,v 1.1 2000/01/14 22:11:32 petere Exp $
+Postgres documentation
+-->
+
+<refentry id="SQL-DROPGROUP">
+ <refmeta>
+  <refentrytitle id="SQL-DROPGROUP-TITLE">
+   DROP GROUP
+  </refentrytitle>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+ <refnamediv>
+  <refname>
+   DROP GROUP
+  </refname>
+  <refpurpose>
+   Removes a group
+  </refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+  <refsynopsisdivinfo>
+   <date>2000-01-14</date>
+  </refsynopsisdivinfo>
+  <synopsis>
+DROP GROUP <replaceable class="PARAMETER">name</replaceable>
+  </synopsis>
+  
+  <refsect2 id="R2-SQL-DROPGROUP-1">
+   <refsect2info>
+    <date>2000-01-14</date>
+   </refsect2info>
+   <title>
+    Inputs
+   </title>
+   <para>
+
+    <variablelist>
+     <varlistentry>
+      <term><replaceable class="PARAMETER">name</replaceable></term>
+      <listitem>
+       <para>
+       The name of an existing group.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+  </refsect2>
+  
+  <refsect2 id="R2-SQL-DROPGROUP-2">
+   <refsect2info>
+    <date>2000-01-14</date>
+   </refsect2info>
+   <title>
+    Outputs
+   </title>
+   <para>
+    <variablelist>
+     <varlistentry>
+      <term><computeroutput>DROP GROUP</computeroutput></term>
+      <listitem>
+       <para>
+       The message returned if the group is successfully deleted.
+       </para>
+      </listitem>
+     </varlistentry>
+
+    </variablelist>
+   </para>
+  </refsect2>
+ </refsynopsisdiv>
+
+ <refsect1 id="R1-SQL-DROPGROUP-1">
+  <refsect1info>
+   <date>2000-01-14</date>
+  </refsect1info>
+  <title>
+   Description
+  </title>
+  <para>
+   <command>DROP GROUP</command> removes the specified group from the database.
+   The users in the group are not deleted.
+  </para>
+  <para>
+   Use <xref linkend="SQL-CREATEGROUP" endterm="SQL-CREATEGROUP-title">
+   to add new groups, and <xref linkend="SQL-ALTERGROUP"
+   endterm="SQL-ALTERGROUP-title"> to change a group's membership.
+  </para>  
+ </refsect1>
+
+ <refsect1 id="R1-SQL-DROPGROUP-2">
+  <title>
+   Usage
+  </title>
+  <para>
+   To drop a group:
+<programlisting>
+DROP GROUP staff;
+</programlisting>
+  </para>
+ </refsect1>
+ <refsect1 id="R1-SQL-DROPGROUP-3">
+  <title>
+   Compatibility
+  </title>
+  
+  <refsect2 id="R2-SQL-DROPGROUP-4">
+   <refsect2info>
+    <date>2000-01-14</date>
+   </refsect2info>
+   <title>
+    SQL92
+   </title>
+   <para>
+    There is no <command>DROP GROUP</command> in <acronym>SQL92</acronym>.
+   </para>
+  </refsect2>
+ </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:
+-->
index 27f339f82dc5e0d3311e73e435e0d705df734e76..b2a96e0090f52beee4436f34002034f251c6fa37 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_user.sgml,v 1.9 1999/12/07 22:41:41 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/ref/drop_user.sgml,v 1.10 2000/01/14 22:11:32 petere Exp $
 Postgres documentation
 -->
 
@@ -15,7 +15,7 @@ Postgres documentation
    DROP USER
   </refname>
   <refpurpose>
-   Removes an user account information
+   Removes a user
   </refpurpose>
  </refnamediv>
  <refsynopsisdiv>
@@ -58,18 +58,17 @@ DROP USER <replaceable class="PARAMETER">name</replaceable>
    <para>
     <variablelist>
      <varlistentry>
-      <term><computeroutput>
-DROP
-       </computeroutput></term>
+      <term><computeroutput>DROP USER</computeroutput></term>
       <listitem>
        <para>
        The message returned if the user is successfully deleted.
        </para>
       </listitem>
      </varlistentry>
+
      <varlistentry>
       <term><computeroutput>
-ERROR: removeUser: user "<replaceable class="parameter">name</replaceable>" does not exist.
+ERROR:  DROP USER: user "<replaceable class="parameter">name</replaceable>" does not exist
        </computeroutput></term>
       <listitem>
        <para>
@@ -77,6 +76,18 @@ ERROR: removeUser: user "<replaceable class="parameter">name</replaceable>" does
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry>
+      <term><computeroutput>
+DROP USER: user "<replaceable class="parameter">name</replaceable>" owns database "<replaceable class="parameter">name</replaceable>", cannot be removed
+       </computeroutput></term>
+      <listitem>
+       <para>
+        You must drop the database first or change its ownership.
+       </para>
+      </listitem>
+     </varlistentry>
+
     </variablelist>
    </para>
   </refsect2>
@@ -90,30 +101,20 @@ ERROR: removeUser: user "<replaceable class="parameter">name</replaceable>" does
    Description
   </title>
   <para>
-   <command>DROP USER</command> removes the specified
-   user from the database,
-   along with any databases owned by the user. It
-   does not remove tables, views, or triggers owned by the
-   named user in databases not owned by the user.
+   <command>DROP USER</command> removes the specified user from the database.
+   It does not remove tables, views, or other objects owned by the user. If the
+   user owns any database you get an error.
   </para>
-
-  <refsect2 id="R2-SQL-DROPUSER-3">
-   <refsect2info>
-    <date>1998-09-22</date>
-   </refsect2info>
-   <title>
-    Notes
-   </title>
-   <para>
-    <command>DROP USER</command> is a <productname>Postgres</productname>
-    language extension.
-   </para>
-   <para>
-    Refer to <command>CREATE USER</command> and
-    <command>ALTER USER</command> for information on
-    how to create or modify user accounts.
-   </para>
-  </refsect2>
+  <para>
+   Use <xref linkend="SQL-CREATEUSER" endterm="SQL-CREATEUSER-title">
+   to add new users, and <xref linkend="SQL-ALTERUSER"
+   endterm="SQL-ALTERUSER-title"> to change a user's properties.
+   <productname>PostgreSQL</productname>
+   comes with a script <xref linkend="APP-DROPUSER"
+   endterm="APP-DROPUSER-title">
+   which has the same functionality as this command (in fact, it calls this command)
+   but can be run from the command shell.
+  </para>  
  </refsect1>
 
  <refsect1 id="R1-SQL-DROPUSER-2">
@@ -122,9 +123,9 @@ ERROR: removeUser: user "<replaceable class="parameter">name</replaceable>" does
   </title>
   <para>
    To drop a user account:
-   <programlisting>
-DROP USER Jonathan;
-   </programlisting>
+<programlisting>
+DROP USER jonathan;
+</programlisting>
   </para>
  </refsect1>
  
index f42665a956482ff672f4691f5a4cd3c98ab229ef..7a8dd8800799cd698c8231f6a4ef9fb36d147409 100644 (file)
@@ -6,7 +6,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.94 1999/12/16 22:19:41 wieck Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.95 2000/01/14 22:11:33 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -97,7 +97,11 @@ CopySendData(void *databuf, int datasize, FILE *fp)
                        fe_eof = true;
        }
        else
+    {
                fwrite(databuf, datasize, 1, fp);
+        if (ferror(fp))
+            elog(ERROR, "CopySendData: %s", strerror(errno));
+    }
 }
 
 static void
@@ -219,7 +223,7 @@ CopyDonePeek(FILE *fp, int c, int pickup)
 
 void
 DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
-          char *filename, char *delim, char *null_print, int fileumask)
+          char *filename, char *delim, char *null_print)
 {
 /*----------------------------------------------------------------------------
   Either unload or reload contents of class <relname>, depending on <from>.
@@ -235,11 +239,6 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
   If in the text format, delimit columns with delimiter <delim> and print
   NULL values as <null_print>.
 
-  <fileumask> is the umask(2) setting to use while creating an output file.
-  This should usually be more liberal than the backend's normal 077 umask,
-  but not always (in particular, "pg_pwd" should be written with 077!).
-  Up through version 6.5, <fileumask> was always 000, which was foolhardy.
-
   When loading in the text format from an input stream (as opposed to
   a file), recognize a "." on a line by itself as EOF. Also recognize
   a stream EOF.  When unloading in the text format to an output stream,
@@ -272,12 +271,11 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
        result = pg_aclcheck(relname, UserName, required_access);
        if (result != ACLCHECK_OK)
                elog(ERROR, "%s: %s", relname, aclcheck_error_strings[result]);
-       else if (!pipe && !superuser())
+    if (!pipe && !superuser())
                elog(ERROR, "You must have Postgres superuser privilege to do a COPY "
                         "directly to or from a file.  Anyone can COPY to stdout or "
                         "from stdin.  Psql's \\copy command also works for anyone.");
-       else
-       {
+
                if (from)
                {                                               /* copy from file to database */
                        if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
@@ -324,7 +322,7 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
                        {
                                mode_t          oumask;         /* Pre-existing umask value */
 
-                               oumask = umask((mode_t) fileumask);
+            oumask = umask((mode_t) 022);
 #ifndef __CYGWIN32__
                                fp = AllocateFile(filename, "w");
 #else
@@ -350,7 +348,6 @@ DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
                        if (IsUnderPostmaster)
                                pq_endcopyout(false);
                }
-       }
 
        /*
         * Close the relation.  If reading, we can release the AccessShareLock
index bb18cad4affb8f31e4c98e65f1969bdc67c6c263..f53d34cb0f386383c21c66ba1402b3cac72d1902 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: user.c,v 1.47 1999/12/21 22:39:01 wieck Exp $
+ * $Id: user.c,v 1.48 2000/01/14 22:11:33 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,6 +31,7 @@
 #include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/array.h"
+#include "utils/builtins.h"
 #include "utils/syscache.h"
 
 static void CheckPgUserAclNotNull(void);
@@ -38,30 +39,26 @@ static void CheckPgUserAclNotNull(void);
 #define SQL_LENGTH     512
 
 /*---------------------------------------------------------------------
- * update_pg_pwd
+ * write_password_file / update_pg_pwd
  *
  * copy the modified contents of pg_shadow to a file used by the postmaster
  * for user authentication.  The file is stored as $PGDATA/pg_pwd.
  *
- * NB: caller is responsible for ensuring that only one backend can
- * execute this routine at a time.  Acquiring AccessExclusiveLock on
- * pg_shadow is the standard way to do that.
+ * This function set is both a trigger function for direct updates to pg_shadow
+ * as well as being called directly from create/alter/drop user.
  *---------------------------------------------------------------------
  */
-
-HeapTuple
-update_pg_pwd(void)
+static void
+write_password_file(Relation rel)
 {
        char       *filename,
                           *tempname;
        int                     bufsize;
-
-
-       /*
-        * This is a trigger, so clean out the information provided by
-        * the trigger manager.
-        */
-       CurrentTriggerData = NULL;
+    FILE       *fp;
+    mode_t      oumask;
+    HeapScanDesc scan;
+    HeapTuple   tuple;
+       TupleDesc dsc = RelationGetDescr(rel);
 
        /*
         * Create a temporary filename to be renamed later.  This prevents the
@@ -71,86 +68,134 @@ update_pg_pwd(void)
        filename = crypt_getpwdfilename();
        bufsize = strlen(filename) + 12;
        tempname = (char *) palloc(bufsize);
+
        snprintf(tempname, bufsize, "%s.%d", filename, MyProcPid);
+    oumask = umask((mode_t) 077);
+    fp = AllocateFile(tempname, "w");
+    umask(oumask);
+    if (fp == NULL)
+        elog(ERROR, "%s: %s", tempname, strerror(errno));
+
+    /* read table */
+    scan = heap_beginscan(rel, false, SnapshotSelf, 0, NULL);
+    while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+    {
+        Datum          datum_n, datum_p, datum_v;
+        bool        null_n, null_p, null_v;
+
+               datum_n = heap_getattr(tuple, Anum_pg_shadow_usename, dsc, &null_n);
+        if (null_n)
+            continue; /* don't allow empty users */
+               datum_p = heap_getattr(tuple, Anum_pg_shadow_passwd, dsc, &null_p);
+        /* It could be argued that people having a null password
+           shouldn't be allowed to connect, because they need
+           to have a password set up first. If you think assuming
+           an empty password in that case is better, erase the following line. */
+        if (null_p)
+            continue;
+               datum_v = heap_getattr(tuple, Anum_pg_shadow_valuntil, dsc, &null_v);
+
+        /* These fake entries are not really necessary. To remove them, the parser
+           in backend/libpq/crypt.c would need to be adjusted. Initdb might also
+           need adjustments. */
+        fprintf(fp, 
+                "%s"
+                CRYPT_PWD_FILE_SEPSTR
+                "0"
+                CRYPT_PWD_FILE_SEPSTR
+                "x"
+                CRYPT_PWD_FILE_SEPSTR
+                "x"
+                CRYPT_PWD_FILE_SEPSTR
+                "x"
+                CRYPT_PWD_FILE_SEPSTR
+                "x"
+                CRYPT_PWD_FILE_SEPSTR
+                "%s"
+                CRYPT_PWD_FILE_SEPSTR
+                "%s\n",
+                nameout(DatumGetName(datum_n)),
+                null_p ? "" : textout((text*)datum_p),
+                null_v ? "\\N" : nabstimeout((AbsoluteTime)datum_v) /* this is how the parser wants it */
+                );
+        if (ferror(fp))
+            elog(ERROR, "%s: %s", tempname, strerror(errno));
+        fflush(fp);
+    }
+    heap_endscan(scan);
+    FreeFile(fp);
 
-       /*
-        * Copy the contents of pg_shadow to the pg_pwd ASCII file using the
-        * SEPCHAR character as the delimiter between fields.  Make sure the
-        * file is created with mode 600 (umask 077).
-        */
-       DoCopy(ShadowRelationName,      /* relname */
-                  false,                               /* binary */
-                  false,                               /* oids */
-                  false,                               /* from */
-                  false,                               /* pipe */
-                  tempname,                    /* filename */
-                  CRYPT_PWD_FILE_SEPSTR, /* delim */
-           "",                  /* nulls */
-                  0077);                               /* fileumask */
-       /*
-        * And rename the temp file to its final name, deleting the old pg_pwd.
-        */
-       rename(tempname, filename);
+    /*
+     * And rename the temp file to its final name, deleting the old pg_pwd.
+     */
+    rename(tempname, filename);
 
-       /*
+    /*
         * Create a flag file the postmaster will detect the next time it
         * tries to authenticate a user.  The postmaster will know to reload
         * the pg_pwd file contents.
         */
        filename = crypt_getpwdreloadfilename();
-       creat(filename, S_IRUSR | S_IWUSR);
+       if (creat(filename, S_IRUSR | S_IWUSR) == -1)
+        elog(ERROR, "%s: %s", filename, strerror(errno));
 
        pfree((void *) tempname);
+}
+
+
+
+/* This is the wrapper for triggers. */
+HeapTuple
+update_pg_pwd(void)
+{
+    Relation rel = heap_openr(ShadowRelationName,  AccessExclusiveLock);
+    write_password_file(rel);
+    heap_close(rel,  AccessExclusiveLock);
 
-       return NULL;
+       /*
+        * This is a trigger, so clean out the information provided by
+        * the trigger manager.
+        */
+       CurrentTriggerData = NULL;
+    return NULL;
 }
 
-/*---------------------------------------------------------------------
- * DefineUser
- *
- * Add the user to the pg_shadow relation, and if specified make sure the
- * user is specified in the desired groups of defined in pg_group.
- *---------------------------------------------------------------------
+
+
+/*
+ * CREATE USER
  */
 void
-DefineUser(CreateUserStmt *stmt, CommandDest dest)
+CreateUser(CreateUserStmt *stmt)
 {
-       char       *pg_shadow,
-                               sql[SQL_LENGTH];
        Relation        pg_shadow_rel;
        TupleDesc       pg_shadow_dsc;
        HeapScanDesc scan;
        HeapTuple       tuple;
+    Datum       new_record[Natts_pg_shadow];
+    char        new_record_nulls[Natts_pg_shadow];
        bool            user_exists = false,
                 sysid_exists = false,
-                               inblock,
-                havesysid,
-                               havepassword,
-                               havevaluntil;
+                havesysid;
        int                     max_id = -1;
     List       *item;
 
-    havesysid    = stmt->sysid >= 0;
-       havepassword = stmt->password && stmt->password[0];
-       havevaluntil = stmt->validUntil && stmt->validUntil[0];
+    havesysid    = stmt->sysid > 0;
 
-       if (havepassword)
+    /* Check some permissions first */
+       if (stmt->password)
                CheckPgUserAclNotNull();
-       if (!(inblock = IsTransactionBlock()))
-               BeginTransactionBlock();
 
-       /*
-        * Make sure the user attempting to create a user can insert into the
-        * pg_shadow relation.
-        */
-       pg_shadow = GetPgUserName();
-       if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR | ACL_AP) != ACLCHECK_OK)
-       {
-               UserAbortTransactionBlock();
-               elog(ERROR, "DefineUser: user \"%s\" does not have SELECT and INSERT privilege for \"%s\"",
-                        pg_shadow, ShadowRelationName);
-               return;
-       }
+    if (!superuser())
+        elog(ERROR, "CREATE USER: permission denied");
+
+    /* The reason for the following is this:
+     * If you start a transaction block, create a user, then roll back the
+     * transaction, the pg_pwd won't get rolled back due to a bug in the
+     * Unix file system ( :}). Hence this is in the interest of security.
+     */
+       if (IsTransactionBlock())
+        elog(ERROR, "CREATE USER: may not be called in a transaction block");
 
        /*
         * Scan the pg_shadow relation to be certain the user or id doesn't already
@@ -184,49 +229,64 @@ DefineUser(CreateUserStmt *stmt, CommandDest dest)
        if (user_exists || sysid_exists)
        {
                heap_close(pg_shadow_rel, AccessExclusiveLock);
-               UserAbortTransactionBlock();
         if (user_exists)
-            elog(ERROR, "DefineUser: user name \"%s\" already exists", stmt->user);
+            elog(ERROR, "CREATE USER: user name \"%s\" already exists", stmt->user);
         else
-            elog(ERROR, "DefineUser: sysid %d is already assigned", stmt->sysid);
+            elog(ERROR, "CREATE USER: sysid %d is already assigned", stmt->sysid);
                return;
        }
 
-       /*
-        * Build the insert statement to be executed.
-        *
-        * XXX Ugly as this code is, it still fails to cope with ' or \ in any of
-        * the provided strings.
-        *
-        * XXX This routine would be *lots* better if it inserted the new
-        * tuple with formtuple/heap_insert.  For one thing, all of the
-        * transaction-block gamesmanship could be eliminated, because
-        * it's only there to make the world safe for a recursive call
-        * to pg_exec_query_dest().
-        */
-       snprintf(sql, SQL_LENGTH,
-                        "insert into %s (usename,usesysid,usecreatedb,usetrace,"
-                        "usesuper,usecatupd,passwd,valuntil) "
-                        "values('%s',%d,'%c','f','%c','%c',%s%s%s,%s%s%s)",
-                        ShadowRelationName,
-                        stmt->user,
-                        havesysid ? stmt->sysid : max_id + 1,
-                        (stmt->createdb && *stmt->createdb) ? 't' : 'f',
-                        (stmt->createuser && *stmt->createuser) ? 't' : 'f',
-                        ((stmt->createdb && *stmt->createdb) ||
-                         (stmt->createuser && *stmt->createuser)) ? 't' : 'f',
-                        havepassword ? "'" : "",
-                        havepassword ? stmt->password : "NULL",
-                        havepassword ? "'" : "",
-                        havevaluntil ? "'" : "",
-                        havevaluntil ? stmt->validUntil : "NULL",
-                        havevaluntil ? "'" : "");
+    /*
+     * Build a tuple to insert
+     */
+    new_record[Anum_pg_shadow_usename-1] = PointerGetDatum(namein(stmt->user)); /* this truncated properly */
+    new_record[Anum_pg_shadow_usesysid-1] = Int32GetDatum(havesysid ? stmt->sysid : max_id + 1);
 
-       /*
-        * XXX If insert fails, say because a bogus valuntil date is given,
-        * need to catch the resulting error and undo our transaction.
-        */
-       pg_exec_query_dest(sql, dest, false);
+    AssertState(BoolIsValid(stmt->createdb));
+    new_record[Anum_pg_shadow_usecreatedb-1] = (Datum)(stmt->createdb);
+    new_record[Anum_pg_shadow_usetrace-1] = (Datum)(false);
+    AssertState(BoolIsValid(stmt->createuser));
+    new_record[Anum_pg_shadow_usesuper-1] = (Datum)(stmt->createuser);
+    /* superuser gets catupd right by default */
+    new_record[Anum_pg_shadow_usecatupd-1] = (Datum)(stmt->createuser);
+
+    if (stmt->password)
+        new_record[Anum_pg_shadow_passwd-1] = PointerGetDatum(textin(stmt->password));
+    if (stmt->validUntil)
+        new_record[Anum_pg_shadow_valuntil-1] = PointerGetDatum(nabstimein(stmt->validUntil));
+
+    new_record_nulls[Anum_pg_shadow_usename-1] = ' ';
+    new_record_nulls[Anum_pg_shadow_usesysid-1] = ' ';
+
+    new_record_nulls[Anum_pg_shadow_usecreatedb-1] = ' ';
+    new_record_nulls[Anum_pg_shadow_usetrace-1] = ' ';
+    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';
+
+    tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
+    Assert(tuple);
+
+    /*
+     * Insert a new record in the pg_shadow table
+     */
+    if (heap_insert(pg_shadow_rel, tuple) == InvalidOid)
+        elog(ERROR, "CREATE USER: heap_insert failed");
+
+    /*
+     * Update indexes
+     */
+    if (RelationGetForm(pg_shadow_rel)->relhasindex) {
+        Relation idescs[Num_pg_shadow_indices];
+      
+        CatalogOpenIndices(Num_pg_shadow_indices, 
+                           Name_pg_shadow_indices, idescs);
+        CatalogIndexInsert(idescs, Num_pg_shadow_indices, pg_shadow_rel, 
+                           tuple);
+        CatalogCloseIndices(Num_pg_shadow_indices, idescs);
+    }
 
        /*
         * Add the user to the groups specified. We'll just call the below
@@ -236,59 +296,49 @@ DefineUser(CreateUserStmt *stmt, CommandDest dest)
     {
         AlterGroupStmt ags;
 
-        ags.name = strVal(lfirst(item));
+        ags.name = strVal(lfirst(item)); /* the group name to add this in */
         ags.action = +1;
-        ags.listUsers = lcons((void*)makeString(stmt->user), NIL);
-        AlterGroup(&ags, dest);
+        ags.listUsers = lcons((void*)makeInteger(havesysid ? stmt->sysid : max_id + 1), NIL);
+        AlterGroup(&ags, "CREATE USER");
     }
 
        /*
         * Write the updated pg_shadow data to the flat password file.
-        * Because we are still holding AccessExclusiveLock on pg_shadow,
-        * we can be sure no other backend will try to write the flat
-        * file at the same time.
         */
-       update_pg_pwd();
-
+    write_password_file(pg_shadow_rel);
        /*
         * Now we can clean up.
         */
        heap_close(pg_shadow_rel, AccessExclusiveLock);
-
-       if (IsTransactionBlock() && !inblock)
-               EndTransactionBlock();
 }
 
 
+
+/*
+ * ALTER USER
+ */
 extern void
-AlterUser(AlterUserStmt *stmt, CommandDest dest)
+AlterUser(AlterUserStmt *stmt)
 {
-
-       char       *pg_shadow,
-                               sql[SQL_LENGTH];
+    Datum       new_record[Natts_pg_shadow];
+    char        new_record_nulls[Natts_pg_shadow];
        Relation        pg_shadow_rel;
        TupleDesc       pg_shadow_dsc;
-       HeapTuple       tuple;
-       bool            inblock;
-    bool        comma = false;
+       HeapTuple       tuple, new_tuple;
+    bool        null;
 
        if (stmt->password)
                CheckPgUserAclNotNull();
-       if (!(inblock = IsTransactionBlock()))
-               BeginTransactionBlock();
 
-       /*
-        * Make sure the user attempting to create a user can insert into the
-        * pg_shadow relation.
-        */
-       pg_shadow = GetPgUserName();
-       if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
-       {
-               UserAbortTransactionBlock();
-               elog(ERROR, "AlterUser: user \"%s\" does not have SELECT and UPDATE privilege for \"%s\"",
-                        pg_shadow, ShadowRelationName);
-               return;
-       }
+    /* must be superuser or just want to change your own password */
+    if (!superuser() &&
+        !(stmt->createdb==0 && stmt->createuser==0 && !stmt->validUntil
+          && stmt->password && strcmp(GetPgUserName(), stmt->user)==0))
+        elog(ERROR, "ALTER USER: permission denied");
+
+    /* see comments in create user */
+       if (IsTransactionBlock())
+        elog(ERROR, "ALTER USER: may not be called in a transaction block");
 
        /*
         * Scan the pg_shadow relation to be certain the user exists.
@@ -304,142 +354,135 @@ AlterUser(AlterUserStmt *stmt, CommandDest dest)
        if (!HeapTupleIsValid(tuple))
        {
                heap_close(pg_shadow_rel, AccessExclusiveLock);
-               UserAbortTransactionBlock();
-               elog(ERROR, "AlterUser: user \"%s\" does not exist", stmt->user);
+               elog(ERROR, "ALTER USER: user \"%s\" does not exist", stmt->user);
        }
 
-    /* look for duplicate sysid */
-       tuple = SearchSysCacheTuple(SHADOWSYSID,
-                                                               Int32GetDatum(stmt->sysid),
-                                                               0, 0, 0);
-    if (HeapTupleIsValid(tuple))
-    {
-        Datum datum;
-        bool null;
+    /*
+     * Build a tuple to update, perusing the information just obtained
+     */
+    new_record[Anum_pg_shadow_usename-1] = PointerGetDatum(namein(stmt->user));
+    new_record_nulls[Anum_pg_shadow_usename-1] = ' ';
 
-               datum = heap_getattr(tuple, Anum_pg_shadow_usename, pg_shadow_dsc, &null);
-        if (datum && !null && strcmp((char *) datum, stmt->user) != 0)
-        {
-            heap_close(pg_shadow_rel, AccessExclusiveLock);
-            UserAbortTransactionBlock();
-            elog(ERROR, "AlterUser: sysid %d is already assigned", stmt->sysid);
-        }
-    }
+    /* sysid - leave as is */
+    new_record[Anum_pg_shadow_usesysid-1] = heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null);
+    new_record_nulls[Anum_pg_shadow_usesysid-1] = null ? 'n' : ' ';
 
+    /* createdb */
+    if (stmt->createdb == 0)
+    {
+        /* don't change */
+        new_record[Anum_pg_shadow_usecreatedb-1] = heap_getattr(tuple, Anum_pg_shadow_usecreatedb, pg_shadow_dsc, &null);
+        new_record_nulls[Anum_pg_shadow_usecreatedb-1] = null ? 'n' : ' ';
+    }
+    else
+    {
+        new_record[Anum_pg_shadow_usecreatedb-1] = (Datum)(stmt->createdb > 0 ? true : false);
+        new_record_nulls[Anum_pg_shadow_usecreatedb-1] = ' ';
+    }
 
-       /*
-        * Create the update statement to modify the user.
-        *
-        * XXX see diatribe in preceding routine.  This code is just as bogus.
-        */
-       snprintf(sql, SQL_LENGTH, "update %s set ", ShadowRelationName);
+    /* trace - leave as is */
+    new_record[Anum_pg_shadow_usetrace-1] = heap_getattr(tuple, Anum_pg_shadow_usetrace, pg_shadow_dsc, &null);
+    new_record_nulls[Anum_pg_shadow_usetrace-1] = null ? 'n' : ' ';
 
-       if (stmt->password)
+    /* createuser (superuser) */
+    if (stmt->createuser == 0)
     {
-               snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
-                                "passwd = '%s'", stmt->password);
-        comma = true;
+        /* don't change */
+        new_record[Anum_pg_shadow_usesuper-1] = heap_getattr(tuple, Anum_pg_shadow_usesuper, pg_shadow_dsc, &null);
+        new_record_nulls[Anum_pg_shadow_usesuper-1] = null ? 'n' : ' ';
     }
-
-    if (stmt->sysid>=0)
+    else
     {
-        if (comma)
-            strcat(sql, ", ");
-        snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
-                 "usesysid = %d", stmt->sysid);
-        comma = true;
+        new_record[Anum_pg_shadow_usesuper-1] = (Datum)(stmt->createuser > 0 ? true : false);
+        new_record_nulls[Anum_pg_shadow_usesuper-1] = ' ';
     }
 
-       if (stmt->createdb)
+    /* catupd - set to false if someone's superuser priv is being yanked */
+    if (stmt->createuser < 0)
     {
-        if (comma)
-            strcat(sql, ", ");
-               snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
-                                "usecreatedb='%c'",
-                                *stmt->createdb ? 't' : 'f');
-        comma = true;
+        new_record[Anum_pg_shadow_usecatupd-1] = (Datum)(false);
+        new_record_nulls[Anum_pg_shadow_usecatupd-1] = ' ';
+    }
+    else
+    {
+        /* leave alone */
+        new_record[Anum_pg_shadow_usecatupd-1] = heap_getattr(tuple, Anum_pg_shadow_usecatupd, pg_shadow_dsc, &null);
+        new_record_nulls[Anum_pg_shadow_usecatupd-1] = null ? 'n' : ' ';
     }
 
-       if (stmt->createuser)
+    /* password */
+    if (stmt->password)
     {
-        if (comma)
-            strcat(sql, ", ");
-               snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
-                                "usesuper='%c'",
-                                *stmt->createuser ? 't' : 'f');
-        comma = true;
+        new_record[Anum_pg_shadow_passwd-1] = PointerGetDatum(textin(stmt->password));
+        new_record_nulls[Anum_pg_shadow_passwd-1] = ' ';
+    }
+    else
+    {
+        /* leave as is */
+        new_record[Anum_pg_shadow_passwd-1] = heap_getattr(tuple, Anum_pg_shadow_passwd, pg_shadow_dsc, &null);
+        new_record_nulls[Anum_pg_shadow_passwd-1] = null ? 'n' : ' ';
     }
 
-       if (stmt->validUntil)
+    /* valid until */
+    if (stmt->validUntil)
+    {    
+        new_record[Anum_pg_shadow_valuntil-1] = PointerGetDatum(nabstimein(stmt->validUntil));
+        new_record_nulls[Anum_pg_shadow_valuntil-1] = ' ';
+    }
+    else
     {
-        if (comma)
-            strcat(sql, ", ");
-               snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
-                                "valuntil='%s'",
-                                stmt->validUntil);
+        /* leave as is */
+        new_record[Anum_pg_shadow_valuntil-1] = heap_getattr(tuple, Anum_pg_shadow_valuntil, pg_shadow_dsc, &null);
+        new_record_nulls[Anum_pg_shadow_valuntil-1] = null ? 'n' : ' ';
     }
 
-       snprintf(sql + strlen(sql), SQL_LENGTH - strlen(sql),
-                        " where usename = '%s'",
-                        stmt->user);
+    new_tuple = heap_formtuple(pg_shadow_dsc, new_record, new_record_nulls);
+    Assert(new_tuple);
+    /* XXX check return value of this? */
+    heap_update(pg_shadow_rel, &tuple->t_self, new_tuple, NULL);
 
-       pg_exec_query_dest(sql, dest, false);
 
-       /*
-        * Add stuff here for groups?
-        */
-    if (stmt->groupElts)
-        elog(NOTICE, "IN GROUP is not implemented for ALTER USER.");
+    /* Update indexes */
+    if (RelationGetForm(pg_shadow_rel)->relhasindex)
+    {
+        Relation idescs[Num_pg_shadow_indices];
+      
+        CatalogOpenIndices(Num_pg_shadow_indices, 
+                           Name_pg_shadow_indices, idescs);
+        CatalogIndexInsert(idescs, Num_pg_shadow_indices, pg_shadow_rel, 
+                           tuple);
+        CatalogCloseIndices(Num_pg_shadow_indices, idescs);
+    }
 
        /*
         * Write the updated pg_shadow data to the flat password file.
-        * Because we are still holding AccessExclusiveLock on pg_shadow,
-        * we can be sure no other backend will try to write the flat
-        * file at the same time.
         */
-       update_pg_pwd();
+    write_password_file(pg_shadow_rel);
 
        /*
         * Now we can clean up.
         */
        heap_close(pg_shadow_rel, AccessExclusiveLock);
 
-       if (IsTransactionBlock() && !inblock)
-               EndTransactionBlock();
 }
 
 
-extern void
-RemoveUser(char *user, CommandDest dest)
+
+/*
+ * DROP USER
+ */
+void
+DropUser(DropUserStmt *stmt)
 {
-       char       *pg_shadow;
-       Relation        pg_shadow_rel,
-                               pg_rel;
-       TupleDesc       pg_dsc;
-       HeapScanDesc scan;
-       HeapTuple       tuple;
-       Datum           datum;
-       char            sql[SQL_LENGTH];
-       bool            n,
-                               inblock;
-       int32           usesysid;
-       int                     ndbase = 0;
-       char      **dbase = NULL;
+       Relation        pg_shadow_rel;
+       TupleDesc       pg_shadow_dsc;
+    List       *item;
 
-       if (!(inblock = IsTransactionBlock()))
-               BeginTransactionBlock();
+    if (!superuser())
+        elog(ERROR, "DROP USER: permission denied");
 
-       /*
-        * Make sure the user attempting to create a user can delete from the
-        * pg_shadow relation.
-        */
-       pg_shadow = GetPgUserName();
-       if (pg_aclcheck(ShadowRelationName, pg_shadow, ACL_RD | ACL_WR) != ACLCHECK_OK)
-       {
-               UserAbortTransactionBlock();
-               elog(ERROR, "RemoveUser: user \"%s\" does not have SELECT and DELETE privilege for \"%s\"",
-                        pg_shadow, ShadowRelationName);
-       }
+       if (IsTransactionBlock())
+        elog(ERROR, "DROP USER: may not be called in a transaction block");
 
        /*
         * Scan the pg_shadow relation to find the usesysid of the user to be
@@ -447,100 +490,109 @@ RemoveUser(char *user, CommandDest dest)
         * our update of the flat password file.
         */
        pg_shadow_rel = heap_openr(ShadowRelationName, AccessExclusiveLock);
-       pg_dsc = RelationGetDescr(pg_shadow_rel);
+       pg_shadow_dsc = RelationGetDescr(pg_shadow_rel);
 
-       tuple = SearchSysCacheTuple(SHADOWNAME,
-                                                               PointerGetDatum(user),
-                                                               0, 0, 0);
-       if (!HeapTupleIsValid(tuple))
-       {
-               heap_close(pg_shadow_rel, AccessExclusiveLock);
-               UserAbortTransactionBlock();
-               elog(ERROR, "RemoveUser: user \"%s\" does not exist", user);
-       }
+    foreach(item, stmt->users)
+    {
+        HeapTuple      tuple,
+            tmp_tuple;
+        Relation    pg_rel;
+        TupleDesc   pg_dsc;
+        ScanKeyData scankey;
+        HeapScanDesc scan;
+        Datum          datum;
+        bool           null;
+        int32          usesysid;
+        const char *user = strVal(lfirst(item));
+
+        tuple = SearchSysCacheTuple(SHADOWNAME,
+                                    PointerGetDatum(user),
+                                    0, 0, 0);
+        if (!HeapTupleIsValid(tuple))
+        {
+            heap_close(pg_shadow_rel, AccessExclusiveLock);
+            elog(ERROR, "DROP USER: user \"%s\" does not exist%s", user,
+                 (length(stmt->users) > 1) ? " (no users removed)" : "");
+        }
 
-       usesysid = (int32) heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_dsc, &n);
+        usesysid = DatumGetInt32(heap_getattr(tuple, Anum_pg_shadow_usesysid, pg_shadow_dsc, &null));
 
-       /*
-        * Perform a scan of the pg_database relation to find the databases
-        * owned by usesysid.  Then drop them.
-        */
-       pg_rel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
-       pg_dsc = RelationGetDescr(pg_rel);
+        /*-------------------
+         * Check if user still owns a database. If so, error out.
+         *
+         * (It used to be that this function would drop the database automatically.
+         *  This is not only very dangerous for people that don't read the manual,
+         *  it doesn't seem to be the behaviour one would expect either.)
+         *                                                   -- petere 2000/01/14)
+         *-------------------*/
+        pg_rel = heap_openr(DatabaseRelationName, AccessExclusiveLock);
+        pg_dsc = RelationGetDescr(pg_rel);
 
-       scan = heap_beginscan(pg_rel, false, SnapshotNow, 0, NULL);
-       while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
-       {
-               datum = heap_getattr(tuple, Anum_pg_database_datdba, pg_dsc, &n);
-
-               if ((int) datum == usesysid)
-               {
-                       datum = heap_getattr(tuple, Anum_pg_database_datname, pg_dsc, &n);
-                       if (memcmp((void *) datum, "template1", 9) != 0)
-                       {
-                               dbase =
-                                       (char **) repalloc((void *) dbase, sizeof(char *) * (ndbase + 1));
-                               dbase[ndbase] = (char *) palloc(NAMEDATALEN + 1);
-                               memcpy((void *) dbase[ndbase], (void *) datum, NAMEDATALEN);
-                               dbase[ndbase++][NAMEDATALEN] = '\0';
-                       }
-               }
-       }
-       heap_endscan(scan);
-       heap_close(pg_rel, AccessExclusiveLock);
+        ScanKeyEntryInitialize(&scankey, 0x0, Anum_pg_database_datdba, F_INT4EQ,
+                               Int32GetDatum(usesysid));
 
-       while (ndbase--)
-       {
-               elog(NOTICE, "Dropping database %s", dbase[ndbase]);
-               snprintf(sql, SQL_LENGTH, "DROP DATABASE %s", dbase[ndbase]);
-               pfree((void *) dbase[ndbase]);
-               pg_exec_query_dest(sql, dest, false);
-       }
-       if (dbase)
-               pfree((void *) dbase);
+        scan = heap_beginscan(pg_rel, false, SnapshotNow, 1, &scankey);
 
-       /*
-        * Since pg_shadow is global over all databases, one of two things
-        * must be done to insure complete consistency.  First, pg_shadow
-        * could be made non-global. This would elminate the code above for
-        * deleting database and would require the addition of code to delete
-        * tables, views, etc owned by the user.
-        *
-        * The second option would be to create a means of deleting tables, view,
-        * etc. owned by the user from other databases.  pg_shadow is global
-        * and so this must be done at some point.
-        *
-        * Let us not forget that the user should be removed from the pg_groups
-        * also.
-        *
-        * Todd A. Brandys 11/18/1997
-        *
-        */
+        if (HeapTupleIsValid(tmp_tuple = heap_getnext(scan, 0)))
+        {
+            datum = heap_getattr(tmp_tuple, Anum_pg_database_datname, pg_dsc, &null);
+            heap_close(pg_shadow_rel, AccessExclusiveLock);
+            elog(ERROR, "DROP USER: user \"%s\" owns database \"%s\", cannot be removed%s",
+                 user, nameout(DatumGetName(datum)),
+                 (length(stmt->users) > 1) ? " (no users removed)" : ""
+                );
+        }
+            
+        heap_endscan(scan);
+        heap_close(pg_rel, AccessExclusiveLock);
 
-       /*
-        * Remove the user from the pg_shadow table
-        */
-       snprintf(sql, SQL_LENGTH,
-               "delete from %s where usename = '%s'", ShadowRelationName, user);
-       pg_exec_query_dest(sql, dest, false);
+        /*
+         * Somehow we'd have to check for tables, views, etc. owned by the user
+         * as well, but those could be spread out over all sorts of databases
+         * which we don't have access to (easily).
+         */
 
-       /*
-        * Write the updated pg_shadow data to the flat password file.
-        * Because we are still holding AccessExclusiveLock on pg_shadow,
-        * we can be sure no other backend will try to write the flat
-        * file at the same time.
-        */
-       update_pg_pwd();
+        /*
+         * Remove the user from the pg_shadow table
+         */
+        heap_delete(pg_shadow_rel, &tuple->t_self, NULL);
+
+        /*
+         * Remove user from groups
+         *
+         * try calling alter group drop user for every group
+         */
+        pg_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
+        pg_dsc = RelationGetDescr(pg_rel);
+        scan = heap_beginscan(pg_rel, false, SnapshotNow, 0, NULL);
+        while (HeapTupleIsValid(tmp_tuple = heap_getnext(scan, 0)))
+        {
+            AlterGroupStmt ags;
+
+            datum = heap_getattr(tmp_tuple, Anum_pg_group_groname, pg_dsc, &null);
+
+            ags.name = nameout(DatumGetName(datum)); /* the group name from which to try to drop the user */
+            ags.action = -1;
+            ags.listUsers = lcons((void*)makeInteger(usesysid), NIL);
+            AlterGroup(&ags, "DROP USER");
+        }
+        heap_endscan(scan);
+        heap_close(pg_rel, AccessExclusiveLock);        
+    }
 
        /*
-        * Now we can clean up.
+        * Write the updated pg_shadow data to the flat password file.
         */
-       heap_close(pg_shadow_rel, AccessExclusiveLock);
+    write_password_file(pg_shadow_rel);
 
-       if (IsTransactionBlock() && !inblock)
-               EndTransactionBlock();
+    /*
+     * Now we can clean up.
+     */
+    heap_close(pg_shadow_rel, AccessExclusiveLock);
 }
 
+
+
 /*
  * CheckPgUserAclNotNull
  *
@@ -556,51 +608,56 @@ CheckPgUserAclNotNull()
                                                           0, 0, 0);
        if (!HeapTupleIsValid(htup))
        {
-               elog(ERROR, "IsPgUserAclNull: class \"%s\" not found",
+        /* BIG problem */
+               elog(ERROR, "IsPgUserAclNull: \"%s\" not found",
                         ShadowRelationName);
        }
 
        if (heap_attisnull(htup, Anum_pg_class_relacl))
        {
-               elog(NOTICE, "To use passwords, you have to revoke permissions on pg_shadow");
-               elog(NOTICE, "so normal users can not read the passwords.");
-               elog(ERROR, "Try 'REVOKE ALL ON pg_shadow FROM PUBLIC'");
+               elog(ERROR,
+             "To use passwords, you have to revoke permissions on %s "
+             "so normal users cannot read the passwords. "
+             "Try 'REVOKE ALL ON \"%s\" FROM PUBLIC'.",
+             ShadowRelationName, ShadowRelationName);
        }
 
        return;
 }
 
 
-/*** GROUP THINGS ***/
 
+/*
+ * CREATE GROUP
+ */
 void
-CreateGroup(CreateGroupStmt *stmt, CommandDest dest)
+CreateGroup(CreateGroupStmt *stmt)
 {
        Relation        pg_group_rel;
        HeapScanDesc scan;
        HeapTuple       tuple;
     TupleDesc   pg_group_dsc;
-       bool            inblock;
     bool        group_exists = false,
                 sysid_exists = false;
-    int         max_id = -1;
+    int         max_id = 0;
     Datum       new_record[Natts_pg_group];
     char        new_record_nulls[Natts_pg_group];
     List       *item, *newlist=NULL;
     ArrayType  *userarray;
 
-
-       if (!(inblock = IsTransactionBlock()))
-               BeginTransactionBlock();
-
        /*
         * Make sure the user can do this.
         */
-       if (pg_aclcheck(GroupRelationName, GetPgUserName(), ACL_RD | ACL_AP) != ACLCHECK_OK)
-       {
-               UserAbortTransactionBlock();
-               elog(ERROR, "CreateGroup: Permission denied.");
-       }
+       if (!superuser())
+               elog(ERROR, "CREATE GROUP: permission denied");
+
+    /*
+     * There is not real reason for this, but it makes it consistent
+     * with create user, and it seems like a good idea anyway.
+     */
+       if (IsTransactionBlock())
+        elog(ERROR, "CREATE GROUP: may not be called in a transaction block");
+
 
        pg_group_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
        pg_group_dsc = RelationGetDescr(pg_group_rel);
@@ -628,11 +685,10 @@ CreateGroup(CreateGroupStmt *stmt, CommandDest dest)
        if (group_exists || sysid_exists)
        {
                heap_close(pg_group_rel, AccessExclusiveLock);
-               UserAbortTransactionBlock();
         if (group_exists)
-            elog(ERROR, "CreateGroup: Group name \"%s\" already exists.", stmt->name);
+            elog(ERROR, "CREATE GROUP: group name \"%s\" already exists", stmt->name);
         else
-            elog(ERROR, "CreateGroup: Group sysid %d is already assigned.", stmt->sysid);
+            elog(ERROR, "CREATE GROUP: group sysid %d is already assigned", stmt->sysid);
        }
 
     /*
@@ -650,8 +706,7 @@ CreateGroup(CreateGroupStmt *stmt, CommandDest dest)
         if (!HeapTupleIsValid(tuple))
         {
             heap_close(pg_group_rel, AccessExclusiveLock);
-            UserAbortTransactionBlock();
-            elog(ERROR, "CreateGroup: User \"%s\" does not exist.", groupuser);
+            elog(ERROR, "CREATE GROUP: user \"%s\" does not exist", groupuser);
         }
 
         v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
@@ -716,33 +771,34 @@ CreateGroup(CreateGroupStmt *stmt, CommandDest dest)
         CatalogCloseIndices(Num_pg_group_indices, idescs);
     }
 
-       heap_close(pg_group_rel, NoLock);
-
-       if (IsTransactionBlock() && !inblock)
-               EndTransactionBlock();
+       heap_close(pg_group_rel, AccessExclusiveLock);
 }
 
 
 
+/*
+ * ALTER GROUP
+ */
 void
-AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
+AlterGroup(AlterGroupStmt *stmt, const char * tag)
 {
        Relation        pg_group_rel;
     TupleDesc   pg_group_dsc;
-       bool            inblock;
     HeapTuple   group_tuple;
 
-       if (!(inblock = IsTransactionBlock()))
-               BeginTransactionBlock();
-
-       /*
+    /*
         * Make sure the user can do this.
         */
-       if (pg_aclcheck(GroupRelationName, GetPgUserName(), ACL_RD | ACL_WR) != ACLCHECK_OK)
-       {
-               UserAbortTransactionBlock();
-               elog(ERROR, "AlterGroup: Permission denied.");
-       }
+       if (!superuser())
+               elog(ERROR, "%s: permission denied", tag);
+
+    /*
+     * There is not real reason for this, but it makes it consistent
+     * with alter user, and it seems like a good idea anyway.
+     */
+       if (IsTransactionBlock())
+        elog(ERROR, "%s: may not be called in a transaction block", tag);
+
 
     pg_group_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
     pg_group_dsc = RelationGetDescr(pg_group_rel);
@@ -755,69 +811,14 @@ AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
     if (!HeapTupleIsValid(group_tuple = SearchSysCacheTupleCopy(GRONAME, PointerGetDatum(stmt->name), 0, 0, 0)))
        {
         heap_close(pg_group_rel, AccessExclusiveLock);
-               UserAbortTransactionBlock();
-               elog(ERROR, "AlterGroup: Group \"%s\" does not exist.", stmt->name);
+               elog(ERROR, "%s: group \"%s\" does not exist", tag, stmt->name);
        }
 
+    AssertState(stmt->action == +1 || stmt->action == -1);
     /*
      * Now decide what to do.
      */
-    if (stmt->action == 0) /* change sysid */
-    {
-        ScanKeyData   keys[2];
-        HeapTuple        tuple;
-        HeapScanDesc  scan;
-        Datum       new_record[Natts_pg_group];
-        char        new_record_nulls[Natts_pg_group];
-        bool null;
-
-        /*
-         * First check if the id is already assigned.
-         */
-        ScanKeyEntryInitialize(&keys[0], 0x0, Anum_pg_group_grosysid, F_INT4EQ,
-                               Int32GetDatum(stmt->sysid));
-        ScanKeyEntryInitialize(&keys[1], 0x0, Anum_pg_group_groname, F_NAMENE,
-                               PointerGetDatum(stmt->name));
-        scan = heap_beginscan(pg_group_rel, false, SnapshotNow, 2, keys);
-
-        if (HeapTupleIsValid(heap_getnext(scan, false)))
-        {
-            heap_endscan(scan);
-            heap_close(pg_group_rel, AccessExclusiveLock);
-            UserAbortTransactionBlock();
-            elog(ERROR, "AlterGroup: Group sysid %d is already assigned.", stmt->sysid);
-        }
-        heap_endscan(scan);
-
-        /*
-         * Insert the new tuple with the updated sysid
-         */
-        new_record[Anum_pg_group_groname-1] = (Datum)(stmt->name);
-        new_record[Anum_pg_group_grosysid-1] = (Datum)(stmt->sysid);
-        new_record[Anum_pg_group_grolist-1] = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
-        new_record_nulls[Anum_pg_group_groname-1] = ' ';
-        new_record_nulls[Anum_pg_group_grosysid-1] = ' ';
-        new_record_nulls[Anum_pg_group_grolist-1] = null ? 'n' : ' ';
-
-        tuple = heap_formtuple(pg_group_dsc, new_record, new_record_nulls);
-        heap_update(pg_group_rel, &group_tuple->t_self, tuple, NULL);
-
-        /* Update indexes */
-        if (RelationGetForm(pg_group_rel)->relhasindex) {
-            Relation idescs[Num_pg_group_indices];
-      
-            CatalogOpenIndices(Num_pg_group_indices, 
-                               Name_pg_group_indices, idescs);
-            CatalogIndexInsert(idescs, Num_pg_group_indices, pg_group_rel, 
-                               tuple);
-            CatalogCloseIndices(Num_pg_group_indices, idescs);
-        }
-    }
-
-    /*
-     * add users to group 
-     */
-    else if (stmt->action > 0)
+    if (stmt->action == +1) /* add users, might also be invoked by create user */
     {
         Datum       new_record[Natts_pg_group];
         char        new_record_nulls[Natts_pg_group] = { ' ', ' ', ' '};
@@ -853,22 +854,34 @@ AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
         foreach(item, stmt->listUsers)
         {
             Value *v;
-            /* Get the uid of the proposed user to add. */
-            tuple = SearchSysCacheTuple(SHADOWNAME,
-                                        PointerGetDatum(strVal(lfirst(item))),
-                                        0, 0, 0);
-            if (!HeapTupleIsValid(tuple))
+            if (strcmp(tag, "ALTER GROUP")==0)
             {
-                heap_close(pg_group_rel, AccessExclusiveLock);
-                UserAbortTransactionBlock();
-                elog(ERROR, "AlterGroup: User \"%s\" does not exist.", strVal(lfirst(item)));
+                /* Get the uid of the proposed user to add. */
+                tuple = SearchSysCacheTuple(SHADOWNAME,
+                                            PointerGetDatum(strVal(lfirst(item))),
+                                            0, 0, 0);
+                if (!HeapTupleIsValid(tuple))
+                {
+                    heap_close(pg_group_rel, AccessExclusiveLock);
+                    elog(ERROR, "%s: user \"%s\" does not exist", tag, strVal(lfirst(item)));
+                }
+                v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
             }
-            
-            v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
+            else if (strcmp(tag, "CREATE USER")==0)
+            {
+                /* in this case we already know the uid and it wouldn't
+                   be in the cache anyway yet */
+                v = lfirst(item);
+            }
+            else
+                elog(ERROR, "AlterGroup: unknown tag %s", tag);
+
             if (!member(v, newlist))
                 newlist = lcons(v, newlist);
             else
-                elog(NOTICE, "AlterGroup: User \"%s\" is already in group \"%s\".", strVal(lfirst(item)), stmt->name);
+                /* we silently assume here that this error will only come up
+                   in a ALTER GROUP statement */
+                elog(NOTICE, "%s: user \"%s\" is already in group \"%s\"", tag, strVal(lfirst(item)), stmt->name);
         }
              
         newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
@@ -906,17 +919,18 @@ AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
         }
     } /* endif alter group add user */
 
-    /*
-     * drop users from group
-     */
-    else if (stmt->action < 0)
+    else if (stmt->action == -1) /*drop users from group */
     {
         Datum         datum;
         bool          null;
+        bool          is_dropuser = strcmp(tag, "DROP USER")==0;
         
         datum = heap_getattr(group_tuple, Anum_pg_group_grolist, pg_group_dsc, &null);
         if (null)
-            elog(NOTICE, "AlterGroup: Group \"%s\"'s membership is NULL.", stmt->name);
+        {
+            if (!is_dropuser)
+                elog(NOTICE, "ALTER GROUP: group \"%s\" does not have any members", stmt->name);
+        }
         else
         {
             HeapTuple    tuple;
@@ -950,22 +964,28 @@ AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
             foreach(item, stmt->listUsers)
             {
                 Value *v;
-                /* Get the uid of the proposed user to drop. */
-                tuple = SearchSysCacheTuple(SHADOWNAME,
-                                            PointerGetDatum(strVal(lfirst(item))),
-                                            0, 0, 0);
-                if (!HeapTupleIsValid(tuple))
+                if (!is_dropuser)
                 {
-                    heap_close(pg_group_rel, AccessExclusiveLock);
-                    UserAbortTransactionBlock();
-                    elog(ERROR, "AlterGroup: User \"%s\" does not exist.", strVal(lfirst(item)));
+                    /* Get the uid of the proposed user to drop. */
+                    tuple = SearchSysCacheTuple(SHADOWNAME,
+                                                PointerGetDatum(strVal(lfirst(item))),
+                                                0, 0, 0);
+                    if (!HeapTupleIsValid(tuple))
+                    {
+                        heap_close(pg_group_rel, AccessExclusiveLock);
+                        elog(ERROR, "ALTER GROUP: user \"%s\" does not exist", strVal(lfirst(item)));
+                    }
+                    v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
+                }
+                else
+                {
+                    /* for dropuser we already know the uid */
+                    v = lfirst(item);
                 }
-            
-                v = makeInteger(((Form_pg_shadow) GETSTRUCT(tuple))->usesysid);
                 if (member(v, newlist))
                     newlist = LispRemove(v, newlist);
-                else
-                    elog(NOTICE, "AlterGroup: User \"%s\" is not in group \"%s\".", strVal(lfirst(item)), stmt->name);
+                else if (!is_dropuser)
+                    elog(NOTICE, "ALTER GROUP: user \"%s\" is not in group \"%s\"", strVal(lfirst(item)), stmt->name);
             }
 
             newarray = palloc(ARR_OVERHEAD(1) + length(newlist) * sizeof(int32));
@@ -1005,40 +1025,40 @@ AlterGroup(AlterGroupStmt *stmt, CommandDest dest)
         } /* endif group not null */
     } /* endif alter group drop user */
 
-    heap_close(pg_group_rel, NoLock);
+    heap_close(pg_group_rel, AccessExclusiveLock);
 
     pfree(group_tuple);
-
-       if (IsTransactionBlock() && !inblock)
-               EndTransactionBlock();
 }
 
 
 
+/*
+ * DROP GROUP
+ */
 void
-DropGroup(DropGroupStmt *stmt, CommandDest dest)
+DropGroup(DropGroupStmt *stmt)
 {
        Relation        pg_group_rel;
        HeapScanDesc scan;
        HeapTuple       tuple;
     TupleDesc   pg_group_dsc;
-       bool            inblock;
     bool        gro_exists = false;
 
-       if (!(inblock = IsTransactionBlock()))
-               BeginTransactionBlock();
-
-       /*
+    /*
         * Make sure the user can do this.
         */
-       if (pg_aclcheck(GroupRelationName, GetPgUserName(), ACL_RD | ACL_WR) != ACLCHECK_OK)
-       {
-               UserAbortTransactionBlock();
-               elog(ERROR, "DropGroup: Permission denied.");
-       }
+       if (!superuser())
+               elog(ERROR, "DROP GROUP: permission denied");
 
     /*
-     * Scan the pg_group table and delete all matching users.
+     * There is not real reason for this, but it makes it consistent
+     * with drop user, and it seems like a good idea anyway.
+     */
+       if (IsTransactionBlock())
+        elog(ERROR, "DROP GROUP: may not be called in a transaction block");
+
+    /*
+     * Scan the pg_group table and delete all matching groups.
      */
        pg_group_rel = heap_openr(GroupRelationName, AccessExclusiveLock);
        pg_group_dsc = RelationGetDescr(pg_group_rel);
@@ -1055,7 +1075,6 @@ DropGroup(DropGroupStmt *stmt, CommandDest dest)
             gro_exists = true;
             heap_delete(pg_group_rel, &tuple->t_self, NULL);
         }
-
        }
 
        heap_endscan(scan);
@@ -1067,12 +1086,8 @@ DropGroup(DropGroupStmt *stmt, CommandDest dest)
     {
         heap_close(pg_group_rel, AccessExclusiveLock);
                UserAbortTransactionBlock();
-               elog(ERROR, "DropGroup: Group \"%s\" does not exist.", stmt->name);
+               elog(ERROR, "DROP GROUP: group \"%s\" does not exist", stmt->name);
     }
 
-       heap_close(pg_group_rel, NoLock);
-
-       if (IsTransactionBlock() && !inblock)
-               EndTransactionBlock();
+       heap_close(pg_group_rel, AccessExclusiveLock);
 }
-
index e21a26dcd6f9d0d06f902be0c6e7c1858f1cbbfc..069af39f05f5f478774d5d7a8c9263fc7221eac5 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.124 2000/01/13 18:26:07 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.125 2000/01/14 22:11:34 petere Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -90,7 +90,6 @@ static Node *doNegate(Node *n);
        char                            chr;
        char                            *str;
        bool                            boolean;
-       bool*                           pboolean;       /* for pg_shadow privileges */
        List                            *list;
        Node                            *node;
        Value                           *value;
@@ -137,11 +136,11 @@ static Node *doNegate(Node *n);
 %type <ival>   opt_lock, lock_type
 %type <boolean>        opt_lmode
 
-%type <pboolean> user_createdb_clause, user_createuser_clause
+%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_group_list, user_group_clause, users_in_new_group_clause
+%type <list>   user_list, user_group_clause, users_in_new_group_clause
 
 %type <boolean>        TriggerActionTime, TriggerForSpec, PLangTrusted
 
@@ -459,8 +458,8 @@ CreateUserStmt:  CREATE USER UserId
                                        n->user = $3;
                     n->sysid = -1;
                                        n->password = NULL;
-                                       n->createdb = $4;
-                                       n->createuser = $5;
+                                       n->createdb = $4 == +1 ? true : false;
+                                       n->createuser = $5 == +1 ? true : false;
                                        n->groupElts = $6;
                                        n->validUntil = $7;
                                        $$ = (Node *)n;
@@ -473,8 +472,8 @@ CreateUserStmt:  CREATE USER UserId
                                        n->user = $3;
                     n->sysid = $5;
                                        n->password = $6;
-                                       n->createdb = $7;
-                                       n->createuser = $8;
+                                       n->createdb = $7 == +1 ? true : false;
+                                       n->createuser = $8 == +1 ? true : false;
                                        n->groupElts = $9;
                                        n->validUntil = $10;
                                        $$ = (Node *)n;
@@ -489,30 +488,26 @@ CreateUserStmt:  CREATE USER UserId
  *****************************************************************************/
 
 AlterUserStmt:  ALTER USER UserId user_createdb_clause
-                user_createuser_clause user_group_clause user_valid_clause
+                user_createuser_clause user_valid_clause
                                {
                                        AlterUserStmt *n = makeNode(AlterUserStmt);
                                        n->user = $3;
-                    n->sysid = -1;
                                        n->password = NULL;
                                        n->createdb = $4;
                                        n->createuser = $5;
-                                       n->groupElts = $6;
-                                       n->validUntil = $7;
+                                       n->validUntil = $6;
                                        $$ = (Node *)n;
                                }
-              | ALTER USER UserId WITH sysid_clause user_passwd_clause
+              | ALTER USER UserId WITH PASSWORD Sconst
                 user_createdb_clause
-                user_createuser_clause user_group_clause user_valid_clause
+                user_createuser_clause user_valid_clause
                                {
                                        AlterUserStmt *n = makeNode(AlterUserStmt);
                                        n->user = $3;
-                    n->sysid = $5;
                                        n->password = $6;
                                        n->createdb = $7;
                                        n->createuser = $8;
-                                       n->groupElts = $9;
-                                       n->validUntil = $10;
+                    n->validUntil = $9;
                                        $$ = (Node *)n;
                                }
                ;
@@ -524,53 +519,38 @@ AlterUserStmt:  ALTER USER UserId user_createdb_clause
  *
  *****************************************************************************/
 
-DropUserStmt:  DROP USER UserId
+DropUserStmt:  DROP USER user_list
                                {
                                        DropUserStmt *n = makeNode(DropUserStmt);
-                                       n->user = $3;
+                                       n->users = $3;
                                        $$ = (Node *)n;
                                }
                ;
 
-user_passwd_clause:  PASSWORD UserId                   { $$ = $2; }
+user_passwd_clause:  PASSWORD Sconst                   { $$ = $2; }
                         | /*EMPTY*/                                    { $$ = NULL; }
                ;
 
-sysid_clause: SYSID Iconst                              { $$ = $2; }
+sysid_clause: SYSID Iconst
+              {
+                  if ($2 <= 0)
+                      elog(ERROR, "sysid must be positive");
+                  $$ = $2;
+              }
                         | /*EMPTY*/                     { $$ = -1; }
         ;
 
-user_createdb_clause:  CREATEDB
-                               {
-                                       bool*  b;
-                                       $$ = (b = (bool*)palloc(sizeof(bool)));
-                                       *b = true;
-                               }
-                       | NOCREATEDB
-                               {
-                                       bool*  b;
-                                       $$ = (b = (bool*)palloc(sizeof(bool)));
-                                       *b = false;
-                               }
-                       | /*EMPTY*/                                                     { $$ = NULL; }
+user_createdb_clause:  CREATEDB                         { $$ = +1; }
+            | NOCREATEDB                    { $$ = -1; }
+                       | /*EMPTY*/                     { $$ = 0; }
                ;
 
-user_createuser_clause:  CREATEUSER
-                               {
-                                       bool*  b;
-                                       $$ = (b = (bool*)palloc(sizeof(bool)));
-                                       *b = true;
-                               }
-                       | NOCREATEUSER
-                               {
-                                       bool*  b;
-                                       $$ = (b = (bool*)palloc(sizeof(bool)));
-                                       *b = false;
-                               }
-                       | /*EMPTY*/                                                     { $$ = NULL; }
+user_createuser_clause:  CREATEUSER                     { $$ = +1; }
+                       | NOCREATEUSER                  { $$ = -1; }
+                       | /*EMPTY*/                     { $$ = 0; }
                ;
 
-user_group_list:  user_group_list ',' UserId
+user_list:  user_list ',' UserId
                                {
                                        $$ = lcons((void*)makeString($3), $1);
                                }
@@ -580,7 +560,7 @@ user_group_list:  user_group_list ',' UserId
                                }
                ;
 
-user_group_clause:  IN GROUP user_group_list    { $$ = $3; }
+user_group_clause:  IN GROUP user_list    { $$ = $3; }
                        | /*EMPTY*/             { $$ = NULL; }
                ;
 
@@ -615,7 +595,7 @@ CreateGroupStmt:  CREATE GROUP UserId
                   }
                 ;
 
-users_in_new_group_clause:  USER user_group_list   { $$ = $2; }
+users_in_new_group_clause:  USER user_list   { $$ = $2; }
                             | /* EMPTY */          { $$ = NULL; }
                 ;                         
 
@@ -626,17 +606,7 @@ users_in_new_group_clause:  USER user_group_list   { $$ = $2; }
  *
  *****************************************************************************/
 
-AlterGroupStmt: ALTER GROUP UserId WITH SYSID Iconst
-                {
-                        AlterGroupStmt *n = makeNode(AlterGroupStmt);
-                       n->name = $3;
-                        n->sysid = $6;
-                        n->action = 0;
-                        n->listUsers = NULL;
-                        $$ = (Node *)n;
-                }
-                |
-                ALTER GROUP UserId ADD USER user_group_list
+AlterGroupStmt: ALTER GROUP UserId ADD USER user_list
                 {
                         AlterGroupStmt *n = makeNode(AlterGroupStmt);
                        n->name = $3;
@@ -646,7 +616,7 @@ AlterGroupStmt: ALTER GROUP UserId WITH SYSID Iconst
                         $$ = (Node *)n;
                 }
                 |
-                ALTER GROUP UserId DROP USER user_group_list
+                ALTER GROUP UserId DROP USER user_list
                 {
                         AlterGroupStmt *n = makeNode(AlterGroupStmt);
                        n->name = $3;
index 0a97e3acacfc48692d2bfdee13632447f965c104..ed1cf862e7fbaf32e86f8b19e21acb8094b0f6e0 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.77 2000/01/13 18:26:10 petere Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.78 2000/01/14 22:11:35 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -266,11 +266,7 @@ ProcessUtility(Node *parsetree,
                                 */
                                           stmt->filename,
                                           stmt->delimiter,
-                       stmt->null_print,
-                               /*
-                                * specify 022 umask while writing files with COPY.
-                                */
-                                          0022);
+                       stmt->null_print);
                        }
                        break;
 
@@ -775,21 +771,21 @@ ProcessUtility(Node *parsetree,
                        PS_SET_STATUS(commandTag = "CREATE USER");
                        CHECK_IF_ABORTED();
 
-                       DefineUser((CreateUserStmt *) parsetree, dest);
+                       CreateUser((CreateUserStmt *) parsetree);
                        break;
 
                case T_AlterUserStmt:
                        PS_SET_STATUS(commandTag = "ALTER USER");
                        CHECK_IF_ABORTED();
 
-                       AlterUser((AlterUserStmt *) parsetree, dest);
+                       AlterUser((AlterUserStmt *) parsetree);
                        break;
 
                case T_DropUserStmt:
                        PS_SET_STATUS(commandTag = "DROP USER");
                        CHECK_IF_ABORTED();
 
-                       RemoveUser(((DropUserStmt *) parsetree)->user, dest);
+                       DropUser((DropUserStmt *) parsetree);
                        break;
 
                case T_LockStmt:
@@ -810,21 +806,21 @@ ProcessUtility(Node *parsetree,
             PS_SET_STATUS(commandTag = "CREATE GROUP");
                        CHECK_IF_ABORTED();
 
-            CreateGroup((CreateGroupStmt *) parsetree, dest);
+            CreateGroup((CreateGroupStmt *) parsetree);
             break;
 
         case T_AlterGroupStmt:
             PS_SET_STATUS(commandTag = "ALTER GROUP");
                        CHECK_IF_ABORTED();
 
-            AlterGroup((AlterGroupStmt *) parsetree, dest);
+            AlterGroup((AlterGroupStmt *) parsetree, "ALTER GROUP");
             break;
 
         case T_DropGroupStmt:
             PS_SET_STATUS(commandTag = "DROP GROUP");
                        CHECK_IF_ABORTED();
 
-            DropGroup((DropGroupStmt *) parsetree, dest);
+            DropGroup((DropGroupStmt *) parsetree);
             break;
 
                        /*
index c1f0a3231db442b13d7572c9c3fc19a49d92a056..51d0d29c7b6cd7ed99085aff663bcb83bb253b84 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/misc/superuser.c,v 1.12 1999/11/24 16:52:45 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/misc/superuser.c,v 1.13 2000/01/14 22:11:36 petere Exp $
  *
  * DESCRIPTION
  *       See superuser().
@@ -18,6 +18,7 @@
 #include "postgres.h"
 #include "catalog/pg_shadow.h"
 #include "utils/syscache.h"
+#include "miscadmin.h"
 
 bool
 superuser(void)
@@ -26,12 +27,10 @@ superuser(void)
        The Postgres user running this command has Postgres superuser
        privileges.
 --------------------------------------------------------------------------*/
-       extern char *UserName;          /* defined in global.c */
-
        HeapTuple       utup;
 
        utup = SearchSysCacheTuple(SHADOWNAME,
-                                                          PointerGetDatum(UserName),
+                                                          PointerGetDatum(GetPgUserName()),
                                                           0, 0, 0);
        Assert(utup != NULL);
        return ((Form_pg_shadow) GETSTRUCT(utup))->usesuper;
index dd184c9a4e80dee6bdec820858d29b7455044c05..8849994b6e988fe0928b0b795f824a20c5875c82 100644 (file)
@@ -8,7 +8,7 @@
 #
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/bin/scripts/Attic/createuser,v 1.5 2000/01/12 19:36:36 petere Exp $
+#    $Header: /cvsroot/pgsql/src/bin/scripts/Attic/createuser,v 1.6 2000/01/14 22:11:36 petere Exp $
 #
 # Note - this should NOT be setuid.
 #
@@ -193,7 +193,7 @@ QUERY="CREATE USER \"$NewUser\""
 
 SUBQUERY=
 [ "$SysID" ] &&    SUBQUERY="$SUBQUERY SYSID $SysID"
-[ "$Password" ] && SUBQUERY="$SUBQUERY PASSWORD \"$Password\""
+[ "$Password" ] && SUBQUERY="$SUBQUERY PASSWORD '$Password'"
 [ "$SUBQUERY" ] &&        QUERY="$QUERY WITH $SUBQUERY"
 
 [ "$CanCreateDb" = t ] && QUERY="$QUERY CREATEDB"
index 1f2af72122b9f51664e3675c861b49eb8059ca78..4560cd3672cf24285fb65569ad5bf987aaac79de 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: copy.h,v 1.7 1999/12/14 00:08:19 momjian Exp $
+ * $Id: copy.h,v 1.8 2000/01/14 22:11:37 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,6 @@
 
 
 void DoCopy(char *relname, bool binary, bool oids, bool from, bool pipe,
-                       char *filename, char *delim, char *null_print, int fileumask);
+                       char *filename, char *delim, char *null_print);
 
 #endif  /* COPY_H */
index 036f1576820af7cec286e6bea2b429b758610fe2..6a52fe613d981e69aa5d8fa32f616098bb52277b 100644 (file)
 #define USER_H
 
 #include "nodes/parsenodes.h"
-#include "tcop/dest.h"
+#include "access/htup.h"
 
-extern void DefineUser(CreateUserStmt *stmt, CommandDest);
-extern void AlterUser(AlterUserStmt *stmt, CommandDest);
-extern void RemoveUser(char *user, CommandDest);
+extern void CreateUser(CreateUserStmt *stmt);
+extern void AlterUser(AlterUserStmt *stmt);
+extern void DropUser(DropUserStmt *stmt);
 
-extern void CreateGroup(CreateGroupStmt *stmt, CommandDest dest);
-extern void AlterGroup(AlterGroupStmt *stmt, CommandDest dest);
-extern void DropGroup(DropGroupStmt *stmt, CommandDest dest);
+extern void CreateGroup(CreateGroupStmt *stmt);
+extern void AlterGroup(AlterGroupStmt *stmt, const char * tag);
+extern void DropGroup(DropGroupStmt *stmt);
 
 extern HeapTuple update_pg_pwd(void);
 
index 714f06d5db07bf38cea03bb4657e6869af6ed397..0b2a2a0e93aebdd4bbd42e6a32a82e2b4901e93d 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.92 1999/12/16 17:24:19 momjian Exp $
+ * $Id: parsenodes.h,v 1.93 2000/01/14 22:11:38 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -270,18 +270,26 @@ typedef struct CreateUserStmt
        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?            */
+       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  */
 } CreateUserStmt;
 
-typedef CreateUserStmt AlterUserStmt;
+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  */
+} AlterUserStmt;
 
 typedef struct DropUserStmt
 {
        NodeTag         type;
-       char       *user;                       /* PostgreSQL user login                          */
+    List       *users;          /* List of users to remove */
 } DropUserStmt;
 
 
@@ -301,7 +309,7 @@ typedef struct AlterGroupStmt
 {
     NodeTag     type;
     char       *name;           /* name of group to alter */
-    int         action;         /* +1 = add, -1 = drop, 0 = other (HACK!) */
+    int         action;         /* +1 = add, -1 = drop user */
     int         sysid;          /* sysid change */
     List       *listUsers;      /* list of users to add/drop */
 } AlterGroupStmt;