Reject incompatible options specified with VACUUM ONLY_DATABASE_STATS
Hackorum builds and tests every patch posted to the lists, not only commitfest submissions. This is Hackorum's own CI rather than the PostgreSQL project's, and it is still under testing - please report anything that looks wrong.
You can run a PostgreSQL built from this patch straight from Docker, with no checkout and no build:
docker run --rm -p 5432:5432 ghcr.io/hackorum-dev/postgres-patch:t254326psql -h localhost -U postgresBuilt from patchset v2 (message #2), October 24, 2026 at 04:13 PM.
Every patchset is also pushed to a branch of our PostgreSQL fork, so you can check out the same tree CI built. Without a PostgreSQL checkout:
git clone --branch t254326_2 https://github.com/hackorum-dev/postgres.gitIn a checkout you already have, add the fork once:
git remote add hackorum https://github.com/hackorum-dev/postgres.gitthen, for this patchset and every later one:
git fetch hackorum t254326_2 && git checkout t254326_2Patchset v2 (message #2) is on t254326_2
Hi,
The VACUUM option ONLY_DATABASE_STATS can only be used with VERBOSE. Other options should be rejected, as the documentation describes:
```
<term><literal>ONLY_DATABASE_STATS</literal></term>
<listitem>
<para>
Specifies that <command>VACUUM</command> should do nothing except
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
except <literal>VERBOSE</literal>.
</para>
```
When ONLY_DATABASE_STATS is enabled, no other option except VERBOSE may be enabled. But the current implementation does not check all options, for example:
```
evantest=# vacuum (ONLY_DATABASE_STATS, SKIP_LOCKED);
ERROR: ONLY_DATABASE_STATS cannot be specified with other VACUUM options
evantest=# vacuum (ONLY_DATABASE_STATS, PARALLEL 1);
VACUUM
```
With SKIP_LOCKED, the command fails as expected, while with PARALLEL 1 the command succeeds, which doesn't follow the doc and may confuse users.
In the fix, I considered whether rejection should be based on presence or value. VACUUM itself uses both approaches. When FULL rejects BUFFER_USAGE_LIMIT, the check is presence based:
```
evantest=# vacuum (FULL, BUFFER_USAGE_LIMIT 0);
ERROR: BUFFER_USAGE_LIMIT cannot be specified for VACUUM FULL
```
When FULL rejects PARALLEL, the check is value based:
```
evantest=# vacuum (FULL, PARALLEL 0);
VACUUM
evantest=# vacuum (FULL, PARALLEL 1);
ERROR: VACUUM FULL cannot be performed in parallel
```
I chose the presence based approach because in the PARALLEL case, the error specifically states that VACUUM FULL cannot be performed in parallel. In contrast, the ONLY_DATABASE_STATS error generically states that it cannot be specified with other VACUUM options, which is closer to when FULL rejects BUFFER_USAGE_LIMIT. Accordingly, I also updated the doc by replacing “enabled” with “specified".
See attached patch for the fix details.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Hi Chao
On 22/10/2026 08:23, Chao Li wrote:
When ONLY_DATABASE_STATS is enabled, no other option except VERBOSE may be enabled. But the current implementation does not check all options, for example:
```
evantest=# vacuum (ONLY_DATABASE_STATS, SKIP_LOCKED);
ERROR: ONLY_DATABASE_STATS cannot be specified with other VACUUM options
evantest=# vacuum (ONLY_DATABASE_STATS, PARALLEL 1);
VACUUM
```
Thanks for the patch!
A few observations:
== error message ==
Instead of creating the new only_database_stats_conflict flag you could
simply add a check in the if (params.options &
VACOPT_ONLY_DATABASE_STATS) block:
@@ -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")));
+ }
v2 attached implements this handling in ExecVacuum -- feel free to
revert it if you disagree.
== regression tests ==
All error cases are handled in the same path, so theoretically only a
few options (representative ones) would suffice. But I realise that if
the code ever changes, we might end up with untested code. I'd be
inclined to leave them as-is, but I thought I should bring it up. If
it's supposed to cover all possible options, you could consider adding
VACUUM (ONLY_DATABASE_STATS, VERBOSE false).
Thanks!
Best, Jim