pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Tom Lane <tgl@sss.pgh.pa.us>
To: Andres Freund <andres@anarazel.de>
Cc: Hans Buschmann <buschmann@nidsa.net>
Cc: pgsql-hackers@postgresql.org <pgsql-hackers@postgresql.org>
Subject: Re: Assorted improvements in pg_dump
Date: Tue, 26 Oct 2021 18:31:05 -0400
Message-ID: <177444.1635287465@sss.pgh.pa.us> (raw)
In-Reply-To: <20211025213550.2gd35lum5duw7urk@alap3.anarazel.de>
References: <7d7eb6128f40401d81b3b7a898b6b4de@W2012-02.nidsa.loc>
	<3780613.1635109855@sss.pgh.pa.us>
	<20211025193024.ssesntsq25muk7py@alap3.anarazel.de>
	<4167039.1635192154@sss.pgh.pa.us>
	<20211025213550.2gd35lum5duw7urk@alap3.anarazel.de>

Andres Freund <andres@anarazel.de> writes:
> I guess we could move the prepared-statement handling into a query execution
> helper. That could then use a hashtable or something similar to check if a
> certain prepared statement already exists. That'd then centrally be extensible
> to deal with multiple connects etc.

That seems like more mechanism than is warranted.  I tried it with a
simple array of booleans, and that seems like not too much of a mess;
see revised 0006 attached.

(0001-0005 are the same as before; including them just to satisfy
the cfbot.)

			regards, tom lane

Attachments:

  [text/x-diff] 0001-fix-component-masking-2.patch (47.1K, ../177444.1635287465@sss.pgh.pa.us/2-0001-fix-component-masking-2.patch)
  download | inline diff:
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index e38ff3c030..310f94605c 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -581,6 +581,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 e9f68e8986..e5ea2c082d 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,
@@ -6900,13 +6917,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\"",
@@ -7276,9 +7289,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);
@@ -7968,9 +7978,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);
@@ -8147,11 +8154,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);
@@ -8261,9 +8269,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);
@@ -8960,9 +8965,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);
@@ -9043,9 +9045,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);
@@ -9118,9 +9117,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);
@@ -9194,9 +9190,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);
@@ -9358,11 +9351,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);
@@ -9508,11 +9502,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);
@@ -9664,6 +9662,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);
 	}
@@ -9917,19 +9918,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.
 	 */
@@ -9990,15 +9983,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;
@@ -10008,7 +10003,7 @@ collectComments(Archive *fout, CommentItem **items)
 	int			i_objsubid;
 	int			ntups;
 	int			i;
-	CommentItem *comments;
+	DumpableObject *dobj;
 
 	query = createPQExpBuffer();
 
@@ -10028,20 +10023,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 = pg_strdup(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 */
+	PQclear(res);
 	destroyPQExpBuffer(query);
-
-	*items = comments;
-	return ntups;
 }
 
 /*
@@ -10051,8 +10078,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:
@@ -10227,8 +10265,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();
@@ -10307,8 +10345,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();
@@ -10432,8 +10470,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 */
@@ -11571,8 +11609,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();
@@ -11623,8 +11661,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;
 
 	/*
@@ -11911,8 +11949,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();
@@ -12405,8 +12443,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 */
@@ -12511,8 +12549,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 */
@@ -12660,8 +12698,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;
 
 	/*
@@ -12953,8 +12991,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();
@@ -13058,8 +13096,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();
@@ -13420,8 +13458,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();
@@ -13665,8 +13703,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();
@@ -13817,8 +13855,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();
@@ -13965,8 +14003,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();
@@ -14299,8 +14337,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();
@@ -14367,8 +14405,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();
@@ -14443,8 +14481,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();
@@ -14509,8 +14547,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();
@@ -14621,8 +14659,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();
@@ -14696,8 +14734,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();
@@ -14889,8 +14927,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();
@@ -15245,20 +15283,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;
@@ -15267,8 +15297,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;
@@ -15324,13 +15354,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;
@@ -15341,7 +15371,7 @@ collectSecLabels(Archive *fout, SecLabelItem **items)
 	int			i_objsubid;
 	int			ntups;
 	int			i;
-	SecLabelItem *labels;
+	DumpableObject *dobj;
 
 	query = createPQExpBuffer();
 
@@ -15361,22 +15391,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 = pg_strdup(PQgetvalue(res, i, i_label));
+		seclabels[nseclabels].provider = pg_strdup(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 */
+	PQclear(res);
 	destroyPQExpBuffer(query);
-
-	*items = labels;
-	return ntups;
 }
 
 /*
@@ -15390,17 +15452,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));
@@ -15420,7 +15482,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)
 	{
@@ -15639,8 +15702,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)
@@ -16381,6 +16443,7 @@ dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
 	DumpOptions *dopt = fout->dopt;
 	PQExpBuffer q;
 
+	/* Do nothing in data-only dump */
 	if (dopt->dataOnly)
 		return;
 
@@ -16431,8 +16494,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 */
@@ -16520,6 +16583,7 @@ dumpIndex(Archive *fout, const IndxInfo *indxinfo)
 	char	   *qindxname;
 	char	   *qqindxname;
 
+	/* Do nothing in data-only dump */
 	if (dopt->dataOnly)
 		return;
 
@@ -16654,6 +16718,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;
 
@@ -16700,8 +16765,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();
@@ -16777,8 +16842,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();
@@ -17430,10 +17495,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;
 
@@ -17669,8 +17731,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();
@@ -17760,8 +17822,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 cc55e598ec..3db73f710c 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: */

  [text/x-diff] 0002-rethink-ACL-handling-2.patch (121.1K, ../177444.1635287465@sss.pgh.pa.us/3-0002-rethink-ACL-handling-2.patch)
  download | inline diff:
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 e5ea2c082d..607abb97d3 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
@@ -7999,7 +7776,6 @@ getEventTriggers(Archive *fout, int *numEventTriggers)
 ProcLangInfo *
 getProcLangs(Archive *fout, int *numProcLangs)
 {
-	DumpOptions *dopt = fout->dopt;
 	PGresult   *res;
 	int			ntups;
 	int			i;
@@ -8013,56 +7789,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 "
@@ -8075,8 +7825,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 "
@@ -8089,8 +7838,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 "
@@ -8103,8 +7851,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 "
@@ -8128,9 +7875,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++)
@@ -8141,24 +7886,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;
 	}
 
@@ -9209,7 +8951,6 @@ getTSConfigurations(Archive *fout, int *numTSConfigs)
 FdwInfo *
 getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
 {
-	DumpOptions *dopt = fout->dopt;
 	PGresult   *res;
 	int			ntups;
 	int			i;
@@ -9222,9 +8963,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 */
@@ -9236,46 +8975,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)
 	{
@@ -9283,8 +8998,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) "
@@ -9300,8 +9014,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) "
@@ -9326,9 +9039,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++)
@@ -9339,23 +9050,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;
 	}
 
@@ -9376,7 +9084,6 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
 ForeignServerInfo *
 getForeignServers(Archive *fout, int *numForeignServers)
 {
-	DumpOptions *dopt = fout->dopt;
 	PGresult   *res;
 	int			ntups;
 	int			i;
@@ -9390,9 +9097,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 */
@@ -9404,53 +9109,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) "
@@ -9476,9 +9155,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++)
@@ -9489,15 +9166,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);
@@ -9506,10 +9183,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;
 	}
 
@@ -9540,9 +9214,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;
 
@@ -9554,13 +9226,16 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs)
 
 	query = createPQExpBuffer();
 
-	if (fout->remoteVersion >= 90600)
-	{
-		PQExpBuffer acl_subquery = createPQExpBuffer();
-		PQExpBuffer racl_subquery = createPQExpBuffer();
-		PQExpBuffer initacl_subquery = createPQExpBuffer();
-		PQExpBuffer initracl_subquery = createPQExpBuffer();
+	appendPQExpBuffer(query,
+					  "SELECT oid, tableoid, "
+					  "(%s defaclrole) AS defaclrole, "
+					  "defaclnamespace, "
+					  "defaclobjtype, "
+					  "defaclacl, ",
+					  username_subquery);
 
+	if (fout->remoteVersion >= 90200)
+	{
 		/*
 		 * Global entries (with defaclnamespace=0) replace the hard-wired
 		 * default ACL for their object type.  We should dump them as deltas
@@ -9568,59 +9243,24 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs)
 		 * for interpreting the ALTER DEFAULT PRIVILEGES commands.  On the
 		 * other hand, non-global entries can only add privileges not revoke
 		 * them.  We must dump those as-is (i.e., as deltas from an empty
-		 * ACL).  We implement that by passing NULL as the object type for
-		 * acldefault(), which works because acldefault() is STRICT.
+		 * ACL).
 		 *
 		 * We can use defaclobjtype as the object type for acldefault(),
 		 * except for the case of 'S' (DEFACLOBJ_SEQUENCE) which must be
 		 * converted to 's'.
 		 */
-		buildACLQueries(acl_subquery, racl_subquery, initacl_subquery,
-						initracl_subquery, "defaclacl", "defaclrole",
-						"pip.initprivs",
-						"CASE WHEN defaclnamespace = 0 THEN"
-						"	  CASE WHEN defaclobjtype = 'S' THEN 's'::\"char\""
-						"	  ELSE defaclobjtype END "
-						"ELSE NULL END",
-						dopt->binary_upgrade);
-
-		appendPQExpBuffer(query, "SELECT d.oid, d.tableoid, "
-						  "(%s d.defaclrole) AS defaclrole, "
-						  "d.defaclnamespace, "
-						  "d.defaclobjtype, "
-						  "%s AS defaclacl, "
-						  "%s AS rdefaclacl, "
-						  "%s AS initdefaclacl, "
-						  "%s AS initrdefaclacl "
-						  "FROM pg_default_acl d "
-						  "LEFT JOIN pg_init_privs pip ON "
-						  "(d.oid = pip.objoid "
-						  "AND pip.classoid = 'pg_default_acl'::regclass "
-						  "AND pip.objsubid = 0) ",
-						  username_subquery,
-						  acl_subquery->data,
-						  racl_subquery->data,
-						  initacl_subquery->data,
-						  initracl_subquery->data);
-
-		destroyPQExpBuffer(acl_subquery);
-		destroyPQExpBuffer(racl_subquery);
-		destroyPQExpBuffer(initacl_subquery);
-		destroyPQExpBuffer(initracl_subquery);
+		appendPQExpBufferStr(query,
+							 "CASE WHEN defaclnamespace = 0 THEN "
+							 "acldefault(CASE WHEN defaclobjtype = 'S' "
+							 "THEN 's'::\"char\" ELSE defaclobjtype END, "
+							 "defaclrole) ELSE '{}' END AS acldefault ");
 	}
 	else
-	{
-		appendPQExpBuffer(query, "SELECT oid, tableoid, "
-						  "(%s defaclrole) AS defaclrole, "
-						  "defaclnamespace, "
-						  "defaclobjtype, "
-						  "defaclacl, "
-						  "NULL AS rdefaclacl, "
-						  "NULL AS initdefaclacl, "
-						  "NULL AS initrdefaclacl "
-						  "FROM pg_default_acl",
-						  username_subquery);
-	}
+		appendPQExpBufferStr(query,
+							 "NULL AS acldefault ");
+
+	appendPQExpBufferStr(query,
+						 "FROM pg_default_acl");
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -9635,9 +9275,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++)
 	{
@@ -9655,12 +9293,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;
@@ -9676,6 +9314,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 --
  *
@@ -10324,8 +10082,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);
 
@@ -10616,8 +10373,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);
@@ -10756,8 +10512,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);
@@ -10828,8 +10583,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);
@@ -11088,8 +10842,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);
@@ -11245,8 +10998,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++)
@@ -11467,8 +11219,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);
@@ -11766,8 +11517,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);
 
@@ -12403,8 +12153,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);
 
@@ -14308,9 +14057,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)
@@ -14709,9 +14456,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);
 
@@ -14798,9 +14543,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)
@@ -14964,15 +14707,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,
@@ -15002,20 +14743,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.
@@ -15025,11 +14753,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 */
@@ -15043,29 +14775,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)
 	{
@@ -15475,8 +15230,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);
 	}
 
 	/*
@@ -15485,7 +15239,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;
@@ -15493,55 +15247,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 "
@@ -15555,11 +15291,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));
 
 			/*
@@ -15570,7 +15311,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 3db73f710c..e3b864f6ba 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);

  [text/x-diff] 0003-move-unsafe-function-calls-2.patch (10.0K, ../177444.1635287465@sss.pgh.pa.us/4-0003-move-unsafe-function-calls-2.patch)
  download | inline diff:
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 607abb97d3..d6075133a1 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 */
 
@@ -15485,12 +15477,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();
@@ -15530,6 +15544,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
 				break;
 			default:
 				reltypename = "TABLE";
+				break;
 		}
 
 		numParents = tbinfo->numParents;
@@ -15551,8 +15566,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)
 		{
@@ -15590,7 +15607,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;
 
@@ -15623,7 +15641,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]);
@@ -15686,7 +15704,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,
@@ -15715,7 +15733,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));
@@ -15898,12 +15916,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));
 			}
 		}
 
@@ -16076,6 +16095,8 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
 								  tbinfo->attfdwoptions[j]);
 		}						/* end loop over columns */
 
+		if (partkeydef)
+			free(partkeydef);
 		if (ftoptions)
 			free(ftoptions);
 		if (srvname)
@@ -16183,6 +16204,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)
@@ -16193,14 +16216,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
@@ -16217,6 +16249,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 e3b864f6ba..9061812c08 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 */
 =

  [text/x-diff] 0004-bulk-queries-2.patch (74.2K, ../177444.1635287465@sss.pgh.pa.us/5-0004-bulk-queries-2.patch)
  download | inline diff:
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index d6075133a1..45bae2ffe1 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6650,13 +6650,15 @@ getInherits(Archive *fout, int *numInherits)
 void
 getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 {
-	int			i,
-				j;
 	PQExpBuffer query = createPQExpBuffer();
+	PQExpBuffer tbloids = createPQExpBuffer();
 	PGresult   *res;
+	int			ntups;
+	int			curtblindx;
 	IndxInfo   *indxinfo;
 	int			i_tableoid,
 				i_oid,
+				i_indrelid,
 				i_indexname,
 				i_parentidx,
 				i_indexdef,
@@ -6676,9 +6678,17 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 				i_indreloptions,
 				i_indstatcols,
 				i_indstatvals;
-	int			ntups;
 
-	for (i = 0; i < numTables; i++)
+	/*
+	 * We want to perform just one query against pg_index.  However, we
+	 * mustn't try to select every row of the catalog and then sort it out on
+	 * the client side, because some of the server-side functions we need
+	 * would be unsafe to apply to tables we don't have lock on.  Hence, we
+	 * build an array of the OIDs of tables we care about (and now have lock
+	 * on!), and use a WHERE clause to constrain which rows are selected.
+	 */
+	appendPQExpBufferChar(tbloids, '{');
+	for (int i = 0; i < numTables; i++)
 	{
 		TableInfo  *tbinfo = &tblinfo[i];
 
@@ -6691,232 +6701,270 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 		if (!tbinfo->interesting)
 			continue;
 
-		pg_log_info("reading indexes for table \"%s.%s\"",
-					tbinfo->dobj.namespace->dobj.name,
-					tbinfo->dobj.name);
+		/* OK, we need info for this table */
+		if (tbloids->len > 1)
+			appendPQExpBufferChar(tbloids, ',');
+		appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
+	}
+	appendPQExpBufferChar(tbloids, '}');
 
+	/*
+	 * The point of the messy-looking outer join is to find a constraint that
+	 * is related by an internal dependency link to the index. If we find one,
+	 * create a CONSTRAINT entry linked to the INDEX entry.  We assume an
+	 * index won't have more than one internal dependency.
+	 *
+	 * As of 9.0 we don't need to look at pg_depend but can check for a match
+	 * to pg_constraint.conindid.  The check on conrelid is redundant but
+	 * useful because that column is indexed while conindid is not.
+	 */
+	if (fout->remoteVersion >= 110000)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT t.tableoid, t.oid, i.indrelid, "
+						  "t.relname AS indexname, "
+						  "inh.inhparent AS parentidx, "
+						  "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
+						  "i.indnkeyatts AS indnkeyatts, "
+						  "i.indnatts AS indnatts, "
+						  "i.indkey, i.indisclustered, "
+						  "i.indisreplident, "
+						  "c.contype, c.conname, "
+						  "c.condeferrable, c.condeferred, "
+						  "c.tableoid AS contableoid, "
+						  "c.oid AS conoid, "
+						  "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
+						  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
+						  "t.reloptions AS indreloptions, "
+						  "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
+						  "  FROM pg_catalog.pg_attribute "
+						  "  WHERE attrelid = i.indexrelid AND "
+						  "    attstattarget >= 0) AS indstatcols,"
+						  "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
+						  "  FROM pg_catalog.pg_attribute "
+						  "  WHERE attrelid = i.indexrelid AND "
+						  "    attstattarget >= 0) AS indstatvals "
+						  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+						  "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
+						  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
+						  "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
+						  "LEFT JOIN pg_catalog.pg_constraint c "
+						  "ON (i.indrelid = c.conrelid AND "
+						  "i.indexrelid = c.conindid AND "
+						  "c.contype IN ('p','u','x')) "
+						  "LEFT JOIN pg_catalog.pg_inherits inh "
+						  "ON (inh.inhrelid = indexrelid) "
+						  "WHERE (i.indisvalid OR t2.relkind = 'p') "
+						  "AND i.indisready "
+						  "ORDER BY i.indrelid, indexname",
+						  tbloids->data);
+	}
+	else if (fout->remoteVersion >= 90400)
+	{
 		/*
-		 * The point of the messy-looking outer join is to find a constraint
-		 * that is related by an internal dependency link to the index. If we
-		 * find one, create a CONSTRAINT entry linked to the INDEX entry.  We
-		 * assume an index won't have more than one internal dependency.
-		 *
-		 * As of 9.0 we don't need to look at pg_depend but can check for a
-		 * match to pg_constraint.conindid.  The check on conrelid is
-		 * redundant but useful because that column is indexed while conindid
-		 * is not.
+		 * the test on indisready is necessary in 9.2, and harmless in
+		 * earlier/later versions
 		 */
-		resetPQExpBuffer(query);
-		if (fout->remoteVersion >= 110000)
-		{
-			appendPQExpBuffer(query,
-							  "SELECT t.tableoid, t.oid, "
-							  "t.relname AS indexname, "
-							  "inh.inhparent AS parentidx, "
-							  "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
-							  "i.indnkeyatts AS indnkeyatts, "
-							  "i.indnatts AS indnatts, "
-							  "i.indkey, i.indisclustered, "
-							  "i.indisreplident, "
-							  "c.contype, c.conname, "
-							  "c.condeferrable, c.condeferred, "
-							  "c.tableoid AS contableoid, "
-							  "c.oid AS conoid, "
-							  "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
-							  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
-							  "t.reloptions AS indreloptions, "
-							  "(SELECT pg_catalog.array_agg(attnum ORDER BY attnum) "
-							  "  FROM pg_catalog.pg_attribute "
-							  "  WHERE attrelid = i.indexrelid AND "
-							  "    attstattarget >= 0) AS indstatcols,"
-							  "(SELECT pg_catalog.array_agg(attstattarget ORDER BY attnum) "
-							  "  FROM pg_catalog.pg_attribute "
-							  "  WHERE attrelid = i.indexrelid AND "
-							  "    attstattarget >= 0) AS indstatvals "
-							  "FROM pg_catalog.pg_index i "
-							  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
-							  "JOIN pg_catalog.pg_class t2 ON (t2.oid = i.indrelid) "
-							  "LEFT JOIN pg_catalog.pg_constraint c "
-							  "ON (i.indrelid = c.conrelid AND "
-							  "i.indexrelid = c.conindid AND "
-							  "c.contype IN ('p','u','x')) "
-							  "LEFT JOIN pg_catalog.pg_inherits inh "
-							  "ON (inh.inhrelid = indexrelid) "
-							  "WHERE i.indrelid = '%u'::pg_catalog.oid "
-							  "AND (i.indisvalid OR t2.relkind = 'p') "
-							  "AND i.indisready "
-							  "ORDER BY indexname",
-							  tbinfo->dobj.catId.oid);
-		}
-		else if (fout->remoteVersion >= 90400)
-		{
-			/*
-			 * the test on indisready is necessary in 9.2, and harmless in
-			 * earlier/later versions
-			 */
-			appendPQExpBuffer(query,
-							  "SELECT t.tableoid, t.oid, "
-							  "t.relname AS indexname, "
-							  "0 AS parentidx, "
-							  "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
-							  "i.indnatts AS indnkeyatts, "
-							  "i.indnatts AS indnatts, "
-							  "i.indkey, i.indisclustered, "
-							  "i.indisreplident, "
-							  "c.contype, c.conname, "
-							  "c.condeferrable, c.condeferred, "
-							  "c.tableoid AS contableoid, "
-							  "c.oid AS conoid, "
-							  "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
-							  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
-							  "t.reloptions AS indreloptions, "
-							  "'' AS indstatcols, "
-							  "'' AS indstatvals "
-							  "FROM pg_catalog.pg_index i "
-							  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
-							  "LEFT JOIN pg_catalog.pg_constraint c "
-							  "ON (i.indrelid = c.conrelid AND "
-							  "i.indexrelid = c.conindid AND "
-							  "c.contype IN ('p','u','x')) "
-							  "WHERE i.indrelid = '%u'::pg_catalog.oid "
-							  "AND i.indisvalid AND i.indisready "
-							  "ORDER BY indexname",
-							  tbinfo->dobj.catId.oid);
-		}
-		else if (fout->remoteVersion >= 90000)
-		{
-			/*
-			 * the test on indisready is necessary in 9.2, and harmless in
-			 * earlier/later versions
-			 */
-			appendPQExpBuffer(query,
-							  "SELECT t.tableoid, t.oid, "
-							  "t.relname AS indexname, "
-							  "0 AS parentidx, "
-							  "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
-							  "i.indnatts AS indnkeyatts, "
-							  "i.indnatts AS indnatts, "
-							  "i.indkey, i.indisclustered, "
-							  "false AS indisreplident, "
-							  "c.contype, c.conname, "
-							  "c.condeferrable, c.condeferred, "
-							  "c.tableoid AS contableoid, "
-							  "c.oid AS conoid, "
-							  "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
-							  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
-							  "t.reloptions AS indreloptions, "
-							  "'' AS indstatcols, "
-							  "'' AS indstatvals "
-							  "FROM pg_catalog.pg_index i "
-							  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
-							  "LEFT JOIN pg_catalog.pg_constraint c "
-							  "ON (i.indrelid = c.conrelid AND "
-							  "i.indexrelid = c.conindid AND "
-							  "c.contype IN ('p','u','x')) "
-							  "WHERE i.indrelid = '%u'::pg_catalog.oid "
-							  "AND i.indisvalid AND i.indisready "
-							  "ORDER BY indexname",
-							  tbinfo->dobj.catId.oid);
-		}
-		else if (fout->remoteVersion >= 80200)
-		{
-			appendPQExpBuffer(query,
-							  "SELECT t.tableoid, t.oid, "
-							  "t.relname AS indexname, "
-							  "0 AS parentidx, "
-							  "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
-							  "i.indnatts AS indnkeyatts, "
-							  "i.indnatts AS indnatts, "
-							  "i.indkey, i.indisclustered, "
-							  "false AS indisreplident, "
-							  "c.contype, c.conname, "
-							  "c.condeferrable, c.condeferred, "
-							  "c.tableoid AS contableoid, "
-							  "c.oid AS conoid, "
-							  "null AS condef, "
-							  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
-							  "t.reloptions AS indreloptions, "
-							  "'' AS indstatcols, "
-							  "'' AS indstatvals "
-							  "FROM pg_catalog.pg_index i "
-							  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
-							  "LEFT JOIN pg_catalog.pg_depend d "
-							  "ON (d.classid = t.tableoid "
-							  "AND d.objid = t.oid "
-							  "AND d.deptype = 'i') "
-							  "LEFT JOIN pg_catalog.pg_constraint c "
-							  "ON (d.refclassid = c.tableoid "
-							  "AND d.refobjid = c.oid) "
-							  "WHERE i.indrelid = '%u'::pg_catalog.oid "
-							  "AND i.indisvalid "
-							  "ORDER BY indexname",
-							  tbinfo->dobj.catId.oid);
-		}
-		else
-		{
-			appendPQExpBuffer(query,
-							  "SELECT t.tableoid, t.oid, "
-							  "t.relname AS indexname, "
-							  "0 AS parentidx, "
-							  "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
-							  "t.relnatts AS indnkeyatts, "
-							  "t.relnatts AS indnatts, "
-							  "i.indkey, i.indisclustered, "
-							  "false AS indisreplident, "
-							  "c.contype, c.conname, "
-							  "c.condeferrable, c.condeferred, "
-							  "c.tableoid AS contableoid, "
-							  "c.oid AS conoid, "
-							  "null AS condef, "
-							  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
-							  "null AS indreloptions, "
-							  "'' AS indstatcols, "
-							  "'' AS indstatvals "
-							  "FROM pg_catalog.pg_index i "
-							  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
-							  "LEFT JOIN pg_catalog.pg_depend d "
-							  "ON (d.classid = t.tableoid "
-							  "AND d.objid = t.oid "
-							  "AND d.deptype = 'i') "
-							  "LEFT JOIN pg_catalog.pg_constraint c "
-							  "ON (d.refclassid = c.tableoid "
-							  "AND d.refobjid = c.oid) "
-							  "WHERE i.indrelid = '%u'::pg_catalog.oid "
-							  "ORDER BY indexname",
-							  tbinfo->dobj.catId.oid);
-		}
+		appendPQExpBuffer(query,
+						  "SELECT t.tableoid, t.oid, i.indrelid, "
+						  "t.relname AS indexname, "
+						  "0 AS parentidx, "
+						  "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
+						  "i.indnatts AS indnkeyatts, "
+						  "i.indnatts AS indnatts, "
+						  "i.indkey, i.indisclustered, "
+						  "i.indisreplident, "
+						  "c.contype, c.conname, "
+						  "c.condeferrable, c.condeferred, "
+						  "c.tableoid AS contableoid, "
+						  "c.oid AS conoid, "
+						  "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
+						  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
+						  "t.reloptions AS indreloptions, "
+						  "'' AS indstatcols, "
+						  "'' AS indstatvals "
+						  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+						  "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
+						  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
+						  "LEFT JOIN pg_catalog.pg_constraint c "
+						  "ON (i.indrelid = c.conrelid AND "
+						  "i.indexrelid = c.conindid AND "
+						  "c.contype IN ('p','u','x')) "
+						  "WHERE i.indisvalid AND i.indisready "
+						  "ORDER BY i.indrelid, indexname",
+						  tbloids->data);
+	}
+	else if (fout->remoteVersion >= 90000)
+	{
+		/*
+		 * the test on indisready is necessary in 9.2, and harmless in
+		 * earlier/later versions
+		 */
+		appendPQExpBuffer(query,
+						  "SELECT t.tableoid, t.oid, i.indrelid, "
+						  "t.relname AS indexname, "
+						  "0 AS parentidx, "
+						  "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
+						  "i.indnatts AS indnkeyatts, "
+						  "i.indnatts AS indnatts, "
+						  "i.indkey, i.indisclustered, "
+						  "false AS indisreplident, "
+						  "c.contype, c.conname, "
+						  "c.condeferrable, c.condeferred, "
+						  "c.tableoid AS contableoid, "
+						  "c.oid AS conoid, "
+						  "pg_catalog.pg_get_constraintdef(c.oid, false) AS condef, "
+						  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
+						  "t.reloptions AS indreloptions, "
+						  "'' AS indstatcols, "
+						  "'' AS indstatvals "
+						  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+						  "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
+						  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
+						  "LEFT JOIN pg_catalog.pg_constraint c "
+						  "ON (i.indrelid = c.conrelid AND "
+						  "i.indexrelid = c.conindid AND "
+						  "c.contype IN ('p','u','x')) "
+						  "WHERE i.indisvalid AND i.indisready "
+						  "ORDER BY i.indrelid, indexname",
+						  tbloids->data);
+	}
+	else if (fout->remoteVersion >= 80200)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT t.tableoid, t.oid, i.indrelid, "
+						  "t.relname AS indexname, "
+						  "0 AS parentidx, "
+						  "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
+						  "i.indnatts AS indnkeyatts, "
+						  "i.indnatts AS indnatts, "
+						  "i.indkey, i.indisclustered, "
+						  "false AS indisreplident, "
+						  "c.contype, c.conname, "
+						  "c.condeferrable, c.condeferred, "
+						  "c.tableoid AS contableoid, "
+						  "c.oid AS conoid, "
+						  "null AS condef, "
+						  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
+						  "t.reloptions AS indreloptions, "
+						  "'' AS indstatcols, "
+						  "'' AS indstatvals "
+						  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+						  "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
+						  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
+						  "LEFT JOIN pg_catalog.pg_depend d "
+						  "ON (d.classid = t.tableoid "
+						  "AND d.objid = t.oid "
+						  "AND d.deptype = 'i') "
+						  "LEFT JOIN pg_catalog.pg_constraint c "
+						  "ON (d.refclassid = c.tableoid "
+						  "AND d.refobjid = c.oid) "
+						  "WHERE i.indisvalid "
+						  "ORDER BY i.indrelid, indexname",
+						  tbloids->data);
+	}
+	else
+	{
+		appendPQExpBuffer(query,
+						  "SELECT t.tableoid, t.oid, i.indrelid, "
+						  "t.relname AS indexname, "
+						  "0 AS parentidx, "
+						  "pg_catalog.pg_get_indexdef(i.indexrelid) AS indexdef, "
+						  "t.relnatts AS indnkeyatts, "
+						  "t.relnatts AS indnatts, "
+						  "i.indkey, i.indisclustered, "
+						  "false AS indisreplident, "
+						  "c.contype, c.conname, "
+						  "c.condeferrable, c.condeferred, "
+						  "c.tableoid AS contableoid, "
+						  "c.oid AS conoid, "
+						  "null AS condef, "
+						  "(SELECT spcname FROM pg_catalog.pg_tablespace s WHERE s.oid = t.reltablespace) AS tablespace, "
+						  "null AS indreloptions, "
+						  "'' AS indstatcols, "
+						  "'' AS indstatvals "
+						  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+						  "JOIN pg_catalog.pg_index i ON (src.tbloid = i.indrelid) "
+						  "JOIN pg_catalog.pg_class t ON (t.oid = i.indexrelid) "
+						  "LEFT JOIN pg_catalog.pg_depend d "
+						  "ON (d.classid = t.tableoid "
+						  "AND d.objid = t.oid "
+						  "AND d.deptype = 'i') "
+						  "LEFT JOIN pg_catalog.pg_constraint c "
+						  "ON (d.refclassid = c.tableoid "
+						  "AND d.refobjid = c.oid) "
+						  "ORDER BY i.indrelid, indexname",
+						  tbloids->data);
+	}
 
-		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
-		ntups = PQntuples(res);
+	ntups = PQntuples(res);
 
-		i_tableoid = PQfnumber(res, "tableoid");
-		i_oid = PQfnumber(res, "oid");
-		i_indexname = PQfnumber(res, "indexname");
-		i_parentidx = PQfnumber(res, "parentidx");
-		i_indexdef = PQfnumber(res, "indexdef");
-		i_indnkeyatts = PQfnumber(res, "indnkeyatts");
-		i_indnatts = PQfnumber(res, "indnatts");
-		i_indkey = PQfnumber(res, "indkey");
-		i_indisclustered = PQfnumber(res, "indisclustered");
-		i_indisreplident = PQfnumber(res, "indisreplident");
-		i_contype = PQfnumber(res, "contype");
-		i_conname = PQfnumber(res, "conname");
-		i_condeferrable = PQfnumber(res, "condeferrable");
-		i_condeferred = PQfnumber(res, "condeferred");
-		i_contableoid = PQfnumber(res, "contableoid");
-		i_conoid = PQfnumber(res, "conoid");
-		i_condef = PQfnumber(res, "condef");
-		i_tablespace = PQfnumber(res, "tablespace");
-		i_indreloptions = PQfnumber(res, "indreloptions");
-		i_indstatcols = PQfnumber(res, "indstatcols");
-		i_indstatvals = PQfnumber(res, "indstatvals");
-
-		tbinfo->indexes = indxinfo =
-			(IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
-		tbinfo->numIndexes = ntups;
+	i_tableoid = PQfnumber(res, "tableoid");
+	i_oid = PQfnumber(res, "oid");
+	i_indrelid = PQfnumber(res, "indrelid");
+	i_indexname = PQfnumber(res, "indexname");
+	i_parentidx = PQfnumber(res, "parentidx");
+	i_indexdef = PQfnumber(res, "indexdef");
+	i_indnkeyatts = PQfnumber(res, "indnkeyatts");
+	i_indnatts = PQfnumber(res, "indnatts");
+	i_indkey = PQfnumber(res, "indkey");
+	i_indisclustered = PQfnumber(res, "indisclustered");
+	i_indisreplident = PQfnumber(res, "indisreplident");
+	i_contype = PQfnumber(res, "contype");
+	i_conname = PQfnumber(res, "conname");
+	i_condeferrable = PQfnumber(res, "condeferrable");
+	i_condeferred = PQfnumber(res, "condeferred");
+	i_contableoid = PQfnumber(res, "contableoid");
+	i_conoid = PQfnumber(res, "conoid");
+	i_condef = PQfnumber(res, "condef");
+	i_tablespace = PQfnumber(res, "tablespace");
+	i_indreloptions = PQfnumber(res, "indreloptions");
+	i_indstatcols = PQfnumber(res, "indstatcols");
+	i_indstatvals = PQfnumber(res, "indstatvals");
 
-		for (j = 0; j < ntups; j++)
+	indxinfo = (IndxInfo *) pg_malloc(ntups * sizeof(IndxInfo));
+
+	/*
+	 * Outer loop iterates once per table, not once per row.  Incrementing of
+	 * j is handled by the inner loop.
+	 */
+	curtblindx = -1;
+	for (int j = 0; j < ntups;)
+	{
+		Oid			indrelid = atooid(PQgetvalue(res, j, i_indrelid));
+		TableInfo  *tbinfo = NULL;
+		int			numinds;
+
+		/* Count rows for this table */
+		for (numinds = 1; numinds < ntups - j; numinds++)
+			if (atooid(PQgetvalue(res, j + numinds, i_indrelid)) != indrelid)
+				break;
+
+		/*
+		 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
+		 * order.
+		 */
+		while (++curtblindx < numTables)
+		{
+			tbinfo = &tblinfo[curtblindx];
+			if (tbinfo->dobj.catId.oid == indrelid)
+				break;
+		}
+		if (curtblindx >= numTables)
+			fatal("unrecognized table OID %u", indrelid);
+		/* cross-check that we only got requested tables */
+		if (!tbinfo->hasindex ||
+			!tbinfo->interesting)
+			fatal("unexpected index data for table \"%s\"",
+				  tbinfo->dobj.name);
+
+		/* Save data for this table */
+		tbinfo->indexes = indxinfo + j;
+		tbinfo->numIndexes = numinds;
+
+		for (int c = 0; c < numinds; c++, j++)
 		{
 			char		contype;
 
@@ -6985,11 +7033,12 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 				indxinfo[j].indexconstraint = 0;
 			}
 		}
-
-		PQclear(res);
 	}
 
+	PQclear(res);
+
 	destroyPQExpBuffer(query);
