Rethink pg_dump's handling of object ACLs.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 6 Dec 2021 17:39:45 +0000 (12:39 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 6 Dec 2021 17:39:45 +0000 (12:39 -0500)
Throw away most of the existing logic for this, as it was very
inefficient thanks to expensive sub-selects executed to collect
ACL data that we very possibly would have no interest in dumping.
Reduce the ACL handling in the initial per-object-type queries
to be just collection of the catalog ACL fields, as it was
originally.  Fetch pg_init_privs data separately in a single
scan of that catalog, and do the merging calculations on the
client side.  Remove the separate code path used for pre-9.6
source servers; there is no good reason to treat them differently
from newer servers that happen to have empty pg_init_privs.

Discussion: https://postgr.es/m/2273648.1634764485@sss.pgh.pa.us
Discussion: https://postgr.es/m/7d7eb6128f40401d81b3b7a898b6b4de@W2012-02.nidsa.loc

src/bin/pg_dump/dumputils.c
src/bin/pg_dump/dumputils.h
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/pg_dump/pg_dumpall.c
src/fe_utils/string_utils.c
src/include/fe_utils/string_utils.h

index ea67e52a3f47ee1a9b3db3d1c1bb734d7e03576b..6e216313c6d56b2bf329c170d2dd7e92d97aa884 100644 (file)
@@ -24,7 +24,7 @@ static bool parseAclItem(const char *item, const char *type,
                         const char *name, const char *subname, int remoteVersion,
                         PQExpBuffer grantee, PQExpBuffer grantor,
                         PQExpBuffer privs, PQExpBuffer privswgo);
-static char *copyAclUserName(PQExpBuffer output, char *input);
+static char *dequoteAclUserName(PQExpBuffer output, char *input);
 static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
                   const char *subname);
 
@@ -39,7 +39,8 @@ static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
  *     TABLE, SEQUENCE, FUNCTION, PROCEDURE, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
  *     FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT)
  * acls: the ACL string fetched from the database
- * racls: the ACL string of any initial-but-now-revoked privileges
+ * baseacls: the initial ACL string for this object; can be
+ *     NULL or empty string to indicate "not available from server"
  * 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
@@ -48,6 +49,12 @@ static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
  * Returns true if okay, false if could not parse the acl string.
  * The resulting commands (if any) are appended to the contents of 'sql'.
  *
+ * baseacls is typically the result of acldefault() for the object's type
+ * and owner.  However, if there is a pg_init_privs entry for the object,
+ * it should instead be the initprivs ACLs.  When acls is itself a
+ * pg_init_privs entry, baseacls is what to dump that relative to; then
+ * it can be either an acldefault() value or an empty ACL "{}".
+ *
  * Note: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES "
  * or something similar, and name is an empty string.
  *
