]> granicus.if.org Git - postgresql/commitdiff
pg_dump: Add a --load-via-partition-root option.
authorRobert Haas <rhaas@postgresql.org>
Tue, 15 Aug 2017 02:54:41 +0000 (22:54 -0400)
committerRobert Haas <rhaas@postgresql.org>
Tue, 15 Aug 2017 02:54:41 +0000 (22:54 -0400)
Rushabh Lathia, reviewed and somewhat revised by me.  Testing by
Rajkumar Raghuwanshi.

Discussion: http://postgr.es/m/CAGPqQf0C1he087bz9xRBOGZBuESYz9X=Fp8Ca_g+TfHgAff75g@mail.gmail.com

doc/src/sgml/ref/pg_dump.sgml
doc/src/sgml/ref/pg_dumpall.sgml
src/bin/pg_dump/common.c
src/bin/pg_dump/pg_backup.h
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dumpall.c

index bafa031e1a8ec4b674260c03da3b5b74663a911e..ad5b6fc703587290840730e46e941d9b6b0ee9a1 100644 (file)
@@ -888,6 +888,21 @@ PostgreSQL documentation
       </listitem>
      </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>
        <term><option>--section=<replaceable class="parameter">sectionname</replaceable></option></term>
        <listitem>
index aa944a2e92ad074d92687dd194e4ecb9ae368664..f8a2521743fb7e71e90bd7fdf2825f85d9a612cc 100644 (file)
@@ -430,6 +430,21 @@ PostgreSQL documentation
       </listitem>
      </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>
       <term><option>--use-set-session-authorization</></term>
       <listitem>
index 47191be86ad73cf2c89057abff8545c433716a7f..4b47951de1b73cd771b3a28edf622d0eadfd9a76 100644 (file)
@@ -66,7 +66,7 @@ static int    numExtensions;
 static ExtensionMemberId *extmembers;
 static int     numextmembers;
 
-static void flagInhTables(TableInfo *tbinfo, int numTables,
+static void flagInhTables(Archive *fout, TableInfo *tbinfo, int numTables,
                          InhInfo *inhinfo, int numInherits);
 static void flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables);
 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 */
        if (g_verbose)
                write_msg(NULL, "finding inheritance relationships\n");
-       flagInhTables(tblinfo, numTables, inhinfo, numInherits);
+       flagInhTables(fout, tblinfo, numTables, inhinfo, numInherits);
 
        if (g_verbose)
                write_msg(NULL, "reading column info for interesting tables\n");
@@ -294,8 +294,8 @@ getSchemaData(Archive *fout, int *numTablesPtr)
 }
 
 /* flagInhTables -
- *      Fill in parent link fields of every target table, and mark
- *      parents of target tables as interesting
+ *      Fill in parent link fields of tables for which we need that information,
+ *      and mark parents of target tables as interesting
  *
  * Note that only direct ancestors of targets are marked interesting.
  * This is sufficient; we don't much care whether they inherited their
@@ -304,34 +304,53 @@ getSchemaData(Archive *fout, int *numTablesPtr)
  * modifies tblinfo
  */
 static void
-flagInhTables(TableInfo *tblinfo, int numTables,
+flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables,
                          InhInfo *inhinfo, int numInherits)
 {
+       DumpOptions *dopt = fout->dopt;
        int                     i,
                                j;
-       int                     numParents;
-       TableInfo **parents;
 
        for (i = 0; i < numTables; i++)
        {
+               bool            find_parents = true;
+               bool            mark_parents = true;
+
                /* Some kinds never have parents */
                if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
                        tblinfo[i].relkind == RELKIND_VIEW ||
                        tblinfo[i].relkind == RELKIND_MATVIEW)
                        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)
-                       continue;
+               {
+                       mark_parents = false;
 
-               /* Find all the immediate parent tables */
-               findParentsByOid(&tblinfo[i], inhinfo, numInherits);
+                       if (!dopt->load_via_partition_root ||
+                               !tblinfo[i].ispartition)
+                               find_parents = false;
+               }
+
+               /* If needed, find all the immediate parent tables. */
+               if (find_parents)
+                       findParentsByOid(&tblinfo[i], inhinfo, numInherits);
 
-               /* Mark the parents as interesting for getTableAttrs */
-               numParents = tblinfo[i].numParents;
-               parents = tblinfo[i].parents;
-               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;
+               }
        }
 }
 
index 144068ac4922167a3eb2c89c92df26661ffd657d..ce3100f09d65a367994a2d3072eba9f9592ac18a 100644 (file)
@@ -157,6 +157,7 @@ typedef struct _dumpOptions
        int                     outputNoTablespaces;
        int                     use_setsessauth;
        int                     enable_row_security;
+       int                     load_via_partition_root;
 
        /* default, if no "inclusion" switches appear, is to dump everything */
        bool            include_everything;
index 2d8bb32dc07338953c3ae9e66319885b3b41360b..628bdea1fd498bcf961267763b6a2859c61c3f8e 100644 (file)
@@ -269,6 +269,7 @@ static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
                                                const char *prefix, Archive *fout);
 static char *get_synchronized_snapshot(Archive *fout);
 static void setupDumpWorker(Archive *AHX);
+static TableInfo *getRootTableInfo(TableInfo *tbinfo);
 
 
 int
@@ -345,6 +346,7 @@ main(int argc, char **argv)
                {"lock-wait-timeout", required_argument, NULL, 2},
                {"no-tablespaces", no_argument, &dopt.outputNoTablespaces, 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},
                {"section", required_argument, NULL, 5},
                {"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-unlogged-table-data     do not dump unlogged table data\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(_("  --serializable-deferrable    wait until the dump can run without anomalies\n"));
        printf(_("  --snapshot=SNAPSHOT          use given snapshot for the dump\n"));
@@ -1902,8 +1905,32 @@ dumpTableData_insert(Archive *fout, void *dcontext)
                        if (insertStmt == NULL)
                        {
                                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 ",
-                                                                 fmtId(classname));
+                                                                 classname);
 
                                /* corner case for zero-column table */
                                if (nfields == 0)
@@ -2025,6 +2052,27 @@ dumpTableData_insert(Archive *fout, void *dcontext)
        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 -
@@ -2041,14 +2089,38 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo)
        PQExpBuffer clistBuf = createPQExpBuffer();
        DataDumperPtr dumpFn;
        char       *copyStmt;
+       const char *copyFrom;
 
        if (!dopt->dump_inserts)
        {
                /* 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);
+
+                       /*
+                        * 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 */
                appendPQExpBuffer(copyBuf, "COPY %s ",
-                                                 fmtId(tbinfo->dobj.name));
+                                                 copyFrom);
                appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n",
                                                  fmtCopyColumnList(tbinfo, clistBuf),
                                                  (tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : "");
index b14bb8e963fcac52087d1c8d6a805e593d329239..c0a0346cd9c9bb39f2a3702a4d58fa0af1334fec 100644 (file)
@@ -80,6 +80,7 @@ static int    no_subscriptions = 0;
 static int     no_unlogged_table_data = 0;
 static int     no_role_passwords = 0;
 static int     server_version;
+static int     load_via_partition_root = 0;
 
 static char role_catalog[10];
 #define PG_AUTHID "pg_authid"
@@ -128,6 +129,7 @@ main(int argc, char *argv[])
                {"lock-wait-timeout", required_argument, NULL, 2},
                {"no-tablespaces", no_argument, &no_tablespaces, 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},
                {"use-set-session-authorization", no_argument, &use_setsessauth, 1},
                {"no-publications", no_argument, &no_publications, 1},
@@ -385,6 +387,8 @@ main(int argc, char *argv[])
                appendPQExpBufferStr(pgdumpopts, " --no-tablespaces");
        if (quote_all_identifiers)
                appendPQExpBufferStr(pgdumpopts, " --quote-all-identifiers");
+       if (load_via_partition_root)
+               appendPQExpBufferStr(pgdumpopts, " --load-via-partition-root");
        if (use_setsessauth)
                appendPQExpBufferStr(pgdumpopts, " --use-set-session-authorization");
        if (no_publications)
@@ -606,6 +610,7 @@ help(void)
        printf(_("  --no-tablespaces             do not dump tablespace assignments\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(_("  --load-via-partition-root    load partitions via the root table\n"));
        printf(_("  --use-set-session-authorization\n"
                         "                               use SET SESSION AUTHORIZATION commands instead of\n"
                         "                               ALTER OWNER commands to set ownership\n"));