diff options
Diffstat (limited to 'src/bin/pg_dump/pg_dump.c')
-rw-r--r-- | src/bin/pg_dump/pg_dump.c | 151 |
1 files changed, 130 insertions, 21 deletions
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 345a16d4894..3f63b34395f 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -302,6 +302,7 @@ static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions, static char *get_synchronized_snapshot(Archive *fout); static void setupDumpWorker(Archive *AHX); static TableInfo *getRootTableInfo(const TableInfo *tbinfo); +static bool forcePartitionRootLoad(const TableInfo *tbinfo); int @@ -2217,11 +2218,13 @@ dumpTableData_insert(Archive *fout, const void *dcontext) insertStmt = createPQExpBuffer(); /* - * When load-via-partition-root is set, get the root table name - * for the partition table, so that we can reload data through the - * root table. + * When load-via-partition-root is set or forced, get the root + * table name for the partition table, so that we can reload data + * through the root table. */ - if (dopt->load_via_partition_root && tbinfo->ispartition) + if (tbinfo->ispartition && + (dopt->load_via_partition_root || + forcePartitionRootLoad(tbinfo))) targettab = getRootTableInfo(tbinfo); else targettab = tbinfo; @@ -2420,6 +2423,35 @@ getRootTableInfo(const TableInfo *tbinfo) } /* + * forcePartitionRootLoad + * Check if we must force load_via_partition_root for this partition. + * + * This is required if any level of ancestral partitioned table has an + * unsafe partitioning scheme. + */ +static bool +forcePartitionRootLoad(const TableInfo *tbinfo) +{ + TableInfo *parentTbinfo; + + Assert(tbinfo->ispartition); + Assert(tbinfo->numParents == 1); + + parentTbinfo = tbinfo->parents[0]; + if (parentTbinfo->unsafe_partitions) + return true; + while (parentTbinfo->ispartition) + { + Assert(parentTbinfo->numParents == 1); + parentTbinfo = parentTbinfo->parents[0]; + if (parentTbinfo->unsafe_partitions) + return true; + } + + return false; +} + +/* * dumpTableData - * dump the contents of a single table * @@ -2433,34 +2465,40 @@ dumpTableData(Archive *fout, const TableDataInfo *tdinfo) PQExpBuffer copyBuf = createPQExpBuffer(); PQExpBuffer clistBuf = createPQExpBuffer(); DataDumperPtr dumpFn; + char *tdDefn = NULL; char *copyStmt; const char *copyFrom; /* We had better have loaded per-column details about this table */ Assert(tbinfo->interesting); + /* + * When load-via-partition-root is set or forced, get the root table name + * for the partition table, so that we can reload data through the root + * table. Then construct a comment to be inserted into the TOC entry's + * defn field, so that such cases can be identified reliably. + */ + if (tbinfo->ispartition && + (dopt->load_via_partition_root || + forcePartitionRootLoad(tbinfo))) + { + TableInfo *parentTbinfo; + + parentTbinfo = getRootTableInfo(tbinfo); + copyFrom = fmtQualifiedDumpable(parentTbinfo); + printfPQExpBuffer(copyBuf, "-- load via partition root %s", + copyFrom); + tdDefn = pg_strdup(copyBuf->data); + } + else + copyFrom = fmtQualifiedDumpable(tbinfo); + if (dopt->dump_inserts == 0) { /* Dump/restore using COPY */ dumpFn = dumpTableData_copy; - - /* - * When load-via-partition-root is set, get the root table name for - * the partition table, so that we can reload data through the root - * table. - */ - if (dopt->load_via_partition_root && tbinfo->ispartition) - { - TableInfo *parentTbinfo; - - parentTbinfo = getRootTableInfo(tbinfo); - copyFrom = fmtQualifiedDumpable(parentTbinfo); - } - else - copyFrom = fmtQualifiedDumpable(tbinfo); - /* must use 2 steps here 'cause fmtId is nonreentrant */ - appendPQExpBuffer(copyBuf, "COPY %s ", + printfPQExpBuffer(copyBuf, "COPY %s ", copyFrom); appendPQExpBuffer(copyBuf, "%s FROM stdin;\n", fmtCopyColumnList(tbinfo, clistBuf)); @@ -2488,6 +2526,7 @@ dumpTableData(Archive *fout, const TableDataInfo *tdinfo) .owner = tbinfo->rolname, .description = "TABLE DATA", .section = SECTION_DATA, + .createStmt = tdDefn, .copyStmt = copyStmt, .deps = &(tbinfo->dobj.dumpId), .nDeps = 1, @@ -7197,6 +7236,76 @@ getInherits(Archive *fout, int *numInherits) } /* + * getPartitioningInfo + * get information about partitioning + * + * For the most part, we only collect partitioning info about tables we + * intend to dump. However, this function has to consider all partitioned + * tables in the database, because we need to know about parents of partitions + * we are going to dump even if the parents themselves won't be dumped. + * + * Specifically, what we need to know is whether each partitioned table + * has an "unsafe" partitioning scheme that requires us to force + * load-via-partition-root mode for its children. Currently the only case + * for which we force that is hash partitioning on enum columns, since the + * hash codes depend on enum value OIDs which won't be replicated across + * dump-and-reload. There are other cases in which load-via-partition-root + * might be necessary, but we expect users to cope with them. + */ +void +getPartitioningInfo(Archive *fout) +{ + PQExpBuffer query; + PGresult *res; + int ntups; + + /* hash partitioning didn't exist before v11 */ + if (fout->remoteVersion < 110000) + return; + /* needn't bother if schema-only dump */ + if (fout->dopt->schemaOnly) + return; + + query = createPQExpBuffer(); + + /* + * Unsafe partitioning schemes are exactly those for which hash enum_ops + * appears among the partition opclasses. We needn't check partstrat. + * + * Note that this query may well retrieve info about tables we aren't + * going to dump and hence have no lock on. That's okay since we need not + * invoke any unsafe server-side functions. + */ + appendPQExpBufferStr(query, + "SELECT partrelid FROM pg_partitioned_table WHERE\n" + "(SELECT c.oid FROM pg_opclass c JOIN pg_am a " + "ON c.opcmethod = a.oid\n" + "WHERE opcname = 'enum_ops' " + "AND opcnamespace = 'pg_catalog'::regnamespace " + "AND amname = 'hash') = ANY(partclass)"); + + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + + ntups = PQntuples(res); + + for (int i = 0; i < ntups; i++) + { + Oid tabrelid = atooid(PQgetvalue(res, i, 0)); + TableInfo *tbinfo; + + tbinfo = findTableByOid(tabrelid); + if (tbinfo == NULL) + fatal("failed sanity check, table OID %u appearing in pg_partitioned_table not found", + tabrelid); + tbinfo->unsafe_partitions = true; + } + + PQclear(res); + + destroyPQExpBuffer(query); +} + +/* * getIndexes * get information about every index on a dumpable table * |