diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml index 7467e86..eaa006d 100644 --- a/doc/src/sgml/ref/pg_dump.sgml +++ b/doc/src/sgml/ref/pg_dump.sgml @@ -545,6 +545,23 @@ PostgreSQL documentation + + + + 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. + + + 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. + + + + + diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml index 97e3420..a5a9394 100644 --- a/doc/src/sgml/ref/pg_restore.sgml +++ b/doc/src/sgml/ref/pg_restore.sgml @@ -432,6 +432,16 @@ + + + + Require that each schema (-n / --schema) and table (-t / --table) + qualifier match at least one schema/table in the backup file. + + + + + diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c index d7506e1..52b2b98 100644 --- a/src/bin/pg_dump/dumputils.c +++ b/src/bin/pg_dump/dumputils.c @@ -1220,6 +1220,7 @@ simple_string_list_append(SimpleStringList *list, const char *val) pg_malloc(offsetof(SimpleStringListCell, val) +strlen(val) + 1); cell->next = NULL; + cell->touched = false; strcpy(cell->val, val); if (list->tail) @@ -1237,7 +1238,23 @@ simple_string_list_member(SimpleStringList *list, const char *val) for (cell = list->head; cell; cell = cell->next) { if (strcmp(cell->val, val) == 0) + { + cell->touched = true; return true; + } } 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; +} diff --git a/src/bin/pg_dump/dumputils.h b/src/bin/pg_dump/dumputils.h index b176746..9f31bbc 100644 --- a/src/bin/pg_dump/dumputils.h +++ b/src/bin/pg_dump/dumputils.h @@ -38,6 +38,8 @@ typedef struct SimpleOidList typedef struct SimpleStringListCell { struct SimpleStringListCell *next; + bool touched; /* true, when this string was searched + and touched */ char val[FLEXIBLE_ARRAY_MEMBER]; /* null-terminated string here */ } SimpleStringListCell; @@ -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 bool simple_string_list_member(SimpleStringList *list, const char *val); +extern const char *simple_string_list_not_touched(SimpleStringList *list); + #endif /* DUMPUTILS_H */ diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index 80df8fc..7126749 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -104,6 +104,7 @@ typedef struct _restoreOptions int column_inserts; int if_exists; int no_security_labels; /* Skip security label entries */ + int strict_names; const char *filename; int dataOnly; diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index 8f1f6c1..2344937 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -108,6 +108,9 @@ static void reduce_dependencies(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 StrictNamesCheck(RestoreOptions *ropt); + + /* * Allocate a new DumpOptions block containing all default values. */ @@ -284,6 +287,10 @@ SetArchiveRestoreOptions(Archive *AHX, RestoreOptions *ropt) te->reqs = _tocEntryRequired(te, curSection, ropt); } + + /* Enforce strict names checking */ + if (ropt->strict_names) + StrictNamesCheck(ropt); } /* Public */ @@ -1104,6 +1111,10 @@ PrintTOCSummary(Archive *AHX, RestoreOptions *ropt) } } + /* Enforce strict names checking */ + if (ropt->strict_names) + StrictNamesCheck(ropt); + if (ropt->filename) RestoreOutput(AH, sav); } @@ -2612,6 +2623,49 @@ processStdStringsEntry(ArchiveHandle *AH, TocEntry *te) 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 _tocEntryRequired(TocEntry *te, teSection curSection, RestoreOptions *ropt) { diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 87dadbf..f98dde1 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -97,6 +97,9 @@ static const char *username_subquery; /* obsolete as of 7.3: */ 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 * @@ -131,10 +134,12 @@ static void setup_connection(Archive *AH, DumpOptions *dopt, static ArchiveFormat parseArchiveFormat(const char *format, ArchiveMode *mode); static void expand_schema_name_patterns(Archive *fout, SimpleStringList *patterns, - SimpleOidList *oids); + SimpleOidList *oids, + bool strict_names); static void expand_table_name_patterns(Archive *fout, SimpleStringList *patterns, - SimpleOidList *oids); + SimpleOidList *oids, + bool strict_names); static NamespaceInfo *findNamespace(Archive *fout, Oid nsoid, Oid objoid); static void dumpTableData(Archive *fout, DumpOptions *dopt, TableDataInfo *tdinfo); static void refreshMatViewData(Archive *fout, TableDataInfo *tdinfo); @@ -333,6 +338,7 @@ main(int argc, char **argv) {"section", required_argument, NULL, 5}, {"serializable-deferrable", no_argument, &dopt.serializable_deferrable, 1}, {"snapshot", required_argument, NULL, 6}, + {"strict-names", no_argument, &strict_names, 1}, {"use-set-session-authorization", no_argument, &dopt.use_setsessauth, 1}, {"no-security-labels", no_argument, &dopt.no_security_labels, 1}, {"no-synchronized-snapshots", no_argument, &dopt.no_synchronized_snapshots, 1}, @@ -690,27 +696,32 @@ main(int argc, char **argv) if (schema_include_patterns.head != NULL) { expand_schema_name_patterns(fout, &schema_include_patterns, - &schema_include_oids); + &schema_include_oids, + strict_names); if (schema_include_oids.head == NULL) exit_horribly(NULL, "No matching schemas were found\n"); } expand_schema_name_patterns(fout, &schema_exclude_patterns, - &schema_exclude_oids); + &schema_exclude_oids, + false); /* non-matching exclusion patterns aren't an error */ /* Expand table selection patterns into OID lists */ if (table_include_patterns.head != NULL) { expand_table_name_patterns(fout, &table_include_patterns, - &table_include_oids); + &table_include_oids, + strict_names); if (table_include_oids.head == NULL) exit_horribly(NULL, "No matching tables were found\n"); } expand_table_name_patterns(fout, &table_exclude_patterns, - &table_exclude_oids); + &table_exclude_oids, + false); expand_table_name_patterns(fout, &tabledata_exclude_patterns, - &tabledata_exclude_oids); + &tabledata_exclude_oids, + false); /* non-matching exclusion patterns aren't an error */ @@ -905,6 +916,8 @@ help(const char *progname) 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 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" " use SET SESSION AUTHORIZATION commands instead of\n" " ALTER OWNER commands to set ownership\n")); @@ -1135,7 +1148,8 @@ parseArchiveFormat(const char *format, ArchiveMode *mode) static void expand_schema_name_patterns(Archive *fout, SimpleStringList *patterns, - SimpleOidList *oids) + SimpleOidList *oids, + bool strict_names) { PQExpBuffer query; PGresult *res; @@ -1151,38 +1165,41 @@ expand_schema_name_patterns(Archive *fout, query = createPQExpBuffer(); /* - * We use UNION ALL rather than UNION; this might sometimes result in - * duplicate entries in the OID list, but we don't care. + * This might sometimes result in duplicate entries in the OID list, + * but we don't care. */ for (cell = patterns->head; cell; cell = cell->next) { - if (cell != patterns->head) - appendPQExpBufferStr(query, "UNION ALL\n"); appendPQExpBuffer(query, "SELECT oid FROM pg_catalog.pg_namespace n\n"); processSQLNamePattern(GetConnection(fout), query, cell->val, false, 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, "Schema \"%s\" not found.\n", cell->val); - for (i = 0; i < PQntuples(res); i++) - { - simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0))); + for (i = 0; i < PQntuples(res); i++) + { + simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0))); + } + + PQclear(res); + resetPQExpBuffer(query); } - PQclear(res); destroyPQExpBuffer(query); } /* * 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 expand_table_name_patterns(Archive *fout, - SimpleStringList *patterns, SimpleOidList *oids) + SimpleStringList *patterns, SimpleOidList *oids, + bool strict_names) { PQExpBuffer query; PGresult *res; @@ -1195,14 +1212,12 @@ expand_table_name_patterns(Archive *fout, query = createPQExpBuffer(); /* - * We use UNION ALL rather than UNION; this might sometimes result in - * duplicate entries in the OID list, but we don't care. + * this might sometimes result in duplicate entries in the OID list, + * but we don't care. */ for (cell = patterns->head; cell; cell = cell->next) { - if (cell != patterns->head) - appendPQExpBufferStr(query, "UNION ALL\n"); appendPQExpBuffer(query, "SELECT c.oid" "\nFROM pg_catalog.pg_class c" @@ -1213,16 +1228,20 @@ expand_table_name_patterns(Archive *fout, processSQLNamePattern(GetConnection(fout), query, cell->val, true, false, "n.nspname", "c.relname", NULL, "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, "Table \"%s\" not found.\n", cell->val); - for (i = 0; i < PQntuples(res); i++) - { - simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0))); + for (i = 0; i < PQntuples(res); i++) + { + simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0))); + } + + PQclear(res); + resetPQExpBuffer(query); } - PQclear(res); destroyPQExpBuffer(query); } diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c index b129488..75c08b9 100644 --- a/src/bin/pg_dump/pg_restore.c +++ b/src/bin/pg_dump/pg_restore.c @@ -77,6 +77,7 @@ main(int argc, char **argv) static int outputNoTablespaces = 0; static int use_setsessauth = 0; static int no_security_labels = 0; + static int strict_names = 0; struct option cmdopts[] = { {"clean", 0, NULL, 'c'}, @@ -118,6 +119,7 @@ main(int argc, char **argv) {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, {"role", required_argument, NULL, 2}, {"section", required_argument, NULL, 3}, + {"strict-names", no_argument, &strict_names, 1}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, {"no-security-labels", no_argument, &no_security_labels, 1}, @@ -345,6 +347,7 @@ main(int argc, char **argv) exit_nicely(1); } opts->if_exists = if_exists; + opts->strict_names = strict_names; if (opts->formatName) { @@ -467,6 +470,8 @@ usage(const char *progname) printf(_(" --no-security-labels do not restore security labels\n")); printf(_(" --no-tablespaces do not restore tablespace assignments\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" " use SET SESSION AUTHORIZATION commands instead of\n" " ALTER OWNER commands to set ownership\n"));