@@ -56,15 +63,19 @@ static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
  */
 bool
 buildACLCommands(const char *name, const char *subname, const char *nspname,
-                const char *type, const char *acls, const char *racls,
+                const char *type, const char *acls, const char *baseacls,
                 const char *owner, const char *prefix, int remoteVersion,
                 PQExpBuffer sql)
 {
    bool        ok = true;
    char      **aclitems = NULL;
-   char      **raclitems = NULL;
+   char      **baseitems = NULL;
+   char      **grantitems = NULL;
+   char      **revokeitems = NULL;
    int         naclitems = 0;
-   int         nraclitems = 0;
+   int         nbaseitems = 0;
+   int         ngrantitems = 0;
+   int         nrevokeitems = 0;
    int         i;
    PQExpBuffer grantee,
                grantor,
@@ -72,37 +83,88 @@ buildACLCommands(const char *name, const char *subname, const char *nspname,
                privswgo;
    PQExpBuffer firstsql,
                secondsql;
-   bool        found_owner_privs = false;
 
-   if (strlen(acls) == 0 && strlen(racls) == 0)
+   /*
+    * If the acl was NULL (initial default state), we need do nothing.  Note
+    * that this is distinguishable from all-privileges-revoked, which will
+    * look like an empty array ("{}").
+    */
+   if (acls == NULL || *acls == '\0')
        return true;            /* object has default permissions */
 
    /* treat empty-string owner same as NULL */
    if (owner && *owner == '\0')
        owner = NULL;
 
-   if (strlen(acls) != 0)
+   /* Parse the acls array */
+   if (!parsePGArray(acls, &aclitems, &naclitems))
+   {
+       if (aclitems)
+           free(aclitems);
+       return false;
+   }
+
+   /* Parse the baseacls, if provided */
+   if (baseacls && *baseacls != '\0')
    {
-       if (!parsePGArray(acls, &aclitems, &naclitems))
+       if (!parsePGArray(baseacls, &baseitems, &nbaseitems))
        {
            if (aclitems)
                free(aclitems);
+           if (baseitems)
+               free(baseitems);
            return false;
        }
    }
 
-   if (strlen(racls) != 0)
+   /*
+    * Compare the actual ACL with the base ACL, extracting the privileges
+    * that need to be granted (i.e., are in the actual ACL but not the base
+    * ACL) and the ones that need to be revoked (the reverse).  We use plain
+    * string comparisons to check for matches.  In principle that could be
+    * fooled by extraneous issues such as whitespace, but since all these
+    * strings are the work of aclitemout(), it should be OK in practice.
+    * Besides, a false mismatch will just cause the output to be a little
+    * more verbose than it really needed to be.
+    *
+    * (If we weren't given a base ACL, this stanza winds up with all the
+    * ACL's items in grantitems and nothing in revokeitems.  It's not worth
+    * special-casing that.)
+    */
+   grantitems = (char **) pg_malloc(naclitems * sizeof(char *));
+   for (i = 0; i < naclitems; i++)
    {
-       if (!parsePGArray(racls, &raclitems, &nraclitems))
+       bool        found = false;
+
+       for (int j = 0; j < nbaseitems; j++)
        {
-           if (aclitems)
-               free(aclitems);
-           if (raclitems)
-               free(raclitems);
-           return false;
+           if (strcmp(aclitems[i], baseitems[j]) == 0)
+           {
+               found = true;
+               break;
+           }
        }
+       if (!found)
+           grantitems[ngrantitems++] = aclitems[i];
    }
+   revokeitems = (char **) pg_malloc(nbaseitems * sizeof(char *));
+   for (i = 0; i < nbaseitems; i++)
+   {
+       bool        found = false;
 
+       for (int j = 0; j < naclitems; j++)
+       {
+           if (strcmp(baseitems[i], aclitems[j]) == 0)
+           {
+               found = true;
+               break;
+           }
+       }
+       if (!found)
+           revokeitems[nrevokeitems++] = baseitems[i];
+   }
+
+   /* Prepare working buffers */
    grantee = createPQExpBuffer();
    grantor = createPQExpBuffer();
    privs = createPQExpBuffer();
@@ -110,50 +172,21 @@ buildACLCommands(const char *name, const char *subname, const char *nspname,
 
    /*
     * At the end, these two will be pasted together to form the result.
-    *
-    * For older systems we use these to ensure that the owner privileges go
-    * before the other ones, as a GRANT could create the default entry for
-    * the object, which generally includes all rights for the owner. In more
-    * recent versions we normally handle this because the owner rights come
-    * first in the ACLs, but older versions might have them after the PUBLIC
-    * privileges.
-    *
-    * For 9.6 and later systems, much of this changes.  With 9.6, we check
-    * the default privileges for the objects at dump time and create two sets
-    * of ACLs- "racls" which are the ACLs to REVOKE from the object (as the
-    * object may have initial privileges on it, along with any default ACLs
-    * which are not part of the current set of privileges), and regular
-    * "acls", which are the ACLs to GRANT to the object.  We handle the
-    * REVOKEs first, followed by the GRANTs.
     */
    firstsql = createPQExpBuffer();
    secondsql = createPQExpBuffer();
 
    /*
-    * For pre-9.6 systems, we always start with REVOKE ALL FROM PUBLIC, as we
-    * don't wish to make any assumptions about what the default ACLs are, and
-    * we do not collect them during the dump phase (and racls will always be
-    * the empty set, see above).
-    *
-    * For 9.6 and later, if any revoke ACLs have been provided, then include
-    * them in 'firstsql'.
+    * If we weren't given baseacls information, we just revoke everything and
+    * then grant what's listed in the ACL.  This avoids having to embed
+    * detailed knowledge about what the defaults are/were, and it's not very
+    * expensive since servers lacking acldefault() are now rare.
     *
-    * Revoke ACLs happen when an object starts out life with a set of
-    * privileges (eg: GRANT SELECT ON pg_class TO PUBLIC;) and the user has
-    * decided to revoke those rights.  Since those objects come into being
-    * with those default privileges, we have to revoke them to match what the
-    * current state of affairs is.  Note that we only started explicitly
-    * tracking such initial rights in 9.6, and prior to that all initial
-    * rights are actually handled by the simple 'REVOKE ALL .. FROM PUBLIC'
-    * case, for initdb-created objects.  Prior to 9.6, we didn't handle
-    * extensions correctly, but we do now by tracking their initial
-    * privileges, in the same way we track initdb initial privileges, see
-    * pg_init_privs.
+    * Otherwise, we need only revoke what's listed in revokeitems.
     */
-   if (remoteVersion < 90600)
+   if (baseacls == NULL || *baseacls == '\0')
    {
-       Assert(nraclitems == 0);
-
+       /* We assume the old defaults only involved the owner and PUBLIC */
        appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
        if (subname)
            appendPQExpBuffer(firstsql, "(%s)", subname);
@@ -161,13 +194,24 @@ buildACLCommands(const char *name, const char *subname, const char *nspname,
        if (nspname && *nspname)
            appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
        appendPQExpBuffer(firstsql, "%s FROM PUBLIC;\n", name);
+       if (owner)
+       {
+           appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
+           if (subname)
+               appendPQExpBuffer(firstsql, "(%s)", subname);
+           appendPQExpBuffer(firstsql, " ON %s ", type);
+           if (nspname && *nspname)
+               appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
+           appendPQExpBuffer(firstsql, "%s FROM %s;\n", name, fmtId(owner));
+       }
    }
    else
    {
        /* Scan individual REVOKE ACL items */
-       for (i = 0; i < nraclitems; i++)
+       for (i = 0; i < nrevokeitems; i++)
        {
-           if (!parseAclItem(raclitems[i], type, name, subname, remoteVersion,
+           if (!parseAclItem(revokeitems[i],
+                             type, name, subname, remoteVersion,
                              grantee, grantor, privs, NULL))
            {
                ok = false;
@@ -195,6 +239,10 @@ buildACLCommands(const char *name, const char *subname, const char *nspname,
    }
 
    /*
+    * At this point we have issued REVOKE statements for all initial and
+    * default privileges that are no longer present on the object, so we are
+    * almost ready to GRANT the privileges listed in grantitems[].
+    *
     * We still need some hacking though to cover the case where new default
     * public privileges are added in new versions: the REVOKE ALL will revoke
     * them, leading to behavior different from what the old version had,
@@ -208,146 +256,92 @@ buildACLCommands(const char *name, const char *subname, const char *nspname,
                          prefix, type, name);
    }
 
-   /* Scan individual ACL items */
-   for (i = 0; i < naclitems; i++)
+   /*
+    * Scan individual ACL items to be granted.
+    *
+    * The order in which privileges appear in the ACL string (the order they
+    * have been GRANT'd in, which the backend maintains) must be preserved to
+    * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on
+    * those are dumped in the correct order.  However, some old server
+    * versions will show grants to PUBLIC before the owner's own grants; for
+    * consistency's sake, force the owner's grants to be output first.
+    */
+   for (i = 0; i < ngrantitems; i++)
    {
-       if (!parseAclItem(aclitems[i], type, name, subname, remoteVersion,
-                         grantee, grantor, privs, privswgo))
-       {
-           ok = false;
-           break;
-       }
-
-       if (grantor->len == 0 && owner)
-           printfPQExpBuffer(grantor, "%s", owner);
-
-       if (privs->len > 0 || privswgo->len > 0)
+       if (parseAclItem(grantitems[i], type, name, subname, remoteVersion,
+                        grantee, grantor, privs, privswgo))
        {
            /*
-            * Prior to 9.6, we had to handle owner privileges in a special
-            * manner by first REVOKE'ing the rights and then GRANT'ing them
-            * after.  With 9.6 and above, what we need to REVOKE and what we
-            * need to GRANT is figured out when we dump and stashed into
-            * "racls" and "acls", respectively.  See above.
+            * If the grantor isn't the owner, we'll need to use SET SESSION
+            * AUTHORIZATION to become the grantor.  Issue the SET/RESET only
+            * if there's something useful to do.
             */
-           if (remoteVersion < 90600 && owner
-               && strcmp(grantee->data, owner) == 0
-               && strcmp(grantor->data, owner) == 0)
+           if (privs->len > 0 || privswgo->len > 0)
            {
-               found_owner_privs = true;
+               PQExpBuffer thissql;
+
+               /* Set owner as grantor if that's not explicit in the ACL */
+               if (grantor->len == 0 && owner)
+                   printfPQExpBuffer(grantor, "%s", owner);
+
+               /* Make sure owner's own grants are output before others */
+               if (owner &&
+                   strcmp(grantee->data, owner) == 0 &&
+                   strcmp(grantor->data, owner) == 0)
+                   thissql = firstsql;
+               else
+                   thissql = secondsql;
 
-               /*
-                * For the owner, the default privilege level is ALL WITH
-                * GRANT OPTION.
-                */
-               if (strcmp(privswgo->data, "ALL") != 0)
-               {
-                   appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
-                   if (subname)
-                       appendPQExpBuffer(firstsql, "(%s)", subname);
-                   appendPQExpBuffer(firstsql, " ON %s ", type);
-                   if (nspname && *nspname)
-                       appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
-                   appendPQExpBuffer(firstsql, "%s FROM %s;\n",
-                                     name, fmtId(grantee->data));
-                   if (privs->len > 0)
-                   {
-                       appendPQExpBuffer(firstsql,
-                                         "%sGRANT %s ON %s ",
-                                         prefix, privs->data, type);
-                       if (nspname && *nspname)
-                           appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
-                       appendPQExpBuffer(firstsql,
-                                         "%s TO %s;\n",
-                                         name, fmtId(grantee->data));
-                   }
-                   if (privswgo->len > 0)
-                   {
-                       appendPQExpBuffer(firstsql,
-                                         "%sGRANT %s ON %s ",
-                                         prefix, privswgo->data, type);
-                       if (nspname && *nspname)
-                           appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
-                       appendPQExpBuffer(firstsql,
-                                         "%s TO %s WITH GRANT OPTION;\n",
-                                         name, fmtId(grantee->data));
-                   }
-               }
-           }
-           else
-           {
-               /*
-                * For systems prior to 9.6, we can assume we are starting
-                * from no privs at this point.
-                *
-                * For 9.6 and above, at this point we have issued REVOKE
-                * statements for all initial and default privileges which are
-                * no longer present on the object (as they were passed in as
-                * 'racls') and we can simply GRANT the rights which are in
-                * 'acls'.
-                */
                if (grantor->len > 0
                    && (!owner || strcmp(owner, grantor->data) != 0))
-                   appendPQExpBuffer(secondsql, "SET SESSION AUTHORIZATION %s;\n",
+                   appendPQExpBuffer(thissql, "SET SESSION AUTHORIZATION %s;\n",
                                      fmtId(grantor->data));
 
                if (privs->len > 0)
                {
-                   appendPQExpBuffer(secondsql, "%sGRANT %s ON %s ",
+                   appendPQExpBuffer(thissql, "%sGRANT %s ON %s ",
                                      prefix, privs->data, type);
                    if (nspname && *nspname)
-                       appendPQExpBuffer(secondsql, "%s.", fmtId(nspname));
-                   appendPQExpBuffer(secondsql, "%s TO ", name);
+                       appendPQExpBuffer(thissql, "%s.", fmtId(nspname));
+                   appendPQExpBuffer(thissql, "%s TO ", name);
                    if (grantee->len == 0)
-                       appendPQExpBufferStr(secondsql, "PUBLIC;\n");
+                       appendPQExpBufferStr(thissql, "PUBLIC;\n");
                    else if (strncmp(grantee->data, "group ",
                                     strlen("group ")) == 0)
-                       appendPQExpBuffer(secondsql, "GROUP %s;\n",
+                       appendPQExpBuffer(thissql, "GROUP %s;\n",
                                          fmtId(grantee->data + strlen("group ")));
                    else
-                       appendPQExpBuffer(secondsql, "%s;\n", fmtId(grantee->data));
+                       appendPQExpBuffer(thissql, "%s;\n", fmtId(grantee->data));
                }
                if (privswgo->len > 0)
                {
-                   appendPQExpBuffer(secondsql, "%sGRANT %s ON %s ",
+                   appendPQExpBuffer(thissql, "%sGRANT %s ON %s ",
                                      prefix, privswgo->data, type);
                    if (nspname && *nspname)
-                       appendPQExpBuffer(secondsql, "%s.", fmtId(nspname));
-                   appendPQExpBuffer(secondsql, "%s TO ", name);
+                       appendPQExpBuffer(thissql, "%s.", fmtId(nspname));
+                   appendPQExpBuffer(thissql, "%s TO ", name);
                    if (grantee->len == 0)
-                       appendPQExpBufferStr(secondsql, "PUBLIC");
+                       appendPQExpBufferStr(thissql, "PUBLIC");
                    else if (strncmp(grantee->data, "group ",
                                     strlen("group ")) == 0)
-                       appendPQExpBuffer(secondsql, "GROUP %s",
+                       appendPQExpBuffer(thissql, "GROUP %s",
                                          fmtId(grantee->data + strlen("group ")));
                    else
-                       appendPQExpBufferStr(secondsql, fmtId(grantee->data));
-                   appendPQExpBufferStr(secondsql, " WITH GRANT OPTION;\n");
+                       appendPQExpBufferStr(thissql, fmtId(grantee->data));
+                   appendPQExpBufferStr(thissql, " WITH GRANT OPTION;\n");
                }
 
                if (grantor->len > 0
                    && (!owner || strcmp(owner, grantor->data) != 0))
-                   appendPQExpBufferStr(secondsql, "RESET SESSION AUTHORIZATION;\n");
+                   appendPQExpBufferStr(thissql, "RESET SESSION AUTHORIZATION;\n");
            }
        }
-   }
-
-   /*
-    * For systems prior to 9.6, if we didn't find any owner privs, the owner
-    * must have revoked 'em all.
-    *
-    * For 9.6 and above, we handle this through the 'racls'.  See above.
-    */
-   if (remoteVersion < 90600 && !found_owner_privs && owner)
-   {
-       appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
-       if (subname)
-           appendPQExpBuffer(firstsql, "(%s)", subname);
-       appendPQExpBuffer(firstsql, " ON %s ", type);
-       if (nspname && *nspname)
-           appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
-       appendPQExpBuffer(firstsql, "%s FROM %s;\n",
-                         name, fmtId(owner));
+       else
+       {
+           /* parseAclItem failed, give up */
+           ok = false;
+           break;
+       }
    }
 
    destroyPQExpBuffer(grantee);
@@ -361,19 +355,23 @@ buildACLCommands(const char *name, const char *subname, const char *nspname,
 
    if (aclitems)
        free(aclitems);
-
-   if (raclitems)
-       free(raclitems);
+   if (baseitems)
+       free(baseitems);
+   if (grantitems)
+       free(grantitems);
+   if (revokeitems)
+       free(revokeitems);
 
    return ok;
 }
 
 /*
- * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry.
+ * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry.
  *
  * type: the object type (TABLES, FUNCTIONS, etc)
  * nspname: schema name, or NULL for global default privileges
  * acls: the ACL string fetched from the database
+ * acldefault: the appropriate default ACL for the object type and owner
  * owner: username of privileges owner (will be passed through fmtId)
  * remoteVersion: version of database
  *
@@ -382,8 +380,7 @@ buildACLCommands(const char *name, const char *subname, const char *nspname,
  */
 bool
 buildDefaultACLCommands(const char *type, const char *nspname,
-                       const char *acls, const char *racls,
-                       const char *initacls, const char *initracls,
+                       const char *acls, const char *acldefault,
                        const char *owner,
                        int remoteVersion,
                        PQExpBuffer sql)
@@ -403,21 +400,12 @@ buildDefaultACLCommands(const char *type, const char *nspname,
    if (nspname)
        appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
 
-   if (strlen(initacls) != 0 || strlen(initracls) != 0)
-   {
-       appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
-       if (!buildACLCommands("", NULL, NULL, type,
-                             initacls, initracls, owner,
-                             prefix->data, remoteVersion, sql))
-       {
-           destroyPQExpBuffer(prefix);
-           return false;
-       }
-       appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
-   }
-
+   /*
+    * There's no such thing as initprivs for a default ACL, so the base ACL
+    * is always just the object-type-specific default.
+    */
    if (!buildACLCommands("", NULL, NULL, type,
-                         acls, racls, owner,
+                         acls, acldefault, owner,
                          prefix->data, remoteVersion, sql))
    {
        destroyPQExpBuffer(prefix);
@@ -467,7 +455,7 @@ parseAclItem(const char *item, const char *type,
    buf = pg_strdup(item);
 
    /* user or group name is string up to = */
-   eqpos = copyAclUserName(grantee, buf);
+   eqpos = dequoteAclUserName(grantee, buf);
    if (*eqpos != '=')
    {
        pg_free(buf);
@@ -479,7 +467,7 @@ parseAclItem(const char *item, const char *type,
    if (slpos)
    {
        *slpos++ = '\0';
-       slpos = copyAclUserName(grantor, slpos);
+       slpos = dequoteAclUserName(grantor, slpos);
        if (*slpos != '\0')
        {
            pg_free(buf);
@@ -603,13 +591,46 @@ do { \
    return true;
 }
 
+/*
+ * Transfer the role name at *input into the output buffer, adding
+ * quoting according to the same rules as putid() in backend's acl.c.
+ */
+void
+quoteAclUserName(PQExpBuffer output, const char *input)
+{
+   const char *src;
+   bool        safe = true;
+
+   for (src = input; *src; src++)
+   {
+       /* This test had better match what putid() does */
+       if (!isalnum((unsigned char) *src) && *src != '_')
+       {
+           safe = false;
+           break;
+       }
+   }
+   if (!safe)
+       appendPQExpBufferChar(output, '"');
+   for (src = input; *src; src++)
+   {
+       /* A double quote character in a username is encoded as "" */
+       if (*src == '"')
+           appendPQExpBufferChar(output, '"');
+       appendPQExpBufferChar(output, *src);
+   }
+   if (!safe)
+       appendPQExpBufferChar(output, '"');
+}
+
 /*
  * Transfer a user or group name starting at *input into the output buffer,
  * dequoting if needed.  Returns a pointer to just past the input name.
  * The name is taken to end at an unquoted '=' or end of string.
+ * Note: unlike quoteAclUserName(), this first clears the output buffer.
  */
 static char *
-copyAclUserName(PQExpBuffer output, char *input)
+dequoteAclUserName(PQExpBuffer output, char *input)
 {
    resetPQExpBuffer(output);
 
@@ -708,137 +729,6 @@ emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
    }
 }
 
-/*
- * buildACLQueries
- *
- * Build the subqueries to extract out the correct set of ACLs to be
- * GRANT'd and REVOKE'd for the specific kind of object, accounting for any
- * initial privileges (from pg_init_privs) and based on if we are in binary
- * upgrade mode or not.
- *
- * Also builds subqueries to extract out the set of ACLs to go from the object
- * default privileges to the privileges in pg_init_privs, if we are in binary
- * upgrade mode, so that those privileges can be set up and recorded in the new
- * cluster before the regular privileges are added on top of those.
- */
-void
-buildACLQueries(PQExpBuffer acl_subquery, PQExpBuffer racl_subquery,
-               PQExpBuffer init_acl_subquery, PQExpBuffer init_racl_subquery,
-               const char *acl_column, const char *acl_owner,
-               const char *initprivs_expr,
-               const char *obj_kind, bool binary_upgrade)
-{
-   /*
-    * To get the delta from what the permissions were at creation time
-    * (either initdb or CREATE EXTENSION) vs. what they are now, we have to
-    * look at two things:
-    *
-    * What privileges have been added, which we calculate by extracting all
-    * the current privileges (using the set of default privileges for the
-    * object type if current privileges are NULL) and then removing those
-    * which existed at creation time (again, using the set of default
-    * privileges for the object type if there were no creation time
-    * privileges).
-    *
-    * What privileges have been removed, which we calculate by extracting the
-    * privileges as they were at creation time (or the default privileges, as
-    * above), and then removing the current privileges (or the default
-    * privileges, if current privileges are NULL).
-    *
-    * As a good cross-check, both directions of these checks should result in
-    * the empty set if both the current ACL and the initial privs are NULL
-    * (meaning, in practice, that the default ACLs were there at init time
-    * and is what the current privileges are).
-    *
-    * We always perform this delta on all ACLs and expect that by the time
-    * these are run the initial privileges will be in place, even in a binary
-    * upgrade situation (see below).
-    *
-    * Finally, the order in which privileges are in the ACL string (the order
-    * they been GRANT'd in, which the backend maintains) must be preserved to
-    * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on
-    * those are dumped in the correct order.
-    */
-   printfPQExpBuffer(acl_subquery,
-                     "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM "
-                     "(SELECT acl, row_n FROM "
-                     "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) "
-                     "WITH ORDINALITY AS perm(acl,row_n) "
-                     "WHERE NOT EXISTS ( "
-                     "SELECT 1 FROM "
-                     "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) "
-                     "AS init(init_acl) WHERE acl = init_acl)) as foo)",
-                     acl_column,
-                     obj_kind,
-                     acl_owner,
-                     initprivs_expr,
-                     obj_kind,
-                     acl_owner);
-
-   printfPQExpBuffer(racl_subquery,
-                     "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM "
-                     "(SELECT acl, row_n FROM "
-                     "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) "
-                     "WITH ORDINALITY AS initp(acl,row_n) "
-                     "WHERE NOT EXISTS ( "
-                     "SELECT 1 FROM "
-                     "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) "
-                     "AS permp(orig_acl) WHERE acl = orig_acl)) as foo)",
-                     initprivs_expr,
-                     obj_kind,
-                     acl_owner,
-                     acl_column,
-                     obj_kind,
-                     acl_owner);
-
-   /*
-    * In binary upgrade mode we don't run the extension script but instead
-    * dump out the objects independently and then recreate them.  To preserve
-    * the initial privileges which were set on extension objects, we need to
-    * grab the set of GRANT and REVOKE commands necessary to get from the
-    * default privileges of an object to the initial privileges as recorded
-    * in pg_init_privs.
-    *
-    * These will then be run ahead of the regular ACL commands, which were
-    * calculated using the queries above, inside of a block which sets a flag
-    * to indicate that the backend should record the results of these GRANT
-    * and REVOKE statements into pg_init_privs.  This is how we preserve the
-    * contents of that catalog across binary upgrades.
-    */
-   if (binary_upgrade)
-   {
-       printfPQExpBuffer(init_acl_subquery,
-                         "CASE WHEN privtype = 'e' THEN "
-                         "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM "
-                         "(SELECT acl, row_n FROM pg_catalog.unnest(%s) "
-                         "WITH ORDINALITY AS initp(acl,row_n) "
-                         "WHERE NOT EXISTS ( "
-                         "SELECT 1 FROM "
-                         "pg_catalog.unnest(pg_catalog.acldefault(%s,%s)) "
-                         "AS privm(orig_acl) WHERE acl = orig_acl)) as foo) END",
-                         initprivs_expr,
-                         obj_kind,
-                         acl_owner);
-
-       printfPQExpBuffer(init_racl_subquery,
-                         "CASE WHEN privtype = 'e' THEN "
-                         "(SELECT pg_catalog.array_agg(acl) FROM "
-                         "(SELECT acl, row_n FROM "
-                         "pg_catalog.unnest(pg_catalog.acldefault(%s,%s)) "
-                         "WITH ORDINALITY AS privp(acl,row_n) "
-                         "WHERE NOT EXISTS ( "
-                         "SELECT 1 FROM pg_catalog.unnest(%s) "
-                         "AS initp(init_acl) WHERE acl = init_acl)) as foo) END",
-                         obj_kind,
-                         acl_owner,
-                         initprivs_expr);
-   }
-   else
-   {
-       printfPQExpBuffer(init_acl_subquery, "NULL");
-       printfPQExpBuffer(init_racl_subquery, "NULL");
-   }
-}
 
 /*
  * Detect whether the given GUC variable is of GUC_LIST_QUOTE type.
index f5465f19aee35d34cad43e88a224dd30d367b1d4..fac7a05c913c68fb935f21ab4149128675ab795d 100644 (file)
 
 
 extern bool buildACLCommands(const char *name, const char *subname, const char *nspname,
-                            const char *type, const char *acls, const char *racls,
+                            const char *type, const char *acls, const char *baseacls,
                             const char *owner, const char *prefix, int remoteVersion,
                             PQExpBuffer sql);
 extern bool buildDefaultACLCommands(const char *type, const char *nspname,
-                                   const char *acls, const char *racls,
-                                   const char *initacls, const char *initracls,
+                                   const char *acls, const char *acldefault,
                                    const char *owner,
                                    int remoteVersion,
                                    PQExpBuffer sql);
+
+extern void quoteAclUserName(PQExpBuffer output, const char *input);
+
 extern void buildShSecLabelQuery(const char *catalog_name,
                                 Oid objectId, PQExpBuffer sql);
 extern void emitShSecLabels(PGconn *conn, PGresult *res,
                            PQExpBuffer buffer, const char *objtype, const char *objname);
 
-extern void buildACLQueries(PQExpBuffer acl_subquery, PQExpBuffer racl_subquery,
-                           PQExpBuffer init_acl_subquery, PQExpBuffer init_racl_subquery,
-                           const char *acl_column, const char *acl_owner,
-                           const char *initprivs_expr,
-                           const char *obj_kind, bool binary_upgrade);
-
 extern bool variable_is_guc_list_quote(const char *name);
 
 extern bool SplitGUCList(char *rawstring, char separator,
index 15f55cbb19d28da06e4a922f1322f5ffde60bd90..75ea57266e2ad2639a0791f20c914773884e4377 100644 (file)
@@ -179,6 +179,7 @@ static NamespaceInfo *findNamespace(Oid nsoid);
 static void dumpTableData(Archive *fout, const TableDataInfo *tdinfo);
 static void refreshMatViewData(Archive *fout, const TableDataInfo *tdinfo);
 static void guessConstraintInheritance(TableInfo *tblinfo, int numTables);
+static void getAdditionalACLs(Archive *fout);
 static void dumpCommentExtended(Archive *fout, const char *type,
                                const char *name, const char *namespace,
                                const char *owner, CatalogId catalogId,
@@ -248,8 +249,7 @@ static void dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo);
 static DumpId dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
                      const char *type, const char *name, const char *subname,
                      const char *nspname, const char *owner,
-                     const char *acls, const char *racls,
-                     const char *initacls, const char *initracls);
+                     const DumpableAcl *dacl);
 
 static void getDependencies(Archive *fout);
 static void BuildArchiveDependencies(Archive *fout);
@@ -888,8 +888,10 @@ main(int argc, char **argv)
    getDependencies(fout);
 
    /*
-    * Collect comments and security labels, if wanted.
+    * Collect ACLs, comments, and security labels, if wanted.
     */
+   if (!dopt.aclsSkip)
+       getAdditionalACLs(fout);
    if (!dopt.no_comments)
        collectComments(fout);
    if (!dopt.no_security_labels)
@@ -2859,19 +2861,18 @@ dumpDatabase(Archive *fout)
                i_frozenxid,
                i_minmxid,
                i_datacl,
-               i_rdatacl,
+               i_acldefault,
                i_datistemplate,
                i_datconnlimit,
                i_tablespace;
    CatalogId   dbCatId;
    DumpId      dbDumpId;
+   DumpableAcl dbdacl;
    const char *datname,
               *dba,
               *encoding,
               *collate,
               *ctype,
-              *datacl,
-              *rdatacl,
               *datistemplate,
               *datconnlimit,
               *tablespace;
@@ -2883,40 +2884,14 @@ dumpDatabase(Archive *fout)
 
    /*
     * Fetch the database-level properties for this database.
-    *
-    * The order in which privileges are in the ACL string (the order they
-    * have been GRANT'd in, which the backend maintains) must be preserved to
-    * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on
-    * those are dumped in the correct order.  Note that initial privileges
-    * (pg_init_privs) are not supported on databases, so this logic cannot
-    * make use of buildACLQueries().
     */
-   if (fout->remoteVersion >= 90600)
+   if (fout->remoteVersion >= 90300)
    {
        appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, "
                          "(%s datdba) AS dba, "
                          "pg_encoding_to_char(encoding) AS encoding, "
                          "datcollate, datctype, datfrozenxid, datminmxid, "
-                         "(SELECT array_agg(acl ORDER BY row_n) FROM "
-                         "  (SELECT acl, row_n FROM "
-                         "     unnest(coalesce(datacl,acldefault('d',datdba))) "
-                         "     WITH ORDINALITY AS perm(acl,row_n) "
-                         "   WHERE NOT EXISTS ( "
-                         "     SELECT 1 "
-                         "     FROM unnest(acldefault('d',datdba)) "
-                         "       AS init(init_acl) "
-                         "     WHERE acl = init_acl)) AS datacls) "
-                         " AS datacl, "
-                         "(SELECT array_agg(acl ORDER BY row_n) FROM "
-                         "  (SELECT acl, row_n FROM "
-                         "     unnest(acldefault('d',datdba)) "
-                         "     WITH ORDINALITY AS initp(acl,row_n) "
-                         "   WHERE NOT EXISTS ( "
-                         "     SELECT 1 "
-                         "     FROM unnest(coalesce(datacl,acldefault('d',datdba))) "
-                         "       AS permp(orig_acl) "
-                         "     WHERE acl = orig_acl)) AS rdatacls) "
-                         " AS rdatacl, "
+                         "datacl, acldefault('d', datdba) AS acldefault, "
                          "datistemplate, datconnlimit, "
                          "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
                          "shobj_description(oid, 'pg_database') AS description "
@@ -2925,13 +2900,14 @@ dumpDatabase(Archive *fout)
                          "WHERE datname = current_database()",
                          username_subquery);
    }
-   else if (fout->remoteVersion >= 90300)
+   else if (fout->remoteVersion >= 90200)
    {
        appendPQExpBuffer(dbQry, "SELECT tableoid, oid, datname, "
                          "(%s datdba) AS dba, "
                          "pg_encoding_to_char(encoding) AS encoding, "
-                         "datcollate, datctype, datfrozenxid, datminmxid, "
-                         "datacl, '' as rdatacl, datistemplate, datconnlimit, "
+                         "datcollate, datctype, datfrozenxid, 0 AS datminmxid, "
+                         "datacl, acldefault('d', datdba) AS acldefault, "
+                         "datistemplate, datconnlimit, "
                          "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
                          "shobj_description(oid, 'pg_database') AS description "
 
@@ -2945,7 +2921,8 @@ dumpDatabase(Archive *fout)
                          "(%s datdba) AS dba, "
                          "pg_encoding_to_char(encoding) AS encoding, "
                          "datcollate, datctype, datfrozenxid, 0 AS datminmxid, "
-                         "datacl, '' as rdatacl, datistemplate, datconnlimit, "
+                         "datacl, NULL AS acldefault, "
+                         "datistemplate, datconnlimit, "
                          "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
                          "shobj_description(oid, 'pg_database') AS description "
 
@@ -2959,7 +2936,8 @@ dumpDatabase(Archive *fout)
                          "(%s datdba) AS dba, "
                          "pg_encoding_to_char(encoding) AS encoding, "
                          "NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, "
-                         "datacl, '' as rdatacl, datistemplate, datconnlimit, "
+                         "datacl, NULL AS acldefault, "
+                         "datistemplate, datconnlimit, "
                          "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
                          "shobj_description(oid, 'pg_database') AS description "
 
@@ -2973,8 +2951,8 @@ dumpDatabase(Archive *fout)
                          "(%s datdba) AS dba, "
                          "pg_encoding_to_char(encoding) AS encoding, "
                          "NULL AS datcollate, NULL AS datctype, datfrozenxid, 0 AS datminmxid, "
-                         "datacl, '' as rdatacl, datistemplate, "
-                         "-1 as datconnlimit, "
+                         "datacl, NULL AS acldefault, "
+                         "datistemplate, -1 AS datconnlimit, "
                          "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace "
                          "FROM pg_database "
                          "WHERE datname = current_database()",
@@ -2993,7 +2971,7 @@ dumpDatabase(Archive *fout)
    i_frozenxid = PQfnumber(res, "datfrozenxid");
    i_minmxid = PQfnumber(res, "datminmxid");
    i_datacl = PQfnumber(res, "datacl");
-   i_rdatacl = PQfnumber(res, "rdatacl");
+   i_acldefault = PQfnumber(res, "acldefault");
    i_datistemplate = PQfnumber(res, "datistemplate");
    i_datconnlimit = PQfnumber(res, "datconnlimit");
    i_tablespace = PQfnumber(res, "tablespace");
@@ -3007,8 +2985,8 @@ dumpDatabase(Archive *fout)
    ctype = PQgetvalue(res, 0, i_ctype);
    frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
    minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
-   datacl = PQgetvalue(res, 0, i_datacl);
-   rdatacl = PQgetvalue(res, 0, i_rdatacl);
+   dbdacl.acl = PQgetvalue(res, 0, i_datacl);
+   dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
    datistemplate = PQgetvalue(res, 0, i_datistemplate);
    datconnlimit = PQgetvalue(res, 0, i_datconnlimit);
    tablespace = PQgetvalue(res, 0, i_tablespace);
@@ -3146,9 +3124,12 @@ dumpDatabase(Archive *fout)
     * Dump ACL if any.  Note that we do not support initial privileges
     * (pg_init_privs) on databases.
     */
+   dbdacl.privtype = 0;
+   dbdacl.initprivs = NULL;
+
    dumpACL(fout, dbDumpId, InvalidDumpId, "DATABASE",
            qdatname, NULL, NULL,
-           dba, datacl, rdatacl, "", "");
+           dba, &dbdacl);
 
    /*
     * Now construct a DATABASE PROPERTIES archive entry to restore any
@@ -3470,59 +3451,30 @@ getBlobs(Archive *fout)
    int         i_oid;
    int         i_lomowner;
    int         i_lomacl;
-   int         i_rlomacl;
-   int         i_initlomacl;
-   int         i_initrlomacl;
+   int         i_acldefault;
 
    pg_log_info("reading large objects");
 
    /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */
-   if (fout->remoteVersion >= 90600)
+   if (fout->remoteVersion >= 90200)
    {
-       PQExpBuffer acl_subquery = createPQExpBuffer();
-       PQExpBuffer racl_subquery = createPQExpBuffer();
-       PQExpBuffer init_acl_subquery = createPQExpBuffer();
-       PQExpBuffer init_racl_subquery = createPQExpBuffer();
-
-       buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
-                       init_racl_subquery, "l.lomacl", "l.lomowner",
-                       "pip.initprivs", "'L'", dopt->binary_upgrade);
-
        appendPQExpBuffer(blobQry,
-                         "SELECT l.oid, (%s l.lomowner) AS rolname, "
-                         "%s AS lomacl, "
-                         "%s AS rlomacl, "
-                         "%s AS initlomacl, "
-                         "%s AS initrlomacl "
-                         "FROM pg_largeobject_metadata l "
-                         "LEFT JOIN pg_init_privs pip ON "
-                         "(l.oid = pip.objoid "
-                         "AND pip.classoid = 'pg_largeobject'::regclass "
-                         "AND pip.objsubid = 0) ",
-                         username_subquery,
-                         acl_subquery->data,
-                         racl_subquery->data,
-                         init_acl_subquery->data,
-                         init_racl_subquery->data);
-
-       destroyPQExpBuffer(acl_subquery);
-       destroyPQExpBuffer(racl_subquery);
-       destroyPQExpBuffer(init_acl_subquery);
-       destroyPQExpBuffer(init_racl_subquery);
+                         "SELECT oid, (%s lomowner) AS rolname, lomacl, "
+                         "acldefault('L', lomowner) AS acldefault "
+                         "FROM pg_largeobject_metadata",
+                         username_subquery);
    }
    else if (fout->remoteVersion >= 90000)
        appendPQExpBuffer(blobQry,
                          "SELECT oid, (%s lomowner) AS rolname, lomacl, "
-                         "NULL AS rlomacl, NULL AS initlomacl, "
-                         "NULL AS initrlomacl "
-                         " FROM pg_largeobject_metadata",
+                         "NULL AS acldefault "
+                         "FROM pg_largeobject_metadata",
                          username_subquery);
    else
        appendPQExpBufferStr(blobQry,
                             "SELECT DISTINCT loid AS oid, "
                             "NULL::name AS rolname, NULL::oid AS lomacl, "
-                            "NULL::oid AS rlomacl, NULL::oid AS initlomacl, "
-                            "NULL::oid AS initrlomacl "
+                            "NULL::oid AS acldefault "
                             " FROM pg_largeobject");
 
    res = ExecuteSqlQuery(fout, blobQry->data, PGRES_TUPLES_OK);
@@ -3530,9 +3482,7 @@ getBlobs(Archive *fout)
    i_oid = PQfnumber(res, "oid");
    i_lomowner = PQfnumber(res, "rolname");
    i_lomacl = PQfnumber(res, "lomacl");
-   i_rlomacl = PQfnumber(res, "rlomacl");
-   i_initlomacl = PQfnumber(res, "initlomacl");
-   i_initrlomacl = PQfnumber(res, "initrlomacl");
+   i_acldefault = PQfnumber(res, "acldefault");
 
    ntups = PQntuples(res);
 
@@ -3549,20 +3499,17 @@ getBlobs(Archive *fout)
        AssignDumpId(&binfo[i].dobj);
 
        binfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_oid));
+       binfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lomacl));
+       binfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+       binfo[i].dacl.privtype = 0;
+       binfo[i].dacl.initprivs = NULL;
        binfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_lomowner));
-       binfo[i].blobacl = pg_strdup(PQgetvalue(res, i, i_lomacl));
-       binfo[i].rblobacl = pg_strdup(PQgetvalue(res, i, i_rlomacl));
-       binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl));
-       binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl));
 
        /* Blobs have data */
        binfo[i].dobj.components |= DUMP_COMPONENT_DATA;
 
        /* Mark whether blob has an ACL */
-       if (!(PQgetisnull(res, i, i_lomacl) &&
-             PQgetisnull(res, i, i_rlomacl) &&
-             PQgetisnull(res, i, i_initlomacl) &&
-             PQgetisnull(res, i, i_initrlomacl)))
+       if (!PQgetisnull(res, i, i_lomacl))
            binfo[i].dobj.components |= DUMP_COMPONENT_ACL;
 
        /*
@@ -3638,8 +3585,7 @@ dumpBlob(Archive *fout, const BlobInfo *binfo)
    if (binfo->dobj.dump & DUMP_COMPONENT_ACL)
        dumpACL(fout, binfo->dobj.dumpId, InvalidDumpId, "LARGE OBJECT",
                binfo->dobj.name, NULL,
-               NULL, binfo->rolname, binfo->blobacl, binfo->rblobacl,
-               binfo->initblobacl, binfo->initrblobacl);
+               NULL, binfo->rolname, &binfo->dacl);
 
    destroyPQExpBuffer(cquery);
    destroyPQExpBuffer(dquery);
@@ -4999,7 +4945,6 @@ binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
 NamespaceInfo *
 getNamespaces(Archive *fout, int *numNamespaces)
 {
-   DumpOptions *dopt = fout->dopt;
    PGresult   *res;
    int         ntups;
    int         i;
@@ -5011,9 +4956,7 @@ getNamespaces(Archive *fout, int *numNamespaces)
    int         i_nspowner;
    int         i_rolname;
    int         i_nspacl;
-   int         i_rnspacl;
-   int         i_initnspacl;
-   int         i_initrnspacl;
+   int         i_acldefault;
 
    query = createPQExpBuffer();
 
@@ -5021,67 +4964,18 @@ getNamespaces(Archive *fout, int *numNamespaces)
     * we fetch all namespaces including system ones, so that every object we
     * read in can be linked to a containing namespace.
     */
-   if (fout->remoteVersion >= 90600)
-   {
-       PQExpBuffer acl_subquery = createPQExpBuffer();
-       PQExpBuffer racl_subquery = createPQExpBuffer();
-       PQExpBuffer init_acl_subquery = createPQExpBuffer();
-       PQExpBuffer init_racl_subquery = createPQExpBuffer();
-
-       /*
-        * Bypass pg_init_privs.initprivs for the public schema, for several
-        * reasons.  First, dropping and recreating the schema detaches it
-        * from its pg_init_privs row, but an empty destination database
-        * starts with this ACL nonetheless.  Second, we support dump/reload
-        * of public schema ownership changes.  ALTER SCHEMA OWNER filters
-        * nspacl through aclnewowner(), but initprivs continues to reflect
-        * the initial owner.  Hence, synthesize the value that nspacl will
-        * have after the restore's ALTER SCHEMA OWNER.  Third, this makes the
-        * destination database match the source's ACL, even if the latter was
-        * an initdb-default ACL, which changed in v15.  An upgrade pulls in
-        * changes to most system object ACLs that the DBA had not customized.
-        * We've made the public schema depart from that, because changing its
-        * ACL so easily breaks applications.
-        */
-       buildACLQueries(acl_subquery, racl_subquery, init_acl_subquery,
-                       init_racl_subquery, "n.nspacl", "n.nspowner",
-                       "CASE WHEN n.nspname = 'public' THEN array["
-                       "  format('%s=UC/%s', "
-                       "         n.nspowner::regrole, n.nspowner::regrole),"
-                       "  format('=U/%s', n.nspowner::regrole)]::aclitem[] "
-                       "ELSE pip.initprivs END",
-                       "'n'", dopt->binary_upgrade);
-
+   if (fout->remoteVersion >= 90200)
        appendPQExpBuffer(query, "SELECT n.tableoid, n.oid, n.nspname, "
                          "n.nspowner, "
                          "(%s nspowner) AS rolname, "
-                         "%s as nspacl, "
-                         "%s as rnspacl, "
-                         "%s as initnspacl, "
-                         "%s as initrnspacl "
-                         "FROM pg_namespace n "
-                         "LEFT JOIN pg_init_privs pip "
-                         "ON (n.oid = pip.objoid "
-                         "AND pip.classoid = 'pg_namespace'::regclass "
-                         "AND pip.objsubid = 0",
-                         username_subquery,
-                         acl_subquery->data,
-                         racl_subquery->data,
-                         init_acl_subquery->data,
-                         init_racl_subquery->data);
-
-       appendPQExpBufferStr(query, ") ");
-
-       destroyPQExpBuffer(acl_subquery);
-       destroyPQExpBuffer(racl_subquery);
-       destroyPQExpBuffer(init_acl_subquery);
-       destroyPQExpBuffer(init_racl_subquery);
-   }
+                         "n.nspacl, "
+                         "acldefault('n', n.nspowner) AS acldefault "
+                         "FROM pg_namespace n",
+                         username_subquery);
    else
        appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, nspowner, "
                          "(%s nspowner) AS rolname, "
-                         "nspacl, NULL as rnspacl, "
-                         "NULL AS initnspacl, NULL as initrnspacl "
+                         "nspacl, NULL AS acldefault "
                          "FROM pg_namespace",
                          username_subquery);
 
@@ -5097,9 +4991,7 @@ getNamespaces(Archive *fout, int *numNamespaces)
    i_nspowner = PQfnumber(res, "nspowner");
    i_rolname = PQfnumber(res, "rolname");
    i_nspacl = PQfnumber(res, "nspacl");
-   i_rnspacl = PQfnumber(res, "rnspacl");
-   i_initnspacl = PQfnumber(res, "initnspacl");
-   i_initrnspacl = PQfnumber(res, "initrnspacl");
+   i_acldefault = PQfnumber(res, "acldefault");
 
    for (i = 0; i < ntups; i++)
    {
@@ -5108,23 +5000,61 @@ getNamespaces(Archive *fout, int *numNamespaces)
        nsinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
        AssignDumpId(&nsinfo[i].dobj);
        nsinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_nspname));
+       nsinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_nspacl));
+       nsinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+       nsinfo[i].dacl.privtype = 0;
+       nsinfo[i].dacl.initprivs = NULL;
        nsinfo[i].nspowner = atooid(PQgetvalue(res, i, i_nspowner));
        nsinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
-       nsinfo[i].nspacl = pg_strdup(PQgetvalue(res, i, i_nspacl));
-       nsinfo[i].rnspacl = pg_strdup(PQgetvalue(res, i, i_rnspacl));
-       nsinfo[i].initnspacl = pg_strdup(PQgetvalue(res, i, i_initnspacl));
-       nsinfo[i].initrnspacl = pg_strdup(PQgetvalue(res, i, i_initrnspacl));
 
        /* Decide whether to dump this namespace */
        selectDumpableNamespace(&nsinfo[i], fout);
 
        /* Mark whether namespace has an ACL */
-       if (!(PQgetisnull(res, i, i_nspacl) &&
-             PQgetisnull(res, i, i_rnspacl) &&
-             PQgetisnull(res, i, i_initnspacl) &&
-             PQgetisnull(res, i, i_initrnspacl)))
+       if (!PQgetisnull(res, i, i_nspacl))
+           nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
+
+       /*
+        * We ignore any pg_init_privs.initprivs entry for the public schema
+        * and assume a predetermined default, for several reasons.  First,
+        * dropping and recreating the schema removes its pg_init_privs entry,
+        * but an empty destination database starts with this ACL nonetheless.
+        * Second, we support dump/reload of public schema ownership changes.
+        * ALTER SCHEMA OWNER filters nspacl through aclnewowner(), but
+        * initprivs continues to reflect the initial owner.  Hence,
+        * synthesize the value that nspacl will have after the restore's
+        * ALTER SCHEMA OWNER.  Third, this makes the destination database
+        * match the source's ACL, even if the latter was an initdb-default
+        * ACL, which changed in v15.  An upgrade pulls in changes to most
+        * system object ACLs that the DBA had not customized.  We've made the
+        * public schema depart from that, because changing its ACL so easily
+        * breaks applications.
+        */
+       if (strcmp(nsinfo[i].dobj.name, "public") == 0)
+       {
+           PQExpBuffer aclarray = createPQExpBuffer();
+           PQExpBuffer aclitem = createPQExpBuffer();
+
+           /* Standard ACL as of v15 is {owner=UC/owner,=U/owner} */
+           appendPQExpBufferChar(aclarray, '{');
+           quoteAclUserName(aclitem, nsinfo[i].rolname);
+           appendPQExpBufferStr(aclitem, "=UC/");
+           quoteAclUserName(aclitem, nsinfo[i].rolname);
+           appendPGArray(aclarray, aclitem->data);
+           resetPQExpBuffer(aclitem);
+           appendPQExpBufferStr(aclitem, "=U/");
+           quoteAclUserName(aclitem, nsinfo[i].rolname);
+           appendPGArray(aclarray, aclitem->data);
+           appendPQExpBufferChar(aclarray, '}');
+
+           nsinfo[i].dacl.privtype = 'i';
+           nsinfo[i].dacl.initprivs = pstrdup(aclarray->data);
            nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
 
+           destroyPQExpBuffer(aclarray);
+           destroyPQExpBuffer(aclitem);
+       }
+
        if (strlen(nsinfo[i].rolname) == 0)
            pg_log_warning("owner of schema \"%s\" appears to be invalid",
                           nsinfo[i].dobj.name);
@@ -5247,7 +5177,6 @@ getExtensions(Archive *fout, int *numExtensions)
 TypeInfo *
 getTypes(Archive *fout, int *numTypes)
 {
-   DumpOptions *dopt = fout->dopt;
    PGresult   *res;
    int         ntups;
    int         i;
@@ -5259,9 +5188,7 @@ getTypes(Archive *fout, int *numTypes)
    int         i_typname;
    int         i_typnamespace;
    int         i_typacl;
-   int         i_rtypacl;
-   int         i_inittypacl;
-   int         i_initrtypacl;
+   int         i_acldefault;
    int         i_rolname;
    int         i_typelem;
    int         i_typrelid;
@@ -5285,52 +5212,11 @@ getTypes(Archive *fout, int *numTypes)
     * cost of the subselect probe for all standard types.  This would have to
     * be revisited if the backend ever allows renaming of array types.
     */
-
-   if (fout->remoteVersion >= 90600)
-   {
-       PQExpBuffer acl_subquery = createPQExpBuffer();
-       PQExpBuffer racl_subquery = createPQExpBuffer();
-       PQExpBuffer initacl_subquery = createPQExpBuffer();
-       PQExpBuffer initracl_subquery = createPQExpBuffer();
-
-       buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
-                       initracl_subquery, "t.typacl", "t.typowner",
-                       "pip.initprivs", "'T'", dopt->binary_upgrade);
-
-       appendPQExpBuffer(query, "SELECT t.tableoid, t.oid, t.typname, "
-                         "t.typnamespace, "
-                         "%s AS typacl, "
-                         "%s AS rtypacl, "
-                         "%s AS inittypacl, "
-                         "%s AS initrtypacl, "
-                         "(%s t.typowner) AS rolname, "
-                         "t.typelem, t.typrelid, "
-                         "CASE WHEN t.typrelid = 0 THEN ' '::\"char\" "
-                         "ELSE (SELECT relkind FROM pg_class WHERE oid = t.typrelid) END AS typrelkind, "
-                         "t.typtype, t.typisdefined, "
-                         "t.typname[0] = '_' AND t.typelem != 0 AND "
-                         "(SELECT typarray FROM pg_type te WHERE oid = t.typelem) = t.oid AS isarray "
-                         "FROM pg_type t "
-                         "LEFT JOIN pg_init_privs pip ON "
-                         "(t.oid = pip.objoid "
-                         "AND pip.classoid = 'pg_type'::regclass "
-                         "AND pip.objsubid = 0) ",
-                         acl_subquery->data,
-                         racl_subquery->data,
-                         initacl_subquery->data,
-                         initracl_subquery->data,
-                         username_subquery);
-
-       destroyPQExpBuffer(acl_subquery);
-       destroyPQExpBuffer(racl_subquery);
-       destroyPQExpBuffer(initacl_subquery);
-       destroyPQExpBuffer(initracl_subquery);
-   }
-   else if (fout->remoteVersion >= 90200)
+   if (fout->remoteVersion >= 90200)
    {
        appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
-                         "typnamespace, typacl, NULL as rtypacl, "
-                         "NULL AS inittypacl, NULL AS initrtypacl, "
+                         "typnamespace, typacl, "
+                         "acldefault('T', typowner) AS acldefault, "
                          "(%s typowner) AS rolname, "
                          "typelem, typrelid, "
                          "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
@@ -5344,8 +5230,7 @@ getTypes(Archive *fout, int *numTypes)
    else if (fout->remoteVersion >= 80300)
    {
        appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
-                         "typnamespace, NULL AS typacl, NULL as rtypacl, "
-                         "NULL AS inittypacl, NULL AS initrtypacl, "
+                         "typnamespace, NULL AS typacl, NULL AS acldefault, "
                          "(%s typowner) AS rolname, "
                          "typelem, typrelid, "
                          "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
@@ -5359,8 +5244,7 @@ getTypes(Archive *fout, int *numTypes)
    else
    {
        appendPQExpBuffer(query, "SELECT tableoid, oid, typname, "
-                         "typnamespace, NULL AS typacl, NULL as rtypacl, "
-                         "NULL AS inittypacl, NULL AS initrtypacl, "
+                         "typnamespace, NULL AS typacl, NULL AS acldefault, "
                          "(%s typowner) AS rolname, "
                          "typelem, typrelid, "
                          "CASE WHEN typrelid = 0 THEN ' '::\"char\" "
@@ -5382,9 +5266,7 @@ getTypes(Archive *fout, int *numTypes)
    i_typname = PQfnumber(res, "typname");
    i_typnamespace = PQfnumber(res, "typnamespace");
    i_typacl = PQfnumber(res, "typacl");
-   i_rtypacl = PQfnumber(res, "rtypacl");
-   i_inittypacl = PQfnumber(res, "inittypacl");
-   i_initrtypacl = PQfnumber(res, "initrtypacl");
+   i_acldefault = PQfnumber(res, "acldefault");
    i_rolname = PQfnumber(res, "rolname");
    i_typelem = PQfnumber(res, "typelem");
    i_typrelid = PQfnumber(res, "typrelid");
@@ -5402,12 +5284,12 @@ getTypes(Archive *fout, int *numTypes)
        tyinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_typname));
        tyinfo[i].dobj.namespace =
            findNamespace(atooid(PQgetvalue(res, i, i_typnamespace)));
+       tyinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_typacl));
+       tyinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+       tyinfo[i].dacl.privtype = 0;
+       tyinfo[i].dacl.initprivs = NULL;
        tyinfo[i].ftypname = NULL;  /* may get filled later */
        tyinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
-       tyinfo[i].typacl = pg_strdup(PQgetvalue(res, i, i_typacl));
-       tyinfo[i].rtypacl = pg_strdup(PQgetvalue(res, i, i_rtypacl));
-       tyinfo[i].inittypacl = pg_strdup(PQgetvalue(res, i, i_inittypacl));
-       tyinfo[i].initrtypacl = pg_strdup(PQgetvalue(res, i, i_initrtypacl));
        tyinfo[i].typelem = atooid(PQgetvalue(res, i, i_typelem));
        tyinfo[i].typrelid = atooid(PQgetvalue(res, i, i_typrelid));
        tyinfo[i].typrelkind = *PQgetvalue(res, i, i_typrelkind);
@@ -5433,10 +5315,7 @@ getTypes(Archive *fout, int *numTypes)
        selectDumpableType(&tyinfo[i], fout);
 
        /* Mark whether type has an ACL */
-       if (!(PQgetisnull(res, i, i_typacl) &&
-             PQgetisnull(res, i, i_rtypacl) &&
-             PQgetisnull(res, i, i_inittypacl) &&
-             PQgetisnull(res, i, i_initrtypacl)))
+       if (!PQgetisnull(res, i, i_typacl))
            tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
 
        /*
@@ -5963,9 +5842,7 @@ getAggregates(Archive *fout, int *numAggs)
    int         i_proargtypes;
    int         i_rolname;
    int         i_aggacl;
-   int         i_raggacl;
-   int         i_initaggacl;
-   int         i_initraggacl;
+   int         i_acldefault;
 
    /*
     * Find all interesting aggregates.  See comment in getFuncs() for the
@@ -5973,16 +5850,8 @@ getAggregates(Archive *fout, int *numAggs)
     */
    if (fout->remoteVersion >= 90600)
    {
-       PQExpBuffer acl_subquery = createPQExpBuffer();
-       PQExpBuffer racl_subquery = createPQExpBuffer();
-       PQExpBuffer initacl_subquery = createPQExpBuffer();
-       PQExpBuffer initracl_subquery = createPQExpBuffer();
        const char *agg_check;
 
-       buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
-                       initracl_subquery, "p.proacl", "p.proowner",
-                       "pip.initprivs", "'f'", dopt->binary_upgrade);
-
        agg_check = (fout->remoteVersion >= 110000 ? "p.prokind = 'a'"
                     : "p.proisagg");
 
@@ -5991,10 +5860,8 @@ getAggregates(Archive *fout, int *numAggs)
                          "p.pronamespace AS aggnamespace, "
                          "p.pronargs, p.proargtypes, "
                          "(%s p.proowner) AS rolname, "
-                         "%s AS aggacl, "
-                         "%s AS raggacl, "
-                         "%s AS initaggacl, "
-                         "%s AS initraggacl "
+                         "p.proacl AS aggacl, "
+                         "acldefault('f', p.proowner) AS acldefault "
                          "FROM pg_proc p "
                          "LEFT JOIN pg_init_privs pip ON "
                          "(p.oid = pip.objoid "
@@ -6006,10 +5873,6 @@ getAggregates(Archive *fout, int *numAggs)
                          "WHERE nspname = 'pg_catalog') OR "
                          "p.proacl IS DISTINCT FROM pip.initprivs",
                          username_subquery,
-                         acl_subquery->data,
-                         racl_subquery->data,
-                         initacl_subquery->data,
-                         initracl_subquery->data,
                          agg_check);
        if (dopt->binary_upgrade)
            appendPQExpBufferStr(query,
@@ -6019,11 +5882,29 @@ getAggregates(Archive *fout, int *numAggs)
                                 "refclassid = 'pg_extension'::regclass AND "
                                 "deptype = 'e')");
        appendPQExpBufferChar(query, ')');
-
-       destroyPQExpBuffer(acl_subquery);
-       destroyPQExpBuffer(racl_subquery);
-       destroyPQExpBuffer(initacl_subquery);
-       destroyPQExpBuffer(initracl_subquery);
+   }
+   else if (fout->remoteVersion >= 90200)
+   {
+       appendPQExpBuffer(query, "SELECT tableoid, oid, proname AS aggname, "
+                         "pronamespace AS aggnamespace, "
+                         "pronargs, proargtypes, "
+                         "(%s proowner) AS rolname, "
+                         "proacl AS aggacl, "
+                         "acldefault('f', proowner) AS acldefault "
+                         "FROM pg_proc p "
+                         "WHERE proisagg AND ("
+                         "pronamespace != "
+                         "(SELECT oid FROM pg_namespace "
+                         "WHERE nspname = 'pg_catalog')",
+                         username_subquery);
+       if (dopt->binary_upgrade)
+           appendPQExpBufferStr(query,
+                                " OR EXISTS(SELECT 1 FROM pg_depend WHERE "
+                                "classid = 'pg_proc'::regclass AND "
+                                "objid = p.oid AND "
+                                "refclassid = 'pg_extension'::regclass AND "
+                                "deptype = 'e')");
+       appendPQExpBufferChar(query, ')');
    }
    else if (fout->remoteVersion >= 80200)
    {
@@ -6032,8 +5913,7 @@ getAggregates(Archive *fout, int *numAggs)
                          "pronargs, proargtypes, "
                          "(%s proowner) AS rolname, "
                          "proacl AS aggacl, "
-                         "NULL AS raggacl, "
-                         "NULL AS initaggacl, NULL AS initraggacl "
+                         "NULL AS acldefault "
                          "FROM pg_proc p "
                          "WHERE proisagg AND ("
                          "pronamespace != "
@@ -6057,8 +5937,7 @@ getAggregates(Archive *fout, int *numAggs)
                          "proargtypes, "
                          "(%s proowner) AS rolname, "
                          "proacl AS aggacl, "
-                         "NULL AS raggacl, "
-                         "NULL AS initaggacl, NULL AS initraggacl "
+                         "NULL AS acldefault "
                          "FROM pg_proc "
                          "WHERE proisagg "
                          "AND pronamespace != "
@@ -6081,9 +5960,7 @@ getAggregates(Archive *fout, int *numAggs)
    i_proargtypes = PQfnumber(res, "proargtypes");
    i_rolname = PQfnumber(res, "rolname");
    i_aggacl = PQfnumber(res, "aggacl");
-   i_raggacl = PQfnumber(res, "raggacl");
-   i_initaggacl = PQfnumber(res, "initaggacl");
-   i_initraggacl = PQfnumber(res, "initraggacl");
+   i_acldefault = PQfnumber(res, "acldefault");
 
    for (i = 0; i < ntups; i++)
    {
@@ -6094,16 +5971,16 @@ getAggregates(Archive *fout, int *numAggs)
        agginfo[i].aggfn.dobj.name = pg_strdup(PQgetvalue(res, i, i_aggname));
        agginfo[i].aggfn.dobj.namespace =
            findNamespace(atooid(PQgetvalue(res, i, i_aggnamespace)));
+       agginfo[i].aggfn.dacl.acl = pg_strdup(PQgetvalue(res, i, i_aggacl));
+       agginfo[i].aggfn.dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+       agginfo[i].aggfn.dacl.privtype = 0;
+       agginfo[i].aggfn.dacl.initprivs = NULL;
        agginfo[i].aggfn.rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
        if (strlen(agginfo[i].aggfn.rolname) == 0)
            pg_log_warning("owner of aggregate function \"%s\" appears to be invalid",
                           agginfo[i].aggfn.dobj.name);
        agginfo[i].aggfn.lang = InvalidOid; /* not currently interesting */
        agginfo[i].aggfn.prorettype = InvalidOid;   /* not saved */
-       agginfo[i].aggfn.proacl = pg_strdup(PQgetvalue(res, i, i_aggacl));
-       agginfo[i].aggfn.rproacl = pg_strdup(PQgetvalue(res, i, i_raggacl));
-       agginfo[i].aggfn.initproacl = pg_strdup(PQgetvalue(res, i, i_initaggacl));
-       agginfo[i].aggfn.initrproacl = pg_strdup(PQgetvalue(res, i, i_initraggacl));
        agginfo[i].aggfn.nargs = atoi(PQgetvalue(res, i, i_pronargs));
        if (agginfo[i].aggfn.nargs == 0)
            agginfo[i].aggfn.argtypes = NULL;
@@ -6119,10 +5996,7 @@ getAggregates(Archive *fout, int *numAggs)
        selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);
 
        /* Mark whether aggregate has an ACL */
-       if (!(PQgetisnull(res, i, i_aggacl) &&
-             PQgetisnull(res, i, i_raggacl) &&
-             PQgetisnull(res, i, i_initaggacl) &&
-             PQgetisnull(res, i, i_initraggacl)))
+       if (!PQgetisnull(res, i, i_aggacl))
            agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
    }
 
@@ -6159,9 +6033,7 @@ getFuncs(Archive *fout, int *numFuncs)
    int         i_proargtypes;
    int         i_prorettype;
    int         i_proacl;
-   int         i_rproacl;
-   int         i_initproacl;
-   int         i_initrproacl;
+   int         i_acldefault;
 
    /*
     * Find all interesting functions.  This is a bit complicated:
@@ -6183,30 +6055,20 @@ getFuncs(Archive *fout, int *numFuncs)
     * to gather the information about them, though they won't be dumped if
     * they are built-in.  Also, in 9.6 and up, include functions in
     * pg_catalog if they have an ACL different from what's shown in
-    * pg_init_privs.
+    * pg_init_privs (so we have to join to pg_init_privs; annoying).
     */
    if (fout->remoteVersion >= 90600)
    {
-       PQExpBuffer acl_subquery = createPQExpBuffer();
-       PQExpBuffer racl_subquery = createPQExpBuffer();
-       PQExpBuffer initacl_subquery = createPQExpBuffer();
-       PQExpBuffer initracl_subquery = createPQExpBuffer();
        const char *not_agg_check;
 
-       buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
-                       initracl_subquery, "p.proacl", "p.proowner",
-                       "pip.initprivs", "'f'", dopt->binary_upgrade);
-
        not_agg_check = (fout->remoteVersion >= 110000 ? "p.prokind <> 'a'"
                         : "NOT p.proisagg");
 
        appendPQExpBuffer(query,
                          "SELECT p.tableoid, p.oid, p.proname, p.prolang, "
                          "p.pronargs, p.proargtypes, p.prorettype, "
-                         "%s AS proacl, "
-                         "%s AS rproacl, "
-                         "%s AS initproacl, "
-                         "%s AS initrproacl, "
+                         "p.proacl, "
+                         "acldefault('f', p.proowner) AS acldefault, "
                          "p.pronamespace, "
                          "(%s p.proowner) AS rolname "
                          "FROM pg_proc p "
@@ -6229,10 +6091,6 @@ getFuncs(Archive *fout, int *numFuncs)
                          "\n  WHERE pg_transform.oid > %u AND "
                          "\n  (p.oid = pg_transform.trffromsql"
                          "\n  OR p.oid = pg_transform.trftosql))",
-                         acl_subquery->data,
-                         racl_subquery->data,
-                         initacl_subquery->data,
-                         initracl_subquery->data,
                          username_subquery,
                          not_agg_check,
                          g_last_builtin_oid,
@@ -6247,23 +6105,23 @@ getFuncs(Archive *fout, int *numFuncs)
        appendPQExpBufferStr(query,
                             "\n  OR p.proacl IS DISTINCT FROM pip.initprivs");
        appendPQExpBufferChar(query, ')');
-
-       destroyPQExpBuffer(acl_subquery);
-       destroyPQExpBuffer(racl_subquery);
-       destroyPQExpBuffer(initacl_subquery);
-       destroyPQExpBuffer(initracl_subquery);
    }
    else
    {
+       const char *acldefault_call;
+
+       acldefault_call = (fout->remoteVersion >= 90200 ?
+                          "acldefault('f', proowner)" : "NULL");
+
        appendPQExpBuffer(query,
                          "SELECT tableoid, oid, proname, prolang, "
                          "pronargs, proargtypes, prorettype, proacl, "
-                         "NULL as rproacl, "
-                         "NULL as initproacl, NULL AS initrproacl, "
+                         "%s AS acldefault, "
                          "pronamespace, "
                          "(%s proowner) AS rolname "
                          "FROM pg_proc p "
                          "WHERE NOT proisagg",
+                         acldefault_call,
                          username_subquery);
        if (fout->remoteVersion >= 90200)
            appendPQExpBufferStr(query,
@@ -6316,9 +6174,7 @@ getFuncs(Archive *fout, int *numFuncs)
    i_proargtypes = PQfnumber(res, "proargtypes");
    i_prorettype = PQfnumber(res, "prorettype");
    i_proacl = PQfnumber(res, "proacl");
-   i_rproacl = PQfnumber(res, "rproacl");
-   i_initproacl = PQfnumber(res, "initproacl");
-   i_initrproacl = PQfnumber(res, "initrproacl");
+   i_acldefault = PQfnumber(res, "acldefault");
 
    for (i = 0; i < ntups; i++)
    {
@@ -6329,13 +6185,13 @@ getFuncs(Archive *fout, int *numFuncs)
        finfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_proname));
        finfo[i].dobj.namespace =
            findNamespace(atooid(PQgetvalue(res, i, i_pronamespace)));
+       finfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_proacl));
+       finfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+       finfo[i].dacl.privtype = 0;
+       finfo[i].dacl.initprivs = NULL;
        finfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
        finfo[i].lang = atooid(PQgetvalue(res, i, i_prolang));
        finfo[i].prorettype = atooid(PQgetvalue(res, i, i_prorettype));
