On Sat, 16 Mar 2024 at 17:36, Ayush Vatsa <ayushvatsa1...@gmail.com> wrote:
>
> Attached is the complete patch with all the required code changes.
> Looking forward to your review and feedback.
>

This looks good to me. I tested it and everything worked as expected.

I ran it through pgindent to fix some whitespace issues and added
another test for the filter option, based on the test case you added.

I'm marking this ready-for-commit (which I'll probably do myself in a
day or two, unless anyone else claims it first).

Regards,
Dean
From f757ebe748ab47d1e1ab40b343af2a43a9183287 Mon Sep 17 00:00:00 2001
From: Ayush Vatsa <ayuva...@amazon.com>
Date: Mon, 25 Dec 2023 14:46:05 +0530
Subject: [PATCH v3] Add support for --exclude-extension in pg_dump

When specified, extensions matching the given pattern are excluded
in dumps.
---
 doc/src/sgml/ref/pg_dump.sgml               | 34 ++++++--
 src/bin/pg_dump/pg_dump.c                   | 33 +++++++-
 src/test/modules/test_pg_dump/t/001_base.pl | 88 ++++++++++++++++++---
 3 files changed, 139 insertions(+), 16 deletions(-)

diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index 0caf56e0e0..8edf03a03d 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -256,6 +256,27 @@ PostgreSQL documentation
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>--exclude-extension=<replaceable class="parameter">pattern</replaceable></option></term>
+      <listitem>
+       <para>
+        Do not dump any extensions matching <replaceable
+        class="parameter">pattern</replaceable>.  The pattern is
+        interpreted according to the same rules as for <option>-e</option>.
+        <option>--exclude-extension</option> can be given more than once to exclude extensions
+        matching any of several patterns.
+       </para>
+
+       <para>
+        When both <option>-e</option> and <option>--exclude-extension</option> are given, the behavior
+        is to dump just the extensions that match at least one <option>-e</option>
+        switch but no <option>--exclude-extension</option> switches.  If <option>--exclude-extension</option>
+        appears without <option>-e</option>, then extensions matching <option>--exclude-extension</option> are
+        excluded from what is otherwise a normal dump.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-E <replaceable class="parameter">encoding</replaceable></option></term>
       <term><option>--encoding=<replaceable class="parameter">encoding</replaceable></option></term>
@@ -848,10 +869,11 @@ PostgreSQL documentation
         <option>--exclude-table-and-children</option> or
         <option>-T</option> for tables,
         <option>-n</option>/<option>--schema</option> for schemas,
-        <option>--include-foreign-data</option> for data on foreign servers and
+        <option>--include-foreign-data</option> for data on foreign servers,
         <option>--exclude-table-data</option>,
-        <option>--exclude-table-data-and-children</option> for table data,
-        <option>-e</option>/<option>--extension</option> for extensions.
+        <option>--exclude-table-data-and-children</option> for table data, and
+        <option>-e</option>/<option>--extension</option> or
+        <option>--exclude-extension</option> for extensions.
         To read from <literal>STDIN</literal>, use <filename>-</filename> as the
         filename.  The <option>--filter</option> option can be specified in
         conjunction with the above listed options for including or excluding
@@ -874,8 +896,7 @@ PostgreSQL documentation
          <listitem>
           <para>
            <literal>extension</literal>: extensions, works like the
-           <option>--extension</option> option. This keyword can only be
-           used with the <literal>include</literal> keyword.
+           <option>-e</option>/<option>--extension</option> option.
           </para>
          </listitem>
          <listitem>
@@ -1278,7 +1299,8 @@ PostgreSQL documentation
        </para>
        <para>
         This option has no effect
-        on <option>-N</option>/<option>--exclude-schema</option>,
+        on <option>--exclude-extension</option>,
+        <option>-N</option>/<option>--exclude-schema</option>,
         <option>-T</option>/<option>--exclude-table</option>,
         or <option>--exclude-table-data</option>.  An exclude pattern failing
         to match any objects is not considered an error.
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index a5149ca823..3ab7c6676a 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -136,6 +136,9 @@ static SimpleOidList foreign_servers_include_oids = {NULL, NULL};
 static SimpleStringList extension_include_patterns = {NULL, NULL};
 static SimpleOidList extension_include_oids = {NULL, NULL};
 
+static SimpleStringList extension_exclude_patterns = {NULL, NULL};
+static SimpleOidList extension_exclude_oids = {NULL, NULL};
+
 static const CatalogId nilCatalogId = {0, 0};
 
 /* override for standard extra_float_digits setting */
@@ -437,6 +440,7 @@ main(int argc, char **argv)
 		{"exclude-table-data-and-children", required_argument, NULL, 14},
 		{"sync-method", required_argument, NULL, 15},
 		{"filter", required_argument, NULL, 16},
