diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index dd103573a5d68c2c9f6df2ef106fac6e5ee6a2b4..15dab71cc0d2ecec70a7160946816d8d92527494 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.207 2009/09/22 23:43:37 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.208 2009/10/05 19:24:32 tgl Exp $ -->
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
  -->
@@ -113,6 +113,11 @@
       <entry>databases within this database cluster</entry>
      </row>
 
+     <row>
+      <entry><link linkend="catalog-pg-default-acl"><structname>pg_default_acl</structname></link></entry>
+      <entry>default privileges for object types</entry>
+     </row>
+
      <row>
       <entry><link linkend="catalog-pg-depend"><structname>pg_depend</structname></link></entry>
       <entry>dependencies between database objects</entry>
@@ -2155,6 +2160,93 @@
  </sect1>
 
 
+ <sect1 id="catalog-pg-default-acl">
+  <title><structname>pg_default_acl</structname></title>
+
+  <indexterm zone="catalog-pg-default-acl">
+   <primary>pg_default_acl</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_default_acl</> stores initial
+   privileges to be assigned to newly created objects.
+  </para>
+
+  <table>
+   <title><structname>pg_default_acl</> Columns</title>
+
+   <tgroup cols="4">
+    <thead>
+     <row>
+      <entry>Name</entry>
+      <entry>Type</entry>
+      <entry>References</entry>
+      <entry>Description</entry>
+     </row>
+    </thead>
+
+    <tbody>
+     <row>
+      <entry><structfield>defaclrole</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry>
+      <entry>The OID of the role associated with this entry</entry>
+     </row>
+
+     <row>
+      <entry><structfield>defaclnamespace</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+      <entry>The OID of the namespace associated with this entry,
+       or 0 if none</entry>
+     </row>
+
+     <row>
+      <entry><structfield>defaclobjtype</structfield></entry>
+      <entry><type>char</type></entry>
+      <entry></entry>
+      <entry>
+       Type of object this entry is for:
+       <literal>r</> = relation (table, view),
+       <literal>S</> = sequence,
+       <literal>f</> = function
+      </entry>
+     </row>
+
+     <row>
+      <entry><structfield>defaclacl</structfield></entry>
+      <entry><type>aclitem[]</type></entry>
+      <entry></entry>
+      <entry>
+       Access privileges that this type of object should have on creation
+      </entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+
+  <para>
+   A <structname>pg_default_acl</> entry shows the initial privileges to
+   be assigned to an object belonging to the indicated user.  There are
+   currently two types of entry: <quote>global</> entries with
+   <structfield>defaclnamespace</> = 0, and <quote>per-schema</> entries
+   that reference a particular schema.  If a global entry is present then
+   it <emphasis>overrides</> the normal hard-wired default privileges
+   for the object type.  A per-schema entry, if present, represents privileges
+   to be <emphasis>added to</> the global or hard-wired default privileges.
+  </para>
+
+  <para>
+   Note that when an ACL entry in another catalog is NULL, it is taken
+   to represent the hard-wired default privileges for its object,
+   <emphasis>not</> whatever might be in <structname>pg_default_acl</>
+   at the moment.  <structname>pg_default_acl</> is only consulted during
+   object creation.
+  </para>
+
+ </sect1>
+
+
  <sect1 id="catalog-pg-depend">
   <title><structname>pg_depend</structname></title>
 
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 845033b6b6696b2bfe11e60f93c99c8dcda35974..c15579c5164916a5134426f1292c16c6db4b5edc 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.75 2009/09/22 23:43:37 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.76 2009/10/05 19:24:33 tgl Exp $
 PostgreSQL documentation
 Complete list of usable sgml source files in this directory.
 -->
@@ -9,6 +9,7 @@ Complete list of usable sgml source files in this directory.
 <!entity alterAggregate     system "alter_aggregate.sgml">
 <!entity alterConversion    system "alter_conversion.sgml">
 <!entity alterDatabase      system "alter_database.sgml">
+<!entity alterDefaultPrivileges system "alter_default_privileges.sgml">
 <!entity alterDomain        system "alter_domain.sgml">
 <!entity alterForeignDataWrapper system "alter_foreign_data_wrapper.sgml">
 <!entity alterFunction      system "alter_function.sgml">
diff --git a/doc/src/sgml/ref/alter_default_privileges.sgml b/doc/src/sgml/ref/alter_default_privileges.sgml
new file mode 100644
index 0000000000000000000000000000000000000000..b2054b178043407e5824aafa890ebf988578272f
--- /dev/null
+++ b/doc/src/sgml/ref/alter_default_privileges.sgml
@@ -0,0 +1,211 @@
+<!--
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_default_privileges.sgml,v 1.1 2009/10/05 19:24:33 tgl Exp $
+PostgreSQL documentation
+-->
+
+<refentry id="SQL-ALTERDEFAULTPRIVILEGES">
+ <refmeta>
+  <refentrytitle id="SQL-ALTERDEFAULTPRIVILEGES-TITLE">ALTER DEFAULT PRIVILEGES</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>ALTER DEFAULT PRIVILEGES</refname>
+  <refpurpose>define default access privileges</refpurpose>
+ </refnamediv>
+
+ <indexterm zone="sql-alterdefaultprivileges">
+  <primary>ALTER DEFAULT PRIVILEGES</primary>
+ </indexterm>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER DEFAULT PRIVILEGES
+    [ FOR { ROLE | USER } <replaceable>target_role</replaceable> [, ...] ]
+    [ IN SCHEMA <replaceable>schema_name</replaceable> [, ...] ]
+    <replaceable class="parameter">abbreviated_grant_or_revoke</replaceable>
+
+<phrase>where <replaceable class="parameter">abbreviated_grant_or_revoke</replaceable> is one of:</phrase>
+
+GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
+    [,...] | ALL [ PRIVILEGES ] }
+    ON TABLE
+    TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
+GRANT { { USAGE | SELECT | UPDATE }
+    [,...] | ALL [ PRIVILEGES ] }
+    ON SEQUENCE
+    TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
+GRANT { EXECUTE | ALL [ PRIVILEGES ] }
+    ON FUNCTION
+    TO { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
+
+REVOKE [ GRANT OPTION FOR ]
+    { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER }
+    [,...] | ALL [ PRIVILEGES ] }
+    ON TABLE
+    FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
+    [ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+    { { USAGE | SELECT | UPDATE }
+    [,...] | ALL [ PRIVILEGES ] }
+    ON SEQUENCE
+    FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
+    [ CASCADE | RESTRICT ]
+
+REVOKE [ GRANT OPTION FOR ]
+    { EXECUTE | ALL [ PRIVILEGES ] }
+    ON FUNCTION
+    FROM { [ GROUP ] <replaceable class="PARAMETER">role_name</replaceable> | PUBLIC } [, ...]
+    [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="sql-alterdefaultprivileges-description">
+  <title>Description</title>
+
+  <para>
+   <command>ALTER DEFAULT PRIVILEGES</> allows you to set the privileges
+   that will be applied to objects created in the future.  (It does not
+   affect privileges assigned to already-existing objects.)  Currently,
+   only the privileges for tables (including views), sequences, and
+   functions can be altered.
+  </para>
+
+  <para>
+   You can change default privileges only for objects that will be created by
+   yourself or by roles that you are a member of.  The privileges can be set
+   globally (i.e., for all objects created in the current database),
+   or just for objects created in specified schemas.  Default privileges
+   that are specified per-schema are added to whatever the global default
+   privileges are for the particular object type.
+  </para>
+
+  <para>
+   As explained under <xref linkend="sql-grant" endterm="sql-grant-title">,
+   the default privileges for any object type normally grant all grantable
+   permissions to the object owner, and may grant some privileges to
+   <literal>PUBLIC</> as well.  However, this behavior can be changed by
+   altering the global default privileges with
+   <command>ALTER DEFAULT PRIVILEGES</>.
+  </para>
+
+ <refsect2>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable>target_role</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing role of which the current role is a member.
+      If <literal>FOR ROLE</> is omitted, the current role is assumed.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable>schema_name</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing schema.  Each <replaceable>target_role</>
+      must have <literal>CREATE</> privileges for each specified schema.
+      If <literal>IN SCHEMA</> is omitted, the global default privileges
+      are altered.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable>role_name</replaceable></term>
+    <listitem>
+     <para>
+      The name of an existing role to grant or revoke privileges for.
+      This parameter, and all the other parameters in
+      <replaceable class="parameter">abbreviated_grant_or_revoke</>,
+      act as described under
+      <xref linkend="sql-grant" endterm="sql-grant-title"> or
+      <xref linkend="sql-revoke" endterm="sql-revoke-title">,
+      except that one is setting permissions for a whole class of objects
+      rather than specific named objects.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect2>
+ </refsect1>
+
+ <refsect1 id="sql-alterdefaultprivileges-notes">
+  <title>Notes</title>
+
+  <para>
+   Use <xref linkend="app-psql">'s <command>\ddp</command> command
+   to obtain information about existing assignments of default privileges.
+   The meaning of the privilege values is the same as explained for
+   <command>\dp</command> under
+   <xref linkend="sql-grant" endterm="sql-grant-title">.
+  </para>
+
+  <para>
+   If you wish to drop a role that has had its global default privileges
+   altered, it is necessary to use <command>DROP OWNED BY</> first,
+   to get rid of the default privileges entry for the role.
+  </para>
+ </refsect1>
+
+ <refsect1 id="sql-alterdefaultprivileges-examples">
+  <title>Examples</title>
+
+  <para>
+   Grant SELECT privilege to everyone for all tables (and views) you
+   subsequently create in schema <literal>myschema</literal>, and allow
+   role <literal>webuser</> to INSERT into them too:
+
+<programlisting>
+ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT SELECT ON TABLE TO PUBLIC;
+ALTER DEFAULT PRIVILEGES IN SCHEMA myschema GRANT INSERT ON TABLE TO webuser;
+</programlisting>
+  </para>
+
+  <para>
+   Undo the above, so that subsequently-created tables won't have any
+   more permissions than normal:
+
+<programlisting>
+ALTER DEFAULT PRIVILEGES IN SCHEMA myschema REVOKE SELECT ON TABLE FROM PUBLIC;
+ALTER DEFAULT PRIVILEGES IN SCHEMA myschema REVOKE INSERT ON TABLE FROM webuser;
+</programlisting>
+  </para>
+
+  <para>
+   Remove the public EXECUTE permission that is normally granted on functions,
+   for all functions subsequently created by role <literal>admin</>:
+
+<programlisting>
+ALTER DEFAULT PRIVILEGES FOR ROLE admin REVOKE EXECUTE ON FUNCTION FROM PUBLIC;
+</programlisting>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   There is no <command>ALTER DEFAULT PRIVILEGES</command> statement in the SQL
+   standard.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-grant" endterm="sql-grant-title"></member>
+   <member><xref linkend="sql-revoke" endterm="sql-revoke-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index 4dddde27b9188abbc7831742d0ce9701fa041a47..2dcf4aa0f0b8f0752cf7e44a08b0328da71d2700 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.77 2009/09/19 10:23:27 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/grant.sgml,v 1.78 2009/10/05 19:24:34 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -80,14 +80,6 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace
    they are different enough to be described separately.
   </para>
 
-  <para>
-   As of <productname>PostgreSQL</productname> 8.1, the concepts of users and
-   groups have been unified into a single kind of entity called a role.
-   It is therefore no longer necessary to use the keyword <literal>GROUP</>
-   to identify whether a grantee is a user or a group.  <literal>GROUP</>
-   is still allowed in the command, but it is a noise word.
-  </para>
-
  <refsect2 id="sql-grant-description-objects">
   <title>GRANT on Database Objects</title>
 
@@ -145,6 +137,9 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace
    security, issue the <command>REVOKE</> in the same transaction that
    creates the object; then there is no window in which another user
    can use the object.)
+   Also, these initial default privilege settings can be changed using the
+   <xref linkend="sql-alterdefaultprivileges" endterm="sql-alterdefaultprivileges-title">
+   command.
   </para>
 
   <para>
@@ -388,6 +383,14 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace
     to revoke access privileges.
    </para>
 
+   <para>
+    Since <productname>PostgreSQL</productname> 8.1, the concepts of users and
+    groups have been unified into a single kind of entity called a role.
+    It is therefore no longer necessary to use the keyword <literal>GROUP</>
+    to identify whether a grantee is a user or a group.  <literal>GROUP</>
+    is still allowed in the command, but it is a noise word.
+   </para>
+
    <para>
     A user may perform <command>SELECT</>, <command>INSERT</>, etc. on a
     column if he holds that privilege for either the specific column or
@@ -518,8 +521,13 @@ GRANT SELECT (col1), UPDATE (col1) ON mytable TO miriam_rw;
     <command>REVOKE</> on an object
     will instantiate the default privileges (producing, for example,
     <literal>{miriam=arwdDxt/miriam}</>) and then modify them per the
-    specified request.  Entries are shown in <quote>Column access
+    specified request.  Similarly, entries are shown in <quote>Column access
     privileges</> only for columns with nondefault privileges.
+    (Note: for this purpose, <quote>default privileges</> always means the
+    built-in default privileges for the object's type.  An object whose
+    privileges have been affected by an <command>ALTER DEFAULT PRIVILEGES</>
+    command will always be shown with an explicit privilege entry that
+    includes the effects of the <command>ALTER</>.)
    </para>
 
    <para>
@@ -602,9 +610,10 @@ GRANT admins TO joe;
  <refsect1>
   <title>See Also</title>
 
-  <simpara>
-   <xref linkend="sql-revoke" endterm="sql-revoke-title">
-  </simpara>
+  <simplelist type="inline">
+   <member><xref linkend="sql-revoke" endterm="sql-revoke-title"></member>
+   <member><xref linkend="sql-alterdefaultprivileges" endterm="sql-alterdefaultprivileges-title"></member>
+  </simplelist>
  </refsect1>
 
 </refentry>
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index f60c3150e9f44941b007964673427b508c9d2b4a..e689d275cb49476566c5b502d73ac128b06f048f 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.229 2009/08/11 12:02:58 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.230 2009/10/05 19:24:34 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -978,6 +978,29 @@ testdb=&gt;
       </varlistentry>
 
 
+      <varlistentry>
+        <term><literal>\ddp [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
+        <listitem>
+        <para>
+        Lists default access privilege settings.  An entry is shown for
+        each role (and schema, if applicable) for which the default
+        privilege settings have been changed from the built-in defaults.
+        If <replaceable class="parameter">pattern</replaceable> is
+        specified, only entries whose role name or schema name matches
+        the pattern are listed.
+        </para>
+
+        <para>
+        The <xref linkend="sql-alterdefaultprivileges"
+        endterm="sql-alterdefaultprivileges-title"> command is used to set
+        default access privileges.  The meaning of the
+        privilege display is explained under
+        <xref linkend="sql-grant" endterm="sql-grant-title">.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\dD[S] [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
         <listitem>
@@ -1142,8 +1165,8 @@ testdb=&gt;
         class="parameter">pattern</replaceable> is specified, only
         those roles whose names match the pattern are listed.
         (This command is now effectively the same as <literal>\du</literal>).
-        If the form <literal>\dg+</literal> is used, additional information 
-        is shown about each role, including the comment for each role.  
+        If the form <literal>\dg+</literal> is used, additional information
+        is shown about each role, including the comment for each role.
         </para>
         </listitem>
       </varlistentry>
@@ -1235,7 +1258,9 @@ testdb=&gt;
         <para>
         The <xref linkend="sql-grant" endterm="sql-grant-title"> and
         <xref linkend="sql-revoke" endterm="sql-revoke-title">
-        commands are used to set access privileges.
+        commands are used to set access privileges.  The meaning of the
+        privilege display is explained under
+        <xref linkend="sql-grant" endterm="sql-grant-title">.
         </para>
         </listitem>
       </varlistentry>
@@ -2045,12 +2070,6 @@ lo_import 152801
         specified, only tables,views and sequences whose names match the pattern are listed.
         </para>
 
-        <para>
-        The <xref linkend="sql-grant" endterm="sql-grant-title"> and
-        <xref linkend="sql-revoke" endterm="sql-revoke-title">
-        commands are used to set access privileges.
-        </para>
-
         <para>
         This is an alias for <command>\dp</command> (<quote>display
         privileges</quote>).
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 48f8040541dfda098d658bac66b54f719e4dce2b..0e72fc5475bd5851652e0bb8f06e7ab911c27c36 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.68 2009/09/22 23:43:37 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.69 2009/10/05 19:24:33 tgl Exp $ -->
 
 <part id="reference">
  <title>Reference</title>
@@ -37,6 +37,7 @@
    &alterAggregate;
    &alterConversion;
    &alterDatabase;
+   &alterDefaultPrivileges;
    &alterDomain;
    &alterForeignDataWrapper;
    &alterFunction;
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index b7624547aac76a6c2cb7a5a0f8f683d31f36fa2e..9d6f854d126bf4366ed7dd5a73aeadf91dae1a0e 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.99 2009/09/27 01:32:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.100 2009/10/05 19:24:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -226,6 +226,7 @@ Boot_CreateStmt:
 													  0,
 													  ONCOMMIT_NOOP,
 													  (Datum) 0,
+													  false,
 													  true);
 						elog(DEBUG4, "relation created with oid %u", id);
 					}
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index e4414ed2bf5c9979d9637b063aa9c3991dbea1a4..53784e9c54b07dfdd22a8b9435e13c74997b7682 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -2,7 +2,7 @@
 #
 # Makefile for backend/catalog
 #
-# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.71 2009/08/26 22:24:43 petere Exp $
+# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.72 2009/10/05 19:24:34 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -37,6 +37,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
 	pg_ts_parser.h pg_ts_template.h \
 	pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
+	pg_default_acl.h \
 	toasting.h indexing.h \
     )
 
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index bb15e78d1c948b0ffcd5072e54a395854166e1f8..b06e587a1b1c6eeb8446d0151816fec2a813e74f 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.154 2009/06/11 14:48:54 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/aclchk.c,v 1.155 2009/10/05 19:24:35 tgl Exp $
  *
  * NOTES
  *	  See acl.h.
