Rethink pg_dump's handling of object ACLs.
authorTom Lane <[email protected]>
Mon, 6 Dec 2021 17:39:45 +0000 (12:39 -0500)
committerTom Lane <[email protected]>
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);