-       finfo[i].proacl = pg_strdup(PQgetvalue(res, i, i_proacl));
-       finfo[i].rproacl = pg_strdup(PQgetvalue(res, i, i_rproacl));
-       finfo[i].initproacl = pg_strdup(PQgetvalue(res, i, i_initproacl));
-       finfo[i].initrproacl = pg_strdup(PQgetvalue(res, i, i_initrproacl));
        finfo[i].nargs = atoi(PQgetvalue(res, i, i_pronargs));
        if (finfo[i].nargs == 0)
            finfo[i].argtypes = NULL;
@@ -6350,10 +6206,7 @@ getFuncs(Archive *fout, int *numFuncs)
        selectDumpableObject(&(finfo[i].dobj), fout);
 
        /* Mark whether function has an ACL */
-       if (!(PQgetisnull(res, i, i_proacl) &&
-             PQgetisnull(res, i, i_rproacl) &&
-             PQgetisnull(res, i, i_initproacl) &&
-             PQgetisnull(res, i, i_initrproacl)))
+       if (!PQgetisnull(res, i, i_proacl))
            finfo[i].dobj.components |= DUMP_COMPONENT_ACL;
 
        if (strlen(finfo[i].rolname) == 0)
@@ -6418,10 +6271,7 @@ getTables(Archive *fout, int *numTables)
    int         i_amname;
    int         i_is_identity_sequence;
    int         i_relacl;
