PFA a patch to provide an --exclude-database option for pg_dumpall. The causes pg_dumpall to skip any database whose name matches the argument pattern. The option can be used multiple times.


Among other use cases, this is useful where a database name is visible but the database is not dumpable by the user. Examples of this occur in some managed Postgres services.


I will add this to the September CF.


cheers


andrew


--
Andrew Dunstan                https://www.2ndQuadrant.com
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index 94d76c3..41a7ed7 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -300,6 +300,28 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+
+     <varlistentry>
+      <term><option>--exclude-database=<replaceable class="parameter">dbname</replaceable></option></term>
+      <listitem>
+       <para>
+        Do not dump databases whose name matches
+        <replaceable class="parameter">dbname</replaceable>.
+        Multiple databases can be excluded by writing multiple
+        <option>--exclude-database</option> switches.  Also, the
+        <replaceable class="parameter">dbname</replaceable> parameter is
+        interpreted as a pattern according to the same rules used by
+        <application>psql</application>'s <literal>\d</literal>
+        commands (see <xref
+        linkend="app-psql-patterns" endterm="app-psql-patterns-title"/>),
+        so multiple databases can also be excluded by writing wildcard
+        characters in the pattern.  When using wildcards, be careful to
+        quote the pattern if needed to prevent the shell from expanding
+        the wildcards.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>--if-exists</option></term>
       <listitem>
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index eb29d31..43058e9 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -52,6 +52,9 @@ static PGconn *connectDatabase(const char *dbname, const char *connstr, const ch
 static char *constructConnStr(const char **keywords, const char **values);
 static PGresult *executeQuery(PGconn *conn, const char *query);
 static void executeCommand(PGconn *conn, const char *query);
+static bool database_excluded(char * datname);
+static void expand_dbname_patterns(PGconn *conn, SimpleStringList *patterns,
+								   SimpleStringList *names);
 
 static char pg_dump_bin[MAXPGPATH];
 static const char *progname;
@@ -87,6 +90,9 @@ static char role_catalog[10];
 static FILE *OPF;
 static char *filename = NULL;
 
+static SimpleStringList database_exclude_patterns = {NULL, NULL};
+static SimpleStringList database_exclude_names = {NULL, NULL};
+
 #define exit_nicely(code) exit(code)
 
 int
@@ -123,6 +129,7 @@ main(int argc, char *argv[])
 		{"column-inserts", no_argument, &column_inserts, 1},
 		{"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1},
 		{"disable-triggers", no_argument, &disable_triggers, 1},
+		{"exclude-database",required_argument, NULL, 5},
 		{"if-exists", no_argument, &if_exists, 1},
 		{"inserts", no_argument, &inserts, 1},
 		{"lock-wait-timeout", required_argument, NULL, 2},
@@ -318,6 +325,10 @@ main(int argc, char *argv[])
 				appendPQExpBufferStr(pgdumpopts, " --no-sync");
 				break;
 
+			case 5:
+				simple_string_list_append(&database_exclude_patterns, optarg);
+				break;
+
 			default:
 				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
 				exit_nicely(1);
@@ -449,6 +460,12 @@ main(int argc, char *argv[])
 	}
 
 	/*
+	 * Get a list of database names that match the exclude patterns
+	 */
+	expand_dbname_patterns(conn, &database_exclude_patterns,
+						   &database_exclude_names);
+
+	/*
 	 * Open the output file if required, otherwise use stdout
 	 */
 	if (filename)
@@ -614,6 +631,7 @@ help(void)
 	printf(_("  --column-inserts             dump data as INSERT commands with column names\n"));
 	printf(_("  --disable-dollar-quoting     disable dollar quoting, use SQL standard quoting\n"));
 	printf(_("  --disable-triggers           disable triggers during data-only restore\n"));
+	printf(_("  --exclude-database=PATTERN   exclude databases whose name matches PATTERN\n"));
 	printf(_("  --if-exists                  use IF EXISTS when dropping objects\n"));
 	printf(_("  --inserts                    dump data as INSERT commands, rather than COPY\n"));
 	printf(_("  --load-via-partition-root    load partitions via the root table\n"));
@@ -1351,6 +1369,66 @@ dumpUserConfig(PGconn *conn, const char *username)
 	destroyPQExpBuffer(buf);
 }
 
