diff --git a/doc/src/sgml/ref/vacuum.sgml b/doc/src/sgml/ref/vacuum.sgml index 19fd748dde0b523be578405479a2c23384f6b1f3..dee1c5bad31cb76aff5859a1c2d55e592f32a4de 100644 --- a/doc/src/sgml/ref/vacuum.sgml +++ b/doc/src/sgml/ref/vacuum.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation <refsynopsisdiv> <synopsis> -VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE } [, ...] ) ] [ <replaceable class="PARAMETER">table_name</replaceable> [ (<replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ] ] +VACUUM [ ( { FULL | FREEZE | VERBOSE | ANALYZE | DISABLE_PAGE_SKIPPING } [, ...] ) ] [ <replaceable class="PARAMETER">table_name</replaceable> [ (<replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ] ] VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ <replaceable class="PARAMETER">table_name</replaceable> ] VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER">table_name</replaceable> [ (<replaceable class="PARAMETER">column_name</replaceable> [, ...] ) ] ] </synopsis> @@ -129,6 +129,25 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] ANALYZE [ <replaceable class="PARAMETER"> </listitem> </varlistentry> + <varlistentry> + <term><literal>DISABLE_PAGE_SKIPPING</literal></term> + <listitem> + <para> + Normally, <command>VACUUM</> will skip pages based on the <link + linkend="vacuum-for-visibility-map">visibility map</>. Pages where + all tuples are known to be frozen can always be skipped, and those + where all tuples are known to be visible to all transactions may be + skipped except when performing an aggressive vacuum. Furthermore, + except when performing an aggressive vacuum, some pages may be skipped + in order to avoid waiting for other sessions to finish using them. + This option disables all page-skipping behavior, and is intended to + be used only the contents of the visibility map are thought to + be suspect, which should happen only if there is a hardware or software + issue causing database corruption. + </para> + </listitem> + </varlistentry> + <varlistentry> <term><replaceable class="PARAMETER">table_name</replaceable></term> <listitem> diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 25a55ab0a5a8a4662a9a8809cf429782871e8094..0563e6347430d43c6cd5d4be5c8456fcc3406946 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -185,6 +185,15 @@ vacuum(int options, RangeVar *relation, Oid relid, VacuumParams *params, errmsg("%s cannot be executed from VACUUM or ANALYZE", stmttype))); + /* + * Sanity check DISABLE_PAGE_SKIPPING option. + */ + if ((options & VACOPT_FULL) != 0 && + (options & VACOPT_DISABLE_PAGE_SKIPPING) != 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("VACUUM option DISABLE_PAGE_SKIPPING cannot be used with FULL"))); + /* * Send info about dead objects to the statistics collector, unless we are * in autovacuum --- autovacuum.c does this for itself. diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c index cb5777ffdf9310d626521599c5fa519edd299d2c..32b6fddc6220c63a6287f1e789241e28aaf6cf11 100644 --- a/src/backend/commands/vacuumlazy.c +++ b/src/backend/commands/vacuumlazy.c @@ -137,8 +137,9 @@ static BufferAccessStrategy vac_strategy; /* non-export function prototypes */ -static void lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, - Relation *Irel, int nindexes, bool aggressive); +static void lazy_scan_heap(Relation onerel, int options, + LVRelStats *vacrelstats, Relation *Irel, int nindexes, + bool aggressive); static void lazy_vacuum_heap(Relation onerel, LVRelStats *vacrelstats); static bool lazy_check_needs_freeze(Buffer buf, bool *hastup); static void lazy_vacuum_index(Relation indrel, @@ -223,15 +224,17 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params, &MultiXactCutoff, &mxactFullScanLimit); /* - * We request an aggressive scan if either the table's frozen Xid is now - * older than or equal to the requested Xid full-table scan limit; or if - * the table's minimum MultiXactId is older than or equal to the requested - * mxid full-table scan limit. + * We request an aggressive scan if the table's frozen Xid is now older + * than or equal to the requested Xid full-table scan limit; or if the + * table's minimum MultiXactId is older than or equal to the requested + * mxid full-table scan limit; or if DISABLE_PAGE_SKIPPING was specified. */ aggressive = TransactionIdPrecedesOrEquals(onerel->rd_rel->relfrozenxid, xidFullScanLimit); aggressive |= MultiXactIdPrecedesOrEquals(onerel->rd_rel->relminmxid, mxactFullScanLimit); + if (options & VACOPT_DISABLE_PAGE_SKIPPING) + aggressive = true; vacrelstats = (LVRelStats *) palloc0(sizeof(LVRelStats)); @@ -246,7 +249,7 @@ lazy_vacuum_rel(Relation onerel, int options, VacuumParams *params, vacrelstats->hasindex = (nindexes > 0); /* Do the vacuuming */ - lazy_scan_heap(onerel, vacrelstats, Irel, nindexes, aggressive); + lazy_scan_heap(onerel, options, vacrelstats, Irel, nindexes, aggressive); /* Done with indexes */ vac_close_indexes(nindexes, Irel, NoLock); @@ -441,7 +444,7 @@ vacuum_log_cleanup_info(Relation rel, LVRelStats *vacrelstats) * reference them have been killed. */ static void -lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, +lazy_scan_heap(Relation onerel, int options, LVRelStats *vacrelstats, Relation *Irel, int nindexes, bool aggressive) { BlockNumber nblocks, @@ -542,25 +545,28 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, * the last page. This is worth avoiding mainly because such a lock must * be replayed on any hot standby, where it can be disruptive. */ - for (next_unskippable_block = 0; - next_unskippable_block < nblocks; - next_unskippable_block++) + next_unskippable_block = 0; + if ((options & VACOPT_DISABLE_PAGE_SKIPPING) == 0) { - uint8 vmstatus; - - vmstatus = visibilitymap_get_status(onerel, next_unskippable_block, - &vmbuffer); - if (aggressive) + while (next_unskippable_block < nblocks) { - if ((vmstatus & VISIBILITYMAP_ALL_FROZEN) == 0) - break; - } - else - { - if ((vmstatus & VISIBILITYMAP_ALL_VISIBLE) == 0) - break; + uint8 vmstatus; + + vmstatus = visibilitymap_get_status(onerel, next_unskippable_block, + &vmbuffer); + if (aggressive) + { + if ((vmstatus & VISIBILITYMAP_ALL_FROZEN) == 0) + break; + } + else + { + if ((vmstatus & VISIBILITYMAP_ALL_VISIBLE) == 0) + break; + } + vacuum_delay_point(); + next_unskippable_block++; } - vacuum_delay_point(); } if (next_unskippable_block >= SKIP_PAGES_THRESHOLD) @@ -594,26 +600,29 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, if (blkno == next_unskippable_block) { /* Time to advance next_unskippable_block */ - for (next_unskippable_block++; - next_unskippable_block < nblocks; - next_unskippable_block++) + next_unskippable_block++; + if ((options & VACOPT_DISABLE_PAGE_SKIPPING) == 0) { - uint8 vmskipflags; - - vmskipflags = visibilitymap_get_status(onerel, - next_unskippable_block, - &vmbuffer); - if (aggressive) + while (next_unskippable_block < nblocks) { - if ((vmskipflags & VISIBILITYMAP_ALL_FROZEN) == 0) - break; - } - else - { - if ((vmskipflags & VISIBILITYMAP_ALL_VISIBLE) == 0) - break; + uint8 vmskipflags; + + vmskipflags = visibilitymap_get_status(onerel, + next_unskippable_block, + &vmbuffer); + if (aggressive) + { + if ((vmskipflags & VISIBILITYMAP_ALL_FROZEN) == 0) + break; + } + else + { + if ((vmskipflags & VISIBILITYMAP_ALL_VISIBLE) == 0) + break; + } + vacuum_delay_point(); + next_unskippable_block++; } - vacuum_delay_point(); } /* @@ -1054,7 +1063,7 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, } else { - bool tuple_totally_frozen; + bool tuple_totally_frozen; num_tuples += 1; hastup = true; @@ -1064,8 +1073,8 @@ lazy_scan_heap(Relation onerel, LVRelStats *vacrelstats, * freezing. Note we already have exclusive buffer lock. */ if (heap_prepare_freeze_tuple(tuple.t_data, FreezeLimit, - MultiXactCutoff, &frozen[nfrozen], - &tuple_totally_frozen)) + MultiXactCutoff, &frozen[nfrozen], + &tuple_totally_frozen)) frozen[nfrozen++].offset = offnum; if (!tuple_totally_frozen) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 2c950f937c49a1513867c590592ba412726c023f..edf4516dacd55c0f4859eae6a00ecbb23c2c2d31 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -9370,6 +9370,16 @@ vacuum_option_elem: | VERBOSE { $$ = VACOPT_VERBOSE; } | FREEZE { $$ = VACOPT_FREEZE; } | FULL { $$ = VACOPT_FULL; } + | IDENT + { + if (strcmp($1, "disable_page_skipping") == 0) + $$ = VACOPT_DISABLE_PAGE_SKIPPING; + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized VACUUM option \"%s\"", $1), + parser_errposition(@1))); + } ; AnalyzeStmt: diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 714cf1550fd1e158cd88786c2e8576dccd18eeb4..d36d9c6d01967ae66741367422fc641784086bcd 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -2822,7 +2822,8 @@ typedef enum VacuumOption VACOPT_FREEZE = 1 << 3, /* FREEZE option */ VACOPT_FULL = 1 << 4, /* FULL (non-concurrent) vacuum */ VACOPT_NOWAIT = 1 << 5, /* don't wait to get lock (autovacuum only) */ - VACOPT_SKIPTOAST = 1 << 6 /* don't process the TOAST table, if any */ + VACOPT_SKIPTOAST = 1 << 6, /* don't process the TOAST table, if any */ + VACOPT_DISABLE_PAGE_SKIPPING = 1 << 7 /* don't skip any pages */ } VacuumOption; typedef struct VacuumStmt diff --git a/src/test/regress/expected/vacuum.out b/src/test/regress/expected/vacuum.out index d2d750382864581da834bdc2871639bfb6227fe6..9b604be4b62e1b472bdc60a31b5c3b19b67f227c 100644 --- a/src/test/regress/expected/vacuum.out +++ b/src/test/regress/expected/vacuum.out @@ -79,5 +79,6 @@ ERROR: ANALYZE cannot be executed from VACUUM or ANALYZE CONTEXT: SQL function "do_analyze" statement 1 SQL function "wrap_do_analyze" statement 1 VACUUM FULL vactst; +VACUUM (DISABLE_PAGE_SKIPPING) vaccluster; DROP TABLE vaccluster; DROP TABLE vactst; diff --git a/src/test/regress/sql/vacuum.sql b/src/test/regress/sql/vacuum.sql index f8412016cf3fc23bad621f5760e3126ea312e0a0..7b819f654ddf51f0a53fb39de0726bc6bbc50d9f 100644 --- a/src/test/regress/sql/vacuum.sql +++ b/src/test/regress/sql/vacuum.sql @@ -60,5 +60,7 @@ VACUUM FULL pg_database; VACUUM FULL vaccluster; VACUUM FULL vactst; +VACUUM (DISABLE_PAGE_SKIPPING) vaccluster; + DROP TABLE vaccluster; DROP TABLE vactst;