-   int         i_rrelacl;
-   int         i_initrelacl;
-   int         i_initrrelacl;
-   int         i_changed_acl;
+   int         i_acldefault;
    int         i_partkeydef;
    int         i_ispartition;
    int         i_partbound;
@@ -6562,67 +6412,14 @@ getTables(Archive *fout, int *numTables)
        appendPQExpBufferStr(query,
                             "false AS is_identity_sequence, ");
 
-   if (fout->remoteVersion >= 90600)
-   {
-       PQExpBuffer acl_subquery = createPQExpBuffer();
-       PQExpBuffer racl_subquery = createPQExpBuffer();
-       PQExpBuffer initacl_subquery = createPQExpBuffer();
-       PQExpBuffer initracl_subquery = createPQExpBuffer();
-       PQExpBuffer attacl_subquery = createPQExpBuffer();
-       PQExpBuffer attracl_subquery = createPQExpBuffer();
-       PQExpBuffer attinitacl_subquery = createPQExpBuffer();
-       PQExpBuffer attinitracl_subquery = createPQExpBuffer();
-
-       buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
-                       initracl_subquery, "c.relacl", "c.relowner",
-                       "pip.initprivs",
-                       "CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
-                       " THEN 's' ELSE 'r' END::\"char\"",
-                       dopt->binary_upgrade);
-
-       buildACLQueries(attacl_subquery, attracl_subquery, attinitacl_subquery,
-                       attinitracl_subquery, "at.attacl", "c.relowner",
-                       "pip.initprivs", "'c'", dopt->binary_upgrade);
-
-       appendPQExpBuffer(query,
-                         "%s AS relacl, %s as rrelacl, "
-                         "%s AS initrelacl, %s as initrrelacl, ",
-                         acl_subquery->data,
-                         racl_subquery->data,
-                         initacl_subquery->data,
-                         initracl_subquery->data);
-
-       appendPQExpBuffer(query,
-                         "EXISTS (SELECT 1 FROM pg_attribute at LEFT JOIN pg_init_privs pip ON "
-                         "(c.oid = pip.objoid "
-                         "AND pip.classoid = 'pg_class'::regclass "
-                         "AND pip.objsubid = at.attnum)"
-                         "WHERE at.attrelid = c.oid AND ("
-                         "%s IS NOT NULL "
-                         "OR %s IS NOT NULL "
-                         "OR %s IS NOT NULL "
-                         "OR %s IS NOT NULL"
-                         "))"
-                         "AS changed_acl, ",
-                         attacl_subquery->data,
-                         attracl_subquery->data,
-                         attinitacl_subquery->data,
-                         attinitracl_subquery->data);
-
-       destroyPQExpBuffer(acl_subquery);
-       destroyPQExpBuffer(racl_subquery);
-       destroyPQExpBuffer(initacl_subquery);
-       destroyPQExpBuffer(initracl_subquery);
-       destroyPQExpBuffer(attacl_subquery);
-       destroyPQExpBuffer(attracl_subquery);
-       destroyPQExpBuffer(attinitacl_subquery);
-       destroyPQExpBuffer(attinitracl_subquery);
-   }
+   if (fout->remoteVersion >= 90200)
+       appendPQExpBufferStr(query,
+                            "c.relacl, "
+                            "acldefault(CASE WHEN c.relkind = " CppAsString2(RELKIND_SEQUENCE)
+                            " THEN 's'::\"char\" ELSE 'r'::\"char\" END, c.relowner) AS acldefault, ");
    else
        appendPQExpBufferStr(query,
-                            "c.relacl, NULL as rrelacl, "
-                            "NULL AS initrelacl, NULL AS initrrelacl, "
-                            "false AS changed_acl, ");
+                            "c.relacl, NULL AS acldefault, ");
 
    if (fout->remoteVersion >= 100000)
        appendPQExpBufferStr(query,
@@ -6644,22 +6441,16 @@ getTables(Archive *fout, int *numTables)
                         "\nFROM pg_class c\n"
                         "LEFT JOIN pg_depend d ON "
                         "(c.relkind = " CppAsString2(RELKIND_SEQUENCE) " AND "
-                        "d.classid = c.tableoid AND d.objid = c.oid AND "
+                        "d.classid = 'pg_class'::regclass AND d.objid = c.oid AND "
                         "d.objsubid = 0 AND "
-                        "d.refclassid = c.tableoid AND d.deptype IN ('a', 'i'))\n"
+                        "d.refclassid = 'pg_class'::regclass AND d.deptype IN ('a', 'i'))\n"
                         "LEFT JOIN pg_tablespace tsp ON (tsp.oid = c.reltablespace)\n");
 
    /*
-    * In 9.6 and up, left join to pg_init_privs to detect if any privileges
-    * are still as-set-at-init, in which case we won't dump out ACL commands
-    * for those.  We also are interested in the amname as of 9.6.
+    * In 9.6 and up, left join to pg_am to pick up the amname.
     */
    if (fout->remoteVersion >= 90600)
        appendPQExpBufferStr(query,
-                            "LEFT JOIN pg_init_privs pip ON "
-                            "(c.oid = pip.objoid "
-                            "AND pip.classoid = 'pg_class'::regclass "
-                            "AND pip.objsubid = 0)\n"
                             "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
 
    /*
@@ -6671,7 +6462,9 @@ getTables(Archive *fout, int *numTables)
     */
    if (fout->remoteVersion >= 80200)
        appendPQExpBufferStr(query,
-                            "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
+                            "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
+                            " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
+                            " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
 
    /*
     * Restrict to interesting relkinds (in particular, not indexes).  Not all
@@ -6745,10 +6538,7 @@ getTables(Archive *fout, int *numTables)
    i_amname = PQfnumber(res, "amname");
    i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
    i_relacl = PQfnumber(res, "relacl");
-   i_rrelacl = PQfnumber(res, "rrelacl");
-   i_initrelacl = PQfnumber(res, "initrelacl");
-   i_initrrelacl = PQfnumber(res, "initrrelacl");
-   i_changed_acl = PQfnumber(res, "changed_acl");
+   i_acldefault = PQfnumber(res, "acldefault");
    i_partkeydef = PQfnumber(res, "partkeydef");
    i_ispartition = PQfnumber(res, "ispartition");
    i_partbound = PQfnumber(res, "partbound");
@@ -6777,6 +6567,10 @@ getTables(Archive *fout, int *numTables)
        tblinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_relname));
        tblinfo[i].dobj.namespace =
            findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
+       tblinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_relacl));
+       tblinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+       tblinfo[i].dacl.privtype = 0;
+       tblinfo[i].dacl.initprivs = NULL;
        tblinfo[i].relkind = *(PQgetvalue(res, i, i_relkind));
        tblinfo[i].reltype = atooid(PQgetvalue(res, i, i_reltype));
        tblinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
@@ -6823,10 +6617,6 @@ getTables(Archive *fout, int *numTables)
        else
            tblinfo[i].amname = pg_strdup(PQgetvalue(res, i, i_amname));
        tblinfo[i].is_identity_sequence = (strcmp(PQgetvalue(res, i, i_is_identity_sequence), "t") == 0);
-       tblinfo[i].relacl = pg_strdup(PQgetvalue(res, i, i_relacl));
-       tblinfo[i].rrelacl = pg_strdup(PQgetvalue(res, i, i_rrelacl));
-       tblinfo[i].initrelacl = pg_strdup(PQgetvalue(res, i, i_initrelacl));
-       tblinfo[i].initrrelacl = pg_strdup(PQgetvalue(res, i, i_initrrelacl));
        tblinfo[i].partkeydef = pg_strdup(PQgetvalue(res, i, i_partkeydef));
        tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
        tblinfo[i].partbound = pg_strdup(PQgetvalue(res, i, i_partbound));
@@ -6862,23 +6652,10 @@ getTables(Archive *fout, int *numTables)
        /* Tables have data */
        tblinfo[i].dobj.components |= DUMP_COMPONENT_DATA;
 
-       /*
-        * Mark whether table has an ACL.
-        *
-        * If the table-level and all column-level ACLs for this table are
-        * unchanged, then we don't need to worry about including the ACLs for
-        * this table.  If any column-level ACLs have been changed, the
-        * 'changed_acl' column from the query will indicate that.
-        *
-        * This can result in a significant performance improvement in cases
-        * where we are only looking to dump out the ACL (eg: pg_catalog).
-        */
-       if (!(PQgetisnull(res, i, i_relacl) &&
-             PQgetisnull(res, i, i_rrelacl) &&
-             PQgetisnull(res, i, i_initrelacl) &&
-             PQgetisnull(res, i, i_initrrelacl) &&
-             strcmp(PQgetvalue(res, i, i_changed_acl), "f") == 0))
+       /* Mark whether table has an ACL */
+       if (!PQgetisnull(res, i, i_relacl))
            tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
+       tblinfo[i].hascolumnACLs = false;   /* may get set later */
 
        /*
         * Read-lock target tables to make sure they aren't DROPPED or altered
@@ -8161,7 +7938,6 @@ getEventTriggers(Archive *fout, int *numEventTriggers)
 ProcLangInfo *
 getProcLangs(Archive *fout, int *numProcLangs)
 {
-   DumpOptions *dopt = fout->dopt;
    PGresult   *res;
    int         ntups;
    int         i;
@@ -8175,56 +7951,30 @@ getProcLangs(Archive *fout, int *numProcLangs)
    int         i_laninline;
    int         i_lanvalidator;
    int         i_lanacl;
-   int         i_rlanacl;
-   int         i_initlanacl;
-   int         i_initrlanacl;
+   int         i_acldefault;
    int         i_lanowner;
 
-   if (fout->remoteVersion >= 90600)
+   if (fout->remoteVersion >= 90200)
    {
-       PQExpBuffer acl_subquery = createPQExpBuffer();
-       PQExpBuffer racl_subquery = createPQExpBuffer();
-       PQExpBuffer initacl_subquery = createPQExpBuffer();
-       PQExpBuffer initracl_subquery = createPQExpBuffer();
-
-       buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
-                       initracl_subquery, "l.lanacl", "l.lanowner",
-                       "pip.initprivs", "'l'", dopt->binary_upgrade);
-
-       /* pg_language has a laninline column */
-       appendPQExpBuffer(query, "SELECT l.tableoid, l.oid, "
-                         "l.lanname, l.lanpltrusted, l.lanplcallfoid, "
-                         "l.laninline, l.lanvalidator, "
-                         "%s AS lanacl, "
-                         "%s AS rlanacl, "
-                         "%s AS initlanacl, "
-                         "%s AS initrlanacl, "
-                         "(%s l.lanowner) AS lanowner "
-                         "FROM pg_language l "
-                         "LEFT JOIN pg_init_privs pip ON "
-                         "(l.oid = pip.objoid "
-                         "AND pip.classoid = 'pg_language'::regclass "
-                         "AND pip.objsubid = 0) "
-                         "WHERE l.lanispl "
-                         "ORDER BY l.oid",
-                         acl_subquery->data,
-                         racl_subquery->data,
-                         initacl_subquery->data,
-                         initracl_subquery->data,
+       /* acldefault() exists */
+       appendPQExpBuffer(query, "SELECT tableoid, oid, "
+                         "lanname, lanpltrusted, lanplcallfoid, "
+                         "laninline, lanvalidator, "
+                         "lanacl, "
+                         "acldefault('l', lanowner) AS acldefault, "
+                         "(%s lanowner) AS lanowner "
+                         "FROM pg_language "
+                         "WHERE lanispl "
+                         "ORDER BY oid",
                          username_subquery);
-
-       destroyPQExpBuffer(acl_subquery);
-       destroyPQExpBuffer(racl_subquery);
-       destroyPQExpBuffer(initacl_subquery);
-       destroyPQExpBuffer(initracl_subquery);
    }
    else if (fout->remoteVersion >= 90000)
    {
        /* pg_language has a laninline column */
        appendPQExpBuffer(query, "SELECT tableoid, oid, "
                          "lanname, lanpltrusted, lanplcallfoid, "
-                         "laninline, lanvalidator, lanacl, NULL AS rlanacl, "
-                         "NULL AS initlanacl, NULL AS initrlanacl, "
+                         "laninline, lanvalidator, "
+                         "lanacl, NULL AS acldefault, "
                          "(%s lanowner) AS lanowner "
                          "FROM pg_language "
                          "WHERE lanispl "
@@ -8237,8 +7987,7 @@ getProcLangs(Archive *fout, int *numProcLangs)
        appendPQExpBuffer(query, "SELECT tableoid, oid, "
                          "lanname, lanpltrusted, lanplcallfoid, "
                          "0 AS laninline, lanvalidator, lanacl, "
-                         "NULL AS rlanacl, "
-                         "NULL AS initlanacl, NULL AS initrlanacl, "
+                         "NULL AS acldefault, "
                          "(%s lanowner) AS lanowner "
                          "FROM pg_language "
                          "WHERE lanispl "
@@ -8251,8 +8000,7 @@ getProcLangs(Archive *fout, int *numProcLangs)
        appendPQExpBuffer(query, "SELECT tableoid, oid, "
                          "lanname, lanpltrusted, lanplcallfoid, "
                          "0 AS laninline, lanvalidator, lanacl, "
-                         "NULL AS rlanacl, "
-                         "NULL AS initlanacl, NULL AS initrlanacl, "
+                         "NULL AS acldefault, "
                          "(%s '10') AS lanowner "
                          "FROM pg_language "
                          "WHERE lanispl "
@@ -8265,8 +8013,7 @@ getProcLangs(Archive *fout, int *numProcLangs)
        appendPQExpBuffer(query, "SELECT tableoid, oid, "
                          "lanname, lanpltrusted, lanplcallfoid, "
                          "0 AS laninline, lanvalidator, lanacl, "
-                         "NULL AS rlanacl, "
-                         "NULL AS initlanacl, NULL AS initrlanacl, "
+                         "NULL AS acldefault, "
                          "(%s '1') AS lanowner "
                          "FROM pg_language "
                          "WHERE lanispl "
@@ -8290,9 +8037,7 @@ getProcLangs(Archive *fout, int *numProcLangs)
    i_laninline = PQfnumber(res, "laninline");
    i_lanvalidator = PQfnumber(res, "lanvalidator");
    i_lanacl = PQfnumber(res, "lanacl");
-   i_rlanacl = PQfnumber(res, "rlanacl");
-   i_initlanacl = PQfnumber(res, "initlanacl");
-   i_initrlanacl = PQfnumber(res, "initrlanacl");
+   i_acldefault = PQfnumber(res, "acldefault");
    i_lanowner = PQfnumber(res, "lanowner");
 
    for (i = 0; i < ntups; i++)
@@ -8303,24 +8048,21 @@ getProcLangs(Archive *fout, int *numProcLangs)
        AssignDumpId(&planginfo[i].dobj);
 
        planginfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_lanname));
+       planginfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_lanacl));
+       planginfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+       planginfo[i].dacl.privtype = 0;
+       planginfo[i].dacl.initprivs = NULL;
        planginfo[i].lanpltrusted = *(PQgetvalue(res, i, i_lanpltrusted)) == 't';
        planginfo[i].lanplcallfoid = atooid(PQgetvalue(res, i, i_lanplcallfoid));
        planginfo[i].laninline = atooid(PQgetvalue(res, i, i_laninline));
        planginfo[i].lanvalidator = atooid(PQgetvalue(res, i, i_lanvalidator));
-       planginfo[i].lanacl = pg_strdup(PQgetvalue(res, i, i_lanacl));
-       planginfo[i].rlanacl = pg_strdup(PQgetvalue(res, i, i_rlanacl));
-       planginfo[i].initlanacl = pg_strdup(PQgetvalue(res, i, i_initlanacl));
-       planginfo[i].initrlanacl = pg_strdup(PQgetvalue(res, i, i_initrlanacl));
        planginfo[i].lanowner = pg_strdup(PQgetvalue(res, i, i_lanowner));
 
        /* Decide whether we want to dump it */
        selectDumpableProcLang(&(planginfo[i]), fout);
 
        /* Mark whether language has an ACL */
-       if (!(PQgetisnull(res, i, i_lanacl) &&
-             PQgetisnull(res, i, i_rlanacl) &&
-             PQgetisnull(res, i, i_initlanacl) &&
-             PQgetisnull(res, i, i_initrlanacl)))
+       if (!PQgetisnull(res, i, i_lanacl))
            planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
    }
 
@@ -9371,7 +9113,6 @@ getTSConfigurations(Archive *fout, int *numTSConfigs)
 FdwInfo *
 getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
 {
-   DumpOptions *dopt = fout->dopt;
    PGresult   *res;
    int         ntups;
    int         i;
@@ -9384,9 +9125,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
    int         i_fdwhandler;
    int         i_fdwvalidator;
    int         i_fdwacl;
-   int         i_rfdwacl;
-   int         i_initfdwacl;
-   int         i_initrfdwacl;
+   int         i_acldefault;
    int         i_fdwoptions;
 
    /* Before 8.4, there are no foreign-data wrappers */
@@ -9398,46 +9137,22 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
 
    query = createPQExpBuffer();
 
-   if (fout->remoteVersion >= 90600)
+   if (fout->remoteVersion >= 90200)
    {
-       PQExpBuffer acl_subquery = createPQExpBuffer();
-       PQExpBuffer racl_subquery = createPQExpBuffer();
-       PQExpBuffer initacl_subquery = createPQExpBuffer();
-       PQExpBuffer initracl_subquery = createPQExpBuffer();
-
-       buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
-                       initracl_subquery, "f.fdwacl", "f.fdwowner",
-                       "pip.initprivs", "'F'", dopt->binary_upgrade);
-
-       appendPQExpBuffer(query, "SELECT f.tableoid, f.oid, f.fdwname, "
-                         "(%s f.fdwowner) AS rolname, "
-                         "f.fdwhandler::pg_catalog.regproc, "
-                         "f.fdwvalidator::pg_catalog.regproc, "
-                         "%s AS fdwacl, "
-                         "%s AS rfdwacl, "
-                         "%s AS initfdwacl, "
-                         "%s AS initrfdwacl, "
+       appendPQExpBuffer(query, "SELECT tableoid, oid, fdwname, "
+                         "(%s fdwowner) AS rolname, "
+                         "fdwhandler::pg_catalog.regproc, "
+                         "fdwvalidator::pg_catalog.regproc, "
+                         "fdwacl, "
+                         "acldefault('F', fdwowner) AS acldefault, "
                          "array_to_string(ARRAY("
                          "SELECT quote_ident(option_name) || ' ' || "
                          "quote_literal(option_value) "
-                         "FROM pg_options_to_table(f.fdwoptions) "
+                         "FROM pg_options_to_table(fdwoptions) "
                          "ORDER BY option_name"
                          "), E',\n    ') AS fdwoptions "
-                         "FROM pg_foreign_data_wrapper f "
-                         "LEFT JOIN pg_init_privs pip ON "
-                         "(f.oid = pip.objoid "
-                         "AND pip.classoid = 'pg_foreign_data_wrapper'::regclass "
-                         "AND pip.objsubid = 0) ",
-                         username_subquery,
-                         acl_subquery->data,
-                         racl_subquery->data,
-                         initacl_subquery->data,
-                         initracl_subquery->data);
-
-       destroyPQExpBuffer(acl_subquery);
-       destroyPQExpBuffer(racl_subquery);
-       destroyPQExpBuffer(initacl_subquery);
-       destroyPQExpBuffer(initracl_subquery);
+                         "FROM pg_foreign_data_wrapper",
+                         username_subquery);
    }
    else if (fout->remoteVersion >= 90100)
    {
@@ -9445,8 +9160,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
                          "(%s fdwowner) AS rolname, "
                          "fdwhandler::pg_catalog.regproc, "
                          "fdwvalidator::pg_catalog.regproc, fdwacl, "
-                         "NULL as rfdwacl, "
-                         "NULL as initfdwacl, NULL AS initrfdwacl, "
+                         "NULL AS acldefault, "
                          "array_to_string(ARRAY("
                          "SELECT quote_ident(option_name) || ' ' || "
                          "quote_literal(option_value) "
@@ -9462,8 +9176,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
                          "(%s fdwowner) AS rolname, "
                          "'-' AS fdwhandler, "
                          "fdwvalidator::pg_catalog.regproc, fdwacl, "
-                         "NULL as rfdwacl, "
-                         "NULL as initfdwacl, NULL AS initrfdwacl, "
+                         "NULL AS acldefault, "
                          "array_to_string(ARRAY("
                          "SELECT quote_ident(option_name) || ' ' || "
                          "quote_literal(option_value) "
@@ -9488,9 +9201,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
    i_fdwhandler = PQfnumber(res, "fdwhandler");
    i_fdwvalidator = PQfnumber(res, "fdwvalidator");
    i_fdwacl = PQfnumber(res, "fdwacl");
-   i_rfdwacl = PQfnumber(res, "rfdwacl");
-   i_initfdwacl = PQfnumber(res, "initfdwacl");
-   i_initrfdwacl = PQfnumber(res, "initrfdwacl");
+   i_acldefault = PQfnumber(res, "acldefault");
    i_fdwoptions = PQfnumber(res, "fdwoptions");
 
    for (i = 0; i < ntups; i++)
@@ -9501,23 +9212,20 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
        AssignDumpId(&fdwinfo[i].dobj);
        fdwinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_fdwname));
        fdwinfo[i].dobj.namespace = NULL;
+       fdwinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
+       fdwinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+       fdwinfo[i].dacl.privtype = 0;
+       fdwinfo[i].dacl.initprivs = NULL;
        fdwinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
        fdwinfo[i].fdwhandler = pg_strdup(PQgetvalue(res, i, i_fdwhandler));
        fdwinfo[i].fdwvalidator = pg_strdup(PQgetvalue(res, i, i_fdwvalidator));
        fdwinfo[i].fdwoptions = pg_strdup(PQgetvalue(res, i, i_fdwoptions));
-       fdwinfo[i].fdwacl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
-       fdwinfo[i].rfdwacl = pg_strdup(PQgetvalue(res, i, i_rfdwacl));
-       fdwinfo[i].initfdwacl = pg_strdup(PQgetvalue(res, i, i_initfdwacl));
-       fdwinfo[i].initrfdwacl = pg_strdup(PQgetvalue(res, i, i_initrfdwacl));
 
        /* Decide whether we want to dump it */
        selectDumpableObject(&(fdwinfo[i].dobj), fout);
 
        /* Mark whether FDW has an ACL */
-       if (!(PQgetisnull(res, i, i_fdwacl) &&
-             PQgetisnull(res, i, i_rfdwacl) &&
-             PQgetisnull(res, i, i_initfdwacl) &&
-             PQgetisnull(res, i, i_initrfdwacl)))
+       if (!PQgetisnull(res, i, i_fdwacl))
            fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
    }
 
