Create an ALTER DEFAULT PRIVILEGES command, which allows users to adjust
authorTom Lane <[email protected]>
Mon, 5 Oct 2009 19:24:49 +0000 (19:24 +0000)
committerTom Lane <[email protected]>
Mon, 5 Oct 2009 19:24:49 +0000 (19:24 +0000)
the privileges that will be applied to subsequently-created objects.

Such adjustments are always per owning role, and can be restricted to objects
created in particular schemas too.  A notable benefit is that users can
override the traditional default privilege settings, eg, the PUBLIC EXECUTE
privilege traditionally granted by default for functions.

Petr Jelinek

48 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/alter_default_privileges.sgml [new file with mode: 0644]
doc/src/sgml/ref/grant.sgml
doc/src/sgml/ref/psql-ref.sgml
doc/src/sgml/reference.sgml
src/backend/bootstrap/bootparse.y
src/backend/catalog/Makefile
src/backend/catalog/aclchk.c
src/backend/catalog/dependency.c
src/backend/catalog/heap.c
src/backend/catalog/index.c
src/backend/catalog/pg_proc.c
src/backend/catalog/pg_shdepend.c
src/backend/catalog/toasting.c
src/backend/commands/cluster.c
src/backend/commands/tablecmds.c
src/backend/executor/execMain.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/tcop/utility.c
src/backend/utils/adt/acl.c
src/backend/utils/cache/syscache.c
src/bin/pg_dump/common.c
src/bin/pg_dump/dumputils.c
src/bin/pg_dump/dumputils.h
src/bin/pg_dump/pg_backup_archiver.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/pg_dump/pg_dump_sort.c
src/bin/pg_dump/pg_dumpall.c
src/bin/psql/command.c
src/bin/psql/describe.c
src/bin/psql/describe.h
src/bin/psql/help.c
src/include/catalog/catversion.h
src/include/catalog/dependency.h
src/include/catalog/heap.h
src/include/catalog/indexing.h
src/include/catalog/pg_default_acl.h [new file with mode: 0644]
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/utils/acl.h
src/include/utils/syscache.h
src/test/regress/expected/privileges.out
src/test/regress/expected/sanity_check.out
src/test/regress/sql/privileges.sql

index cc40c140ff455aba97ea174620ec893ab326bfd6..ef50e65f472c598a55eb30a93f36267ed662d95a 100644 (file)
       <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>
  </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>
 
index dbf3a3a953cea7cbff5a44aa30e1e404a70a3011..87f33c8f5a4ce4020c4a2af4c69cc8308d5aed9c 100644 (file)
@@ -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 (file)
index 0000000..ce17662
--- /dev/null
@@ -0,0 +1,211 @@
+<!--
+$PostgreSQL$
+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>
index 8aaedb3008209d9e8c173bff0a4450ec765357c3..4a993b6ed496cb158e47a94e907d0c2084db7b2a 100644 (file)
@@ -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>
index 85e937556dbd37315d21219db304cb0c2a7c4a66..8dba804c6cfa72d053736dc39348ebbe56ad9cb1 100644 (file)
@@ -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>).
index 723bb9c519b7c0b4102decc2e92a360ad80f1137..ac95ec8469c67ef6da2b981de7ce193b191bea6d 100644 (file)
@@ -37,6 +37,7 @@
    &alterAggregate;
    &alterConversion;
    &alterDatabase;
+   &alterDefaultPrivileges;
    &alterDomain;
    &alterForeignDataWrapper;
    &alterFunction;
index 3a3f504756aa03eb7857781fd7c33fecd589e552..12038922678b89b6dc51ff8b74ef972f6e4e3a95 100644 (file)
@@ -226,6 +226,7 @@ Boot_CreateStmt:
                                                                                                          0,
                                                                                                          ONCOMMIT_NOOP,
                                                                                                          (Datum) 0,
+                                                                                                         false,
                                                                                                          true);
                                                elog(DEBUG4, "relation created with oid %u", id);
                                        }
index 861cb1debc50853a3556d90e99f6e6c08146ad26..cb77a769ce236dcb3b77d5457ab9cd5821bfdf43 100644 (file)
@@ -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 \
     )
 
index ec4aaf0bf14ab9c063a469944fcac76ed1241f04..25a8f6ae669857fbe15179077eca14dba429a5e6 100644 (file)
@@ -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"
 #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;
+}
index cb9a9c2675024e7879990e4d4dd2296a96971392..7051caf54f174757ab23a824aa5d302877ba6f14 100644 (file)
@@ -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,
index e404dbe40e72bdc4ae7162d3f9fcebadf1ac518b..c258bd69c2ad4e8235a956cc7cfb34c769cb61b5 100644 (file)
@@ -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);
+               }
        }
 
        /*
index 824d4efead4b3c702f8278febe53461b1a16285c..c397bcb760508c62d82e8433c6106b6fa0bc19b9 100644 (file)
@@ -664,6 +664,7 @@ index_create(Oid heapRelationId,
         */
        InsertPgClassTuple(pg_class, indexRelation,
                                           RelationGetRelid(indexRelation),
+                                          (Datum) 0,
                                           reloptions);
 
        /* done with pg_class */
index d9e1e21a1704709d11192a53920d72bc6a8d85ff..4c1ecb156134a49883e86c690f576a9ad703a94b 100644 (file)
@@ -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);
index cd04053759f5a068969b83048e3174beacffb807..c4b6ebf7c5ca03576392740ee995afca10032bf0 100644 (file)
@@ -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 */
index be171503f3733929e582d86e0b11f6e871886375..b16f986265b1296f5d064f13bf10ac5e6815a760 100644 (file)
@@ -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 */
index 29602a4eea0d2a6db739fd22704332ce99c7b187..a20a909cf5f9050252964cb4ae281b4e1e5ca2b0 100644 (file)
@@ -713,6 +713,7 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
                                                                                  0,
                                                                                  ONCOMMIT_NOOP,
                                                                                  reloptions,
+                                                                                 false,
                                                                                  allowSystemTableMods);
 
        ReleaseSysCache(tuple);
index 90281d38e790764bd048c53d2e7e5996184e0da5..d3b89b12d7dbbddb2fc97acbc0330fce3ba3a386 100644 (file)
@@ -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
index 6c2fd67734b5e35973cbdc647f743115d56a095c..fc9785e3a769cbf018e8fad224017ec09a4b85c5 100644 (file)
@@ -2909,6 +2909,7 @@ OpenIntoRel(QueryDesc *queryDesc)
                                                                                          0,
                                                                                          into->onCommit,
                                                                                          reloptions,
+                                                                                         true,
                                                                                          allowSystemTableMods);
 
        FreeTupleDesc(tupdesc);
index 6420e4adb8323efdd67c2262fe65419539e749e3..067df4709b026777d588a939fd5cc15ab13fa870 100644 (file)
@@ -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;
index f125719a3c73af22f8d08e0bdf6fc5e3a8eac888..ac641e6aa12a808103d99d6f5c0010196956012e 100644 (file)
@@ -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;
index 732b1d7adc9f554c011355ac4b0d80270f1462fd..548b2f7e2340de7ff6e9132b9a547050f79fdd24 100644 (file)
@@ -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;
+                       &