PROCESS_TOAST [ <replaceable class="parameter">boolean</replaceable> ]
TRUNCATE [ <replaceable class="parameter">boolean</replaceable> ]
PARALLEL <replaceable class="parameter">integer</replaceable>
+ SKIP_DATABASE_STATS [ <replaceable class="parameter">boolean</replaceable> ]
+ ONLY_DATABASE_STATS [ <replaceable class="parameter">boolean</replaceable> ]
<phrase>and <replaceable class="parameter">table_and_columns</replaceable> is:</phrase>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>SKIP_DATABASE_STATS</literal></term>
+ <listitem>
+ <para>
+ Specifies that <command>VACUUM</command> should skip updating the
+ database-wide statistics about oldest unfrozen XIDs. Normally
+ <command>VACUUM</command> will update these statistics once at the
+ end of the command. However, this can take awhile in a database
+ with a very large number of tables, and it will accomplish nothing
+ unless the table that had contained the oldest unfrozen XID was
+ among those vacuumed. Moreover, if multiple <command>VACUUM</command>
+ commands are issued in parallel, only one of them can update the
+ database-wide statistics at a time. Therefore, if an application
+ intends to issue a series of many <command>VACUUM</command>
+ commands, it can be helpful to set this option in all but the last
+ such command; or set it in all the commands and separately
+ issue <literal>VACUUM (ONLY_DATABASE_STATS)</literal> afterwards.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <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>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><replaceable class="parameter">boolean</replaceable></term>
<listitem>
bool full = false;
bool disable_page_skipping = false;
bool process_toast = true;
+ bool skip_database_stats = false;
+ bool only_database_stats = false;
ListCell *lc;
/* index_cleanup and truncate values unspecified for now */
params.nworkers = nworkers;
}
}
+ else if (strcmp(opt->defname, "skip_database_stats") == 0)
+ skip_database_stats = defGetBoolean(opt);
+ else if (strcmp(opt->defname, "only_database_stats") == 0)
+ only_database_stats = defGetBoolean(opt);
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
(freeze ? VACOPT_FREEZE : 0) |
(full ? VACOPT_FULL : 0) |
(disable_page_skipping ? VACOPT_DISABLE_PAGE_SKIPPING : 0) |
- (process_toast ? VACOPT_PROCESS_TOAST : 0);
+ (process_toast ? VACOPT_PROCESS_TOAST : 0) |
+ (skip_database_stats ? VACOPT_SKIP_DATABASE_STATS : 0) |
+ (only_database_stats ? VACOPT_ONLY_DATABASE_STATS : 0);
/* sanity checks on options */
Assert(params.options & (VACOPT_VACUUM | VACOPT_ANALYZE));
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("PROCESS_TOAST required with VACUUM FULL")));
+ /* sanity check for ONLY_DATABASE_STATS */
+ if (params->options & VACOPT_ONLY_DATABASE_STATS)
+ {
+ Assert(params->options & VACOPT_VACUUM);
+ if (relations != NIL)
+ 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 explicitly */
+ if (params->options & ~(VACOPT_VACUUM |
+ VACOPT_VERBOSE |
+ 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")));
+ }
+
/*
* Create special memory context for cross-transaction storage.
*
* Build list of relation(s) to process, putting any new data in
* vac_context for safekeeping.
*/
- if (relations != NIL)
+ if (params->options & VACOPT_ONLY_DATABASE_STATS)
+ {
+ /* We don't process any tables in this case */
+ Assert(relations == NIL);
+ }
+ else if (relations != NIL)
{
List *newrels = NIL;
ListCell *lc;
StartTransactionCommand();
}
- if ((params->options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
+ if ((params->options & VACOPT_VACUUM) &&
+ !(params->options & VACOPT_SKIP_DATABASE_STATS))
{
/*
* Update pg_database.datfrozenxid, and truncate pg_xact if possible.
- * (autovacuum.c does this for itself.)
*/
vac_update_datfrozenxid();
}
Assert((options & (VACOPT_VACUUM | VACOPT_ANALYZE)) != 0);
- /*
+ /*----------
* A role has privileges to vacuum or analyze the relation if any of the
* following are true:
* - the role is a superuser
* - the role owns the relation
* - the role owns the current database and the relation is not shared
* - the role has been granted the MAINTAIN privilege on the relation
+ *----------
*/
if (object_ownercheck(RelationRelationId, relid, GetUserId()) ||
(object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) && !reltuple->relisshared) ||
tab->at_relid = relid;
tab->at_sharedrel = classForm->relisshared;
- /* Note that this skips toast relations */
- tab->at_params.options = (dovacuum ? VACOPT_VACUUM : 0) |
+ /*
+ * Select VACUUM options. Note we don't say VACOPT_PROCESS_TOAST, so
+ * that vacuum() skips toast relations. Also note we tell vacuum() to
+ * skip vac_update_datfrozenxid(); we'll do that separately.
+ */
+ tab->at_params.options =
+ (dovacuum ? (VACOPT_VACUUM | VACOPT_SKIP_DATABASE_STATS) : 0) |
(doanalyze ? VACOPT_ANALYZE : 0) |
(!wraparound ? VACOPT_SKIP_LOCKED : 0);
COMPLETE_WITH("FULL", "FREEZE", "ANALYZE", "VERBOSE",
"DISABLE_PAGE_SKIPPING", "SKIP_LOCKED",
"INDEX_CLEANUP", "PROCESS_TOAST",
- "TRUNCATE", "PARALLEL");
- else if (TailMatches("FULL|FREEZE|ANALYZE|VERBOSE|DISABLE_PAGE_SKIPPING|SKIP_LOCKED|PROCESS_TOAST|TRUNCATE"))
+ "TRUNCATE", "PARALLEL", "SKIP_DATABASE_STATS",
+ "ONLY_DATABASE_STATS");
+ else if (TailMatches("FULL|FREEZE|ANALYZE|VERBOSE|DISABLE_PAGE_SKIPPING|SKIP_LOCKED|PROCESS_TOAST|TRUNCATE|SKIP_DATABASE_STATS|ONLY_DATABASE_STATS"))
COMPLETE_WITH("ON", "OFF");
else if (TailMatches("INDEX_CLEANUP"))
COMPLETE_WITH("AUTO", "ON", "OFF");
'SQL VACUUM run');
$node->issues_sql_like(
[ 'vacuumdb', '-f', 'postgres' ],
- qr/statement: VACUUM \(FULL\).*;/,
+ qr/statement: VACUUM \(SKIP_DATABASE_STATS, FULL\).*;/,
'vacuumdb -f');
$node->issues_sql_like(
[ 'vacuumdb', '-F', 'postgres' ],
- qr/statement: VACUUM \(FREEZE\).*;/,
+ qr/statement: VACUUM \(SKIP_DATABASE_STATS, FREEZE\).*;/,
'vacuumdb -F');
$node->issues_sql_like(
[ 'vacuumdb', '-zj2', 'postgres' ],
- qr/statement: VACUUM \(ANALYZE\).*;/,
+ qr/statement: VACUUM \(SKIP_DATABASE_STATS, ANALYZE\).*;/,
'vacuumdb -zj2');
$node->issues_sql_like(
[ 'vacuumdb', '-Z', 'postgres' ],
'vacuumdb -Z');
$node->issues_sql_like(
[ 'vacuumdb', '--disable-page-skipping', 'postgres' ],
- qr/statement: VACUUM \(DISABLE_PAGE_SKIPPING\).*;/,
+ qr/statement: VACUUM \(DISABLE_PAGE_SKIPPING, SKIP_DATABASE_STATS\).*;/,
'vacuumdb --disable-page-skipping');
$node->issues_sql_like(
[ 'vacuumdb', '--skip-locked', 'postgres' ],
- qr/statement: VACUUM \(SKIP_LOCKED\).*;/,
+ qr/statement: VACUUM \(SKIP_DATABASE_STATS, SKIP_LOCKED\).*;/,
'vacuumdb --skip-locked');
$node->issues_sql_like(
[ 'vacuumdb', '--skip-locked', '--analyze-only', 'postgres' ],
'--analyze-only and --disable-page-skipping specified together');
$node->issues_sql_like(
[ 'vacuumdb', '--no-index-cleanup', 'postgres' ],
- qr/statement: VACUUM \(INDEX_CLEANUP FALSE\).*;/,
+ qr/statement: VACUUM \(INDEX_CLEANUP FALSE, SKIP_DATABASE_STATS\).*;/,
'vacuumdb --no-index-cleanup');
$node->command_fails(
[ 'vacuumdb', '--analyze-only', '--no-index-cleanup', 'postgres' ],
'--analyze-only and --no-index-cleanup specified together');
$node->issues_sql_like(
[ 'vacuumdb', '--no-truncate', 'postgres' ],
- qr/statement: VACUUM \(TRUNCATE FALSE\).*;/,
+ qr/statement: VACUUM \(TRUNCATE FALSE, SKIP_DATABASE_STATS\).*;/,
'vacuumdb --no-truncate');
$node->command_fails(
[ 'vacuumdb', '--analyze-only', '--no-truncate', 'postgres' ],
'--analyze-only and --no-truncate specified together');
$node->issues_sql_like(
[ 'vacuumdb', '--no-process-toast', 'postgres' ],
- qr/statement: VACUUM \(PROCESS_TOAST FALSE\).*;/,
+ qr/statement: VACUUM \(PROCESS_TOAST FALSE, SKIP_DATABASE_STATS\).*;/,
'vacuumdb --no-process-toast');
$node->command_fails(
[ 'vacuumdb', '--analyze-only', '--no-process-toast', 'postgres' ],
'--analyze-only and --no-process-toast specified together');
$node->issues_sql_like(
[ 'vacuumdb', '-P', 2, 'postgres' ],
- qr/statement: VACUUM \(PARALLEL 2\).*;/,
+ qr/statement: VACUUM \(SKIP_DATABASE_STATS, PARALLEL 2\).*;/,
'vacuumdb -P 2');
$node->issues_sql_like(
[ 'vacuumdb', '-P', 0, 'postgres' ],
- qr/statement: VACUUM \(PARALLEL 0\).*;/,
+ qr/statement: VACUUM \(SKIP_DATABASE_STATS, PARALLEL 0\).*;/,
'vacuumdb -P 0');
$node->command_ok([qw(vacuumdb -Z --table=pg_am dbname=template1)],
'vacuumdb with connection string');
'negative parallel degree');
$node->issues_sql_like(
[ 'vacuumdb', '--analyze', '--table', 'vactable(a, b)', 'postgres' ],
- qr/statement: VACUUM \(ANALYZE\) public.vactable\(a, b\);/,
+ qr/statement: VACUUM \(SKIP_DATABASE_STATS, ANALYZE\) public.vactable\(a, b\);/,
'vacuumdb --analyze with complete column list');
$node->issues_sql_like(
[ 'vacuumdb', '--analyze-only', '--table', 'vactable(b)', 'postgres' ],
'vacuumdb --table --min-xid-age');
$node->issues_sql_like(
[ 'vacuumdb', '--schema', '"Foo"', 'postgres' ],
- qr/VACUUM "Foo".bar/,
+ qr/VACUUM \(SKIP_DATABASE_STATS\) "Foo".bar/,
'vacuumdb --schema');
$node->issues_sql_like(
[ 'vacuumdb', '--exclude-schema', '"Foo"', 'postgres' ],
bool force_index_cleanup;
bool do_truncate;
bool process_toast;
+ bool skip_database_stats;
} vacuumingOptions;
/* object filter options */
pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
"--parallel", "13");
+ /* skip_database_stats is used automatically if server supports it */
+ vacopts->skip_database_stats = (PQserverVersion(conn) >= 160000);
+
if (!quiet)
{
if (stage != ANALYZE_NO_STAGE)
} while (cell != NULL);
if (!ParallelSlotsWaitCompletion(sa))
+ {
failed = true;
+ goto finish;
+ }
+
+ /* If we used SKIP_DATABASE_STATS, mop up with ONLY_DATABASE_STATS */
+ if (vacopts->skip_database_stats && stage == ANALYZE_NO_STAGE)
+ {
+ const char *cmd = "VACUUM (ONLY_DATABASE_STATS);";
+ ParallelSlot *free_slot = ParallelSlotsGetIdle(sa, NULL);
+
+ if (!free_slot)
+ {
+ failed = true;
+ goto finish;
+ }
+
+ ParallelSlotSetHandler(free_slot, TableCommandResultHandler, NULL);
+ run_vacuum_command(free_slot->connection, cmd, echo, NULL);
+
+ if (!ParallelSlotsWaitCompletion(sa))
+ failed = true;
+ }
finish:
ParallelSlotsTerminate(sa);
appendPQExpBuffer(sql, "%sPROCESS_TOAST FALSE", sep);
sep = comma;
}
+ if (vacopts->skip_database_stats)
+ {
+ /* SKIP_DATABASE_STATS is supported since v16 */
+ Assert(serverVersion >= 160000);
+ appendPQExpBuffer(sql, "%sSKIP_DATABASE_STATS", sep);
+ sep = comma;
+ }
if (vacopts->skip_locked)
{
/* SKIP_LOCKED is supported since v12 */
continue;
if (!consumeQueryResult(&sa->slots[i]))
return false;
+ /* Mark connection as idle */
+ sa->slots[i].inUse = false;
+ ParallelSlotClearHandler(&sa->slots[i]);
}
return true;
#define VACOPT_SKIP_LOCKED 0x20 /* skip if cannot get lock */
#define VACOPT_PROCESS_TOAST 0x40 /* process the TOAST table, if any */
#define VACOPT_DISABLE_PAGE_SKIPPING 0x80 /* don't skip any pages */
+#define VACOPT_SKIP_DATABASE_STATS 0x100 /* skip vac_update_datfrozenxid() */
+#define VACOPT_ONLY_DATABASE_STATS 0x200 /* only vac_update_datfrozenxid() */
/*
* Values used by index_cleanup and truncate params.
VACUUM (PROCESS_TOAST FALSE) vactst;
VACUUM (PROCESS_TOAST FALSE, FULL) vactst;
ERROR: PROCESS_TOAST required with VACUUM FULL
+-- SKIP_DATABASE_STATS option
+VACUUM (SKIP_DATABASE_STATS) vactst;
+-- ONLY_DATABASE_STATS option
+VACUUM (ONLY_DATABASE_STATS);
+VACUUM (ONLY_DATABASE_STATS) vactst; -- error
+ERROR: ONLY_DATABASE_STATS cannot be specified with a list of tables
DROP TABLE vaccluster;
DROP TABLE vactst;
DROP TABLE vacparted;
VACUUM (PROCESS_TOAST FALSE) vactst;
VACUUM (PROCESS_TOAST FALSE, FULL) vactst;
+-- SKIP_DATABASE_STATS option
+VACUUM (SKIP_DATABASE_STATS) vactst;
+
+-- ONLY_DATABASE_STATS option
+VACUUM (ONLY_DATABASE_STATS);
+VACUUM (ONLY_DATABASE_STATS) vactst; -- error
+
DROP TABLE vaccluster;
DROP TABLE vactst;
DROP TABLE vacparted;