Commit d0242602 authored by Teodor Sigaev's avatar Teodor Sigaev

Check existency of table/schema for -t/-n option (pg_dump/pg_restore)

Patch provides command line option --strict-names which requires that at
least one table/schema should present for each -t/-n option.

Pavel Stehule <pavel.stehule@gmail.com>
parent b5217d69
...@@ -544,6 +544,23 @@ PostgreSQL documentation ...@@ -544,6 +544,23 @@ PostgreSQL documentation
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--strict-names</></term>
<listitem>
<para>
Require that each schema (-n / --schema) and table (-t / --table)
qualifier match at least one schema/table in the database to be dumped.
Note that if none of the schema/table qualifiers find matches pg_dump
will generate an error even without --strict-names.
</para>
<para>
This option has no effect on -N/--exclude-schema, -T/--exclude_table
or --exclude-table-date. An exclude pattern failing to match
any objects is not considered an error.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>-T <replaceable class="parameter">table</replaceable></option></term> <term><option>-T <replaceable class="parameter">table</replaceable></option></term>
<term><option>--exclude-table=<replaceable class="parameter">table</replaceable></option></term> <term><option>--exclude-table=<replaceable class="parameter">table</replaceable></option></term>
......
...@@ -431,6 +431,16 @@ ...@@ -431,6 +431,16 @@
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--strict-names</></term>
<listitem>
<para>
Require that each schema (-n / --schema) and table (-t / --table)
qualifier match at least one schema/table in the backup file.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>-T <replaceable class="parameter">trigger</replaceable></option></term> <term><option>-T <replaceable class="parameter">trigger</replaceable></option></term>
<term><option>--trigger=<replaceable class="parameter">trigger</replaceable></option></term> <term><option>--trigger=<replaceable class="parameter">trigger</replaceable></option></term>
......
...@@ -1220,6 +1220,7 @@ simple_string_list_append(SimpleStringList *list, const char *val) ...@@ -1220,6 +1220,7 @@ simple_string_list_append(SimpleStringList *list, const char *val)
pg_malloc(offsetof(SimpleStringListCell, val) +strlen(val) + 1); pg_malloc(offsetof(SimpleStringListCell, val) +strlen(val) + 1);
cell->next = NULL; cell->next = NULL;
cell->touched = false;
strcpy(cell->val, val); strcpy(cell->val, val);
if (list->tail) if (list->tail)
...@@ -1237,7 +1238,23 @@ simple_string_list_member(SimpleStringList *list, const char *val) ...@@ -1237,7 +1238,23 @@ simple_string_list_member(SimpleStringList *list, const char *val)
for (cell = list->head; cell; cell = cell->next) for (cell = list->head; cell; cell = cell->next)
{ {
if (strcmp(cell->val, val) == 0) if (strcmp(cell->val, val) == 0)
{
cell->touched = true;
return true; return true;
}
} }
return false; return false;
} }
const char *
simple_string_list_not_touched(SimpleStringList *list)
{
SimpleStringListCell *cell;
for (cell = list->head; cell; cell = cell->next)
{
if (!cell->touched)
return cell->val;
}
return NULL;
}
...@@ -38,6 +38,8 @@ typedef struct SimpleOidList ...@@ -38,6 +38,8 @@ typedef struct SimpleOidList
typedef struct SimpleStringListCell typedef struct SimpleStringListCell
{ {
struct SimpleStringListCell *next; struct SimpleStringListCell *next;
bool touched; /* true, when this string was searched
and touched */
char val[FLEXIBLE_ARRAY_MEMBER]; /* null-terminated string here */ char val[FLEXIBLE_ARRAY_MEMBER]; /* null-terminated string here */
} SimpleStringListCell; } SimpleStringListCell;
...@@ -103,5 +105,7 @@ extern void set_dump_section(const char *arg, int *dumpSections); ...@@ -103,5 +105,7 @@ extern void set_dump_section(const char *arg, int *dumpSections);
extern void simple_string_list_append(SimpleStringList *list, const char *val); extern void simple_string_list_append(SimpleStringList *list, const char *val);
extern bool simple_string_list_member(SimpleStringList *list, const char *val); extern bool simple_string_list_member(SimpleStringList *list, const char *val);
extern const char *simple_string_list_not_touched(SimpleStringList *list);
#endif /* DUMPUTILS_H */ #endif /* DUMPUTILS_H */
...@@ -104,6 +104,7 @@ typedef struct _restoreOptions ...@@ -104,6 +104,7 @@ typedef struct _restoreOptions
int column_inserts; int column_inserts;
int if_exists; int if_exists;
int no_security_labels; /* Skip security label entries */ int no_security_labels; /* Skip security label entries */
int strict_names;
const char *filename; const char *filename;
int dataOnly; int dataOnly;
......
...@@ -108,6 +108,9 @@ static void reduce_dependencies(ArchiveHandle *AH, TocEntry *te, ...@@ -108,6 +108,9 @@ static void reduce_dependencies(ArchiveHandle *AH, TocEntry *te,
static void mark_create_done(ArchiveHandle *AH, TocEntry *te); static void mark_create_done(ArchiveHandle *AH, TocEntry *te);
static void inhibit_data_for_failed_table(ArchiveHandle *AH, TocEntry *te); static void inhibit_data_for_failed_table(ArchiveHandle *AH, TocEntry *te);
static void StrictNamesCheck(RestoreOptions *ropt);
/* /*
* Allocate a new DumpOptions block containing all default values. * Allocate a new DumpOptions block containing all default values.
*/ */
...@@ -284,6 +287,10 @@ SetArchiveRestoreOptions(Archive *AHX, RestoreOptions *ropt) ...@@ -284,6 +287,10 @@ SetArchiveRestoreOptions(Archive *AHX, RestoreOptions *ropt)
te->reqs = _tocEntryRequired(te, curSection, ropt); te->reqs = _tocEntryRequired(te, curSection, ropt);
} }
/* Enforce strict names checking */
if (ropt->strict_names)
StrictNamesCheck(ropt);
} }
/* Public */ /* Public */
...@@ -1104,6 +1111,10 @@ PrintTOCSummary(Archive *AHX, RestoreOptions *ropt) ...@@ -1104,6 +1111,10 @@ PrintTOCSummary(Archive *AHX, RestoreOptions *ropt)
} }
} }
/* Enforce strict names checking */
if (ropt->strict_names)
StrictNamesCheck(ropt);
if (ropt->filename) if (ropt->filename)
RestoreOutput(AH, sav); RestoreOutput(AH, sav);
} }
...@@ -2612,6 +2623,49 @@ processStdStringsEntry(ArchiveHandle *AH, TocEntry *te) ...@@ -2612,6 +2623,49 @@ processStdStringsEntry(ArchiveHandle *AH, TocEntry *te)
te->defn); te->defn);
} }
static void
StrictNamesCheck(RestoreOptions *ropt)
{
const char *missing_name;
Assert(ropt->strict_names);
if (ropt->schemaNames.head != NULL)
{
missing_name = simple_string_list_not_touched(&ropt->schemaNames);
if (missing_name != NULL)
exit_horribly(modulename, "Schema \"%s\" not found.\n", missing_name);
}
if (ropt->tableNames.head != NULL)
{
missing_name = simple_string_list_not_touched(&ropt->tableNames);
if (missing_name != NULL)
exit_horribly(modulename, "Table \"%s\" not found.\n", missing_name);
}
if (ropt->indexNames.head != NULL)
{
missing_name = simple_string_list_not_touched(&ropt->indexNames);
if (missing_name != NULL)
exit_horribly(modulename, "Index \"%s\" not found.\n", missing_name);
}
if (ropt->functionNames.head != NULL)
{
missing_name = simple_string_list_not_touched(&ropt->functionNames);
if (missing_name != NULL)
exit_horribly(modulename, "Function \"%s\" not found.\n", missing_name);
}
if (ropt->triggerNames.head != NULL)
{
missing_name = simple_string_list_not_touched(&ropt->triggerNames);
if (missing_name != NULL)
exit_horribly(modulename, "Trigger \"%s\" not found.\n", missing_name);
}
}
static teReqs static teReqs
_tocEntryRequired(TocEntry *te, teSection curSection, RestoreOptions *ropt) _tocEntryRequired(TocEntry *te, teSection curSection, RestoreOptions *ropt)
{ {
......
...@@ -97,6 +97,9 @@ static const char *username_subquery; ...@@ -97,6 +97,9 @@ static const char *username_subquery;
/* obsolete as of 7.3: */ /* obsolete as of 7.3: */
static Oid g_last_builtin_oid; /* value of the last builtin oid */ static Oid g_last_builtin_oid; /* value of the last builtin oid */
/* The specified names/patterns should to match at least one entity */
static int strict_names = 0;
/* /*
* Object inclusion/exclusion lists * Object inclusion/exclusion lists
* *
...@@ -131,10 +134,12 @@ static void setup_connection(Archive *AH, DumpOptions *dopt, ...@@ -131,10 +134,12 @@ static void setup_connection(Archive *AH, DumpOptions *dopt,
static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode); static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode);
static void expand_schema_name_patterns(Archive *fout, static void expand_schema_name_patterns(Archive *fout,
SimpleStringList *patterns, SimpleStringList *patterns,
SimpleOidList *oids); SimpleOidList *oids,
bool strict_names);
static void expand_table_name_patterns(Archive *fout, static void expand_table_name_patterns(Archive *fout,
SimpleStringList *patterns, SimpleStringList *patterns,
SimpleOidList *oids); SimpleOidList *oids,
bool strict_names);
static NamespaceInfo *findNamespace(Archive *fout, Oid nsoid, Oid objoid); static NamespaceInfo *findNamespace(Archive *fout, Oid nsoid, Oid objoid);
static void dumpTableData(Archive *fout, DumpOptions *dopt, TableDataInfo *tdinfo); static void dumpTableData(Archive *fout, DumpOptions *dopt, TableDataInfo *tdinfo);
static void refreshMatViewData(Archive *fout, TableDataInfo *tdinfo); static void refreshMatViewData(Archive *fout, TableDataInfo *tdinfo);
...@@ -333,6 +338,7 @@ main(int argc, char **argv) ...@@ -333,6 +338,7 @@ main(int argc, char **argv)
{"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},
{"snapshot", required_argument, NULL, 6}, {"snapshot", required_argument, NULL, 6},
{"strict-names", no_argument, &strict_names, 1},
{"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1}, {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1},
{"no-security-labels", no_argument, &dopt.no_security_labels, 1}, {"no-security-labels", no_argument, &dopt.no_security_labels, 1},
{"no-synchronized-snapshots", no_argument, &dopt.no_synchronized_snapshots, 1}, {"no-synchronized-snapshots", no_argument, &dopt.no_synchronized_snapshots, 1},
...@@ -690,27 +696,32 @@ main(int argc, char **argv) ...@@ -690,27 +696,32 @@ main(int argc, char **argv)
if (schema_include_patterns.head != NULL) if (schema_include_patterns.head != NULL)
{ {
expand_schema_name_patterns(fout, &schema_include_patterns, expand_schema_name_patterns(fout, &schema_include_patterns,
&schema_include_oids); &schema_include_oids,
strict_names);
if (schema_include_oids.head == NULL) if (schema_include_oids.head == NULL)
exit_horribly(NULL, "No matching schemas were found\n"); exit_horribly(NULL, "No matching schemas were found\n");
} }
expand_schema_name_patterns(fout, &schema_exclude_patterns, expand_schema_name_patterns(fout, &schema_exclude_patterns,
&schema_exclude_oids); &schema_exclude_oids,
false);
/* non-matching exclusion patterns aren't an error */ /* non-matching exclusion patterns aren't an error */
/* Expand table selection patterns into OID lists */ /* Expand table selection patterns into OID lists */
if (table_include_patterns.head != NULL) if (table_include_patterns.head != NULL)
{ {
expand_table_name_patterns(fout, &table_include_patterns, expand_table_name_patterns(fout, &table_include_patterns,
&table_include_oids); &table_include_oids,
strict_names);
if (table_include_oids.head == NULL) if (table_include_oids.head == NULL)
exit_horribly(NULL, "No matching tables were found\n"); exit_horribly(NULL, "No matching tables were found\n");
} }
expand_table_name_patterns(fout, &table_exclude_patterns, expand_table_name_patterns(fout, &table_exclude_patterns,
&table_exclude_oids); &table_exclude_oids,
false);
expand_table_name_patterns(fout, &tabledata_exclude_patterns, expand_table_name_patterns(fout, &tabledata_exclude_patterns,
&tabledata_exclude_oids); &tabledata_exclude_oids,
false);
/* non-matching exclusion patterns aren't an error */ /* non-matching exclusion patterns aren't an error */
...@@ -905,6 +916,8 @@ help(const char *progname) ...@@ -905,6 +916,8 @@ help(const char *progname)
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 synchronous snapshot for the dump\n")); printf(_(" --snapshot=SNAPSHOT use given synchronous snapshot for the dump\n"));
printf(_(" --strict-names require table and/or schema include patterns to\n"
" match at least one entity each\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"));
...@@ -1135,7 +1148,8 @@ parseArchiveFormat(const char *format, ArchiveMode *mode) ...@@ -1135,7 +1148,8 @@ parseArchiveFormat(const char *format, ArchiveMode *mode)
static void static void
expand_schema_name_patterns(Archive *fout, expand_schema_name_patterns(Archive *fout,
SimpleStringList *patterns, SimpleStringList *patterns,
SimpleOidList *oids) SimpleOidList *oids,
bool strict_names)
{ {
PQExpBuffer query; PQExpBuffer query;
PGresult *res; PGresult *res;
...@@ -1151,38 +1165,41 @@ expand_schema_name_patterns(Archive *fout, ...@@ -1151,38 +1165,41 @@ expand_schema_name_patterns(Archive *fout,
query = createPQExpBuffer(); query = createPQExpBuffer();
/* /*
* We use UNION ALL rather than UNION; this might sometimes result in * This might sometimes result in duplicate entries in the OID list,
* duplicate entries in the OID list, but we don't care. * but we don't care.
*/ */
for (cell = patterns->head; cell; cell = cell->next) for (cell = patterns->head; cell; cell = cell->next)
{ {
if (cell != patterns->head)
appendPQExpBufferStr(query, "UNION ALL\n");
appendPQExpBuffer(query, appendPQExpBuffer(query,
"SELECT oid FROM pg_catalog.pg_namespace n\n"); "SELECT oid FROM pg_catalog.pg_namespace n\n");
processSQLNamePattern(GetConnection(fout), query, cell->val, false, processSQLNamePattern(GetConnection(fout), query, cell->val, false,
false, NULL, "n.nspname", NULL, NULL); false, NULL, "n.nspname", NULL, NULL);
}
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
if (strict_names && PQntuples(res) == 0)
exit_horribly(NULL, "No matching table(s) were found for pattern \"%s\"\n", cell->val);
for (i = 0; i < PQntuples(res); i++) for (i = 0; i < PQntuples(res); i++)
{ {
simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0))); simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
}
PQclear(res);
resetPQExpBuffer(query);
} }
PQclear(res);
destroyPQExpBuffer(query); destroyPQExpBuffer(query);
} }
/* /*
* Find the OIDs of all tables matching the given list of patterns, * Find the OIDs of all tables matching the given list of patterns,
* and append them to the given OID list. * and append them to the given OID list.
*/ */
static void static void
expand_table_name_patterns(Archive *fout, expand_table_name_patterns(Archive *fout,
SimpleStringList *patterns, SimpleOidList *oids) SimpleStringList *patterns, SimpleOidList *oids,
bool strict_names)
{ {
PQExpBuffer query; PQExpBuffer query;
PGresult *res; PGresult *res;
...@@ -1195,14 +1212,12 @@ expand_table_name_patterns(Archive *fout, ...@@ -1195,14 +1212,12 @@ expand_table_name_patterns(Archive *fout,
query = createPQExpBuffer(); query = createPQExpBuffer();
/* /*
* We use UNION ALL rather than UNION; this might sometimes result in * this might sometimes result in duplicate entries in the OID list,
* duplicate entries in the OID list, but we don't care. * but we don't care.
*/ */
for (cell = patterns->head; cell; cell = cell->next) for (cell = patterns->head; cell; cell = cell->next)
{ {
if (cell != patterns->head)
appendPQExpBufferStr(query, "UNION ALL\n");
appendPQExpBuffer(query, appendPQExpBuffer(query,
"SELECT c.oid" "SELECT c.oid"
"\nFROM pg_catalog.pg_class c" "\nFROM pg_catalog.pg_class c"
...@@ -1213,16 +1228,20 @@ expand_table_name_patterns(Archive *fout, ...@@ -1213,16 +1228,20 @@ expand_table_name_patterns(Archive *fout,
processSQLNamePattern(GetConnection(fout), query, cell->val, true, processSQLNamePattern(GetConnection(fout), query, cell->val, true,
false, "n.nspname", "c.relname", NULL, false, "n.nspname", "c.relname", NULL,
"pg_catalog.pg_table_is_visible(c.oid)"); "pg_catalog.pg_table_is_visible(c.oid)");
}
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
if (strict_names && PQntuples(res) == 0)
exit_horribly(NULL, "No matching table(s) were found for pattern \"%s\"\n", cell->val);
for (i = 0; i < PQntuples(res); i++) for (i = 0; i < PQntuples(res); i++)
{ {
simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0))); simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
}
PQclear(res);
resetPQExpBuffer(query);
} }
PQclear(res);
destroyPQExpBuffer(query); destroyPQExpBuffer(query);
} }
......
...@@ -77,6 +77,7 @@ main(int argc, char **argv) ...@@ -77,6 +77,7 @@ main(int argc, char **argv)
static int outputNoTablespaces = 0; static int outputNoTablespaces = 0;
static int use_setsessauth = 0; static int use_setsessauth = 0;
static int no_security_labels = 0; static int no_security_labels = 0;
static int strict_names = 0;
struct option cmdopts[] = { struct option cmdopts[] = {
{"clean", 0, NULL, 'c'}, {"clean", 0, NULL, 'c'},
...@@ -118,6 +119,7 @@ main(int argc, char **argv) ...@@ -118,6 +119,7 @@ main(int argc, char **argv)
{"no-tablespaces", no_argument, &outputNoTablespaces, 1}, {"no-tablespaces", no_argument, &outputNoTablespaces, 1},
{"role", required_argument, NULL, 2}, {"role", required_argument, NULL, 2},
{"section", required_argument, NULL, 3}, {"section", required_argument, NULL, 3},
{"strict-names", no_argument, &strict_names, 1},
{"use-set-session-authorization", no_argument, &use_setsessauth, 1}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1},
{"no-security-labels", no_argument, &no_security_labels, 1}, {"no-security-labels", no_argument, &no_security_labels, 1},
...@@ -345,6 +347,7 @@ main(int argc, char **argv) ...@@ -345,6 +347,7 @@ main(int argc, char **argv)
exit_nicely(1); exit_nicely(1);
} }
opts->if_exists = if_exists; opts->if_exists = if_exists;
opts->strict_names = strict_names;
if (opts->formatName) if (opts->formatName)
{ {
...@@ -467,6 +470,8 @@ usage(const char *progname) ...@@ -467,6 +470,8 @@ usage(const char *progname)
printf(_(" --no-security-labels do not restore security labels\n")); printf(_(" --no-security-labels do not restore security labels\n"));
printf(_(" --no-tablespaces do not restore tablespace assignments\n")); printf(_(" --no-tablespaces do not restore tablespace assignments\n"));
printf(_(" --section=SECTION restore named section (pre-data, data, or post-data)\n")); printf(_(" --section=SECTION restore named section (pre-data, data, or post-data)\n"));
printf(_(" --strict-names require table and/or schema include patterns to\n"
" match at least one entity each\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"));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment