diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bin/pg_dump/pg_dump.c | 134 | ||||
-rw-r--r-- | src/bin/pg_dump/t/002_pg_dump.pl | 2 |
2 files changed, 99 insertions, 37 deletions
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index e3240708284..9202a43eb20 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -15764,6 +15764,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) DumpOptions *dopt = fout->dopt; PQExpBuffer q = createPQExpBuffer(); PQExpBuffer delq = createPQExpBuffer(); + PQExpBuffer extra = createPQExpBuffer(); char *qrelname; char *qualrelname; int numParents; @@ -15830,7 +15831,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) char *partkeydef = NULL; char *ftoptions = NULL; char *srvname = NULL; - char *foreign = ""; + const char *foreign = ""; /* * Set reltypename, and collect any relkind-specific data that we @@ -16188,51 +16189,98 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) tbinfo->relkind == RELKIND_FOREIGN_TABLE || tbinfo->relkind == RELKIND_PARTITIONED_TABLE)) { + bool firstitem; + + /* + * Drop any dropped columns. Merge the pg_attribute manipulations + * into a single SQL command, so that we don't cause repeated + * relcache flushes on the target table. Otherwise we risk O(N^2) + * relcache bloat while dropping N columns. + */ + resetPQExpBuffer(extra); + firstitem = true; for (j = 0; j < tbinfo->numatts; j++) { if (tbinfo->attisdropped[j]) { - appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped column.\n"); - appendPQExpBuffer(q, "UPDATE pg_catalog.pg_attribute\n" - "SET attlen = %d, " - "attalign = '%c', attbyval = false\n" - "WHERE attname = ", + if (firstitem) + { + appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate dropped columns.\n" + "UPDATE pg_catalog.pg_attribute\n" + "SET attlen = v.dlen, " + "attalign = v.dalign, " + "attbyval = false\n" + "FROM (VALUES "); + firstitem = false; + } + else + appendPQExpBufferStr(q, ",\n "); + appendPQExpBufferChar(q, '('); + appendStringLiteralAH(q, tbinfo->attnames[j], fout); + appendPQExpBuffer(q, ", %d, '%c')", tbinfo->attlen[j], tbinfo->attalign[j]); - appendStringLiteralAH(q, tbinfo->attnames[j], fout); - appendPQExpBufferStr(q, "\n AND attrelid = "); - appendStringLiteralAH(q, qualrelname, fout); - appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); - - if (tbinfo->relkind == RELKIND_RELATION || - tbinfo->relkind == RELKIND_PARTITIONED_TABLE) - appendPQExpBuffer(q, "ALTER TABLE ONLY %s ", - qualrelname); - else - appendPQExpBuffer(q, "ALTER FOREIGN TABLE ONLY %s ", - qualrelname); - appendPQExpBuffer(q, "DROP COLUMN %s;\n", + /* The ALTER ... DROP COLUMN commands must come after */ + appendPQExpBuffer(extra, "ALTER %sTABLE ONLY %s ", + foreign, qualrelname); + appendPQExpBuffer(extra, "DROP COLUMN %s;\n", fmtId(tbinfo->attnames[j])); } - else if (!tbinfo->attislocal[j]) + } + if (!firstitem) + { + appendPQExpBufferStr(q, ") v(dname, dlen, dalign)\n" + "WHERE attrelid = "); + appendStringLiteralAH(q, qualrelname, fout); + appendPQExpBufferStr(q, "::pg_catalog.regclass\n" + " AND attname = v.dname;\n"); + /* Now we can issue the actual DROP COLUMN commands */ + appendBinaryPQExpBuffer(q, extra->data, extra->len); + } + + /* + * Fix up inherited columns. As above, do the pg_attribute + * manipulations in a single SQL command. + */ + firstitem = true; + for (j = 0; j < tbinfo->numatts; j++) + { + if (!tbinfo->attisdropped[j] && + !tbinfo->attislocal[j]) { - appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited column.\n"); - appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n" - "SET attislocal = false\n" - "WHERE attname = "); + if (firstitem) + { + appendPQExpBufferStr(q, "\n-- For binary upgrade, recreate inherited columns.\n"); + appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_attribute\n" + "SET attislocal = false\n" + "WHERE attrelid = "); + appendStringLiteralAH(q, qualrelname, fout); + appendPQExpBufferStr(q, "::pg_catalog.regclass\n" + " AND attname IN ("); + firstitem = false; + } + else + appendPQExpBufferStr(q, ", "); appendStringLiteralAH(q, tbinfo->attnames[j], fout); - appendPQExpBufferStr(q, "\n AND attrelid = "); - appendStringLiteralAH(q, qualrelname, fout); - appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); } } + if (!firstitem) + appendPQExpBufferStr(q, ");\n"); /* * Add inherited CHECK constraints, if any. * * For partitions, they were already dumped, and conislocal * doesn't need fixing. + * + * As above, issue only one direct manipulation of pg_constraint. + * Although it is tempting to merge the ALTER ADD CONSTRAINT + * commands into one as well, refrain for now due to concern about + * possible backend memory bloat if there are many such + * constraints. */ + resetPQExpBuffer(extra); + firstitem = true; for (k = 0; k < tbinfo->ncheck; k++) { ConstraintInfo *constr = &(tbinfo->checkexprs[k]); @@ -16240,18 +16288,31 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) if (constr->separate || constr->conislocal || tbinfo->ispartition) continue; - appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraint.\n"); + if (firstitem) + appendPQExpBufferStr(q, "\n-- For binary upgrade, set up inherited constraints.\n"); appendPQExpBuffer(q, "ALTER %sTABLE ONLY %s ADD CONSTRAINT %s %s;\n", foreign, qualrelname, fmtId(constr->dobj.name), constr->condef); - appendPQExpBufferStr(q, "UPDATE pg_catalog.pg_constraint\n" - "SET conislocal = false\n" - "WHERE contype = 'c' AND conname = "); - appendStringLiteralAH(q, constr->dobj.name, fout); - appendPQExpBufferStr(q, "\n AND conrelid = "); - appendStringLiteralAH(q, qualrelname, fout); - appendPQExpBufferStr(q, "::pg_catalog.regclass;\n"); + /* Update pg_constraint after all the ALTER TABLEs */ + if (firstitem) + { + appendPQExpBufferStr(extra, "UPDATE pg_catalog.pg_constraint\n" + "SET conislocal = false\n" + "WHERE contype = 'c' AND conrelid = "); + appendStringLiteralAH(extra, qualrelname, fout); + appendPQExpBufferStr(extra, "::pg_catalog.regclass\n"); + appendPQExpBufferStr(extra, " AND conname IN ("); + firstitem = false; + } + else + appendPQExpBufferStr(extra, ", "); + appendStringLiteralAH(extra, constr->dobj.name, fout); + } + if (!firstitem) + { + appendPQExpBufferStr(extra, ");\n"); + appendBinaryPQExpBuffer(q, extra->data, extra->len); } if (numParents > 0 && !tbinfo->ispartition) @@ -16438,7 +16499,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) if (tbinfo->relkind == RELKIND_FOREIGN_TABLE && tbinfo->attfdwoptions[j][0] != '\0') appendPQExpBuffer(q, - "ALTER FOREIGN TABLE %s ALTER COLUMN %s OPTIONS (\n" + "ALTER FOREIGN TABLE ONLY %s ALTER COLUMN %s OPTIONS (\n" " %s\n" ");\n", qualrelname, @@ -16539,6 +16600,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo) destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(extra); free(qrelname); free(qualrelname); } diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl index d3dd8784d64..5bcc2244d58 100644 --- a/src/bin/pg_dump/t/002_pg_dump.pl +++ b/src/bin/pg_dump/t/002_pg_dump.pl @@ -1154,7 +1154,7 @@ my %tests = ( 'ALTER FOREIGN TABLE foreign_table ALTER COLUMN c1 OPTIONS' => { regexp => qr/^ - \QALTER FOREIGN TABLE dump_test.foreign_table ALTER COLUMN c1 OPTIONS (\E\n + \QALTER FOREIGN TABLE ONLY dump_test.foreign_table ALTER COLUMN c1 OPTIONS (\E\n \s+\Qcolumn_name 'col1'\E\n \Q);\E\n /xm, |