aboutsummaryrefslogtreecommitdiff
path: root/src/bin/pg_dump/pg_dump.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/pg_dump/pg_dump.c')
-rw-r--r--src/bin/pg_dump/pg_dump.c151
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 98da9c54401..17f84d28a95 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -317,6 +317,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
@@ -2192,11 +2193,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;
@@ -2395,6 +2398,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
*
@@ -2408,34 +2440,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));
@@ -2463,6 +2501,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,
@@ -6655,6 +6694,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)
+ pg_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
*