@@ -9538,7 +9246,6 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
 ForeignServerInfo *
 getForeignServers(Archive *fout, int *numForeignServers)
 {
-   DumpOptions *dopt = fout->dopt;
    PGresult   *res;
    int         ntups;
    int         i;
@@ -9552,9 +9259,7 @@ getForeignServers(Archive *fout, int *numForeignServers)
    int         i_srvtype;
    int         i_srvversion;
    int         i_srvacl;
-   int         i_rsrvacl;
-   int         i_initsrvacl;
-   int         i_initrsrvacl;
+   int         i_acldefault;
    int         i_srvoptions;
 
    /* Before 8.4, there are no foreign servers */
@@ -9566,53 +9271,27 @@ getForeignServers(Archive *fout, int *numForeignServers)
 
    query = createPQExpBuffer();
 
-   if (fout->remoteVersion >= 90600)
+   if (fout->remoteVersion >= 90200)
    {
-       PQExpBuffer acl_subquery = createPQExpBuffer();
-       PQExpBuffer racl_subquery = createPQExpBuffer();
-       PQExpBuffer initacl_subquery = createPQExpBuffer();
-       PQExpBuffer initracl_subquery = createPQExpBuffer();
-
-       buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
-                       initracl_subquery, "f.srvacl", "f.srvowner",
-                       "pip.initprivs", "'S'", dopt->binary_upgrade);
-
-       appendPQExpBuffer(query, "SELECT f.tableoid, f.oid, f.srvname, "
-                         "(%s f.srvowner) AS rolname, "
-                         "f.srvfdw, f.srvtype, f.srvversion, "
-                         "%s AS srvacl, "
-                         "%s AS rsrvacl, "
-                         "%s AS initsrvacl, "
-                         "%s AS initrsrvacl, "
+       appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, "
+                         "(%s srvowner) AS rolname, "
+                         "srvfdw, srvtype, srvversion, srvacl, "
+                         "acldefault('S', srvowner) AS acldefault, "
                          "array_to_string(ARRAY("
                          "SELECT quote_ident(option_name) || ' ' || "
                          "quote_literal(option_value) "
-                         "FROM pg_options_to_table(f.srvoptions) "
+                         "FROM pg_options_to_table(srvoptions) "
                          "ORDER BY option_name"
                          "), E',\n    ') AS srvoptions "
-                         "FROM pg_foreign_server f "
-                         "LEFT JOIN pg_init_privs pip "
-                         "ON (f.oid = pip.objoid "
-                         "AND pip.classoid = 'pg_foreign_server'::regclass "
-                         "AND pip.objsubid = 0) ",
-                         username_subquery,
-                         acl_subquery->data,
-                         racl_subquery->data,
-                         initacl_subquery->data,
-                         initracl_subquery->data);
-
-       destroyPQExpBuffer(acl_subquery);
-       destroyPQExpBuffer(racl_subquery);
-       destroyPQExpBuffer(initacl_subquery);
-       destroyPQExpBuffer(initracl_subquery);
+                         "FROM pg_foreign_server",
+                         username_subquery);
    }
    else
    {
        appendPQExpBuffer(query, "SELECT tableoid, oid, srvname, "
                          "(%s srvowner) AS rolname, "
                          "srvfdw, srvtype, srvversion, srvacl, "
-                         "NULL AS rsrvacl, "
-                         "NULL AS initsrvacl, NULL AS initrsrvacl, "
+                         "NULL AS acldefault, "
                          "array_to_string(ARRAY("
                          "SELECT quote_ident(option_name) || ' ' || "
                          "quote_literal(option_value) "
@@ -9638,9 +9317,7 @@ getForeignServers(Archive *fout, int *numForeignServers)
    i_srvtype = PQfnumber(res, "srvtype");
    i_srvversion = PQfnumber(res, "srvversion");
    i_srvacl = PQfnumber(res, "srvacl");
-   i_rsrvacl = PQfnumber(res, "rsrvacl");
-   i_initsrvacl = PQfnumber(res, "initsrvacl");
-   i_initrsrvacl = PQfnumber(res, "initrsrvacl");
+   i_acldefault = PQfnumber(res, "acldefault");
    i_srvoptions = PQfnumber(res, "srvoptions");
 
    for (i = 0; i < ntups; i++)
@@ -9651,15 +9328,15 @@ getForeignServers(Archive *fout, int *numForeignServers)
        AssignDumpId(&srvinfo[i].dobj);
        srvinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_srvname));
        srvinfo[i].dobj.namespace = NULL;
