Rationalize vacuuming options and parameters

We were involving the parser too much in setting up initial vacuuming
parameters.  This patch moves that responsibility elsewhere to simplify
code, and also to make future additions easier.  To do this, create a
new struct VacuumParams which is filled just prior to vacuum execution,
instead of at parse time; for user-invoked vacuuming this is set up in a
new function ExecVacuum, while autovacuum sets it up by itself.

While at it, add a new member VACOPT_SKIPTOAST to enum VacuumOption,
only set by autovacuum, which is used to disable vacuuming of the toast
table instead of the old do_toast parameter; this relieves the argument
list of vacuum() and some callees a bit.  This partially makes up for
having added more arguments in an effort to avoid having autovacuum from
constructing a VacuumStmt parse node.

Author: Michael Paquier. Some tweaks by Álvaro
Reviewed by: Robert Haas, Stephen Frost, Álvaro Herrera
This commit is contained in:
Alvaro Herrera 2015-03-18 11:52:33 -03:00
parent 4559167c6b
commit 0d83138974
10 changed files with 159 additions and 170 deletions

View File

@ -85,7 +85,7 @@ static MemoryContext anl_context = NULL;
static BufferAccessStrategy vac_strategy; static BufferAccessStrategy vac_strategy;
static void do_analyze_rel(Relation onerel, VacuumStmt *vacstmt, static void do_analyze_rel(Relation onerel, int options, List *va_cols,
AcquireSampleRowsFunc acquirefunc, BlockNumber relpages, AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
bool inh, bool in_outer_xact, int elevel); bool inh, bool in_outer_xact, int elevel);
static void BlockSampler_Init(BlockSampler bs, BlockNumber nblocks, static void BlockSampler_Init(BlockSampler bs, BlockNumber nblocks,
@ -115,7 +115,7 @@ static Datum ind_fetch_func(VacAttrStatsP stats, int rownum, bool *isNull);
* analyze_rel() -- analyze one relation * analyze_rel() -- analyze one relation
*/ */
void void
analyze_rel(Oid relid, VacuumStmt *vacstmt, analyze_rel(Oid relid, RangeVar *relation, int options, List *va_cols,
bool in_outer_xact, BufferAccessStrategy bstrategy) bool in_outer_xact, BufferAccessStrategy bstrategy)
{ {
Relation onerel; Relation onerel;
@ -124,7 +124,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
BlockNumber relpages = 0; BlockNumber relpages = 0;
/* Select logging level */ /* Select logging level */
if (vacstmt->options & VACOPT_VERBOSE) if (options & VACOPT_VERBOSE)
elevel = INFO; elevel = INFO;
else else
elevel = DEBUG2; elevel = DEBUG2;
@ -144,7 +144,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
* matter if we ever try to accumulate stats on dead tuples.) If the rel * matter if we ever try to accumulate stats on dead tuples.) If the rel
* has been dropped since we last saw it, we don't need to process it. * has been dropped since we last saw it, we don't need to process it.
*/ */
if (!(vacstmt->options & VACOPT_NOWAIT)) if (!(options & VACOPT_NOWAIT))
onerel = try_relation_open(relid, ShareUpdateExclusiveLock); onerel = try_relation_open(relid, ShareUpdateExclusiveLock);
else if (ConditionalLockRelationOid(relid, ShareUpdateExclusiveLock)) else if (ConditionalLockRelationOid(relid, ShareUpdateExclusiveLock))
onerel = try_relation_open(relid, NoLock); onerel = try_relation_open(relid, NoLock);
@ -155,7 +155,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE), (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping analyze of \"%s\" --- lock not available", errmsg("skipping analyze of \"%s\" --- lock not available",
vacstmt->relation->relname))); relation->relname)));
} }
if (!onerel) if (!onerel)
return; return;
@ -167,7 +167,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
(pg_database_ownercheck(MyDatabaseId, GetUserId()) && !onerel->rd_rel->relisshared))) (pg_database_ownercheck(MyDatabaseId, GetUserId()) && !onerel->rd_rel->relisshared)))
{ {
/* No need for a WARNING if we already complained during VACUUM */ /* No need for a WARNING if we already complained during VACUUM */
if (!(vacstmt->options & VACOPT_VACUUM)) if (!(options & VACOPT_VACUUM))
{ {
if (onerel->rd_rel->relisshared) if (onerel->rd_rel->relisshared)
ereport(WARNING, ereport(WARNING,
@ -248,7 +248,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
else else
{ {
/* No need for a WARNING if we already complained during VACUUM */ /* No need for a WARNING if we already complained during VACUUM */
if (!(vacstmt->options & VACOPT_VACUUM)) if (!(options & VACOPT_VACUUM))
ereport(WARNING, ereport(WARNING,
(errmsg("skipping \"%s\" --- cannot analyze non-tables or special system tables", (errmsg("skipping \"%s\" --- cannot analyze non-tables or special system tables",
RelationGetRelationName(onerel)))); RelationGetRelationName(onerel))));
@ -266,14 +266,14 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
/* /*
* Do the normal non-recursive ANALYZE. * Do the normal non-recursive ANALYZE.
*/ */
do_analyze_rel(onerel, vacstmt, acquirefunc, relpages, do_analyze_rel(onerel, options, va_cols, acquirefunc, relpages,
false, in_outer_xact, elevel); false, in_outer_xact, elevel);
/* /*
* If there are child tables, do recursive ANALYZE. * If there are child tables, do recursive ANALYZE.
*/ */
if (onerel->rd_rel->relhassubclass) if (onerel->rd_rel->relhassubclass)
do_analyze_rel(onerel, vacstmt, acquirefunc, relpages, do_analyze_rel(onerel, options, va_cols, acquirefunc, relpages,
true, in_outer_xact, elevel); true, in_outer_xact, elevel);
/* /*
@ -302,7 +302,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt,
* acquirefunc for each child table. * acquirefunc for each child table.
*/ */
static void static void
do_analyze_rel(Relation onerel, VacuumStmt *vacstmt, do_analyze_rel(Relation onerel, int options, List *va_cols,
AcquireSampleRowsFunc acquirefunc, BlockNumber relpages, AcquireSampleRowsFunc acquirefunc, BlockNumber relpages,
bool inh, bool in_outer_xact, int elevel) bool inh, bool in_outer_xact, int elevel)
{ {
@ -372,14 +372,14 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
* *
* Note that system attributes are never analyzed. * Note that system attributes are never analyzed.
*/ */
if (vacstmt->va_cols != NIL) if (va_cols != NIL)
{ {
ListCell *le; ListCell *le;
vacattrstats = (VacAttrStats **) palloc(list_length(vacstmt->va_cols) * vacattrstats = (VacAttrStats **) palloc(list_length(va_cols) *
sizeof(VacAttrStats *)); sizeof(VacAttrStats *));
tcnt = 0; tcnt = 0;
foreach(le, vacstmt->va_cols) foreach(le, va_cols)
{ {
char *col = strVal(lfirst(le)); char *col = strVal(lfirst(le));
@ -436,7 +436,7 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
thisdata->indexInfo = indexInfo = BuildIndexInfo(Irel[ind]); thisdata->indexInfo = indexInfo = BuildIndexInfo(Irel[ind]);
thisdata->tupleFract = 1.0; /* fix later if partial */ thisdata->tupleFract = 1.0; /* fix later if partial */
if (indexInfo->ii_Expressions != NIL && vacstmt->va_cols == NIL) if (indexInfo->ii_Expressions != NIL && va_cols == NIL)
{ {
ListCell *indexpr_item = list_head(indexInfo->ii_Expressions); ListCell *indexpr_item = list_head(indexInfo->ii_Expressions);
@ -595,7 +595,7 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
* VACUUM ANALYZE, don't overwrite the accurate count already inserted by * VACUUM ANALYZE, don't overwrite the accurate count already inserted by
* VACUUM. * VACUUM.
*/ */
if (!inh && !(vacstmt->options & VACOPT_VACUUM)) if (!inh && !(options & VACOPT_VACUUM))
{ {
for (ind = 0; ind < nindexes; ind++) for (ind = 0; ind < nindexes; ind++)
{ {
@ -623,7 +623,7 @@ do_analyze_rel(Relation onerel, VacuumStmt *vacstmt,
pgstat_report_analyze(onerel, totalrows, totaldeadrows); pgstat_report_analyze(onerel, totalrows, totaldeadrows);
/* If this isn't part of VACUUM ANALYZE, let index AMs do cleanup */ /* If this isn't part of VACUUM ANALYZE, let index AMs do cleanup */
if (!(vacstmt->options & VACOPT_VACUUM)) if (!(options & VACOPT_VACUUM))
{ {
for (ind = 0; ind < nindexes; ind++) for (ind = 0; ind < nindexes; ind++)
{ {

View File

@ -71,35 +71,79 @@ static void vac_truncate_clog(TransactionId frozenXID,
MultiXactId minMulti, MultiXactId minMulti,
TransactionId lastSaneFrozenXid, TransactionId lastSaneFrozenXid,
MultiXactId lastSaneMinMulti); MultiXactId lastSaneMinMulti);
static bool vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, static bool vacuum_rel(Oid relid, RangeVar *relation, int options,
bool for_wraparound); VacuumParams *params);
/*
* Primary entry point for manual VACUUM and ANALYZE commands
*
* This is mainly a preparation wrapper for the real operations that will
* happen in vacuum().
*/
void
ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel)
{
VacuumParams params;
/* sanity checks on options */
Assert(vacstmt->options & (VACOPT_VACUUM | VACOPT_ANALYZE));
Assert((vacstmt->options & VACOPT_VACUUM) ||
!(vacstmt->options & (VACOPT_FULL | VACOPT_FREEZE)));
Assert((vacstmt->options & VACOPT_ANALYZE) || vacstmt->va_cols == NIL);
Assert(!(vacstmt->options & VACOPT_SKIPTOAST));
/*
* All freeze ages are zero if the FREEZE option is given; otherwise pass
* them as -1 which means to use the default values.
*/
if (vacstmt->options & VACOPT_FREEZE)
{
params.freeze_min_age = 0;
params.freeze_table_age = 0;
params.multixact_freeze_min_age = 0;
params.multixact_freeze_table_age = 0;
}
else
{
params.freeze_min_age = -1;
params.freeze_table_age = -1;
params.multixact_freeze_min_age = -1;
params.multixact_freeze_table_age = -1;
}
/* user-invoked vacuum is never "for wraparound" */
params.is_wraparound = false;
/* Now go through the common routine */
vacuum(vacstmt->options, vacstmt->relation, InvalidOid, &params,
vacstmt->va_cols, NULL, isTopLevel);
}
/* /*
* Primary entry point for VACUUM and ANALYZE commands. * Primary entry point for VACUUM and ANALYZE commands.
* *
* relid is normally InvalidOid; if it is not, then it provides the relation * options is a bitmask of VacuumOption flags, indicating what to do.
* OID to be processed, and vacstmt->relation is ignored. (The non-invalid
* case is currently only used by autovacuum.)
* *
* do_toast is passed as FALSE by autovacuum, because it processes TOAST * relid, if not InvalidOid, indicate the relation to process; otherwise,
* tables separately. * the RangeVar is used. (The latter must always be passed, because it's
* used for error messages.)
* *
* for_wraparound is used by autovacuum to let us know when it's forcing * params contains a set of parameters that can be used to customize the
* a vacuum for wraparound, which should not be auto-canceled. * behavior.
*
* va_cols is a list of columns to analyze, or NIL to process them all.
* *
* bstrategy is normally given as NULL, but in autovacuum it can be passed * bstrategy is normally given as NULL, but in autovacuum it can be passed
* in to use the same buffer strategy object across multiple vacuum() calls. * in to use the same buffer strategy object across multiple vacuum() calls.
* *
* isTopLevel should be passed down from ProcessUtility. * isTopLevel should be passed down from ProcessUtility.
* *
* It is the caller's responsibility that vacstmt and bstrategy * It is the caller's responsibility that all parameters are allocated in a
* (if given) be allocated in a memory context that won't disappear * memory context that will not disappear at transaction commit.
* at transaction commit.
*/ */
void void
vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast, vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params,
BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel) List *va_cols, BufferAccessStrategy bstrategy, bool isTopLevel)
{ {
const char *stmttype; const char *stmttype;
volatile bool in_outer_xact, volatile bool in_outer_xact,
@ -107,13 +151,9 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
List *relations; List *relations;
static bool in_vacuum = false; static bool in_vacuum = false;
/* sanity checks on options */ Assert(params != NULL);
Assert(vacstmt->options & (VACOPT_VACUUM | VACOPT_ANALYZE));
Assert((vacstmt->options & VACOPT_VACUUM) ||
!(vacstmt->options & (VACOPT_FULL | VACOPT_FREEZE)));
Assert((vacstmt->options & VACOPT_ANALYZE) || vacstmt->va_cols == NIL);
stmttype = (vacstmt->options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE"; stmttype = (options & VACOPT_VACUUM) ? "VACUUM" : "ANALYZE";
/* /*
* We cannot run VACUUM inside a user transaction block; if we were inside * We cannot run VACUUM inside a user transaction block; if we were inside
@ -123,7 +163,7 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
* *
* ANALYZE (without VACUUM) can run either way. * ANALYZE (without VACUUM) can run either way.
*/ */
if (vacstmt->options & VACOPT_VACUUM) if (options & VACOPT_VACUUM)
{ {
PreventTransactionChain(isTopLevel, stmttype); PreventTransactionChain(isTopLevel, stmttype);
in_outer_xact = false; in_outer_xact = false;
@ -143,7 +183,7 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
* Send info about dead objects to the statistics collector, unless we are * Send info about dead objects to the statistics collector, unless we are
* in autovacuum --- autovacuum.c does this for itself. * in autovacuum --- autovacuum.c does this for itself.
*/ */
if ((vacstmt->options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess()) if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
pgstat_vacuum_stat(); pgstat_vacuum_stat();
/* /*
@ -175,7 +215,7 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
* Build list of relations to process, unless caller gave us one. (If we * Build list of relations to process, unless caller gave us one. (If we
* build one, we put it in vac_context for safekeeping.) * build one, we put it in vac_context for safekeeping.)
*/ */
relations = get_rel_oids(relid, vacstmt->relation); relations = get_rel_oids(relid, relation);
/* /*
* Decide whether we need to start/commit our own transactions. * Decide whether we need to start/commit our own transactions.
@ -191,11 +231,11 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
* transaction block, and also in an autovacuum worker, use own * transaction block, and also in an autovacuum worker, use own
* transactions so we can release locks sooner. * transactions so we can release locks sooner.
*/ */
if (vacstmt->options & VACOPT_VACUUM) if (options & VACOPT_VACUUM)
use_own_xacts = true; use_own_xacts = true;
else else
{ {
Assert(vacstmt->options & VACOPT_ANALYZE); Assert(options & VACOPT_ANALYZE);
if (IsAutoVacuumWorkerProcess()) if (IsAutoVacuumWorkerProcess())
use_own_xacts = true; use_own_xacts = true;
else if (in_outer_xact) else if (in_outer_xact)
@ -245,13 +285,13 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
{ {
Oid relid = lfirst_oid(cur); Oid relid = lfirst_oid(cur);
if (vacstmt->options & VACOPT_VACUUM) if (options & VACOPT_VACUUM)
{ {
if (!vacuum_rel(relid, vacstmt, do_toast, for_wraparound)) if (!vacuum_rel(relid, relation, options, params))
continue; continue;
} }
if (vacstmt->options & VACOPT_ANALYZE) if (options & VACOPT_ANALYZE)
{ {
/* /*
* If using separate xacts, start one for analyze. Otherwise, * If using separate xacts, start one for analyze. Otherwise,
@ -264,7 +304,8 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
PushActiveSnapshot(GetTransactionSnapshot()); PushActiveSnapshot(GetTransactionSnapshot());
} }
analyze_rel(relid, vacstmt, in_outer_xact, vac_strategy); analyze_rel(relid, relation, options,
va_cols, in_outer_xact, vac_strategy);
if (use_own_xacts) if (use_own_xacts)
{ {
@ -299,7 +340,7 @@ vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast,
StartTransactionCommand(); StartTransactionCommand();
} }
if ((vacstmt->options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess()) if ((options & VACOPT_VACUUM) && !IsAutoVacuumWorkerProcess())
{ {
/* /*
* Update pg_database.datfrozenxid, and truncate pg_clog if possible. * Update pg_database.datfrozenxid, and truncate pg_clog if possible.
@ -1113,7 +1154,7 @@ vac_truncate_clog(TransactionId frozenXID,
* At entry and exit, we are not inside a transaction. * At entry and exit, we are not inside a transaction.
*/ */
static bool static bool
vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound) vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
{ {
LOCKMODE lmode; LOCKMODE lmode;
Relation onerel; Relation onerel;
@ -1123,6 +1164,8 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
int save_sec_context; int save_sec_context;
int save_nestlevel; int save_nestlevel;
Assert(params != NULL);
/* Begin a transaction for vacuuming this relation */ /* Begin a transaction for vacuuming this relation */
StartTransactionCommand(); StartTransactionCommand();
@ -1132,7 +1175,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
*/ */
PushActiveSnapshot(GetTransactionSnapshot()); PushActiveSnapshot(GetTransactionSnapshot());
if (!(vacstmt->options & VACOPT_FULL)) if (!(options & VACOPT_FULL))
{ {
/* /*
* In lazy vacuum, we can set the PROC_IN_VACUUM flag, which lets * In lazy vacuum, we can set the PROC_IN_VACUUM flag, which lets
@ -1156,7 +1199,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
*/ */
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
MyPgXact->vacuumFlags |= PROC_IN_VACUUM; MyPgXact->vacuumFlags |= PROC_IN_VACUUM;
if (for_wraparound) if (params->is_wraparound)
MyPgXact->vacuumFlags |= PROC_VACUUM_FOR_WRAPAROUND; MyPgXact->vacuumFlags |= PROC_VACUUM_FOR_WRAPAROUND;
LWLockRelease(ProcArrayLock); LWLockRelease(ProcArrayLock);
} }
@ -1172,7 +1215,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
* vacuum, but just ShareUpdateExclusiveLock for concurrent vacuum. Either * vacuum, but just ShareUpdateExclusiveLock for concurrent vacuum. Either
* way, we can be sure that no other backend is vacuuming the same table. * way, we can be sure that no other backend is vacuuming the same table.
*/ */
lmode = (vacstmt->options & VACOPT_FULL) ? AccessExclusiveLock : ShareUpdateExclusiveLock; lmode = (options & VACOPT_FULL) ? AccessExclusiveLock : ShareUpdateExclusiveLock;
/* /*
* Open the relation and get the appropriate lock on it. * Open the relation and get the appropriate lock on it.
@ -1183,7 +1226,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
* If we've been asked not to wait for the relation lock, acquire it first * If we've been asked not to wait for the relation lock, acquire it first
* in non-blocking mode, before calling try_relation_open(). * in non-blocking mode, before calling try_relation_open().
*/ */
if (!(vacstmt->options & VACOPT_NOWAIT)) if (!(options & VACOPT_NOWAIT))
onerel = try_relation_open(relid, lmode); onerel = try_relation_open(relid, lmode);
else if (ConditionalLockRelationOid(relid, lmode)) else if (ConditionalLockRelationOid(relid, lmode))
onerel = try_relation_open(relid, NoLock); onerel = try_relation_open(relid, NoLock);
@ -1194,7 +1237,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
ereport(LOG, ereport(LOG,
(errcode(ERRCODE_LOCK_NOT_AVAILABLE), (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
errmsg("skipping vacuum of \"%s\" --- lock not available", errmsg("skipping vacuum of \"%s\" --- lock not available",
vacstmt->relation->relname))); relation->relname)));
} }
if (!onerel) if (!onerel)
@ -1286,7 +1329,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
* us to process it. In VACUUM FULL, though, the toast table is * us to process it. In VACUUM FULL, though, the toast table is
* automatically rebuilt by cluster_rel so we shouldn't recurse to it. * automatically rebuilt by cluster_rel so we shouldn't recurse to it.
*/ */
if (do_toast && !(vacstmt->options & VACOPT_FULL)) if (!(options & VACOPT_SKIPTOAST) && !(options & VACOPT_FULL))
toast_relid = onerel->rd_rel->reltoastrelid; toast_relid = onerel->rd_rel->reltoastrelid;
else else
toast_relid = InvalidOid; toast_relid = InvalidOid;
@ -1305,7 +1348,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
/* /*
* Do the actual work --- either FULL or "lazy" vacuum * Do the actual work --- either FULL or "lazy" vacuum
*/ */
if (vacstmt->options & VACOPT_FULL) if (options & VACOPT_FULL)
{ {
/* close relation before vacuuming, but hold lock until commit */ /* close relation before vacuuming, but hold lock until commit */
relation_close(onerel, NoLock); relation_close(onerel, NoLock);
@ -1313,10 +1356,10 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
/* VACUUM FULL is now a variant of CLUSTER; see cluster.c */ /* VACUUM FULL is now a variant of CLUSTER; see cluster.c */
cluster_rel(relid, InvalidOid, false, cluster_rel(relid, InvalidOid, false,
(vacstmt->options & VACOPT_VERBOSE) != 0); (options & VACOPT_VERBOSE) != 0);
} }
else else
lazy_vacuum_rel(onerel, vacstmt, vac_strategy); lazy_vacuum_rel(onerel, options, params, vac_strategy);
/* Roll back any GUC changes executed by index functions */ /* Roll back any GUC changes executed by index functions */
AtEOXact_GUC(false, save_nestlevel); AtEOXact_GUC(false, save_nestlevel);
@ -1342,7 +1385,7 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
* totally unimportant for toast relations. * totally unimportant for toast relations.
*/ */
if (toast_relid != InvalidOid) if (toast_relid != InvalidOid)
vacuum_rel(toast_relid, vacstmt, false, for_wraparound); vacuum_rel(toast_relid, relation, options, params);
/* /*
* Now release the session-level lock on the master table. * Now release the session-level lock on the master table.

View File

@ -169,7 +169,7 @@ static bool heap_page_is_all_visible(Relation rel, Buffer buf,
* and locked the relation. * and locked the relation.
*/ */
void void
lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params,
BufferAccessStrategy bstrategy) BufferAccessStrategy bstrategy)
{ {
LVRelStats *vacrelstats; LVRelStats *vacrelstats;
@ -193,6 +193,8 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
TransactionId new_frozen_xid; TransactionId new_frozen_xid;
MultiXactId new_min_multi; MultiXactId new_min_multi;
Assert(params != NULL);
/* measure elapsed time iff autovacuum logging requires it */ /* measure elapsed time iff autovacuum logging requires it */
if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0) if (IsAutoVacuumWorkerProcess() && Log_autovacuum_min_duration >= 0)
{ {
@ -200,7 +202,7 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
starttime = GetCurrentTimestamp(); starttime = GetCurrentTimestamp();
} }
if (vacstmt->options & VACOPT_VERBOSE) if (options & VACOPT_VERBOSE)
elevel = INFO; elevel = INFO;
else else
elevel = DEBUG2; elevel = DEBUG2;
@ -208,9 +210,10 @@ lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt,
vac_strategy = bstrategy; vac_strategy = bstrategy;
vacuum_set_xid_limits(onerel, vacuum_set_xid_limits(onerel,
vacstmt->freeze_min_age, vacstmt->freeze_table_age, params->freeze_min_age,
vacstmt->multixact_freeze_min_age, params->freeze_table_age,
vacstmt->multixact_freeze_table_age, params->multixact_freeze_min_age,
params->multixact_freeze_table_age,
&OldestXmin, &FreezeLimit, &xidFullScanLimit, &OldestXmin, &FreezeLimit, &xidFullScanLimit,
&MultiXactCutoff, &mxactFullScanLimit); &MultiXactCutoff, &mxactFullScanLimit);

View File

@ -3300,10 +3300,6 @@ _copyVacuumStmt(const VacuumStmt *from)
VacuumStmt *newnode = makeNode(VacuumStmt); VacuumStmt *newnode = makeNode(VacuumStmt);
COPY_SCALAR_FIELD(options); COPY_SCALAR_FIELD(options);
COPY_SCALAR_FIELD(freeze_min_age);
COPY_SCALAR_FIELD(freeze_table_age);
COPY_SCALAR_FIELD(multixact_freeze_min_age);
COPY_SCALAR_FIELD(multixact_freeze_table_age);
COPY_NODE_FIELD(relation); COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(va_cols); COPY_NODE_FIELD(va_cols);

View File

@ -1503,10 +1503,6 @@ static bool
_equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b) _equalVacuumStmt(const VacuumStmt *a, const VacuumStmt *b)
{ {
COMPARE_SCALAR_FIELD(options); COMPARE_SCALAR_FIELD(options);
COMPARE_SCALAR_FIELD(freeze_min_age);
COMPARE_SCALAR_FIELD(freeze_table_age);
COMPARE_SCALAR_FIELD(multixact_freeze_min_age);
COMPARE_SCALAR_FIELD(multixact_freeze_table_age);
COMPARE_NODE_FIELD(relation); COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(va_cols); COMPARE_NODE_FIELD(va_cols);

View File

@ -9034,12 +9034,10 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
n->options = VACOPT_VACUUM; n->options = VACOPT_VACUUM;
if ($2) if ($2)
n->options |= VACOPT_FULL; n->options |= VACOPT_FULL;
if ($3)
n->options |= VACOPT_FREEZE;
if ($4) if ($4)
n->options |= VACOPT_VERBOSE; n->options |= VACOPT_VERBOSE;
n->freeze_min_age = $3 ? 0 : -1;
n->freeze_table_age = $3 ? 0 : -1;
n->multixact_freeze_min_age = $3 ? 0 : -1;
n->multixact_freeze_table_age = $3 ? 0 : -1;
n->relation = NULL; n->relation = NULL;
n->va_cols = NIL; n->va_cols = NIL;
$$ = (Node *)n; $$ = (Node *)n;
@ -9050,12 +9048,10 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
n->options = VACOPT_VACUUM; n->options = VACOPT_VACUUM;
if ($2) if ($2)
n->options |= VACOPT_FULL; n->options |= VACOPT_FULL;
if ($3)
n->options |= VACOPT_FREEZE;
if ($4) if ($4)
n->options |= VACOPT_VERBOSE; n->options |= VACOPT_VERBOSE;
n->freeze_min_age = $3 ? 0 : -1;
n->freeze_table_age = $3 ? 0 : -1;
n->multixact_freeze_min_age = $3 ? 0 : -1;
n->multixact_freeze_table_age = $3 ? 0 : -1;
n->relation = $5; n->relation = $5;
n->va_cols = NIL; n->va_cols = NIL;
$$ = (Node *)n; $$ = (Node *)n;
@ -9066,30 +9062,16 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
n->options |= VACOPT_VACUUM; n->options |= VACOPT_VACUUM;
if ($2) if ($2)
n->options |= VACOPT_FULL; n->options |= VACOPT_FULL;
if ($3)
n->options |= VACOPT_FREEZE;
if ($4) if ($4)
n->options |= VACOPT_VERBOSE; n->options |= VACOPT_VERBOSE;
n->freeze_min_age = $3 ? 0 : -1;
n->freeze_table_age = $3 ? 0 : -1;
n->multixact_freeze_min_age = $3 ? 0 : -1;
n->multixact_freeze_table_age = $3 ? 0 : -1;
$$ = (Node *)n; $$ = (Node *)n;
} }
| VACUUM '(' vacuum_option_list ')' | VACUUM '(' vacuum_option_list ')'
{ {
VacuumStmt *n = makeNode(VacuumStmt); VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_VACUUM | $3; n->options = VACOPT_VACUUM | $3;
if (n->options & VACOPT_FREEZE)
{
n->freeze_min_age = n->freeze_table_age = 0;
n->multixact_freeze_min_age = 0;
n->multixact_freeze_table_age = 0;
}
else
{
n->freeze_min_age = n->freeze_table_age = -1;
n->multixact_freeze_min_age = -1;
n->multixact_freeze_table_age = -1;
}
n->relation = NULL; n->relation = NULL;
n->va_cols = NIL; n->va_cols = NIL;
$$ = (Node *) n; $$ = (Node *) n;
@ -9098,18 +9080,6 @@ VacuumStmt: VACUUM opt_full opt_freeze opt_verbose
{ {
VacuumStmt *n = makeNode(VacuumStmt); VacuumStmt *n = makeNode(VacuumStmt);
n->options = VACOPT_VACUUM | $3; n->options = VACOPT_VACUUM | $3;
if (n->options & VACOPT_FREEZE)
{
n->freeze_min_age = n->freeze_table_age = 0;
n->multixact_freeze_min_age = 0;
n->multixact_freeze_table_age = 0;
}
else
{
n->freeze_min_age = n->freeze_table_age = -1;
n->multixact_freeze_min_age = -1;
n->multixact_freeze_table_age = -1;
}
n->relation = $5; n->relation = $5;
n->va_cols = $6; n->va_cols = $6;
if (n->va_cols != NIL) /* implies analyze */ if (n->va_cols != NIL) /* implies analyze */
@ -9137,10 +9107,6 @@ AnalyzeStmt:
n->options = VACOPT_ANALYZE; n->options = VACOPT_ANALYZE;
if ($2) if ($2)
n->options |= VACOPT_VERBOSE; n->options |= VACOPT_VERBOSE;
n->freeze_min_age = -1;
n->freeze_table_age = -1;
n->multixact_freeze_min_age = -1;
n->multixact_freeze_table_age = -1;
n->relation = NULL; n->relation = NULL;
n->va_cols = NIL; n->va_cols = NIL;
$$ = (Node *)n; $$ = (Node *)n;
@ -9151,10 +9117,6 @@ AnalyzeStmt:
n->options = VACOPT_ANALYZE; n->options = VACOPT_ANALYZE;
if ($2) if ($2)
n->options |= VACOPT_VERBOSE; n->options |= VACOPT_VERBOSE;
n->freeze_min_age = -1;
n->freeze_table_age = -1;
n->multixact_freeze_min_age = -1;
n->multixact_freeze_table_age = -1;
n->relation = $3; n->relation = $3;
n->va_cols = $4; n->va_cols = $4;
$$ = (Node *)n; $$ = (Node *)n;

View File

@ -184,16 +184,11 @@ typedef struct av_relation
typedef struct autovac_table typedef struct autovac_table
{ {
Oid at_relid; Oid at_relid;
bool at_dovacuum; int at_vacoptions; /* bitmask of VacuumOption */
bool at_doanalyze; VacuumParams at_params;
int at_freeze_min_age;
int at_freeze_table_age;
int at_multixact_freeze_min_age;
int at_multixact_freeze_table_age;
int at_vacuum_cost_delay; int at_vacuum_cost_delay;
int at_vacuum_cost_limit; int at_vacuum_cost_limit;
bool at_dobalance; bool at_dobalance;
bool at_wraparound;
char *at_relname; char *at_relname;
char *at_nspname; char *at_nspname;
char *at_datname; char *at_datname;
@ -2301,7 +2296,7 @@ do_autovacuum(void)
* next table in our list. * next table in our list.
*/ */
HOLD_INTERRUPTS(); HOLD_INTERRUPTS();
if (tab->at_dovacuum) if (tab->at_vacoptions & VACOPT_VACUUM)
errcontext("automatic vacuum of table \"%s.%s.%s\"", errcontext("automatic vacuum of table \"%s.%s.%s\"",
tab->at_datname, tab->at_nspname, tab->at_relname); tab->at_datname, tab->at_nspname, tab->at_relname);
else else
@ -2528,15 +2523,17 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
tab = palloc(sizeof(autovac_table)); tab = palloc(sizeof(autovac_table));
tab->at_relid = relid; tab->at_relid = relid;
tab->at_dovacuum = dovacuum; tab->at_vacoptions = VACOPT_SKIPTOAST |
tab->at_doanalyze = doanalyze; (dovacuum ? VACOPT_VACUUM : 0) |
tab->at_freeze_min_age = freeze_min_age; (doanalyze ? VACOPT_ANALYZE : 0) |
tab->at_freeze_table_age = freeze_table_age; (wraparound ? VACOPT_NOWAIT : 0);
tab->at_multixact_freeze_min_age = multixact_freeze_min_age; tab->at_params.freeze_min_age = freeze_min_age;
tab->at_multixact_freeze_table_age = multixact_freeze_table_age; tab->at_params.freeze_table_age = freeze_table_age;
tab->at_params.multixact_freeze_min_age = multixact_freeze_min_age;
tab->at_params.multixact_freeze_table_age = multixact_freeze_table_age;
tab->at_params.is_wraparound = wraparound;
tab->at_vacuum_cost_limit = vac_cost_limit; tab->at_vacuum_cost_limit = vac_cost_limit;
tab->at_vacuum_cost_delay = vac_cost_delay; tab->at_vacuum_cost_delay = vac_cost_delay;
tab->at_wraparound = wraparound;
tab->at_relname = NULL; tab->at_relname = NULL;
tab->at_nspname = NULL; tab->at_nspname = NULL;
tab->at_datname = NULL; tab->at_datname = NULL;
@ -2737,39 +2734,22 @@ relation_needs_vacanalyze(Oid relid,
* Vacuum and/or analyze the specified table * Vacuum and/or analyze the specified table
*/ */
static void static void
autovacuum_do_vac_analyze(autovac_table *tab, autovacuum_do_vac_analyze(autovac_table *tab, BufferAccessStrategy bstrategy)
BufferAccessStrategy bstrategy)
{ {
VacuumStmt vacstmt; RangeVar rangevar;
RangeVar rangevar;
/* Set up command parameters --- use local variables instead of palloc */ /* Set up command parameters --- use local variables instead of palloc */
MemSet(&vacstmt, 0, sizeof(vacstmt));
MemSet(&rangevar, 0, sizeof(rangevar)); MemSet(&rangevar, 0, sizeof(rangevar));
rangevar.schemaname = tab->at_nspname; rangevar.schemaname = tab->at_nspname;
rangevar.relname = tab->at_relname; rangevar.relname = tab->at_relname;
rangevar.location = -1; rangevar.location = -1;
vacstmt.type = T_VacuumStmt;
if (!tab->at_wraparound)
vacstmt.options = VACOPT_NOWAIT;
if (tab->at_dovacuum)
vacstmt.options |= VACOPT_VACUUM;
if (tab->at_doanalyze)
vacstmt.options |= VACOPT_ANALYZE;
vacstmt.freeze_min_age = tab->at_freeze_min_age;
vacstmt.freeze_table_age = tab->at_freeze_table_age;
vacstmt.multixact_freeze_min_age = tab->at_multixact_freeze_min_age;
vacstmt.multixact_freeze_table_age = tab->at_multixact_freeze_table_age;
/* we pass the OID, but might need this anyway for an error message */
vacstmt.relation = &rangevar;
vacstmt.va_cols = NIL;
/* Let pgstat know what we're doing */ /* Let pgstat know what we're doing */
autovac_report_activity(tab); autovac_report_activity(tab);
vacuum(&vacstmt, tab->at_relid, false, bstrategy, tab->at_wraparound, true); vacuum(tab->at_vacoptions, &rangevar, tab->at_relid, &tab->at_params, NIL,
bstrategy, true);
} }
/* /*
@ -2791,10 +2771,10 @@ autovac_report_activity(autovac_table *tab)
int len; int len;
/* Report the command and possible options */ /* Report the command and possible options */
if (tab->at_dovacuum) if (tab->at_vacoptions & VACOPT_VACUUM)
snprintf(activity, MAX_AUTOVAC_ACTIV_LEN, snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
"autovacuum: VACUUM%s", "autovacuum: VACUUM%s",
tab->at_doanalyze ? " ANALYZE" : ""); tab->at_vacoptions & VACOPT_ANALYZE ? " ANALYZE" : "");
else else
snprintf(activity, MAX_AUTOVAC_ACTIV_LEN, snprintf(activity, MAX_AUTOVAC_ACTIV_LEN,
"autovacuum: ANALYZE"); "autovacuum: ANALYZE");
@ -2806,7 +2786,7 @@ autovac_report_activity(autovac_table *tab)
snprintf(activity + len, MAX_AUTOVAC_ACTIV_LEN - len, snprintf(activity + len, MAX_AUTOVAC_ACTIV_LEN - len,
" %s.%s%s", tab->at_nspname, tab->at_relname, " %s.%s%s", tab->at_nspname, tab->at_relname,
tab->at_wraparound ? " (to prevent wraparound)" : ""); tab->at_params.is_wraparound ? " (to prevent wraparound)" : "");
/* Set statement_timestamp() to current time for pg_stat_activity */ /* Set statement_timestamp() to current time for pg_stat_activity */
SetCurrentStatementStartTimestamp(); SetCurrentStatementStartTimestamp();

View File

@ -627,7 +627,7 @@ standard_ProcessUtility(Node *parsetree,
/* we choose to allow this during "read only" transactions */ /* we choose to allow this during "read only" transactions */
PreventCommandDuringRecovery((stmt->options & VACOPT_VACUUM) ? PreventCommandDuringRecovery((stmt->options & VACOPT_VACUUM) ?
"VACUUM" : "ANALYZE"); "VACUUM" : "ANALYZE");
vacuum(stmt, InvalidOid, true, NULL, false, isTopLevel); ExecVacuum(stmt, isTopLevel);
} }
break; break;

View File

@ -130,6 +130,19 @@ typedef struct VacAttrStats
int rowstride; int rowstride;
} VacAttrStats; } VacAttrStats;
/*
* Parameters customizing behavior of VACUUM and ANALYZE.
*/
typedef struct VacuumParams
{
int freeze_min_age; /* min freeze age, -1 to use default */
int freeze_table_age; /* age at which to scan whole table */
int multixact_freeze_min_age; /* min multixact freeze age,
* -1 to use default */
int multixact_freeze_table_age; /* multixact age at which to
* scan whole table */
bool is_wraparound; /* force a for-wraparound vacuum */
} VacuumParams;
/* GUC parameters */ /* GUC parameters */
extern PGDLLIMPORT int default_statistics_target; /* PGDLLIMPORT for extern PGDLLIMPORT int default_statistics_target; /* PGDLLIMPORT for
@ -141,8 +154,10 @@ extern int vacuum_multixact_freeze_table_age;
/* in commands/vacuum.c */ /* in commands/vacuum.c */
extern void vacuum(VacuumStmt *vacstmt, Oid relid, bool do_toast, extern void ExecVacuum(VacuumStmt *vacstmt, bool isTopLevel);
BufferAccessStrategy bstrategy, bool for_wraparound, bool isTopLevel); extern void vacuum(int options, RangeVar *relation, Oid relid,
VacuumParams *params, List *va_cols,
BufferAccessStrategy bstrategy, bool isTopLevel);
extern void vac_open_indexes(Relation relation, LOCKMODE lockmode, extern void vac_open_indexes(Relation relation, LOCKMODE lockmode,
int *nindexes, Relation **Irel); int *nindexes, Relation **Irel);
extern void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode); extern void vac_close_indexes(int nindexes, Relation *Irel, LOCKMODE lockmode);
@ -171,12 +186,13 @@ extern void vac_update_datfrozenxid(void);
extern void vacuum_delay_point(void); extern void vacuum_delay_point(void);
/* in commands/vacuumlazy.c */ /* in commands/vacuumlazy.c */
extern void lazy_vacuum_rel(Relation onerel, VacuumStmt *vacstmt, extern void lazy_vacuum_rel(Relation onerel, int options,
BufferAccessStrategy bstrategy); VacuumParams *params, BufferAccessStrategy bstrategy);
/* in commands/analyze.c */ /* in commands/analyze.c */
extern void analyze_rel(Oid relid, VacuumStmt *vacstmt, extern void analyze_rel(Oid relid, RangeVar *relation, int options,
bool in_outer_xact, BufferAccessStrategy bstrategy); List *va_cols, bool in_outer_xact,
BufferAccessStrategy bstrategy);
extern bool std_typanalyze(VacAttrStats *stats); extern bool std_typanalyze(VacAttrStats *stats);
extern double anl_random_fract(void); extern double anl_random_fract(void);
extern double anl_init_selection_state(int n); extern double anl_init_selection_state(int n);

View File

@ -2608,9 +2608,7 @@ typedef struct ClusterStmt
* *
* Even though these are nominally two statements, it's convenient to use * Even though these are nominally two statements, it's convenient to use
* just one node type for both. Note that at least one of VACOPT_VACUUM * just one node type for both. Note that at least one of VACOPT_VACUUM
* and VACOPT_ANALYZE must be set in options. VACOPT_FREEZE is an internal * and VACOPT_ANALYZE must be set in options.
* convenience for the grammar and is not examined at runtime --- the
* freeze_min_age and freeze_table_age fields are what matter.
* ---------------------- * ----------------------
*/ */
typedef enum VacuumOption typedef enum VacuumOption
@ -2620,19 +2618,14 @@ typedef enum VacuumOption
VACOPT_VERBOSE = 1 << 2, /* print progress info */ VACOPT_VERBOSE = 1 << 2, /* print progress info */
VACOPT_FREEZE = 1 << 3, /* FREEZE option */ VACOPT_FREEZE = 1 << 3, /* FREEZE option */
VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */ VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */
VACOPT_NOWAIT = 1 << 5 /* don't wait to get lock (autovacuum only) */ VACOPT_NOWAIT = 1 << 5, /* don't wait to get lock (autovacuum only) */
VACOPT_SKIPTOAST = 1 << 6 /* don't process the TOAST table, if any */
} VacuumOption; } VacuumOption;
typedef struct VacuumStmt typedef struct VacuumStmt
{ {
NodeTag type; NodeTag type;
int options; /* OR of VacuumOption flags */ int options; /* OR of VacuumOption flags */
int freeze_min_age; /* min freeze age, or -1 to use default */
int freeze_table_age; /* age at which to scan whole table */
int multixact_freeze_min_age; /* min multixact freeze age,
* or -1 to use default */
int multixact_freeze_table_age; /* multixact age at which to
* scan whole table */
RangeVar *relation; /* single table to process, or NULL */ RangeVar *relation; /* single table to process, or NULL */
List *va_cols; /* list of column names, or NIL for all */ List *va_cols; /* list of column names, or NIL for all */
} VacuumStmt; } VacuumStmt;