@@ -27,6 +27,7 @@
 #include "catalog/pg_authid.h"
 #include "catalog/pg_conversion.h"
 #include "catalog/pg_database.h"
+#include "catalog/pg_default_acl.h"
 #include "catalog/pg_foreign_data_wrapper.h"
 #include "catalog/pg_foreign_server.h"
 #include "catalog/pg_language.h"
@@ -51,6 +52,51 @@
 #include "utils/tqual.h"
 
 
+/*
+ * The information about one Grant/Revoke statement, in internal format: object
+ * and grantees names have been turned into Oids, the privilege list is an
+ * AclMode bitmask.  If 'privileges' is ACL_NO_RIGHTS (the 0 value) and
+ * all_privs is true, 'privileges' will be internally set to the right kind of
+ * ACL_ALL_RIGHTS_*, depending on the object type (NB - this will modify the
+ * InternalGrant struct!)
+ *
+ * Note: 'all_privs' and 'privileges' represent object-level privileges only.
+ * There might also be column-level privilege specifications, which are
+ * represented in col_privs (this is a list of untransformed AccessPriv nodes).
+ * Column privileges are only valid for objtype ACL_OBJECT_RELATION.
+ */
+typedef struct
+{
+	bool		is_grant;
+	GrantObjectType objtype;
+	List	   *objects;
+	bool		all_privs;
+	AclMode		privileges;
+	List	   *col_privs;
+	List	   *grantees;
+	bool		grant_option;
+	DropBehavior behavior;
+} InternalGrant;
+
+/*
+ * Internal format used by ALTER DEFAULT PRIVILEGES.
+ */
+typedef struct
+{
+	Oid			roleid;				/* owning role */
+	Oid			nspid;				/* namespace, or InvalidOid if none */
+	/* remaining fields are same as in InternalGrant: */
+	bool		is_grant;
+	GrantObjectType objtype;
+	bool		all_privs;
+	AclMode		privileges;
+	List	   *grantees;
+	bool		grant_option;
+	DropBehavior behavior;
+} InternalDefaultACL;
+
+
+static void ExecGrantStmt_oids(InternalGrant *istmt);
 static void ExecGrant_Relation(InternalGrant *grantStmt);
 static void ExecGrant_Database(InternalGrant *grantStmt);
 static void ExecGrant_Fdw(InternalGrant *grantStmt);
@@ -60,6 +106,9 @@ static void ExecGrant_Language(InternalGrant *grantStmt);
 static void ExecGrant_Namespace(InternalGrant *grantStmt);
 static void ExecGrant_Tablespace(InternalGrant *grantStmt);
 
+static void SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames);
+static void SetDefaultACL(InternalDefaultACL *iacls);
+
 static List *objectNamesToOids(GrantObjectType objtype, List *objnames);
 static void expand_col_privileges(List *colnames, Oid table_oid,
 					  AclMode this_privileges,
@@ -361,11 +410,11 @@ ExecuteGrantStmt(GrantStmt *stmt)
 			errormsg = gettext_noop("invalid privilege type %s for foreign server");
 			break;
 		default:
+			elog(ERROR, "unrecognized GrantStmt.objtype: %d",
+				 (int) stmt->objtype);
 			/* keep compiler quiet */
 			all_privileges = ACL_NO_RIGHTS;
 			errormsg = NULL;
-			elog(ERROR, "unrecognized GrantStmt.objtype: %d",
-				 (int) stmt->objtype);
 	}
 
 	if (stmt->privileges == NIL)
@@ -421,11 +470,9 @@ ExecuteGrantStmt(GrantStmt *stmt)
 /*
  * ExecGrantStmt_oids
  *
- * "Internal" entrypoint for granting and revoking privileges.	This is
- * exported for pg_shdepend.c to use in revoking privileges when dropping
- * a role.
+ * Internal entry point for granting and revoking privileges.
  */
-void
+static void
 ExecGrantStmt_oids(InternalGrant *istmt)
 {
 	switch (istmt->objtype)
@@ -609,6 +656,563 @@ objectNamesToOids(GrantObjectType objtype, List *objnames)
 	return objects;
 }
 
