Hi,

Commit 7ca548f23a6 reverted pg_dumpall's non-text output format support
per Noah Misch's post-commit review. That revert was scoped carefully to
remove only the feature itself, but its commit message notes one piece of
unavoidable collateral damage:

  Because the feature restructured pg_dumpall.c and pg_restore.c (pg_restore's
  main() was split into restore_one_database() plus a dispatcher) and
  interleaved its option checks with the conflicting-option refactor in the
  same regions, the cosmetic check_mut_excl_opts() reflow of those two files'
  option blocks is inseparable from the feature and comes out with it; the
  behavior is unchanged.

In other words, pg_restore.c and pg_dumpall.c both went back to their old,
ad hoc pairwise "if (a && b) pg_fatal(...)" style option-conflict checks,
even though converting them to use the shared check_mut_excl_opts() helper
(added by b2898baaf7e, and already used by pg_dump.c) is a purely mechanical,
behavior-preserving cleanup with no dependency on the reverted feature's
design.

Attached are two small, independent patches that redo that conversion on
top of current master:

  0001 - pg_restore.c: converts its *-only / --no-* / --statistics /
         --clean / --single-transaction conflict checks to
         check_mut_excl_opts().

  0002 - pg_dumpall.c: same conversion for --exclude-database, the *-only
         options, their --no-* counterparts, --statistics, and --clean.
         Also tracks -s/--schema-only locally (schema_only), which
         pg_dumpall previously just passed through to pg_dump without
         recording, since check_mut_excl_opts() needs it. Updates the
         expected error text in the TAP tests to match
         check_mut_excl_opts()'s "options X and Y cannot be used together"
         wording, and adds coverage for a couple of option pairs that
         previously had no dedicated test.

Both patches preserve the exact pre-existing error-message wording and
option-reporting order for every combination that already had test
coverage; I verified this with the full src/bin/pg_dump TAP suite
(make check), which passes cleanly with both patches applied.

They're independent of each other and can be applied/reviewed in either
order or separately.

I think we can commit those into v19 and master. Please review and let
me know feedback.

-- 
Thanks and Regards
Mahendra Singh Thalor
EnterpriseDB: http://www.enterprisedb.com
From 9d0f85c942c71b4bffdc08c1b26aec60a8f412f6 Mon Sep 17 00:00:00 2001
From: Mahendra Singh Thalor <[email protected]>
Date: Tue, 14 Jul 2026 13:35:48 +0530
Subject: [PATCH] pg_restore: Use check_mut_excl_opts() for mutually-exclusive
 options

Replace the ad hoc pairwise if/pg_fatal() checks for conflicting
command-line options with calls to the shared check_mut_excl_opts()
helper, matching the style already used in pg_dump.c. This covers
the *-only options, their --no-* counterparts, --statistics vs
--no-statistics, --statistics vs *-only, --clean vs --data-only,
--single-transaction vs --transaction-size, and -C vs
--single-transaction.

No behavior change; error message wording and option-reporting
order are preserved exactly.
---
 src/bin/pg_dump/pg_restore.c | 75 +++++++++++++++---------------------
 1 file changed, 30 insertions(+), 45 deletions(-)

diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 84b8d410c9e..8be609c5170 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -377,56 +377,41 @@ main(int argc, char **argv)
 			pg_fatal("invalid restrict key");
 	}
 
-	/* reject conflicting "-only" options */
-	if (data_only && schema_only)
-		pg_fatal("options %s and %s cannot be used together",
-				 "-s/--schema-only", "-a/--data-only");
-	if (schema_only && statistics_only)
-		pg_fatal("options %s and %s cannot be used together",
-				 "-s/--schema-only", "--statistics-only");
-	if (data_only && statistics_only)
-		pg_fatal("options %s and %s cannot be used together",
-				 "-a/--data-only", "--statistics-only");
-
-	/* reject conflicting "-only" and "no-" options */
-	if (data_only && no_data)
-		pg_fatal("options %s and %s cannot be used together",
-				 "-a/--data-only", "--no-data");
-	if (schema_only && no_schema)
-		pg_fatal("options %s and %s cannot be used together",
-				 "-s/--schema-only", "--no-schema");
-	if (statistics_only && no_statistics)
-		pg_fatal("options %s and %s cannot be used together",
-				 "--statistics-only", "--no-statistics");
-
-	/* reject conflicting "no-" options */
-	if (with_statistics && no_statistics)
-		pg_fatal("options %s and %s cannot be used together",
-				 "--statistics", "--no-statistics");
-
-	/* reject conflicting "only-" options */
-	if (data_only && with_statistics)
-		pg_fatal("options %s and %s cannot be used together",
-				 "-a/--data-only", "--statistics");
-	if (schema_only && with_statistics)
-		pg_fatal("options %s and %s cannot be used together",
-				 "-s/--schema-only", "--statistics");
-
-	if (data_only && opts->dropSchema)
-		pg_fatal("options %s and %s cannot be used together",
-				 "-c/--clean", "-a/--data-only");
-
-	if (opts->single_txn && opts->txn_size > 0)
-		pg_fatal("options %s and %s cannot be used together",
-				 "-1/--single-transaction", "--transaction-size");
+	/* *-only options are incompatible with each other */
+	check_mut_excl_opts(schema_only, "-s/--schema-only",
+						data_only, "-a/--data-only",
+						statistics_only, "--statistics-only");
+
+	/* --no-* and *-only for same thing are incompatible */
+	check_mut_excl_opts(data_only, "-a/--data-only",
+						no_data, "--no-data");
+	check_mut_excl_opts(schema_only, "-s/--schema-only",
+						no_schema, "--no-schema");
+	check_mut_excl_opts(statistics_only, "--statistics-only",
+						no_statistics, "--no-statistics");
+
+	/* --statistics and --no-statistics are incompatible */
+	check_mut_excl_opts(with_statistics, "--statistics",
+						no_statistics, "--no-statistics");
+
+	/* --statistics is incompatible with *-only (except --statistics-only) */
+	check_mut_excl_opts(data_only, "-a/--data-only",
+						schema_only, "-s/--schema-only",
+						with_statistics, "--statistics");
+
+	/* --clean is incompatible with --data-only */
+	check_mut_excl_opts(opts->dropSchema, "-c/--clean",
+						data_only, "-a/--data-only");
+
+	check_mut_excl_opts(opts->single_txn, "-1/--single-transaction",
+						opts->txn_size > 0, "--transaction-size");
 
 	/*
 	 * -C is not compatible with -1, because we can't create a database inside
 	 * a transaction block.
 	 */
-	if (opts->createDB && opts->single_txn)
-		pg_fatal("options %s and %s cannot be used together",
-				 "-C/--create", "-1/--single-transaction");
+	check_mut_excl_opts(opts->createDB, "-C/--create",
+						opts->single_txn, "-1/--single-transaction");
 
 	/* Can't do single-txn mode with multiple connections */
 	if (opts->single_txn && numWorkers > 1)
-- 
2.52.0

From 73deb24ac22d7051175263e6d75335e0f818822c Mon Sep 17 00:00:00 2001
From: Mahendra Singh Thalor <[email protected]>
Date: Tue, 14 Jul 2026 13:42:36 +0530
Subject: [PATCH] pg_dumpall: Use check_mut_excl_opts() for mutually-exclusive
 options

Replace the ad hoc if/pg_log_error() and if/pg_fatal() checks for
conflicting command-line options with calls to the shared
check_mut_excl_opts() helper, matching the style already used in
pg_dump.c and pg_restore.c. This covers --exclude-database vs the
global *-only options, the *-only options against each other, their
--no-* counterparts, --statistics vs --no-statistics, --statistics
vs *-only, and --clean vs --data-only.

Track -s/--schema-only locally (as schema_only) so it can
participate in these checks, matching how the other *-only options
are already tracked.