+/*
+ * Find a list of database names that match the given patterns.
+ * This is similar to code in pg_dump.c for handling matching table names.
+ */
+static void
+expand_dbname_patterns(PGconn *conn,
+					   SimpleStringList *patterns,
+					   SimpleStringList *names)
+{
+	PQExpBuffer query;
+	PGresult   *res;
+	SimpleStringListCell *cell;
+	int			i;
+
+	if (patterns->head == NULL)
+		return;					/* nothing to do */
+
+	query = createPQExpBuffer();
+
+	/*
+	 * The loop below runs multiple SELECTs. It might sometimes result in
+	 * duplicate entries in the names list, but we don't care, since a
+	 * database is going to be excluded if it matches any entry on the list.
+	 */
+
+	for (cell = patterns->head; cell; cell = cell->next)
+	{
+		appendPQExpBuffer(query,
+						  "SELECT datname FROM pg_catalog.pg_database n\n");
+		processSQLNamePattern(conn, query, cell->val, false,
+							  false, NULL, "datname", NULL, NULL);
+
+		res = executeQuery(conn, query->data);
+		for (i = 0; i < PQntuples(res); i++)
+		{
+			simple_string_list_append(names, PQgetvalue(res, i, 0));
+		}
+
+		PQclear(res);
+		resetPQExpBuffer(query);
+	}
+
+	destroyPQExpBuffer(query);
+}
+
+/*
+ * Is the datname on the list of excluded databases?
+ */
+static bool
+database_excluded(char * datname)
+{
+	SimpleStringListCell *cell;
+
+	for (cell = (&database_exclude_names)->head; cell; cell = cell->next)
+	{
+		if (strcmp(datname, cell->val) == 0)
+			return true;
+	}
+	return false;
+}
 
 /*
  * Dump contents of databases.
@@ -1388,6 +1466,15 @@ dumpDatabases(PGconn *conn)
 		if (strcmp(dbname, "template0") == 0)
 			continue;
 
+		/* Skip any explicitly excluded database */
+		if (database_excluded(dbname))
+		{
+			if (verbose)
+				fprintf(stderr, _("%s: excluding database \"%s\"...\n"),
+						progname, dbname);
+			continue;
+		}
+
 		if (verbose)
 			fprintf(stderr, _("%s: dumping database \"%s\"...\n"), progname, dbname);
 
diff --git a/src/bin/pg_dump/t/001_basic.pl b/src/bin/pg_dump/t/001_basic.pl
index 17edf44..65c6f9f 100644
--- a/src/bin/pg_dump/t/001_basic.pl
+++ b/src/bin/pg_dump/t/001_basic.pl
@@ -4,7 +4,7 @@ use warnings;
 use Config;
 use PostgresNode;
 use TestLib;
-use Test::More tests => 70;
+use Test::More tests => 72;
 
 my $tempdir       = TestLib::tempdir;
 my $tempdir_short = TestLib::tempdir_short;
@@ -150,3 +150,8 @@ command_fails_like(
 	[ 'pg_dumpall', '--if-exists' ],
 	qr/\Qpg_dumpall: option --if-exists requires option -c\/--clean\E/,
 	'pg_dumpall: option --if-exists requires option -c/--clean');
+
+command_fails_like(
+	[ 'pg_dumpall', '--exclude-database' ],
+	qr/\Qpg_dumpall: option '--exclude-database' requires an argument\E/,
+	"pg_dumpall: option '--exclude-database' requires an argument");

Reply via email to