From aed78dfb8a321416583198aa2497530b6ec40c9f Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Sat, 24 Oct 2026 17:42:38 +0200
Subject: [PATCH v2] Reject incompatible options specified with VACUUM
 ONLY_DATABASE_STATS

The existing check examines only option bits, so it misses PARALLEL,
INDEX_CLEANUP, TRUNCATE, and BUFFER_USAGE_LIMIT. These options are
accepted even though they have no effect with ONLY_DATABASE_STATS.

Track incompatible options while parsing and reject any explicitly
specified option other than VERBOSE, regardless of its value. This also
rejects explicitly disabled options and explicit PROCESS_MAIN and
PROCESS_TOAST options, while allowing their implicit defaults.

Update the documentation and add regression coverage.

Author: Chao Li <lic@highgo.com>
---
 doc/src/sgml/ref/vacuum.sgml         |  2 +-
 src/backend/commands/vacuum.c        | 20 +++++++++--------
 src/test/regress/expected/vacuum.out | 32 ++++++++++++++++++++++++++++
 src/test/regress/sql/vacuum.sql      | 17 +++++++++++++++
 4 files changed, 61 insertions(+), 10 deletions(-)

diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml
index 3c85846df26..0df6082f27b 100644
--- a/doc/src/sgml/ref/vacuum.sgml
+++ b/doc/src/sgml/ref/vacuum.sgml
@@ -303,7 +303,7 @@ VACUUM [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] [ <re
       update the database-wide statistics about oldest unfrozen XIDs.
       When this option is specified,
       the <replaceable class="parameter">table_and_columns</replaceable>
-      list must be empty, and no other option may be enabled
+      list must be empty, and no other option may be specified
       except <literal>VERBOSE</literal>.
      </para>
     </listitem>
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index c7595d59bb2..7df7f302967 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -378,15 +378,17 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 					 errmsg("ONLY_DATABASE_STATS cannot be specified with a list of tables")));
-		/* don't require people to turn off PROCESS_TOAST/MAIN explicitly */
-		if (params.options & ~(VACOPT_VACUUM |
-							   VACOPT_VERBOSE |
-							   VACOPT_PROCESS_MAIN |
-							   VACOPT_PROCESS_TOAST |
-							   VACOPT_ONLY_DATABASE_STATS))
-			ereport(ERROR,
-					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("ONLY_DATABASE_STATS cannot be specified with other VACUUM options")));
+		/* only VERBOSE may be specified alongside ONLY_DATABASE_STATS */
+		foreach(lc, vacstmt->options)
+		{
+			DefElem    *opt = lfirst_node(DefElem, lc);
+
+			if (strcmp(opt->defname, "verbose") != 0 &&
+				strcmp(opt->defname, "only_database_stats") != 0)
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("ONLY_DATABASE_STATS cannot be specified with other VACUUM options")));
+		}
 	}
 
 	/*
diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out
index d4696bc3325..e3f544d428a 100644
--- a/src/test/regress/expected/vacuum.out
+++ b/src/test/regress/expected/vacuum.out
@@ -551,6 +551,38 @@ VACUUM (SKIP_DATABASE_STATS) vactst;
 VACUUM (ONLY_DATABASE_STATS);
 VACUUM (ONLY_DATABASE_STATS) vactst;  -- error
 ERROR:  ONLY_DATABASE_STATS cannot be specified with a list of tables
+VACUUM (ONLY_DATABASE_STATS, FREEZE);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (ONLY_DATABASE_STATS, ANALYZE);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (ONLY_DATABASE_STATS, FULL);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (ONLY_DATABASE_STATS, SKIP_LOCKED);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (ONLY_DATABASE_STATS, DISABLE_PAGE_SKIPPING);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (ONLY_DATABASE_STATS, SKIP_DATABASE_STATS);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (ONLY_DATABASE_STATS, PARALLEL 1);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (ONLY_DATABASE_STATS, INDEX_CLEANUP ON);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (ONLY_DATABASE_STATS, INDEX_CLEANUP AUTO);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (ONLY_DATABASE_STATS, INDEX_CLEANUP);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (ONLY_DATABASE_STATS, TRUNCATE ON);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (ONLY_DATABASE_STATS, BUFFER_USAGE_LIMIT '1MB');  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (ONLY_DATABASE_STATS, PROCESS_MAIN ON);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (ONLY_DATABASE_STATS, PROCESS_TOAST ON);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+VACUUM (PARALLEL 1, ONLY_DATABASE_STATS);  -- error
+ERROR:  ONLY_DATABASE_STATS cannot be specified with other VACUUM options
+-- VERBOSE is allowed.
+VACUUM (ONLY_DATABASE_STATS, VERBOSE);
 DROP VIEW vac_option_tab_counts;
 DROP TABLE vac_option_tab;
 DROP TABLE vaccluster;
diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql
index 247b8e23b23..8f1fc17d299 100644
--- a/src/test/regress/sql/vacuum.sql
+++ b/src/test/regress/sql/vacuum.sql
@@ -411,6 +411,23 @@ VACUUM (SKIP_DATABASE_STATS) vactst;
 -- ONLY_DATABASE_STATS option
 VACUUM (ONLY_DATABASE_STATS);
 VACUUM (ONLY_DATABASE_STATS) vactst;  -- error
+VACUUM (ONLY_DATABASE_STATS, FREEZE);  -- error
+VACUUM (ONLY_DATABASE_STATS, ANALYZE);  -- error
+VACUUM (ONLY_DATABASE_STATS, FULL);  -- error
+VACUUM (ONLY_DATABASE_STATS, SKIP_LOCKED);  -- error
+VACUUM (ONLY_DATABASE_STATS, DISABLE_PAGE_SKIPPING);  -- error
+VACUUM (ONLY_DATABASE_STATS, SKIP_DATABASE_STATS);  -- error
+VACUUM (ONLY_DATABASE_STATS, PARALLEL 1);  -- error
+VACUUM (ONLY_DATABASE_STATS, INDEX_CLEANUP ON);  -- error
+VACUUM (ONLY_DATABASE_STATS, INDEX_CLEANUP AUTO);  -- error
+VACUUM (ONLY_DATABASE_STATS, INDEX_CLEANUP);  -- error
+VACUUM (ONLY_DATABASE_STATS, TRUNCATE ON);  -- error
+VACUUM (ONLY_DATABASE_STATS, BUFFER_USAGE_LIMIT '1MB');  -- error
+VACUUM (ONLY_DATABASE_STATS, PROCESS_MAIN ON);  -- error
+VACUUM (ONLY_DATABASE_STATS, PROCESS_TOAST ON);  -- error
+VACUUM (PARALLEL 1, ONLY_DATABASE_STATS);  -- error
+-- VERBOSE is allowed.
+VACUUM (ONLY_DATABASE_STATS, VERBOSE);
 
 DROP VIEW vac_option_tab_counts;
 DROP TABLE vac_option_tab;
-- 
2.56.0