Update the expected error text in the TAP tests to match
check_mut_excl_opts()'s "options X and Y cannot be used together"
wording, and add coverage for the option pairs that previously had
no dedicated test.
---
 src/bin/pg_dump/pg_dumpall.c                | 77 +++++++++++----------
 src/bin/pg_dump/t/001_basic.pl              | 34 ++++++++-
 src/bin/pg_dump/t/005_pg_dump_filterfile.pl |  4 +-
 3 files changed, 73 insertions(+), 42 deletions(-)

diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index c53e77c2878..0743bb48e48 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -27,6 +27,7 @@
 #include "common/string.h"
 #include "connectdb.h"
 #include "dumputils.h"
+#include "fe_utils/option_utils.h"
 #include "fe_utils/string_utils.h"
 #include "filter.h"
 #include "getopt_long.h"
@@ -201,6 +202,7 @@ main(int argc, char *argv[])
 	bool		data_only = false;
 	bool		globals_only = false;
 	bool		roles_only = false;
+	bool		schema_only = false;
 	bool		tablespaces_only = false;
 	PGconn	   *conn;
 	int			encoding;
@@ -299,6 +301,7 @@ main(int argc, char *argv[])
 				break;
 
 			case 's':
+				schema_only = true;
 				appendPQExpBufferStr(pgdumpopts, " -s");
 				break;
 
@@ -395,50 +398,48 @@ main(int argc, char *argv[])
 		exit_nicely(1);
 	}
 
-	if (database_exclude_patterns.head != NULL &&
-		(globals_only || roles_only || tablespaces_only))
-	{
-		pg_log_error("option %s cannot be used together with %s, %s, or %s",
-					 "--exclude-database",
-					 "-g/--globals-only", "-r/--roles-only", "-t/--tablespaces-only");
-		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
-		exit_nicely(1);
-	}
+	/* --exclude-database is incompatible with global *-only options */
+	check_mut_excl_opts(database_exclude_patterns.head, "--exclude-database",
+						globals_only, "-g/--globals-only",
+						roles_only, "-r/--roles-only",
+						tablespaces_only, "-t/--tablespaces-only");
+
+	/* *-only options are incompatible with each other */
+	check_mut_excl_opts(data_only, "-a/--data-only",
+						globals_only, "-g/--globals-only",
+						roles_only, "-r/--roles-only",
+						schema_only, "-s/--schema-only",
+						statistics_only, "--statistics-only",
+						tablespaces_only, "-t/--tablespaces-only");
+
+	/* --no-* and *-only for same thing are incompatible */
+	check_mut_excl_opts(data_only, "-a/--data-only",
+						no_data, "--no-data");
+	check_mut_excl_opts(schema_only, "-s/--schema-only",
+						no_schema, "--no-schema");
+	check_mut_excl_opts(statistics_only, "--statistics-only",
+						no_statistics, "--no-statistics");
+
+	/* --statistics and --no-statistics are incompatible */
+	check_mut_excl_opts(with_statistics, "--statistics",
+						no_statistics, "--no-statistics");
+
+	/* --statistics is incompatible with *-only (except --statistics-only) */
+	check_mut_excl_opts(with_statistics, "--statistics",
+						data_only, "-a/--data-only",
+						globals_only, "-g/--globals-only",
+						roles_only, "-r/--roles-only",
+						schema_only, "-s/--schema-only",
+						tablespaces_only, "-t/--tablespaces-only");
 
-	/* Make sure the user hasn't specified a mix of globals-only options */
-	if (globals_only && roles_only)
-	{
-		pg_log_error("options %s and %s cannot be used together",
-					 "-g/--globals-only", "-r/--roles-only");
-		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
-		exit_nicely(1);
-	}
-
-	if (globals_only && tablespaces_only)
-	{
-		pg_log_error("options %s and %s cannot be used together",
-					 "-g/--globals-only", "-t/--tablespaces-only");
-		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
-		exit_nicely(1);
-	}
+	/* --clean and --data-only are incompatible */
+	check_mut_excl_opts(output_clean, "-c/--clean",
+						data_only, "-a/--data-only");
 
 	if (if_exists && !output_clean)
 		pg_fatal("option %s requires option %s",
 				 "--if-exists", "-c/--clean");
 
