Assorted improvements in pg_dump - Mailing list pgsql-hackers

From Tom Lane
Subject Assorted improvements in pg_dump
Date
Msg-id 2273648.1634764485@sss.pgh.pa.us
Whole thread Raw
List pgsql-hackers
Attached is a series of patches that address some performance and
correctness problems we've recently identified in pg_dump.

Patch 0001 refactors the way pg_dump keeps track of which "components"
(definition, data, comment, ACL, etc) of a dumpable object need to be
dumped.  The problem addressed here is that the current coding breaks
the intended optimization that we don't run pg_dump's secondary data
collection queries for objects we're not actually going to dump.
That's because we initialize DumpableObject.dump to DUMP_COMPONENT_ALL
(0xFFFF), and then we're not very careful about clearing meaningless
bits out of that.  That leads to the bitmask not being zero when it's
tested at the start of a per-object dump function such as dumpFunc(),
even though DUMP_COMPONENT_DEFINITION might be clear and the object
might not have any comment, security label, or ACL.  An example
of the problem here is that we'll end up running through dumpFunc()
for every function defined in an extension, even though the only
ones we'd print anything for are those with modified ACLs.  In a
database with a lot of extensions, that results in a lot of useless
queries and consequent performance problems [1].

There are a couple of ways we could rewrite this, but what seemed
to me to be the clearest and most robust is to separate
DumpableObject.dump into two bitmasks, one recording the components
we've requested to dump and the other recording the components we've
actually found the object to possess.  (The existing logic conflates
these purposes by setting and later clearing bits, which I think is
confusing and also bug-prone.)  Then, when we reach the point where we
need to decide if there's anything to do, we can AND those two masks
together to derive the set of things we are really going to dump.

So the patch adds a "components" field with the same bits that "dump"
has, and arranges to set appropriate bits of that field as we discover
that objects have comments, ACLs, etc.  (I wonder if we should rename
"dump", perhaps to "requests" or the like.  But I didn't do so here.)
It's straightforward other than that, with just a few things maybe
worthy of note:
1. collectComments and collectSecLabels are now run unconditionally,
not on-demand, because we need those bit-setting actions to happen
before we start dumping objects.
2. I had to add collection of pg_class.reltype so that when we find
a comment or seclabel for a composite type's column, we can redirect
the bit-setting action from the pg_class entry to the pg_type entry.
3. Testing for no-components-to-dump is now centralized in
dumpDumpableObject(), instead of each dumpXXX function doing it.


Patch 0002 rethinks the way we handle dumping of ACLs.   My main
goal here was to get rid of the expensive sub-selects that the
pg_init_privs patch added.  The core idea is to drop all of those
in favor of just reading pg_init_privs once and loading the
information into the DumpableObjects, much the same way as we
handle comments and seclabels.  I also realized that that patch's
insistence on doing assembly/disassembly of ACLs in SQL was a big
performance loser.  We can calculate the delta between two ACLs
right in buildACLCommands, just by doing string comparisons after
we've disassembled the aclitems array into elements, so it's really
pretty cheap.

This also led me to the conclusion that it was a bad idea to have
preserved the "old" (pre-9.6) logic in buildACLCommands.  The new
approach of calculating "add" and "remove" lists as deltas from an
acldefault() value works perfectly well before 9.6, at least for
branches back to 9.2 where acldefault() was added.  For older servers,
I thought briefly about putting hard-wired knowledge of the older
branches' default ACLs into dumputils.c, but rejected that in favor of
emitting REVOKE ALL and then emitting all the ACL's items when we are
lacking acldefault() results.  This turns out to be just as efficient,
and sometimes more so, as what the old code was doing.  For example,
comparing pg_dump of the 9.1 regression database between old and new
logic, the only difference is

@@ -60,9 +60,7 @@
 -- Name: SCHEMA public; Type: ACL; Schema: -; Owner: postgres
 --

-REVOKE ALL ON SCHEMA public FROM PUBLIC;
-REVOKE ALL ON SCHEMA public FROM postgres;
-GRANT ALL ON SCHEMA public TO postgres;
+REVOKE USAGE ON SCHEMA public FROM PUBLIC;
 GRANT ALL ON SCHEMA public TO PUBLIC;

for a net savings of two commands.  The REVOKE USAGE appears because
of the hacks Noah added in a7a7be1f2 and b073c3ccd to cause pg_dump
to believe that schema public has a specific initprivs value even
when it doesn't.  In HEAD, that code only triggers for dumps from
servers >= 9.6, but this patch makes it apply before that too, which
seems correct to me.  A target server >= v15 is going to have that
ACL for the public schema, no matter what the source version is.

(BTW, to replicate that behavior I ended up having to write some
client-side functions to construct the text form of an aclitems array.
The duplication of logic with aclitemout() is a bit annoying.  Not
sure if it's worth trying to refactor to combine code.)

0002 does not yet incorporate any logic change corresponding to
the bug fix under discussion at [2], but it will be easy to add.
I left that out for now so as not to change any test results.


Lastly, patch 0003 addresses the concern I raised at [3] that it's
unsafe to call pg_get_partkeydef() and pg_get_expr(relpartbound)
in getTables().  Looking closer I realized that we can't cast
pg_class.reloftype to regtype at that point either, since regtypeout
is going to notice if the type has been concurrently dropped.

In [3] I'd imagined that we could just delay those calls to a second
query in getTables(), but that doesn't work at all: if we apply
these functions to every row of pg_class, we still risk failure
against any relation that we didn't lock.  So there seems little
alternative but to push these functions out to secondary queries
executed later.

Arguably, 0003 is a bug fix that we should consider back-patching.
However, I've not heard field reports of the problems it fixes,
so maybe there's no need to bother.


Putting all of this together, I did some performance measurements
comparing HEAD pg_dump to pg_dump with these three patches.  On
my machine, using the current regression database as a test case,
I find that "pg_dump -s regression >/dev/null" requires 1.21 sec
on HEAD and 0.915 sec with these patches, or about 24% faster.
Even more importantly, the time to execute getTables' query drops
from 129 ms to 16 ms.  Since that's the window before we can start
to acquire table locks, making it as short as possible is useful.

To have another data point, I also experimented with a database
constructed like this:

do $$
begin
  for i in 1..2500 loop
    execute 'create table tst' || i || ' (f1 int primary key, f2 text)';
  end loop;
end $$;

For that, "pg_dump -s" required 4.94 sec vs 4.38 sec, or about 11%
faster, and the getTables query dropped from 316 ms to 41 ms.

There's some other micro-optimizations I'm thinking about, but this
probably gets most of the available win, and it seems like a coherent
set of changes to present at once.

            regards, tom lane

[1] https://www.postgresql.org/message-id/1414363.1630341759%40sss.pgh.pa.us
[2]
https://www.postgresql.org/message-id/flat/CAA3qoJnr2%2B1dVJObNtfec%3DqW4Z0nz%3DA9%2Br5bZKoTSy5RDjskMw%40mail.gmail.com
[3] https://www.postgresql.org/message-id/1462940.1634496313%40sss.pgh.pa.us

diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 1f24e79665..7682188a68 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -609,6 +609,8 @@ AssignDumpId(DumpableObject *dobj)
     dobj->namespace = NULL;        /* may be set later */
     dobj->dump = DUMP_COMPONENT_ALL;    /* default assumption */
     dobj->dump_contains = DUMP_COMPONENT_ALL;    /* default assumption */
+    /* All objects have definitions; we may set more components bits later */
+    dobj->components = DUMP_COMPONENT_DEFINITION;
     dobj->ext_member = false;    /* default assumption */
     dobj->depends_on_ext = false;    /* default assumption */
     dobj->dependencies = NULL;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index ed8ed2f266..29c631c8c3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -134,6 +134,14 @@ static const CatalogId nilCatalogId = {0, 0};
 static bool have_extra_float_digits = false;
 static int    extra_float_digits;

+/* sorted table of comments */
+static CommentItem *comments = NULL;
+static int    ncomments = 0;
+
+/* sorted table of security labels */
+static SecLabelItem *seclabels = NULL;
+static int    nseclabels = 0;
+
 /*
  * The default number of rows per INSERT when
  * --inserts is specified without --rows-per-insert
@@ -182,14 +190,14 @@ static inline void dumpComment(Archive *fout, const char *type,
                                int subid, DumpId dumpId);
 static int    findComments(Archive *fout, Oid classoid, Oid objoid,
                          CommentItem **items);
-static int    collectComments(Archive *fout, CommentItem **items);
+static void collectComments(Archive *fout);
 static void dumpSecLabel(Archive *fout, const char *type, const char *name,
                          const char *namespace, const char *owner,
                          CatalogId catalogId, int subid, DumpId dumpId);
 static int    findSecLabels(Archive *fout, Oid classoid, Oid objoid,
                           SecLabelItem **items);
-static int    collectSecLabels(Archive *fout, SecLabelItem **items);
-static void dumpDumpableObject(Archive *fout, const DumpableObject *dobj);
+static void collectSecLabels(Archive *fout);
+static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
 static void dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo);
 static void dumpExtension(Archive *fout, const ExtensionInfo *extinfo);
 static void dumpType(Archive *fout, const TypeInfo *tyinfo);
@@ -290,8 +298,9 @@ static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
                                                      Oid pg_type_oid,
                                                      bool force_array_type,
                                                      bool include_multirange_type);
-static void binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
-                                                    PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
+static void binary_upgrade_set_type_oids_by_rel(Archive *fout,
+                                                PQExpBuffer upgrade_buffer,
+                                                const TableInfo *tbinfo);
 static void binary_upgrade_set_pg_class_oids(Archive *fout,
                                              PQExpBuffer upgrade_buffer,
                                              Oid pg_class_oid, bool is_index);
@@ -878,6 +887,14 @@ main(int argc, char **argv)
      */
     getDependencies(fout);

+    /*
+     * Collect comments and security labels, if wanted.
+     */
+    if (!dopt.no_comments)
+        collectComments(fout);
+    if (!dopt.no_security_labels)
+        collectSecLabels(fout);
+
     /* Lastly, create dummy objects to represent the section boundaries */
     boundaryObjs = createBoundaryObjects();

@@ -1631,6 +1648,13 @@ selectDumpableNamespace(NamespaceInfo *nsinfo, Archive *fout)
         if (nsinfo->nspowner == ROLE_PG_DATABASE_OWNER)
             nsinfo->dobj.dump &= ~DUMP_COMPONENT_DEFINITION;
         nsinfo->dobj.dump_contains = DUMP_COMPONENT_ALL;
+
+        /*
+         * Also, make like it has a comment even if it doesn't; this is so
+         * that we'll emit a command to drop the comment, if appropriate.
+         * (Without this, we'd not call dumpCommentExtended for it.)
+         */
+        nsinfo->dobj.components |= DUMP_COMPONENT_COMMENT;
     }
     else
         nsinfo->dobj.dump_contains = nsinfo->dobj.dump = DUMP_COMPONENT_ALL;
@@ -2471,7 +2495,7 @@ getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind)
  * Make a dumpable object for the data of this specific table
  *
  * Note: we make a TableDataInfo if and only if we are going to dump the
