Check existency of table/schema for -t/-n option (pg_dump/pg_restore)
authorTeodor Sigaev
Mon, 14 Sep 2015 13:19:49 +0000 (16:19 +0300)
committerTeodor Sigaev
Mon, 14 Sep 2015 13:19:49 +0000 (16:19 +0300)
Patch provides command line option --strict-names which requires that at
least one table/schema should present for each -t/-n option.

Pavel Stehule 

doc/src/sgml/ref/pg_dump.sgml
doc/src/sgml/ref/pg_restore.sgml
src/bin/pg_dump/dumputils.c
src/bin/pg_dump/dumputils.h
src/bin/pg_dump/pg_backup.h
src/bin/pg_dump/pg_backup_archiver.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_restore.c

index 352d7e8a2b49d31c42f01909d30bac4829e2e78e..cfaec967403c3211df44af51b3aa505cc806156a 100644 (file)
@@ -544,6 +544,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.
+       
+      
+     
+
      
       
       
index 97e3420489d4e6302aae113b44038010855c2090..a5a939443d90ce0b7aa51c0b1ea74b4dffe80d39 100644 (file)
       
      
 
+     
+      
+      
+       
+        Require that each schema (-n / --schema) and table (-t / --table)
+        qualifier match at least one schema/table in the backup file.
+       
+      
+     
+
      
       
       
index d7506e119e256ec95a91bd90ab0b7759a543a0f4..52b2b98ed0ef74655b1df3746b110772f27b54cd 100644 (file)
@@ -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;
+}
index b1767468c13c7657db9198dc628550c75c51a61b..9f31bbc20af67598e38b96145a8f52410afc73ae 100644 (file)
@@ -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 */
index 80df8fcefd19011410775385572ce91eecf95784..71267498dd708c88a1612d9455169dfbc696fd30 100644 (file)
@@ -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;
index 8f1f6c1a24b09e12d72283dca363a1a74ddbf483..2344937851b4b5551afe15d4c8151c004b31cdea 100644 (file)
@@ -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)
 {
index 87dadbf08a596e445cf7464ab31621d3c7bba2d7..6eee00fd2d71cdbdbc224492164b9f2fc44f6381 100644 (file)
@@ -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, "No matching table(s) were found for pattern \"%s\"\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, "No matching table(s) were found for pattern \"%s\"\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);
 }
 
index b12948823c3795979b6fcb11997da6a28885135b..75c08b9830474f51504284336ef17fd1b9efa5fa 100644 (file)
@@ -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"));