+	destroyPQExpBuffer(tbloids);
 }
 
 /*
@@ -7076,22 +7125,31 @@ getExtendedStatistics(Archive *fout)
 void
 getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
 {
-	int			i,
-				j;
-	ConstraintInfo *constrinfo;
-	PQExpBuffer query;
+	PQExpBuffer query = createPQExpBuffer();
+	PQExpBuffer tbloids = createPQExpBuffer();
 	PGresult   *res;
+	int			ntups;
+	int			curtblindx;
+	TableInfo  *tbinfo = NULL;
+	ConstraintInfo *constrinfo;
 	int			i_contableoid,
 				i_conoid,
+				i_conrelid,
 				i_conname,
 				i_confrelid,
 				i_conindid,
 				i_condef;
-	int			ntups;
 
-	query = createPQExpBuffer();
-
-	for (i = 0; i < numTables; i++)
+	/*
+	 * We want to perform just one query against pg_constraint.  However, we
+	 * mustn't try to select every row of the catalog and then sort it out on
+	 * the client side, because some of the server-side functions we need
+	 * would be unsafe to apply to tables we don't have lock on.  Hence, we
+	 * build an array of the OIDs of tables we care about (and now have lock
+	 * on!), and use a WHERE clause to constrain which rows are selected.
+	 */
+	appendPQExpBufferChar(tbloids, '{');
+	for (int i = 0; i < numTables; i++)
 	{
 		TableInfo  *tbinfo = &tblinfo[i];
 
@@ -7104,95 +7162,118 @@ getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
 			!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
 			continue;
 
-		pg_log_info("reading foreign key constraints for table \"%s.%s\"",
-					tbinfo->dobj.namespace->dobj.name,
-					tbinfo->dobj.name);
+		/* OK, we need info for this table */
+		if (tbloids->len > 1)
+			appendPQExpBufferChar(tbloids, ',');
+		appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
+	}
+	appendPQExpBufferChar(tbloids, '}');
 
-		resetPQExpBuffer(query);
-		if (fout->remoteVersion >= 110000)
-			appendPQExpBuffer(query,
-							  "SELECT tableoid, oid, conname, confrelid, conindid, "
-							  "pg_catalog.pg_get_constraintdef(oid) AS condef "
-							  "FROM pg_catalog.pg_constraint "
-							  "WHERE conrelid = '%u'::pg_catalog.oid "
-							  "AND conparentid = 0 "
-							  "AND contype = 'f'",
-							  tbinfo->dobj.catId.oid);
-		else
-			appendPQExpBuffer(query,
-							  "SELECT tableoid, oid, conname, confrelid, 0 as conindid, "
-							  "pg_catalog.pg_get_constraintdef(oid) AS condef "
-							  "FROM pg_catalog.pg_constraint "
-							  "WHERE conrelid = '%u'::pg_catalog.oid "
-							  "AND contype = 'f'",
-							  tbinfo->dobj.catId.oid);
-		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	appendPQExpBufferStr(query,
+						 "SELECT c.tableoid, c.oid, "
+						 "conrelid, conname, confrelid, ");
+	if (fout->remoteVersion >= 110000)
+		appendPQExpBufferStr(query, "conindid, ");
+	else
+		appendPQExpBufferStr(query, "0 AS conindid, ");
+	appendPQExpBuffer(query,
+					  "pg_catalog.pg_get_constraintdef(c.oid) AS condef\n"
+					  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+					  "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
+					  "WHERE contype = 'f' ",
+					  tbloids->data);
+	if (fout->remoteVersion >= 110000)
+		appendPQExpBufferStr(query,
+							 "AND conparentid = 0 ");
+	appendPQExpBufferStr(query,
+						 "ORDER BY conrelid, conname");
 
-		ntups = PQntuples(res);
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
-		i_contableoid = PQfnumber(res, "tableoid");
-		i_conoid = PQfnumber(res, "oid");
-		i_conname = PQfnumber(res, "conname");
-		i_confrelid = PQfnumber(res, "confrelid");
-		i_conindid = PQfnumber(res, "conindid");
-		i_condef = PQfnumber(res, "condef");
+	ntups = PQntuples(res);
 
-		constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
+	i_contableoid = PQfnumber(res, "tableoid");
+	i_conoid = PQfnumber(res, "oid");
+	i_conrelid = PQfnumber(res, "conrelid");
+	i_conname = PQfnumber(res, "conname");
+	i_confrelid = PQfnumber(res, "confrelid");
+	i_conindid = PQfnumber(res, "conindid");
+	i_condef = PQfnumber(res, "condef");
 
-		for (j = 0; j < ntups; j++)
-		{
-			TableInfo  *reftable;
-
-			constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
-			constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
-			constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
-			AssignDumpId(&constrinfo[j].dobj);
-			constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
-			constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
-			constrinfo[j].contable = tbinfo;
-			constrinfo[j].condomain = NULL;
-			constrinfo[j].contype = 'f';
-			constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
-			constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
-			constrinfo[j].conindex = 0;
-			constrinfo[j].condeferrable = false;
-			constrinfo[j].condeferred = false;
-			constrinfo[j].conislocal = true;
-			constrinfo[j].separate = true;
+	constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
 
-			/*
-			 * Restoring an FK that points to a partitioned table requires
-			 * that all partition indexes have been attached beforehand.
-			 * Ensure that happens by making the constraint depend on each
-			 * index partition attach object.
-			 */
-			reftable = findTableByOid(constrinfo[j].confrelid);
-			if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
+	curtblindx = -1;
+	for (int j = 0; j < ntups; j++)
+	{
+		Oid			conrelid = atooid(PQgetvalue(res, j, i_conrelid));
+		TableInfo  *reftable;
+
+		/*
+		 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
+		 * order.
+		 */
+		if (tbinfo == NULL || tbinfo->dobj.catId.oid != conrelid)
+		{
+			while (++curtblindx < numTables)
 			{
-				Oid			indexOid = atooid(PQgetvalue(res, j, i_conindid));
+				tbinfo = &tblinfo[curtblindx];
+				if (tbinfo->dobj.catId.oid == conrelid)
+					break;
+			}
+			if (curtblindx >= numTables)
+				fatal("unrecognized table OID %u", conrelid);
+		}
+
+		constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
+		constrinfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_contableoid));
+		constrinfo[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_conoid));
+		AssignDumpId(&constrinfo[j].dobj);
+		constrinfo[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
+		constrinfo[j].dobj.namespace = tbinfo->dobj.namespace;
+		constrinfo[j].contable = tbinfo;
+		constrinfo[j].condomain = NULL;
+		constrinfo[j].contype = 'f';
+		constrinfo[j].condef = pg_strdup(PQgetvalue(res, j, i_condef));
+		constrinfo[j].confrelid = atooid(PQgetvalue(res, j, i_confrelid));
+		constrinfo[j].conindex = 0;
+		constrinfo[j].condeferrable = false;
+		constrinfo[j].condeferred = false;
+		constrinfo[j].conislocal = true;
+		constrinfo[j].separate = true;
+
+		/*
+		 * Restoring an FK that points to a partitioned table requires that
+		 * all partition indexes have been attached beforehand. Ensure that
+		 * happens by making the constraint depend on each index partition
+		 * attach object.
+		 */
+		reftable = findTableByOid(constrinfo[j].confrelid);
+		if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE)
+		{
+			Oid			indexOid = atooid(PQgetvalue(res, j, i_conindid));
 
-				if (indexOid != InvalidOid)
+			if (indexOid != InvalidOid)
+			{
+				for (int k = 0; k < reftable->numIndexes; k++)
 				{
-					for (int k = 0; k < reftable->numIndexes; k++)
-					{
-						IndxInfo   *refidx;
+					IndxInfo   *refidx;
 
-						/* not our index? */
-						if (reftable->indexes[k].dobj.catId.oid != indexOid)
-							continue;
+					/* not our index? */
+					if (reftable->indexes[k].dobj.catId.oid != indexOid)
+						continue;
 
-						refidx = &reftable->indexes[k];
-						addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
-						break;
-					}
+					refidx = &reftable->indexes[k];
+					addConstrChildIdxDeps(&constrinfo[j].dobj, refidx);
+					break;
 				}
 			}
 		}
-
-		PQclear(res);
 	}
 
+	PQclear(res);
+
 	destroyPQExpBuffer(query);
+	destroyPQExpBuffer(tbloids);
 }
 
 /*
@@ -7436,13 +7517,15 @@ getRules(Archive *fout, int *numRules)
 void
 getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
 {
-	int			i,
-				j;
 	PQExpBuffer query = createPQExpBuffer();
+	PQExpBuffer tbloids = createPQExpBuffer();
 	PGresult   *res;
+	int			ntups;
+	int			curtblindx;
 	TriggerInfo *tginfo;
 	int			i_tableoid,
 				i_oid,
+				i_tgrelid,
 				i_tgname,
 				i_tgfname,
 				i_tgtype,
@@ -7457,9 +7540,17 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
 				i_tgdeferrable,
 				i_tginitdeferred,
 				i_tgdef;
-	int			ntups;
 
-	for (i = 0; i < numTables; i++)
+	/*
+	 * We want to perform just one query against pg_trigger.  However, we
+	 * mustn't try to select every row of the catalog and then sort it out on
+	 * the client side, because some of the server-side functions we need
+	 * would be unsafe to apply to tables we don't have lock on.  Hence, we
+	 * build an array of the OIDs of tables we care about (and now have lock
+	 * on!), and use a WHERE clause to constrain which rows are selected.
+	 */
+	appendPQExpBufferChar(tbloids, '{');
+	for (int i = 0; i < numTables; i++)
 	{
 		TableInfo  *tbinfo = &tblinfo[i];
 
@@ -7467,143 +7558,178 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
 			!(tbinfo->dobj.dump & DUMP_COMPONENT_DEFINITION))
 			continue;
 
-		pg_log_info("reading triggers for table \"%s.%s\"",
-					tbinfo->dobj.namespace->dobj.name,
-					tbinfo->dobj.name);
+		/* OK, we need info for this table */
+		if (tbloids->len > 1)
+			appendPQExpBufferChar(tbloids, ',');
+		appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
+	}
+	appendPQExpBufferChar(tbloids, '}');
 
-		resetPQExpBuffer(query);
-		if (fout->remoteVersion >= 130000)
-		{
-			/*
-			 * NB: think not to use pretty=true in pg_get_triggerdef.  It
-			 * could result in non-forward-compatible dumps of WHEN clauses
-			 * due to under-parenthesization.
-			 *
-			 * NB: We need to see tgisinternal triggers in partitions, in case
-			 * the tgenabled flag has been changed from the parent.
-			 */
-			appendPQExpBuffer(query,
-							  "SELECT t.tgname, "
-							  "t.tgfoid::pg_catalog.regproc AS tgfname, "
-							  "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
-							  "t.tgenabled, t.tableoid, t.oid, t.tgisinternal "
-							  "FROM pg_catalog.pg_trigger t "
-							  "LEFT JOIN pg_catalog.pg_trigger u ON u.oid = t.tgparentid "
-							  "WHERE t.tgrelid = '%u'::pg_catalog.oid "
-							  "AND (NOT t.tgisinternal OR t.tgenabled != u.tgenabled)",
-							  tbinfo->dobj.catId.oid);
-		}
-		else if (fout->remoteVersion >= 110000)
-		{
-			/*
-			 * NB: We need to see tgisinternal triggers in partitions, in case
-			 * the tgenabled flag has been changed from the parent. No
-			 * tgparentid in version 11-12, so we have to match them via
-			 * pg_depend.
-			 *
-			 * See above about pretty=true in pg_get_triggerdef.
-			 */
-			appendPQExpBuffer(query,
-							  "SELECT t.tgname, "
-							  "t.tgfoid::pg_catalog.regproc AS tgfname, "
-							  "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
-							  "t.tgenabled, t.tableoid, t.oid, t.tgisinternal "
-							  "FROM pg_catalog.pg_trigger t "
-							  "LEFT JOIN pg_catalog.pg_depend AS d ON "
-							  " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
-							  " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
-							  " d.objid = t.oid "
-							  "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
-							  "WHERE t.tgrelid = '%u'::pg_catalog.oid "
-							  "AND (NOT t.tgisinternal%s)",
-							  tbinfo->dobj.catId.oid,
-							  tbinfo->ispartition ?
-							  " OR t.tgenabled != pt.tgenabled" : "");
-		}
-		else if (fout->remoteVersion >= 90000)
-		{
-			/* See above about pretty=true in pg_get_triggerdef */
-			appendPQExpBuffer(query,
-							  "SELECT t.tgname, "
-							  "t.tgfoid::pg_catalog.regproc AS tgfname, "
-							  "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
-							  "t.tgenabled, false as tgisinternal, "
-							  "t.tableoid, t.oid "
-							  "FROM pg_catalog.pg_trigger t "
-							  "WHERE tgrelid = '%u'::pg_catalog.oid "
-							  "AND NOT tgisinternal",
-							  tbinfo->dobj.catId.oid);
-		}
-		else if (fout->remoteVersion >= 80300)
-		{
-			/*
-			 * We ignore triggers that are tied to a foreign-key constraint
-			 */
-			appendPQExpBuffer(query,
-							  "SELECT tgname, "
-							  "tgfoid::pg_catalog.regproc AS tgfname, "
-							  "tgtype, tgnargs, tgargs, tgenabled, "
-							  "false as tgisinternal, "
-							  "tgisconstraint, tgconstrname, tgdeferrable, "
-							  "tgconstrrelid, tginitdeferred, tableoid, oid, "
-							  "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname "
-							  "FROM pg_catalog.pg_trigger t "
-							  "WHERE tgrelid = '%u'::pg_catalog.oid "
-							  "AND tgconstraint = 0",
-							  tbinfo->dobj.catId.oid);
-		}
-		else
-		{
-			/*
-			 * We ignore triggers that are tied to a foreign-key constraint,
-			 * but in these versions we have to grovel through pg_constraint
-			 * to find out
-			 */
-			appendPQExpBuffer(query,
-							  "SELECT tgname, "
-							  "tgfoid::pg_catalog.regproc AS tgfname, "
-							  "tgtype, tgnargs, tgargs, tgenabled, "
-							  "false as tgisinternal, "
-							  "tgisconstraint, tgconstrname, tgdeferrable, "
-							  "tgconstrrelid, tginitdeferred, tableoid, oid, "
-							  "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname "
-							  "FROM pg_catalog.pg_trigger t "
-							  "WHERE tgrelid = '%u'::pg_catalog.oid "
-							  "AND (NOT tgisconstraint "
-							  " OR NOT EXISTS"
-							  "  (SELECT 1 FROM pg_catalog.pg_depend d "
-							  "   JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
-							  "   WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))",
-							  tbinfo->dobj.catId.oid);
-		}
+	if (fout->remoteVersion >= 130000)
+	{
+		/*
+		 * NB: think not to use pretty=true in pg_get_triggerdef.  It could
+		 * result in non-forward-compatible dumps of WHEN clauses due to
+		 * under-parenthesization.
+		 *
+		 * NB: We need to see tgisinternal triggers in partitions, in case the
+		 * tgenabled flag has been changed from the parent.
+		 */
+		appendPQExpBuffer(query,
+						  "SELECT t.tgrelid, t.tgname, "
+						  "t.tgfoid::pg_catalog.regproc AS tgfname, "
+						  "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
+						  "t.tgenabled, t.tableoid, t.oid, t.tgisinternal\n"
+						  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+						  "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
+						  "LEFT JOIN pg_catalog.pg_trigger u ON (u.oid = t.tgparentid) "
+						  "WHERE (NOT t.tgisinternal OR t.tgenabled != u.tgenabled) "
+						  "ORDER BY t.tgrelid, t.tgname",
+						  tbloids->data);
+	}
+	else if (fout->remoteVersion >= 110000)
+	{
+		/*
+		 * NB: We need to see tgisinternal triggers in partitions, in case the
+		 * tgenabled flag has been changed from the parent. No tgparentid in
+		 * version 11-12, so we have to match them via pg_depend.
+		 *
+		 * See above about pretty=true in pg_get_triggerdef.
+		 */
+		appendPQExpBuffer(query,
+						  "SELECT t.tgrelid, t.tgname, "
+						  "t.tgfoid::pg_catalog.regproc AS tgfname, "
+						  "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
+						  "t.tgenabled, t.tableoid, t.oid, t.tgisinternal "
+						  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+						  "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
+						  "LEFT JOIN pg_catalog.pg_depend AS d ON "
+						  " d.classid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
+						  " d.refclassid = 'pg_catalog.pg_trigger'::pg_catalog.regclass AND "
+						  " d.objid = t.oid "
+						  "LEFT JOIN pg_catalog.pg_trigger AS pt ON pt.oid = refobjid "
+						  "WHERE (NOT t.tgisinternal OR t.tgenabled != pt.tgenabled) "
+						  "ORDER BY t.tgrelid, t.tgname",
+						  tbloids->data);
+	}
+	else if (fout->remoteVersion >= 90000)
+	{
+		/* See above about pretty=true in pg_get_triggerdef */
+		appendPQExpBuffer(query,
+						  "SELECT t.tgrelid, t.tgname, "
+						  "t.tgfoid::pg_catalog.regproc AS tgfname, "
+						  "pg_catalog.pg_get_triggerdef(t.oid, false) AS tgdef, "
+						  "t.tgenabled, false as tgisinternal, "
+						  "t.tableoid, t.oid "
+						  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+						  "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
+						  "WHERE NOT tgisinternal "
+						  "ORDER BY t.tgrelid, t.tgname",
+						  tbloids->data);
+	}
+	else if (fout->remoteVersion >= 80300)
+	{
+		/*
+		 * We ignore triggers that are tied to a foreign-key constraint
+		 */
+		appendPQExpBuffer(query,
+						  "SELECT t.tgrelid, tgname, "
+						  "tgfoid::pg_catalog.regproc AS tgfname, "
+						  "tgtype, tgnargs, tgargs, tgenabled, "
+						  "false as tgisinternal, "
+						  "tgisconstraint, tgconstrname, tgdeferrable, "
+						  "tgconstrrelid, tginitdeferred, t.tableoid, t.oid, "
+						  "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname "
+						  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+						  "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
+						  "WHERE tgconstraint = 0 "
+						  "ORDER BY t.tgrelid, t.tgname",
+						  tbloids->data);
+	}
+	else
+	{
+		/*
+		 * We ignore triggers that are tied to a foreign-key constraint, but
+		 * in these versions we have to grovel through pg_constraint to find
+		 * out
+		 */
+		appendPQExpBuffer(query,
+						  "SELECT t.tgrelid, tgname, "
+						  "tgfoid::pg_catalog.regproc AS tgfname, "
+						  "tgtype, tgnargs, tgargs, tgenabled, "
+						  "false as tgisinternal, "
+						  "tgisconstraint, tgconstrname, tgdeferrable, "
+						  "tgconstrrelid, tginitdeferred, t.tableoid, t.oid, "
+						  "tgconstrrelid::pg_catalog.regclass AS tgconstrrelname "
+						  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+						  "JOIN pg_catalog.pg_trigger t ON (src.tbloid = t.tgrelid) "
+						  "WHERE (NOT tgisconstraint "
+						  " OR NOT EXISTS"
+						  "  (SELECT 1 FROM pg_catalog.pg_depend d "
+						  "   JOIN pg_catalog.pg_constraint c ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) "
+						  "   WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f')) "
+						  "ORDER BY t.tgrelid, t.tgname",
+						  tbloids->data);
+	}
 