- * table data; the "dump" flag in such objects isn't used.
+ * table data; the "dump" field in such objects isn't very interesting.
  */
 static void
 makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
@@ -2531,6 +2555,9 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
     tdinfo->filtercond = NULL;    /* might get set later */
     addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);

+    /* A TableDataInfo contains data, of course */
+    tdinfo->dobj.components |= DUMP_COMPONENT_DATA;
+
     tbinfo->dataObj = tdinfo;

     /* Make sure that we'll collect per-column info for this table. */
@@ -3491,11 +3518,15 @@ getBlobs(Archive *fout)
         binfo[i].initblobacl = pg_strdup(PQgetvalue(res, i, i_initlomacl));
         binfo[i].initrblobacl = pg_strdup(PQgetvalue(res, i, i_initrlomacl));

-        if (PQgetisnull(res, i, i_lomacl) &&
-            PQgetisnull(res, i, i_rlomacl) &&
-            PQgetisnull(res, i, i_initlomacl) &&
-            PQgetisnull(res, i, i_initrlomacl))
-            binfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+        /* 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)))
+            binfo[i].dobj.components |= DUMP_COMPONENT_ACL;

         /*
          * In binary-upgrade mode for blobs, we do *not* dump out the blob
@@ -3519,6 +3550,7 @@ getBlobs(Archive *fout)
         bdata->catId = nilCatalogId;
         AssignDumpId(bdata);
         bdata->name = pg_strdup("BLOBS");
+        bdata->components |= DUMP_COMPONENT_DATA;
     }

     PQclear(res);
@@ -3566,7 +3598,7 @@ dumpBlob(Archive *fout, const BlobInfo *binfo)
                      binfo->dobj.catId, 0, binfo->dobj.dumpId);

     /* Dump ACL if any */