+/*
+ * ALTER DEFAULT PRIVILEGES statement
+ */
+void
+ExecAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *stmt)
+{
+	GrantStmt  *action = stmt->action;
+	InternalDefaultACL iacls;
+	ListCell   *cell;
+	List	   *rolenames = NIL;
+	List	   *nspnames = NIL;
+	DefElem    *drolenames = NULL;
+	DefElem    *dnspnames = NULL;
+	AclMode		all_privileges;
+	const char *errormsg;
+
+	/* Deconstruct the "options" part of the statement */
+	foreach(cell, stmt->options)
+	{
+		DefElem    *defel = (DefElem *) lfirst(cell);
+
+		if (strcmp(defel->defname, "schemas") == 0)
+		{
+			if (dnspnames)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			dnspnames = defel;
+		}
+		else if (strcmp(defel->defname, "roles") == 0)
+		{
+			if (drolenames)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("conflicting or redundant options")));
+			drolenames = defel;
+		}
+		else
+			elog(ERROR, "option \"%s\" not recognized", defel->defname);
+	}
+
+	if (dnspnames)
+		nspnames = (List *) dnspnames->arg;
+	if (drolenames)
+		rolenames = (List *) drolenames->arg;
+
+	/* Prepare the InternalDefaultACL representation of the statement */
+	/* roleid to be filled below */
+	/* nspid to be filled in SetDefaultACLsInSchemas */
+	iacls.is_grant = action->is_grant;
+	iacls.objtype = action->objtype;
+	/* all_privs to be filled below */
+	/* privileges to be filled below */
+	iacls.grantees = NIL;		/* filled below */
+	iacls.grant_option = action->grant_option;
+	iacls.behavior = action->behavior;
+
+	/*
+	 * Convert the PrivGrantee list into an Oid list.  Note that at this point
+	 * we insert an ACL_ID_PUBLIC into the list if an empty role name is
+	 * detected (which is what the grammar uses if PUBLIC is found), so
+	 * downstream there shouldn't be any additional work needed to support
+	 * this case.
+	 */
+	foreach(cell, action->grantees)
+	{
+		PrivGrantee *grantee = (PrivGrantee *) lfirst(cell);
+
+		if (grantee->rolname == NULL)
+			iacls.grantees = lappend_oid(iacls.grantees, ACL_ID_PUBLIC);
+		else
+			iacls.grantees =
+				lappend_oid(iacls.grantees,
+							get_roleid_checked(grantee->rolname));
+	}
+
+	/*
+	 * Convert action->privileges, a list of privilege strings,
+	 * into an AclMode bitmask.
+	 */
+	switch (action->objtype)
+	{
+		case ACL_OBJECT_RELATION:
+			all_privileges = ACL_ALL_RIGHTS_RELATION;
+			errormsg = gettext_noop("invalid privilege type %s for relation");
+			break;
+		case ACL_OBJECT_SEQUENCE:
+			all_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+			errormsg = gettext_noop("invalid privilege type %s for sequence");
+			break;
+		case ACL_OBJECT_FUNCTION:
+			all_privileges = ACL_ALL_RIGHTS_FUNCTION;
+			errormsg = gettext_noop("invalid privilege type %s for function");
+			break;
+		default:
+			elog(ERROR, "unrecognized GrantStmt.objtype: %d",
+				 (int) action->objtype);
+			/* keep compiler quiet */
+			all_privileges = ACL_NO_RIGHTS;
+			errormsg = NULL;
+	}
+
+	if (action->privileges == NIL)
+	{
+		iacls.all_privs = true;
+
+		/*
+		 * will be turned into ACL_ALL_RIGHTS_* by the internal routines
+		 * depending on the object type
+		 */
+		iacls.privileges = ACL_NO_RIGHTS;
+	}
+	else
+	{
+		iacls.all_privs = false;
+		iacls.privileges = ACL_NO_RIGHTS;
+
+		foreach(cell, action->privileges)
+		{
+			AccessPriv *privnode = (AccessPriv *) lfirst(cell);
+			AclMode		priv;
+
+			if (privnode->cols)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
+						 errmsg("default privileges cannot be set for columns")));
+
+			if (privnode->priv_name == NULL)	/* parser mistake? */
+				elog(ERROR, "AccessPriv node must specify privilege");
+			priv = string_to_privilege(privnode->priv_name);
+
+			if (priv & ~((AclMode) all_privileges))
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_GRANT_OPERATION),
+						 errmsg(errormsg, privilege_to_string(priv))));
+
+			iacls.privileges |= priv;
+		}
+	}
+
+	if (rolenames == NIL)
+	{
+		/* Set permissions for myself */
+		iacls.roleid = GetUserId();
+
+		SetDefaultACLsInSchemas(&iacls, nspnames);
+	}
+	else
+	{
+		/* Look up the role OIDs and do permissions checks */
+		ListCell   *rolecell;
+
+		foreach(rolecell, rolenames)
+		{
+			char	   *rolename = strVal(lfirst(rolecell));
+
+			iacls.roleid = get_roleid_checked(rolename);
+
+			/*
+			 * We insist that calling user be a member of each target role.
+			 * If he has that, he could become that role anyway via SET ROLE,
+			 * so FOR ROLE is just a syntactic convenience and doesn't give
+			 * any special privileges.
+			 */
+			check_is_member_of_role(GetUserId(), iacls.roleid);
+
+			SetDefaultACLsInSchemas(&iacls, nspnames);
+		}
+	}
+}
+
+/*
+ * Process ALTER DEFAULT PRIVILEGES for a list of target schemas
+ *
+ * All fields of *iacls except nspid were filled already
+ */
+static void
+SetDefaultACLsInSchemas(InternalDefaultACL *iacls, List *nspnames)
+{
+	if (nspnames == NIL)
+	{
+		/* Set database-wide permissions if no schema was specified */
+		iacls->nspid = InvalidOid;
+
+		SetDefaultACL(iacls);
+	}
+	else
+	{
+		/* Look up the schema OIDs and do permissions checks */
+		ListCell   *nspcell;
+
+		foreach(nspcell, nspnames)
+		{
+			char	   *nspname = strVal(lfirst(nspcell));
+			AclResult	aclresult;
+
+			/*
+			 * Normally we'd use LookupCreationNamespace here, but it's
+			 * important to do the permissions check against the target role
+			 * not the calling user, so write it out in full.  We require
+			 * CREATE privileges, since without CREATE you won't be able to do
+			 * anything using the default privs anyway.
+			 */
+			iacls->nspid = GetSysCacheOid(NAMESPACENAME,
+										  CStringGetDatum(nspname),
+										  0, 0, 0);
+			if (!OidIsValid(iacls->nspid))
+				ereport(ERROR,
+						(errcode(ERRCODE_UNDEFINED_SCHEMA),
+						 errmsg("schema \"%s\" does not exist", nspname)));
+
+			aclresult = pg_namespace_aclcheck(iacls->nspid, iacls->roleid,
+											  ACL_CREATE);
+			if (aclresult != ACLCHECK_OK)
+				aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+							   nspname);
+
+			SetDefaultACL(iacls);
+		}
+	}
+}
+
+
+/*
+ * Create or update a pg_default_acl entry
+ */
+static void
+SetDefaultACL(InternalDefaultACL *iacls)
+{
+	AclMode		this_privileges = iacls->privileges;
+	char		objtype;
+	Relation	rel;
+	HeapTuple	tuple;
+	bool		isNew;
+	Acl		   *old_acl;
+	Acl		   *new_acl;
+	HeapTuple	newtuple;
+	Datum		values[Natts_pg_default_acl];
+	bool		nulls[Natts_pg_default_acl];
+	bool		replaces[Natts_pg_default_acl];
+	int			noldmembers;
+	int			nnewmembers;
+	Oid		   *oldmembers;
+	Oid		   *newmembers;
+
+	rel = heap_open(DefaultAclRelationId, RowExclusiveLock);
+
+	/*
+	 * Convert ACL object type to pg_default_acl object type
+	 * and handle all_privs option
+	 */
+	switch (iacls->objtype)
+	{
+		case ACL_OBJECT_RELATION:
+			objtype = DEFACLOBJ_RELATION;
+			if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+				this_privileges = ACL_ALL_RIGHTS_RELATION;
+			break;
+
+		case ACL_OBJECT_SEQUENCE:
+			objtype = DEFACLOBJ_SEQUENCE;
+			if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+				this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
+			break;
+
+		case ACL_OBJECT_FUNCTION:
+			objtype = DEFACLOBJ_FUNCTION;
+			if (iacls->all_privs && this_privileges == ACL_NO_RIGHTS)
+				this_privileges = ACL_ALL_RIGHTS_FUNCTION;
+			break;
+
+		default:
+			elog(ERROR, "unrecognized objtype: %d",
+				 (int) iacls->objtype);
+			objtype = 0;		/* keep compiler quiet */
+			break;
+	}
+
+	/* Search for existing row for this object type in catalog */
+	tuple = SearchSysCache(DEFACLROLENSPOBJ,
+						   ObjectIdGetDatum(iacls->roleid),
+						   ObjectIdGetDatum(iacls->nspid),
+						   CharGetDatum(objtype),
+						   0);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		Datum		aclDatum;
+		bool		isNull;
+
+		aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple,
+								   Anum_pg_default_acl_defaclacl,
+								   &isNull);
+		if (!isNull)
+			old_acl = DatumGetAclPCopy(aclDatum);
+		else
+			old_acl = NULL;
+		isNew = false;
+	}
+	else
+	{
+		old_acl = NULL;
+		isNew = true;
+	}
+
+	if (old_acl == NULL)
+	{
+		/*
+		 * If we are creating a global entry, start with the hard-wired
+		 * defaults and modify as per command.  Otherwise, start with an empty
+		 * ACL and modify that.  This is needed because global entries
+		 * replace the hard-wired defaults, while others do not.
+		 */
+		if (!OidIsValid(iacls->nspid))
+			old_acl = acldefault(iacls->objtype, iacls->roleid);
+		else
+			old_acl = make_empty_acl();
+	}
+
+	/*
+	 * We need the members of both old and new ACLs so we can correct the
+	 * shared dependency information.  Collect data before
+	 * merge_acl_with_grant throws away old_acl.
+	 */
+	noldmembers = aclmembers(old_acl, &oldmembers);
+
+	/*
+	 * Generate new ACL.  Grantor of rights is always the same as the
+	 * target role.
+	 */
+	new_acl = merge_acl_with_grant(old_acl,
+								   iacls->is_grant,
+								   iacls->grant_option,
+								   iacls->behavior,
+								   iacls->grantees,
+								   this_privileges,
+								   iacls->roleid,
+								   iacls->roleid);
+
+	/* finished building new ACL value, now insert it */
+	MemSet(values, 0, sizeof(values));
+	MemSet(nulls, false, sizeof(nulls));
+	MemSet(replaces, false, sizeof(replaces));
+
+	if (isNew)
+	{
+		values[Anum_pg_default_acl_defaclrole - 1] = ObjectIdGetDatum(iacls->roleid);
+		values[Anum_pg_default_acl_defaclnamespace - 1] = ObjectIdGetDatum(iacls->nspid);
+		values[Anum_pg_default_acl_defaclobjtype - 1] = CharGetDatum(objtype);
+		values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
+
+		newtuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+		simple_heap_insert(rel, newtuple);
+	}
+	else
+	{
+		values[Anum_pg_default_acl_defaclacl - 1] = PointerGetDatum(new_acl);
+		replaces[Anum_pg_default_acl_defaclacl - 1] = true;
+
+		newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel),
+									 values, nulls, replaces);
+		simple_heap_update(rel, &newtuple->t_self, newtuple);
+	}
+
+	/* keep the catalog indexes up to date */
+	CatalogUpdateIndexes(rel, newtuple);
+
+	/* these dependencies don't change in an update */
+	if (isNew)
+	{
+		/* dependency on role */
+		recordDependencyOnOwner(DefaultAclRelationId,
+								HeapTupleGetOid(newtuple),
+								iacls->roleid);
+
+		/* dependency on namespace */
+		if (OidIsValid(iacls->nspid))
+		{
+			ObjectAddress myself,
+						  referenced;
+
+			myself.classId = DefaultAclRelationId;
+			myself.objectId = HeapTupleGetOid(newtuple);
+			myself.objectSubId = 0;
+
+			referenced.classId = NamespaceRelationId;
+			referenced.objectId = iacls->nspid;
+			referenced.objectSubId = 0;
+
+			recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
+		}
+	}
+
+	/*
+	 * Update the shared dependency ACL info
+	 */
+	nnewmembers = aclmembers(new_acl, &newmembers);
+
+	updateAclDependencies(DefaultAclRelationId, HeapTupleGetOid(newtuple), 0,
+						  iacls->roleid, iacls->is_grant,
+						  noldmembers, oldmembers,
+						  nnewmembers, newmembers);
+
+	pfree(new_acl);
+
+	if (HeapTupleIsValid(tuple))
+		ReleaseSysCache(tuple);
+
+	heap_close(rel, RowExclusiveLock);
+}
+
+
+/*
+ * RemoveRoleFromObjectACL
+ *
+ * Used by shdepDropOwned to remove mentions of a role in ACLs
+ */
+void
+RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
+{
+	if (classid == DefaultAclRelationId)
+	{
+		InternalDefaultACL iacls;
+		Form_pg_default_acl pg_default_acl_tuple;
+		Relation	rel;
+		ScanKeyData skey[1];
+		SysScanDesc scan;
+		HeapTuple	tuple;
+
+		/* first fetch info needed by SetDefaultACL */
+		rel = heap_open(DefaultAclRelationId, AccessShareLock);
+
+		ScanKeyInit(&skey[0],
+					ObjectIdAttributeNumber,
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(objid));
+
+		scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
+								  SnapshotNow, 1, skey);
+
+		tuple = systable_getnext(scan);
+
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "could not find tuple for default ACL %u", objid);
+
+		pg_default_acl_tuple = (Form_pg_default_acl) GETSTRUCT(tuple);
+
+		iacls.roleid = pg_default_acl_tuple->defaclrole;
+		iacls.nspid = pg_default_acl_tuple->defaclnamespace;
+
+		switch (pg_default_acl_tuple->defaclobjtype)
+		{
+			case DEFACLOBJ_RELATION:
+				iacls.objtype = ACL_OBJECT_RELATION;
+				break;
+			case ACL_OBJECT_SEQUENCE:
+				iacls.objtype = ACL_OBJECT_SEQUENCE;
+				break;
+			case DEFACLOBJ_FUNCTION:
+				iacls.objtype = ACL_OBJECT_FUNCTION;
+				break;
+			default:
+				/* Shouldn't get here */
+				elog(ERROR, "unexpected default ACL type %d",
+					 pg_default_acl_tuple->defaclobjtype);
+				break;
+		}
+
+		systable_endscan(scan);
+		heap_close(rel, AccessShareLock);
+
+		iacls.is_grant = false;
+		iacls.all_privs = true;
+		iacls.privileges = ACL_NO_RIGHTS;
+		iacls.grantees = list_make1_oid(roleid);
+		iacls.grant_option = false;
+		iacls.behavior = DROP_CASCADE;
+
+		/* Do it */
+		SetDefaultACL(&iacls);
+	}
+	else
+	{
+		InternalGrant istmt;
+
+		switch (classid)
+		{
+			case RelationRelationId:
+				/* it's OK to use RELATION for a sequence */
+				istmt.objtype = ACL_OBJECT_RELATION;
+				break;
+			case DatabaseRelationId:
+				istmt.objtype = ACL_OBJECT_DATABASE;
+				break;
+			case ProcedureRelationId:
+				istmt.objtype = ACL_OBJECT_FUNCTION;
+				break;
+			case LanguageRelationId:
+				istmt.objtype = ACL_OBJECT_LANGUAGE;
+				break;
+			case NamespaceRelationId:
+				istmt.objtype = ACL_OBJECT_NAMESPACE;
+				break;
+			case TableSpaceRelationId:
+				istmt.objtype = ACL_OBJECT_TABLESPACE;
+				break;
+			default:
+				elog(ERROR, "unexpected object class %u", classid);
+				break;
+		}
+		istmt.is_grant = false;
+		istmt.objects = list_make1_oid(objid);
+		istmt.all_privs = true;
+		istmt.privileges = ACL_NO_RIGHTS;
+		istmt.col_privs = NIL;
+		istmt.grantees = list_make1_oid(roleid);
+		istmt.grant_option = false;
+		istmt.behavior = DROP_CASCADE;
+
+		ExecGrantStmt_oids(&istmt);
+	}
+}
+
+
+/*
+ * Remove a pg_default_acl entry
+ */
+void
+RemoveDefaultACLById(Oid defaclOid)
+{
+	Relation	rel;
+	ScanKeyData skey[1];
+	SysScanDesc scan;
+	HeapTuple	tuple;
+
+	rel = heap_open(DefaultAclRelationId, RowExclusiveLock);
+
+	ScanKeyInit(&skey[0],
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(defaclOid));
+
+	scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
+							  SnapshotNow, 1, skey);
+
+	tuple = systable_getnext(scan);
+
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "could not find tuple for default ACL %u", defaclOid);
+
+	simple_heap_delete(rel, &tuple->t_self);
+
+	systable_endscan(scan);
+	heap_close(rel, RowExclusiveLock);
+}
+
+
 /*
  * expand_col_privileges
  *
@@ -3532,3 +4136,106 @@ pg_conversion_ownercheck(Oid conv_oid, Oid roleid)
 
 	return has_privs_of_role(roleid, ownerId);
 }
+
+/*
+ * Fetch pg_default_acl entry for given role, namespace and object type
+ * (object type must be given in pg_default_acl's encoding).
+ * Returns NULL if no such entry.
+ */
+static Acl *
+get_default_acl_internal(Oid roleId, Oid nsp_oid, char objtype)
+{
+	Acl		   *result = NULL;
+	HeapTuple	tuple;
+
+	tuple = SearchSysCache(DEFACLROLENSPOBJ,
+						   ObjectIdGetDatum(roleId),
+						   ObjectIdGetDatum(nsp_oid),
+						   CharGetDatum(objtype),
+						   0);
+
+	if (HeapTupleIsValid(tuple))
+	{
+		Datum	aclDatum;
+		bool	isNull;
+
+		aclDatum = SysCacheGetAttr(DEFACLROLENSPOBJ, tuple,
+								   Anum_pg_default_acl_defaclacl,
+								   &isNull);
+		if (!isNull)
+			result = DatumGetAclPCopy(aclDatum);
+		ReleaseSysCache(tuple);
+	}
+
+	return result;
+}
+
+/*
+ * Get default permissions for newly created object within given schema
+ *
+ * Returns NULL if built-in system defaults should be used
+ */
+Acl *
+get_user_default_acl(GrantObjectType objtype, Oid ownerId, Oid nsp_oid)
+{
+	Acl		   *result;
+	Acl		   *glob_acl;
+	Acl		   *schema_acl;
+	Acl		   *def_acl;
+	char		defaclobjtype;
+
+	/*
+	 * Use NULL during bootstrap, since pg_default_acl probably isn't there
+	 * yet.
+	 */
+	if (IsBootstrapProcessingMode())
+		return NULL;
+
+	/* Check if object type is supported in pg_default_acl */
+	switch (objtype)
+	{
+		case ACL_OBJECT_RELATION:
+			defaclobjtype = DEFACLOBJ_RELATION;
+			break;
+
+		case ACL_OBJECT_SEQUENCE:
+			defaclobjtype = DEFACLOBJ_SEQUENCE;
+			break;
+
+		case ACL_OBJECT_FUNCTION:
+			defaclobjtype = DEFACLOBJ_FUNCTION;
+			break;
+
+		default:
+			return NULL;
+	}
+
+	/* Look up the relevant pg_default_acl entries */
+	glob_acl = get_default_acl_internal(ownerId, InvalidOid, defaclobjtype);
+	schema_acl = get_default_acl_internal(ownerId, nsp_oid, defaclobjtype);
+
+	/* Quick out if neither entry exists */
+	if (glob_acl == NULL && schema_acl == NULL)
+		return NULL;
+
+	/* We need to know the hard-wired default value, too */
+	def_acl = acldefault(objtype, ownerId);
+
+	/* If there's no global entry, substitute the hard-wired default */
+	if (glob_acl == NULL)
+		glob_acl = def_acl;
+
+	/* Merge in any per-schema privileges */
+	result = aclmerge(glob_acl, schema_acl, ownerId);
+
+	/*
+	 * For efficiency, we want to return NULL if the result equals default.
+	 * This requires sorting both arrays to get an accurate comparison.
+	 */
+	aclitemsort(result);
+	aclitemsort(def_acl);
+	if (aclequal(result, def_acl))
+		result = NULL;
+
+	return result;
+}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index c3d87ef59eb94f4f689c007d10c8f7b940c56f7d..8a07c69c7e85681e47c5aeafc10b1f8b26192d00 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.91 2009/09/22 15:46:34 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/dependency.c,v 1.92 2009/10/05 19:24:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,6 +32,7 @@
 #include "catalog/pg_conversion.h"
 #include "catalog/pg_conversion_fn.h"
 #include "catalog/pg_database.h"
+#include "catalog/pg_default_acl.h"
 #include "catalog/pg_depend.h"
 #include "catalog/pg_foreign_data_wrapper.h"
 #include "catalog/pg_foreign_server.h"
@@ -64,6 +65,7 @@
 #include "parser/parsetree.h"
 #include "rewrite/rewriteRemove.h"
 #include "storage/lmgr.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/guc.h"
@@ -146,7 +148,8 @@ static const Oid object_classes[MAX_OCLASS] = {
 	TableSpaceRelationId,		/* OCLASS_TBLSPACE */
 	ForeignDataWrapperRelationId,	/* OCLASS_FDW */
 	ForeignServerRelationId,	/* OCLASS_FOREIGN_SERVER */
-	UserMappingRelationId		/* OCLASS_USER_MAPPING */
+	UserMappingRelationId,		/* OCLASS_USER_MAPPING */
+	DefaultAclRelationId		/* OCLASS_DEFACL */
 };
 
 
@@ -1136,6 +1139,10 @@ doDeletion(const ObjectAddress *object)
 			RemoveUserMappingById(object->objectId);
 			break;
 
+		case OCLASS_DEFACL:
+			RemoveDefaultACLById(object->objectId);
+			break;
+
 		default:
 			elog(ERROR, "unrecognized object class: %u",
 				 object->classId);
@@ -2055,6 +2062,10 @@ getObjectClass(const ObjectAddress *object)
 		case UserMappingRelationId:
 			Assert(object->objectSubId == 0);
 			return OCLASS_USER_MAPPING;
+
+		case DefaultAclRelationId:
+			Assert(object->objectSubId == 0);
+			return OCLASS_DEFACL;
 	}
 
 	/* shouldn't get here */
@@ -2597,6 +2608,69 @@ getObjectDescription(const ObjectAddress *object)
 				break;
 			}
 