-		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
-		ntups = PQntuples(res);
+	ntups = PQntuples(res);
 
-		i_tableoid = PQfnumber(res, "tableoid");
-		i_oid = PQfnumber(res, "oid");
-		i_tgname = PQfnumber(res, "tgname");
-		i_tgfname = PQfnumber(res, "tgfname");
-		i_tgtype = PQfnumber(res, "tgtype");
-		i_tgnargs = PQfnumber(res, "tgnargs");
-		i_tgargs = PQfnumber(res, "tgargs");
-		i_tgisconstraint = PQfnumber(res, "tgisconstraint");
-		i_tgconstrname = PQfnumber(res, "tgconstrname");
-		i_tgconstrrelid = PQfnumber(res, "tgconstrrelid");
-		i_tgconstrrelname = PQfnumber(res, "tgconstrrelname");
-		i_tgenabled = PQfnumber(res, "tgenabled");
-		i_tgisinternal = PQfnumber(res, "tgisinternal");
-		i_tgdeferrable = PQfnumber(res, "tgdeferrable");
-		i_tginitdeferred = PQfnumber(res, "tginitdeferred");
-		i_tgdef = PQfnumber(res, "tgdef");
-
-		tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
-
-		tbinfo->numTriggers = ntups;
-		tbinfo->triggers = tginfo;
+	i_tableoid = PQfnumber(res, "tableoid");
+	i_oid = PQfnumber(res, "oid");
+	i_tgrelid = PQfnumber(res, "tgrelid");
+	i_tgname = PQfnumber(res, "tgname");
+	i_tgfname = PQfnumber(res, "tgfname");
+	i_tgtype = PQfnumber(res, "tgtype");
+	i_tgnargs = PQfnumber(res, "tgnargs");
+	i_tgargs = PQfnumber(res, "tgargs");
+	i_tgisconstraint = PQfnumber(res, "tgisconstraint");
+	i_tgconstrname = PQfnumber(res, "tgconstrname");
+	i_tgconstrrelid = PQfnumber(res, "tgconstrrelid");
+	i_tgconstrrelname = PQfnumber(res, "tgconstrrelname");
+	i_tgenabled = PQfnumber(res, "tgenabled");
+	i_tgisinternal = PQfnumber(res, "tgisinternal");
+	i_tgdeferrable = PQfnumber(res, "tgdeferrable");
+	i_tginitdeferred = PQfnumber(res, "tginitdeferred");
+	i_tgdef = PQfnumber(res, "tgdef");
+
+	tginfo = (TriggerInfo *) pg_malloc(ntups * sizeof(TriggerInfo));
 
-		for (j = 0; j < ntups; j++)
+	/*
+	 * Outer loop iterates once per table, not once per row.  Incrementing of
+	 * j is handled by the inner loop.
+	 */
+	curtblindx = -1;
+	for (int j = 0; j < ntups;)
+	{
+		Oid			tgrelid = atooid(PQgetvalue(res, j, i_tgrelid));
+		TableInfo  *tbinfo = NULL;
+		int			numtrigs;
+
+		/* Count rows for this table */
+		for (numtrigs = 1; numtrigs < ntups - j; numtrigs++)
+			if (atooid(PQgetvalue(res, j + numtrigs, i_tgrelid)) != tgrelid)
+				break;
+
+		/*
+		 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
+		 * order.
+		 */
+		while (++curtblindx < numTables)
+		{
+			tbinfo = &tblinfo[curtblindx];
+			if (tbinfo->dobj.catId.oid == tgrelid)
+				break;
+		}
+		if (curtblindx >= numTables)
+			fatal("unrecognized table OID %u", tgrelid);
+
+		/* Save data for this table */
+		tbinfo->triggers = tginfo + j;
+		tbinfo->numTriggers = numtrigs;
+
+		for (int c = 0; c < numtrigs; c++, j++)
 		{
 			tginfo[j].dobj.objType = DO_TRIGGER;
 			tginfo[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
@@ -7666,11 +7792,12 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
 				}
 			}
 		}
-
-		PQclear(res);
 	}
 
+	PQclear(res);
+
 	destroyPQExpBuffer(query);
+	destroyPQExpBuffer(tbloids);
 }
 
 /*
@@ -8124,12 +8251,6 @@ getTransforms(Archive *fout, int *numTransforms)
  *	  for each interesting table, read info about its attributes
  *	  (names, types, default values, CHECK constraints, etc)
  *
- * This is implemented in a very inefficient way right now, looping
- * through the tblinfo and doing a join per table to find the attrs and their
- * types.  However, because we want type names and so forth to be named
- * relative to the schema of each table, we couldn't do it in just one
- * query.  (Maybe one query per schema?)
- *
  *	modifies tblinfo
  */
 void
@@ -8137,6 +8258,12 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 {
 	DumpOptions *dopt = fout->dopt;
 	PQExpBuffer q = createPQExpBuffer();
+	PQExpBuffer tbloids = createPQExpBuffer();
+	PQExpBuffer checkoids = createPQExpBuffer();
+	PGresult   *res;
+	int			ntups;
+	int			curtblindx;
+	int			i_attrelid;
 	int			i_attnum;
 	int			i_attname;
 	int			i_atttypname;
@@ -8158,12 +8285,21 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 	int			i_attmissingval;
 	int			i_atthasdef;
 
+	/*
+	 * We want to perform just one query against pg_attribute, and then just
+	 * one against pg_attrdef (for DEFAULTs) and one against pg_constraint
+	 * (for CHECK constraints).  However, we mustn't try to select every row
+	 * of those catalogs and then sort it out on the client side, because some
+	 * of the server-side functions we need would be unsafe to apply to tables
+	 * we don't have lock on.  Hence, we build an array of the OIDs of tables
+	 * we care about (and now have lock on!), and use a WHERE clause to
+	 * constrain which rows are selected.
+	 */
+	appendPQExpBufferChar(tbloids, '{');
+	appendPQExpBufferChar(checkoids, '{');
 	for (int i = 0; i < numTables; i++)
 	{
 		TableInfo  *tbinfo = &tblinfo[i];
-		PGresult   *res;
-		int			ntups;
-		bool		hasdefaults;
 
 		/* Don't bother to collect info for sequences */
 		if (tbinfo->relkind == RELKIND_SEQUENCE)
@@ -8173,390 +8309,499 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 		if (!tbinfo->interesting)
 			continue;
 
-		/* find all the user attributes and their types */
+		/* OK, we need info for this table */
+		if (tbloids->len > 1)
+			appendPQExpBufferChar(tbloids, ',');
+		appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
+
+		if (tbinfo->ncheck > 0)
+		{
+			/* Also make a list of the ones with check constraints */
+			if (checkoids->len > 1)
+				appendPQExpBufferChar(checkoids, ',');
+			appendPQExpBuffer(checkoids, "%u", tbinfo->dobj.catId.oid);
+		}
+	}
+	appendPQExpBufferChar(tbloids, '}');
+	appendPQExpBufferChar(checkoids, '}');
+
+	/* find all the user attributes and their types */
+	appendPQExpBufferStr(q,
+						 "SELECT\n"
+						 "a.attrelid,\n"
+						 "a.attnum,\n"
+						 "a.attname,\n"
+						 "a.atttypmod,\n"
+						 "a.attstattarget,\n"
+						 "a.attstorage,\n"
+						 "t.typstorage,\n"
+						 "a.attnotnull,\n"
+						 "a.atthasdef,\n"
+						 "a.attisdropped,\n"
+						 "a.attlen,\n"
+						 "a.attalign,\n"
+						 "a.attislocal,\n"
+						 "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n");
 
+	if (fout->remoteVersion >= 90000)
+		appendPQExpBufferStr(q,
+							 "array_to_string(a.attoptions, ', ') AS attoptions,\n");
+	else
+		appendPQExpBufferStr(q,
+							 "'' AS attoptions,\n");
+
+	if (fout->remoteVersion >= 90100)
+	{
 		/*
-		 * we must read the attribute names in attribute number order! because
-		 * we will use the attnum to index into the attnames array later.
+		 * Since we only want to dump COLLATE clauses for attributes whose
+		 * collation is different from their type's default, we use a CASE
+		 * here to suppress uninteresting attcollations cheaply.
 		 */
-		pg_log_info("finding the columns and types of table \"%s.%s\"",
-					tbinfo->dobj.namespace->dobj.name,
-					tbinfo->dobj.name);
+		appendPQExpBufferStr(q,
+							 "CASE WHEN a.attcollation <> t.typcollation "
+							 "THEN a.attcollation ELSE 0 END AS attcollation,\n");
+	}
+	else
+		appendPQExpBufferStr(q,
+							 "0 AS attcollation,\n");
 
-		resetPQExpBuffer(q);
+	if (fout->remoteVersion >= 140000)
+		appendPQExpBufferStr(q,
+							 "a.attcompression AS attcompression,\n");
+	else
+		appendPQExpBufferStr(q,
+							 "'' AS attcompression,\n");
 
+	if (fout->remoteVersion >= 90200)
 		appendPQExpBufferStr(q,
-							 "SELECT\n"
-							 "a.attnum,\n"
-							 "a.attname,\n"
-							 "a.atttypmod,\n"
-							 "a.attstattarget,\n"
-							 "a.attstorage,\n"
-							 "t.typstorage,\n"
-							 "a.attnotnull,\n"
-							 "a.atthasdef,\n"
-							 "a.attisdropped,\n"
-							 "a.attlen,\n"
-							 "a.attalign,\n"
-							 "a.attislocal,\n"
-							 "pg_catalog.format_type(t.oid, a.atttypmod) AS atttypname,\n");
-
-		if (fout->remoteVersion >= 90000)
-			appendPQExpBufferStr(q,
-								 "array_to_string(a.attoptions, ', ') AS attoptions,\n");
-		else
-			appendPQExpBufferStr(q,
-								 "'' AS attoptions,\n");
+							 "pg_catalog.array_to_string(ARRAY("
+							 "SELECT pg_catalog.quote_ident(option_name) || "
+							 "' ' || pg_catalog.quote_literal(option_value) "
+							 "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
+							 "ORDER BY option_name"
+							 "), E',\n    ') AS attfdwoptions,\n");
+	else
+		appendPQExpBufferStr(q,
+							 "'' AS attfdwoptions,\n");
 
-		if (fout->remoteVersion >= 90100)
-		{
-			/*
-			 * Since we only want to dump COLLATE clauses for attributes whose
-			 * collation is different from their type's default, we use a CASE
-			 * here to suppress uninteresting attcollations cheaply.
-			 */
-			appendPQExpBufferStr(q,
-								 "CASE WHEN a.attcollation <> t.typcollation "
-								 "THEN a.attcollation ELSE 0 END AS attcollation,\n");
-		}
-		else
-			appendPQExpBufferStr(q,
-								 "0 AS attcollation,\n");
+	if (fout->remoteVersion >= 100000)
+		appendPQExpBufferStr(q,
+							 "a.attidentity,\n");
+	else
+		appendPQExpBufferStr(q,
+							 "'' AS attidentity,\n");
 
-		if (fout->remoteVersion >= 140000)
-			appendPQExpBuffer(q,
-							  "a.attcompression AS attcompression,\n");
-		else
-			appendPQExpBuffer(q,
-							  "'' AS attcompression,\n");
+	if (fout->remoteVersion >= 110000)
+		appendPQExpBufferStr(q,
+							 "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
+							 "THEN a.attmissingval ELSE null END AS attmissingval,\n");
+	else
+		appendPQExpBufferStr(q,
+							 "NULL AS attmissingval,\n");
 
-		if (fout->remoteVersion >= 90200)
-			appendPQExpBufferStr(q,
-								 "pg_catalog.array_to_string(ARRAY("
-								 "SELECT pg_catalog.quote_ident(option_name) || "
-								 "' ' || pg_catalog.quote_literal(option_value) "
-								 "FROM pg_catalog.pg_options_to_table(attfdwoptions) "
-								 "ORDER BY option_name"
-								 "), E',\n    ') AS attfdwoptions,\n");
-		else
-			appendPQExpBufferStr(q,
-								 "'' AS attfdwoptions,\n");
+	if (fout->remoteVersion >= 120000)
+		appendPQExpBufferStr(q,
+							 "a.attgenerated\n");
+	else
+		appendPQExpBufferStr(q,
+							 "'' AS attgenerated\n");
 
-		if (fout->remoteVersion >= 100000)
-			appendPQExpBufferStr(q,
-								 "a.attidentity,\n");
-		else
-			appendPQExpBufferStr(q,
-								 "'' AS attidentity,\n");
+	/* need left join to pg_type to not fail on dropped columns ... */
+	appendPQExpBuffer(q,
+					  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+					  "JOIN pg_catalog.pg_attribute a ON (src.tbloid = a.attrelid) "
+					  "LEFT JOIN pg_catalog.pg_type t "
+					  "ON (a.atttypid = t.oid)\n"
+					  "WHERE a.attnum > 0::pg_catalog.int2\n"
+					  "ORDER BY a.attrelid, a.attnum",
+					  tbloids->data);
 
-		if (fout->remoteVersion >= 110000)
-			appendPQExpBufferStr(q,
-								 "CASE WHEN a.atthasmissing AND NOT a.attisdropped "
-								 "THEN a.attmissingval ELSE null END AS attmissingval,\n");
-		else
-			appendPQExpBufferStr(q,
-								 "NULL AS attmissingval,\n");
+	res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
 
-		if (fout->remoteVersion >= 120000)
-			appendPQExpBufferStr(q,
-								 "a.attgenerated\n");
-		else
-			appendPQExpBufferStr(q,
-								 "'' AS attgenerated\n");
+	ntups = PQntuples(res);
 
-		/* need left join here to not fail on dropped columns ... */
-		appendPQExpBuffer(q,
-						  "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
-						  "ON a.atttypid = t.oid\n"
-						  "WHERE a.attrelid = '%u'::pg_catalog.oid "
-						  "AND a.attnum > 0::pg_catalog.int2\n"
-						  "ORDER BY a.attnum",
-						  tbinfo->dobj.catId.oid);
+	i_attrelid = PQfnumber(res, "attrelid");
+	i_attnum = PQfnumber(res, "attnum");
+	i_attname = PQfnumber(res, "attname");
+	i_atttypname = PQfnumber(res, "atttypname");
+	i_atttypmod = PQfnumber(res, "atttypmod");
+	i_attstattarget = PQfnumber(res, "attstattarget");
+	i_attstorage = PQfnumber(res, "attstorage");
+	i_typstorage = PQfnumber(res, "typstorage");
+	i_attidentity = PQfnumber(res, "attidentity");
+	i_attgenerated = PQfnumber(res, "attgenerated");
+	i_attisdropped = PQfnumber(res, "attisdropped");
+	i_attlen = PQfnumber(res, "attlen");
+	i_attalign = PQfnumber(res, "attalign");
+	i_attislocal = PQfnumber(res, "attislocal");
+	i_attnotnull = PQfnumber(res, "attnotnull");
+	i_attoptions = PQfnumber(res, "attoptions");
+	i_attcollation = PQfnumber(res, "attcollation");
+	i_attcompression = PQfnumber(res, "attcompression");
+	i_attfdwoptions = PQfnumber(res, "attfdwoptions");
+	i_attmissingval = PQfnumber(res, "attmissingval");
+	i_atthasdef = PQfnumber(res, "atthasdef");
 
-		res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
+	/* Within the next loop, we'll accumulate OIDs of tables with defaults */
+	resetPQExpBuffer(tbloids);
+	appendPQExpBufferChar(tbloids, '{');
 