+       srvinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_srvacl));
+       srvinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+       srvinfo[i].dacl.privtype = 0;
+       srvinfo[i].dacl.initprivs = NULL;
        srvinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
        srvinfo[i].srvfdw = atooid(PQgetvalue(res, i, i_srvfdw));
        srvinfo[i].srvtype = pg_strdup(PQgetvalue(res, i, i_srvtype));
        srvinfo[i].srvversion = pg_strdup(PQgetvalue(res, i, i_srvversion));
        srvinfo[i].srvoptions = pg_strdup(PQgetvalue(res, i, i_srvoptions));
-       srvinfo[i].srvacl = pg_strdup(PQgetvalue(res, i, i_srvacl));
-       srvinfo[i].rsrvacl = pg_strdup(PQgetvalue(res, i, i_rsrvacl));
-       srvinfo[i].initsrvacl = pg_strdup(PQgetvalue(res, i, i_initsrvacl));
-       srvinfo[i].initrsrvacl = pg_strdup(PQgetvalue(res, i, i_initrsrvacl));
 
        /* Decide whether we want to dump it */
        selectDumpableObject(&(srvinfo[i].dobj), fout);
@@ -9668,10 +9345,7 @@ getForeignServers(Archive *fout, int *numForeignServers)
        srvinfo[i].dobj.components |= DUMP_COMPONENT_USERMAP;
 
        /* Mark whether server has an ACL */
-       if (!(PQgetisnull(res, i, i_srvacl) &&
-             PQgetisnull(res, i, i_rsrvacl) &&
-             PQgetisnull(res, i, i_initsrvacl) &&
-             PQgetisnull(res, i, i_initrsrvacl)))
+       if (!PQgetisnull(res, i, i_srvacl))
            srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
    }
 
@@ -9702,9 +9376,7 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs)
    int         i_defaclnamespace;
    int         i_defaclobjtype;
    int         i_defaclacl;
-   int         i_rdefaclacl;
-   int         i_initdefaclacl;
-   int         i_initrdefaclacl;
+   int         i_acldefault;
    int         i,
                ntups;
 
@@ -9716,13 +9388,16 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs)
 
    query = createPQExpBuffer();
 
-   if (fout->remoteVersion >= 90600)
-   {
-       PQExpBuffer acl_subquery = createPQExpBuffer();
-       PQExpBuffer racl_subquery = createPQExpBuffer();
-       PQExpBuffer initacl_subquery = createPQExpBuffer();
-       PQExpBuffer initracl_subquery = createPQExpBuffer();
+   appendPQExpBuffer(query,
+                     "SELECT oid, tableoid, "
+                     "(%s defaclrole) AS defaclrole, "
+                     "defaclnamespace, "
+                     "defaclobjtype, "
+                     "defaclacl, ",
+                     username_subquery);
 
+   if (fout->remoteVersion >= 90200)
+   {
        /*
         * Global entries (with defaclnamespace=0) replace the hard-wired
         * default ACL for their object type.  We should dump them as deltas
@@ -9730,59 +9405,24 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs)
         * for interpreting the ALTER DEFAULT PRIVILEGES commands.  On the
         * other hand, non-global entries can only add privileges not revoke
         * them.  We must dump those as-is (i.e., as deltas from an empty
-        * ACL).  We implement that by passing NULL as the object type for
-        * acldefault(), which works because acldefault() is STRICT.
+        * ACL).
         *
         * We can use defaclobjtype as the object type for acldefault(),
         * except for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be
         * converted to 's'.
         */
-       buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
-                       initracl_subquery, "defaclacl", "defaclrole",
-                       "pip.initprivs",
-                       "CASE WHEN defaclnamespace = 0 THEN"
-                       "     CASE WHEN defaclobjtype = 'S' THEN 's'::\"char\""
-                       "     ELSE defaclobjtype END "
-                       "ELSE NULL END",
-                       dopt->binary_upgrade);
-
-       appendPQExpBuffer(query, "SELECT d.oid, d.tableoid, "
-                         "(%s d.defaclrole) AS defaclrole, "
-                         "d.defaclnamespace, "
-                         "d.defaclobjtype, "
-                         "%s AS defaclacl, "
-                         "%s AS rdefaclacl, "
-                         "%s AS initdefaclacl, "
-                         "%s AS initrdefaclacl "
-                         "FROM pg_default_acl d "
-                         "LEFT JOIN pg_init_privs pip ON "
-                         "(d.oid = pip.objoid "
-                         "AND pip.classoid = 'pg_default_acl'::regclass "
-                         "AND pip.objsubid = 0) ",
-                         username_subquery,
-                         acl_subquery->data,
-                         racl_subquery->data,
-                         initacl_subquery->data,
-                         initracl_subquery->data);
-
-       destroyPQExpBuffer(acl_subquery);
-       destroyPQExpBuffer(racl_subquery);
-       destroyPQExpBuffer(initacl_subquery);
-       destroyPQExpBuffer(initracl_subquery);
+       appendPQExpBufferStr(query,
+                            "CASE WHEN defaclnamespace = 0 THEN "
+                            "acldefault(CASE WHEN defaclobjtype = 'S' "
+                            "THEN 's'::\"char\" ELSE defaclobjtype END, "
+                            "defaclrole) ELSE '{}' END AS acldefault ");
    }
    else
-   {
-       appendPQExpBuffer(query, "SELECT oid, tableoid, "
-                         "(%s defaclrole) AS defaclrole, "
-                         "defaclnamespace, "
-                         "defaclobjtype, "
-                         "defaclacl, "
-                         "NULL AS rdefaclacl, "
-                         "NULL AS initdefaclacl, "
-                         "NULL AS initrdefaclacl "
-                         "FROM pg_default_acl",
-                         username_subquery);
-   }
+       appendPQExpBufferStr(query,
+                            "NULL AS acldefault ");
+
+   appendPQExpBufferStr(query,
+                        "FROM pg_default_acl");
 
    res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -9797,9 +9437,7 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs)
    i_defaclnamespace = PQfnumber(res, "defaclnamespace");
    i_defaclobjtype = PQfnumber(res, "defaclobjtype");
    i_defaclacl = PQfnumber(res, "defaclacl");
-   i_rdefaclacl = PQfnumber(res, "rdefaclacl");
-   i_initdefaclacl = PQfnumber(res, "initdefaclacl");
-   i_initrdefaclacl = PQfnumber(res, "initrdefaclacl");
+   i_acldefault = PQfnumber(res, "acldefault");
 
    for (i = 0; i < ntups; i++)
    {
@@ -9817,12 +9455,12 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs)
        else
            daclinfo[i].dobj.namespace = NULL;
 