+		case OCLASS_DEFACL:
+			{
+				Relation	defaclrel;
+				ScanKeyData skey[1];
+				SysScanDesc rcscan;
+				HeapTuple	tup;
+				Form_pg_default_acl defacl;
+
+				defaclrel = heap_open(DefaultAclRelationId, AccessShareLock);
+
+				ScanKeyInit(&skey[0],
+							ObjectIdAttributeNumber,
+							BTEqualStrategyNumber, F_OIDEQ,
+							ObjectIdGetDatum(object->objectId));
+
+				rcscan = systable_beginscan(defaclrel, DefaultAclOidIndexId,
+											true, SnapshotNow, 1, skey);
+
+				tup = systable_getnext(rcscan);
+
+				if (!HeapTupleIsValid(tup))
+					elog(ERROR, "could not find tuple for default ACL %u",
+						 object->objectId);
+
+				defacl = (Form_pg_default_acl) GETSTRUCT(tup);
+
+				switch (defacl->defaclobjtype)
+				{
+					case DEFACLOBJ_RELATION:
+						appendStringInfo(&buffer,
+										 _("default privileges on new relations belonging to role %s"),
+										 GetUserNameFromId(defacl->defaclrole));
+						break;
+					case DEFACLOBJ_SEQUENCE:
+						appendStringInfo(&buffer,
+										 _("default privileges on new sequences belonging to role %s"),
+										 GetUserNameFromId(defacl->defaclrole));
+						break;
+					case DEFACLOBJ_FUNCTION:
+						appendStringInfo(&buffer,
+										 _("default privileges on new functions belonging to role %s"),
+										 GetUserNameFromId(defacl->defaclrole));
+						break;
+					default:
+						/* shouldn't get here */
+						appendStringInfo(&buffer,
+										 _("default privileges belonging to role %s"),
+										 GetUserNameFromId(defacl->defaclrole));
+						break;
+				}
+
+				if (OidIsValid(defacl->defaclnamespace))
+				{
+					appendStringInfo(&buffer,
+									_(" in schema %s"),
+									get_namespace_name(defacl->defaclnamespace));
+				}
+
+				systable_endscan(rcscan);
+				heap_close(defaclrel, AccessShareLock);
+				break;
+			}
+
 		default:
 			appendStringInfo(&buffer, "unrecognized object %u %u %d",
 							 object->classId,
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e9bdc882238189748b277afb35fb54bb42c6c6b3..a013306284700ed21dd4a0530f59ad474cbe16cc 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.359 2009/09/26 22:42:01 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.360 2009/10/05 19:24:35 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -59,6 +59,7 @@
 #include "storage/bufmgr.h"
 #include "storage/freespace.h"
 #include "storage/smgr.h"
+#include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/inval.h"
@@ -74,6 +75,7 @@ static void AddNewRelationTuple(Relation pg_class_desc,
 					Oid new_rel_oid, Oid new_type_oid,
 					Oid relowner,
 					char relkind,
+					Datum relacl,
 					Datum reloptions);
 static Oid AddNewRelationType(const char *typeName,
 				   Oid typeNamespace,
@@ -636,14 +638,16 @@ AddNewAttributeTuples(Oid new_rel_oid,
  * Caller has already opened and locked pg_class.
  * Tuple data is taken from new_rel_desc->rd_rel, except for the
  * variable-width fields which are not present in a cached reldesc.
- * We always initialize relacl to NULL (i.e., default permissions),
- * and reloptions is set to the passed-in text array (if any).
+ * relacl and reloptions are passed in Datum form (to avoid having
+ * to reference the data types in heap.h).  Pass (Datum) 0 to set them
+ * to NULL.
  * --------------------------------
  */
 void
 InsertPgClassTuple(Relation pg_class_desc,
 				   Relation new_rel_desc,
 				   Oid new_rel_oid,
+				   Datum relacl,
 				   Datum reloptions)
 {
 	Form_pg_class rd_rel = new_rel_desc->rd_rel;
@@ -678,8 +682,10 @@ InsertPgClassTuple(Relation pg_class_desc,
 	values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
 	values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
 	values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
-	/* start out with empty permissions */
-	nulls[Anum_pg_class_relacl - 1] = true;
+	if (relacl != (Datum) 0)
+		values[Anum_pg_class_relacl - 1] = relacl;
+	else
+		nulls[Anum_pg_class_relacl - 1] = true;
 	if (reloptions != (Datum) 0)
 		values[Anum_pg_class_reloptions - 1] = reloptions;
 	else
@@ -715,6 +721,7 @@ AddNewRelationTuple(Relation pg_class_desc,
 					Oid new_type_oid,
 					Oid relowner,
 					char relkind,
+					Datum relacl,
 					Datum reloptions)
 {
 	Form_pg_class new_rel_reltup;
@@ -775,7 +782,8 @@ AddNewRelationTuple(Relation pg_class_desc,
 	new_rel_desc->rd_att->tdtypeid = new_type_oid;
 
 	/* Now build and insert the tuple */
-	InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions);
+	InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid,
+					   relacl, reloptions);
 }
 
 
@@ -831,6 +839,27 @@ AddNewRelationType(const char *typeName,
  *		heap_create_with_catalog
  *
  *		creates a new cataloged relation.  see comments above.
+ *
+ * Arguments:
+ *	relname: name to give to new rel
+ *	relnamespace: OID of namespace it goes in
+ *	reltablespace: OID of tablespace it goes in
+ *	relid: OID to assign to new rel, or InvalidOid to select a new OID
+ *	reltypeid: OID to assign to rel's rowtype, or InvalidOid to select one
+ *	ownerid: OID of new rel's owner
+ *	tupdesc: tuple descriptor (source of column definitions)
+ *	cooked_constraints: list of precooked check constraints and defaults
+ *	relkind: relkind for new rel
+ *	shared_relation: TRUE if it's to be a shared relation
+ *	oidislocal: TRUE if oid column (if any) should be marked attislocal
+ *	oidinhcount: attinhcount to assign to oid column (if any)
+ *	oncommit: ON COMMIT marking (only relevant if it's a temp table)
+ *	reloptions: reloptions in Datum form, or (Datum) 0 if none
+ *	use_user_acl: TRUE if should look for user-defined default permissions;
+ *		if FALSE, relacl is always set NULL
+ *	allow_system_table_mods: TRUE to allow creation in system namespaces
+ *
+ * Returns the OID of the new relation
  * --------------------------------
  */
 Oid
@@ -848,10 +877,12 @@ heap_create_with_catalog(const char *relname,
 						 int oidinhcount,
 						 OnCommitAction oncommit,
 						 Datum reloptions,
+						 bool use_user_acl,
 						 bool allow_system_table_mods)
 {
 	Relation	pg_class_desc;
 	Relation	new_rel_desc;
+	Acl		   *relacl;
 	Oid			old_type_oid;
 	Oid			new_type_oid;
 	Oid			new_array_oid = InvalidOid;
@@ -920,6 +951,30 @@ heap_create_with_catalog(const char *relname,
 		relid = GetNewRelFileNode(reltablespace, shared_relation,
 								  pg_class_desc);
 
+	/*
+	 * Determine the relation's initial permissions.
+	 */
+	if (use_user_acl)
+	{
+		switch (relkind)
+		{
+			case RELKIND_RELATION:
+			case RELKIND_VIEW:
+				relacl = get_user_default_acl(ACL_OBJECT_RELATION, ownerid,
+											  relnamespace);
+				break;
+			case RELKIND_SEQUENCE:
+				relacl = get_user_default_acl(ACL_OBJECT_SEQUENCE, ownerid,
+											  relnamespace);
+				break;
+			default:
+				relacl = NULL;
+				break;
+		}
+	}
+	else
+		relacl = NULL;
+
 	/*
 	 * Create the relcache entry (mostly dummy at this point) and the physical
 	 * disk file.  (If we fail further down, it's the smgr's responsibility to
@@ -1027,6 +1082,7 @@ heap_create_with_catalog(const char *relname,
 						new_type_oid,
 						ownerid,
 						relkind,
+						PointerGetDatum(relacl),
 						reloptions);
 
 	/*
@@ -1037,12 +1093,15 @@ heap_create_with_catalog(const char *relname,
 
 	/*
 	 * Make a dependency link to force the relation to be deleted if its
-	 * namespace is.  Also make a dependency link to its owner.
+	 * namespace is.  Also make a dependency link to its owner, as well
+	 * as dependencies for any roles mentioned in the default ACL.
 	 *
 	 * For composite types, these dependencies are tracked for the pg_type
 	 * entry, so we needn't record them here.  Likewise, TOAST tables don't
 	 * need a namespace dependency (they live in a pinned namespace) nor an
-	 * owner dependency (they depend indirectly through the parent table).
+	 * owner dependency (they depend indirectly through the parent table),
+	 * nor should they have any ACL entries.
+	 *
 	 * Also, skip this in bootstrap mode, since we don't make dependencies
 	 * while bootstrapping.
 	 */
@@ -1062,6 +1121,18 @@ heap_create_with_catalog(const char *relname,
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 		recordDependencyOnOwner(RelationRelationId, relid, ownerid);
+
+		if (relacl != NULL)
+		{
+			int			nnewmembers;
+			Oid		   *newmembers;
+
+			nnewmembers = aclmembers(relacl, &newmembers);
+			updateAclDependencies(RelationRelationId, relid, 0,
+								  ownerid, true,
+								  0, NULL,
+								  nnewmembers, newmembers);
+		}
 	}
 
 	/*
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 759950b226528dbad2d32725bf3eb354af61dad1..b43b2910e73eff61b4b4cac05f2edfa667400006 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.321 2009/08/02 22:14:52 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.322 2009/10/05 19:24:35 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -664,6 +664,7 @@ index_create(Oid heapRelationId,
 	 */
 	InsertPgClassTuple(pg_class, indexRelation,
 					   RelationGetRelid(indexRelation),
+					   (Datum) 0,
 					   reloptions);
 
 	/* done with pg_class */
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 1fda61cc160f4215b9b7eede9c0f603e60cc1937..db69c127ad4798e9af80deb7e75cf792fd4c3efd 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.166 2009/10/02 18:13:04 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.167 2009/10/05 19:24:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -90,6 +90,7 @@ ProcedureCreate(const char *procedureName,
 	bool		internalOutParam = false;
 	Oid			variadicType = InvalidOid;
 	Oid			proowner = GetUserId();
+	Acl		   *proacl = NULL;
 	Relation	rel;
 	HeapTuple	tup;
 	HeapTuple	oldtup;
@@ -331,8 +332,7 @@ ProcedureCreate(const char *procedureName,
 		values[Anum_pg_proc_proconfig - 1] = proconfig;
 	else
 		nulls[Anum_pg_proc_proconfig - 1] = true;
-	/* start out with empty permissions */
-	nulls[Anum_pg_proc_proacl - 1] = true;
+	/* proacl will be determined later */
 
 	rel = heap_open(ProcedureRelationId, RowExclusiveLock);
 	tupDesc = RelationGetDescr(rel);
@@ -489,6 +489,15 @@ ProcedureCreate(const char *procedureName,
 	else
 	{
 		/* Creating a new procedure */
+
+		/* First, get default permissions and set up proacl */
+		proacl = get_user_default_acl(ACL_OBJECT_FUNCTION, proowner,
+									  procNamespace);
+		if (proacl != NULL)
+			values[Anum_pg_proc_proacl - 1] = PointerGetDatum(proacl);
+		else
+			nulls[Anum_pg_proc_proacl - 1] = true;
+
 		tup = heap_form_tuple(tupDesc, values, nulls);
 		simple_heap_insert(rel, tup);
 		is_update = false;
@@ -543,6 +552,19 @@ ProcedureCreate(const char *procedureName,
 	if (!is_update)
 		recordDependencyOnOwner(ProcedureRelationId, retval, proowner);
 
+	/* dependency on any roles mentioned in ACL */
+	if (!is_update && proacl != NULL)
+	{
+		int			nnewmembers;
+		Oid		   *newmembers;
+
+		nnewmembers = aclmembers(proacl, &newmembers);
+		updateAclDependencies(ProcedureRelationId, retval, 0,
+							  proowner, true,
+							  0, NULL,
+							  nnewmembers, newmembers);
+	}
+
 	heap_freetuple(tup);
 
 	heap_close(rel, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index 08977c38633e4643e7c1a07e672816bb11077b4b..869ec1fdd57952d8ed106f18c23d28e46774f69e 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.34 2009/06/11 14:48:55 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/pg_shdepend.c,v 1.35 2009/10/05 19:24:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,7 @@
 #include "catalog/pg_authid.h"
 #include "catalog/pg_conversion.h"
 #include "catalog/pg_database.h"
+#include "catalog/pg_default_acl.h"
 #include "catalog/pg_language.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_operator.h"
@@ -1180,7 +1181,6 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
 		while ((tuple = systable_getnext(scan)) != NULL)
 		{
 			Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tuple);
-			InternalGrant istmt;
 			ObjectAddress obj;
 
 			/* We only operate on objects in the current database */
@@ -1195,42 +1195,9 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
 					elog(ERROR, "unexpected dependency type");
 					break;
 				case SHARED_DEPENDENCY_ACL:
-					switch (sdepForm->classid)
-					{
-						case RelationRelationId:
-							/* it's OK to use RELATION for a sequence */
-							istmt.objtype = ACL_OBJECT_RELATION;
-							break;
-						case DatabaseRelationId:
-							istmt.objtype = ACL_OBJECT_DATABASE;
-							break;
-						case ProcedureRelationId:
-							istmt.objtype = ACL_OBJECT_FUNCTION;
-							break;
-						case LanguageRelationId:
-							istmt.objtype = ACL_OBJECT_LANGUAGE;
-							break;
-						case NamespaceRelationId:
-							istmt.objtype = ACL_OBJECT_NAMESPACE;
-							break;
-						case TableSpaceRelationId:
-							istmt.objtype = ACL_OBJECT_TABLESPACE;
-							break;
-						default:
-							elog(ERROR, "unexpected object type %d",
-								 sdepForm->classid);
-							break;
-					}
-					istmt.is_grant = false;
-					istmt.objects = list_make1_oid(sdepForm->objid);
-					istmt.all_privs = true;
-					istmt.privileges = ACL_NO_RIGHTS;
-					istmt.col_privs = NIL;
-					istmt.grantees = list_make1_oid(roleid);
-					istmt.grant_option = false;
-					istmt.behavior = DROP_CASCADE;
-
-					ExecGrantStmt_oids(&istmt);
+					RemoveRoleFromObjectACL(roleid,
+											sdepForm->classid,
+											sdepForm->objid);
 					break;
 				case SHARED_DEPENDENCY_OWNER:
 					/* Save it for deletion below */
@@ -1365,8 +1332,15 @@ shdepReassignOwned(List *roleids, Oid newrole)
 					AlterLanguageOwner_oid(sdepForm->objid, newrole);
 					break;
 
+				case DefaultAclRelationId:
+					/*
+					 * Ignore default ACLs; they should be handled by
+					 * DROP OWNED, not REASSIGN OWNED.
+					 */
+					break;
+
 				default:
-					elog(ERROR, "unexpected classid %d", sdepForm->classid);
+					elog(ERROR, "unexpected classid %u", sdepForm->classid);
 					break;
 			}
 			/* Make sure the next iteration will see my changes */
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index ac7b52734ddedd3c18a3f23e5e51bd498877d579..dfc178724efdc7479ca5b25f35eafa6a3d836d89 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.19 2009/09/26 22:42:01 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.20 2009/10/05 19:24:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -213,6 +213,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 										   0,
 										   ONCOMMIT_NOOP,
 										   reloptions,
+										   false,
 										   true);
 
 	/* make the toast relation visible, else index creation will fail */
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 5c483614e33870f683fd66b7605cf4a0a66eb280..a05fb4736cc301f4601775c7d119eeecba6e28ae 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.187 2009/09/26 22:42:01 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.188 2009/10/05 19:24:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -713,6 +713,7 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
 										  0,
 										  ONCOMMIT_NOOP,
 										  reloptions,
+										  false,
 										  allowSystemTableMods);
 
 	ReleaseSysCache(tuple);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 07b4af611c412074f8f300e2871a74b2c397a5b9..697b80d1b41d473fce90c9b241fe5cd592571439 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.299 2009/09/26 22:42:01 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.300 2009/10/05 19:24:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -521,6 +521,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
 										  parentOidCount,
 										  stmt->oncommit,
 										  reloptions,
+										  true,
 										  allowSystemTableMods);
 
 	StoreCatalogInheritance(relationId, inheritOids);
@@ -6098,6 +6099,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 			case OCLASS_FDW:
 			case OCLASS_FOREIGN_SERVER:
 			case OCLASS_USER_MAPPING:
+			case OCLASS_DEFACL:
 
 				/*
 				 * We don't expect any of these sorts of objects to depend on
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index d18d66c64a9190af50c2c4e90b7a0f9fd9a731a6..493ec2b1af1dcba066804a63520507357865e37c 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.329 2009/09/27 20:09:57 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.330 2009/10/05 19:24:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2909,6 +2909,7 @@ OpenIntoRel(QueryDesc *queryDesc)
 											  0,
 											  into->onCommit,
 											  reloptions,
+											  true,
 											  allowSystemTableMods);
 
 	FreeTupleDesc(tupdesc);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 5feff5d1691d75f5b431ced8180573179f3eb761..0264b2b3392e48831002df493388e674fe70529d 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.438 2009/09/22 23:43:37 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.439 2009/10/05 19:24:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2345,6 +2345,17 @@ _copyGrantRoleStmt(GrantRoleStmt *from)
 	return newnode;
 }
 
+static AlterDefaultPrivilegesStmt *
+_copyAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *from)
+{
+	AlterDefaultPrivilegesStmt *newnode = makeNode(AlterDefaultPrivilegesStmt);
+
+	COPY_NODE_FIELD(options);
+	COPY_NODE_FIELD(action);
+
+	return newnode;
+}
+
 static DeclareCursorStmt *
 _copyDeclareCursorStmt(DeclareCursorStmt *from)
 {
@@ -3760,6 +3771,9 @@ copyObject(void *from)
 		case T_GrantRoleStmt:
 			retval = _copyGrantRoleStmt(from);
 			break;
+		case T_AlterDefaultPrivilegesStmt:
+			retval = _copyAlterDefaultPrivilegesStmt(from);
+			break;
 		case T_DeclareCursorStmt:
 			retval = _copyDeclareCursorStmt(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index d7ed08cc68b77755781f036ee014150be2a2c29d..5d3cbbda1e48a01582a8dcfb7c576549dc3dbba7 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -22,7 +22,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.361 2009/09/22 23:43:38 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.362 2009/10/05 19:24:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1028,6 +1028,15 @@ _equalGrantRoleStmt(GrantRoleStmt *a, GrantRoleStmt *b)
 	return true;
 }
 
+static bool
+_equalAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *a, AlterDefaultPrivilegesStmt *b)
+{
+	COMPARE_NODE_FIELD(options);
+	COMPARE_NODE_FIELD(action);
+
+	return true;
+}
+
 static bool
 _equalDeclareCursorStmt(DeclareCursorStmt *a, DeclareCursorStmt *b)
 {
@@ -2537,6 +2546,9 @@ equal(void *a, void *b)
 		case T_GrantRoleStmt:
 			retval = _equalGrantRoleStmt(a, b);
 			break;
+		case T_AlterDefaultPrivilegesStmt:
+			retval = _equalAlterDefaultPrivilegesStmt(a, b);
+			break;
 		case T_DeclareCursorStmt:
 			retval = _equalDeclareCursorStmt(a, b);
 			break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9a203a5a48dd10ada3f107eae8a747ca6ff9c9ec..af2f080b6341938684e2e6897691617e149d4143 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.679 2009/09/22 23:43:38 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.680 2009/10/05 19:24:38 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -188,7 +188,9 @@ static TypeName *TableFuncTypeName(List *columns);
 		AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
 		AlterForeignServerStmt AlterGroupStmt
 		AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
-		AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt
+		AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
+		AlterRoleStmt AlterRoleSetStmt
+		AlterDefaultPrivilegesStmt DefACLAction
 		AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
 		ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
 		CreateDomainStmt CreateGroupStmt CreateOpClassStmt
@@ -269,6 +271,9 @@ static TypeName *TableFuncTypeName(List *columns);
 %type <privtarget> privilege_target
 %type <funwithargs> function_with_argtypes
 %type <list>	function_with_argtypes_list
+%type <ival>	defacl_privilege_target
+%type <defelt>	DefACLOption
+%type <list>	DefACLOptionList
 
 %type <list>	stmtblock stmtmulti
 				OptTableElementList TableElementList OptInherit definition
@@ -625,6 +630,7 @@ stmtmulti:	stmtmulti ';' stmt
 stmt :
 			AlterDatabaseStmt
 			| AlterDatabaseSetStmt
+			| AlterDefaultPrivilegesStmt
 			| AlterDomainStmt
 			| AlterFdwStmt
 			| AlterForeignServerStmt
@@ -1891,7 +1897,7 @@ reloption_list:
 		;
 
 /* This should match def_elem and also allow qualified names */
-reloption_elem:	
+reloption_elem:
 			ColLabel '=' def_arg
 				{
 					$$ = makeDefElem($1, (Node *) $3);
@@ -4576,6 +4582,93 @@ opt_granted_by: GRANTED BY RoleId						{ $$ = $3; }
 			| /*EMPTY*/									{ $$ = NULL; }
 		;
 
+/*****************************************************************************
+ *
+ * ALTER DEFAULT PRIVILEGES statement
+ *
+ *****************************************************************************/
+
+AlterDefaultPrivilegesStmt:
+			ALTER DEFAULT PRIVILEGES DefACLOptionList DefACLAction
+				{
+					AlterDefaultPrivilegesStmt *n = makeNode(AlterDefaultPrivilegesStmt);
+					n->options = $4;
+					n->action = (GrantStmt *) $5;
+					$$ = (Node*)n;
+				}
+		;
+
+DefACLOptionList:
+			DefACLOptionList DefACLOption			{ $$ = lappend($1, $2); }
+			| /* EMPTY */							{ $$ = NIL; }
+		;
+
+DefACLOption:
+			IN_P SCHEMA name_list
+				{
+					$$ = makeDefElem("schemas", (Node *)$3);
+				}
+			| FOR ROLE name_list
+				{
+					$$ = makeDefElem("roles", (Node *)$3);
+				}
+			| FOR USER name_list
+				{
+					$$ = makeDefElem("roles", (Node *)$3);
+				}
+		;
+
+/*
+ * This should match GRANT/REVOKE, except that target objects are missing
+ * and we only allow a subset of object types.
+ */
+DefACLAction:
+			GRANT privileges ON defacl_privilege_target TO grantee_list
+			opt_grant_grant_option
+				{
+					GrantStmt *n = makeNode(GrantStmt);
+					n->is_grant = true;
+					n->privileges = $2;
+					n->objtype = $4;
+					n->objects = NIL;
+					n->grantees = $6;
+					n->grant_option = $7;
+					$$ = (Node*)n;
+				}
+			| REVOKE privileges ON defacl_privilege_target
+			FROM grantee_list opt_drop_behavior
+				{
+					GrantStmt *n = makeNode(GrantStmt);
+					n->is_grant = false;
+					n->grant_option = false;
+					n->privileges = $2;
+					n->objtype = $4;
+					n->objects = NIL;
+					n->grantees = $6;
+					n->behavior = $7;
+					$$ = (Node *)n;
+				}
+			| REVOKE GRANT OPTION FOR privileges ON defacl_privilege_target
+			FROM grantee_list opt_drop_behavior
+				{
+					GrantStmt *n = makeNode(GrantStmt);
+					n->is_grant = false;
+					n->grant_option = true;
+					n->privileges = $5;
+					n->objtype = $7;
+					n->objects = NIL;
+					n->grantees = $9;
+					n->behavior = $10;
+					$$ = (Node *)n;
+				}
+		;
+
+defacl_privilege_target:
+			TABLE			{ $$ = ACL_OBJECT_RELATION; }
+			| FUNCTION		{ $$ = ACL_OBJECT_FUNCTION; }
+			| SEQUENCE		{ $$ = ACL_OBJECT_SEQUENCE; }
+		;
+
 
 /*****************************************************************************
  *
@@ -8632,10 +8725,11 @@ a_expr:		c_expr									{ $$ = $1; }
 					$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "<>", $1, (Node *) $6, @2);
 				}
 			/*
-			 *	Ideally we would not use hard-wired operators below but instead use
-			 *	opclasses.  However, mixed data types and other issues make this
-			 *	difficult:  http://archives.postgresql.org/pgsql-hackers/2008-08/msg01142.php
-			 */			
+			 *	Ideally we would not use hard-wired operators below but
+			 *	instead use opclasses.  However, mixed data types and other
+			 *	issues make this difficult:
+			 *	http://archives.postgresql.org/pgsql-hackers/2008-08/msg01142.php
+			 */
 			| a_expr BETWEEN opt_asymmetric b_expr AND b_expr		%prec BETWEEN
 				{
 					$$ = (Node *) makeA_Expr(AEXPR_AND, NIL,
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 0d2079fc0aaba3aa1b045dcbfd2c61ebae055575..4d7b3c2e9de7a85d91dc3c0d12ce7384a846b444 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.314 2009/09/22 23:43:38 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.315 2009/10/05 19:24:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -199,6 +199,7 @@ check_xact_readonly(Node *parsetree)
 		case T_DropPropertyStmt:
 		case T_GrantStmt:
 		case T_GrantRoleStmt:
+		case T_AlterDefaultPrivilegesStmt:
 		case T_TruncateStmt:
 		case T_DropOwnedStmt:
 		case T_ReassignOwnedStmt:
@@ -701,6 +702,10 @@ ProcessUtility(Node *parsetree,
 			GrantRole((GrantRoleStmt *) parsetree);
 			break;
 
+		case T_AlterDefaultPrivilegesStmt:
+			ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt *) parsetree);
+			break;
+
 			/*
 			 * **************** object creation / destruction *****************
 			 */
@@ -1687,6 +1692,10 @@ CreateCommandTag(Node *parsetree)
 			}
 			break;
 
+		case T_AlterDefaultPrivilegesStmt:
+			tag = "ALTER DEFAULT PRIVILEGES";
+			break;
+
 		case T_DefineStmt:
 			switch (((DefineStmt *) parsetree)->kind)
 			{
@@ -2240,6 +2249,10 @@ GetCommandLogLevel(Node *parsetree)
 			lev = LOGSTMT_DDL;
 			break;
 
+		case T_AlterDefaultPrivilegesStmt:
+			lev = LOGSTMT_DDL;
+			break;
+
 		case T_DefineStmt:
 			lev = LOGSTMT_DDL;
 			break;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 83d93ad34836241959f5833b70047523a62dba30..1264dfddb323a19b479cdcfa2facfbf33d093282 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.149 2009/08/03 21:11:39 joe Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.150 2009/10/05 19:24:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -77,6 +77,7 @@ static Acl *allocacl(int n);
 static void check_acl(const Acl *acl);
 static const char *aclparse(const char *s, AclItem *aip);
 static bool aclitem_match(const AclItem *a1, const AclItem *a2);
+static int aclitemComparator(const void *arg1, const void *arg2);
 static void check_circularity(const Acl *old_acl, const AclItem *mod_aip,
 				  Oid ownerId);
 static Acl *recursive_revoke(Acl *acl, Oid grantee, AclMode revoke_privs,
@@ -382,6 +383,15 @@ allocacl(int n)
 	return new_acl;
 }
 
+/*
+ * Create a zero-entry ACL
+ */
+Acl *
+make_empty_acl(void)
+{
+	return allocacl(0);
+}
+
 /*
  * Copy an ACL
  */
@@ -423,6 +433,98 @@ aclconcat(const Acl *left_acl, const Acl *right_acl)
 	return result_acl;
 }
 
+/*
+ * Merge two ACLs
+ *
+ * This produces a properly merged ACL with no redundant entries.
+ * Returns NULL on NULL input.
+ */
+Acl *
+aclmerge(const Acl *left_acl, const Acl *right_acl, Oid ownerId)
+{
+	Acl		   *result_acl;
+	AclItem    *aip;
+	int			i,
+				num;
+
+	/* Check for cases where one or both are empty/null */
+	if (left_acl == NULL || ACL_NUM(left_acl) == 0)
+	{
+		if (right_acl == NULL || ACL_NUM(right_acl) == 0)
+			return NULL;
+		else
+			return aclcopy(right_acl);
+	}
+	else
+	{
+		if (right_acl == NULL || ACL_NUM(right_acl) == 0)
+			return aclcopy(left_acl);
+	}
+
+	/* Merge them the hard way, one item at a time */
+	result_acl = aclcopy(left_acl);
+
+	aip = ACL_DAT(right_acl);
+	num = ACL_NUM(right_acl);
+
+	for (i = 0; i < num; i++, aip++)
+	{
+		Acl *tmp_acl;
+
+		tmp_acl = aclupdate(result_acl, aip, ACL_MODECHG_ADD,
+							ownerId, DROP_RESTRICT);
+		pfree(result_acl);
+		result_acl = tmp_acl;
+	}
+
+	return result_acl;
+}
+
+/*
+ * Sort the items in an ACL (into an arbitrary but consistent order)
+ */
+void
+aclitemsort(Acl *acl)
+{
+	if (acl != NULL && ACL_NUM(acl) > 1)
+		qsort(ACL_DAT(acl), ACL_NUM(acl), sizeof(AclItem), aclitemComparator);
+}
+
+/*
+ * Check if two ACLs are exactly equal
+ *
+ * This will not detect equality if the two arrays contain the same items
+ * in different orders.  To handle that case, sort both inputs first,
+ * using aclitemsort().
+ */
+bool
+aclequal(const Acl *left_acl, const Acl *right_acl)
+{
+	/* Check for cases where one or both are empty/null */
+	if (left_acl == NULL || ACL_NUM(left_acl) == 0)
+	{
+		if (right_acl == NULL || ACL_NUM(right_acl) == 0)
+			return true;
+		else
+			return false;
+	}
+	else
+	{
+		if (right_acl == NULL || ACL_NUM(right_acl) == 0)
+			return false;
+	}
+
+	if (ACL_NUM(left_acl) != ACL_NUM(right_acl))
+		return false;
+
+	if (memcmp(ACL_DAT(left_acl),
+			   ACL_DAT(right_acl),
+			   ACL_NUM(left_acl) * sizeof(AclItem)) == 0)
+		return true;
+
+	return false;
+}
+
 /*
  * Verify that an ACL array is acceptable (one-dimensional and has no nulls)
  */
@@ -555,6 +657,31 @@ aclitem_match(const AclItem *a1, const AclItem *a2)
 		a1->ai_grantor == a2->ai_grantor;
 }
 
+/*
+ * aclitemComparator
+ *		qsort comparison function for AclItems
+ */
+static int
+aclitemComparator(const void *arg1, const void *arg2)
+{
+	const AclItem *a1 = (const AclItem *) arg1;
+	const AclItem *a2 = (const AclItem *) arg2;
+
+	if (a1->ai_grantee > a2->ai_grantee)
+		return 1;
+	if (a1->ai_grantee < a2->ai_grantee)
+		return -1;
+	if (a1->ai_grantor > a2->ai_grantor)
+		return 1;
+	if (a1->ai_grantor < a2->ai_grantor)
+		return -1;
+	if (a1->ai_privs > a2->ai_privs)
+		return 1;
+	if (a1->ai_privs < a2->ai_privs)
+		return -1;
+	return 0;
+}
+
 /*
  * aclitem equality operator
  */
@@ -593,6 +720,9 @@ hash_aclitem(PG_FUNCTION_ARGS)
  *
  * Change this routine if you want to alter the default access policy for
  * newly-created objects (or any object with a NULL acl entry).
+ *
+ * Note that these are the hard-wired "defaults" that are used in the
+ * absence of any pg_default_acl entry.
  */
 Acl *
 acldefault(GrantObjectType objtype, Oid ownerId)
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 922c4a626f7b700d62b402d8106234814946c264..efed498f120d065950d566b61a5a0b2fde46b9e9 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.120 2009/06/11 14:49:05 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.121 2009/10/05 19:24:45 tgl Exp $
  *
  * NOTES
  *	  These routines allow the parser/planner/executor to perform
@@ -31,6 +31,7 @@
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_conversion.h"
 #include "catalog/pg_database.h"
+#include "catalog/pg_default_acl.h"
 #include "catalog/pg_enum.h"
 #include "catalog/pg_foreign_data_wrapper.h"
 #include "catalog/pg_foreign_server.h"
@@ -344,6 +345,18 @@ static const struct cachedesc cacheinfo[] = {
 		},
 		4
 	},
+	{DefaultAclRelationId,		/* DEFACLROLENSPOBJ */
+		DefaultAclRoleNspObjIndexId,
+		0,
+		3,
+		{
+			Anum_pg_default_acl_defaclrole,
+			Anum_pg_default_acl_defaclnamespace,
+			Anum_pg_default_acl_defaclobjtype,
+			0
+		},
+		256
+	},
 	{EnumRelationId,			/* ENUMOID */
 		EnumOidIndexId,
 		0,
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 789638fec454ef2b37b7a2cf9d4981cc28d94baf..ea314be0fa5280e9bd4ee85baa8893903e5a8540 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.107 2009/06/11 14:49:07 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.108 2009/10/05 19:24:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -93,6 +93,7 @@ getSchemaData(int *numTablesPtr)
 	TSConfigInfo *cfginfo;
 	FdwInfo    *fdwinfo;
 	ForeignServerInfo *srvinfo;
+	DefaultACLInfo *daclinfo;
 	int			numNamespaces;
 	int			numAggregates;
 	int			numInherits;
@@ -108,6 +109,7 @@ getSchemaData(int *numTablesPtr)
 	int			numTSConfigs;
 	int			numForeignDataWrappers;
 	int			numForeignServers;
+	int			numDefaultACLs;
 
 	if (g_verbose)
 		write_msg(NULL, "reading schemas\n");
@@ -166,6 +168,10 @@ getSchemaData(int *numTablesPtr)
 		write_msg(NULL, "reading user-defined foreign servers\n");
 	srvinfo = getForeignServers(&numForeignServers);
 
+	if (g_verbose)
+		write_msg(NULL, "reading default privileges\n");
+	daclinfo = getDefaultACLs(&numDefaultACLs);
+
 	if (g_verbose)
 		write_msg(NULL, "reading user-defined operator families\n");
 	opfinfo = getOpfamilies(&numOpfamilies);
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index 8037c1d7e1652c99a0bce1ce1cae6f40d9385d11..a9a27625707928e276249ef8cbf3e5dff6342a7a 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.48 2009/08/04 21:56:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.c,v 1.49 2009/10/05 19:24:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -490,18 +490,22 @@ parsePGArray(const char *atext, char ***itemarray, int *nitems)
  *	acls: the ACL string fetched from the database
  *	owner: username of object owner (will be passed through fmtId); can be
  *		NULL or empty string to indicate "no owner known"
+ *	prefix: string to prefix to each generated command; typically empty
  *	remoteVersion: version of database
  *
  * Returns TRUE if okay, FALSE if could not parse the acl string.
  * The resulting commands (if any) are appended to the contents of 'sql'.
  *
+ * Note: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES "
+ * or something similar, and name is an empty string.
+ *
  * Note: beware of passing a fmtId() result directly as 'name' or 'subname',
  * since this routine uses fmtId() internally.
  */
 bool
 buildACLCommands(const char *name, const char *subname,
 				 const char *type, const char *acls, const char *owner,
-				 int remoteVersion,
+				 const char *prefix, int remoteVersion,
 				 PQExpBuffer sql)
 {
 	char	  **aclitems;
@@ -549,7 +553,7 @@ buildACLCommands(const char *name, const char *subname,
 	 * wire-in knowledge about the default public privileges for different
 	 * kinds of objects.
 	 */
-	appendPQExpBuffer(firstsql, "REVOKE ALL");
+	appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
 	if (subname)
 		appendPQExpBuffer(firstsql, "(%s)", subname);
 	appendPQExpBuffer(firstsql, " ON %s %s FROM PUBLIC;\n", type, name);
@@ -564,8 +568,8 @@ buildACLCommands(const char *name, const char *subname,
 	if (remoteVersion < 80200 && strcmp(type, "DATABASE") == 0)
 	{
 		/* database CONNECT priv didn't exist before 8.2 */
-		appendPQExpBuffer(firstsql, "GRANT CONNECT ON %s %s TO PUBLIC;\n",
-						  type, name);
+		appendPQExpBuffer(firstsql, "%sGRANT CONNECT ON %s %s TO PUBLIC;\n",
+						  prefix, type, name);
 	}
 
 	/* Scan individual ACL items */
@@ -594,20 +598,20 @@ buildACLCommands(const char *name, const char *subname,
 					? strcmp(privswgo->data, "ALL") != 0
 					: strcmp(privs->data, "ALL") != 0)
 				{
-					appendPQExpBuffer(firstsql, "REVOKE ALL");
+					appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
 					if (subname)
 						appendPQExpBuffer(firstsql, "(%s)", subname);
 					appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
 									  type, name, fmtId(grantee->data));
 					if (privs->len > 0)
 						appendPQExpBuffer(firstsql,
-										  "GRANT %s ON %s %s TO %s;\n",
-										  privs->data, type, name,
+										  "%sGRANT %s ON %s %s TO %s;\n",
+										  prefix, privs->data, type, name,
 										  fmtId(grantee->data));
 					if (privswgo->len > 0)
 						appendPQExpBuffer(firstsql,
-							  "GRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
-										  privswgo->data, type, name,
+							  "%sGRANT %s ON %s %s TO %s WITH GRANT OPTION;\n",
+										  prefix, privswgo->data, type, name,
 										  fmtId(grantee->data));
 				}
 			}
@@ -623,8 +627,8 @@ buildACLCommands(const char *name, const char *subname,
 
 				if (privs->len > 0)
 				{
-					appendPQExpBuffer(secondsql, "GRANT %s ON %s %s TO ",
-									  privs->data, type, name);
+					appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
+									  prefix, privs->data, type, name);
 					if (grantee->len == 0)
 						appendPQExpBuffer(secondsql, "PUBLIC;\n");
 					else if (strncmp(grantee->data, "group ",
@@ -636,8 +640,8 @@ buildACLCommands(const char *name, const char *subname,
 				}
 				if (privswgo->len > 0)
 				{
-					appendPQExpBuffer(secondsql, "GRANT %s ON %s %s TO ",
-									  privswgo->data, type, name);
+					appendPQExpBuffer(secondsql, "%sGRANT %s ON %s %s TO ",
+									  prefix, privswgo->data, type, name);
 					if (grantee->len == 0)
 						appendPQExpBuffer(secondsql, "PUBLIC");
 					else if (strncmp(grantee->data, "group ",
@@ -661,7 +665,7 @@ buildACLCommands(const char *name, const char *subname,
 	 */
 	if (!found_owner_privs && owner)
 	{
-		appendPQExpBuffer(firstsql, "REVOKE ALL");
+		appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
 		if (subname)
 			appendPQExpBuffer(firstsql, "(%s)", subname);
 		appendPQExpBuffer(firstsql, " ON %s %s FROM %s;\n",
@@ -682,6 +686,50 @@ buildACLCommands(const char *name, const char *subname,
 	return true;
 }
 
+/*
+ * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry.
+ *
+ *	type: the object type (as seen in GRANT command)
+ *	nspname: schema name, or NULL for global default privileges
+ *	acls: the ACL string fetched from the database
+ *	owner: username of privileges owner (will be passed through fmtId)
+ *	remoteVersion: version of database
+ *
+ * Returns TRUE if okay, FALSE if could not parse the acl string.
+ * The resulting commands (if any) are appended to the contents of 'sql'.
+ */
+bool
+buildDefaultACLCommands(const char *type, const char *nspname,
+						const char *acls, const char *owner,
+						int remoteVersion,
+						PQExpBuffer sql)
+{
+	bool		result;
+	PQExpBuffer prefix;
+
+	prefix = createPQExpBuffer();
+
+	/*
+	 * We incorporate the target role directly into the command, rather than
+	 * playing around with SET ROLE or anything like that.  This is so that
+	 * a permissions error leads to nothing happening, rather than
+	 * changing default privileges for the wrong user.
+	 */
+	appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s ",
+					  fmtId(owner));
+	if (nspname)
+		appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
+
+	result = buildACLCommands("", NULL,
+							  type, acls, owner,
+							  prefix->data, remoteVersion,
+							  sql);
+
+	destroyPQExpBuffer(prefix);
+
+	return result;
+}
+
 /*
  * This will parse an aclitem string, having the general form
  *		username=privilegecodes/grantor
diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h
index 3c56c3f9a4d886f5c4f4ecf72747dbd76544af8c..a5bfe1bcfda30d8a620ce988567094f70d260c0b 100644
--- a/src/bin/pg_dump/dumputils.h
+++ b/src/bin/pg_dump/dumputils.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.h,v 1.25 2009/08/04 21:56:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/dumputils.h,v 1.26 2009/10/05 19:24:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,8 +34,12 @@ extern int	parse_version(const char *versionString);
 extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems);
 extern bool buildACLCommands(const char *name, const char *subname,
 				 const char *type, const char *acls, const char *owner,
-				 int remoteVersion,
+				 const char *prefix, int remoteVersion,
 				 PQExpBuffer sql);
+extern bool buildDefaultACLCommands(const char *type, const char *nspname,
+						const char *acls, const char *owner,
+						int remoteVersion,
+						PQExpBuffer sql);
 extern void processSQLNamePattern(PGconn *conn, PQExpBuffer buf,
 					  const char *pattern,
 					  bool have_where, bool force_escape,
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 20bd3eb7eacd1b0b24228024b69a64dc212e5329..e15e4dbdb9e4bf1e6c120ca34ab478cfb754d08a 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *		$PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.175 2009/08/07 22:48:34 tgl Exp $
+ *		$PostgreSQL: pgsql/src/bin/pg_dump/pg_backup_archiver.c,v 1.176 2009/10/05 19:24:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2072,7 +2072,8 @@ ReadToc(ArchiveHandle *AH)
 			 * the entries into sections
 			 */
 			if (strcmp(te->desc, "COMMENT") == 0 ||
-				strcmp(te->desc, "ACL") == 0)
+				strcmp(te->desc, "ACL") == 0 ||
+				strcmp(te->desc, "DEFAULT ACL") == 0)
 				te->section = SECTION_NONE;
 			else if (strcmp(te->desc, "TABLE DATA") == 0 ||
 					 strcmp(te->desc, "BLOBS") == 0 ||
@@ -2227,7 +2228,8 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
 		return 0;
 
 	/* If it's an ACL, maybe ignore it */
-	if ((!include_acls || ropt->aclsSkip) && strcmp(te->desc, "ACL") == 0)
+	if ((!include_acls || ropt->aclsSkip) &&
+		(strcmp(te->desc, "ACL") == 0 || strcmp(te->desc, "DEFAULT ACL") == 0))
 		return 0;
 
 	if (!ropt->create && strcmp(te->desc, "DATABASE") == 0)
@@ -2721,12 +2723,14 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
 	/* ACLs are dumped only during acl pass */
 	if (acl_pass)
 	{
-		if (strcmp(te->desc, "ACL") != 0)
+		if (!(strcmp(te->desc, "ACL") == 0 ||
+			  strcmp(te->desc, "DEFAULT ACL") == 0))
 			return;
 	}
 	else
 	{
-		if (strcmp(te->desc, "ACL") == 0)
+		if (strcmp(te->desc, "ACL") == 0 ||
+			strcmp(te->desc, "DEFAULT ACL") == 0)
 			return;
 	}
 
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 34ebc27168dbd853ca8d658d6339eaf63cac56b5..d1715eccce8ec2e081715a3cf22b8fba86c3e112 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12,7 +12,7 @@
  *	by PostgreSQL
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.548 2009/09/22 23:43:38 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.549 2009/10/05 19:24:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,6 +34,7 @@
 #include "access/sysattr.h"
 #include "catalog/pg_cast.h"
 #include "catalog/pg_class.h"
+#include "catalog/pg_default_acl.h"
 #include "catalog/pg_largeobject.h"
 #include "catalog/pg_proc.h"
 #include "catalog/pg_trigger.h"
@@ -162,6 +163,7 @@ static void dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo);
 static void dumpUserMappings(Archive *fout, const char *target,
 				 const char *servername, const char *namespace,
 				 const char *owner, CatalogId catalogId, DumpId dumpId);
+static void dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo);
 
 static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
 		const char *type, const char *name, const char *subname,
@@ -1049,6 +1051,23 @@ selectDumpableType(TypeInfo *tinfo)
 		tinfo->dobj.dump = true;
 }
 
+/*
+ * selectDumpableDefaultACL: policy-setting subroutine
+ *		Mark a default ACL as to be dumped or not
+ *
+ * For per-schema default ACLs, dump if the schema is to be dumped.
+ * Otherwise dump if we are dumping "everything".  Note that dataOnly
+ * and aclsSkip are checked separately.
+ */
+static void
+selectDumpableDefaultACL(DefaultACLInfo *dinfo)
+{
+	if (dinfo->dobj.namespace)
+		dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump;
+	else
+		dinfo->dobj.dump = include_everything;
+}
+
 /*
  * selectDumpableObject: policy-setting subroutine
  *		Mark a generic dumpable object as to be dumped or not
@@ -1779,7 +1798,7 @@ dumpDatabase(Archive *AH)
 		PQExpBuffer loFrozenQry = createPQExpBuffer();
 		PQExpBuffer loOutQry = createPQExpBuffer();
 		int			i_relfrozenxid;
-		
+
 		appendPQExpBuffer(loFrozenQry, "SELECT relfrozenxid\n"
 							"FROM pg_catalog.pg_class\n"
 							"WHERE oid = %d;\n",
@@ -1808,7 +1827,7 @@ dumpDatabase(Archive *AH)
 					 loOutQry->data, "", NULL,
 					 NULL, 0,
 					 NULL, NULL);
-						  
+
 		PQclear(lo_res);
 		destroyPQExpBuffer(loFrozenQry);
 		destroyPQExpBuffer(loOutQry);
@@ -5645,6 +5664,94 @@ getForeignServers(int *numForeignServers)
 	return srvinfo;
 }
 
+/*
+ * getDefaultACLs:
+ *	  read all default ACL information in the system catalogs and return
+ *	  them in the DefaultACLInfo structure
+ *
+ *	numDefaultACLs is set to the number of ACLs read in
+ */
+DefaultACLInfo *
+getDefaultACLs(int *numDefaultACLs)
+{
+	DefaultACLInfo *daclinfo;
+	PQExpBuffer query;
+	PGresult   *res;
+	int			i_oid;
+	int			i_tableoid;
+	int			i_defaclrole;
+	int			i_defaclnamespace;
+	int			i_defaclobjtype;
+	int			i_defaclacl;
+	int			i,
+				ntups;
+
+	if (g_fout->remoteVersion < 80500)
+	{
+		*numDefaultACLs = 0;
+		return NULL;
+	}
+
+	query = createPQExpBuffer();
+
+	/* Make sure we are in proper schema */
+	selectSourceSchema("pg_catalog");
+
+	appendPQExpBuffer(query, "SELECT oid, tableoid, "
+					  "(%s defaclrole) AS defaclrole, "
+					  "defaclnamespace, "
+					  "defaclobjtype, "
+					  "defaclacl "
+					  "FROM pg_default_acl",
+					  username_subquery);
+
+	res = PQexec(g_conn, query->data);
+	check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+	ntups = PQntuples(res);
+	*numDefaultACLs = ntups;
+
+	daclinfo = (DefaultACLInfo *) malloc(ntups * sizeof(DefaultACLInfo));
+
+	i_oid = PQfnumber(res, "oid");
+	i_tableoid = PQfnumber(res, "tableoid");
+	i_defaclrole = PQfnumber(res, "defaclrole");
+	i_defaclnamespace = PQfnumber(res, "defaclnamespace");
+	i_defaclobjtype = PQfnumber(res, "defaclobjtype");
+	i_defaclacl = PQfnumber(res, "defaclacl");
+
+	for (i = 0; i < ntups; i++)
+	{
+		Oid		nspid = atooid(PQgetvalue(res, i, i_defaclnamespace));
+
+		daclinfo[i].dobj.objType = DO_DEFAULT_ACL;
+		daclinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+		daclinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+		AssignDumpId(&daclinfo[i].dobj);
+		/* cheesy ... is it worth coming up with a better object name? */
+		daclinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_defaclobjtype));
+
+		if (nspid != InvalidOid)
+			daclinfo[i].dobj.namespace = findNamespace(nspid,
+													   daclinfo[i].dobj.catId.oid);
+		else
+			daclinfo[i].dobj.namespace = NULL;
+
+		daclinfo[i].defaclrole = strdup(PQgetvalue(res, i, i_defaclrole));
+		daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
+		daclinfo[i].defaclacl = strdup(PQgetvalue(res, i, i_defaclacl));
+
+		/* Decide whether we want to dump it */
+		selectDumpableDefaultACL(&(daclinfo[i]));
+	}
+
+	PQclear(res);
+
+	destroyPQExpBuffer(query);
+
+	return daclinfo;
+}
+
 /*
  * dumpComment --
  *
@@ -6058,6 +6165,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
 		case DO_FOREIGN_SERVER:
 			dumpForeignServer(fout, (ForeignServerInfo *) dobj);
 			break;
+		case DO_DEFAULT_ACL:
+			dumpDefaultACL(fout, (DefaultACLInfo *) dobj);
+			break;
 		case DO_BLOBS:
 			ArchiveEntry(fout, dobj->catId, dobj->dumpId,
 						 dobj->name, NULL, NULL, "",
@@ -9791,6 +9901,72 @@ dumpUserMappings(Archive *fout, const char *target,
 	destroyPQExpBuffer(q);
 }
 
+/*
+ * Write out default privileges information
+ */
+static void
+dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo)
+{
+	PQExpBuffer q;
+	PQExpBuffer tag;
+	const char *type;
+
+	/* Skip if not to be dumped */
+	if (!daclinfo->dobj.dump || dataOnly || aclsSkip)
+		return;
+
+	q = createPQExpBuffer();
+	tag = createPQExpBuffer();
+
+	switch (daclinfo->defaclobjtype)
+	{
+		case DEFACLOBJ_RELATION:
+			type = "TABLE";
+			break;
+		case DEFACLOBJ_SEQUENCE:
+			type = "SEQUENCE";
+			break;
+		case DEFACLOBJ_FUNCTION:
+			type = "FUNCTION";
+			break;
+		default:
+			/* shouldn't get here */
+			write_msg(NULL, "unknown object type (%d) in default privileges\n",
+					  (int) daclinfo->defaclobjtype);
+			exit_nicely();
+			type = "";			/* keep compiler quiet */
+	}
+
+	appendPQExpBuffer(tag, "DEFAULT %s PRIVILEGES", type);
+
+	/* build the actual command(s) for this tuple */
+	if (!buildDefaultACLCommands(type,
+								 daclinfo->dobj.namespace != NULL ?
+								 daclinfo->dobj.namespace->dobj.name : NULL,
+								 daclinfo->defaclacl,
+								 daclinfo->defaclrole,
+								 fout->remoteVersion,
+								 q))
+	{
+		write_msg(NULL, "could not parse default ACL list (%s)\n",
+				  daclinfo->defaclacl);
+		exit_nicely();
+	}
+
+	ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
+				 tag->data,
+				 daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL,
+				 NULL,
+				 daclinfo->defaclrole,
+				 false, "DEFAULT ACL", SECTION_NONE,
+				 q->data, "", NULL,
+				 daclinfo->dobj.dependencies, daclinfo->dobj.nDeps,
+				 NULL, NULL);
+
+	destroyPQExpBuffer(tag);
+	destroyPQExpBuffer(q);
+}
+
 /*----------
  * Write out grant/revoke information
  *
@@ -9820,7 +9996,8 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
 
 	sql = createPQExpBuffer();
 
-	if (!buildACLCommands(name, subname, type, acls, owner, fout->remoteVersion, sql))
+	if (!buildACLCommands(name, subname, type, acls, owner,
+						  "", fout->remoteVersion, sql))
 	{
 		write_msg(NULL, "could not parse ACL list (%s) for object \"%s\" (%s)\n",
 				  acls, name, type);
@@ -10263,7 +10440,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
 								  fmtId(tbinfo->dobj.name));
 				appendPQExpBuffer(q, "ALTER COLUMN %s ",
 								  fmtId(tbinfo->attnames[j]));
-				appendPQExpBuffer(q, "SET STATISTICS DISTINCT %g;\n", 
+				appendPQExpBuffer(q, "SET STATISTICS DISTINCT %g;\n",
 								  tbinfo->attdistinct[j]);
 			}
 
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index beec160110c590bb5feb3642309ca02f21b61236..5b2c3d1e6bffcb8e17add646e7b4b9e85b8513a1 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.157 2009/09/22 23:43:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.h,v 1.158 2009/10/05 19:24:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -114,6 +114,7 @@ typedef enum
 	DO_TSCONFIG,
 	DO_FDW,
 	DO_FOREIGN_SERVER,
+	DO_DEFAULT_ACL,
 	DO_BLOBS,
 	DO_BLOB_COMMENTS
 } DumpableObjectType;
@@ -432,6 +433,14 @@ typedef struct _foreignServerInfo
 	char	   *srvoptions;
 } ForeignServerInfo;
 
+typedef struct _defaultACLInfo
+{
+	DumpableObject dobj;
+	char	   *defaclrole;
+	char	    defaclobjtype;
+	char	   *defaclacl;
+} DefaultACLInfo;
+
 /* global decls */
 extern bool force_quotes;		/* double-quotes for identifiers flag */
 extern bool g_verbose;			/* verbose flag */
@@ -516,5 +525,6 @@ extern TSTemplateInfo *getTSTemplates(int *numTSTemplates);
 extern TSConfigInfo *getTSConfigurations(int *numTSConfigs);
 extern FdwInfo *getForeignDataWrappers(int *numForeignDataWrappers);
 extern ForeignServerInfo *getForeignServers(int *numForeignServers);
+extern DefaultACLInfo *getDefaultACLs(int *numDefaultACLs);
 
 #endif   /* PG_DUMP_H */
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index a56f429a7a8c8865f42a5c85272d0eef59008158..4e95618318bee638b28be95d57fac2cdeb1ed728 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump_sort.c,v 1.25 2009/06/11 14:49:07 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump_sort.c,v 1.26 2009/10/05 19:24:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,8 +23,8 @@ static const char *modulename = gettext_noop("sorter");
  * Objects are sorted by priority levels, and within an equal priority level
  * by OID.	(This is a relatively crude hack to provide semi-reasonable
  * behavior for old databases without full dependency info.)  Note: text
- * search and foreign-data objects can't really happen here, so the rather
- * bogus priorities for them don't matter.
+ * search, foreign-data, and default ACL objects can't really happen here,
+ * so the rather bogus priorities for them don't matter.
  */
 static const int oldObjectTypePriority[] =
 {
@@ -54,6 +54,7 @@ static const int oldObjectTypePriority[] =
 	5,							/* DO_TSCONFIG */
 	3,							/* DO_FDW */
 	4,							/* DO_FOREIGN_SERVER */
+	17,							/* DO_DEFAULT_ACL */
 	10,							/* DO_BLOBS */
 	11							/* DO_BLOB_COMMENTS */
 };
@@ -90,6 +91,7 @@ static const int newObjectTypePriority[] =
 	13,							/* DO_TSCONFIG */
 	14,							/* DO_FDW */
 	15,							/* DO_FOREIGN_SERVER */
+	27,							/* DO_DEFAULT_ACL */
 	20,							/* DO_BLOBS */
 	21							/* DO_BLOB_COMMENTS */
 };
@@ -1139,6 +1141,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
 					 "FOREIGN SERVER %s  (ID %d OID %u)",
 					 obj->name, obj->dumpId, obj->catId.oid);
 			return;
+		case DO_DEFAULT_ACL:
+			snprintf(buf, bufsize,
+					 "DEFAULT ACL %s  (ID %d OID %u)",
+					 obj->name, obj->dumpId, obj->catId.oid);
+			return;
 		case DO_BLOBS:
 			snprintf(buf, bufsize,
 					 "BLOBS  (ID %d)",
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 454b95c106cec7f24c7c43ca2c0be5d7beb12b86..f0a4d67d2afc9b457598bf3033755b0767943ffb 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.126 2009/06/11 14:49:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dumpall.c,v 1.127 2009/10/05 19:24:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -989,7 +989,7 @@ dumpTablespaces(PGconn *conn)
 
 		if (!skip_acls &&
 			!buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner,
-							  server_version, buf))
+							  "", server_version, buf))
 		{
 			fprintf(stderr, _("%s: could not parse ACL list (%s) for tablespace \"%s\"\n"),
 					progname, spcacl, fspcname);
@@ -1289,7 +1289,7 @@ dumpCreateDB(PGconn *conn)
 
 		if (!skip_acls &&
 			!buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner,
-							  server_version, buf))
+							  "", server_version, buf))
 		{
 			fprintf(stderr, _("%s: could not parse ACL list (%s) for database \"%s\"\n"),
 					progname, dbacl, fdbname);
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 67f05a89de0e5c1c0e385d3509e878000621a374..d94d8b80c526c833c5ac3de797acf0613abffb73 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2009, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.207 2009/09/13 22:18:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.208 2009/10/05 19:24:46 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "command.h"
@@ -361,7 +361,10 @@ exec_command(const char *cmd,
 				success = listCasts(pattern);
 				break;
 			case 'd':
-				success = objectDescription(pattern, show_system);
+				if (strcmp(cmd, "ddp") == 0)
+					success = listDefaultACLs(pattern);
+				else
+					success = objectDescription(pattern, show_system);
 				break;
 			case 'D':
 				success = listDomains(pattern, show_system);
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 6e288da67a88b5e7c0dd92898ca8ea383ed88c5c..1644623812c991982d160dbf187e44880ae61156 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -8,7 +8,7 @@
  *
  * Copyright (c) 2000-2009, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.226 2009/07/29 20:56:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.227 2009/10/05 19:24:46 tgl Exp $
  */
 #include "postgres_fe.h"
 
@@ -732,6 +732,73 @@ permissionsList(const char *pattern)
 }
 
 
+/*
+ * \ddp
+ *
+ * List DefaultACLs.  The pattern can match either schema or role name.
+ */
+bool
+listDefaultACLs(const char *pattern)
+{
+	PQExpBufferData buf;
+	PGresult   *res;
+	printQueryOpt myopt = pset.popt;
+	static const bool translate_columns[] = {false, false, true, false};
+
+	if (pset.sversion < 80500)
+	{
+		fprintf(stderr, _("The server (version %d.%d) does not support altering default privileges.\n"),
+				pset.sversion / 10000, (pset.sversion / 100) % 100);
+		return true;
+	}
+
+	initPQExpBuffer(&buf);
+
+	printfPQExpBuffer(&buf,
+					  "SELECT pg_catalog.pg_get_userbyid(d.defaclrole) AS \"%s\",\n"
+					  "  n.nspname AS \"%s\",\n"
+					  "  CASE d.defaclobjtype WHEN 'r' THEN '%s' WHEN 'S' THEN '%s' WHEN 'f' THEN '%s' END AS \"%s\",\n"
+					  "  ",
+					  gettext_noop("Owner"),
+					  gettext_noop("Schema"),
+					  gettext_noop("table"),
+					  gettext_noop("sequence"),
+					  gettext_noop("function"),
+					  gettext_noop("Type"));
+
+	printACLColumn(&buf, "d.defaclacl");
+
+	appendPQExpBuffer(&buf, "\nFROM pg_catalog.pg_default_acl d\n"
+	   "     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.defaclnamespace\n");
+
+	processSQLNamePattern(pset.db, &buf, pattern, false, false,
+						  NULL,
+						  "n.nspname",
+						  "pg_catalog.pg_get_userbyid(d.defaclrole)",
+						  NULL);
+
+	appendPQExpBuffer(&buf, "ORDER BY 1, 2, 3;");
+
+	res = PSQLexec(buf.data, false);
+	if (!res)
+	{
+		termPQExpBuffer(&buf);
+		return false;
+	}
+
+	myopt.nullPrint = NULL;
+	printfPQExpBuffer(&buf, _("Default access privileges"));
+	myopt.title = buf.data;
+	myopt.translate_header = true;
+	myopt.translate_columns = translate_columns;
+
+	printQuery(res, &myopt, pset.queryFout, pset.logfile);
+
+	termPQExpBuffer(&buf);
+	PQclear(res);
+	return true;
+}
+
 
 /*
  * Get object comments
diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h
index 769ee9e975dd3dd2053383a8cf59739740833503..169ceb3739ad2ec68f484dc1da87ec908c4dd69c 100644
--- a/src/bin/psql/describe.h
+++ b/src/bin/psql/describe.h
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2009, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/describe.h,v 1.40 2009/04/21 15:49:06 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.h,v 1.41 2009/10/05 19:24:46 tgl Exp $
  */
 #ifndef DESCRIBE_H
 #define DESCRIBE_H
@@ -30,6 +30,9 @@ extern bool describeRoles(const char *pattern, bool verbose);
 /* \z (or \dp) */
 extern bool permissionsList(const char *pattern);
 
+/* \ddp */
+extern bool listDefaultACLs(const char *pattern);
+
 /* \dd */
 extern bool objectDescription(const char *pattern, bool showSystem);
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 947eff050a1dc2d9d9cb98df8c6546fccfbc3192..f21099a69232c2148f4c25caf1a5183486c06927 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2009, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.152 2009/09/18 05:00:42 petere Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.153 2009/10/05 19:24:46 tgl Exp $
  */
 #include "postgres_fe.h"
 
@@ -201,6 +201,7 @@ slashUsage(unsigned short int pager)
 	fprintf(output, _("  \\dc[S]  [PATTERN]      list conversions\n"));
 	fprintf(output, _("  \\dC     [PATTERN]      list casts\n"));
 	fprintf(output, _("  \\dd[S]  [PATTERN]      show comments on objects\n"));
+	fprintf(output, _("  \\ddp    [PATTERN]      list default privileges\n"));
 	fprintf(output, _("  \\dD[S]  [PATTERN]      list domains\n"));
 	fprintf(output, _("  \\des[+] [PATTERN]      list foreign servers\n"));
 	fprintf(output, _("  \\deu[+] [PATTERN]      list user mappings\n"));
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 4ae915755663d988732392054bff8331240886c9..1d3bfb0726cc989ebf006994cbd1051cccab6c56 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.540 2009/09/26 22:42:01 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.541 2009/10/05 19:24:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200909261
+#define CATALOG_VERSION_NO	200910051
 
 #endif
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index fe04aab964389bc7d979ffea3eebfd3d2c5b69eb..954d3808a855aff69408ab23305161cd3c182264 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.40 2009/06/11 14:49:09 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.41 2009/10/05 19:24:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -146,6 +146,7 @@ typedef enum ObjectClass
 	OCLASS_FDW,					/* pg_foreign_data_wrapper */
 	OCLASS_FOREIGN_SERVER,		/* pg_foreign_server */
 	OCLASS_USER_MAPPING,		/* pg_user_mapping */
+	OCLASS_DEFACL,				/* pg_default_acl */
 	MAX_OCLASS					/* MUST BE LAST */
 } ObjectClass;
 
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 0d42cf11963624dd92f2813aa9271d0eb83aa380..5faf9c1c014fb6c6ff1fb81886a887da294d2450 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.93 2009/09/26 22:42:02 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.94 2009/10/05 19:24:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,6 +57,7 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 int oidinhcount,
 						 OnCommitAction oncommit,
 						 Datum reloptions,
+						 bool use_user_acl,
 						 bool allow_system_table_mods);
 
 extern void heap_drop_with_catalog(Oid relid);
@@ -76,6 +77,7 @@ extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
 extern void InsertPgClassTuple(Relation pg_class_desc,
 				   Relation new_rel_desc,
 				   Oid new_rel_oid,
+				   Datum relacl,
 				   Datum reloptions);
 
 extern List *AddRelationNewConstraints(Relation rel,
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ce117a8eecd98b97cf519067f73194489ce6a6fa..0272334f2e52da8c9992dda4a61bb07befa83ef8 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.108 2009/06/11 14:49:09 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.109 2009/10/05 19:24:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -267,6 +267,11 @@ DECLARE_UNIQUE_INDEX(pg_user_mapping_oid_index, 174, on pg_user_mapping using bt
 DECLARE_UNIQUE_INDEX(pg_user_mapping_user_server_index, 175, on pg_user_mapping using btree(umuser oid_ops, umserver oid_ops));
 #define UserMappingUserServerIndexId	175
 
+DECLARE_UNIQUE_INDEX(pg_default_acl_role_nsp_obj_index, 827, on pg_default_acl using btree(defaclrole oid_ops, defaclnamespace oid_ops, defaclobjtype char_ops));
+#define DefaultAclRoleNspObjIndexId	827
+DECLARE_UNIQUE_INDEX(pg_default_acl_oid_index, 828, on pg_default_acl using btree(oid oid_ops));
+#define DefaultAclOidIndexId	828
+
 /* last step of initialization script: build the indexes declared above */
 BUILD_INDICES
 
diff --git a/src/include/catalog/pg_default_acl.h b/src/include/catalog/pg_default_acl.h
new file mode 100644
index 0000000000000000000000000000000000000000..cfd1f2b3a9b052d94f61985528533cfa419d489e
--- /dev/null
+++ b/src/include/catalog/pg_default_acl.h
@@ -0,0 +1,75 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_default_acl.h
+ *	  definition of default ACLs for new objects.
+ *
+ *
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/include/catalog/pg_default_acl.h,v 1.1 2009/10/05 19:24:48 tgl Exp $
+ *
+ * NOTES
+ *	  the genbki.sh script reads this file and generates .bki
+ *	  information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_DEFAULT_ACL_H
+#define PG_DEFAULT_ACL_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_default_acl definition.  cpp turns this into
+ *		typedef struct FormData_pg_default_acl
+ * ----------------
+ */
+#define DefaultAclRelationId	826
+
+CATALOG(pg_default_acl,826)
+{
+	Oid			defaclrole;			/* OID of role owning this ACL */
+	Oid			defaclnamespace;	/* OID of namespace, or 0 for all */
+	char		defaclobjtype;		/* see DEFACLOBJ_xxx constants below */
+
+	/*
+	 * VARIABLE LENGTH FIELDS start here.
+	 */
+
+	aclitem		defaclacl[1];		/* permissions to add at CREATE time */
+} FormData_pg_default_acl;
+
+/* ----------------
+ *		Form_pg_default_acl corresponds to a pointer to a tuple with
+ *		the format of pg_default_acl relation.
+ * ----------------
+ */
+typedef FormData_pg_default_acl *Form_pg_default_acl;
+
+/* ----------------
+ *		compiler constants for pg_default_acl
+ * ----------------
+ */
+
+#define Natts_pg_default_acl					4
+#define Anum_pg_default_acl_defaclrole			1
+#define Anum_pg_default_acl_defaclnamespace		2
+#define Anum_pg_default_acl_defaclobjtype		3
+#define Anum_pg_default_acl_defaclacl			4
+
+/* ----------------
+ *		pg_default_acl has no initial contents
+ * ----------------
+ */
+
+/*
+ * Types of objects for which the user is allowed to specify default
+ * permissions through pg_default_acl.  These codes are used in the
+ * defaclobjtype column.
+ */
+#define DEFACLOBJ_RELATION		'r'		/* table, view */
+#define DEFACLOBJ_SEQUENCE		'S'		/* sequence */
+#define DEFACLOBJ_FUNCTION		'f'		/* function */
+
+#endif /* PG_DEFAULT_ACL_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index d842c9c3199f83d83f5822a502fd510be300794c..5fd046e95b85f5442b322f9bc3a97842b4a22749 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.226 2009/09/22 23:43:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.227 2009/10/05 19:24:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -261,6 +261,7 @@ typedef enum NodeTag
 	T_SetOperationStmt,
 	T_GrantStmt,
 	T_GrantRoleStmt,
+	T_AlterDefaultPrivilegesStmt,
 	T_ClosePortalStmt,
 	T_ClusterStmt,
 	T_CopyStmt,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 1ce28b77541ff6cb835576b56b4ff01baf8847f2..24066b9b38efb36f62c5140cf563bfa8946eaa00 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.402 2009/09/22 23:43:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.403 2009/10/05 19:24:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1259,6 +1259,17 @@ typedef struct GrantRoleStmt
 	DropBehavior behavior;		/* drop behavior (for REVOKE) */
 } GrantRoleStmt;
 
+/* ----------------------
+ *	Alter Default Privileges Statement
+ * ----------------------
+ */
+typedef struct AlterDefaultPrivilegesStmt
+{
+	NodeTag		type;
+	List	   *options;		/* list of DefElem */
+	GrantStmt  *action;			/* GRANT/REVOKE action (with objects=NIL) */
+} AlterDefaultPrivilegesStmt;
+
 /* ----------------------
  *		Copy Statement
  *
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 5c38822d5d7cb17ef64f111175e080049d28704b..977b00de7981bb7904346ebc7c0ad4a20d28b5d5 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.108 2009/06/11 14:49:13 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/acl.h,v 1.109 2009/10/05 19:24:49 tgl Exp $
  *
  * NOTES
  *	  An ACL array is simply an array of AclItems, representing the union
@@ -193,41 +193,23 @@ typedef enum AclObjectKind
 	MAX_ACL_KIND				/* MUST BE LAST */
 } AclObjectKind;
 
-/*
- * The information about one Grant/Revoke statement, in internal format: object
- * and grantees names have been turned into Oids, the privilege list is an
- * AclMode bitmask.  If 'privileges' is ACL_NO_RIGHTS (the 0 value) and
- * all_privs is true, 'privileges' will be internally set to the right kind of
- * ACL_ALL_RIGHTS_*, depending on the object type (NB - this will modify the
- * InternalGrant struct!)
- *
- * Note: 'all_privs' and 'privileges' represent object-level privileges only.
- * There might also be column-level privilege specifications, which are
- * represented in col_privs (this is a list of untransformed AccessPriv nodes).
- * Column privileges are only valid for objtype ACL_OBJECT_RELATION.
- */
-typedef struct
-{
-	bool		is_grant;
-	GrantObjectType objtype;
-	List	   *objects;
-	bool		all_privs;
-	AclMode		privileges;
-	List	   *col_privs;
-	List	   *grantees;
-	bool		grant_option;
-	DropBehavior behavior;
-} InternalGrant;
 
 /*
  * routines used internally
  */
 extern Acl *acldefault(GrantObjectType objtype, Oid ownerId);
+extern Acl *get_user_default_acl(GrantObjectType objtype, Oid ownerId,
+								 Oid nsp_oid);
+
 extern Acl *aclupdate(const Acl *old_acl, const AclItem *mod_aip,
 		  int modechg, Oid ownerId, DropBehavior behavior);
 extern Acl *aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId);
+extern Acl *make_empty_acl(void);
 extern Acl *aclcopy(const Acl *orig_acl);
 extern Acl *aclconcat(const Acl *left_acl, const Acl *right_acl);
+extern Acl *aclmerge(const Acl *left_acl, const Acl *right_acl, Oid ownerId);
+extern void aclitemsort(Acl *acl);
+extern bool aclequal(const Acl *left_acl, const Acl *right_acl);
 
 extern AclMode aclmask(const Acl *acl, Oid roleid, Oid ownerId,
 		AclMode mask, AclMaskHow how);
@@ -261,7 +243,10 @@ extern Datum hash_aclitem(PG_FUNCTION_ARGS);
  * prototypes for functions in aclchk.c
  */
 extern void ExecuteGrantStmt(GrantStmt *stmt);
-extern void ExecGrantStmt_oids(InternalGrant *istmt);
+extern void ExecAlterDefaultPrivilegesStmt(AlterDefaultPrivilegesStmt *stmt);
+
+extern void RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid);
+extern void RemoveDefaultACLById(Oid defaclOid);
 
 extern AclMode pg_attribute_aclmask(Oid table_oid, AttrNumber attnum,
 					 Oid roleid, AclMode mask, AclMaskHow how);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 1428b28d15449ca921aead0bb20c74ba7468860f..f0647e3419407dab07df274f4b7dcb5d419249dd 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.74 2009/01/01 17:24:02 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.75 2009/10/05 19:24:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,6 +49,7 @@ enum SysCacheIdentifier
 	CONSTROID,
 	CONVOID,
 	DATABASEOID,
+	DEFACLROLENSPOBJ,
 	ENUMOID,
 	ENUMTYPOIDNAME,
 	FOREIGNDATAWRAPPERNAME,
diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out
index 809b65621713431156c8038033a23bacc130315c..24239276dbfb5b3945429e2fe6c0dfd753b0a309 100644
--- a/src/test/regress/expected/privileges.out
+++ b/src/test/regress/expected/privileges.out
@@ -836,6 +836,117 @@ SELECT has_sequence_privilege('x_seq', 'USAGE');
  t
 (1 row)
 
+-- test default ACLs
+\c -
+CREATE SCHEMA testns;
+GRANT ALL ON SCHEMA testns TO regressuser1;
+CREATE TABLE testns.acltest1 (x int);
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- no
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT SELECT ON TABLE TO public;
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- no
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+DROP TABLE testns.acltest1;
+CREATE TABLE testns.acltest1 (x int);
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- yes
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT INSERT ON TABLE TO regressuser1;
+DROP TABLE testns.acltest1;
+CREATE TABLE testns.acltest1 (x int);
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- yes
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- yes
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns REVOKE INSERT ON TABLE FROM regressuser1;
+DROP TABLE testns.acltest1;
+CREATE TABLE testns.acltest1 (x int);
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- yes
+ has_table_privilege 
+---------------------
+ t
+(1 row)
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+ has_table_privilege 
+---------------------
+ f
+(1 row)
+
+ALTER DEFAULT PRIVILEGES FOR ROLE regressuser1 REVOKE EXECUTE ON FUNCTION FROM public;
+SET ROLE regressuser1;
+CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
+SELECT has_function_privilege('regressuser2', 'testns.foo()', 'EXECUTE'); -- no
+ has_function_privilege 
+------------------------
+ f
+(1 row)
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT EXECUTE ON FUNCTION to public;
+DROP FUNCTION testns.foo();
+CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
+SELECT has_function_privilege('regressuser2', 'testns.foo()', 'EXECUTE'); -- yes
+ has_function_privilege 
+------------------------
+ t
+(1 row)
+
+DROP FUNCTION testns.foo();
+RESET ROLE;
+SELECT count(*)
+  FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid
+  WHERE nspname = 'testns';
+ count 
+-------
+     2
+(1 row)
+
+DROP SCHEMA testns CASCADE;
+NOTICE:  drop cascades to table testns.acltest1
+SELECT d.*     -- check that entries went away
+  FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid
+  WHERE nspname IS NULL AND defaclnamespace != 0;
+ defaclrole | defaclnamespace | defaclobjtype | defaclacl 
+------------+-----------------+---------------+-----------
+(0 rows)
+
 -- clean up
 \c
 drop sequence x_seq;
@@ -860,7 +971,9 @@ DROP TABLE atestp1;
 DROP TABLE atestp2;
 DROP GROUP regressgroup1;
 DROP GROUP regressgroup2;
+-- these are needed to clean up permissions
 REVOKE USAGE ON LANGUAGE sql FROM regressuser1;
+DROP OWNED BY regressuser1;
 DROP USER regressuser1;
 DROP USER regressuser2;
 DROP USER regressuser3;
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 7213192d5f33bf10b97482edeb32623d6ffa2cf1..1994edc905e32e356965c78edd0ff0593844d4de 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -95,6 +95,7 @@ SELECT relname, relhasindex
  pg_constraint           | t
  pg_conversion           | t
  pg_database             | t
+ pg_default_acl          | t
  pg_depend               | t
  pg_description          | t
  pg_enum                 | t
@@ -151,7 +152,7 @@ SELECT relname, relhasindex
  timetz_tbl              | f
  tinterval_tbl           | f
  varchar_tbl             | f
-(140 rows)
+(141 rows)
 
 --
 -- another sanity check: every system catalog that has OIDs should have
diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql
index 917e8e5da4e14513d18cb14f909b436f90a17ec6..eaa879efa22d14dc1ede5f394420db5babca6ce9 100644
--- a/src/test/regress/sql/privileges.sql
+++ b/src/test/regress/sql/privileges.sql
@@ -484,6 +484,73 @@ SET SESSION AUTHORIZATION regressuser2;
 
 SELECT has_sequence_privilege('x_seq', 'USAGE');
 
+
+-- test default ACLs
+\c -
+
+CREATE SCHEMA testns;
+GRANT ALL ON SCHEMA testns TO regressuser1;
+
+CREATE TABLE testns.acltest1 (x int);
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- no
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT SELECT ON TABLE TO public;
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- no
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+
+DROP TABLE testns.acltest1;
+CREATE TABLE testns.acltest1 (x int);
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- yes
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT INSERT ON TABLE TO regressuser1;
+
+DROP TABLE testns.acltest1;
+CREATE TABLE testns.acltest1 (x int);
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- yes
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- yes
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns REVOKE INSERT ON TABLE FROM regressuser1;
+
+DROP TABLE testns.acltest1;
+CREATE TABLE testns.acltest1 (x int);
+
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'SELECT'); -- yes
+SELECT has_table_privilege('regressuser1', 'testns.acltest1', 'INSERT'); -- no
+
+ALTER DEFAULT PRIVILEGES FOR ROLE regressuser1 REVOKE EXECUTE ON FUNCTION FROM public;
+
+SET ROLE regressuser1;
+
+CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
+
+SELECT has_function_privilege('regressuser2', 'testns.foo()', 'EXECUTE'); -- no
+
+ALTER DEFAULT PRIVILEGES IN SCHEMA testns GRANT EXECUTE ON FUNCTION to public;
+
+DROP FUNCTION testns.foo();
+CREATE FUNCTION testns.foo() RETURNS int AS 'select 1' LANGUAGE sql;
+
+SELECT has_function_privilege('regressuser2', 'testns.foo()', 'EXECUTE'); -- yes
+
+DROP FUNCTION testns.foo();
+
+RESET ROLE;
+
+SELECT count(*)
+  FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid
+  WHERE nspname = 'testns';
+
+DROP SCHEMA testns CASCADE;
+
+SELECT d.*     -- check that entries went away
+  FROM pg_default_acl d LEFT JOIN pg_namespace n ON defaclnamespace = n.oid
+  WHERE nspname IS NULL AND defaclnamespace != 0;
+
 -- clean up
 
 \c
@@ -513,7 +580,10 @@ DROP TABLE atestp2;
 DROP GROUP regressgroup1;
 DROP GROUP regressgroup2;
 
+-- these are needed to clean up permissions
 REVOKE USAGE ON LANGUAGE sql FROM regressuser1;
+DROP OWNED BY regressuser1;
+
 DROP USER regressuser1;
 DROP USER regressuser2;
 DROP USER regressuser3;