-		ntups = PQntuples(res);
+	/*
+	 * Outer loop iterates once per table, not once per row.  Incrementing of
+	 * r is handled by the inner loop.
+	 */
+	curtblindx = -1;
+	for (int r = 0; r < ntups;)
+	{
+		Oid			attrelid = atooid(PQgetvalue(res, r, i_attrelid));
+		TableInfo  *tbinfo = NULL;
+		int			numatts;
+		bool		hasdefaults;
+
+		/* Count rows for this table */
+		for (numatts = 1; numatts < ntups - r; numatts++)
+			if (atooid(PQgetvalue(res, r + numatts, i_attrelid)) != attrelid)
+				break;
 
-		tbinfo->numatts = ntups;
-		tbinfo->attnames = (char **) pg_malloc(ntups * sizeof(char *));
-		tbinfo->atttypnames = (char **) pg_malloc(ntups * sizeof(char *));
-		tbinfo->atttypmod = (int *) pg_malloc(ntups * sizeof(int));
-		tbinfo->attstattarget = (int *) pg_malloc(ntups * sizeof(int));
-		tbinfo->attstorage = (char *) pg_malloc(ntups * sizeof(char));
-		tbinfo->typstorage = (char *) pg_malloc(ntups * sizeof(char));
-		tbinfo->attidentity = (char *) pg_malloc(ntups * sizeof(char));
-		tbinfo->attgenerated = (char *) pg_malloc(ntups * sizeof(char));
-		tbinfo->attisdropped = (bool *) pg_malloc(ntups * sizeof(bool));
-		tbinfo->attlen = (int *) pg_malloc(ntups * sizeof(int));
-		tbinfo->attalign = (char *) pg_malloc(ntups * sizeof(char));
-		tbinfo->attislocal = (bool *) pg_malloc(ntups * sizeof(bool));
-		tbinfo->attoptions = (char **) pg_malloc(ntups * sizeof(char *));
-		tbinfo->attcollation = (Oid *) pg_malloc(ntups * sizeof(Oid));
-		tbinfo->attcompression = (char *) pg_malloc(ntups * sizeof(char));
-		tbinfo->attfdwoptions = (char **) pg_malloc(ntups * sizeof(char *));
-		tbinfo->attmissingval = (char **) pg_malloc(ntups * sizeof(char *));
-		tbinfo->notnull = (bool *) pg_malloc(ntups * sizeof(bool));
-		tbinfo->inhNotNull = (bool *) pg_malloc(ntups * sizeof(bool));
-		tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(ntups * sizeof(AttrDefInfo *));
+		/*
+		 * Locate the associated TableInfo; we rely on tblinfo[] being in OID
+		 * order.
+		 */
+		while (++curtblindx < numTables)
+		{
+			tbinfo = &tblinfo[curtblindx];
+			if (tbinfo->dobj.catId.oid == attrelid)
+				break;
+		}
+		if (curtblindx >= numTables)
+			fatal("unrecognized table OID %u", attrelid);
+		/* cross-check that we only got requested tables */
+		if (tbinfo->relkind == RELKIND_SEQUENCE ||
+			!tbinfo->interesting)
+			fatal("unexpected column data for table \"%s\"",
+				  tbinfo->dobj.name);
+
+		/* Save data for this table */
+		tbinfo->numatts = numatts;
+		tbinfo->attnames = (char **) pg_malloc(numatts * sizeof(char *));
+		tbinfo->atttypnames = (char **) pg_malloc(numatts * sizeof(char *));
+		tbinfo->atttypmod = (int *) pg_malloc(numatts * sizeof(int));
+		tbinfo->attstattarget = (int *) pg_malloc(numatts * sizeof(int));
+		tbinfo->attstorage = (char *) pg_malloc(numatts * sizeof(char));
+		tbinfo->typstorage = (char *) pg_malloc(numatts * sizeof(char));
+		tbinfo->attidentity = (char *) pg_malloc(numatts * sizeof(char));
+		tbinfo->attgenerated = (char *) pg_malloc(numatts * sizeof(char));
+		tbinfo->attisdropped = (bool *) pg_malloc(numatts * sizeof(bool));
+		tbinfo->attlen = (int *) pg_malloc(numatts * sizeof(int));
+		tbinfo->attalign = (char *) pg_malloc(numatts * sizeof(char));
+		tbinfo->attislocal = (bool *) pg_malloc(numatts * sizeof(bool));
+		tbinfo->attoptions = (char **) pg_malloc(numatts * sizeof(char *));
+		tbinfo->attcollation = (Oid *) pg_malloc(numatts * sizeof(Oid));
+		tbinfo->attcompression = (char *) pg_malloc(numatts * sizeof(char));
+		tbinfo->attfdwoptions = (char **) pg_malloc(numatts * sizeof(char *));
+		tbinfo->attmissingval = (char **) pg_malloc(numatts * sizeof(char *));
+		tbinfo->notnull = (bool *) pg_malloc(numatts * sizeof(bool));
+		tbinfo->inhNotNull = (bool *) pg_malloc(numatts * sizeof(bool));
+		tbinfo->attrdefs = (AttrDefInfo **) pg_malloc(numatts * sizeof(AttrDefInfo *));
 		hasdefaults = false;
 
-		i_attnum = PQfnumber(res, "attnum");
-		i_attname = PQfnumber(res, "attname");
-		i_atttypname = PQfnumber(res, "atttypname");
-		i_atttypmod = PQfnumber(res, "atttypmod");
-		i_attstattarget = PQfnumber(res, "attstattarget");
-		i_attstorage = PQfnumber(res, "attstorage");
-		i_typstorage = PQfnumber(res, "typstorage");
-		i_attidentity = PQfnumber(res, "attidentity");
-		i_attgenerated = PQfnumber(res, "attgenerated");
-		i_attisdropped = PQfnumber(res, "attisdropped");
-		i_attlen = PQfnumber(res, "attlen");
-		i_attalign = PQfnumber(res, "attalign");
-		i_attislocal = PQfnumber(res, "attislocal");
-		i_attnotnull = PQfnumber(res, "attnotnull");
-		i_attoptions = PQfnumber(res, "attoptions");
-		i_attcollation = PQfnumber(res, "attcollation");
-		i_attcompression = PQfnumber(res, "attcompression");
-		i_attfdwoptions = PQfnumber(res, "attfdwoptions");
-		i_attmissingval = PQfnumber(res, "attmissingval");
-		i_atthasdef = PQfnumber(res, "atthasdef");
-
-		for (int j = 0; j < ntups; j++)
+		for (int j = 0; j < numatts; j++, r++)
 		{
-			if (j + 1 != atoi(PQgetvalue(res, j, i_attnum)))
+			if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
 				fatal("invalid column numbering in table \"%s\"",
 					  tbinfo->dobj.name);
-			tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, j, i_attname));
-			tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, j, i_atttypname));
-			tbinfo->atttypmod[j] = atoi(PQgetvalue(res, j, i_atttypmod));
-			tbinfo->attstattarget[j] = atoi(PQgetvalue(res, j, i_attstattarget));
-			tbinfo->attstorage[j] = *(PQgetvalue(res, j, i_attstorage));
-			tbinfo->typstorage[j] = *(PQgetvalue(res, j, i_typstorage));
-			tbinfo->attidentity[j] = *(PQgetvalue(res, j, i_attidentity));
-			tbinfo->attgenerated[j] = *(PQgetvalue(res, j, i_attgenerated));
+			tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
+			tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
+			tbinfo->atttypmod[j] = atoi(PQgetvalue(res, r, i_atttypmod));
+			tbinfo->attstattarget[j] = atoi(PQgetvalue(res, r, i_attstattarget));
+			tbinfo->attstorage[j] = *(PQgetvalue(res, r, i_attstorage));
+			tbinfo->typstorage[j] = *(PQgetvalue(res, r, i_typstorage));
+			tbinfo->attidentity[j] = *(PQgetvalue(res, r, i_attidentity));
+			tbinfo->attgenerated[j] = *(PQgetvalue(res, r, i_attgenerated));
 			tbinfo->needs_override = tbinfo->needs_override || (tbinfo->attidentity[j] == ATTRIBUTE_IDENTITY_ALWAYS);
-			tbinfo->attisdropped[j] = (PQgetvalue(res, j, i_attisdropped)[0] == 't');
-			tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen));
-			tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign));
-			tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
-			tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
-			tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, j, i_attoptions));
-			tbinfo->attcollation[j] = atooid(PQgetvalue(res, j, i_attcollation));
-			tbinfo->attcompression[j] = *(PQgetvalue(res, j, i_attcompression));
-			tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, j, i_attfdwoptions));
-			tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, j, i_attmissingval));
+			tbinfo->attisdropped[j] = (PQgetvalue(res, r, i_attisdropped)[0] == 't');
+			tbinfo->attlen[j] = atoi(PQgetvalue(res, r, i_attlen));
+			tbinfo->attalign[j] = *(PQgetvalue(res, r, i_attalign));
+			tbinfo->attislocal[j] = (PQgetvalue(res, r, i_attislocal)[0] == 't');
+			tbinfo->notnull[j] = (PQgetvalue(res, r, i_attnotnull)[0] == 't');
+			tbinfo->attoptions[j] = pg_strdup(PQgetvalue(res, r, i_attoptions));
+			tbinfo->attcollation[j] = atooid(PQgetvalue(res, r, i_attcollation));
+			tbinfo->attcompression[j] = *(PQgetvalue(res, r, i_attcompression));
+			tbinfo->attfdwoptions[j] = pg_strdup(PQgetvalue(res, r, i_attfdwoptions));
+			tbinfo->attmissingval[j] = pg_strdup(PQgetvalue(res, r, i_attmissingval));
 			tbinfo->attrdefs[j] = NULL; /* fix below */
-			if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
+			if (PQgetvalue(res, r, i_atthasdef)[0] == 't')
 				hasdefaults = true;
 			/* these flags will be set in flagInhAttrs() */
 			tbinfo->inhNotNull[j] = false;
 		}
 
-		PQclear(res);
-
-		/*
-		 * Get info about column defaults.  This is skipped for a data-only
-		 * dump, as it is only needed for table schemas.
-		 */
-		if (!dopt->dataOnly && hasdefaults)
+		if (hasdefaults)
 		{
-			AttrDefInfo *attrdefs;
-			int			numDefaults;
-
-			pg_log_info("finding default expressions of table \"%s.%s\"",
-						tbinfo->dobj.namespace->dobj.name,
-						tbinfo->dobj.name);
-
-			printfPQExpBuffer(q, "SELECT tableoid, oid, adnum, "
-							  "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc "
-							  "FROM pg_catalog.pg_attrdef "
-							  "WHERE adrelid = '%u'::pg_catalog.oid",
-							  tbinfo->dobj.catId.oid);
-
-			res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
+			/* Collect OIDs of interesting tables that have defaults */
+			if (tbloids->len > 1)
+				appendPQExpBufferChar(tbloids, ',');
+			appendPQExpBuffer(tbloids, "%u", tbinfo->dobj.catId.oid);
+		}
+	}
 
-			numDefaults = PQntuples(res);
-			attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
+	PQclear(res);
 
-			for (int j = 0; j < numDefaults; j++)
-			{
-				int			adnum;
+	/*
+	 * Now get info about column defaults.  This is skipped for a data-only
+	 * dump, as it is only needed for table schemas.
+	 */
+	if (!dopt->dataOnly && tbloids->len > 1)
+	{
+		AttrDefInfo *attrdefs;
+		int			numDefaults;
+		TableInfo  *tbinfo = NULL;
 
-				adnum = atoi(PQgetvalue(res, j, 2));
+		pg_log_info("finding table default expressions");
 
-				if (adnum <= 0 || adnum > ntups)
-					fatal("invalid adnum value %d for table \"%s\"",
-						  adnum, tbinfo->dobj.name);
+		appendPQExpBufferChar(tbloids, '}');
 
-				/*
-				 * dropped columns shouldn't have defaults, but just in case,
-				 * ignore 'em
-				 */
-				if (tbinfo->attisdropped[adnum - 1])
-					continue;
+		printfPQExpBuffer(q, "SELECT a.tableoid, a.oid, adrelid, adnum, "
+						  "pg_catalog.pg_get_expr(adbin, adrelid) AS adsrc\n"
+						  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+						  "JOIN pg_catalog.pg_attrdef a ON (src.tbloid = a.adrelid)\n"
+						  "ORDER BY a.adrelid, a.adnum",
+						  tbloids->data);
 
-				attrdefs[j].dobj.objType = DO_ATTRDEF;
-				attrdefs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
-				attrdefs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
-				AssignDumpId(&attrdefs[j].dobj);
-				attrdefs[j].adtable = tbinfo;
-				attrdefs[j].adnum = adnum;
-				attrdefs[j].adef_expr = pg_strdup(PQgetvalue(res, j, 3));
+		res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
 
-				attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
-				attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
+		numDefaults = PQntuples(res);
+		attrdefs = (AttrDefInfo *) pg_malloc(numDefaults * sizeof(AttrDefInfo));
 
-				attrdefs[j].dobj.dump = tbinfo->dobj.dump;
+		curtblindx = -1;
+		for (int j = 0; j < numDefaults; j++)
+		{
+			Oid			adtableoid = atooid(PQgetvalue(res, j, 0));
+			Oid			adoid = atooid(PQgetvalue(res, j, 1));
+			Oid			adrelid = atooid(PQgetvalue(res, j, 2));
+			int			adnum = atoi(PQgetvalue(res, j, 3));
+			char	   *adsrc = PQgetvalue(res, j, 4);
 
-				/*
-				 * Figure out whether the default/generation expression should
-				 * be dumped as part of the main CREATE TABLE (or similar)
-				 * command or as a separate ALTER TABLE (or similar) command.
-				 * The preference is to put it into the CREATE command, but in
-				 * some cases that's not possible.
-				 */
-				if (tbinfo->attgenerated[adnum - 1])
-				{
-					/*
-					 * Column generation expressions cannot be dumped
-					 * separately, because there is no syntax for it.  The
-					 * !shouldPrintColumn case below will be tempted to set
-					 * them to separate if they are attached to an inherited
-					 * column without a local definition, but that would be
-					 * wrong and unnecessary, because generation expressions
-					 * are always inherited, so there is no need to set them
-					 * again in child tables, and there is no syntax for it
-					 * either.  By setting separate to false here we prevent
-					 * the "default" from being processed as its own dumpable
-					 * object, and flagInhAttrs() will remove it from the
-					 * table when it detects that it belongs to an inherited
-					 * column.
-					 */
-					attrdefs[j].separate = false;
-				}
-				else if (tbinfo->relkind == RELKIND_VIEW)
-				{
-					/*
-					 * Defaults on a VIEW must always be dumped as separate
-					 * ALTER TABLE commands.
-					 */
-					attrdefs[j].separate = true;
-				}
-				else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
-				{
-					/* column will be suppressed, print default separately */
-					attrdefs[j].separate = true;
-				}
-				else
+			/*
+			 * Locate the associated TableInfo; we rely on tblinfo[] being in
+			 * OID order.
+			 */
+			if (tbinfo == NULL || tbinfo->dobj.catId.oid != adrelid)
+			{
+				while (++curtblindx < numTables)
 				{
-					attrdefs[j].separate = false;
+					tbinfo = &tblinfo[curtblindx];
+					if (tbinfo->dobj.catId.oid == adrelid)
+						break;
 				}
+				if (curtblindx >= numTables)
+					fatal("unrecognized table OID %u", adrelid);
+			}
 
-				if (!attrdefs[j].separate)
-				{
-					/*
-					 * Mark the default as needing to appear before the table,
-					 * so that any dependencies it has must be emitted before
-					 * the CREATE TABLE.  If this is not possible, we'll
-					 * change to "separate" mode while sorting dependencies.
-					 */
-					addObjectDependency(&tbinfo->dobj,
-										attrdefs[j].dobj.dumpId);
-				}
+			if (adnum <= 0 || adnum > tbinfo->numatts)
+				fatal("invalid adnum value %d for table \"%s\"",
+					  adnum, tbinfo->dobj.name);
 
-				tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
-			}
-			PQclear(res);
-		}
+			/*
+			 * dropped columns shouldn't have defaults, but just in case,
+			 * ignore 'em
+			 */
+			if (tbinfo->attisdropped[adnum - 1])
+				continue;
 
-		/*
-		 * Get info about table CHECK constraints.  This is skipped for a
-		 * data-only dump, as it is only needed for table schemas.
-		 */
-		if (tbinfo->ncheck > 0 && !dopt->dataOnly)
-		{
-			ConstraintInfo *constrs;
-			int			numConstrs;
+			attrdefs[j].dobj.objType = DO_ATTRDEF;
+			attrdefs[j].dobj.catId.tableoid = adtableoid;
+			attrdefs[j].dobj.catId.oid = adoid;
+			AssignDumpId(&attrdefs[j].dobj);
+			attrdefs[j].adtable = tbinfo;
+			attrdefs[j].adnum = adnum;
+			attrdefs[j].adef_expr = pg_strdup(adsrc);
+
+			attrdefs[j].dobj.name = pg_strdup(tbinfo->dobj.name);
+			attrdefs[j].dobj.namespace = tbinfo->dobj.namespace;
 
-			pg_log_info("finding check constraints for table \"%s.%s\"",
-						tbinfo->dobj.namespace->dobj.name,
-						tbinfo->dobj.name);
+			attrdefs[j].dobj.dump = tbinfo->dobj.dump;
 
-			resetPQExpBuffer(q);
-			if (fout->remoteVersion >= 90200)
+			/*
+			 * Figure out whether the default/generation expression should be
+			 * dumped as part of the main CREATE TABLE (or similar) command or
+			 * as a separate ALTER TABLE (or similar) command. The preference
+			 * is to put it into the CREATE command, but in some cases that's
+			 * not possible.
+			 */
+			if (tbinfo->attgenerated[adnum - 1])
+			{
+				/*
+				 * Column generation expressions cannot be dumped separately,
+				 * because there is no syntax for it.  The !shouldPrintColumn
+				 * case below will be tempted to set them to separate if they
+				 * are attached to an inherited column without a local
+				 * definition, but that would be wrong and unnecessary,
+				 * because generation expressions are always inherited, so
+				 * there is no need to set them again in child tables, and
+				 * there is no syntax for it either.  By setting separate to
+				 * false here we prevent the "default" from being processed as
+				 * its own dumpable object, and flagInhAttrs() will remove it
+				 * from the table when it detects that it belongs to an
+				 * inherited column.
+				 */
+				attrdefs[j].separate = false;
+			}
+			else if (tbinfo->relkind == RELKIND_VIEW)
 			{
 				/*
-				 * convalidated is new in 9.2 (actually, it is there in 9.1,
-				 * but it wasn't ever false for check constraints until 9.2).
+				 * Defaults on a VIEW must always be dumped as separate ALTER
+				 * TABLE commands.
 				 */
-				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
-								  "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
-								  "conislocal, convalidated "
-								  "FROM pg_catalog.pg_constraint "
-								  "WHERE conrelid = '%u'::pg_catalog.oid "
-								  "   AND contype = 'c' "
-								  "ORDER BY conname",
-								  tbinfo->dobj.catId.oid);
+				attrdefs[j].separate = true;
 			}
-			else if (fout->remoteVersion >= 80400)
+			else if (!shouldPrintColumn(dopt, tbinfo, adnum - 1))
 			{
-				/* conislocal is new in 8.4 */
-				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
-								  "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
-								  "conislocal, true AS convalidated "
-								  "FROM pg_catalog.pg_constraint "
-								  "WHERE conrelid = '%u'::pg_catalog.oid "
-								  "   AND contype = 'c' "
-								  "ORDER BY conname",
-								  tbinfo->dobj.catId.oid);
+				/* column will be suppressed, print default separately */
+				attrdefs[j].separate = true;
 			}
 			else
 			{
-				appendPQExpBuffer(q, "SELECT tableoid, oid, conname, "
-								  "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
-								  "true AS conislocal, true AS convalidated "
-								  "FROM pg_catalog.pg_constraint "
-								  "WHERE conrelid = '%u'::pg_catalog.oid "
-								  "   AND contype = 'c' "
-								  "ORDER BY conname",
-								  tbinfo->dobj.catId.oid);
+				attrdefs[j].separate = false;
 			}
 