+       daclinfo[i].dacl.acl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
+       daclinfo[i].dacl.acldefault = pg_strdup(PQgetvalue(res, i, i_acldefault));
+       daclinfo[i].dacl.privtype = 0;
+       daclinfo[i].dacl.initprivs = NULL;
        daclinfo[i].defaclrole = pg_strdup(PQgetvalue(res, i, i_defaclrole));
        daclinfo[i].defaclobjtype = *(PQgetvalue(res, i, i_defaclobjtype));
-       daclinfo[i].defaclacl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
-       daclinfo[i].rdefaclacl = pg_strdup(PQgetvalue(res, i, i_rdefaclacl));
-       daclinfo[i].initdefaclacl = pg_strdup(PQgetvalue(res, i, i_initdefaclacl));
-       daclinfo[i].initrdefaclacl = pg_strdup(PQgetvalue(res, i, i_initrdefaclacl));
 
        /* Default ACLs are ACLs, of course */
        daclinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
@@ -9838,6 +9476,126 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs)
    return daclinfo;
 }
 
+/*
+ * getAdditionalACLs
+ *
+ * We have now created all the DumpableObjects, and collected the ACL data
+ * that appears in the directly-associated catalog entries.  However, there's
+ * more ACL-related info to collect.  If any of a table's columns have ACLs,
+ * we must set the TableInfo's DUMP_COMPONENT_ACL components flag, as well as
+ * its hascolumnACLs flag (we won't store the ACLs themselves here, though).
+ * Also, in versions having the pg_init_privs catalog, read that and load the
+ * information into the relevant DumpableObjects.
+ */
+static void
+getAdditionalACLs(Archive *fout)
+{
+   PQExpBuffer query = createPQExpBuffer();
+   PGresult   *res;
+   int         ntups,
+               i;
+
+   /* Check for per-column ACLs */
+   if (fout->remoteVersion >= 80400)
+   {
+       appendPQExpBufferStr(query,
+                            "SELECT DISTINCT attrelid FROM pg_attribute "
+                            "WHERE attacl IS NOT NULL");
+
+       res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+       ntups = PQntuples(res);
+       for (i = 0; i < ntups; i++)
+       {
+           Oid         relid = atooid(PQgetvalue(res, i, 0));
+           TableInfo  *tblinfo;
+
+           tblinfo = findTableByOid(relid);
+           /* OK to ignore tables we haven't got a DumpableObject for */
+           if (tblinfo)
+           {
+               tblinfo->dobj.components |= DUMP_COMPONENT_ACL;
+               tblinfo->hascolumnACLs = true;
+           }
+       }
+       PQclear(res);
+   }
+
+   /* Fetch initial-privileges data */
+   if (fout->remoteVersion >= 90600)
+   {
+       printfPQExpBuffer(query,
+                         "SELECT objoid, classoid, objsubid, privtype, initprivs "
+                         "FROM pg_init_privs");
+
+       res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+       ntups = PQntuples(res);
+       for (i = 0; i < ntups; i++)
+       {
+           Oid         objoid = atooid(PQgetvalue(res, i, 0));
+           Oid         classoid = atooid(PQgetvalue(res, i, 1));
+           int         objsubid = atoi(PQgetvalue(res, i, 2));
+           char        privtype = *(PQgetvalue(res, i, 3));
+           char       *initprivs = PQgetvalue(res, i, 4);
+           CatalogId   objId;
+           DumpableObject *dobj;
+
+           objId.tableoid = classoid;
+           objId.oid = objoid;
+           dobj = findObjectByCatalogId(objId);
+           /* OK to ignore entries we haven't got a DumpableObject for */
+           if (dobj)
+           {
+               /* Cope with sub-object initprivs */
+               if (objsubid != 0)
+               {
+                   if (dobj->objType == DO_TABLE)
+                   {
+                       /* For a column initpriv, set the table's ACL flags */
+                       dobj->components |= DUMP_COMPONENT_ACL;
+                       ((TableInfo *) dobj)->hascolumnACLs = true;
+                   }
+                   else
+                       pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
+                                      classoid, objoid, objsubid);
+                   continue;
+               }
+
+               /*
+                * We ignore any pg_init_privs.initprivs entry for the public
+                * schema, as explained in getNamespaces().
+                */
+               if (dobj->objType == DO_NAMESPACE &&
+                   strcmp(dobj->name, "public") == 0)
+                   continue;
+
+               /* Else it had better be of a type we think has ACLs */
+               if (dobj->objType == DO_NAMESPACE ||
+                   dobj->objType == DO_TYPE ||
+                   dobj->objType == DO_FUNC ||
+                   dobj->objType == DO_AGG ||
+                   dobj->objType == DO_TABLE ||
+                   dobj->objType == DO_PROCLANG ||
+                   dobj->objType == DO_FDW ||
+                   dobj->objType == DO_FOREIGN_SERVER)
+               {
+                   DumpableObjectWithAcl *daobj = (DumpableObjectWithAcl *) dobj;
+
+                   daobj->dacl.privtype = privtype;
+                   daobj->dacl.initprivs = pstrdup(initprivs);
+               }
+               else
+                   pg_log_warning("unsupported pg_init_privs entry: %u %u %d",
+                                  classoid, objoid, objsubid);
+           }
+       }
+       PQclear(res);
+   }
+
+   destroyPQExpBuffer(query);
+}
+
 /*
  * dumpCommentExtended --
  *
@@ -10490,8 +10248,7 @@ dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
    if (nspinfo->dobj.dump & DUMP_COMPONENT_ACL)
        dumpACL(fout, nspinfo->dobj.dumpId, InvalidDumpId, "SCHEMA",
                qnspname, NULL, NULL,
-               nspinfo->rolname, nspinfo->nspacl, nspinfo->rnspacl,
-               nspinfo->initnspacl, nspinfo->initrnspacl);
+               nspinfo->rolname, &nspinfo->dacl);
 
    free(qnspname);
 
@@ -10782,8 +10539,7 @@ dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
        dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
                qtypname, NULL,
                tyinfo->dobj.namespace->dobj.name,
-               tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
-               tyinfo->inittypacl, tyinfo->initrtypacl);
+               tyinfo->rolname, &tyinfo->dacl);
 
    PQclear(res);
    destroyPQExpBuffer(q);
@@ -10922,8 +10678,7 @@ dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
        dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
                qtypname, NULL,
                tyinfo->dobj.namespace->dobj.name,
-               tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
-               tyinfo->inittypacl, tyinfo->initrtypacl);
+               tyinfo->rolname, &tyinfo->dacl);
 
    PQclear(res);
    destroyPQExpBuffer(q);
@@ -10994,8 +10749,7 @@ dumpUndefinedType(Archive *fout, const TypeInfo *tyinfo)
        dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
                qtypname, NULL,
                tyinfo->dobj.namespace->dobj.name,
-               tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
-               tyinfo->inittypacl, tyinfo->initrtypacl);
+               tyinfo->rolname, &tyinfo->dacl);
 
    destroyPQExpBuffer(q);
    destroyPQExpBuffer(delq);
@@ -11254,8 +11008,7 @@ dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
        dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
                qtypname, NULL,
                tyinfo->dobj.namespace->dobj.name,
-               tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
-               tyinfo->inittypacl, tyinfo->initrtypacl);
+               tyinfo->rolname, &tyinfo->dacl);
 
    PQclear(res);
    destroyPQExpBuffer(q);
@@ -11411,8 +11164,7 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
        dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
                qtypname, NULL,
                tyinfo->dobj.namespace->dobj.name,
-               tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
-               tyinfo->inittypacl, tyinfo->initrtypacl);
+               tyinfo->rolname, &tyinfo->dacl);
 
    /* Dump any per-constraint comments */
    for (i = 0; i < tyinfo->nDomChecks; i++)
@@ -11633,8 +11385,7 @@ dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
        dumpACL(fout, tyinfo->dobj.dumpId, InvalidDumpId, "TYPE",
                qtypname, NULL,
                tyinfo->dobj.namespace->dobj.name,
-               tyinfo->rolname, tyinfo->typacl, tyinfo->rtypacl,
-               tyinfo->inittypacl, tyinfo->initrtypacl);
+               tyinfo->rolname, &tyinfo->dacl);
 
    PQclear(res);
    destroyPQExpBuffer(q);
@@ -11932,8 +11683,7 @@ dumpProcLang(Archive *fout, const ProcLangInfo *plang)
    if (plang->lanpltrusted && plang->dobj.dump & DUMP_COMPONENT_ACL)
        dumpACL(fout, plang->dobj.dumpId, InvalidDumpId, "LANGUAGE",
                qlanname, NULL, NULL,
-               plang->lanowner, plang->lanacl, plang->rlanacl,
-               plang->initlanacl, plang->initrlanacl);
+               plang->lanowner, &plang->dacl);
 
    free(qlanname);
 
@@ -12569,8 +12319,7 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
        dumpACL(fout, finfo->dobj.dumpId, InvalidDumpId, keyword,
                funcsig, NULL,
                finfo->dobj.namespace->dobj.name,
-               finfo->rolname, finfo->proacl, finfo->rproacl,
-               finfo->initproacl, finfo->initrproacl);
+               finfo->rolname, &finfo->dacl);
 
    PQclear(res);
 
@@ -14474,9 +14223,7 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
        dumpACL(fout, agginfo->aggfn.dobj.dumpId, InvalidDumpId,
                "FUNCTION", aggsig, NULL,
                agginfo->aggfn.dobj.namespace->dobj.name,
-               agginfo->aggfn.rolname, agginfo->aggfn.proacl,
-               agginfo->aggfn.rproacl,
-               agginfo->aggfn.initproacl, agginfo->aggfn.initrproacl);
+               agginfo->aggfn.rolname, &agginfo->aggfn.dacl);
 
    free(aggsig);
    if (aggfullsig)
@@ -14875,9 +14622,7 @@ dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
    if (fdwinfo->dobj.dump & DUMP_COMPONENT_ACL)
        dumpACL(fout, fdwinfo->dobj.dumpId, InvalidDumpId,
                "FOREIGN DATA WRAPPER", qfdwname, NULL,
-               NULL, fdwinfo->rolname,
-               fdwinfo->fdwacl, fdwinfo->rfdwacl,
-               fdwinfo->initfdwacl, fdwinfo->initrfdwacl);
+               NULL, fdwinfo->rolname, &fdwinfo->dacl);
 
    free(qfdwname);
 
@@ -14964,9 +14709,7 @@ dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
    if (srvinfo->dobj.dump & DUMP_COMPONENT_ACL)
        dumpACL(fout, srvinfo->dobj.dumpId, InvalidDumpId,
                "FOREIGN SERVER", qsrvname, NULL,
-               NULL, srvinfo->rolname,
-               srvinfo->srvacl, srvinfo->rsrvacl,
-               srvinfo->initsrvacl, srvinfo->initrsrvacl);
+               NULL, srvinfo->rolname, &srvinfo->dacl);
 
    /* Dump user mappings */
    if (srvinfo->dobj.dump & DUMP_COMPONENT_USERMAP)
@@ -15130,15 +14873,13 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
    if (!buildDefaultACLCommands(type,
                                 daclinfo->dobj.namespace != NULL ?
                                 daclinfo->dobj.namespace->dobj.name : NULL,
-                                daclinfo->defaclacl,
-                                daclinfo->rdefaclacl,
-                                daclinfo->initdefaclacl,
-                                daclinfo->initrdefaclacl,
+                                daclinfo->dacl.acl,
+                                daclinfo->dacl.acldefault,
                                 daclinfo->defaclrole,
                                 fout->remoteVersion,
                                 q))
        fatal("could not parse default ACL list (%s)",
-             daclinfo->defaclacl);
+             daclinfo->dacl.acl);
 
    if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
        ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
@@ -15168,20 +14909,7 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
  *     (Currently we assume that subname is only provided for table columns.)
  * 'nspname' is the namespace the object is in (NULL if none).
  * 'owner' is the owner, NULL if there is no owner (for languages).
- * 'acls' contains the ACL string of the object from the appropriate system
- *         catalog field; it will be passed to buildACLCommands for building the
- *         appropriate GRANT commands.
- * 'racls' contains the ACL string of any initial-but-now-revoked ACLs of the
- *         object; it will be passed to buildACLCommands for building the
- *         appropriate REVOKE commands.
- * 'initacls' In binary-upgrade mode, ACL string of the object's initial
- *         privileges, to be recorded into pg_init_privs
- * 'initracls' In binary-upgrade mode, ACL string of the object's
- *         revoked-from-default privileges, to be recorded into pg_init_privs
- *
- * NB: initacls/initracls are needed because extensions can set privileges on
- * an object during the extension's script file and we record those into
- * pg_init_privs as that object's initial privileges.
+ * 'dacl' is the DumpableAcl struct fpr the object.
  *
  * Returns the dump ID assigned to the ACL TocEntry, or InvalidDumpId if
  * no ACL entry was created.
@@ -15191,11 +14919,15 @@ static DumpId
 dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
        const char *type, const char *name, const char *subname,
        const char *nspname, const char *owner,
-       const char *acls, const char *racls,
-       const char *initacls, const char *initracls)
+       const DumpableAcl *dacl)
 {
    DumpId      aclDumpId = InvalidDumpId;
    DumpOptions *dopt = fout->dopt;
+   const char *acls = dacl->acl;
+   const char *acldefault = dacl->acldefault;
+   char        privtype = dacl->privtype;
+   const char *initprivs = dacl->initprivs;
+   const char *baseacls;
    PQExpBuffer sql;
 
    /* Do nothing if ACL dump is not enabled */
@@ -15209,29 +14941,52 @@ dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
    sql = createPQExpBuffer();
 
    /*
-    * Check to see if this object has had any initial ACLs included for it.
-    * If so, we are in binary upgrade mode and these are the ACLs to turn
-    * into GRANT and REVOKE statements to set and record the initial
-    * privileges for an extension object.  Let the backend know that these
-    * are to be recorded by calling binary_upgrade_set_record_init_privs()
-    * before and after.
+    * In binary upgrade mode, we don't run an extension's script but instead
+    * dump out the objects independently and then recreate them.  To preserve
+    * any initial privileges which were set on extension objects, we need to
+    * compute the set of GRANT and REVOKE commands necessary to get from the
+    * default privileges of an object to its initial privileges as recorded
+    * in pg_init_privs.
+    *
+    * At restore time, we apply these commands after having called
+    * binary_upgrade_set_record_init_privs(true).  That tells the backend to
+    * copy the results into pg_init_privs.  This is how we preserve the
+    * contents of that catalog across binary upgrades.
     */
-   if (strlen(initacls) != 0 || strlen(initracls) != 0)
+   if (dopt->binary_upgrade && privtype == 'e' &&
+       initprivs && *initprivs != '\0')
    {
        appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
        if (!buildACLCommands(name, subname, nspname, type,
-                             initacls, initracls, owner,
+                             initprivs, acldefault, owner,
                              "", fout->remoteVersion, sql))
-           fatal("could not parse initial GRANT ACL list (%s) or initial REVOKE ACL list (%s) for object \"%s\" (%s)",
-                 initacls, initracls, name, type);
+           fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
+                 initprivs, acldefault, name, type);
        appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
    }
 
+   /*
+    * Now figure the GRANT and REVOKE commands needed to get to the object's
+    * actual current ACL, starting from the initprivs if given, else from the
+    * object-type-specific default.  Also, while buildACLCommands will assume
+    * that a NULL/empty acls string means it needn't do anything, what that
+    * actually represents is the object-type-specific default; so we need to
+    * substitute the acldefault string to get the right results in that case.
+    */
+   if (initprivs && *initprivs != '\0')
+   {
+       baseacls = initprivs;
+       if (acls == NULL || *acls == '\0')
+           acls = acldefault;
+   }
+   else
+       baseacls = acldefault;
+
    if (!buildACLCommands(name, subname, nspname, type,
-                         acls, racls, owner,
+                         acls, baseacls, owner,
                          "", fout->remoteVersion, sql))
-       fatal("could not parse GRANT ACL list (%s) or REVOKE ACL list (%s) for object \"%s\" (%s)",
-             acls, racls, name, type);
+       fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
+             acls, baseacls, name, type);
 
    if (sql->len > 0)
    {
@@ -15641,8 +15396,7 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
            dumpACL(fout, tbinfo->dobj.dumpId, InvalidDumpId,
                    objtype, namecopy, NULL,
                    tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
-                   tbinfo->relacl, tbinfo->rrelacl,
-                   tbinfo->initrelacl, tbinfo->initrrelacl);
+                   &tbinfo->dacl);
    }
 
    /*
@@ -15651,7 +15405,7 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
     * miss ACLs on system columns.  Doing it this way also allows us to dump
     * ACLs for catalogs that we didn't mark "interesting" back in getTables.
     */