+		{"exclude-extension", required_argument, NULL, 17},
 
 		{NULL, 0, NULL, 0}
 	};
@@ -672,6 +676,11 @@ main(int argc, char **argv)
 				read_dump_filters(optarg, &dopt);
 				break;
 
+			case 17:			/* exclude extension(s) */
+				simple_string_list_append(&extension_exclude_patterns,
+										  optarg);
+				break;
+
 			default:
 				/* getopt_long already emitted a complaint */
 				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
@@ -890,6 +899,10 @@ main(int argc, char **argv)
 		if (extension_include_oids.head == NULL)
 			pg_fatal("no matching extensions were found");
 	}
+	expand_extension_name_patterns(fout, &extension_exclude_patterns,
+								   &extension_exclude_oids,
+								   false);
+	/* non-matching exclusion patterns aren't an error */
 
 	/*
 	 * Dumping LOs is the default for dumps where an inclusion switch is not
@@ -1095,6 +1108,7 @@ help(const char *progname)
 	printf(_("  -c, --clean                  clean (drop) database objects before recreating\n"));
 	printf(_("  -C, --create                 include commands to create database in dump\n"));
 	printf(_("  -e, --extension=PATTERN      dump the specified extension(s) only\n"));
+	printf(_("  --exclude-extension=PATTERN  do NOT dump the specified extension(s)\n"));
 	printf(_("  -E, --encoding=ENCODING      dump the data in encoding ENCODING\n"));
 	printf(_("  -n, --schema=PATTERN         dump the specified schema(s) only\n"));
 	printf(_("  -N, --exclude-schema=PATTERN do NOT dump the specified schema(s)\n"));
@@ -2028,6 +2042,12 @@ selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
 			extinfo->dobj.dump = extinfo->dobj.dump_contains =
 				dopt->include_everything ?
 				DUMP_COMPONENT_ALL : DUMP_COMPONENT_NONE;
+
+		/* check that the extension is not explicitly excluded */
+		if (extinfo->dobj.dump &&
+			simple_oid_list_member(&extension_exclude_oids,
+								   extinfo->dobj.catId.oid))
+			extinfo->dobj.dump = extinfo->dobj.dump_contains = DUMP_COMPONENT_NONE;
 	}
 }
 
@@ -18265,6 +18285,15 @@ processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
 									curext->dobj.catId.oid))
 			continue;
 