-    if (binfo->blobacl && (binfo->dobj.dump & DUMP_COMPONENT_ACL))
+    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,
@@ -3697,6 +3729,8 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)

         if (tbinfo->rowsec)
         {
+            tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
+
             /*
              * Note: use tableoid 0 so that this object won't be mistaken for
              * something that pg_depend entries apply to.
@@ -3766,6 +3800,8 @@ getPolicies(Archive *fout, TableInfo tblinfo[], int numTables)
             if (!(tbinfo->dobj.dump & DUMP_COMPONENT_POLICY))
                 continue;

+            tbinfo->dobj.components |= DUMP_COMPONENT_POLICY;
+
             polinfo[j].dobj.objType = DO_POLICY;
             polinfo[j].dobj.catId.tableoid =
                 atooid(PQgetvalue(res, j, i_tableoid));
@@ -3818,6 +3854,7 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
     const char *cmd;
     char       *tag;

+    /* Do nothing in data-only dump */
     if (dopt->dataOnly)
         return;

@@ -3838,7 +3875,7 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
          * explicitly, because it will not match anything in pg_depend (unlike
          * the case for other PolicyInfo objects).
          */
-        if (polinfo->dobj.dump & DUMP_COMPONENT_POLICY)
+        if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
             ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
                          ARCHIVE_OPTS(.tag = polinfo->dobj.name,
                                       .namespace = polinfo->dobj.namespace->dobj.name,
@@ -3900,7 +3937,7 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo)

     tag = psprintf("%s %s", tbinfo->dobj.name, polinfo->dobj.name);

-    if (polinfo->dobj.dump & DUMP_COMPONENT_POLICY)
+    if (polinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
         ArchiveEntry(fout, polinfo->dobj.catId, polinfo->dobj.dumpId,
                      ARCHIVE_OPTS(.tag = tag,
                                   .namespace = polinfo->dobj.namespace->dobj.name,
@@ -4045,9 +4082,6 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
     char       *qpubname;
     bool        first = true;

-    if (!(pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
-        return;
-
     delq = createPQExpBuffer();
     query = createPQExpBuffer();

@@ -4103,13 +4137,14 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo)

     appendPQExpBufferStr(query, ");\n");

-    ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
-                 ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
-                              .owner = pubinfo->rolname,
-                              .description = "PUBLICATION",
-                              .section = SECTION_POST_DATA,
-                              .createStmt = query->data,
-                              .dropStmt = delq->data));
+    if (pubinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+        ArchiveEntry(fout, pubinfo->dobj.catId, pubinfo->dobj.dumpId,
+                     ARCHIVE_OPTS(.tag = pubinfo->dobj.name,
+                                  .owner = pubinfo->rolname,
+                                  .description = "PUBLICATION",
+                                  .section = SECTION_POST_DATA,
+                                  .createStmt = query->data,
+                                  .dropStmt = delq->data));

     if (pubinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
         dumpComment(fout, "PUBLICATION", qpubname,
@@ -4225,9 +4260,6 @@ dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
     PQExpBuffer query;
     char       *tag;

-    if (!(pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
-        return;
-
     tag = psprintf("%s %s", pubinfo->dobj.name, tbinfo->dobj.name);

     query = createPQExpBuffer();
@@ -4244,13 +4276,14 @@ dumpPublicationTable(Archive *fout, const PublicationRelInfo *pubrinfo)
      * owner field anyway to ensure that the command is run by the correct
      * role at restore time.
      */
-    ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
-                 ARCHIVE_OPTS(.tag = tag,
-                              .namespace = tbinfo->dobj.namespace->dobj.name,
-                              .owner = pubinfo->rolname,
-                              .description = "PUBLICATION TABLE",
-                              .section = SECTION_POST_DATA,
-                              .createStmt = query->data));
+    if (pubrinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+        ArchiveEntry(fout, pubrinfo->dobj.catId, pubrinfo->dobj.dumpId,
+                     ARCHIVE_OPTS(.tag = tag,
+                                  .namespace = tbinfo->dobj.namespace->dobj.name,
+                                  .owner = pubinfo->rolname,
+                                  .description = "PUBLICATION TABLE",
+                                  .section = SECTION_POST_DATA,
+                                  .createStmt = query->data));

     free(tag);
     destroyPQExpBuffer(query);
@@ -4420,9 +4453,6 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
     int            i;
     char        two_phase_disabled[] = {LOGICALREP_TWOPHASE_STATE_DISABLED, '\0'};

-    if (!(subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
-        return;
-
     delq = createPQExpBuffer();
     query = createPQExpBuffer();

@@ -4468,13 +4498,14 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)

     appendPQExpBufferStr(query, ");\n");

-    ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
-                 ARCHIVE_OPTS(.tag = subinfo->dobj.name,
-                              .owner = subinfo->rolname,
-                              .description = "SUBSCRIPTION",
-                              .section = SECTION_POST_DATA,
-                              .createStmt = query->data,
-                              .dropStmt = delq->data));
+    if (subinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+        ArchiveEntry(fout, subinfo->dobj.catId, subinfo->dobj.dumpId,
+                     ARCHIVE_OPTS(.tag = subinfo->dobj.name,
+                                  .owner = subinfo->rolname,
+                                  .description = "SUBSCRIPTION",
+                                  .section = SECTION_POST_DATA,
+                                  .createStmt = query->data,
+                                  .dropStmt = delq->data));

     if (subinfo->dobj.dump & DUMP_COMPONENT_COMMENT)
         dumpComment(fout, "SUBSCRIPTION", qsubname,
@@ -4662,30 +4693,15 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
 }

 static void
-binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
-                                        PQExpBuffer upgrade_buffer,
-                                        Oid pg_rel_oid)
+binary_upgrade_set_type_oids_by_rel(Archive *fout,
+                                    PQExpBuffer upgrade_buffer,
+                                    const TableInfo *tbinfo)
 {
-    PQExpBuffer upgrade_query = createPQExpBuffer();
-    PGresult   *upgrade_res;
-    Oid            pg_type_oid;
-
-    appendPQExpBuffer(upgrade_query,
-                      "SELECT c.reltype AS crel "
-                      "FROM pg_catalog.pg_class c "
-                      "WHERE c.oid = '%u'::pg_catalog.oid;",
-                      pg_rel_oid);
-
-    upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
-
-    pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
+    Oid            pg_type_oid = tbinfo->reltype;

     if (OidIsValid(pg_type_oid))
         binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
                                                  pg_type_oid, false, false);
-
-    PQclear(upgrade_res);
-    destroyPQExpBuffer(upgrade_query);
 }

 static void
@@ -4940,18 +4956,12 @@ getNamespaces(Archive *fout, int *numNamespaces)
         /* Decide whether to dump this namespace */
         selectDumpableNamespace(&nsinfo[i], fout);

-        /*
-         * Do not try to dump ACL if the ACL is empty or the default.
-         *
-         * This is useful because, for some schemas/objects, the only
-         * component we are going to try and dump is the ACL and if we can
-         * remove that then 'dump' goes to zero/false and we don't consider
-         * this object for dumping at all later on.
-         */
-        if (PQgetisnull(res, i, i_nspacl) && PQgetisnull(res, i, i_rnspacl) &&
-            PQgetisnull(res, i, i_initnspacl) &&
-            PQgetisnull(res, i, i_initrnspacl))
-            nsinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+        /* 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)))
+            nsinfo[i].dobj.components |= DUMP_COMPONENT_ACL;

         if (strlen(nsinfo[i].rolname) == 0)
             pg_log_warning("owner of schema \"%s\" appears to be invalid",
@@ -5260,11 +5270,12 @@ getTypes(Archive *fout, int *numTypes)
         /* Decide whether we want to dump it */
         selectDumpableType(&tyinfo[i], fout);

-        /* Do not try to dump ACL if no ACL exists. */
-        if (PQgetisnull(res, i, i_typacl) && PQgetisnull(res, i, i_rtypacl) &&
-            PQgetisnull(res, i, i_inittypacl) &&
-            PQgetisnull(res, i, i_initrtypacl))
-            tyinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+        /* 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)))
+            tyinfo[i].dobj.components |= DUMP_COMPONENT_ACL;

         /*
          * If it's a domain, fetch info about its constraints, if any
@@ -5387,9 +5398,6 @@ getOperators(Archive *fout, int *numOprs)
         /* Decide whether we want to dump it */
         selectDumpableObject(&(oprinfo[i].dobj), fout);

-        /* Operators do not currently have ACLs. */
-        oprinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
-
         if (strlen(oprinfo[i].rolname) == 0)
             pg_log_warning("owner of operator \"%s\" appears to be invalid",
                            oprinfo[i].dobj.name);
@@ -5469,9 +5477,6 @@ getCollations(Archive *fout, int *numCollations)

         /* Decide whether we want to dump it */
         selectDumpableObject(&(collinfo[i].dobj), fout);
-
-        /* Collations do not currently have ACLs. */
-        collinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -5541,9 +5546,6 @@ getConversions(Archive *fout, int *numConversions)

         /* Decide whether we want to dump it */
         selectDumpableObject(&(convinfo[i].dobj), fout);
-
-        /* Conversions do not currently have ACLs. */
-        convinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -5614,9 +5616,6 @@ getAccessMethods(Archive *fout, int *numAccessMethods)

         /* Decide whether we want to dump it */
         selectDumpableAccessMethod(&(aminfo[i]), fout);
-
-        /* Access methods do not currently have ACLs. */
-        aminfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -5686,9 +5685,6 @@ getOpclasses(Archive *fout, int *numOpclasses)
         /* Decide whether we want to dump it */
         selectDumpableObject(&(opcinfo[i].dobj), fout);

-        /* Op Classes do not currently have ACLs. */
-        opcinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
-
         if (strlen(opcinfo[i].rolname) == 0)
             pg_log_warning("owner of operator class \"%s\" appears to be invalid",
                            opcinfo[i].dobj.name);
@@ -5769,9 +5765,6 @@ getOpfamilies(Archive *fout, int *numOpfamilies)
         /* Decide whether we want to dump it */
         selectDumpableObject(&(opfinfo[i].dobj), fout);

-        /* Extensions do not currently have ACLs. */
-        opfinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
-
         if (strlen(opfinfo[i].rolname) == 0)
             pg_log_warning("owner of operator family \"%s\" appears to be invalid",
                            opfinfo[i].dobj.name);
@@ -5963,11 +5956,12 @@ getAggregates(Archive *fout, int *numAggs)
         /* Decide whether we want to dump it */
         selectDumpableObject(&(agginfo[i].aggfn.dobj), fout);

-        /* Do not try to dump ACL if no ACL exists. */
-        if (PQgetisnull(res, i, i_aggacl) && PQgetisnull(res, i, i_raggacl) &&
-            PQgetisnull(res, i, i_initaggacl) &&
-            PQgetisnull(res, i, i_initraggacl))
-            agginfo[i].aggfn.dobj.dump &= ~DUMP_COMPONENT_ACL;
+        /* 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)))
+            agginfo[i].aggfn.dobj.components |= DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -6193,11 +6187,12 @@ getFuncs(Archive *fout, int *numFuncs)
         /* Decide whether we want to dump it */
         selectDumpableObject(&(finfo[i].dobj), fout);

-        /* Do not try to dump ACL if no ACL exists. */
-        if (PQgetisnull(res, i, i_proacl) && PQgetisnull(res, i, i_rproacl) &&
-            PQgetisnull(res, i, i_initproacl) &&
-            PQgetisnull(res, i, i_initrproacl))
-            finfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+        /* 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)))
+            finfo[i].dobj.components |= DUMP_COMPONENT_ACL;

         if (strlen(finfo[i].rolname) == 0)
             pg_log_warning("owner of function \"%s\" appears to be invalid",
@@ -6232,6 +6227,7 @@ getTables(Archive *fout, int *numTables)
     int            i_relname;
     int            i_relnamespace;
     int            i_relkind;
+    int            i_reltype;
     int            i_rolname;
     int            i_relchecks;
     int            i_relhasindex;
@@ -6282,7 +6278,7 @@ getTables(Archive *fout, int *numTables)

     appendPQExpBuffer(query,
                       "SELECT c.tableoid, c.oid, c.relname, "
-                      "c.relnamespace, c.relkind, "
+                      "c.relnamespace, c.relkind, c.reltype, "
                       "(%s c.relowner) AS rolname, "
                       "c.relchecks, "
                       "c.relhasindex, c.relhasrules, c.relpages, "
@@ -6558,6 +6554,7 @@ getTables(Archive *fout, int *numTables)
     i_relname = PQfnumber(res, "relname");
     i_relnamespace = PQfnumber(res, "relnamespace");
     i_relkind = PQfnumber(res, "relkind");
+    i_reltype = PQfnumber(res, "reltype");
     i_rolname = PQfnumber(res, "rolname");
     i_relchecks = PQfnumber(res, "relchecks");
     i_relhasindex = PQfnumber(res, "relhasindex");
@@ -6619,6 +6616,7 @@ getTables(Archive *fout, int *numTables)
         tblinfo[i].dobj.namespace =
             findNamespace(atooid(PQgetvalue(res, i, i_relnamespace)));
         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));
         tblinfo[i].ncheck = atoi(PQgetvalue(res, i, i_relchecks));
         tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
@@ -6681,11 +6679,30 @@ getTables(Archive *fout, int *numTables)
         else
             selectDumpableTable(&tblinfo[i], fout);

-        tblinfo[i].interesting = tblinfo[i].dobj.dump ? true : false;
+        /*
+         * Now, consider the table "interesting" if we need to dump its
+         * definition or its data.  Later on, we'll skip a lot of data
+         * collection for uninteresting tables.
+         *
+         * Note: the "interesting" flag will also be set by flagInhTables for
+         * parents of interesting tables, so that we collect necessary
+         * inheritance info even when the parents are not themselves being
+         * dumped.  This is the main reason why we need an "interesting" flag
+         * that's separate from the components-to-dump bitmask.
+         */
+        tblinfo[i].interesting = (tblinfo[i].dobj.dump &
+                                  (DUMP_COMPONENT_DEFINITION |
+                                   DUMP_COMPONENT_DATA)) != 0;
+
         tblinfo[i].dummy_view = false;    /* might get set during sort */
         tblinfo[i].postponed_def = false;    /* might get set during sort */

+        /* 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
@@ -6694,11 +6711,12 @@ getTables(Archive *fout, int *numTables)
          * 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)
-            tblinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+        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))
+            tblinfo[i].dobj.components |= DUMP_COMPONENT_ACL;

         /*
          * Read-lock target tables to make sure they aren't DROPPED or altered
@@ -6715,10 +6733,9 @@ getTables(Archive *fout, int *numTables)
          * We only need to lock the table for certain components; see
          * pg_dump.h
          */
-        if (tblinfo[i].dobj.dump &&
+        if ((tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK) &&
             (tblinfo[i].relkind == RELKIND_RELATION ||
-             tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE) &&
-            (tblinfo[i].dobj.dump & DUMP_COMPONENTS_REQUIRING_LOCK))
+             tblinfo[i].relkind == RELKIND_PARTITIONED_TABLE))
         {
             resetPQExpBuffer(query);
             appendPQExpBuffer(query,
@@ -6901,13 +6918,9 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
             continue;

         /*
-         * Ignore indexes of tables whose definitions are not to be dumped.
-         *
-         * We also need indexes on partitioned tables which have partitions to
-         * be dumped, in order to dump the indexes on the partitions.
+         * We can ignore indexes of uninteresting tables.
          */
-        if (!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION) &&
-            !tbinfo->interesting)
+        if (!tbinfo->interesting)
             continue;

         pg_log_info("reading indexes for table \"%s.%s\"",
@@ -7275,9 +7288,6 @@ getExtendedStatistics(Archive *fout)

         /* Decide whether we want to dump it */
         selectDumpableObject(&(statsextinfo[i].dobj), fout);
-
-        /* Stats objects do not currently have ACLs. */
-        statsextinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -7967,9 +7977,6 @@ getEventTriggers(Archive *fout, int *numEventTriggers)

         /* Decide whether we want to dump it */
         selectDumpableObject(&(evtinfo[i].dobj), fout);
-
-        /* Event Triggers do not currently have ACLs. */
-        evtinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -8146,11 +8153,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
         /* Decide whether we want to dump it */
         selectDumpableProcLang(&(planginfo[i]), fout);

-        /* Do not try to dump ACL if no ACL exists. */
-        if (PQgetisnull(res, i, i_lanacl) && PQgetisnull(res, i, i_rlanacl) &&
-            PQgetisnull(res, i, i_initlanacl) &&
-            PQgetisnull(res, i, i_initrlanacl))
-            planginfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+        /* 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)))
+            planginfo[i].dobj.components |= DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -8260,9 +8268,6 @@ getCasts(Archive *fout, int *numCasts)

         /* Decide whether we want to dump it */
         selectDumpableCast(&(castinfo[i]), fout);
-
-        /* Casts do not currently have ACLs. */
-        castinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -8959,9 +8964,6 @@ getTSParsers(Archive *fout, int *numTSParsers)

         /* Decide whether we want to dump it */
         selectDumpableObject(&(prsinfo[i].dobj), fout);
-
-        /* Text Search Parsers do not currently have ACLs. */
-        prsinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -9042,9 +9044,6 @@ getTSDictionaries(Archive *fout, int *numTSDicts)

         /* Decide whether we want to dump it */
         selectDumpableObject(&(dictinfo[i].dobj), fout);
-
-        /* Text Search Dictionaries do not currently have ACLs. */
-        dictinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -9117,9 +9116,6 @@ getTSTemplates(Archive *fout, int *numTSTemplates)

         /* Decide whether we want to dump it */
         selectDumpableObject(&(tmplinfo[i].dobj), fout);
-
-        /* Text Search Templates do not currently have ACLs. */
-        tmplinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -9193,9 +9189,6 @@ getTSConfigurations(Archive *fout, int *numTSConfigs)

         /* Decide whether we want to dump it */
         selectDumpableObject(&(cfginfo[i].dobj), fout);
-
-        /* Text Search Configurations do not currently have ACLs. */
-        cfginfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -9357,11 +9350,12 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
         /* Decide whether we want to dump it */
         selectDumpableObject(&(fdwinfo[i].dobj), fout);

-        /* Do not try to dump ACL if no ACL exists. */
-        if (PQgetisnull(res, i, i_fdwacl) && PQgetisnull(res, i, i_rfdwacl) &&
-            PQgetisnull(res, i, i_initfdwacl) &&
-            PQgetisnull(res, i, i_initrfdwacl))
-            fdwinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+        /* 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)))
+            fdwinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -9507,11 +9501,15 @@ getForeignServers(Archive *fout, int *numForeignServers)
         /* Decide whether we want to dump it */
         selectDumpableObject(&(srvinfo[i].dobj), fout);

-        /* Do not try to dump ACL if no ACL exists. */
-        if (PQgetisnull(res, i, i_srvacl) && PQgetisnull(res, i, i_rsrvacl) &&
-            PQgetisnull(res, i, i_initsrvacl) &&
-            PQgetisnull(res, i, i_initrsrvacl))
-            srvinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
+        /* Servers have user mappings */
+        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)))
+            srvinfo[i].dobj.components |= DUMP_COMPONENT_ACL;
     }

     PQclear(res);
@@ -9646,6 +9644,9 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs)
         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;
+
         /* Decide whether we want to dump it */
         selectDumpableDefaultACL(&(daclinfo[i]), dopt);
     }
@@ -9899,19 +9900,11 @@ static int
 findComments(Archive *fout, Oid classoid, Oid objoid,
              CommentItem **items)
 {
-    /* static storage for table of comments */
-    static CommentItem *comments = NULL;
-    static int    ncomments = -1;
-
     CommentItem *middle = NULL;
     CommentItem *low;
     CommentItem *high;
     int            nmatch;

-    /* Get comments if we didn't already */
-    if (ncomments < 0)
-        ncomments = collectComments(fout, &comments);
-
     /*
      * Do binary search to find some item matching the object.
      */
@@ -9972,15 +9965,17 @@ findComments(Archive *fout, Oid classoid, Oid objoid,
 /*
  * collectComments --
  *
- * Construct a table of all comments available for database objects.
+ * Construct a table of all comments available for database objects;
+ * also set the has-comment component flag for each relevant object.
+ *
  * We used to do per-object queries for the comments, but it's much faster
  * to pull them all over at once, and on most databases the memory cost
  * isn't high.
  *
  * The table is sorted by classoid/objid/objsubid for speed in lookup.
  */
-static int
-collectComments(Archive *fout, CommentItem **items)
+static void
+collectComments(Archive *fout)
 {
     PGresult   *res;
     PQExpBuffer query;
@@ -9990,7 +9985,7 @@ collectComments(Archive *fout, CommentItem **items)
     int            i_objsubid;
     int            ntups;
     int            i;
-    CommentItem *comments;
+    DumpableObject *dobj;

     query = createPQExpBuffer();

@@ -10010,20 +10005,52 @@ collectComments(Archive *fout, CommentItem **items)
     ntups = PQntuples(res);

     comments = (CommentItem *) pg_malloc(ntups * sizeof(CommentItem));
+    ncomments = 0;
+    dobj = NULL;

     for (i = 0; i < ntups; i++)
     {
-        comments[i].descr = PQgetvalue(res, i, i_description);
-        comments[i].classoid = atooid(PQgetvalue(res, i, i_classoid));
-        comments[i].objoid = atooid(PQgetvalue(res, i, i_objoid));
-        comments[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid));
+        CatalogId    objId;
+        int            subid;
+
+        objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
+        objId.oid = atooid(PQgetvalue(res, i, i_objoid));
+        subid = atoi(PQgetvalue(res, i, i_objsubid));
+
+        /* We needn't remember comments that don't match any dumpable object */
+        if (dobj == NULL ||
+            dobj->catId.tableoid != objId.tableoid ||
+            dobj->catId.oid != objId.oid)
+            dobj = findObjectByCatalogId(objId);
+        if (dobj == NULL)
+            continue;
+
+        /*
+         * Comments on columns of composite types are linked to the type's
+         * pg_class entry, but we need to set the DUMP_COMPONENT_COMMENT flag
+         * in the type's own DumpableObject.
+         */
+        if (subid != 0 && dobj->objType == DO_TABLE &&
+            ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
+        {
+            TypeInfo   *cTypeInfo;
+
+            cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
+            if (cTypeInfo)
+                cTypeInfo->dobj.components |= DUMP_COMPONENT_COMMENT;
+        }
+        else
+            dobj->components |= DUMP_COMPONENT_COMMENT;
+
+        comments[ncomments].descr = PQgetvalue(res, i, i_description);
+        comments[ncomments].classoid = objId.tableoid;
+        comments[ncomments].objoid = objId.oid;
+        comments[ncomments].objsubid = subid;
+        ncomments++;
     }

     /* Do NOT free the PGresult since we are keeping pointers into it */
     destroyPQExpBuffer(query);
-
-    *items = comments;
-    return ntups;
 }

 /*
@@ -10033,8 +10060,19 @@ collectComments(Archive *fout, CommentItem **items)
  * ArchiveEntries (TOC objects) for each object to be dumped.
  */
 static void
-dumpDumpableObject(Archive *fout, const DumpableObject *dobj)
+dumpDumpableObject(Archive *fout, DumpableObject *dobj)
 {
+    /*
+     * Clear any dump-request bits for components that don't exist for this
+     * object.  (This makes it safe to initially use DUMP_COMPONENT_ALL as the
+     * request for every kind of object.)
+     */
+    dobj->dump &= dobj->components;
+
+    /* Now, short-circuit if there's nothing to be done here. */
+    if (dobj->dump == 0)
+        return;
+
     switch (dobj->objType)
     {
         case DO_NAMESPACE:
@@ -10209,8 +10247,8 @@ dumpNamespace(Archive *fout, const NamespaceInfo *nspinfo)
     PQExpBuffer delq;
     char       *qnspname;

-    /* Skip if not to be dumped */
-    if (!nspinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     q = createPQExpBuffer();
@@ -10289,8 +10327,8 @@ dumpExtension(Archive *fout, const ExtensionInfo *extinfo)
     PQExpBuffer delq;
     char       *qextname;

-    /* Skip if not to be dumped */
-    if (!extinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     q = createPQExpBuffer();
@@ -10414,8 +10452,8 @@ dumpType(Archive *fout, const TypeInfo *tyinfo)
 {
     DumpOptions *dopt = fout->dopt;

-    /* Skip if not to be dumped */
-    if (!tyinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     /* Dump out in proper style */
@@ -11553,8 +11591,8 @@ dumpShellType(Archive *fout, const ShellTypeInfo *stinfo)
     DumpOptions *dopt = fout->dopt;
     PQExpBuffer q;

-    /* Skip if not to be dumped */
-    if (!stinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     q = createPQExpBuffer();
@@ -11605,8 +11643,8 @@ dumpProcLang(Archive *fout, const ProcLangInfo *plang)
     FuncInfo   *inlineInfo = NULL;
     FuncInfo   *validatorInfo = NULL;

-    /* Skip if not to be dumped */
-    if (!plang->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     /*
@@ -11892,8 +11930,8 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
     const char *keyword;
     int            i;

-    /* Skip if not to be dumped */
-    if (!finfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     query = createPQExpBuffer();
@@ -12385,8 +12423,8 @@ dumpCast(Archive *fout, const CastInfo *cast)
     const char *sourceType;
     const char *targetType;

-    /* Skip if not to be dumped */
-    if (!cast->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     /* Cannot dump if we don't have the cast function's info */
@@ -12491,8 +12529,8 @@ dumpTransform(Archive *fout, const TransformInfo *transform)
     char       *lanname;
     const char *transformType;

-    /* Skip if not to be dumped */
-    if (!transform->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     /* Cannot dump if we don't have the transform functions' info */
@@ -12640,8 +12678,8 @@ dumpOpr(Archive *fout, const OprInfo *oprinfo)
     char       *oprregproc;
     char       *oprref;

-    /* Skip if not to be dumped */
-    if (!oprinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     /*
@@ -12933,8 +12971,8 @@ dumpAccessMethod(Archive *fout, const AccessMethodInfo *aminfo)
     PQExpBuffer delq;
     char       *qamname;

-    /* Skip if not to be dumped */
-    if (!aminfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     q = createPQExpBuffer();
@@ -13038,8 +13076,8 @@ dumpOpclass(Archive *fout, const OpclassInfo *opcinfo)
     bool        needComma;
     int            i;

-    /* Skip if not to be dumped */
-    if (!opcinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     query = createPQExpBuffer();
@@ -13400,8 +13438,8 @@ dumpOpfamily(Archive *fout, const OpfamilyInfo *opfinfo)
     bool        needComma;
     int            i;

-    /* Skip if not to be dumped */
-    if (!opfinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     query = createPQExpBuffer();
@@ -13645,8 +13683,8 @@ dumpCollation(Archive *fout, const CollInfo *collinfo)
     const char *collcollate;
     const char *collctype;

-    /* Skip if not to be dumped */
-    if (!collinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     query = createPQExpBuffer();
@@ -13797,8 +13835,8 @@ dumpConversion(Archive *fout, const ConvInfo *convinfo)
     const char *conproc;
     bool        condefault;

-    /* Skip if not to be dumped */
-    if (!convinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     query = createPQExpBuffer();
@@ -13945,8 +13983,8 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
     const char *proparallel;
     char        defaultfinalmodify;

-    /* Skip if not to be dumped */
-    if (!agginfo->aggfn.dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     query = createPQExpBuffer();
@@ -14279,8 +14317,8 @@ dumpTSParser(Archive *fout, const TSParserInfo *prsinfo)
     PQExpBuffer delq;
     char       *qprsname;

-    /* Skip if not to be dumped */
-    if (!prsinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     q = createPQExpBuffer();
@@ -14347,8 +14385,8 @@ dumpTSDictionary(Archive *fout, const TSDictInfo *dictinfo)
     char       *nspname;
     char       *tmplname;

-    /* Skip if not to be dumped */
-    if (!dictinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     q = createPQExpBuffer();
@@ -14423,8 +14461,8 @@ dumpTSTemplate(Archive *fout, const TSTemplateInfo *tmplinfo)
     PQExpBuffer delq;
     char       *qtmplname;

-    /* Skip if not to be dumped */
-    if (!tmplinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     q = createPQExpBuffer();
@@ -14489,8 +14527,8 @@ dumpTSConfig(Archive *fout, const TSConfigInfo *cfginfo)
     int            i_tokenname;
     int            i_dictname;

-    /* Skip if not to be dumped */
-    if (!cfginfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     q = createPQExpBuffer();
@@ -14601,8 +14639,8 @@ dumpForeignDataWrapper(Archive *fout, const FdwInfo *fdwinfo)
     PQExpBuffer delq;
     char       *qfdwname;

-    /* Skip if not to be dumped */
-    if (!fdwinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     q = createPQExpBuffer();
@@ -14676,8 +14714,8 @@ dumpForeignServer(Archive *fout, const ForeignServerInfo *srvinfo)
     char       *qsrvname;
     char       *fdwname;

-    /* Skip if not to be dumped */
-    if (!srvinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     q = createPQExpBuffer();
@@ -14867,8 +14905,8 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
     PQExpBuffer tag;
     const char *type;

-    /* Skip if not to be dumped */
-    if (!daclinfo->dobj.dump || dopt->dataOnly || dopt->aclsSkip)
+    /* Do nothing in data-only dump, or if we're skipping ACLs */
+    if (dopt->dataOnly || dopt->aclsSkip)
         return;

     q = createPQExpBuffer();
@@ -15223,20 +15261,12 @@ dumpTableSecLabel(Archive *fout, const TableInfo *tbinfo, const char *reltypenam
 static int
 findSecLabels(Archive *fout, Oid classoid, Oid objoid, SecLabelItem **items)
 {
-    /* static storage for table of security labels */
-    static SecLabelItem *labels = NULL;
-    static int    nlabels = -1;
-
     SecLabelItem *middle = NULL;
     SecLabelItem *low;
     SecLabelItem *high;
     int            nmatch;

-    /* Get security labels if we didn't already */
-    if (nlabels < 0)
-        nlabels = collectSecLabels(fout, &labels);
-
-    if (nlabels <= 0)            /* no labels, so no match is possible */
+    if (nseclabels <= 0)        /* no labels, so no match is possible */
     {
         *items = NULL;
         return 0;
@@ -15245,8 +15275,8 @@ findSecLabels(Archive *fout, Oid classoid, Oid objoid, SecLabelItem **items)
     /*
      * Do binary search to find some item matching the object.
      */
-    low = &labels[0];
-    high = &labels[nlabels - 1];
+    low = &seclabels[0];
+    high = &seclabels[nseclabels - 1];
     while (low <= high)
     {
         middle = low + (high - low) / 2;
@@ -15302,13 +15332,13 @@ findSecLabels(Archive *fout, Oid classoid, Oid objoid, SecLabelItem **items)
 /*
  * collectSecLabels
  *
- * Construct a table of all security labels available for database objects.
- * It's much faster to pull them all at once.
+ * Construct a table of all security labels available for database objects;
+ * also set the has-seclabel component flag for each relevant object.
  *
  * The table is sorted by classoid/objid/objsubid for speed in lookup.
  */
-static int
-collectSecLabels(Archive *fout, SecLabelItem **items)
+static void
+collectSecLabels(Archive *fout)
 {
     PGresult   *res;
     PQExpBuffer query;
@@ -15319,7 +15349,7 @@ collectSecLabels(Archive *fout, SecLabelItem **items)
     int            i_objsubid;
     int            ntups;
     int            i;
-    SecLabelItem *labels;
+    DumpableObject *dobj;

     query = createPQExpBuffer();

@@ -15339,22 +15369,54 @@ collectSecLabels(Archive *fout, SecLabelItem **items)

     ntups = PQntuples(res);

-    labels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
+    seclabels = (SecLabelItem *) pg_malloc(ntups * sizeof(SecLabelItem));
+    nseclabels = 0;
+    dobj = NULL;

     for (i = 0; i < ntups; i++)
     {
-        labels[i].label = PQgetvalue(res, i, i_label);
-        labels[i].provider = PQgetvalue(res, i, i_provider);
-        labels[i].classoid = atooid(PQgetvalue(res, i, i_classoid));
-        labels[i].objoid = atooid(PQgetvalue(res, i, i_objoid));
-        labels[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid));
+        CatalogId    objId;
+        int            subid;
+
+        objId.tableoid = atooid(PQgetvalue(res, i, i_classoid));
+        objId.oid = atooid(PQgetvalue(res, i, i_objoid));
+        subid = atoi(PQgetvalue(res, i, i_objsubid));
+
+        /* We needn't remember labels that don't match any dumpable object */
+        if (dobj == NULL ||
+            dobj->catId.tableoid != objId.tableoid ||
+            dobj->catId.oid != objId.oid)
+            dobj = findObjectByCatalogId(objId);
+        if (dobj == NULL)
+            continue;
+
+        /*
+         * Labels on columns of composite types are linked to the type's
+         * pg_class entry, but we need to set the DUMP_COMPONENT_SECLABEL flag
+         * in the type's own DumpableObject.
+         */
+        if (subid != 0 && dobj->objType == DO_TABLE &&
+            ((TableInfo *) dobj)->relkind == RELKIND_COMPOSITE_TYPE)
+        {
+            TypeInfo   *cTypeInfo;
+
+            cTypeInfo = findTypeByOid(((TableInfo *) dobj)->reltype);
+            if (cTypeInfo)
+                cTypeInfo->dobj.components |= DUMP_COMPONENT_SECLABEL;
+        }
+        else
+            dobj->components |= DUMP_COMPONENT_SECLABEL;
+
+        seclabels[nseclabels].label = PQgetvalue(res, i, i_label);
+        seclabels[nseclabels].provider = PQgetvalue(res, i, i_provider);
+        seclabels[nseclabels].classoid = objId.tableoid;
+        seclabels[nseclabels].objoid = objId.oid;
+        seclabels[nseclabels].objsubid = subid;
+        nseclabels++;
     }

     /* Do NOT free the PGresult since we are keeping pointers into it */
     destroyPQExpBuffer(query);
-
-    *items = labels;
-    return ntups;
 }

 /*
@@ -15368,17 +15430,17 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
     DumpId        tableAclDumpId = InvalidDumpId;
     char       *namecopy;

-    /*
-     * noop if we are not dumping anything about this table, or if we are
-     * doing a data-only dump
-     */
-    if (!tbinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

-    if (tbinfo->relkind == RELKIND_SEQUENCE)
-        dumpSequence(fout, tbinfo);
-    else
-        dumpTableSchema(fout, tbinfo);
+    if (tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION)
+    {
+        if (tbinfo->relkind == RELKIND_SEQUENCE)
+            dumpSequence(fout, tbinfo);
+        else
+            dumpTableSchema(fout, tbinfo);
+    }

     /* Handle the ACL here */
     namecopy = pg_strdup(fmtId(tbinfo->dobj.name));
@@ -15398,7 +15460,8 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
     /*
      * Handle column ACLs, if any.  Note: we pull these with a separate query
      * rather than trying to fetch them during getTableAttrs, so that we won't
-     * miss ACLs on system columns.
+     * 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)
     {
@@ -15617,8 +15680,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
                        qrelname);

     if (dopt->binary_upgrade)
-        binary_upgrade_set_type_oids_by_rel_oid(fout, q,
-                                                tbinfo->dobj.catId.oid);
+        binary_upgrade_set_type_oids_by_rel(fout, q, tbinfo);

     /* Is it a table or a view? */
     if (tbinfo->relkind == RELKIND_VIEW)
@@ -16359,6 +16421,7 @@ dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
     DumpOptions *dopt = fout->dopt;
     PQExpBuffer q;

+    /* Do nothing in data-only dump */
     if (dopt->dataOnly)
         return;

@@ -16409,8 +16472,8 @@ dumpAttrDef(Archive *fout, const AttrDefInfo *adinfo)
     char       *tag;
     char       *foreign;

-    /* Skip if table definition not to be dumped */
-    if (!tbinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     /* Skip if not "separate"; it was dumped in the table's definition */
@@ -16498,6 +16561,7 @@ dumpIndex(Archive *fout, const IndxInfo *indxinfo)
     char       *qindxname;
     char       *qqindxname;

+    /* Do nothing in data-only dump */
     if (dopt->dataOnly)
         return;

@@ -16632,6 +16696,7 @@ dumpIndex(Archive *fout, const IndxInfo *indxinfo)
 static void
 dumpIndexAttach(Archive *fout, const IndexAttachInfo *attachinfo)
 {
+    /* Do nothing in data-only dump */
     if (fout->dopt->dataOnly)
         return;

@@ -16678,8 +16743,8 @@ dumpStatisticsExt(Archive *fout, const StatsExtInfo *statsextinfo)
     PGresult   *res;
     char       *stxdef;

-    /* Skip if not to be dumped */
-    if (!statsextinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     q = createPQExpBuffer();
@@ -16755,8 +16820,8 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
     char       *tag = NULL;
     char       *foreign;

-    /* Skip if not to be dumped */
-    if (!coninfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     q = createPQExpBuffer();
@@ -17408,10 +17473,7 @@ dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
     int            findx;
     char       *tag;

-    /*
-     * we needn't check dobj.dump because TriggerInfo wouldn't have been
-     * created in the first place for non-dumpable triggers
-     */
+    /* Do nothing in data-only dump */
     if (dopt->dataOnly)
         return;

@@ -17647,8 +17709,8 @@ dumpEventTrigger(Archive *fout, const EventTriggerInfo *evtinfo)
     PQExpBuffer delqry;
     char       *qevtname;

-    /* Skip if not to be dumped */
-    if (!evtinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     query = createPQExpBuffer();
@@ -17738,8 +17800,8 @@ dumpRule(Archive *fout, const RuleInfo *rinfo)
     PGresult   *res;
     char       *tag;

-    /* Skip if not to be dumped */
-    if (!rinfo->dobj.dump || dopt->dataOnly)
+    /* Do nothing in data-only dump */
+    if (dopt->dataOnly)
         return;

     /*
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 29af845ece..98b258cebc 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -84,8 +84,13 @@ typedef enum
     DO_SUBSCRIPTION
 } DumpableObjectType;

-/* component types of an object which can be selected for dumping */
-typedef uint32 DumpComponents;    /* a bitmask of dump object components */
+/*
+ * DumpComponents is a bitmask of the potentially dumpable components of
+ * a database object: its core definition, plus optional attributes such
+ * as ACL, comments, etc.  The NONE and ALL symbols are convenient
+ * shorthands.
+ */
+typedef uint32 DumpComponents;
 #define DUMP_COMPONENT_NONE            (0)
 #define DUMP_COMPONENT_DEFINITION    (1 << 0)
 #define DUMP_COMPONENT_DATA            (1 << 1)
@@ -130,8 +135,9 @@ typedef struct _dumpableObject
     DumpId        dumpId;            /* assigned by AssignDumpId() */
     char       *name;            /* object name (should never be NULL) */
     struct _namespaceInfo *namespace;    /* containing namespace, or NULL */
-    DumpComponents dump;        /* bitmask of components to dump */
+    DumpComponents dump;        /* bitmask of components requested to dump */
     DumpComponents dump_contains;    /* as above, but for contained objects */
+    DumpComponents components;    /* bitmask of components available to dump */
     bool        ext_member;        /* true if object is member of extension */
     bool        depends_on_ext; /* true if object depends on an extension */
     DumpId       *dependencies;    /* dumpIds of objects this one depends on */
@@ -288,6 +294,7 @@ typedef struct _tableInfo
     uint32        toast_frozenxid;    /* toast table's relfrozenxid, if any */
     uint32        toast_minmxid;    /* toast table's relminmxid */
     int            ncheck;            /* # of CHECK expressions */
+    Oid            reltype;        /* OID of table's composite type, if any */
     char       *reloftype;        /* underlying type for typed table */
     Oid            foreign_server; /* foreign server oid, if applicable */
     /* these two are set only if table is a sequence owned by a column: */
diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c
index ea67e52a3f..6e216313c6 100644
--- a/src/bin/pg_dump/dumputils.c
+++ b/src/bin/pg_dump/dumputils.c
@@ -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 a 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.
diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h
index f5465f19ae..fac7a05c91 100644
--- a/src/bin/pg_dump/dumputils.h
+++ b/src/bin/pg_dump/dumputils.h
@@ -37,26 +37,22 @@


 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,
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 29c631c8c3..5829bfb6a3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -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)
@@ -2822,19 +2824,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;
@@ -2846,40 +2847,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 "
@@ -2888,13 +2863,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 "

@@ -2908,7 +2884,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 "

@@ -2922,7 +2899,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 "

@@ -2936,8 +2914,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()",
@@ -2956,7 +2934,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");
@@ -2970,8 +2948,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);
@@ -3109,9 +3087,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
@@ -3433,59 +3414,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);
@@ -3493,9 +3445,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);

@@ -3512,20 +3462,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;

         /*
@@ -3601,8 +3548,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);
@@ -4837,7 +4783,6 @@ binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
 NamespaceInfo *
 getNamespaces(Archive *fout, int *numNamespaces)
 {
-    DumpOptions *dopt = fout->dopt;
     PGresult   *res;
     int            ntups;
     int            i;
@@ -4849,9 +4794,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();

@@ -4859,67 +4802,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);

@@ -4935,9 +4829,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++)
     {
@@ -4946,23 +4838,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);
@@ -5085,7 +5015,6 @@ getExtensions(Archive *fout, int *numExtensions)
 TypeInfo *
 getTypes(Archive *fout, int *numTypes)
 {
-    DumpOptions *dopt = fout->dopt;
     PGresult   *res;
     int            ntups;
     int            i;
@@ -5097,9 +5026,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;
@@ -5123,52 +5050,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\" "
@@ -5182,8 +5068,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\" "
@@ -5197,8 +5082,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\" "
@@ -5220,9 +5104,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");
@@ -5240,12 +5122,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);
@@ -5271,10 +5153,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;

         /*
@@ -5801,9 +5680,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
@@ -5811,16 +5688,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");

@@ -5829,10 +5698,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 "
@@ -5844,10 +5711,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,
@@ -5857,11 +5720,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)
     {
@@ -5870,8 +5751,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 != "
@@ -5895,8 +5775,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 != "
@@ -5919,9 +5798,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++)
     {
@@ -5932,16 +5809,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;
@@ -5957,10 +5834,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;
     }

@@ -5997,9 +5871,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:
@@ -6021,30 +5893,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 "
@@ -6067,10 +5929,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,
@@ -6085,23 +5943,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,
@@ -6154,9 +6012,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++)
     {
@@ -6167,13 +6023,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;
@@ -6188,10 +6044,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)
@@ -6256,10 +6109,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;
@@ -6400,67 +6250,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,
@@ -6482,22 +6279,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");

     /*
@@ -6509,7 +6300,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
@@ -6583,10 +6376,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");
@@ -6615,6 +6405,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));
@@ -6661,10 +6455,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));
@@ -6700,23 +6490,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
@@ -7998,7 +7775,6 @@ getEventTriggers(Archive *fout, int *numEventTriggers)
 ProcLangInfo *
 getProcLangs(Archive *fout, int *numProcLangs)
 {
-    DumpOptions *dopt = fout->dopt;
     PGresult   *res;
     int            ntups;
     int            i;
@@ -8012,56 +7788,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 "
@@ -8074,8 +7824,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 "
@@ -8088,8 +7837,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 "
@@ -8102,8 +7850,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 "
@@ -8127,9 +7874,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++)
@@ -8140,24 +7885,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;
     }

@@ -9208,7 +8950,6 @@ getTSConfigurations(Archive *fout, int *numTSConfigs)
 FdwInfo *
 getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
 {
-    DumpOptions *dopt = fout->dopt;
     PGresult   *res;
     int            ntups;
     int            i;
@@ -9221,9 +8962,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 */
@@ -9235,46 +8974,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)
     {
@@ -9282,8 +8997,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) "
@@ -9299,8 +9013,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) "
@@ -9325,9 +9038,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++)
@@ -9338,23 +9049,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;
     }

@@ -9375,7 +9083,6 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
 ForeignServerInfo *
 getForeignServers(Archive *fout, int *numForeignServers)
 {
-    DumpOptions *dopt = fout->dopt;
     PGresult   *res;
     int            ntups;
     int            i;
@@ -9389,9 +9096,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 */
@@ -9403,53 +9108,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) "
@@ -9475,9 +9154,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++)
@@ -9488,15 +9165,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);
@@ -9505,10 +9182,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;
     }

@@ -9539,9 +9213,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;

@@ -9553,56 +9225,24 @@ 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();
-
-        buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
-                        initracl_subquery, "defaclacl", "defaclrole",
-                        "pip.initprivs",
-                        "CASE WHEN defaclobjtype = 'S' THEN 's' ELSE defaclobjtype END::\"char\"",
-                        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);
+    appendPQExpBuffer(query,
+                      "SELECT oid, tableoid, "
+                      "(%s defaclrole) AS defaclrole, "
+                      "defaclnamespace, "
+                      "defaclobjtype, "
+                      "defaclacl, ",
+                      username_subquery);

-        destroyPQExpBuffer(acl_subquery);
-        destroyPQExpBuffer(racl_subquery);
-        destroyPQExpBuffer(initacl_subquery);
-        destroyPQExpBuffer(initracl_subquery);
-    }
+    if (fout->remoteVersion >= 90200)
+        appendPQExpBufferStr(query,
+                             "acldefault(CASE WHEN defaclobjtype = 'S' "
+                             "THEN 's'::\"char\" ELSE defaclobjtype END, defaclrole) 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);

@@ -9617,9 +9257,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++)
     {
@@ -9637,12 +9275,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;
@@ -9658,6 +9296,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 --
  *
@@ -10306,8 +10064,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);

@@ -10598,8 +10355,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);
@@ -10738,8 +10494,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);
@@ -10810,8 +10565,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);
@@ -11070,8 +10824,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);
@@ -11227,8 +10980,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++)
@@ -11449,8 +11201,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);
@@ -11748,8 +11499,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);

@@ -12384,8 +12134,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);

@@ -14288,9 +14037,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)
@@ -14689,9 +14436,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);

@@ -14778,9 +14523,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)
@@ -14942,15 +14685,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,
@@ -14980,20 +14721,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.
@@ -15003,11 +14731,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 */
@@ -15021,29 +14753,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)
     {
@@ -15453,8 +15208,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);
     }

     /*
@@ -15463,7 +15217,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;
@@ -15471,55 +15225,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 "
@@ -15533,11 +15269,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));

             /*
@@ -15548,7 +15289,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);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 98b258cebc..5325a1b196 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -145,16 +145,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
@@ -170,6 +190,7 @@ typedef struct _extensionInfo
 typedef struct _typeInfo
 {
     DumpableObject dobj;
+    DumpableAcl dacl;

     /*
      * Note: dobj.name is the raw pg_type.typname entry.  ftypname is the
@@ -178,10 +199,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 */
@@ -206,15 +223,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 */
@@ -269,11 +283,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 */
@@ -285,6 +296,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? */
@@ -477,14 +489,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;

@@ -549,49 +558,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;

 /*
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index c29101704a..44114f3f71 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -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\"",
diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c
index 3efee4e7ee..81e623602e 100644
--- a/src/fe_utils/string_utils.c
+++ b/src/fe_utils/string_utils.c
@@ -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.
  *
diff --git a/src/include/fe_utils/string_utils.h b/src/include/fe_utils/string_utils.h
index caafb97d29..e12e61cddb 100644
--- a/src/include/fe_utils/string_utils.h
+++ b/src/include/fe_utils/string_utils.h
@@ -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);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 5829bfb6a3..e2e629b9c4 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6110,20 +6110,23 @@ getTables(Archive *fout, int *numTables)
     int            i_is_identity_sequence;
     int            i_relacl;
     int            i_acldefault;
-    int            i_partkeydef;
     int            i_ispartition;
-    int            i_partbound;

     /*
      * Find all the tables and table-like objects.
      *
+     * We must fetch all tables in this phase because otherwise we cannot
+     * correctly identify inherited columns, owned sequences, etc.
+     *
      * We include system catalogs, so that we can work if a user table is
      * defined to inherit from a system catalog (pretty weird, but...)
      *
      * Note: in this phase we should collect only a minimal amount of
      * information about each table, basically just enough to decide if it is
-     * interesting. We must fetch all tables in this phase because otherwise
-     * we cannot correctly identify inherited columns, owned sequences, etc.
+     * interesting.  In particular, since we do not yet have lock on any user
+     * table, we MUST NOT invoke any server-side data collection functions
+     * (for instance, pg_get_partkeydef()).  Those are likely to fail or give
+     * wrong answers if any concurrent DDL is happening.
      */

     appendPQExpBuffer(query,
@@ -6217,10 +6220,10 @@ getTables(Archive *fout, int *numTables)

     if (fout->remoteVersion >= 90000)
         appendPQExpBufferStr(query,
-                             "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS
reloftype,"); 
+                             "c.reloftype, ");
     else
         appendPQExpBufferStr(query,
-                             "NULL AS reloftype, ");
+                             "0 AS reloftype, ");

     if (fout->remoteVersion >= 90100)
         appendPQExpBufferStr(query,
@@ -6261,14 +6264,10 @@ getTables(Archive *fout, int *numTables)

     if (fout->remoteVersion >= 100000)
         appendPQExpBufferStr(query,
-                             "pg_get_partkeydef(c.oid) AS partkeydef, "
-                             "c.relispartition AS ispartition, "
-                             "pg_get_expr(c.relpartbound, c.oid) AS partbound ");
+                             "c.relispartition AS ispartition ");
     else
         appendPQExpBufferStr(query,
-                             "NULL AS partkeydef, "
-                             "false AS ispartition, "
-                             "NULL AS partbound ");
+                             "false AS ispartition ");

     /*
      * Left join to pg_depend to pick up dependency info linking sequences to
@@ -6377,9 +6376,7 @@ getTables(Archive *fout, int *numTables)
     i_is_identity_sequence = PQfnumber(res, "is_identity_sequence");
     i_relacl = PQfnumber(res, "relacl");
     i_acldefault = PQfnumber(res, "acldefault");
-    i_partkeydef = PQfnumber(res, "partkeydef");
     i_ispartition = PQfnumber(res, "ispartition");
-    i_partbound = PQfnumber(res, "partbound");

     if (dopt->lockWaitTimeout)
     {
@@ -6445,19 +6442,14 @@ getTables(Archive *fout, int *numTables)
         else
             tblinfo[i].checkoption = pg_strdup(PQgetvalue(res, i, i_checkoption));
         tblinfo[i].toast_reloptions = pg_strdup(PQgetvalue(res, i, i_toastreloptions));
-        if (PQgetisnull(res, i, i_reloftype))
-            tblinfo[i].reloftype = NULL;
-        else
-            tblinfo[i].reloftype = pg_strdup(PQgetvalue(res, i, i_reloftype));
+        tblinfo[i].reloftype = atooid(PQgetvalue(res, i, i_reloftype));
         tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
         if (PQgetisnull(res, i, i_amname))
             tblinfo[i].amname = NULL;
         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].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));

         /* other fields were zeroed above */

@@ -15463,12 +15455,34 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
     }
     else
     {
+        char       *partkeydef = NULL;
         char       *ftoptions = NULL;
         char       *srvname = NULL;
         char       *foreign = "";

+        /*
+         * Set reltypename, and collect any relkind-specific data that we
+         * didn't fetch during getTables().
+         */
         switch (tbinfo->relkind)
         {
+            case RELKIND_PARTITIONED_TABLE:
+                {
+                    PQExpBuffer query = createPQExpBuffer();
+                    PGresult   *res;
+
+                    reltypename = "TABLE";
+
+                    /* retrieve partition key definition */
+                    appendPQExpBuffer(query,
+                                      "SELECT pg_get_partkeydef('%u')",
+                                      tbinfo->dobj.catId.oid);
+                    res = ExecuteSqlQueryForSingleRow(fout, query->data);
+                    partkeydef = pg_strdup(PQgetvalue(res, 0, 0));
+                    PQclear(res);
+                    destroyPQExpBuffer(query);
+                    break;
+                }
             case RELKIND_FOREIGN_TABLE:
                 {
                     PQExpBuffer query = createPQExpBuffer();
@@ -15508,6 +15522,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
                 break;
             default:
                 reltypename = "TABLE";
+                break;
         }

         numParents = tbinfo->numParents;
@@ -15529,8 +15544,10 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
          * Attach to type, if reloftype; except in case of a binary upgrade,
          * we dump the table normally and attach it to the type afterward.
          */
-        if (tbinfo->reloftype && !dopt->binary_upgrade)
-            appendPQExpBuffer(q, " OF %s", tbinfo->reloftype);
+        if (OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade)
+            appendPQExpBuffer(q, " OF %s",
+                              getFormattedTypeName(fout, tbinfo->reloftype,
+                                                   zeroIsError));

         if (tbinfo->relkind != RELKIND_MATVIEW)
         {
@@ -15568,7 +15585,8 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
                      * Skip column if fully defined by reloftype, except in
                      * binary upgrade
                      */
-                    if (tbinfo->reloftype && !print_default && !print_notnull &&
+                    if (OidIsValid(tbinfo->reloftype) &&
+                        !print_default && !print_notnull &&
                         !dopt->binary_upgrade)
                         continue;

@@ -15601,7 +15619,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
                      * table ('OF type_name'), but in binary-upgrade mode,
                      * print it in that case too.
                      */
-                    if (dopt->binary_upgrade || !tbinfo->reloftype)
+                    if (dopt->binary_upgrade || !OidIsValid(tbinfo->reloftype))
                     {
                         appendPQExpBuffer(q, " %s",
                                           tbinfo->atttypnames[j]);
@@ -15664,7 +15682,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)

             if (actual_atts)
                 appendPQExpBufferStr(q, "\n)");
-            else if (!(tbinfo->reloftype && !dopt->binary_upgrade))
+            else if (!(OidIsValid(tbinfo->reloftype) && !dopt->binary_upgrade))
             {
                 /*
                  * No attributes? we must have a parenthesized attribute list,
@@ -15693,7 +15711,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
             }

             if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
-                appendPQExpBuffer(q, "\nPARTITION BY %s", tbinfo->partkeydef);
+                appendPQExpBuffer(q, "\nPARTITION BY %s", partkeydef);

             if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
                 appendPQExpBuffer(q, "\nSERVER %s", fmtId(srvname));
@@ -15876,12 +15894,13 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
                 }
             }

-            if (tbinfo->reloftype)
+            if (OidIsValid(tbinfo->reloftype))
             {
                 appendPQExpBufferStr(q, "\n-- For binary upgrade, set up typed tables this way.\n");
                 appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
                                   qualrelname,
-                                  tbinfo->reloftype);
+                                  getFormattedTypeName(fout, tbinfo->reloftype,
+                                                       zeroIsError));
             }
         }

@@ -16161,6 +16180,8 @@ dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
 {
     DumpOptions *dopt = fout->dopt;
     PQExpBuffer q;
+    PGresult   *res;
+    char       *partbound;

     /* Do nothing in data-only dump */
     if (dopt->dataOnly)
@@ -16171,14 +16192,23 @@ dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)

     q = createPQExpBuffer();

-    /* Perform ALTER TABLE on the parent */
+    /* Fetch the partition's partbound */
     appendPQExpBuffer(q,
+                      "SELECT pg_get_expr(c.relpartbound, c.oid) "
+                      "FROM pg_class c "
+                      "WHERE c.oid = '%u'",
+                      attachinfo->partitionTbl->dobj.catId.oid);
+    res = ExecuteSqlQueryForSingleRow(fout, q->data);
+    partbound = PQgetvalue(res, 0, 0);
+
+    /* Perform ALTER TABLE on the parent */
+    printfPQExpBuffer(q,
                       "ALTER TABLE ONLY %s ",
                       fmtQualifiedDumpable(attachinfo->parentTbl));
     appendPQExpBuffer(q,
                       "ATTACH PARTITION %s %s;\n",
                       fmtQualifiedDumpable(attachinfo->partitionTbl),
-                      attachinfo->partitionTbl->partbound);
+                      partbound);

     /*
      * There is no point in creating a drop query as the drop is done by table
@@ -16195,6 +16225,7 @@ dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
                               .section = SECTION_PRE_DATA,
                               .createStmt = q->data));

+    PQclear(res);
     destroyPQExpBuffer(q);
 }

diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 5325a1b196..62f1ae5a90 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -307,7 +307,7 @@ typedef struct _tableInfo
     uint32        toast_minmxid;    /* toast table's relminmxid */
     int            ncheck;            /* # of CHECK expressions */
     Oid            reltype;        /* OID of table's composite type, if any */
-    char       *reloftype;        /* underlying type for typed table */
+    Oid            reloftype;        /* underlying type for typed table */
     Oid            foreign_server; /* foreign server oid, if applicable */
     /* these two are set only if table is a sequence owned by a column: */
     Oid            owning_tab;        /* OID of table owning sequence */
@@ -346,8 +346,6 @@ typedef struct _tableInfo
     bool       *inhNotNull;        /* true if NOT NULL is inherited */
     struct _attrDefInfo **attrdefs; /* DEFAULT expressions */
     struct _constraintInfo *checkexprs; /* CHECK constraints */
-    char       *partkeydef;        /* partition key definition */
-    char       *partbound;        /* partition bound definition */
     bool        needs_override; /* has GENERATED ALWAYS AS IDENTITY */
     char       *amname;            /* relation access method */


pgsql-hackers by date:

Previous
From: Stephen Frost
Date:
Subject: Re: Delegating superuser tasks to new security roles (Was: Granting control of SUSET gucs to non-superusers)
Next
From: Jeff Davis
Date:
Subject: Re: Delegating superuser tasks to new security roles (Was: Granting control of SUSET gucs to non-superusers)