-   if (fout->remoteVersion >= 80400 && tbinfo->dobj.dump & DUMP_COMPONENT_ACL)
+   if ((tbinfo->dobj.dump & DUMP_COMPONENT_ACL) && tbinfo->hascolumnACLs)
    {
        PQExpBuffer query = createPQExpBuffer();
        PGresult   *res;
@@ -15659,55 +15413,37 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
 
        if (fout->remoteVersion >= 90600)
        {
-           PQExpBuffer acl_subquery = createPQExpBuffer();
-           PQExpBuffer racl_subquery = createPQExpBuffer();
-           PQExpBuffer initacl_subquery = createPQExpBuffer();
-           PQExpBuffer initracl_subquery = createPQExpBuffer();
-
-           buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
-                           initracl_subquery, "at.attacl", "c.relowner",
-                           "pip.initprivs", "'c'", dopt->binary_upgrade);
-
+           /*
+            * In principle we should call acldefault('c', relowner) to get
+            * the default ACL for a column.  However, we don't currently
+            * store the numeric OID of the relowner in TableInfo.  We could
+            * convert the owner name using regrole, but that creates a risk
+            * of failure due to concurrent role renames.  Given that the
+            * default ACL for columns is empty and is likely to stay that
+            * way, it's not worth extra cycles and risk to avoid hard-wiring
+            * that knowledge here.
+            */
            appendPQExpBuffer(query,
                              "SELECT at.attname, "
-                             "%s AS attacl, "
-                             "%s AS rattacl, "
-                             "%s AS initattacl, "
-                             "%s AS initrattacl "
+                             "at.attacl, "
+                             "'{}' AS acldefault, "
+                             "pip.privtype, pip.initprivs "
                              "FROM pg_catalog.pg_attribute at "
-                             "JOIN pg_catalog.pg_class c ON (at.attrelid = c.oid) "
                              "LEFT JOIN pg_catalog.pg_init_privs pip ON "
                              "(at.attrelid = pip.objoid "
                              "AND pip.classoid = 'pg_catalog.pg_class'::pg_catalog.regclass "
                              "AND at.attnum = pip.objsubid) "
                              "WHERE at.attrelid = '%u'::pg_catalog.oid AND "
                              "NOT at.attisdropped "
-                             "AND ("
-                             "%s IS NOT NULL OR "
-                             "%s IS NOT NULL OR "
-                             "%s IS NOT NULL OR "
-                             "%s IS NOT NULL)"
+                             "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
                              "ORDER BY at.attnum",
-                             acl_subquery->data,
-                             racl_subquery->data,
-                             initacl_subquery->data,
-                             initracl_subquery->data,
-                             tbinfo->dobj.catId.oid,
-                             acl_subquery->data,
-                             racl_subquery->data,
-                             initacl_subquery->data,
-                             initracl_subquery->data);
-
-           destroyPQExpBuffer(acl_subquery);
-           destroyPQExpBuffer(racl_subquery);
-           destroyPQExpBuffer(initacl_subquery);
-           destroyPQExpBuffer(initracl_subquery);
+                             tbinfo->dobj.catId.oid);
        }
        else
        {
            appendPQExpBuffer(query,
-                             "SELECT attname, attacl, NULL as rattacl, "
-                             "NULL AS initattacl, NULL AS initrattacl "
+                             "SELECT attname, attacl, '{}' AS acldefault, "
+                             "NULL AS privtype, NULL AS initprivs "
                              "FROM pg_catalog.pg_attribute "
                              "WHERE attrelid = '%u'::pg_catalog.oid AND NOT attisdropped "
                              "AND attacl IS NOT NULL "
@@ -15721,11 +15457,16 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
        {
            char       *attname = PQgetvalue(res, i, 0);
            char       *attacl = PQgetvalue(res, i, 1);
-           char       *rattacl = PQgetvalue(res, i, 2);
-           char       *initattacl = PQgetvalue(res, i, 3);
-           char       *initrattacl = PQgetvalue(res, i, 4);
+           char       *acldefault = PQgetvalue(res, i, 2);
+           char        privtype = *(PQgetvalue(res, i, 3));
+           char       *initprivs = PQgetvalue(res, i, 4);
+           DumpableAcl coldacl;
            char       *attnamecopy;
 
+           coldacl.acl = attacl;
+           coldacl.acldefault = acldefault;
+           coldacl.privtype = privtype;
+           coldacl.initprivs = initprivs;
            attnamecopy = pg_strdup(fmtId(attname));
 
            /*
@@ -15736,7 +15477,7 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
            dumpACL(fout, tbinfo->dobj.dumpId, tableAclDumpId,
                    "TABLE", namecopy, attnamecopy,
                    tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
-                   attacl, rattacl, initattacl, initrattacl);
+                   &coldacl);
            free(attnamecopy);
        }
        PQclear(res);
index 6e9a76103c87dd001cb62161fea9532569763a78..b21b91f8bc9cba766059d95a95d7bee818d9f37c 100644 (file)
@@ -146,16 +146,36 @@ typedef struct _dumpableObject
    int         allocDeps;      /* allocated size of dependencies[] */
 } DumpableObject;
 
+/*
+ * Object types that have ACLs must store them in a DumpableAcl sub-struct,
+ * which must immediately follow the DumpableObject base struct.
+ *
+ * Note: when dumping from a pre-9.2 server, which lacks the acldefault()
+ * function, acldefault will be NULL or empty.
+ */
+typedef struct _dumpableAcl
+{
+   char       *acl;            /* the object's actual ACL string */
+   char       *acldefault;     /* default ACL for the object's type & owner */
+   /* these fields come from the object's pg_init_privs entry, if any: */
+   char        privtype;       /* entry type, 'i' or 'e'; 0 if no entry */
+   char       *initprivs;      /* the object's initial ACL string, or NULL */
+} DumpableAcl;
+
+/* Generic struct that can be used to access any object type having an ACL */
+typedef struct _dumpableObjectWithAcl
+{
+   DumpableObject dobj;
+   DumpableAcl dacl;
+} DumpableObjectWithAcl;
+
 typedef struct _namespaceInfo
 {
    DumpableObject dobj;
+   DumpableAcl dacl;
    bool        create;         /* CREATE SCHEMA, or just set owner? */
    Oid         nspowner;
    char       *rolname;        /* name of owner, or empty string */
-   char       *nspacl;
-   char       *rnspacl;
-   char       *initnspacl;
-   char       *initrnspacl;
 } NamespaceInfo;
 
 typedef struct _extensionInfo
@@ -171,6 +191,7 @@ typedef struct _extensionInfo
 typedef struct _typeInfo
 {
    DumpableObject dobj;
+   DumpableAcl dacl;
 
    /*
     * Note: dobj.name is the raw pg_type.typname entry.  ftypname is the
@@ -179,10 +200,6 @@ typedef struct _typeInfo
     */
    char       *ftypname;
    char       *rolname;        /* name of owner, or empty string */
-   char       *typacl;
-   char       *rtypacl;
-   char       *inittypacl;
-   char       *initrtypacl;
    Oid         typelem;
    Oid         typrelid;
    char        typrelkind;     /* 'r', 'v', 'c', etc */
@@ -207,15 +224,12 @@ typedef struct _shellTypeInfo
 typedef struct _funcInfo
 {
    DumpableObject dobj;
+   DumpableAcl dacl;
    char       *rolname;        /* name of owner, or empty string */
    Oid         lang;
    int         nargs;
    Oid        *argtypes;
    Oid         prorettype;
-   char       *proacl;
-   char       *rproacl;
-   char       *initproacl;
-   char       *initrproacl;
 } FuncInfo;
 
 /* AggInfo is a superset of FuncInfo */
@@ -270,11 +284,8 @@ typedef struct _tableInfo
     * These fields are collected for every table in the database.
     */
    DumpableObject dobj;
+   DumpableAcl dacl;
    char       *rolname;        /* name of owner, or empty string */
-   char       *relacl;
-   char       *rrelacl;
-   char       *initrelacl;
-   char       *initrrelacl;
    char        relkind;
    char        relpersistence; /* relation persistence */
    bool        relispopulated; /* relation is populated */
@@ -286,6 +297,7 @@ typedef struct _tableInfo
    bool        hasindex;       /* does it have any indexes? */
    bool        hasrules;       /* does it have any rules? */
    bool        hastriggers;    /* does it have any triggers? */
+   bool        hascolumnACLs;  /* do any columns have non-default ACLs? */
    bool        rowsec;         /* is row security enabled? */
    bool        forcerowsec;    /* is row security forced? */
    bool        hasoids;        /* does it have OIDs? */
@@ -478,14 +490,11 @@ typedef struct _constraintInfo
 typedef struct _procLangInfo
 {
    DumpableObject dobj;
+   DumpableAcl dacl;
    bool        lanpltrusted;
    Oid         lanplcallfoid;
    Oid         laninline;
    Oid         lanvalidator;
-   char       *lanacl;
-   char       *rlanacl;
-   char       *initlanacl;
-   char       *initrlanacl;
    char       *lanowner;       /* name of owner, or empty string */
 } ProcLangInfo;
 
@@ -550,49 +559,37 @@ typedef struct _cfgInfo
 typedef struct _fdwInfo
 {
    DumpableObject dobj;
+   DumpableAcl dacl;
    char       *rolname;
    char       *fdwhandler;
    char       *fdwvalidator;
    char       *fdwoptions;
-   char       *fdwacl;
-   char       *rfdwacl;
-   char       *initfdwacl;
-   char       *initrfdwacl;
 } FdwInfo;
 
 typedef struct _foreignServerInfo
 {
    DumpableObject dobj;
+   DumpableAcl dacl;
    char       *rolname;
    Oid         srvfdw;
    char       *srvtype;
    char       *srvversion;
-   char       *srvacl;
-   char       *rsrvacl;
-   char       *initsrvacl;
-   char       *initrsrvacl;
    char       *srvoptions;
 } ForeignServerInfo;
 
 typedef struct _defaultACLInfo
 {
    DumpableObject dobj;
+   DumpableAcl dacl;
    char       *defaclrole;
    char        defaclobjtype;
-   char       *defaclacl;
-   char       *rdefaclacl;
-   char       *initdefaclacl;
-   char       *initrdefaclacl;
 } DefaultACLInfo;
 
 typedef struct _blobInfo
 {
    DumpableObject dobj;
+   DumpableAcl dacl;
    char       *rolname;
-   char       *blobacl;
-   char       *rblobacl;
-   char       *initblobacl;
-   char       *initrblobacl;
 } BlobInfo;
 
 /*
index c29101704a54586f43a24c9ae38af32f1ee9b801..44114f3f71de8093bb3cde2e8a612e5e2e8a1516 100644 (file)
@@ -1166,55 +1166,12 @@ dumpTablespaces(PGconn *conn)
    /*
     * Get all tablespaces except built-in ones (which we assume are named
     * pg_xxx)
-    *
-    * For the tablespace ACLs, as of 9.6, we extract both the positive (as
-    * spcacl) and negative (as rspcacl) ACLs, relative to the default ACL for
-    * tablespaces, which are then passed to buildACLCommands() below.
-    *
-    * See buildACLQueries() and buildACLCommands().
-    *
-    * The order in which privileges are in the ACL string (the order they
-    * have been GRANT'd in, which the backend maintains) must be preserved to
-    * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on
-    * those are dumped in the correct order.
-    *
-    * Note that we do not support initial privileges (pg_init_privs) on
-    * tablespaces, so this logic cannot make use of buildACLQueries().
     */
-   if (server_version >= 90600)
-       res = executeQuery(conn, "SELECT oid, spcname, "
-                          "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
-                          "pg_catalog.pg_tablespace_location(oid), "
-                          "(SELECT array_agg(acl ORDER BY row_n) FROM "
-                          "  (SELECT acl, row_n FROM "
-                          "     unnest(coalesce(spcacl,acldefault('t',spcowner))) "
-                          "     WITH ORDINALITY AS perm(acl,row_n) "
-                          "   WHERE NOT EXISTS ( "
-                          "     SELECT 1 "
-                          "     FROM unnest(acldefault('t',spcowner)) "
-                          "       AS init(init_acl) "
-                          "     WHERE acl = init_acl)) AS spcacls) "
-                          " AS spcacl, "
-                          "(SELECT array_agg(acl ORDER BY row_n) FROM "
-                          "  (SELECT acl, row_n FROM "
-                          "     unnest(acldefault('t',spcowner)) "
-                          "     WITH ORDINALITY AS initp(acl,row_n) "
-                          "   WHERE NOT EXISTS ( "
-                          "     SELECT 1 "
-                          "     FROM unnest(coalesce(spcacl,acldefault('t',spcowner))) "
-                          "       AS permp(orig_acl) "
-                          "     WHERE acl = orig_acl)) AS rspcacls) "
-                          " AS rspcacl, "
-                          "array_to_string(spcoptions, ', '),"
-                          "pg_catalog.shobj_description(oid, 'pg_tablespace') "
-                          "FROM pg_catalog.pg_tablespace "
-                          "WHERE spcname !~ '^pg_' "
-                          "ORDER BY 1");
-   else if (server_version >= 90200)
+   if (server_version >= 90200)
        res = executeQuery(conn, "SELECT oid, spcname, "
                           "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
                           "pg_catalog.pg_tablespace_location(oid), "
-                          "spcacl, '' as rspcacl, "
+                          "spcacl, acldefault('t', spcowner) AS acldefault, "
                           "array_to_string(spcoptions, ', '),"
                           "pg_catalog.shobj_description(oid, 'pg_tablespace') "
                           "FROM pg_catalog.pg_tablespace "
@@ -1223,7 +1180,7 @@ dumpTablespaces(PGconn *conn)
    else if (server_version >= 90000)
        res = executeQuery(conn, "SELECT oid, spcname, "
                           "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
-                          "spclocation, spcacl, '' as rspcacl, "
+                          "spclocation, spcacl, NULL AS acldefault, "
                           "array_to_string(spcoptions, ', '),"
                           "pg_catalog.shobj_description(oid, 'pg_tablespace') "
                           "FROM pg_catalog.pg_tablespace "
@@ -1232,7 +1189,7 @@ dumpTablespaces(PGconn *conn)
    else if (server_version >= 80200)
        res = executeQuery(conn, "SELECT oid, spcname, "
                           "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
-                          "spclocation, spcacl, '' as rspcacl, null, "
+                          "spclocation, spcacl, NULL AS acldefault, null, "
                           "pg_catalog.shobj_description(oid, 'pg_tablespace') "
                           "FROM pg_catalog.pg_tablespace "
                           "WHERE spcname !~ '^pg_' "
@@ -1240,7 +1197,7 @@ dumpTablespaces(PGconn *conn)
    else
        res = executeQuery(conn, "SELECT oid, spcname, "
                           "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
-                          "spclocation, spcacl, '' as rspcacl, "
+                          "spclocation, spcacl, NULL AS acldefault, "
                           "null, null "
                           "FROM pg_catalog.pg_tablespace "
                           "WHERE spcname !~ '^pg_' "
@@ -1257,7 +1214,7 @@ dumpTablespaces(PGconn *conn)
        char       *spcowner = PQgetvalue(res, i, 2);
        char       *spclocation = PQgetvalue(res, i, 3);
        char       *spcacl = PQgetvalue(res, i, 4);
-       char       *rspcacl = PQgetvalue(res, i, 5);
+       char       *acldefault = PQgetvalue(res, i, 5);
        char       *spcoptions = PQgetvalue(res, i, 6);
        char       *spccomment = PQgetvalue(res, i, 7);
        char       *fspcname;
@@ -1276,9 +1233,11 @@ dumpTablespaces(PGconn *conn)
            appendPQExpBuffer(buf, "ALTER TABLESPACE %s SET (%s);\n",
                              fspcname, spcoptions);
 
+       /* tablespaces can't have initprivs */
+
        if (!skip_acls &&
            !buildACLCommands(fspcname, NULL, NULL, "TABLESPACE",
-                             spcacl, rspcacl,
+                             spcacl, acldefault,
                              spcowner, "", server_version, buf))
        {
            pg_log_error("could not parse ACL list (%s) for tablespace \"%s\"",
index 3efee4e7eed7f4c5c94e74182151ca4a07d5fa6c..81e623602ec5bedf726af9d6d9ccc61e92a9b597 100644 (file)
@@ -726,6 +726,69 @@ parsePGArray(const char *atext, char ***itemarray, int *nitems)
 }
 
 
+/*
+ * Append one element to the text representation of a 1-dimensional Postgres
+ * array.
+ *
+ * The caller must provide the initial '{' and closing '}' of the array.
+ * This function handles all else, including insertion of commas and
+ * quoting of values.
+ *
+ * We assume that typdelim is ','.
+ */
+void
+appendPGArray(PQExpBuffer buffer, const char *value)
+{
+   bool        needquote;
+   const char *tmp;
+
+   if (buffer->data[buffer->len - 1] != '{')
+       appendPQExpBufferChar(buffer, ',');
+
+   /* Decide if we need quotes; this should match array_out()'s choices. */
+   if (value[0] == '\0')
+       needquote = true;       /* force quotes for empty string */
+   else if (pg_strcasecmp(value, "NULL") == 0)
+       needquote = true;       /* force quotes for literal NULL */
+   else
+       needquote = false;
+
+   if (!needquote)
+   {
+       for (tmp = value; *tmp; tmp++)
+       {
+           char        ch = *tmp;
+
+           if (ch == '"' || ch == '\\' ||
+               ch == '{' || ch == '}' || ch == ',' ||
+           /* these match array_isspace(): */
+               ch == ' ' || ch == '\t' || ch == '\n' ||
+               ch == '\r' || ch == '\v' || ch == '\f')
+           {
+               needquote = true;
+               break;
+           }
+       }
+   }
+
+   if (needquote)
+   {
+       appendPQExpBufferChar(buffer, '"');
+       for (tmp = value; *tmp; tmp++)
+       {
+           char        ch = *tmp;
+
+           if (ch == '"' || ch == '\\')
+               appendPQExpBufferChar(buffer, '\\');
+           appendPQExpBufferChar(buffer, ch);
+       }
+       appendPQExpBufferChar(buffer, '"');
+   }
+   else
+       appendPQExpBufferStr(buffer, value);
+}
+
+
 /*
  * Format a reloptions array and append it to the given buffer.
  *
index caafb97d2937e75e1f92db806cb257ec676246c3..e12e61cddb314b51f333db7167879f9905f1632d 100644 (file)
@@ -46,6 +46,7 @@ extern void appendConnStrVal(PQExpBuffer buf, const char *str);
 extern void appendPsqlMetaConnect(PQExpBuffer buf, const char *dbname);
 
 extern bool parsePGArray(const char *atext, char ***itemarray, int *nitems);
+extern void appendPGArray(PQExpBuffer buffer, const char *value);
 
 extern bool appendReloptionsArray(PQExpBuffer buffer, const char *reloptions,
                                  const char *prefix, int encoding, bool std_strings);