-			res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
+			if (!attrdefs[j].separate)
+			{
+				/*
+				 * Mark the default as needing to appear before the table, so
+				 * that any dependencies it has must be emitted before the
+				 * CREATE TABLE.  If this is not possible, we'll change to
+				 * "separate" mode while sorting dependencies.
+				 */
+				addObjectDependency(&tbinfo->dobj,
+									attrdefs[j].dobj.dumpId);
+			}
+
+			tbinfo->attrdefs[adnum - 1] = &attrdefs[j];
+		}
+
+		PQclear(res);
+	}
 
-			numConstrs = PQntuples(res);
-			if (numConstrs != tbinfo->ncheck)
+	/*
+	 * Get info about table CHECK constraints.  This is skipped for a
+	 * data-only dump, as it is only needed for table schemas.
+	 */
+	if (!dopt->dataOnly && checkoids->len > 2)
+	{
+		ConstraintInfo *constrs;
+		int			numConstrs;
+		int			i_tableoid;
+		int			i_oid;
+		int			i_conrelid;
+		int			i_conname;
+		int			i_consrc;
+		int			i_conislocal;
+		int			i_convalidated;
+
+		pg_log_info("finding table check constraints");
+
+		resetPQExpBuffer(q);
+		appendPQExpBufferStr(q,
+							 "SELECT c.tableoid, c.oid, conrelid, conname, "
+							 "pg_catalog.pg_get_constraintdef(c.oid) AS consrc, ");
+		if (fout->remoteVersion >= 90200)
+		{
+			/*
+			 * convalidated is new in 9.2 (actually, it is there in 9.1, but
+			 * it wasn't ever false for check constraints until 9.2).
+			 */
+			appendPQExpBufferStr(q,
+								 "conislocal, convalidated ");
+		}
+		else if (fout->remoteVersion >= 80400)
+		{
+			/* conislocal is new in 8.4 */
+			appendPQExpBufferStr(q,
+								 "conislocal, true AS convalidated ");
+		}
+		else
+		{
+			appendPQExpBufferStr(q,
+								 "true AS conislocal, true AS convalidated ");
+		}
+		appendPQExpBuffer(q,
+						  "FROM unnest('%s'::pg_catalog.oid[]) AS src(tbloid)\n"
+						  "JOIN pg_catalog.pg_constraint c ON (src.tbloid = c.conrelid)\n"
+						  "WHERE contype = 'c' "
+						  "ORDER BY c.conrelid, c.conname",
+						  checkoids->data);
+
+		res = ExecuteSqlQuery(fout, q->data, PGRES_TUPLES_OK);
+
+		numConstrs = PQntuples(res);
+		constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
+
+		i_tableoid = PQfnumber(res, "tableoid");
+		i_oid = PQfnumber(res, "oid");
+		i_conrelid = PQfnumber(res, "conrelid");
+		i_conname = PQfnumber(res, "conname");
+		i_consrc = PQfnumber(res, "consrc");
+		i_conislocal = PQfnumber(res, "conislocal");
+		i_convalidated = PQfnumber(res, "convalidated");
+
+		/* As above, this loop iterates once per table, not once per row */
+		curtblindx = -1;
+		for (int j = 0; j < numConstrs;)
+		{
+			Oid			conrelid = atooid(PQgetvalue(res, j, i_conrelid));
+			TableInfo  *tbinfo = NULL;
+			int			numcons;
+
+			/* Count rows for this table */
+			for (numcons = 1; numcons < numConstrs - j; numcons++)
+				if (atooid(PQgetvalue(res, j + numcons, i_conrelid)) != conrelid)
+					break;
+
+			/*
+			 * Locate the associated TableInfo; we rely on tblinfo[] being in
+			 * OID order.
+			 */
+			while (++curtblindx < numTables)
+			{
+				tbinfo = &tblinfo[curtblindx];
+				if (tbinfo->dobj.catId.oid == conrelid)
+					break;
+			}
+			if (curtblindx >= numTables)
+				fatal("unrecognized table OID %u", conrelid);
+
+			if (numcons != tbinfo->ncheck)
 			{
 				pg_log_error(ngettext("expected %d check constraint on table \"%s\" but found %d",
 									  "expected %d check constraints on table \"%s\" but found %d",
 									  tbinfo->ncheck),
-							 tbinfo->ncheck, tbinfo->dobj.name, numConstrs);
+							 tbinfo->ncheck, tbinfo->dobj.name, numcons);
 				pg_log_error("(The system catalogs might be corrupted.)");
 				exit_nicely(1);
 			}
 
-			constrs = (ConstraintInfo *) pg_malloc(numConstrs * sizeof(ConstraintInfo));
-			tbinfo->checkexprs = constrs;
+			tbinfo->checkexprs = constrs + j;
 
-			for (int j = 0; j < numConstrs; j++)
+			for (int c = 0; c < numcons; c++, j++)
 			{
-				bool		validated = PQgetvalue(res, j, 5)[0] == 't';
+				bool		validated = PQgetvalue(res, j, i_convalidated)[0] == 't';
 
 				constrs[j].dobj.objType = DO_CONSTRAINT;
-				constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, 0));
-				constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, 1));
+				constrs[j].dobj.catId.tableoid = atooid(PQgetvalue(res, j, i_tableoid));
+				constrs[j].dobj.catId.oid = atooid(PQgetvalue(res, j, i_oid));
 				AssignDumpId(&constrs[j].dobj);
-				constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, 2));
+				constrs[j].dobj.name = pg_strdup(PQgetvalue(res, j, i_conname));
 				constrs[j].dobj.namespace = tbinfo->dobj.namespace;
 				constrs[j].contable = tbinfo;
 				constrs[j].condomain = NULL;
 				constrs[j].contype = 'c';
-				constrs[j].condef = pg_strdup(PQgetvalue(res, j, 3));
+				constrs[j].condef = pg_strdup(PQgetvalue(res, j, i_consrc));
 				constrs[j].confrelid = InvalidOid;
 				constrs[j].conindex = 0;
 				constrs[j].condeferrable = false;
 				constrs[j].condeferred = false;
-				constrs[j].conislocal = (PQgetvalue(res, j, 4)[0] == 't');
+				constrs[j].conislocal = (PQgetvalue(res, j, i_conislocal)[0] == 't');
 
 				/*
 				 * An unvalidated constraint needs to be dumped separately, so
@@ -8586,11 +8831,14 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 				 * constraint must be split out from the table definition.
 				 */
 			}
-			PQclear(res);
 		}
+
+		PQclear(res);
 	}
 
 	destroyPQExpBuffer(q);
+	destroyPQExpBuffer(tbloids);
+	destroyPQExpBuffer(checkoids);
 }
 
 /*

  [text/x-diff] 0005-fix-toast-sched-2.patch (4.6K, ../177444.1635287465@sss.pgh.pa.us/6-0005-fix-toast-sched-2.patch)
  download | inline diff:
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 45bae2ffe1..e54c3c04cb 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -2425,13 +2425,26 @@ dumpTableData(Archive *fout, const TableDataInfo *tdinfo)
 		/*
 		 * Set the TocEntry's dataLength in case we are doing a parallel dump
 		 * and want to order dump jobs by table size.  We choose to measure
-		 * dataLength in table pages during dump, so no scaling is needed.
+		 * dataLength in table pages (including TOAST pages) during dump, so
+		 * no scaling is needed.
+		 *
 		 * However, relpages is declared as "integer" in pg_class, and hence
 		 * also in TableInfo, but it's really BlockNumber a/k/a unsigned int.
 		 * Cast so that we get the right interpretation of table sizes
 		 * exceeding INT_MAX pages.
 		 */
 		te->dataLength = (BlockNumber) tbinfo->relpages;
+		te->dataLength += (BlockNumber) tbinfo->toastpages;
+
+		/*
+		 * If pgoff_t is only 32 bits wide, the above refinement is useless,
+		 * and instead we'd better worry about integer overflow.  Clamp to
+		 * INT_MAX if the correct result exceeds that.
+		 */
+		if (sizeof(te->dataLength) == 4 &&
+			(tbinfo->relpages < 0 || tbinfo->toastpages < 0 ||
+			 te->dataLength < 0))
+			te->dataLength = INT_MAX;
 	}
 
 	destroyPQExpBuffer(copyBuf);
@@ -6086,6 +6099,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relhasindex;
 	int			i_relhasrules;
 	int			i_relpages;
+	int			i_toastpages;
 	int			i_owning_tab;
 	int			i_owning_col;
 	int			i_reltablespace;
@@ -6135,6 +6149,7 @@ getTables(Archive *fout, int *numTables)
 					  "(%s c.relowner) AS rolname, "
 					  "c.relchecks, "
 					  "c.relhasindex, c.relhasrules, c.relpages, "
+					  "tc.relpages AS toastpages, "
 					  "d.refobjid AS owning_tab, "
 					  "d.refobjsubid AS owning_col, "
 					  "tsp.spcname AS reltablespace, ",
@@ -6291,17 +6306,14 @@ getTables(Archive *fout, int *numTables)
 							 "LEFT JOIN pg_am am ON (c.relam = am.oid)\n");
 
 	/*
-	 * We don't need any data from the TOAST table before 8.2.
-	 *
 	 * We purposefully ignore toast OIDs for partitioned tables; the reason is
 	 * that versions 10 and 11 have them, but later versions do not, so
 	 * emitting them causes the upgrade to fail.
 	 */
-	if (fout->remoteVersion >= 80200)
-		appendPQExpBufferStr(query,
-							 "LEFT JOIN pg_class tc ON (c.reltoastrelid = tc.oid"
-							 " AND tc.relkind = " CppAsString2(RELKIND_TOASTVALUE)
-							 " AND c.relkind <> " CppAsString2(RELKIND_PARTITIONED_TABLE) ")\n");
+	appendPQExpBufferStr(query,
+						 "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
@@ -6352,6 +6364,7 @@ getTables(Archive *fout, int *numTables)
 	i_relhasindex = PQfnumber(res, "relhasindex");
 	i_relhasrules = PQfnumber(res, "relhasrules");
 	i_relpages = PQfnumber(res, "relpages");
+	i_toastpages = PQfnumber(res, "toastpages");
 	i_owning_tab = PQfnumber(res, "owning_tab");
 	i_owning_col = PQfnumber(res, "owning_col");
 	i_reltablespace = PQfnumber(res, "reltablespace");
@@ -6413,6 +6426,10 @@ getTables(Archive *fout, int *numTables)
 		tblinfo[i].hasindex = (strcmp(PQgetvalue(res, i, i_relhasindex), "t") == 0);
 		tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
 		tblinfo[i].relpages = atoi(PQgetvalue(res, i, i_relpages));
+		if (PQgetisnull(res, i, i_toastpages))
+			tblinfo[i].toastpages = 0;
+		else
+			tblinfo[i].toastpages = atoi(PQgetvalue(res, i, i_toastpages));
 		if (PQgetisnull(res, i, i_owning_tab))
 		{
 			tblinfo[i].owning_tab = InvalidOid;
@@ -10229,7 +10246,7 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
 				 * about is allowing blob dumping to be parallelized, not just
 				 * getting a smarter estimate for the single TOC entry.)
 				 */
-				te->dataLength = MaxBlockNumber;
+				te->dataLength = INT_MAX;
 			}
 			break;
 		case DO_POLICY:
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 9061812c08..0936ec49bf 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -314,6 +314,7 @@ typedef struct _tableInfo
 	int			owning_col;		/* attr # of column owning sequence */
 	bool		is_identity_sequence;
 	int			relpages;		/* table's size in pages (from pg_class) */
+	int			toastpages;		/* toast table's size in pages, if any */
 
 	bool		interesting;	/* true if need to collect more data */
 	bool		dummy_view;		/* view's real definition must be postponed */

  [text/x-diff] 0006-prepare-repeated-queries-3.patch (34.9K, ../177444.1635287465@sss.pgh.pa.us/7-0006-prepare-repeated-queries-3.patch)
  download | inline diff:
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 6af10a85a2..d3aac0dbdf 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -58,6 +58,23 @@ typedef enum _teSection
 	SECTION_POST_DATA			/* stuff to be processed after data */
 } teSection;
 
+/* We need one enum entry per prepared query in pg_dump */
+enum _dumpPreparedQueries
+{
+	PREPQUERY_DUMPAGG,
+	PREPQUERY_DUMPBASETYPE,
+	PREPQUERY_DUMPCOMPOSITETYPE,
+	PREPQUERY_DUMPDOMAIN,
+	PREPQUERY_DUMPENUMTYPE,
+	PREPQUERY_DUMPFUNC,
+	PREPQUERY_DUMPOPR,
+	PREPQUERY_DUMPRANGETYPE,
+	PREPQUERY_DUMPTABLEATTACH,
+	PREPQUERY_GETCOLUMNACLS,
+	PREPQUERY_GETDOMAINCONSTRAINTS,
+	NUM_PREP_QUERIES			/* must be last */
+};
+
 /* Parameters needed by ConnectDatabase; same for dump and restore */
 typedef struct _connParams
 {
@@ -214,6 +231,9 @@ typedef struct Archive
 	bool		exit_on_error;	/* whether to exit on SQL errors... */
 	int			n_errors;		/* number of errors (if no die) */
 
+	/* prepared-query status */
+	bool	   *is_prepared;	/* indexed by enum _dumpPreparedQueries */
+
 	/* The rest is private */
 } Archive;
 
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e54c3c04cb..212203a32e 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -1208,6 +1208,12 @@ setup_connection(Archive *AH, const char *dumpencoding,
 			ExecuteSqlStatement(AH, "SET row_security = off");
 	}
 
+	/*
+	 * Initialize prepared-query state to "nothing prepared".  We do this here
+	 * so that a parallel dump worker will have its own state.
+	 */
+	AH->is_prepared = (bool *) pg_malloc0(NUM_PREP_QUERIES * sizeof(bool));
+
 	/*
 	 * Start transaction-snapshot mode transaction to dump consistent data.
 	 */
@@ -7332,7 +7338,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
 {
 	int			i;
 	ConstraintInfo *constrinfo;
-	PQExpBuffer query;
+	PQExpBuffer query = createPQExpBuffer();
 	PGresult   *res;
 	int			i_tableoid,
 				i_oid,
@@ -7340,25 +7346,35 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
 				i_consrc;
 	int			ntups;
 
-	query = createPQExpBuffer();
+	if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
+	{
+		/* Set up query for constraint-specific details */
+		appendPQExpBufferStr(query,
+							 "PREPARE getDomainConstraints(pg_catalog.oid) AS\n");
+
+		if (fout->remoteVersion >= 90100)
+			appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
+								 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
+								 "convalidated "
+								 "FROM pg_catalog.pg_constraint "
+								 "WHERE contypid = $1 "
+								 "ORDER BY conname");
+		else
+			appendPQExpBufferStr(query, "SELECT tableoid, oid, conname, "
+								 "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
+								 "true as convalidated "
+								 "FROM pg_catalog.pg_constraint "
+								 "WHERE contypid = $1 "
+								 "ORDER BY conname");
 
-	if (fout->remoteVersion >= 90100)
-		appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
-						  "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
-						  "convalidated "
-						  "FROM pg_catalog.pg_constraint "
-						  "WHERE contypid = '%u'::pg_catalog.oid "
-						  "ORDER BY conname",
-						  tyinfo->dobj.catId.oid);
+		ExecuteSqlStatement(fout, query->data);
 
-	else
-		appendPQExpBuffer(query, "SELECT tableoid, oid, conname, "
-						  "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
-						  "true as convalidated "
-						  "FROM pg_catalog.pg_constraint "
-						  "WHERE contypid = '%u'::pg_catalog.oid "
-						  "ORDER BY conname",
-						  tyinfo->dobj.catId.oid);
+		fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS] = true;
+	}
+
+	printfPQExpBuffer(query,
+					  "EXECUTE getDomainConstraints('%u')",
+					  tyinfo->dobj.catId.oid);
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -10527,18 +10543,31 @@ dumpEnumType(Archive *fout, const TypeInfo *tyinfo)
 	int			i_enumlabel;
 	int			i_oid;
 
