@@ -15764,6 +15764,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
1576415764DumpOptions *dopt = fout->dopt;
1576515765PQExpBuffer q = createPQExpBuffer();
1576615766PQExpBuffer delq = createPQExpBuffer();
15767+ PQExpBuffer extra = createPQExpBuffer();
1576715768char *qrelname;
1576815769char *qualrelname;
1576915770intnumParents;
@@ -15830,7 +15831,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
1583015831char *partkeydef = NULL;
1583115832char *ftoptions = NULL;
1583215833char *srvname = NULL;
15833- char *foreign = "";
15834+ const char *foreign = "";
1583415835
1583515836/*
1583615837 * Set reltypename, and collect any relkind-specific data that we
@@ -16188,70 +16189,130 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
1618816189 tbinfo->relkind == RELKIND_FOREIGN_TABLE ||
1618916190 tbinfo->relkind == RELKIND_PARTITIONED_TABLE))
1619016191{
16192+ boolfirstitem;
16193+
16194+ /*
16195+ * Drop any dropped columns. Merge the pg_attribute manipulations
16196+ * into a single SQL command, so that we don't cause repeated
16197+ * relcache flushes on the target table. Otherwise we risk O(N^2)
16198+ * relcache bloat while dropping N columns.
16199+ */
16200+ resetPQExpBuffer(extra);
16201+ firstitem = true;
1619116202for (j = 0; j < tbinfo->numatts; j++)
1619216203{
1619316204if (tbinfo->attisdropped[j])
1619416205{
16195- appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped column.\n");
16196- appendPQExpBuffer(q, "UPDATE pg_catalog.pg_attribute\n"
16197- "SET attlen = %d, "
16198- "attalign = '%c', attbyval = false\n"
16199- "WHERE attname = ",
16206+ if (firstitem)
16207+ {
16208+ appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n"
16209+ "UPDATE pg_catalog.pg_attribute\n"
16210+ "SET attlen = v.dlen, "
16211+ "attalign = v.dalign, "
16212+ "attbyval = false\n"
16213+ "FROM (VALUES ");
16214+ firstitem = false;
16215+ }
16216+ else
16217+ appendPQExpBufferStr(q, ",\n ");
16218+ appendPQExpBufferChar(q, '(');
16219+ appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16220+ appendPQExpBuffer(q, ", %d, '%c')",
1620016221 tbinfo->attlen[j],
1620116222 tbinfo->attalign[j]);
16202- appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16203- appendPQExpBufferStr(q, "\n AND attrelid = ");
16204- appendStringLiteralAH(q, qualrelname, fout);
16205- appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16206-
16207- if (tbinfo->relkind == RELKIND_RELATION ||
16208- tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
16209- appendPQExpBuffer(q, "ALTER TABLE ONLY %s ",
16210- qualrelname);
16211- else
16212- appendPQExpBuffer(q, "ALTER FOREIGN TABLE ONLY %s ",
16213- qualrelname);
16214- appendPQExpBuffer(q, "DROP COLUMN %s;\n",
16223+ /* The ALTER ... DROP COLUMN commands must come after */
16224+ appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ",
16225+ foreign, qualrelname);
16226+ appendPQExpBuffer(extra, "DROP COLUMN %s;\n",
1621516227 fmtId(tbinfo->attnames[j]));
1621616228}
16217- else if (!tbinfo->attislocal[j])
16229+ }
16230+ if (!firstitem)
16231+ {
16232+ appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n"
16233+ "WHERE attrelid = ");
16234+ appendStringLiteralAH(q, qualrelname, fout);
16235+ appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
16236+ " AND attname = v.dname;\n");
16237+ /* Now we can issue the actual DROP COLUMN commands */
16238+ appendBinaryPQExpBuffer(q, extra->data, extra->len);
16239+ }
16240+
16241+ /*
16242+ * Fix up inherited columns. As above, do the pg_attribute
16243+ * manipulations in a single SQL command.
16244+ */
16245+ firstitem = true;
16246+ for (j = 0; j < tbinfo->numatts; j++)
16247+ {
16248+ if (!tbinfo->attisdropped[j] &&
16249+ !tbinfo->attislocal[j])
1621816250{
16219- appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited column.\n");
16220- appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
16221- "SET attislocal = false\n"
16222- "WHERE attname = ");
16251+ if (firstitem)
16252+ {
16253+ appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n");
16254+ appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n"
16255+ "SET attislocal = false\n"
16256+ "WHERE attrelid = ");
16257+ appendStringLiteralAH(q, qualrelname, fout);
16258+ appendPQExpBufferStr(q, "::pg_catalog.regclass\n"
16259+ " AND attname IN (");
16260+ firstitem = false;
16261+ }
16262+ else
16263+ appendPQExpBufferStr(q, ", ");
1622316264appendStringLiteralAH(q, tbinfo->attnames[j], fout);
16224- appendPQExpBufferStr(q, "\n AND attrelid = ");
16225- appendStringLiteralAH(q, qualrelname, fout);
16226- appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
1622716265}
1622816266}
16267+ if (!firstitem)
16268+ appendPQExpBufferStr(q, ");\n");
1622916269
1623016270/*
1623116271 * Add inherited CHECK constraints, if any.
1623216272 *
1623316273 * For partitions, they were already dumped, and conislocal
1623416274 * doesn't need fixing.
16275+ *
16276+ * As above, issue only one direct manipulation of pg_constraint.
16277+ * Although it is tempting to merge the ALTER ADD CONSTRAINT
16278+ * commands into one as well, refrain for now due to concern about
16279+ * possible backend memory bloat if there are many such
16280+ * constraints.
1623516281 */
16282+ resetPQExpBuffer(extra);
16283+ firstitem = true;
1623616284for (k = 0; k < tbinfo->ncheck; k++)
1623716285{
1623816286ConstraintInfo *constr = &(tbinfo->checkexprs[k]);
1623916287
1624016288if (constr->separate || constr->conislocal || tbinfo->ispartition)
1624116289continue;
1624216290
16243- appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraint.\n");
16291+ if (firstitem)
16292+ appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n");
1624416293appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n",
1624516294 foreign, qualrelname,
1624616295 fmtId(constr->dobj.name),
1624716296 constr->condef);
16248- appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n"
16249- "SET conislocal = false\n"
16250- "WHERE contype = 'c' AND conname = ");
16251- appendStringLiteralAH(q, constr->dobj.name, fout);
16252- appendPQExpBufferStr(q, "\n AND conrelid = ");
16253- appendStringLiteralAH(q, qualrelname, fout);
16254- appendPQExpBufferStr(q, "::pg_catalog.regclass;\n");
16297+ /* Update pg_constraint after all the ALTER TABLEs */
16298+ if (firstitem)
16299+ {
16300+ appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n"
16301+ "SET conislocal = false\n"
16302+ "WHERE contype = 'c' AND conrelid = ");
16303+ appendStringLiteralAH(extra, qualrelname, fout);
16304+ appendPQExpBufferStr(extra, "::pg_catalog.regclass\n");
16305+ appendPQExpBufferStr(extra, " AND conname IN (");
16306+ firstitem = false;
16307+ }
16308+ else
16309+ appendPQExpBufferStr(extra, ", ");
16310+ appendStringLiteralAH(extra, constr->dobj.name, fout);
16311+ }
16312+ if (!firstitem)
16313+ {
16314+ appendPQExpBufferStr(extra, ");\n");
16315+ appendBinaryPQExpBuffer(q, extra->data, extra->len);
1625516316}
1625616317
1625716318if (numParents > 0 && !tbinfo->ispartition)
@@ -16438,7 +16499,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
1643816499if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
1643916500tbinfo->attfdwoptions[j][0] != '\0')
1644016501appendPQExpBuffer(q,
16441- "ALTER FOREIGN TABLE %s ALTER COLUMN %s OPTIONS (\n"
16502+ "ALTER FOREIGN TABLEONLY %s ALTER COLUMN %s OPTIONS (\n"
1644216503 " %s\n"
1644316504 ");\n",
1644416505 qualrelname,
@@ -16539,6 +16600,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
1653916600
1654016601destroyPQExpBuffer(q);
1654116602destroyPQExpBuffer(delq);
16603+ destroyPQExpBuffer(extra);
1654216604free(qrelname);
1654316605free(qualrelname);
1654416606}