@@ -119,6 +119,8 @@ static SimpleStringList table_exclude_patterns = {NULL, NULL};
119119static SimpleOidList table_exclude_oids = {NULL, NULL};
120120static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
121121static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
122+ static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
123+ static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
122124
123125
124126/* placeholders for the delimiters for comments */
@@ -153,6 +155,9 @@ static void expand_schema_name_patterns(Archive *fout,
153155 SimpleStringList *patterns,
154156 SimpleOidList *oids,
155157 bool strict_names);
158+ static void expand_foreign_server_name_patterns(Archive *fout,
159+ SimpleStringList *patterns,
160+ SimpleOidList *oids);
156161static void expand_table_name_patterns(Archive *fout,
157162 SimpleStringList *patterns,
158163 SimpleOidList *oids,
@@ -385,6 +390,7 @@ main(int argc, char **argv)
385390 {"no-sync", no_argument, NULL, 7},
386391 {"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
387392 {"rows-per-insert", required_argument, NULL, 10},
393+ {"include-foreign-data", required_argument, NULL, 11},
388394
389395 {NULL, 0, NULL, 0}
390396 };
@@ -600,6 +606,11 @@ main(int argc, char **argv)
600606 dopt.dump_inserts = (int) rowsPerInsert;
601607 break;
602608
609+ case 11: /* include foreign data */
610+ simple_string_list_append(&foreign_servers_include_patterns,
611+ optarg);
612+ break;
613+
603614 default:
604615 fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
605616 exit_nicely(1);
@@ -641,6 +652,12 @@ main(int argc, char **argv)
641652 exit_nicely(1);
642653 }
643654
655+ if (dopt.schemaOnly && foreign_servers_include_patterns.head != NULL)
656+ fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
657+
658+ if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
659+ fatal("option --include-foreign-data is not supported with parallel backup");
660+
644661 if (dopt.dataOnly && dopt.outputClean)
645662 {
646663 pg_log_error("options -c/--clean and -a/--data-only cannot be used together");
@@ -808,6 +825,9 @@ main(int argc, char **argv)
808825 &tabledata_exclude_oids,
809826 false);
810827
828+ expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
829+ &foreign_servers_include_oids);
830+
811831 /* non-matching exclusion patterns aren't an error */
812832
813833 /*
@@ -1011,6 +1031,9 @@ help(const char *progname)
10111031 printf(_(" --exclude-table-data=PATTERN do NOT dump data for the specified table(s)\n"));
10121032 printf(_(" --extra-float-digits=NUM override default setting for extra_float_digits\n"));
10131033 printf(_(" --if-exists use IF EXISTS when dropping objects\n"));
1034+ printf(_(" --include-foreign-data=PATTERN\n"
1035+ " include data of foreign tables in\n"
1036+ " foreign servers matching PATTERN\n"));
10141037 printf(_(" --inserts dump data as INSERT commands, rather than COPY\n"));
10151038 printf(_(" --load-via-partition-root load partitions via the root table\n"));
10161039 printf(_(" --no-comments do not dump comments\n"));
@@ -1330,6 +1353,51 @@ expand_schema_name_patterns(Archive *fout,
13301353 destroyPQExpBuffer(query);
13311354}
13321355
1356+ /*
1357+ * Find the OIDs of all foreign servers matching the given list of patterns,
1358+ * and append them to the given OID list.
1359+ */
1360+ static void
1361+ expand_foreign_server_name_patterns(Archive *fout,
1362+ SimpleStringList *patterns,
1363+ SimpleOidList *oids)
1364+ {
1365+ PQExpBuffer query;
1366+ PGresult *res;
1367+ SimpleStringListCell *cell;
1368+ int i;
1369+
1370+ if (patterns->head == NULL)
1371+ return; /* nothing to do */
1372+
1373+ query = createPQExpBuffer();
1374+
1375+ /*
1376+ * The loop below runs multiple SELECTs might sometimes result in
1377+ * duplicate entries in the OID list, but we don't care.
1378+ */
1379+
1380+ for (cell = patterns->head; cell; cell = cell->next)
1381+ {
1382+ appendPQExpBuffer(query,
1383+ "SELECT oid FROM pg_catalog.pg_foreign_server s\n");
1384+ processSQLNamePattern(GetConnection(fout), query, cell->val, false,
1385+ false, NULL, "s.srvname", NULL, NULL);
1386+
1387+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
1388+ if (PQntuples(res) == 0)
1389+ fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
1390+
1391+ for (i = 0; i < PQntuples(res); i++)
1392+ simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
1393+
1394+ PQclear(res);
1395+ resetPQExpBuffer(query);
1396+ }
1397+
1398+ destroyPQExpBuffer(query);
1399+ }
1400+
13331401/*
13341402 * Find the OIDs of all tables matching the given list of patterns,
13351403 * and append them to the given OID list. See also expand_dbname_patterns()
@@ -1775,7 +1843,6 @@ selectDumpableObject(DumpableObject *dobj, Archive *fout)
17751843 * - this routine is called by the Archiver when it wants the table
17761844 * to be dumped.
17771845 */
1778-
17791846static int
17801847dumpTableData_copy(Archive *fout, void *dcontext)
17811848{
@@ -1806,7 +1873,12 @@ dumpTableData_copy(Archive *fout, void *dcontext)
18061873 */
18071874 column_list = fmtCopyColumnList(tbinfo, clistBuf);
18081875
1809- if (tdinfo->filtercond)
1876+ /*
1877+ * Use COPY (SELECT ...) TO when dumping a foreign table's data, and when
1878+ * a filter condition was specified. For other cases a simple COPY
1879+ * suffices.
1880+ */
1881+ if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
18101882 {
18111883 /* Note: this syntax is only supported in 8.2 and up */
18121884 appendPQExpBufferStr(q, "COPY (SELECT ");
@@ -1818,9 +1890,10 @@ dumpTableData_copy(Archive *fout, void *dcontext)
18181890 }
18191891 else
18201892 appendPQExpBufferStr(q, "* ");
1893+
18211894 appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
18221895 fmtQualifiedDumpable(tbinfo),
1823- tdinfo->filtercond);
1896+ tdinfo->filtercond ? tdinfo->filtercond : "" );
18241897 }
18251898 else
18261899 {
@@ -2336,8 +2409,11 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
23362409 /* Skip VIEWs (no data to dump) */
23372410 if (tbinfo->relkind == RELKIND_VIEW)
23382411 return;
2339- /* Skip FOREIGN TABLEs (no data to dump) */
2340- if (tbinfo->relkind == RELKIND_FOREIGN_TABLE)
2412+ /* Skip FOREIGN TABLEs (no data to dump) unless requested explicitly */
2413+ if (tbinfo->relkind == RELKIND_FOREIGN_TABLE &&
2414+ (foreign_servers_include_oids.head == NULL ||
2415+ !simple_oid_list_member(&foreign_servers_include_oids,
2416+ tbinfo->foreign_server)))
23412417 return;
23422418 /* Skip partitioned tables (data in partitions) */
23432419 if (tbinfo->relkind == RELKIND_PARTITIONED_TABLE)
@@ -5999,6 +6075,7 @@ getTables(Archive *fout, int *numTables)
59996075 int i_toastreloptions;
60006076 int i_reloftype;
60016077 int i_relpages;
6078+ int i_foreignserver;
60026079 int i_is_identity_sequence;
60036080 int i_changed_acl;
60046081 int i_partkeydef;
@@ -6095,6 +6172,9 @@ getTables(Archive *fout, int *numTables)
60956172 "tc.relminmxid AS tminmxid, "
60966173 "c.relpersistence, c.relispopulated, "
60976174 "c.relreplident, c.relpages, am.amname, "
6175+ "CASE WHEN c.relkind = 'f' THEN "
6176+ "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
6177+ "ELSE 0 END AS foreignserver, "
60986178 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
60996179 "d.refobjid AS owning_tab, "
61006180 "d.refobjsubid AS owning_col, "
@@ -6185,6 +6265,9 @@ getTables(Archive *fout, int *numTables)
61856265 "c.relpersistence, c.relispopulated, "
61866266 "c.relreplident, c.relpages, "
61876267 "NULL AS amname, "
6268+ "CASE WHEN c.relkind = 'f' THEN "
6269+ "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
6270+ "ELSE 0 END AS foreignserver, "
61886271 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
61896272 "d.refobjid AS owning_tab, "
61906273 "d.refobjsubid AS owning_col, "
@@ -6235,6 +6318,9 @@ getTables(Archive *fout, int *numTables)
62356318 "c.relpersistence, c.relispopulated, "
62366319 "c.relreplident, c.relpages, "
62376320 "NULL AS amname, "
6321+ "CASE WHEN c.relkind = 'f' THEN "
6322+ "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
6323+ "ELSE 0 END AS foreignserver, "
62386324 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
62396325 "d.refobjid AS owning_tab, "
62406326 "d.refobjsubid AS owning_col, "
@@ -6285,6 +6371,9 @@ getTables(Archive *fout, int *numTables)
62856371 "c.relpersistence, c.relispopulated, "
62866372 "'d' AS relreplident, c.relpages, "
62876373 "NULL AS amname, "
6374+ "CASE WHEN c.relkind = 'f' THEN "
6375+ "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
6376+ "ELSE 0 END AS foreignserver, "
62886377 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
62896378 "d.refobjid AS owning_tab, "
62906379 "d.refobjsubid AS owning_col, "
@@ -6335,6 +6424,9 @@ getTables(Archive *fout, int *numTables)
63356424 "c.relpersistence, 't' as relispopulated, "
63366425 "'d' AS relreplident, c.relpages, "
63376426 "NULL AS amname, "
6427+ "CASE WHEN c.relkind = 'f' THEN "
6428+ "(SELECT ftserver FROM pg_catalog.pg_foreign_table WHERE ftrelid = c.oid) "
6429+ "ELSE 0 END AS foreignserver, "
63386430 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
63396431 "d.refobjid AS owning_tab, "
63406432 "d.refobjsubid AS owning_col, "
@@ -6383,6 +6475,7 @@ getTables(Archive *fout, int *numTables)
63836475 "'p' AS relpersistence, 't' as relispopulated, "
63846476 "'d' AS relreplident, c.relpages, "
63856477 "NULL AS amname, "
6478+ "NULL AS foreignserver, "
63866479 "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
63876480 "d.refobjid AS owning_tab, "
63886481 "d.refobjsubid AS owning_col, "
@@ -6430,6 +6523,7 @@ getTables(Archive *fout, int *numTables)
64306523 "'p' AS relpersistence, 't' as relispopulated, "
64316524 "'d' AS relreplident, c.relpages, "
64326525 "NULL AS amname, "
6526+ "NULL AS foreignserver, "
64336527 "NULL AS reloftype, "
64346528 "d.refobjid AS owning_tab, "
64356529 "d.refobjsubid AS owning_col, "
@@ -6477,6 +6571,7 @@ getTables(Archive *fout, int *numTables)
64776571 "'p' AS relpersistence, 't' as relispopulated, "
64786572 "'d' AS relreplident, c.relpages, "
64796573 "NULL AS amname, "
6574+ "NULL AS foreignserver, "
64806575 "NULL AS reloftype, "
64816576 "d.refobjid AS owning_tab, "
64826577 "d.refobjsubid AS owning_col, "
@@ -6523,6 +6618,7 @@ getTables(Archive *fout, int *numTables)
65236618 "'p' AS relpersistence, 't' as relispopulated, "
65246619 "'d' AS relreplident, relpages, "
65256620 "NULL AS amname, "
6621+ "NULL AS foreignserver, "
65266622 "NULL AS reloftype, "
65276623 "d.refobjid AS owning_tab, "
65286624 "d.refobjsubid AS owning_col, "
@@ -6590,6 +6686,7 @@ getTables(Archive *fout, int *numTables)
65906686 i_relispopulated = PQfnumber(res, "relispopulated");
65916687 i_relreplident = PQfnumber(res, "relreplident");
65926688 i_relpages = PQfnumber(res, "relpages");
6689+ i_foreignserver = PQfnumber(res, "foreignserver");
65936690 i_owning_tab = PQfnumber(res, "owning_tab");
65946691 i_owning_col = PQfnumber(res, "owning_col");
65956692 i_reltablespace = PQfnumber(res, "reltablespace");
@@ -6714,6 +6811,9 @@ getTables(Archive *fout, int *numTables)
67146811 tblinfo[i].ispartition = (strcmp(PQgetvalue(res, i, i_ispartition), "t") == 0);
67156812 tblinfo[i].partbound = pg_strdup(PQgetvalue(res, i, i_partbound));
67166813
6814+ /* foreign server */
6815+ tblinfo[i].foreign_server = atooid(PQgetvalue(res, i, i_foreignserver));
6816+
67176817 /*
67186818 * Read-lock target tables to make sure they aren't DROPPED or altered
67196819 * in schema before we get around to dumping them.
0 commit comments