-	if (fout->remoteVersion >= 90100)
-		appendPQExpBuffer(query, "SELECT oid, enumlabel "
-						  "FROM pg_catalog.pg_enum "
-						  "WHERE enumtypid = '%u'"
-						  "ORDER BY enumsortorder",
-						  tyinfo->dobj.catId.oid);
-	else
-		appendPQExpBuffer(query, "SELECT oid, enumlabel "
-						  "FROM pg_catalog.pg_enum "
-						  "WHERE enumtypid = '%u'"
-						  "ORDER BY oid",
-						  tyinfo->dobj.catId.oid);
+	if (!fout->is_prepared[PREPQUERY_DUMPENUMTYPE])
+	{
+		/* Set up query for enum-specific details */
+		appendPQExpBufferStr(query,
+							 "PREPARE dumpEnumType(pg_catalog.oid) AS\n");
+
+		if (fout->remoteVersion >= 90100)
+			appendPQExpBufferStr(query, "SELECT oid, enumlabel "
+								 "FROM pg_catalog.pg_enum "
+								 "WHERE enumtypid = $1 "
+								 "ORDER BY enumsortorder");
+		else
+			appendPQExpBufferStr(query, "SELECT oid, enumlabel "
+								 "FROM pg_catalog.pg_enum "
+								 "WHERE enumtypid = $1 "
+								 "ORDER BY oid");
+
+		ExecuteSqlStatement(fout, query->data);
+
+		fout->is_prepared[PREPQUERY_DUMPENUMTYPE] = true;
+	}
+
+	printfPQExpBuffer(query,
+					  "EXECUTE dumpEnumType('%u')",
+					  tyinfo->dobj.catId.oid);
 
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
@@ -10657,29 +10686,43 @@ dumpRangeType(Archive *fout, const TypeInfo *tyinfo)
 	char	   *qualtypname;
 	char	   *procname;
 
-	appendPQExpBuffer(query,
-					  "SELECT ");
+	if (!fout->is_prepared[PREPQUERY_DUMPRANGETYPE])
+	{
+		/* Set up query for range-specific details */
+		appendPQExpBufferStr(query,
+							 "PREPARE dumpRangeType(pg_catalog.oid) AS\n");
 
-	if (fout->remoteVersion >= 140000)
-		appendPQExpBuffer(query,
-						  "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
-	else
-		appendPQExpBuffer(query,
-						  "NULL AS rngmultitype, ");
+		appendPQExpBufferStr(query,
+							 "SELECT ");
 
-	appendPQExpBuffer(query,
-					  "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
-					  "opc.opcname AS opcname, "
-					  "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
-					  "  WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
-					  "opc.opcdefault, "
-					  "CASE WHEN rngcollation = st.typcollation THEN 0 "
-					  "     ELSE rngcollation END AS collation, "
-					  "rngcanonical, rngsubdiff "
-					  "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
-					  "     pg_catalog.pg_opclass opc "
-					  "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
-					  "rngtypid = '%u'",
+		if (fout->remoteVersion >= 140000)
+			appendPQExpBufferStr(query,
+								 "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
+		else
+			appendPQExpBufferStr(query,
+								 "NULL AS rngmultitype, ");
+
+		appendPQExpBufferStr(query,
+							 "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
+							 "opc.opcname AS opcname, "
+							 "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
+							 "  WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
+							 "opc.opcdefault, "
+							 "CASE WHEN rngcollation = st.typcollation THEN 0 "
+							 "     ELSE rngcollation END AS collation, "
+							 "rngcanonical, rngsubdiff "
+							 "FROM pg_catalog.pg_range r, pg_catalog.pg_type st, "
+							 "     pg_catalog.pg_opclass opc "
+							 "WHERE st.oid = rngsubtype AND opc.oid = rngsubopc AND "
+							 "rngtypid = $1");
+
+		ExecuteSqlStatement(fout, query->data);
+
+		fout->is_prepared[PREPQUERY_DUMPRANGETYPE] = true;
+	}
+
+	printfPQExpBuffer(query,
+					  "EXECUTE dumpRangeType('%u')",
 					  tyinfo->dobj.catId.oid);
 
 	res = ExecuteSqlQueryForSingleRow(fout, query->data);
@@ -10887,55 +10930,68 @@ dumpBaseType(Archive *fout, const TypeInfo *tyinfo)
 	char	   *typdefault;
 	bool		typdefault_is_literal = false;
 
-	/* Fetch type-specific details */
-	appendPQExpBufferStr(query, "SELECT typlen, "
-						 "typinput, typoutput, typreceive, typsend, "
-						 "typreceive::pg_catalog.oid AS typreceiveoid, "
-						 "typsend::pg_catalog.oid AS typsendoid, "
-						 "typanalyze, "
-						 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
-						 "typdelim, typbyval, typalign, typstorage, ");
-
-	if (fout->remoteVersion >= 80300)
-		appendPQExpBufferStr(query,
-							 "typmodin, typmodout, "
-							 "typmodin::pg_catalog.oid AS typmodinoid, "
-							 "typmodout::pg_catalog.oid AS typmodoutoid, ");
-	else
+	if (!fout->is_prepared[PREPQUERY_DUMPBASETYPE])
+	{
+		/* Set up query for type-specific details */
 		appendPQExpBufferStr(query,
-							 "'-' AS typmodin, '-' AS typmodout, "
-							 "0 AS typmodinoid, 0 AS typmodoutoid, ");
+							 "PREPARE dumpBaseType(pg_catalog.oid) AS\n");
 
-	if (fout->remoteVersion >= 80400)
-		appendPQExpBufferStr(query,
-							 "typcategory, typispreferred, ");
-	else
-		appendPQExpBufferStr(query,
-							 "'U' AS typcategory, false AS typispreferred, ");
+		appendPQExpBufferStr(query, "SELECT typlen, "
+							 "typinput, typoutput, typreceive, typsend, "
+							 "typreceive::pg_catalog.oid AS typreceiveoid, "
+							 "typsend::pg_catalog.oid AS typsendoid, "
+							 "typanalyze, "
+							 "typanalyze::pg_catalog.oid AS typanalyzeoid, "
+							 "typdelim, typbyval, typalign, typstorage, ");
 
-	if (fout->remoteVersion >= 90100)
-		appendPQExpBufferStr(query, "(typcollation <> 0) AS typcollatable, ");
-	else
-		appendPQExpBufferStr(query, "false AS typcollatable, ");
+		if (fout->remoteVersion >= 80300)
+			appendPQExpBufferStr(query,
+								 "typmodin, typmodout, "
+								 "typmodin::pg_catalog.oid AS typmodinoid, "
+								 "typmodout::pg_catalog.oid AS typmodoutoid, ");
+		else
+			appendPQExpBufferStr(query,
+								 "'-' AS typmodin, '-' AS typmodout, "
+								 "0 AS typmodinoid, 0 AS typmodoutoid, ");
 
-	if (fout->remoteVersion >= 140000)
-		appendPQExpBufferStr(query,
-							 "typsubscript, "
-							 "typsubscript::pg_catalog.oid AS typsubscriptoid, ");
-	else
-		appendPQExpBufferStr(query,
-							 "'-' AS typsubscript, 0 AS typsubscriptoid, ");
+		if (fout->remoteVersion >= 80400)
+			appendPQExpBufferStr(query,
+								 "typcategory, typispreferred, ");
+		else
+			appendPQExpBufferStr(query,
+								 "'U' AS typcategory, false AS typispreferred, ");
 
-	/* Before 8.4, pg_get_expr does not allow 0 for its second arg */
-	if (fout->remoteVersion >= 80400)
-		appendPQExpBufferStr(query,
-							 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault ");
-	else
-		appendPQExpBufferStr(query,
-							 "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, typdefault ");
+		if (fout->remoteVersion >= 90100)
+			appendPQExpBufferStr(query, "(typcollation <> 0) AS typcollatable, ");
+		else
+			appendPQExpBufferStr(query, "false AS typcollatable, ");
 
-	appendPQExpBuffer(query, "FROM pg_catalog.pg_type "
-					  "WHERE oid = '%u'::pg_catalog.oid",
+		if (fout->remoteVersion >= 140000)
+			appendPQExpBufferStr(query,
+								 "typsubscript, "
+								 "typsubscript::pg_catalog.oid AS typsubscriptoid, ");
+		else
+			appendPQExpBufferStr(query,
+								 "'-' AS typsubscript, 0 AS typsubscriptoid, ");
+
+		/* Before 8.4, pg_get_expr does not allow 0 for its second arg */
+		if (fout->remoteVersion >= 80400)
+			appendPQExpBufferStr(query,
+								 "pg_catalog.pg_get_expr(typdefaultbin, 0) AS typdefaultbin, typdefault ");
+		else
+			appendPQExpBufferStr(query,
+								 "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, typdefault ");
+
+		appendPQExpBufferStr(query, "FROM pg_catalog.pg_type "
+							 "WHERE oid = $1");
+
+		ExecuteSqlStatement(fout, query->data);
+
+		fout->is_prepared[PREPQUERY_DUMPBASETYPE] = true;
+	}
+
+	printfPQExpBuffer(query,
+					  "EXECUTE dumpBaseType('%u')",
 					  tyinfo->dobj.catId.oid);
 
 	res = ExecuteSqlQueryForSingleRow(fout, query->data);
@@ -11130,32 +11186,44 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
 	Oid			typcollation;
 	bool		typdefault_is_literal = false;
 
-	/* Fetch domain specific details */
-	if (fout->remoteVersion >= 90100)
+	if (!fout->is_prepared[PREPQUERY_DUMPDOMAIN])
 	{
-		/* typcollation is new in 9.1 */
-		appendPQExpBuffer(query, "SELECT t.typnotnull, "
-						  "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
-						  "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
-						  "t.typdefault, "
-						  "CASE WHEN t.typcollation <> u.typcollation "
-						  "THEN t.typcollation ELSE 0 END AS typcollation "
-						  "FROM pg_catalog.pg_type t "
-						  "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
-						  "WHERE t.oid = '%u'::pg_catalog.oid",
-						  tyinfo->dobj.catId.oid);
-	}
-	else
-	{
-		appendPQExpBuffer(query, "SELECT typnotnull, "
-						  "pg_catalog.format_type(typbasetype, typtypmod) AS typdefn, "
-						  "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
-						  "typdefault, 0 AS typcollation "
-						  "FROM pg_catalog.pg_type "
-						  "WHERE oid = '%u'::pg_catalog.oid",
-						  tyinfo->dobj.catId.oid);
+		/* Set up query for domain-specific details */
+		appendPQExpBufferStr(query,
+							 "PREPARE dumpDomain(pg_catalog.oid) AS\n");
+
+		if (fout->remoteVersion >= 90100)
+		{
+			/* typcollation is new in 9.1 */
+			appendPQExpBufferStr(query, "SELECT t.typnotnull, "
+								 "pg_catalog.format_type(t.typbasetype, t.typtypmod) AS typdefn, "
+								 "pg_catalog.pg_get_expr(t.typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
+								 "t.typdefault, "
+								 "CASE WHEN t.typcollation <> u.typcollation "
+								 "THEN t.typcollation ELSE 0 END AS typcollation "
+								 "FROM pg_catalog.pg_type t "
+								 "LEFT JOIN pg_catalog.pg_type u ON (t.typbasetype = u.oid) "
+								 "WHERE t.oid = $1");
+		}
+		else
+		{
+			appendPQExpBufferStr(query, "SELECT typnotnull, "
+								 "pg_catalog.format_type(typbasetype, typtypmod) AS typdefn, "
+								 "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, "
+								 "typdefault, 0 AS typcollation "
+								 "FROM pg_catalog.pg_type "
+								 "WHERE oid = $1");
+		}
+
+		ExecuteSqlStatement(fout, query->data);
+
+		fout->is_prepared[PREPQUERY_DUMPDOMAIN] = true;
 	}
 
+	printfPQExpBuffer(query,
+					  "EXECUTE dumpDomain('%u')",
+					  tyinfo->dobj.catId.oid);
+
 	res = ExecuteSqlQueryForSingleRow(fout, query->data);
 
 	typnotnull = PQgetvalue(res, 0, PQfnumber(res, "typnotnull"));
@@ -11308,45 +11376,57 @@ dumpCompositeType(Archive *fout, const TypeInfo *tyinfo)
 	int			i;
 	int			actual_atts;
 
-	/* Fetch type specific details */
-	if (fout->remoteVersion >= 90100)
-	{
-		/*
-		 * attcollation is new in 9.1.  Since we only want to dump COLLATE
-		 * clauses for attributes whose collation is different from their
-		 * type's default, we use a CASE here to suppress uninteresting
-		 * attcollations cheaply.  atttypid will be 0 for dropped columns;
-		 * collation does not matter for those.
-		 */
-		appendPQExpBuffer(query, "SELECT a.attname, "
-						  "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
-						  "a.attlen, a.attalign, a.attisdropped, "
-						  "CASE WHEN a.attcollation <> at.typcollation "
-						  "THEN a.attcollation ELSE 0 END AS attcollation "
-						  "FROM pg_catalog.pg_type ct "
-						  "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
-						  "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
-						  "WHERE ct.oid = '%u'::pg_catalog.oid "
-						  "ORDER BY a.attnum ",
-						  tyinfo->dobj.catId.oid);
-	}
-	else
+	if (!fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE])
 	{
-		/*
-		 * Since ALTER TYPE could not drop columns until 9.1, attisdropped
-		 * should always be false.
-		 */
-		appendPQExpBuffer(query, "SELECT a.attname, "
-						  "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
-						  "a.attlen, a.attalign, a.attisdropped, "
-						  "0 AS attcollation "
-						  "FROM pg_catalog.pg_type ct, pg_catalog.pg_attribute a "
-						  "WHERE ct.oid = '%u'::pg_catalog.oid "
-						  "AND a.attrelid = ct.typrelid "
-						  "ORDER BY a.attnum ",
-						  tyinfo->dobj.catId.oid);
+		/* Set up query for type-specific details */
+		appendPQExpBufferStr(query,
+							 "PREPARE dumpCompositeType(pg_catalog.oid) AS\n");
+
+		if (fout->remoteVersion >= 90100)
+		{
+			/*
+			 * attcollation is new in 9.1.  Since we only want to dump COLLATE
+			 * clauses for attributes whose collation is different from their
+			 * type's default, we use a CASE here to suppress uninteresting
+			 * attcollations cheaply.  atttypid will be 0 for dropped columns;
+			 * collation does not matter for those.
+			 */
+			appendPQExpBufferStr(query, "SELECT a.attname, "
+								 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
+								 "a.attlen, a.attalign, a.attisdropped, "
+								 "CASE WHEN a.attcollation <> at.typcollation "
+								 "THEN a.attcollation ELSE 0 END AS attcollation "
+								 "FROM pg_catalog.pg_type ct "
+								 "JOIN pg_catalog.pg_attribute a ON a.attrelid = ct.typrelid "
+								 "LEFT JOIN pg_catalog.pg_type at ON at.oid = a.atttypid "
+								 "WHERE ct.oid = $1 "
+								 "ORDER BY a.attnum");
+		}
+		else
+		{
+			/*
+			 * Since ALTER TYPE could not drop columns until 9.1, attisdropped
+			 * should always be false.
+			 */
+			appendPQExpBufferStr(query, "SELECT a.attname, "
+								 "pg_catalog.format_type(a.atttypid, a.atttypmod) AS atttypdefn, "
+								 "a.attlen, a.attalign, a.attisdropped, "
+								 "0 AS attcollation "
+								 "FROM pg_catalog.pg_type ct, pg_catalog.pg_attribute a "
+								 "WHERE ct.oid = $1 "
+								 "AND a.attrelid = ct.typrelid "
+								 "ORDER BY a.attnum");
+		}
+
+		ExecuteSqlStatement(fout, query->data);
+
+		fout->is_prepared[PREPQUERY_DUMPCOMPOSITETYPE] = true;
 	}
 
+	printfPQExpBuffer(query,
+					  "EXECUTE dumpCompositeType('%u')",
+					  tyinfo->dobj.catId.oid);
+
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
 	ntups = PQntuples(res);
@@ -11965,96 +12045,109 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
 	delqry = createPQExpBuffer();
 	asPart = createPQExpBuffer();
 
-	/* Fetch function-specific details */
-	appendPQExpBufferStr(query,
-						 "SELECT\n"
-						 "proretset,\n"
-						 "prosrc,\n"
-						 "probin,\n"
-						 "provolatile,\n"
-						 "proisstrict,\n"
-						 "prosecdef,\n"
-						 "lanname,\n");
-
-	if (fout->remoteVersion >= 80300)
-		appendPQExpBufferStr(query,
-							 "proconfig,\n"
-							 "procost,\n"
-							 "prorows,\n");
-	else
-		appendPQExpBufferStr(query,
-							 "null AS proconfig,\n"
-							 "0 AS procost,\n"
-							 "0 AS prorows,\n");
-
-	if (fout->remoteVersion >= 80400)
+	if (!fout->is_prepared[PREPQUERY_DUMPFUNC])
 	{
-		/*
-		 * In 8.4 and up we rely on pg_get_function_arguments and
-		 * pg_get_function_result instead of examining proallargtypes etc.
-		 */
+		/* Set up query for function-specific details */
 		appendPQExpBufferStr(query,
-							 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
-							 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
-							 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n");
-	}
-	else if (fout->remoteVersion >= 80100)
-		appendPQExpBufferStr(query,
-							 "proallargtypes,\n"
-							 "proargmodes,\n"
-							 "proargnames,\n");
-	else
-		appendPQExpBufferStr(query,
-							 "null AS proallargtypes,\n"
-							 "null AS proargmodes,\n"
-							 "proargnames,\n");
+							 "PREPARE dumpFunc(pg_catalog.oid) AS\n");
 
-	if (fout->remoteVersion >= 90200)
-		appendPQExpBufferStr(query,
-							 "proleakproof,\n");
-	else
 		appendPQExpBufferStr(query,
-							 "false AS proleakproof,\n");
+							 "SELECT\n"
+							 "proretset,\n"
+							 "prosrc,\n"
+							 "probin,\n"
+							 "provolatile,\n"
+							 "proisstrict,\n"
+							 "prosecdef,\n"
+							 "lanname,\n");
+
+		if (fout->remoteVersion >= 80300)
+			appendPQExpBufferStr(query,
+								 "proconfig,\n"
+								 "procost,\n"
+								 "prorows,\n");
+		else
+			appendPQExpBufferStr(query,
+								 "null AS proconfig,\n"
+								 "0 AS procost,\n"
+								 "0 AS prorows,\n");
 
-	if (fout->remoteVersion >= 90500)
-		appendPQExpBufferStr(query,
-							 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
+		if (fout->remoteVersion >= 80400)
+		{
+			/*
+			 * In 8.4 and up we rely on pg_get_function_arguments and
+			 * pg_get_function_result instead of examining proallargtypes etc.
+			 */
+			appendPQExpBufferStr(query,
+								 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
+								 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n"
+								 "pg_catalog.pg_get_function_result(p.oid) AS funcresult,\n");
+		}
+		else if (fout->remoteVersion >= 80100)
+			appendPQExpBufferStr(query,
+								 "proallargtypes,\n"
+								 "proargmodes,\n"
+								 "proargnames,\n");
+		else
+			appendPQExpBufferStr(query,
+								 "null AS proallargtypes,\n"
+								 "null AS proargmodes,\n"
+								 "proargnames,\n");
 
-	if (fout->remoteVersion >= 90600)
-		appendPQExpBufferStr(query,
-							 "proparallel,\n");
-	else
-		appendPQExpBufferStr(query,
-							 "'u' AS proparallel,\n");
+		if (fout->remoteVersion >= 90200)
+			appendPQExpBufferStr(query,
+								 "proleakproof,\n");
+		else
+			appendPQExpBufferStr(query,
+								 "false AS proleakproof,\n");
 
-	if (fout->remoteVersion >= 110000)
-		appendPQExpBufferStr(query,
-							 "prokind,\n");
-	else if (fout->remoteVersion >= 80400)
-		appendPQExpBufferStr(query,
-							 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
-	else
-		appendPQExpBufferStr(query,
-							 "'f' AS prokind,\n");
+		if (fout->remoteVersion >= 90500)
+			appendPQExpBufferStr(query,
+								 "array_to_string(protrftypes, ' ') AS protrftypes,\n");
 
-	if (fout->remoteVersion >= 120000)
-		appendPQExpBufferStr(query,
-							 "prosupport,\n");
-	else
-		appendPQExpBufferStr(query,
-							 "'-' AS prosupport,\n");
+		if (fout->remoteVersion >= 90600)
+			appendPQExpBufferStr(query,
+								 "proparallel,\n");
+		else
+			appendPQExpBufferStr(query,
+								 "'u' AS proparallel,\n");
+
+		if (fout->remoteVersion >= 110000)
+			appendPQExpBufferStr(query,
+								 "prokind,\n");
+		else if (fout->remoteVersion >= 80400)
+			appendPQExpBufferStr(query,
+								 "CASE WHEN proiswindow THEN 'w' ELSE 'f' END AS prokind,\n");
+		else
+			appendPQExpBufferStr(query,
+								 "'f' AS prokind,\n");
+
+		if (fout->remoteVersion >= 120000)
+			appendPQExpBufferStr(query,
+								 "prosupport,\n");
+		else
+			appendPQExpBufferStr(query,
+								 "'-' AS prosupport,\n");
+
+		if (fout->remoteVersion >= 140000)
+			appendPQExpBufferStr(query,
+								 "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
+		else
+			appendPQExpBufferStr(query,
+								 "NULL AS prosqlbody\n");
 
-	if (fout->remoteVersion >= 140000)
-		appendPQExpBufferStr(query,
-							 "pg_get_function_sqlbody(p.oid) AS prosqlbody\n");
-	else
 		appendPQExpBufferStr(query,
-							 "NULL AS prosqlbody\n");
+							 "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
+							 "WHERE p.oid = $1 "
+							 "AND l.oid = p.prolang");
 
-	appendPQExpBuffer(query,
-					  "FROM pg_catalog.pg_proc p, pg_catalog.pg_language l\n"
-					  "WHERE p.oid = '%u'::pg_catalog.oid "
-					  "AND l.oid = p.prolang",
+		ExecuteSqlStatement(fout, query->data);
+
+		fout->is_prepared[PREPQUERY_DUMPFUNC] = true;
+	}
+
+	printfPQExpBuffer(query,
+					  "EXECUTE dumpFunc('%u')",
 					  finfo->dobj.catId.oid);
 
 	res = ExecuteSqlQueryForSingleRow(fout, query->data);
@@ -12721,38 +12814,51 @@ dumpOpr(Archive *fout, const OprInfo *oprinfo)
 	oprid = createPQExpBuffer();
 	details = createPQExpBuffer();
 
-	if (fout->remoteVersion >= 80300)
-	{
-		appendPQExpBuffer(query, "SELECT oprkind, "
-						  "oprcode::pg_catalog.regprocedure, "
-						  "oprleft::pg_catalog.regtype, "
-						  "oprright::pg_catalog.regtype, "
-						  "oprcom, "
-						  "oprnegate, "
-						  "oprrest::pg_catalog.regprocedure, "
-						  "oprjoin::pg_catalog.regprocedure, "
-						  "oprcanmerge, oprcanhash "
-						  "FROM pg_catalog.pg_operator "
-						  "WHERE oid = '%u'::pg_catalog.oid",
-						  oprinfo->dobj.catId.oid);
-	}
-	else
+	if (!fout->is_prepared[PREPQUERY_DUMPOPR])
 	{
-		appendPQExpBuffer(query, "SELECT oprkind, "
-						  "oprcode::pg_catalog.regprocedure, "
-						  "oprleft::pg_catalog.regtype, "
-						  "oprright::pg_catalog.regtype, "
-						  "oprcom, "
-						  "oprnegate, "
-						  "oprrest::pg_catalog.regprocedure, "
-						  "oprjoin::pg_catalog.regprocedure, "
-						  "(oprlsortop != 0) AS oprcanmerge, "
-						  "oprcanhash "
-						  "FROM pg_catalog.pg_operator "
-						  "WHERE oid = '%u'::pg_catalog.oid",
-						  oprinfo->dobj.catId.oid);
+		/* Set up query for operator-specific details */
+		appendPQExpBufferStr(query,
+							 "PREPARE dumpOpr(pg_catalog.oid) AS\n");
+
+		if (fout->remoteVersion >= 80300)
+		{
+			appendPQExpBufferStr(query, "SELECT oprkind, "
+								 "oprcode::pg_catalog.regprocedure, "
+								 "oprleft::pg_catalog.regtype, "
+								 "oprright::pg_catalog.regtype, "
+								 "oprcom, "
+								 "oprnegate, "
+								 "oprrest::pg_catalog.regprocedure, "
+								 "oprjoin::pg_catalog.regprocedure, "
+								 "oprcanmerge, oprcanhash "
+								 "FROM pg_catalog.pg_operator "
+								 "WHERE oid = $1");
+		}
+		else
+		{
+			appendPQExpBufferStr(query, "SELECT oprkind, "
+								 "oprcode::pg_catalog.regprocedure, "
+								 "oprleft::pg_catalog.regtype, "
+								 "oprright::pg_catalog.regtype, "
+								 "oprcom, "
+								 "oprnegate, "
+								 "oprrest::pg_catalog.regprocedure, "
+								 "oprjoin::pg_catalog.regprocedure, "
+								 "(oprlsortop != 0) AS oprcanmerge, "
+								 "oprcanhash "
+								 "FROM pg_catalog.pg_operator "
+								 "WHERE oid = $1");
+		}
+
+		ExecuteSqlStatement(fout, query->data);
+
+		fout->is_prepared[PREPQUERY_DUMPOPR] = true;
 	}
 
+	printfPQExpBuffer(query,
+					  "EXECUTE dumpOpr('%u')",
+					  oprinfo->dobj.catId.oid);
+
 	res = ExecuteSqlQueryForSingleRow(fout, query->data);
 
 	i_oprkind = PQfnumber(res, "oprkind");
@@ -14018,77 +14124,90 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 	delq = createPQExpBuffer();
 	details = createPQExpBuffer();
 
-	/* Get aggregate-specific details */
-	appendPQExpBufferStr(query,
-						 "SELECT\n"
-						 "aggtransfn,\n"
-						 "aggfinalfn,\n"
-						 "aggtranstype::pg_catalog.regtype,\n"
-						 "agginitval,\n");
-
-	if (fout->remoteVersion >= 80100)
-		appendPQExpBufferStr(query,
-							 "aggsortop,\n");
-	else
+	if (!fout->is_prepared[PREPQUERY_DUMPAGG])
+	{
+		/* Set up query for aggregate-specific details */
 		appendPQExpBufferStr(query,
-							 "0 AS aggsortop,\n");
+							 "PREPARE dumpAgg(pg_catalog.oid) AS\n");
 
-	if (fout->remoteVersion >= 80400)
 		appendPQExpBufferStr(query,
-							 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
-							 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
+							 "SELECT "
+							 "aggtransfn,\n"
+							 "aggfinalfn,\n"
+							 "aggtranstype::pg_catalog.regtype,\n"
+							 "agginitval,\n");
 
-	if (fout->remoteVersion >= 90400)
-		appendPQExpBufferStr(query,
-							 "aggkind,\n"
-							 "aggmtransfn,\n"
-							 "aggminvtransfn,\n"
-							 "aggmfinalfn,\n"
-							 "aggmtranstype::pg_catalog.regtype,\n"
-							 "aggfinalextra,\n"
-							 "aggmfinalextra,\n"
-							 "aggtransspace,\n"
-							 "aggmtransspace,\n"
-							 "aggminitval,\n");
-	else
-		appendPQExpBufferStr(query,
-							 "'n' AS aggkind,\n"
-							 "'-' AS aggmtransfn,\n"
-							 "'-' AS aggminvtransfn,\n"
-							 "'-' AS aggmfinalfn,\n"
-							 "0 AS aggmtranstype,\n"
-							 "false AS aggfinalextra,\n"
-							 "false AS aggmfinalextra,\n"
-							 "0 AS aggtransspace,\n"
-							 "0 AS aggmtransspace,\n"
-							 "NULL AS aggminitval,\n");
+		if (fout->remoteVersion >= 80100)
+			appendPQExpBufferStr(query,
+								 "aggsortop,\n");
+		else
+			appendPQExpBufferStr(query,
+								 "0 AS aggsortop,\n");
 
-	if (fout->remoteVersion >= 90600)
-		appendPQExpBufferStr(query,
-							 "aggcombinefn,\n"
-							 "aggserialfn,\n"
-							 "aggdeserialfn,\n"
-							 "proparallel,\n");
-	else
-		appendPQExpBufferStr(query,
-							 "'-' AS aggcombinefn,\n"
-							 "'-' AS aggserialfn,\n"
-							 "'-' AS aggdeserialfn,\n"
-							 "'u' AS proparallel,\n");
+		if (fout->remoteVersion >= 80400)
+			appendPQExpBufferStr(query,
+								 "pg_catalog.pg_get_function_arguments(p.oid) AS funcargs,\n"
+								 "pg_catalog.pg_get_function_identity_arguments(p.oid) AS funciargs,\n");
+
+		if (fout->remoteVersion >= 90400)
+			appendPQExpBufferStr(query,
+								 "aggkind,\n"
+								 "aggmtransfn,\n"
+								 "aggminvtransfn,\n"
+								 "aggmfinalfn,\n"
+								 "aggmtranstype::pg_catalog.regtype,\n"
+								 "aggfinalextra,\n"
+								 "aggmfinalextra,\n"
+								 "aggtransspace,\n"
+								 "aggmtransspace,\n"
+								 "aggminitval,\n");
+		else
+			appendPQExpBufferStr(query,
+								 "'n' AS aggkind,\n"
+								 "'-' AS aggmtransfn,\n"
+								 "'-' AS aggminvtransfn,\n"
+								 "'-' AS aggmfinalfn,\n"
+								 "0 AS aggmtranstype,\n"
+								 "false AS aggfinalextra,\n"
+								 "false AS aggmfinalextra,\n"
+								 "0 AS aggtransspace,\n"
+								 "0 AS aggmtransspace,\n"
+								 "NULL AS aggminitval,\n");
+
+		if (fout->remoteVersion >= 90600)
+			appendPQExpBufferStr(query,
+								 "aggcombinefn,\n"
+								 "aggserialfn,\n"
+								 "aggdeserialfn,\n"
+								 "proparallel,\n");
+		else
+			appendPQExpBufferStr(query,
+								 "'-' AS aggcombinefn,\n"
+								 "'-' AS aggserialfn,\n"
+								 "'-' AS aggdeserialfn,\n"
+								 "'u' AS proparallel,\n");
+
+		if (fout->remoteVersion >= 110000)
+			appendPQExpBufferStr(query,
+								 "aggfinalmodify,\n"
+								 "aggmfinalmodify\n");
+		else
+			appendPQExpBufferStr(query,
+								 "'0' AS aggfinalmodify,\n"
+								 "'0' AS aggmfinalmodify\n");
 
-	if (fout->remoteVersion >= 110000)
-		appendPQExpBufferStr(query,
-							 "aggfinalmodify,\n"
-							 "aggmfinalmodify\n");
-	else
 		appendPQExpBufferStr(query,
-							 "'0' AS aggfinalmodify,\n"
-							 "'0' AS aggmfinalmodify\n");
+							 "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
+							 "WHERE a.aggfnoid = p.oid "
+							 "AND p.oid = $1");
 
-	appendPQExpBuffer(query,
-					  "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
-					  "WHERE a.aggfnoid = p.oid "
-					  "AND p.oid = '%u'::pg_catalog.oid",
+		ExecuteSqlStatement(fout, query->data);
+
+		fout->is_prepared[PREPQUERY_DUMPAGG] = true;
+	}
+
+	printfPQExpBuffer(query,
+					  "EXECUTE dumpAgg('%u')",
 					  agginfo->aggfn.dobj.catId.oid);
 
 	res = ExecuteSqlQueryForSingleRow(fout, query->data);
@@ -15502,46 +15621,59 @@ dumpTable(Archive *fout, const TableInfo *tbinfo)
 		PGresult   *res;
 		int			i;
 
-		if (fout->remoteVersion >= 90600)
+		if (!fout->is_prepared[PREPQUERY_GETCOLUMNACLS])
 		{
-			/*
-			 * 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, "
-							  "at.attacl, "
-							  "'{}' AS acldefault, "
-							  "pip.privtype, pip.initprivs "
-							  "FROM pg_catalog.pg_attribute at "
-							  "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 (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
-							  "ORDER BY at.attnum",
-							  tbinfo->dobj.catId.oid);
-		}
-		else
-		{
-			appendPQExpBuffer(query,
-							  "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 "
-							  "ORDER BY attnum",
-							  tbinfo->dobj.catId.oid);
+			/* Set up query for column ACLs */
+			appendPQExpBufferStr(query,
+								 "PREPARE getColumnACLs(pg_catalog.oid) AS\n");
+
+			if (fout->remoteVersion >= 90600)
+			{
+				/*
+				 * 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.
+				 */
+				appendPQExpBufferStr(query,
+									 "SELECT at.attname, "
+									 "at.attacl, "
+									 "'{}' AS acldefault, "
+									 "pip.privtype, pip.initprivs "
+									 "FROM pg_catalog.pg_attribute at "
+									 "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 = $1 AND "
+									 "NOT at.attisdropped "
+									 "AND (at.attacl IS NOT NULL OR pip.initprivs IS NOT NULL) "
+									 "ORDER BY at.attnum");
+			}
+			else
+			{
+				appendPQExpBufferStr(query,
+									 "SELECT attname, attacl, '{}' AS acldefault, "
+									 "NULL AS privtype, NULL AS initprivs "
+									 "FROM pg_catalog.pg_attribute "
+									 "WHERE attrelid = $1 AND NOT attisdropped "
+									 "AND attacl IS NOT NULL "
+									 "ORDER BY attnum");
+			}
+
+			ExecuteSqlStatement(fout, query->data);
+
+			fout->is_prepared[PREPQUERY_GETCOLUMNACLS] = true;
 		}
 
+		printfPQExpBuffer(query,
+						  "EXECUTE getColumnACLs('%u')",
+						  tbinfo->dobj.catId.oid);
+
 		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
 		for (i = 0; i < PQntuples(res); i++)
@@ -16481,12 +16613,26 @@ dumpTableAttach(Archive *fout, const TableAttachInfo *attachinfo)
 
 	q = createPQExpBuffer();
 
-	/* Fetch the partition's partbound */
-	appendPQExpBuffer(q,
-					  "SELECT pg_get_expr(c.relpartbound, c.oid) "
-					  "FROM pg_class c "
-					  "WHERE c.oid = '%u'",
+	if (!fout->is_prepared[PREPQUERY_DUMPTABLEATTACH])
+	{
+		/* Set up query for partbound details */
+		appendPQExpBufferStr(q,
+							 "PREPARE dumpTableAttach(pg_catalog.oid) AS\n");
+
+		appendPQExpBufferStr(q,
+							 "SELECT pg_get_expr(c.relpartbound, c.oid) "
+							 "FROM pg_class c "
+							 "WHERE c.oid = $1");
+
+		ExecuteSqlStatement(fout, q->data);
+
+		fout->is_prepared[PREPQUERY_DUMPTABLEATTACH] = true;
+	}
+
+	printfPQExpBuffer(q,
+					  "EXECUTE dumpTableAttach('%u')",
 					  attachinfo->partitionTbl->dobj.catId.oid);
+
 	res = ExecuteSqlQueryForSingleRow(fout, q->data);
 	partbound = PQgetvalue(res, 0, 0);
 =

view thread (13+ messages)  latest in thread

Message-ID: <177444.1635287465@sss.pgh.pa.us>
Permalink:  ../177444.1635287465@sss.pgh.pa.us/
Also on:    postgresql.org/message-id/177444.1635287465@sss.pgh.pa.us

 ·  · 

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-hackers@postgresql.org
  Cc: tgl@sss.pgh.pa.us, andres@anarazel.de, buschmann@nidsa.net
  Subject: Re: Assorted improvements in pg_dump
  In-Reply-To: <177444.1635287465@sss.pgh.pa.us>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox