pg_dump: Add a --load-via-partition-root option.

Rushabh Lathia, reviewed and somewhat revised by me.  Testing by
Rajkumar Raghuwanshi.

Discussion: http://postgr.es/m/CAGPqQf0C1he087bz9xRBOGZBuESYz9X=Fp8Ca_g+TfHgAff75g@mail.gmail.com
This commit is contained in:
Robert Haas 2017-08-14 22:54:41 -04:00
parent d2bc501573
commit 23d7680d04
6 changed files with 145 additions and 18 deletions

View File

@ -888,6 +888,21 @@ PostgreSQL documentation
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--load-via-partition-root</></term>
<listitem>
<para>
When dumping a COPY or INSERT statement for a partitioned table,
target the root of the partitioning hierarchy which contains it rather
than the partition itself. This may be useful when reloading data on
a server where rows do not always fall into the same partitions as
they did on the original server. This could happen, for example, if
the partitioning column is of type text and the two system have
different definitions of the collation used to partition the data.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--section=<replaceable class="parameter">sectionname</replaceable></option></term> <term><option>--section=<replaceable class="parameter">sectionname</replaceable></option></term>
<listitem> <listitem>

View File

@ -430,6 +430,21 @@ PostgreSQL documentation
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--load-via-partition-root</></term>
<listitem>
<para>
When dumping a COPY or INSERT statement for a partitioned table,
target the root of the partitioning hierarchy which contains it rather
than the partition itself. This may be useful when reloading data on
a server where rows do not always fall into the same partitions as
they did on the original server. This could happen, for example, if
the partitioning column is of type text and the two system have
different definitions of the collation used to partition the data.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--use-set-session-authorization</></term> <term><option>--use-set-session-authorization</></term>
<listitem> <listitem>

View File

@ -66,7 +66,7 @@ static int numExtensions;
static ExtensionMemberId *extmembers; static ExtensionMemberId *extmembers;
static int numextmembers; static int numextmembers;
static void flagInhTables(TableInfo *tbinfo, int numTables, static void flagInhTables(Archive *fout, TableInfo *tbinfo, int numTables,
InhInfo *inhinfo, int numInherits); InhInfo *inhinfo, int numInherits);
static void flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables); static void flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables);
static DumpableObject **buildIndexArray(void *objArray, int numObjs, static DumpableObject **buildIndexArray(void *objArray, int numObjs,
@ -243,7 +243,7 @@ getSchemaData(Archive *fout, int *numTablesPtr)
/* Link tables to parents, mark parents of target tables interesting */ /* Link tables to parents, mark parents of target tables interesting */
if (g_verbose) if (g_verbose)
write_msg(NULL, "finding inheritance relationships\n"); write_msg(NULL, "finding inheritance relationships\n");
flagInhTables(tblinfo, numTables, inhinfo, numInherits); flagInhTables(fout, tblinfo, numTables, inhinfo, numInherits);
if (g_verbose) if (g_verbose)
write_msg(NULL, "reading column info for interesting tables\n"); write_msg(NULL, "reading column info for interesting tables\n");
@ -294,8 +294,8 @@ getSchemaData(Archive *fout, int *numTablesPtr)
} }
/* flagInhTables - /* flagInhTables -
* Fill in parent link fields of every target table, and mark * Fill in parent link fields of tables for which we need that information,
* parents of target tables as interesting * and mark parents of target tables as interesting
* *
* Note that only direct ancestors of targets are marked interesting. * Note that only direct ancestors of targets are marked interesting.
* This is sufficient; we don't much care whether they inherited their * This is sufficient; we don't much care whether they inherited their
@ -304,34 +304,53 @@ getSchemaData(Archive *fout, int *numTablesPtr)
* modifies tblinfo * modifies tblinfo
*/ */
static void static void
flagInhTables(TableInfo *tblinfo, int numTables, flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables,
InhInfo *inhinfo, int numInherits) InhInfo *inhinfo, int numInherits)
{ {
DumpOptions *dopt = fout->dopt;
int i, int i,
j; j;
int numParents;
TableInfo **parents;
for (i = 0; i < numTables; i++) for (i = 0; i < numTables; i++)
{ {
bool find_parents = true;
bool mark_parents = true;
/* Some kinds never have parents */ /* Some kinds never have parents */
if (tblinfo[i].relkind == RELKIND_SEQUENCE || if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
tblinfo[i].relkind == RELKIND_VIEW || tblinfo[i].relkind == RELKIND_VIEW ||
tblinfo[i].relkind == RELKIND_MATVIEW) tblinfo[i].relkind == RELKIND_MATVIEW)
continue; continue;
/* Don't bother computing anything for non-target tables, either */ /*
* Normally, we don't bother computing anything for non-target tables,
* but if load-via-partition-root is specified, we gather information
* on every partition in the system so that getRootTableInfo can trace
* from any given to leaf partition all the way up to the root. (We
* don't need to mark them as interesting for getTableAttrs, though.)
*/
if (!tblinfo[i].dobj.dump) if (!tblinfo[i].dobj.dump)
continue; {
mark_parents = false;
/* Find all the immediate parent tables */ if (!dopt->load_via_partition_root ||
findParentsByOid(&tblinfo[i], inhinfo, numInherits); !tblinfo[i].ispartition)
find_parents = false;
}
/* Mark the parents as interesting for getTableAttrs */ /* If needed, find all the immediate parent tables. */
numParents = tblinfo[i].numParents; if (find_parents)
parents = tblinfo[i].parents; findParentsByOid(&tblinfo[i], inhinfo, numInherits);
for (j = 0; j < numParents; j++)
parents[j]->interesting = true; /* If needed, mark the parents as interesting for getTableAttrs. */
if (mark_parents)
{
int numParents = tblinfo[i].numParents;
TableInfo **parents = tblinfo[i].parents;
for (j = 0; j < numParents; j++)
parents[j]->interesting = true;
}
} }
} }

View File

@ -157,6 +157,7 @@ typedef struct _dumpOptions
int outputNoTablespaces; int outputNoTablespaces;
int use_setsessauth; int use_setsessauth;
int enable_row_security; int enable_row_security;
int load_via_partition_root;
/* default, if no "inclusion" switches appear, is to dump everything */ /* default, if no "inclusion" switches appear, is to dump everything */
bool include_everything; bool include_everything;

View File

@ -269,6 +269,7 @@ static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
const char *prefix, Archive *fout); const char *prefix, Archive *fout);
static char *get_synchronized_snapshot(Archive *fout); static char *get_synchronized_snapshot(Archive *fout);
static void setupDumpWorker(Archive *AHX); static void setupDumpWorker(Archive *AHX);
static TableInfo *getRootTableInfo(TableInfo *tbinfo);
int int
@ -345,6 +346,7 @@ main(int argc, char **argv)
{"lock-wait-timeout", required_argument, NULL, 2}, {"lock-wait-timeout", required_argument, NULL, 2},
{"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1}, {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 1},
{"quote-all-identifiers", no_argument, &quote_all_identifiers, 1}, {"quote-all-identifiers", no_argument, &quote_all_identifiers, 1},
{"load-via-partition-root", no_argument, &dopt.load_via_partition_root, 1},
{"role", required_argument, NULL, 3}, {"role", required_argument, NULL, 3},
{"section", required_argument, NULL, 5}, {"section", required_argument, NULL, 5},
{"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1}, {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1},
@ -959,6 +961,7 @@ help(const char *progname)
printf(_(" --no-tablespaces do not dump tablespace assignments\n")); printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
printf(_(" --no-unlogged-table-data do not dump unlogged table data\n")); printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n")); printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
printf(_(" --load-via-partition-root load partitions via the root table\n"));
printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n")); printf(_(" --section=SECTION dump named section (pre-data, data, or post-data)\n"));
printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n")); printf(_(" --serializable-deferrable wait until the dump can run without anomalies\n"));
printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n")); printf(_(" --snapshot=SNAPSHOT use given snapshot for the dump\n"));
@ -1902,8 +1905,32 @@ dumpTableData_insert(Archive *fout, void *dcontext)
if (insertStmt == NULL) if (insertStmt == NULL)
{ {
insertStmt = createPQExpBuffer(); 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.
*/
if (dopt->load_via_partition_root && tbinfo->ispartition)
{
TableInfo *parentTbinfo;
parentTbinfo = getRootTableInfo(tbinfo);
/*
* When we loading data through the root, we will qualify
* the table name. This is needed because earlier
* search_path will be set for the partition table.
*/
classname = (char *) fmtQualifiedId(fout->remoteVersion,
parentTbinfo->dobj.namespace->dobj.name,
parentTbinfo->dobj.name);
}
else
classname = fmtId(tbinfo->dobj.name);
appendPQExpBuffer(insertStmt, "INSERT INTO %s ", appendPQExpBuffer(insertStmt, "INSERT INTO %s ",
fmtId(classname)); classname);
/* corner case for zero-column table */ /* corner case for zero-column table */
if (nfields == 0) if (nfields == 0)
@ -2025,6 +2052,27 @@ dumpTableData_insert(Archive *fout, void *dcontext)
return 1; return 1;
} }
/*
* getRootTableInfo:
* get the root TableInfo for the given partition table.
*/
static TableInfo *
getRootTableInfo(TableInfo *tbinfo)
{
TableInfo *parentTbinfo;
Assert(tbinfo->ispartition);
Assert(tbinfo->numParents == 1);
parentTbinfo = tbinfo->parents[0];
while (parentTbinfo->ispartition)
{
Assert(parentTbinfo->numParents == 1);
parentTbinfo = parentTbinfo->parents[0];
}
return parentTbinfo;
}
/* /*
* dumpTableData - * dumpTableData -
@ -2041,14 +2089,38 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo)
PQExpBuffer clistBuf = createPQExpBuffer(); PQExpBuffer clistBuf = createPQExpBuffer();
DataDumperPtr dumpFn; DataDumperPtr dumpFn;
char *copyStmt; char *copyStmt;
const char *copyFrom;
if (!dopt->dump_inserts) if (!dopt->dump_inserts)
{ {
/* Dump/restore using COPY */ /* Dump/restore using COPY */
dumpFn = dumpTableData_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);
/*
* When we load data through the root, we will qualify the table
* name, because search_path is set for the partition.
*/
copyFrom = fmtQualifiedId(fout->remoteVersion,
parentTbinfo->dobj.namespace->dobj.name,
parentTbinfo->dobj.name);
}
else
copyFrom = fmtId(tbinfo->dobj.name);
/* must use 2 steps here 'cause fmtId is nonreentrant */ /* must use 2 steps here 'cause fmtId is nonreentrant */
appendPQExpBuffer(copyBuf, "COPY %s ", appendPQExpBuffer(copyBuf, "COPY %s ",
fmtId(tbinfo->dobj.name)); copyFrom);
appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n", appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n",
fmtCopyColumnList(tbinfo, clistBuf), fmtCopyColumnList(tbinfo, clistBuf),
(tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : ""); (tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : "");

View File

@ -80,6 +80,7 @@ static int no_subscriptions = 0;
static int no_unlogged_table_data = 0; static int no_unlogged_table_data = 0;
static int no_role_passwords = 0; static int no_role_passwords = 0;
static int server_version; static int server_version;
static int load_via_partition_root = 0;
static char role_catalog[10]; static char role_catalog[10];
#define PG_AUTHID "pg_authid" #define PG_AUTHID "pg_authid"
@ -128,6 +129,7 @@ main(int argc, char *argv[])
{"lock-wait-timeout", required_argument, NULL, 2}, {"lock-wait-timeout", required_argument, NULL, 2},
{"no-tablespaces", no_argument, &no_tablespaces, 1}, {"no-tablespaces", no_argument, &no_tablespaces, 1},
{"quote-all-identifiers", no_argument, &quote_all_identifiers, 1}, {"quote-all-identifiers", no_argument, &quote_all_identifiers, 1},
{"load-via-partition-root", no_argument, &load_via_partition_root, 1},
{"role", required_argument, NULL, 3}, {"role", required_argument, NULL, 3},
{"use-set-session-authorization", no_argument, &use_setsessauth, 1}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1},
{"no-publications", no_argument, &no_publications, 1}, {"no-publications", no_argument, &no_publications, 1},
@ -385,6 +387,8 @@ main(int argc, char *argv[])
appendPQExpBufferStr(pgdumpopts, " --no-tablespaces"); appendPQExpBufferStr(pgdumpopts, " --no-tablespaces");
if (quote_all_identifiers) if (quote_all_identifiers)
appendPQExpBufferStr(pgdumpopts, " --quote-all-identifiers"); appendPQExpBufferStr(pgdumpopts, " --quote-all-identifiers");
if (load_via_partition_root)
appendPQExpBufferStr(pgdumpopts, " --load-via-partition-root");
if (use_setsessauth) if (use_setsessauth)
appendPQExpBufferStr(pgdumpopts, " --use-set-session-authorization"); appendPQExpBufferStr(pgdumpopts, " --use-set-session-authorization");
if (no_publications) if (no_publications)
@ -606,6 +610,7 @@ help(void)
printf(_(" --no-tablespaces do not dump tablespace assignments\n")); printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
printf(_(" --no-unlogged-table-data do not dump unlogged table data\n")); printf(_(" --no-unlogged-table-data do not dump unlogged table data\n"));
printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n")); printf(_(" --quote-all-identifiers quote all identifiers, even if not key words\n"));
printf(_(" --load-via-partition-root load partitions via the root table\n"));
printf(_(" --use-set-session-authorization\n" printf(_(" --use-set-session-authorization\n"
" use SET SESSION AUTHORIZATION commands instead of\n" " use SET SESSION AUTHORIZATION commands instead of\n"
" ALTER OWNER commands to set ownership\n")); " ALTER OWNER commands to set ownership\n"));