-	/* --clean and --data-only are incompatible */
-	if (output_clean && data_only)
-		pg_fatal("options %s and %s cannot be used together",
-				 "-c/--clean", "-a/--data-only");
-
-	if (roles_only && tablespaces_only)
-	{
-		pg_log_error("options %s and %s cannot be used together",
-					 "-r/--roles-only", "-t/--tablespaces-only");
-		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
-		exit_nicely(1);
-	}
-
 	/*
 	 * If password values are not required in the dump, switch to using
 	 * pg_roles which is equally useful, just more likely to have unrestricted
diff --git a/src/bin/pg_dump/t/001_basic.pl b/src/bin/pg_dump/t/001_basic.pl
index b2558046224..d89ba4a4ceb 100644
--- a/src/bin/pg_dump/t/001_basic.pl
+++ b/src/bin/pg_dump/t/001_basic.pl
@@ -246,8 +246,38 @@ command_fails_like(
 # also fails for -r and -t, but it seems pointless to add more tests for those.
 command_fails_like(
 	[ 'pg_dumpall', '--exclude-database=foo', '--globals-only' ],
-	qr/\Qpg_dumpall: error: option --exclude-database cannot be used together with -g\/--globals-only\E/,
-	'pg_dumpall: option --exclude-database cannot be used together with -g/--globals-only'
+	qr/\Qpg_dumpall: error: options --exclude-database and -g\/--globals-only cannot be used together\E/,
+	'pg_dumpall: options --exclude-database and -g/--globals-only cannot be used together'
+);
+
+command_fails_like(
+	[ 'pg_dumpall', '-a', '--no-data' ],
+	qr/\Qpg_dumpall: error: options -a\/--data-only and --no-data cannot be used together\E/,
+	'pg_dumpall: options -a\/--data-only and --no-data cannot be used together'
+);
+
+command_fails_like(
+	[ 'pg_dumpall', '-s', '--no-schema' ],
+	qr/\Qpg_dumpall: error: options -s\/--schema-only and --no-schema cannot be used together\E/,
+	'pg_dumpall: options -s\/--schema-only and --no-schema cannot be used together'
+);
+
+command_fails_like(
+	[ 'pg_dumpall', '--statistics-only', '--no-statistics' ],
+	qr/\Qpg_dumpall: error: options --statistics-only and --no-statistics cannot be used together\E/,
+	'pg_dumpall: options --statistics-only and --no-statistics cannot be used together'
+);
+
+command_fails_like(
+	[ 'pg_dumpall', '--statistics', '--no-statistics' ],
+	qr/\Qpg_dumpall: error: options --statistics and --no-statistics cannot be used together\E/,
+	'pg_dumpall: options --statistics-only and --no-statistics cannot be used together'
+);
+
+command_fails_like(
+	[ 'pg_dumpall', '--statistics', '--tablespaces-only' ],
+	qr/\Qpg_dumpall: error: options --statistics and -t\/--tablespaces-only cannot be used together\E/,
+	'pg_dumpall: options --statistics and -t\/--tablespaces-only cannot be used together'
 );
 
 done_testing();
diff --git a/src/bin/pg_dump/t/005_pg_dump_filterfile.pl b/src/bin/pg_dump/t/005_pg_dump_filterfile.pl
index 72a7c90c6af..cecf0442088 100644
--- a/src/bin/pg_dump/t/005_pg_dump_filterfile.pl
+++ b/src/bin/pg_dump/t/005_pg_dump_filterfile.pl
@@ -570,8 +570,8 @@ command_fails_like(
 		'--filter' => "$tempdir/inputfile.txt",
 		'--globals-only'
 	],
-	qr/\Qpg_dumpall: error: option --exclude-database cannot be used together with -g\/--globals-only\E/,
-	'pg_dumpall: option --exclude-database cannot be used together with -g/--globals-only'
+	qr/\Qpg_dumpall: error: options --exclude-database and -g\/--globals-only cannot be used together\E/,
+	'pg_dumpall: options --exclude-database and -g/--globals-only cannot be used together'
 );
 
 # Test invalid filter command
-- 
2.52.0

Reply via email to