+		/*
+		 * Check if this extension is listed as to exclude in the dump.  If
+		 * yes, any table data associated with it is discarded.
+		 */
+		if (extension_exclude_oids.head != NULL &&
+			simple_oid_list_member(&extension_exclude_oids,
+								   curext->dobj.catId.oid))
+			continue;
+
 		if (strlen(extconfig) != 0 || strlen(extcondition) != 0)
 		{
 			int			j;
@@ -18965,7 +18994,6 @@ read_dump_filters(const char *filename, DumpOptions *dopt)
 				case FILTER_OBJECT_TYPE_FUNCTION:
 				case FILTER_OBJECT_TYPE_INDEX:
 				case FILTER_OBJECT_TYPE_TRIGGER:
-				case FILTER_OBJECT_TYPE_EXTENSION:
 				case FILTER_OBJECT_TYPE_FOREIGN_DATA:
 					pg_log_filter_error(&fstate, _("%s filter for \"%s\" is not allowed"),
 										"exclude",
@@ -18973,6 +19001,9 @@ read_dump_filters(const char *filename, DumpOptions *dopt)
 					exit_nicely(1);
 					break;
 
+				case FILTER_OBJECT_TYPE_EXTENSION:
+					simple_string_list_append(&extension_exclude_patterns, objname);
+					break;
 				case FILTER_OBJECT_TYPE_TABLE_DATA:
 					simple_string_list_append(&tabledata_exclude_patterns,
 											  objname);
diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl
index b8c30c2387..4266f26c65 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -220,6 +220,19 @@ my %pgdump_runs = (
 			'--extension=test_pg_dump', 'postgres',
 		],
 	},
+	exclude_extension => {
+		dump_cmd => [
+			'pg_dump', '--no-sync', "--file=$tempdir/exclude_extension.sql",
+			'--exclude-extension=test_pg_dump', 'postgres',
+		],
+	},
+	exclude_extension_filter => {
+		dump_cmd => [
+			'pg_dump', '--no-sync',
+			"--file=$tempdir/exclude_extension_filter.sql",
+			"--filter=$tempdir/exclude_extension_filter.txt", 'postgres',
+		],
+	},
 
 	# plpgsql in the list blocks the dump of extension test_pg_dump
 	without_extension => {
@@ -299,6 +312,8 @@ my %full_runs = (
 	no_owner => 1,
 	privileged_internals => 1,
 	with_extension => 1,
+	exclude_extension => 1,
+	exclude_extension_filter => 1,
 	without_extension => 1);
 
 my %tests = (
@@ -325,7 +340,12 @@ my %tests = (
 			schema_only => 1,
 			section_pre_data => 1,
 		},
-		unlike => { binary_upgrade => 1, without_extension => 1 },
+		unlike => {
+			binary_upgrade => 1,
+			exclude_extension => 1,
+			exclude_extension_filter => 1,
+			without_extension => 1
+		},
 	},
 
 	'CREATE ROLE regress_dump_test_role' => {
@@ -434,7 +454,11 @@ my %tests = (
 			section_data => 1,
 			extension_schema => 1,
 		},
-		unlike => { without_extension => 1, },
+		unlike => {
+			exclude_extension => 1,
+			exclude_extension_filter => 1,
+			without_extension => 1,
+		},
 	},
 
 	'CREATE TABLE regress_pg_dump_table' => {
@@ -460,6 +484,8 @@ my %tests = (
 		unlike => {
 			binary_upgrade => 1,
 			exclude_table => 1,
+			exclude_extension => 1,
+			exclude_extension_filter => 1,
 			without_extension => 1,
 		},
 	},
@@ -483,7 +509,12 @@ my %tests = (
 			schema_only => 1,
 			section_pre_data => 1,
 		},
-		unlike => { no_privs => 1, without_extension => 1, },
+		unlike => {
+			no_privs => 1,
+			exclude_extension => 1,
+			exclude_extension_filter => 1,
+			without_extension => 1,
+		},
 	},
 
 	'REVOKE GRANT OPTION FOR UPDATE ON SEQUENCE wgo_then_regular' => {
@@ -500,7 +531,12 @@ my %tests = (
 			schema_only => 1,
 			section_pre_data => 1,
 		},
-		unlike => { no_privs => 1, without_extension => 1, },
+		unlike => {
+			no_privs => 1,
+			exclude_extension => 1,
+			exclude_extension_filter => 1,
+			without_extension => 1,
+		},
 	},
 
 	'CREATE ACCESS METHOD regress_test_am' => {
@@ -520,7 +556,11 @@ my %tests = (
 			schema_only => 1,
 			section_pre_data => 1,
 		},
-		unlike => { without_extension => 1, },
+		unlike => {
+			exclude_extension => 1,
+			exclude_extension_filter => 1,
+			without_extension => 1,
+		},
 	},
 
 	'GRANT SELECT regress_pg_dump_table_added pre-ALTER EXTENSION' => {
@@ -545,7 +585,12 @@ my %tests = (
 			schema_only => 1,
 			section_pre_data => 1,
 		},
-		unlike => { no_privs => 1, without_extension => 1, },
+		unlike => {
+			no_privs => 1,
+			exclude_extension => 1,
+			exclude_extension_filter => 1,
+			without_extension => 1,
+		},
 	},
 
 	'GRANT SELECT ON TABLE regress_pg_dump_table' => {
@@ -579,7 +624,12 @@ my %tests = (
 			schema_only => 1,
 			section_pre_data => 1,
 		},
-		unlike => { no_privs => 1, without_extension => 1 },
+		unlike => {
+			no_privs => 1,
+			exclude_extension => 1,
+			exclude_extension_filter => 1,
+			without_extension => 1
+		},
 	  },
 
 	'GRANT USAGE ON regress_pg_dump_table_col1_seq TO regress_dump_test_role'
@@ -595,7 +645,12 @@ my %tests = (
 			schema_only => 1,
 			section_pre_data => 1,
 		},
-		unlike => { no_privs => 1, without_extension => 1, },
+		unlike => {
+			no_privs => 1,
+			exclude_extension => 1,
+			exclude_extension_filter => 1,
+			without_extension => 1,
+		},
 	  },
 
 	'GRANT USAGE ON regress_pg_dump_seq TO regress_dump_test_role' => {
@@ -617,7 +672,12 @@ my %tests = (
 			schema_only => 1,
 			section_pre_data => 1,
 		},
-		unlike => { no_privs => 1, without_extension => 1, },
+		unlike => {
+			no_privs => 1,
+			exclude_extension => 1,
+			exclude_extension_filter => 1,
+			without_extension => 1,
+		},
 	},
 
 	# Objects included in extension part of a schema created by this extension */
@@ -818,6 +878,16 @@ foreach my $test (
 # Send the combined set of commands to psql
 $node->safe_psql('postgres', $create_sql);
 
+#########################################
+# Create filter file for exclude_extension_filter test
+
+my $filterfile;
+
+open $filterfile, '>', "$tempdir/exclude_extension_filter.txt"
+  or die "unable to open filter file for writing";
+print $filterfile "exclude extension test_pg_dump\n";
+close $filterfile;
+
 #########################################
 # Run all runs
 
-- 
2.35.3

Reply via email to