diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index 0b2e2de87b..7dc3041247 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -1104,6 +1104,24 @@ PostgreSQL documentation
+
+
+
+
+ When dumping data for table, only include rows
+ that meet the filter_clause condition.
+ if contains subquery, uses schema-qualified name otherwise
+ it is error because pg_dump uses schema-qualified object name to identifies the tables.
+ This option is useful when you want to dump only a subset of a particular table.
+ can be given more than once to provide different filters
+ for multiple tables. Note that if multiple options refer to the same table,
+ only the first filter_clause will be applied. If necessary, use quotes in your shell to
+ provide an argument that contains spaces.
+ E.g. --where=mytable:"created_at >= '2018-01-01' AND test = 'f'"
+
+
+
+
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index d3ca54e4dc..418684e272 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -121,6 +121,8 @@ static SimpleStringList tabledata_exclude_patterns = {NULL, NULL};
static SimpleOidList tabledata_exclude_oids = {NULL, NULL};
static SimpleStringList foreign_servers_include_patterns = {NULL, NULL};
static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
+static SimpleStringList tabledata_where_patterns = {NULL, NULL};
+static SimpleOidList tabledata_where_oids = {NULL, NULL};
static const CatalogId nilCatalogId = {0, 0};
@@ -156,7 +158,8 @@ static void expand_foreign_server_name_patterns(Archive *fout,
static void expand_table_name_patterns(Archive *fout,
SimpleStringList *patterns,
SimpleOidList *oids,
- bool strict_names);
+ bool strict_names,
+ bool match_data);
static NamespaceInfo *findNamespace(Archive *fout, Oid nsoid);
static void dumpTableData(Archive *fout, TableDataInfo *tdinfo);
static void refreshMatViewData(Archive *fout, TableDataInfo *tdinfo);
@@ -387,6 +390,7 @@ main(int argc, char **argv)
{"on-conflict-do-nothing", no_argument, &dopt.do_nothing, 1},
{"rows-per-insert", required_argument, NULL, 10},
{"include-foreign-data", required_argument, NULL, 11},
+ {"where", required_argument, NULL, 12},
{NULL, 0, NULL, 0}
};
@@ -604,6 +608,10 @@ main(int argc, char **argv)
optarg);
break;
+ case 12: /* table data WHERE clause */
+ simple_string_list_append(&tabledata_where_patterns, optarg);
+ break;
+
default:
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit_nicely(1);
@@ -806,17 +814,26 @@ main(int argc, char **argv)
{
expand_table_name_patterns(fout, &table_include_patterns,
&table_include_oids,
- strict_names);
+ strict_names, false);
if (table_include_oids.head == NULL)
fatal("no matching tables were found");
}
+
+ if (tabledata_where_patterns.head != NULL)
+ {
+ expand_table_name_patterns(fout, &tabledata_where_patterns,
+ &tabledata_where_oids,
+ true, true);
+ if (tabledata_where_oids.head == NULL)
+ fatal("no matching table was found");
+ }
expand_table_name_patterns(fout, &table_exclude_patterns,
&table_exclude_oids,
- false);
+ false, false);
expand_table_name_patterns(fout, &tabledata_exclude_patterns,
&tabledata_exclude_oids,
- false);
+ false, false);
expand_foreign_server_name_patterns(fout, &foreign_servers_include_patterns,
&foreign_servers_include_oids);
@@ -1047,6 +1064,7 @@ help(const char *progname)
printf(_(" --use-set-session-authorization\n"
" use SET SESSION AUTHORIZATION commands instead of\n"
" ALTER OWNER commands to set ownership\n"));
+ printf(_(" --where=TABLE:WHERE_CLAUSE only dump selected rows for the given table\n"));
printf(_("\nConnection options:\n"));
printf(_(" -d, --dbname=DBNAME database to dump\n"));
@@ -1394,16 +1412,20 @@ expand_foreign_server_name_patterns(Archive *fout,
/*
* Find the OIDs of all tables matching the given list of patterns,
* and append them to the given OID list. See also expand_dbname_patterns()
- * in pg_dumpall.c
+ * in pg_dumpall.c .If match_data is set, then each pattern is first split on the
+ * ':' character, and the portion after the colon is appended to
+ * the SimpleOidList extra data.
*/
static void
expand_table_name_patterns(Archive *fout,
SimpleStringList *patterns, SimpleOidList *oids,
- bool strict_names)
+ bool strict_names, bool match_data)
{
PQExpBuffer query;
PGresult *res;
SimpleStringListCell *cell;
+ char *extra_data;
+ char *colon_char;
int i;
if (patterns->head == NULL)
@@ -1418,6 +1440,19 @@ expand_table_name_patterns(Archive *fout,
for (cell = patterns->head; cell; cell = cell->next)
{
+ /* When match_data is set, split the pattern on the first unquoted ':' character,
+ * and treat the second-half as extra data to append to the list.
+ */
+ extra_data = NULL;
+ if (match_data)
+ {
+ colon_char = (char*) findUnquotedChar(cell->val, ':');
+ if (colon_char)
+ {
+ *colon_char = '\0';
+ extra_data = colon_char+1;
+ }
+ }
/*
* Query must remain ABSOLUTELY devoid of unqualified names. This
* would be unnecessary given a pg_table_is_visible() variant taking a
@@ -1444,9 +1479,12 @@ expand_table_name_patterns(Archive *fout,
if (strict_names && PQntuples(res) == 0)
fatal("no matching tables were found for pattern \"%s\"", cell->val);
+ if (extra_data && PQntuples(res) != 1)
+ fatal("multiple matching tables \"%s\" on where clause", cell->val);
+
for (i = 0; i < PQntuples(res); i++)
{
- simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
+ simple_oid_list_append_data(oids, atooid(PQgetvalue(res, i, 0)), extra_data);
}
PQclear(res);
@@ -1873,6 +1911,7 @@ dumpTableData_copy(Archive *fout, void *dcontext)
*/
if (tdinfo->filtercond || tbinfo->relkind == RELKIND_FOREIGN_TABLE)
{
+ ArchiveHandle *AH = (ArchiveHandle *) fout;
/* Note: this syntax is only supported in 8.2 and up */
appendPQExpBufferStr(q, "COPY (SELECT ");
/* klugery to get rid of parens in column list */
@@ -1887,14 +1926,23 @@ dumpTableData_copy(Archive *fout, void *dcontext)
appendPQExpBuffer(q, "FROM %s %s) TO stdout;",
fmtQualifiedDumpable(tbinfo),
tdinfo->filtercond ? tdinfo->filtercond : "");
+
+ if (tbinfo->relkind != RELKIND_FOREIGN_TABLE)
+ {
+ res = PQexec(AH->connection, q->data);
+ if (PQresultStatus(res) != PGRES_COPY_OUT)
+ fatal("processing of table \"%s\" failed", fmtQualifiedDumpable(tbinfo));
+ }
+ else
+ res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
}
else
{
appendPQExpBuffer(q, "COPY %s %s TO stdout;",
fmtQualifiedDumpable(tbinfo),
column_list);
+ res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
}
- res = ExecuteSqlQuery(fout, q->data, PGRES_COPY_OUT);
PQclear(res);
destroyPQExpBuffer(clistBuf);
@@ -2013,9 +2061,19 @@ dumpTableData_insert(Archive *fout, void *dcontext)
"SELECT * FROM ONLY %s",
fmtQualifiedDumpable(tbinfo));
if (tdinfo->filtercond)
+ {
+ ArchiveHandle *AH = (ArchiveHandle *) fout;
+ PGresult *result;
+
appendPQExpBuffer(q, " %s", tdinfo->filtercond);
+ result = PQexec(AH->connection, q->data);
- ExecuteSqlStatement(fout, q->data);
+ if (PQresultStatus(result) != PGRES_COMMAND_OK)
+ fatal("processing of table \"%s\" failed", fmtQualifiedDumpable(tbinfo));
+ PQclear(result);
+ }
+ else
+ ExecuteSqlStatement(fout, q->data);
while (1)
{
@@ -2391,6 +2449,7 @@ static void
makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
{
TableDataInfo *tdinfo;
+ char *filter_clause;
/*
* Nothing to do if we already decided to dump the table. This will
@@ -2443,6 +2502,22 @@ makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo)
tdinfo->dobj.namespace = tbinfo->dobj.namespace;
tdinfo->tdtable = tbinfo;
tdinfo->filtercond = NULL; /* might get set later */
+
+ /*
+ * --where=: may be provided for this table.
+ * If provided, filter_clause will be something like "foo < 5", so wrap it in a WHERE clause.
+ */
+ filter_clause = NULL;
+ if (simple_oid_list_find_data(&tabledata_where_oids,
+ tbinfo->dobj.catId.oid,
+ (void**) &filter_clause))
+ {
+ if (filter_clause)
+ {
+ tdinfo->filtercond = psprintf("WHERE %s", filter_clause);
+ }
+ }
+
addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
tbinfo->dataObj = tdinfo;
diff --git a/src/fe_utils/simple_list.c b/src/fe_utils/simple_list.c
index 5294fba8bb..92b3162ab9 100644
--- a/src/fe_utils/simple_list.c
+++ b/src/fe_utils/simple_list.c
@@ -24,12 +24,31 @@
*/
void
simple_oid_list_append(SimpleOidList *list, Oid val)
+{
+ simple_oid_list_append_data(list, val, NULL);
+}
+
+/*
+ * Is OID present in the list?
+ */
+bool
+simple_oid_list_member(SimpleOidList *list, Oid val)
+{
+ return simple_oid_list_find_data(list, val, NULL);
+}
+
+/*
+ * Append an OID to the list, along with extra pointer-sized data.
+ */
+void
+simple_oid_list_append_data(SimpleOidList *list, Oid val, void *extra_data)
{
SimpleOidListCell *cell;
cell = (SimpleOidListCell *) pg_malloc(sizeof(SimpleOidListCell));
cell->next = NULL;
cell->val = val;
+ cell->extra_data = extra_data;
if (list->tail)
list->tail->next = cell;
@@ -40,16 +59,22 @@ simple_oid_list_append(SimpleOidList *list, Oid val)
/*
* Is OID present in the list?
+ * If so, return true, and provide pointer-sized data by setting result of extra_data parameter.
+ * If not, return false.
*/
bool
-simple_oid_list_member(SimpleOidList *list, Oid val)
+simple_oid_list_find_data(SimpleOidList *list, Oid val, void **extra_data)
{
SimpleOidListCell *cell;
for (cell = list->head; cell; cell = cell->next)
{
if (cell->val == val)
+ {
+ if (extra_data)
+ *extra_data = cell->extra_data;
return true;
+ }
}
return false;
}
diff --git a/src/fe_utils/string_utils.c b/src/fe_utils/string_utils.c
index b4bba26934..d4d5f165c9 100644
--- a/src/fe_utils/string_utils.c
+++ b/src/fe_utils/string_utils.c
@@ -1027,3 +1027,34 @@ processSQLNamePattern(PGconn *conn, PQExpBuffer buf, const char *pattern,
return added_clause;
#undef WHEREAND
}
+
+/*
+ * findUnquotedChar
+ *
+ * Scan a string and return a pointer to the first character that
+ * matches a given character, which isn't in double-quotes.
+ *
+ * For example, searching for a colon inside of:
+ * 'hello"this:is:in:quotes"but:this:is:not'
+ * would return a pointer to this ^ colon. (the one after 'but')
+ *
+ * Returns NULL if not found.
+ *
+ * str: string to scan.
+ * find_char: character to find.
+ */
+const char *
+findUnquotedChar(const char *str, char find_char)
+{
+ const char *p = str;
+ bool inquotes = false;
+
+ while (*p++)
+ {
+ if (*p == '"')
+ inquotes = !inquotes;
+ else if ((*p == find_char) && !inquotes)
+ return p;
+ }
+ return NULL;
+}
diff --git a/src/include/fe_utils/simple_list.h b/src/include/fe_utils/simple_list.h
index db04e677f2..b300c02874 100644
--- a/src/include/fe_utils/simple_list.h
+++ b/src/include/fe_utils/simple_list.h
@@ -21,6 +21,7 @@ typedef struct SimpleOidListCell
{
struct SimpleOidListCell *next;
Oid val;
+ void *extra_data;
} SimpleOidListCell;
typedef struct SimpleOidList
@@ -59,6 +60,9 @@ extern void simple_oid_list_append(SimpleOidList *list, Oid val);
extern bool simple_oid_list_member(SimpleOidList *list, Oid val);
extern void simple_oid_list_destroy(SimpleOidList *list);
+extern void simple_oid_list_append_data(SimpleOidList *list, Oid val, void *extra_data);
+extern bool simple_oid_list_find_data(SimpleOidList *list, Oid val, void **extra_data);
+
extern void simple_string_list_append(SimpleStringList *list, const char *val);
extern bool simple_string_list_member(SimpleStringList *list, const char *val);
extern void simple_string_list_destroy(SimpleStringList *list);
diff --git a/src/include/fe_utils/string_utils.h b/src/include/fe_utils/string_utils.h
index 5924d3248a..3aee484650 100644
--- a/src/include/fe_utils/string_utils.h
+++ b/src/include/fe_utils/string_utils.h
@@ -56,4 +56,6 @@ extern bool processSQLNamePattern(PGconn *conn, PQExpBuffer buf,
const char *schemavar, const char *namevar,
const char *altnamevar, const char *visibilityrule);
+extern const char *findUnquotedChar(const char *str, char find_char);
+
#endif /* STRING_UTILS_H */