Support disabling index bypassing by VACUUM.

Generalize the INDEX_CLEANUP VACUUM parameter (and the corresponding
reloption): make it into a ternary style boolean parameter.  It now
exposes a third option, "auto".  The "auto" option (which is now the
default) enables the "bypass index vacuuming" optimization added by
commit 1e55e7d1.

"VACUUM (INDEX_CLEANUP TRUE)" is redefined to once again make VACUUM
simply do any required index vacuuming, regardless of how few dead
tuples are encountered during the first scan of the target heap relation
(unless there are exactly zero).  This gives users a way of opting out
of the "bypass index vacuuming" optimization, if for whatever reason
that proves necessary.  It is also expected to be used by PostgreSQL
developers as a testing option from time to time.

"VACUUM (INDEX_CLEANUP FALSE)" does the same thing as it always has: it
forcibly disables both index vacuuming and index cleanup.  It's not
expected to be used much in PostgreSQL 14.  The failsafe mechanism added
by commit 1e55e7d1 addresses the same problem in a simpler way.
INDEX_CLEANUP can now be thought of as a testing and compatibility
option.

Author: Peter Geoghegan <pg@bowt.ie>
Reviewed-By: Masahiko Sawada <sawada.mshk@gmail.com>
Reviewed-By: Justin Pryzby <pryzby@telsasoft.com>
Discussion: https://postgr.es/m/CAH2-WznrBoCST4_Gxh_G9hA8NzGUbeBGnOUC8FcXcrhqsv6OHQ@mail.gmail.com
This commit is contained in:
Peter Geoghegan 2021-06-18 20:04:07 -07:00
parent 09126984a2
commit 3499df0dee
13 changed files with 313 additions and 127 deletions

View File

@ -1473,20 +1473,27 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</varlistentry> </varlistentry>
<varlistentry id="reloption-vacuum-index-cleanup" xreflabel="vacuum_index_cleanup"> <varlistentry id="reloption-vacuum-index-cleanup" xreflabel="vacuum_index_cleanup">
<term><literal>vacuum_index_cleanup</literal>, <literal>toast.vacuum_index_cleanup</literal> (<type>boolean</type>) <term><literal>vacuum_index_cleanup</literal>, <literal>toast.vacuum_index_cleanup</literal> (<type>enum</type>)
<indexterm> <indexterm>
<primary><varname>vacuum_index_cleanup</varname> storage parameter</primary> <primary><varname>vacuum_index_cleanup</varname> storage parameter</primary>
</indexterm> </indexterm>
</term> </term>
<listitem> <listitem>
<para> <para>
Enables or disables index cleanup when <command>VACUUM</command> is Forces or disables index cleanup when <command>VACUUM</command>
run on this table. The default value is <literal>true</literal>. is run on this table. The default value is
Disabling index cleanup can speed up <command>VACUUM</command> very <literal>AUTO</literal>. With <literal>OFF</literal>, index
significantly, but may also lead to severely bloated indexes if table cleanup is disabled, with <literal>ON</literal> it is enabled,
modifications are frequent. The <literal>INDEX_CLEANUP</literal> and with <literal>AUTO</literal> a decision is made dynamically,
parameter of <link linkend="sql-vacuum"><command>VACUUM</command></link>, if specified, overrides each time <command>VACUUM</command> runs. The dynamic behavior
the value of this option. allows <command>VACUUM</command> to avoid needlessly scanning
indexes to remove very few dead tuples. Forcibly disabling all
index cleanup can speed up <command>VACUUM</command> very
significantly, but may also lead to severely bloated indexes if
table modifications are frequent. The
<literal>INDEX_CLEANUP</literal> parameter of <link
linkend="sql-vacuum"><command>VACUUM</command></link>, if
specified, overrides the value of this option.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>

View File

@ -32,7 +32,7 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet
ANALYZE [ <replaceable class="parameter">boolean</replaceable> ] ANALYZE [ <replaceable class="parameter">boolean</replaceable> ]
DISABLE_PAGE_SKIPPING [ <replaceable class="parameter">boolean</replaceable> ] DISABLE_PAGE_SKIPPING [ <replaceable class="parameter">boolean</replaceable> ]
SKIP_LOCKED [ <replaceable class="parameter">boolean</replaceable> ] SKIP_LOCKED [ <replaceable class="parameter">boolean</replaceable> ]
INDEX_CLEANUP [ <replaceable class="parameter">boolean</replaceable> ] INDEX_CLEANUP { AUTO | ON | OFF }
PROCESS_TOAST [ <replaceable class="parameter">boolean</replaceable> ] PROCESS_TOAST [ <replaceable class="parameter">boolean</replaceable> ]
TRUNCATE [ <replaceable class="parameter">boolean</replaceable> ] TRUNCATE [ <replaceable class="parameter">boolean</replaceable> ]
PARALLEL <replaceable class="parameter">integer</replaceable> PARALLEL <replaceable class="parameter">integer</replaceable>
@ -193,20 +193,45 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet
<term><literal>INDEX_CLEANUP</literal></term> <term><literal>INDEX_CLEANUP</literal></term>
<listitem> <listitem>
<para> <para>
Specifies that <command>VACUUM</command> should attempt to remove Normally, <command>VACUUM</command> will skip index vacuuming
index entries pointing to dead tuples. This is normally the desired when there are very few dead tuples in the table. The cost of
behavior and is the default unless the processing all of the table's indexes is expected to greatly
<literal>vacuum_index_cleanup</literal> option has been set to false exceed the benefit of removing dead index tuples when this
for the table to be vacuumed. Setting this option to false may be happens. This option can be used to force
useful when it is necessary to make vacuum run as quickly as possible, <command>VACUUM</command> to process indexes when there are more
for example to avoid imminent transaction ID wraparound than zero dead tuples. The default is <literal>AUTO</literal>,
(see <xref linkend="vacuum-for-wraparound"/>). However, if index which allows <command>VACUUM</command> to skip index vacuuming
cleanup is not performed regularly, performance may suffer, because when appropriate. If <literal>INDEX_CLEANUP</literal> is set to
as the table is modified, indexes will accumulate dead tuples <literal>ON</literal>, <command>VACUUM</command> will
and the table itself will accumulate dead line pointers that cannot be conservatively remove all dead tuples from indexes. This may be
removed until index cleanup is completed. This option has no effect useful for backwards compatibility with earlier releases of
for tables that do not have an index and is ignored if the <productname>PostgreSQL</productname> where this was the
<literal>FULL</literal> option is used. standard behavior.
</para>
<para>
<literal>INDEX_CLEANUP</literal> can also be set to
<literal>OFF</literal> to force <command>VACUUM</command> to
<emphasis>always</emphasis> skip index vacuuming, even when
there are many dead tuples in the table. This may be useful
when it is necessary to make <command>VACUUM</command> run as
quickly as possible to avoid imminent transaction ID wraparound
(see <xref linkend="vacuum-for-wraparound"/>). However, the
wraparound failsafe mechanism controlled by <xref
linkend="guc-vacuum-failsafe-age"/> will generally trigger
automatically to avoid transaction ID wraparound failure, and
should be preferred. If index cleanup is not performed
regularly, performance may suffer, because as the table is
modified indexes will accumulate dead tuples and the table
itself will accumulate dead line pointers that cannot be removed
until index cleanup is completed.
</para>
<para>
This option has no effect for tables that have no index and is
ignored if the <literal>FULL</literal> option is used. It also
has no effect on the transaction ID wraparound failsafe
mechanism. When triggered it will skip index vacuuming, even
when <literal>INDEX_CLEANUP</literal> is set to
<literal>ON</literal>.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
@ -217,7 +242,7 @@ VACUUM [ FULL ] [ FREEZE ] [ VERBOSE ] [ ANALYZE ] [ <replaceable class="paramet
<para> <para>
Specifies that <command>VACUUM</command> should attempt to process the Specifies that <command>VACUUM</command> should attempt to process the
corresponding <literal>TOAST</literal> table for each relation, if one corresponding <literal>TOAST</literal> table for each relation, if one
exists. This is normally the desired behavior and is the default. exists. This is usually the desired behavior and is the default.
Setting this option to false may be useful when it is only necessary to Setting this option to false may be useful when it is only necessary to
vacuum the main relation. This option is required when the vacuum the main relation. This option is required when the
<literal>FULL</literal> option is used. <literal>FULL</literal> option is used.

View File

@ -244,6 +244,21 @@ PostgreSQL documentation
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>--force-index-cleanup</option></term>
<listitem>
<para>
Always remove index entries pointing to dead tuples.
</para>
<note>
<para>
This option is only available for servers running
<productname>PostgreSQL</productname> 12 and later.
</para>
</note>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>--no-process-toast</option></term> <term><option>--no-process-toast</option></term>
<listitem> <listitem>

View File

@ -140,15 +140,6 @@ static relopt_bool boolRelOpts[] =
}, },
false false
}, },
{
{
"vacuum_index_cleanup",
"Enables index vacuuming and index cleanup",
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
ShareUpdateExclusiveLock
},
true
},
{ {
{ {
"vacuum_truncate", "vacuum_truncate",
@ -474,6 +465,21 @@ static relopt_real realRelOpts[] =
{{NULL}} {{NULL}}
}; };
/* values from StdRdOptIndexCleanup */
relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
{"off", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
{"true", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
{"false", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
{"yes", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
{"no", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
{"1", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
{"0", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
{(const char *) NULL} /* list terminator */
};
/* values from GistOptBufferingMode */ /* values from GistOptBufferingMode */
relopt_enum_elt_def gistBufferingOptValues[] = relopt_enum_elt_def gistBufferingOptValues[] =
{ {
@ -494,6 +500,17 @@ relopt_enum_elt_def viewCheckOptValues[] =
static relopt_enum enumRelOpts[] = static relopt_enum enumRelOpts[] =
{ {
{
{
"vacuum_index_cleanup",
"Controls index vacuuming and index cleanup",
RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
ShareUpdateExclusiveLock
},
StdRdOptIndexCleanupValues,
STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO,
gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
},
{ {
{ {
"buffering", "buffering",

View File

@ -308,11 +308,16 @@ typedef struct LVRelState
Relation rel; Relation rel;
Relation *indrels; Relation *indrels;
int nindexes; int nindexes;
/* Do index vacuuming/cleanup? */
/* Wraparound failsafe has been triggered? */
bool failsafe_active;
/* Consider index vacuuming bypass optimization? */
bool consider_bypass_optimization;
/* Doing index vacuuming, index cleanup, rel truncation? */
bool do_index_vacuuming; bool do_index_vacuuming;
bool do_index_cleanup; bool do_index_cleanup;
/* Wraparound failsafe in effect? (implies !do_index_vacuuming) */ bool do_rel_truncate;
bool do_failsafe;
/* Buffer access strategy and parallel state */ /* Buffer access strategy and parallel state */
BufferAccessStrategy bstrategy; BufferAccessStrategy bstrategy;
@ -405,7 +410,7 @@ static void lazy_scan_prune(LVRelState *vacrel, Buffer buf,
BlockNumber blkno, Page page, BlockNumber blkno, Page page,
GlobalVisState *vistest, GlobalVisState *vistest,
LVPagePruneState *prunestate); LVPagePruneState *prunestate);
static void lazy_vacuum(LVRelState *vacrel, bool onecall); static void lazy_vacuum(LVRelState *vacrel);
static bool lazy_vacuum_all_indexes(LVRelState *vacrel); static bool lazy_vacuum_all_indexes(LVRelState *vacrel);
static void lazy_vacuum_heap_rel(LVRelState *vacrel); static void lazy_vacuum_heap_rel(LVRelState *vacrel);
static int lazy_vacuum_heap_page(LVRelState *vacrel, BlockNumber blkno, static int lazy_vacuum_heap_page(LVRelState *vacrel, BlockNumber blkno,
@ -435,8 +440,7 @@ static IndexBulkDeleteResult *lazy_cleanup_one_index(Relation indrel,
double reltuples, double reltuples,
bool estimated_count, bool estimated_count,
LVRelState *vacrel); LVRelState *vacrel);
static bool should_attempt_truncation(LVRelState *vacrel, static bool should_attempt_truncation(LVRelState *vacrel);
VacuumParams *params);
static void lazy_truncate_heap(LVRelState *vacrel); static void lazy_truncate_heap(LVRelState *vacrel);
static BlockNumber count_nondeletable_pages(LVRelState *vacrel, static BlockNumber count_nondeletable_pages(LVRelState *vacrel,
bool *lock_waiter_detected); bool *lock_waiter_detected);
@ -506,10 +510,6 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
TransactionId FreezeLimit; TransactionId FreezeLimit;
MultiXactId MultiXactCutoff; MultiXactId MultiXactCutoff;
Assert(params != NULL);
Assert(params->index_cleanup != VACOPT_TERNARY_DEFAULT);
Assert(params->truncate != VACOPT_TERNARY_DEFAULT);
/* measure elapsed time iff autovacuum logging requires it */ /* measure elapsed time iff autovacuum logging requires it */
if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0) if (IsAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
{ {
@ -557,14 +557,41 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
vacrel->rel = rel; vacrel->rel = rel;
vac_open_indexes(vacrel->rel, RowExclusiveLock, &vacrel->nindexes, vac_open_indexes(vacrel->rel, RowExclusiveLock, &vacrel->nindexes,
&vacrel->indrels); &vacrel->indrels);
vacrel->failsafe_active = false;
vacrel->consider_bypass_optimization = true;
/*
* The index_cleanup param either disables index vacuuming and cleanup or
* forces it to go ahead when we would otherwise apply the index bypass
* optimization. The default is 'auto', which leaves the final decision
* up to lazy_vacuum().
*
* The truncate param allows user to avoid attempting relation truncation,
* though it can't force truncation to happen.
*/
Assert(params->index_cleanup != VACOPTVALUE_UNSPECIFIED);
Assert(params->truncate != VACOPTVALUE_UNSPECIFIED &&
params->truncate != VACOPTVALUE_AUTO);
vacrel->do_index_vacuuming = true; vacrel->do_index_vacuuming = true;
vacrel->do_index_cleanup = true; vacrel->do_index_cleanup = true;
vacrel->do_failsafe = false; vacrel->do_rel_truncate = (params->truncate != VACOPTVALUE_DISABLED);
if (params->index_cleanup == VACOPT_TERNARY_DISABLED) if (params->index_cleanup == VACOPTVALUE_DISABLED)
{ {
/* Force disable index vacuuming up-front */
vacrel->do_index_vacuuming = false; vacrel->do_index_vacuuming = false;
vacrel->do_index_cleanup = false; vacrel->do_index_cleanup = false;
} }
else if (params->index_cleanup == VACOPTVALUE_ENABLED)
{
/* Force index vacuuming. Note that failsafe can still bypass. */
vacrel->consider_bypass_optimization = false;
}
else
{
/* Default/auto, make all decisions dynamically */
Assert(params->index_cleanup == VACOPTVALUE_AUTO);
}
vacrel->bstrategy = bstrategy; vacrel->bstrategy = bstrategy;
vacrel->old_rel_pages = rel->rd_rel->relpages; vacrel->old_rel_pages = rel->rd_rel->relpages;
vacrel->old_live_tuples = rel->rd_rel->reltuples; vacrel->old_live_tuples = rel->rd_rel->reltuples;
@ -632,7 +659,7 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
/* /*
* Optionally truncate the relation. * Optionally truncate the relation.
*/ */
if (should_attempt_truncation(vacrel, params)) if (should_attempt_truncation(vacrel))
{ {
/* /*
* Update error traceback information. This is the last phase during * Update error traceback information. This is the last phase during
@ -791,7 +818,7 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
{ {
msgfmt = _(" %u pages from table (%.2f%% of total) have %lld dead item identifiers\n"); msgfmt = _(" %u pages from table (%.2f%% of total) have %lld dead item identifiers\n");
if (!vacrel->do_failsafe) if (!vacrel->failsafe_active)
appendStringInfoString(&buf, _("index scan bypassed:")); appendStringInfoString(&buf, _("index scan bypassed:"));
else else
appendStringInfoString(&buf, _("index scan bypassed by failsafe:")); appendStringInfoString(&buf, _("index scan bypassed by failsafe:"));
@ -893,8 +920,7 @@ lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
next_fsm_block_to_vacuum; next_fsm_block_to_vacuum;
PGRUsage ru0; PGRUsage ru0;
Buffer vmbuffer = InvalidBuffer; Buffer vmbuffer = InvalidBuffer;
bool skipping_blocks, bool skipping_blocks;
have_vacuumed_indexes = false;
StringInfoData buf; StringInfoData buf;
const int initprog_index[] = { const int initprog_index[] = {
PROGRESS_VACUUM_PHASE, PROGRESS_VACUUM_PHASE,
@ -1048,7 +1074,7 @@ lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
* scanning of last page. * scanning of last page.
*/ */
#define FORCE_CHECK_PAGE() \ #define FORCE_CHECK_PAGE() \
(blkno == nblocks - 1 && should_attempt_truncation(vacrel, params)) (blkno == nblocks - 1 && should_attempt_truncation(vacrel))
pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno); pgstat_progress_update_param(PROGRESS_VACUUM_HEAP_BLKS_SCANNED, blkno);
@ -1166,8 +1192,8 @@ lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
} }
/* Remove the collected garbage tuples from table and indexes */ /* Remove the collected garbage tuples from table and indexes */
lazy_vacuum(vacrel, false); vacrel->consider_bypass_optimization = false;
have_vacuumed_indexes = true; lazy_vacuum(vacrel);
/* /*
* Vacuum the Free Space Map to make newly-freed space visible on * Vacuum the Free Space Map to make newly-freed space visible on
@ -1579,7 +1605,7 @@ lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
/* If any tuples need to be deleted, perform final vacuum cycle */ /* If any tuples need to be deleted, perform final vacuum cycle */
if (dead_tuples->num_tuples > 0) if (dead_tuples->num_tuples > 0)
lazy_vacuum(vacrel, !have_vacuumed_indexes); lazy_vacuum(vacrel);
/* /*
* Vacuum the remainder of the Free Space Map. We must do this whether or * Vacuum the remainder of the Free Space Map. We must do this whether or
@ -2064,9 +2090,9 @@ retry:
* wraparound. * wraparound.
*/ */
static void static void
lazy_vacuum(LVRelState *vacrel, bool onecall) lazy_vacuum(LVRelState *vacrel)
{ {
bool do_bypass_optimization; bool bypass;
/* Should not end up here with no indexes */ /* Should not end up here with no indexes */
Assert(vacrel->nindexes > 0); Assert(vacrel->nindexes > 0);
@ -2099,8 +2125,8 @@ lazy_vacuum(LVRelState *vacrel, bool onecall)
* It's far easier to ensure that 99%+ of all UPDATEs against a table use * It's far easier to ensure that 99%+ of all UPDATEs against a table use
* HOT through careful tuning. * HOT through careful tuning.
*/ */
do_bypass_optimization = false; bypass = false;
if (onecall && vacrel->rel_pages > 0) if (vacrel->consider_bypass_optimization && vacrel->rel_pages > 0)
{ {
BlockNumber threshold; BlockNumber threshold;
@ -2132,12 +2158,11 @@ lazy_vacuum(LVRelState *vacrel, bool onecall)
* expanded to cover more cases then this may need to be reconsidered. * expanded to cover more cases then this may need to be reconsidered.
*/ */
threshold = (double) vacrel->rel_pages * BYPASS_THRESHOLD_PAGES; threshold = (double) vacrel->rel_pages * BYPASS_THRESHOLD_PAGES;
do_bypass_optimization = bypass = (vacrel->lpdead_item_pages < threshold &&
(vacrel->lpdead_item_pages < threshold && vacrel->lpdead_items < MAXDEADTUPLES(32L * 1024L * 1024L));
vacrel->lpdead_items < MAXDEADTUPLES(32L * 1024L * 1024L));
} }
if (do_bypass_optimization) if (bypass)
{ {
/* /*
* There are almost zero TIDs. Behave as if there were precisely * There are almost zero TIDs. Behave as if there were precisely
@ -2177,7 +2202,7 @@ lazy_vacuum(LVRelState *vacrel, bool onecall)
* vacuuming or heap vacuuming. This VACUUM operation won't end up * vacuuming or heap vacuuming. This VACUUM operation won't end up
* back here again. * back here again.
*/ */
Assert(vacrel->do_failsafe); Assert(vacrel->failsafe_active);
} }
/* /*
@ -2259,7 +2284,7 @@ lazy_vacuum_all_indexes(LVRelState *vacrel)
*/ */
Assert(vacrel->num_index_scans > 0 || Assert(vacrel->num_index_scans > 0 ||
vacrel->dead_tuples->num_tuples == vacrel->lpdead_items); vacrel->dead_tuples->num_tuples == vacrel->lpdead_items);
Assert(allindexes || vacrel->do_failsafe); Assert(allindexes || vacrel->failsafe_active);
/* /*
* Increase and report the number of index scans. * Increase and report the number of index scans.
@ -2580,7 +2605,7 @@ static bool
lazy_check_wraparound_failsafe(LVRelState *vacrel) lazy_check_wraparound_failsafe(LVRelState *vacrel)
{ {
/* Don't warn more than once per VACUUM */ /* Don't warn more than once per VACUUM */
if (vacrel->do_failsafe) if (vacrel->failsafe_active)
return true; return true;
if (unlikely(vacuum_xid_failsafe_check(vacrel->relfrozenxid, if (unlikely(vacuum_xid_failsafe_check(vacrel->relfrozenxid,
@ -2589,9 +2614,12 @@ lazy_check_wraparound_failsafe(LVRelState *vacrel)
Assert(vacrel->do_index_vacuuming); Assert(vacrel->do_index_vacuuming);
Assert(vacrel->do_index_cleanup); Assert(vacrel->do_index_cleanup);
vacrel->failsafe_active = true;
/* Disable index vacuuming, index cleanup, and heap rel truncation */
vacrel->do_index_vacuuming = false; vacrel->do_index_vacuuming = false;
vacrel->do_index_cleanup = false; vacrel->do_index_cleanup = false;
vacrel->do_failsafe = true; vacrel->do_rel_truncate = false;
ereport(WARNING, ereport(WARNING,
(errmsg("bypassing nonessential maintenance of table \"%s.%s.%s\" as a failsafe after %d index scans", (errmsg("bypassing nonessential maintenance of table \"%s.%s.%s\" as a failsafe after %d index scans",
@ -3136,14 +3164,11 @@ lazy_cleanup_one_index(Relation indrel, IndexBulkDeleteResult *istat,
* careful to depend only on fields that lazy_scan_heap updates on-the-fly. * careful to depend only on fields that lazy_scan_heap updates on-the-fly.
*/ */
static bool static bool
should_attempt_truncation(LVRelState *vacrel, VacuumParams *params) should_attempt_truncation(LVRelState *vacrel)
{ {
BlockNumber possibly_freeable; BlockNumber possibly_freeable;
if (params->truncate == VACOPT_TERNARY_DISABLED) if (!vacrel->do_rel_truncate || vacrel->failsafe_active)
return false;
if (vacrel->do_failsafe)
return false; return false;
possibly_freeable = vacrel->rel_pages - vacrel->nonempty_pages; possibly_freeable = vacrel->rel_pages - vacrel->nonempty_pages;
@ -3207,7 +3232,6 @@ lazy_truncate_heap(LVRelState *vacrel)
* We failed to establish the lock in the specified number of * We failed to establish the lock in the specified number of
* retries. This means we give up truncating. * retries. This means we give up truncating.
*/ */
lock_waiter_detected = true;
ereport(elevel, ereport(elevel,
(errmsg("\"%s\": stopping truncate due to conflicting lock request", (errmsg("\"%s\": stopping truncate due to conflicting lock request",
vacrel->relname))); vacrel->relname)));
@ -3399,9 +3423,8 @@ count_nondeletable_pages(LVRelState *vacrel, bool *lock_waiter_detected)
/* /*
* Note: any non-unused item should be taken as a reason to keep * Note: any non-unused item should be taken as a reason to keep
* this page. We formerly thought that DEAD tuples could be * this page. Even an LP_DEAD item makes truncation unsafe, since
* thrown away, but that's not so, because we'd not have cleaned * we must not have cleaned out its index entries.
* out their index entries.
*/ */
if (ItemIdIsUsed(itemid)) if (ItemIdIsUsed(itemid))
{ {

View File

@ -88,7 +88,7 @@ static void vac_truncate_clog(TransactionId frozenXID,
MultiXactId lastSaneMinMulti); MultiXactId lastSaneMinMulti);
static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params); static bool vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params);
static double compute_parallel_delay(void); static double compute_parallel_delay(void);
static VacOptTernaryValue get_vacopt_ternary_value(DefElem *def); static VacOptValue get_vacoptval_from_boolean(DefElem *def);
/* /*
* Primary entry point for manual VACUUM and ANALYZE commands * Primary entry point for manual VACUUM and ANALYZE commands
@ -109,9 +109,9 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
bool process_toast = true; bool process_toast = true;
ListCell *lc; ListCell *lc;
/* Set default value */ /* index_cleanup and truncate values unspecified for now */
params.index_cleanup = VACOPT_TERNARY_DEFAULT; params.index_cleanup = VACOPTVALUE_UNSPECIFIED;
params.truncate = VACOPT_TERNARY_DEFAULT; params.truncate = VACOPTVALUE_UNSPECIFIED;
/* By default parallel vacuum is enabled */ /* By default parallel vacuum is enabled */
params.nworkers = 0; params.nworkers = 0;
@ -142,11 +142,25 @@ ExecVacuum(ParseState *pstate, VacuumStmt *vacstmt, bool isTopLevel)
else if (strcmp(opt->defname, "disable_page_skipping") == 0) else if (strcmp(opt->defname, "disable_page_skipping") == 0)
disable_page_skipping = defGetBoolean(opt); disable_page_skipping = defGetBoolean(opt);
else if (strcmp(opt->defname, "index_cleanup") == 0) else if (strcmp(opt->defname, "index_cleanup") == 0)
params.index_cleanup = get_vacopt_ternary_value(opt); {
/* Interpret no string as the default, which is 'auto' */
if (!opt->arg)
params.index_cleanup = VACOPTVALUE_AUTO;
else
{
char *sval = defGetString(opt);
/* Try matching on 'auto' string, or fall back on boolean */
if (pg_strcasecmp(sval, "auto") == 0)
params.index_cleanup = VACOPTVALUE_AUTO;
else
params.index_cleanup = get_vacoptval_from_boolean(opt);
}
}
else if (strcmp(opt->defname, "process_toast") == 0) else if (strcmp(opt->defname, "process_toast") == 0)
process_toast = defGetBoolean(opt); process_toast = defGetBoolean(opt);
else if (strcmp(opt->defname, "truncate") == 0) else if (strcmp(opt->defname, "truncate") == 0)
params.truncate = get_vacopt_ternary_value(opt); params.truncate = get_vacoptval_from_boolean(opt);
else if (strcmp(opt->defname, "parallel") == 0) else if (strcmp(opt->defname, "parallel") == 0)
{ {
if (opt->arg == NULL) if (opt->arg == NULL)
@ -1938,24 +1952,43 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams *params)
lockrelid = rel->rd_lockInfo.lockRelId; lockrelid = rel->rd_lockInfo.lockRelId;
LockRelationIdForSession(&lockrelid, lmode); LockRelationIdForSession(&lockrelid, lmode);
/* Set index cleanup option based on reloptions if not yet */ /*
if (params->index_cleanup == VACOPT_TERNARY_DEFAULT) * Set index_cleanup option based on index_cleanup reloption if it wasn't
* specified in VACUUM command, or when running in an autovacuum worker
*/
if (params->index_cleanup == VACOPTVALUE_UNSPECIFIED)
{ {
if (rel->rd_options == NULL || StdRdOptIndexCleanup vacuum_index_cleanup;
((StdRdOptions *) rel->rd_options)->vacuum_index_cleanup)
params->index_cleanup = VACOPT_TERNARY_ENABLED; if (rel->rd_options == NULL)
vacuum_index_cleanup = STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO;
else else
params->index_cleanup = VACOPT_TERNARY_DISABLED; vacuum_index_cleanup =
((StdRdOptions *) rel->rd_options)->vacuum_index_cleanup;
if (vacuum_index_cleanup == STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO)
params->index_cleanup = VACOPTVALUE_AUTO;
else if (vacuum_index_cleanup == STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON)
params->index_cleanup = VACOPTVALUE_ENABLED;
else
{
Assert(vacuum_index_cleanup ==
STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF);
params->index_cleanup = VACOPTVALUE_DISABLED;
}
} }
/* Set truncate option based on reloptions if not yet */ /*
if (params->truncate == VACOPT_TERNARY_DEFAULT) * Set truncate option based on truncate reloption if it wasn't specified
* in VACUUM command, or when running in an autovacuum worker
*/
if (params->truncate == VACOPTVALUE_UNSPECIFIED)
{ {
if (rel->rd_options == NULL || if (rel->rd_options == NULL ||
((StdRdOptions *) rel->rd_options)->vacuum_truncate) ((StdRdOptions *) rel->rd_options)->vacuum_truncate)
params->truncate = VACOPT_TERNARY_ENABLED; params->truncate = VACOPTVALUE_ENABLED;
else else
params->truncate = VACOPT_TERNARY_DISABLED; params->truncate = VACOPTVALUE_DISABLED;
} }
/* /*
@ -2217,11 +2250,11 @@ compute_parallel_delay(void)
/* /*
* A wrapper function of defGetBoolean(). * A wrapper function of defGetBoolean().
* *
* This function returns VACOPT_TERNARY_ENABLED and VACOPT_TERNARY_DISABLED * This function returns VACOPTVALUE_ENABLED and VACOPTVALUE_DISABLED instead
* instead of true and false. * of true and false.
*/ */
static VacOptTernaryValue static VacOptValue
get_vacopt_ternary_value(DefElem *def) get_vacoptval_from_boolean(DefElem *def)
{ {
return defGetBoolean(def) ? VACOPT_TERNARY_ENABLED : VACOPT_TERNARY_DISABLED; return defGetBoolean(def) ? VACOPTVALUE_ENABLED : VACOPTVALUE_DISABLED;
} }

View File

@ -2976,8 +2976,14 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
tab->at_params.options = (dovacuum ? VACOPT_VACUUM : 0) | tab->at_params.options = (dovacuum ? VACOPT_VACUUM : 0) |
(doanalyze ? VACOPT_ANALYZE : 0) | (doanalyze ? VACOPT_ANALYZE : 0) |
(!wraparound ? VACOPT_SKIP_LOCKED : 0); (!wraparound ? VACOPT_SKIP_LOCKED : 0);
tab->at_params.index_cleanup = VACOPT_TERNARY_DEFAULT;
tab->at_params.truncate = VACOPT_TERNARY_DEFAULT; /*
* index_cleanup and truncate are unspecified at first in autovacuum.
* They will be filled in with usable values using their reloptions
* (or reloption defaults) later.
*/
tab->at_params.index_cleanup = VACOPTVALUE_UNSPECIFIED;
tab->at_params.truncate = VACOPTVALUE_UNSPECIFIED;
/* As of now, we don't support parallel vacuum for autovacuum */ /* As of now, we don't support parallel vacuum for autovacuum */
tab->at_params.nworkers = -1; tab->at_params.nworkers = -1;
tab->at_params.freeze_min_age = freeze_min_age; tab->at_params.freeze_min_age = freeze_min_age;

View File

@ -3917,8 +3917,10 @@ psql_completion(const char *text, int start, int end)
"DISABLE_PAGE_SKIPPING", "SKIP_LOCKED", "DISABLE_PAGE_SKIPPING", "SKIP_LOCKED",
"INDEX_CLEANUP", "PROCESS_TOAST", "INDEX_CLEANUP", "PROCESS_TOAST",
"TRUNCATE", "PARALLEL"); "TRUNCATE", "PARALLEL");
else if (TailMatches("FULL|FREEZE|ANALYZE|VERBOSE|DISABLE_PAGE_SKIPPING|SKIP_LOCKED|INDEX_CLEANUP|PROCESS_TOAST|TRUNCATE")) else if (TailMatches("FULL|FREEZE|ANALYZE|VERBOSE|DISABLE_PAGE_SKIPPING|SKIP_LOCKED|PROCESS_TOAST|TRUNCATE"))
COMPLETE_WITH("ON", "OFF"); COMPLETE_WITH("ON", "OFF");
else if (TailMatches("INDEX_CLEANUP"))
COMPLETE_WITH("AUTO", "ON", "OFF");
} }
else if (HeadMatches("VACUUM") && TailMatches("(")) else if (HeadMatches("VACUUM") && TailMatches("("))
/* "VACUUM (" should be caught above, so assume we want columns */ /* "VACUUM (" should be caught above, so assume we want columns */

View File

@ -39,7 +39,8 @@ typedef struct vacuumingOptions
int min_mxid_age; int min_mxid_age;
int parallel_workers; /* >= 0 indicates user specified the int parallel_workers; /* >= 0 indicates user specified the
* parallel degree, otherwise -1 */ * parallel degree, otherwise -1 */
bool do_index_cleanup; bool no_index_cleanup;
bool force_index_cleanup;
bool do_truncate; bool do_truncate;
bool process_toast; bool process_toast;
} vacuumingOptions; } vacuumingOptions;
@ -99,8 +100,9 @@ main(int argc, char *argv[])
{"min-xid-age", required_argument, NULL, 6}, {"min-xid-age", required_argument, NULL, 6},
{"min-mxid-age", required_argument, NULL, 7}, {"min-mxid-age", required_argument, NULL, 7},
{"no-index-cleanup", no_argument, NULL, 8}, {"no-index-cleanup", no_argument, NULL, 8},
{"no-truncate", no_argument, NULL, 9}, {"force-index-cleanup", no_argument, NULL, 9},
{"no-process-toast", no_argument, NULL, 10}, {"no-truncate", no_argument, NULL, 10},
{"no-process-toast", no_argument, NULL, 11},
{NULL, 0, NULL, 0} {NULL, 0, NULL, 0}
}; };
@ -126,7 +128,8 @@ main(int argc, char *argv[])
/* initialize options */ /* initialize options */
memset(&vacopts, 0, sizeof(vacopts)); memset(&vacopts, 0, sizeof(vacopts));
vacopts.parallel_workers = -1; vacopts.parallel_workers = -1;
vacopts.do_index_cleanup = true; vacopts.no_index_cleanup = false;
vacopts.force_index_cleanup = false;
vacopts.do_truncate = true; vacopts.do_truncate = true;
vacopts.process_toast = true; vacopts.process_toast = true;
@ -233,12 +236,15 @@ main(int argc, char *argv[])
} }
break; break;
case 8: case 8:
vacopts.do_index_cleanup = false; vacopts.no_index_cleanup = true;
break; break;
case 9: case 9:
vacopts.do_truncate = false; vacopts.force_index_cleanup = true;
break; break;
case 10: case 10:
vacopts.do_truncate = false;
break;
case 11:
vacopts.process_toast = false; vacopts.process_toast = false;
break; break;
default: default:
@ -285,12 +291,18 @@ main(int argc, char *argv[])
"disable-page-skipping"); "disable-page-skipping");
exit(1); exit(1);
} }
if (!vacopts.do_index_cleanup) if (vacopts.no_index_cleanup)
{ {
pg_log_error("cannot use the \"%s\" option when performing only analyze", pg_log_error("cannot use the \"%s\" option when performing only analyze",
"no-index-cleanup"); "no-index-cleanup");
exit(1); exit(1);
} }
if (vacopts.force_index_cleanup)
{
pg_log_error("cannot use the \"%s\" option when performing only analyze",
"force-index-cleanup");
exit(1);
}
if (!vacopts.do_truncate) if (!vacopts.do_truncate)
{ {
pg_log_error("cannot use the \"%s\" option when performing only analyze", pg_log_error("cannot use the \"%s\" option when performing only analyze",
@ -323,6 +335,14 @@ main(int argc, char *argv[])
} }
} }
/* Prohibit --no-index-cleanup and --force-index-cleanup together */
if (vacopts.no_index_cleanup && vacopts.force_index_cleanup)
{
pg_log_error("cannot use the \"%s\" option with the \"%s\" option",
"no-index-cleanup", "force-index-cleanup");
exit(1);
}
/* fill cparams except for dbname, which is set below */ /* fill cparams except for dbname, which is set below */
cparams.pghost = host; cparams.pghost = host;
cparams.pgport = port; cparams.pgport = port;
@ -453,7 +473,7 @@ vacuum_one_database(ConnParams *cparams,
exit(1); exit(1);
} }
if (!vacopts->do_index_cleanup && PQserverVersion(conn) < 120000) if (vacopts->no_index_cleanup && PQserverVersion(conn) < 120000)
{ {
PQfinish(conn); PQfinish(conn);
pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s", pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
@ -461,6 +481,14 @@ vacuum_one_database(ConnParams *cparams,
exit(1); exit(1);
} }
if (vacopts->force_index_cleanup && PQserverVersion(conn) < 120000)
{
PQfinish(conn);
pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
"force-index-cleanup", "12");
exit(1);
}
if (!vacopts->do_truncate && PQserverVersion(conn) < 120000) if (!vacopts->do_truncate && PQserverVersion(conn) < 120000)
{ {
PQfinish(conn); PQfinish(conn);
@ -878,13 +906,29 @@ prepare_vacuum_command(PQExpBuffer sql, int serverVersion,
appendPQExpBuffer(sql, "%sDISABLE_PAGE_SKIPPING", sep); appendPQExpBuffer(sql, "%sDISABLE_PAGE_SKIPPING", sep);
sep = comma; sep = comma;
} }
if (!vacopts->do_index_cleanup) if (vacopts->no_index_cleanup)
{ {
/* INDEX_CLEANUP is supported since v12 */ /* "INDEX_CLEANUP FALSE" has been supported since v12 */
Assert(serverVersion >= 120000); Assert(serverVersion >= 120000);
Assert(!vacopts->force_index_cleanup);
appendPQExpBuffer(sql, "%sINDEX_CLEANUP FALSE", sep); appendPQExpBuffer(sql, "%sINDEX_CLEANUP FALSE", sep);
sep = comma; sep = comma;
} }
if (vacopts->force_index_cleanup)
{
/*
* "INDEX_CLEANUP TRUE" has been supported since v12.
*
* Though --force-index-cleanup was added to vacuumdb in v14,
* the "INDEX_CLEANUP TRUE" server/VACUUM behavior has never
* changed. No reason not to support --force-index-cleanup on
* v12+.
*/
Assert(serverVersion >= 120000);
Assert(!vacopts->no_index_cleanup);
appendPQExpBuffer(sql, "%sINDEX_CLEANUP TRUE", sep);
sep = comma;
}
if (!vacopts->do_truncate) if (!vacopts->do_truncate)
{ {
/* TRUNCATE is supported since v12 */ /* TRUNCATE is supported since v12 */
@ -998,6 +1042,7 @@ help(const char *progname)
printf(_(" --min-mxid-age=MXID_AGE minimum multixact ID age of tables to vacuum\n")); printf(_(" --min-mxid-age=MXID_AGE minimum multixact ID age of tables to vacuum\n"));
printf(_(" --min-xid-age=XID_AGE minimum transaction ID age of tables to vacuum\n")); printf(_(" --min-xid-age=XID_AGE minimum transaction ID age of tables to vacuum\n"));
printf(_(" --no-index-cleanup don't remove index entries that point to dead tuples\n")); printf(_(" --no-index-cleanup don't remove index entries that point to dead tuples\n"));
printf(_(" --force-index-cleanup always remove index entries that point to dead tuples\n"));
printf(_(" --no-process-toast skip the TOAST table associated with the table to vacuum\n")); printf(_(" --no-process-toast skip the TOAST table associated with the table to vacuum\n"));
printf(_(" --no-truncate don't truncate empty pages at the end of the table\n")); printf(_(" --no-truncate don't truncate empty pages at the end of the table\n"));
printf(_(" -P, --parallel=PARALLEL_WORKERS use this many background workers for vacuum, if available\n")); printf(_(" -P, --parallel=PARALLEL_WORKERS use this many background workers for vacuum, if available\n"));

View File

@ -185,17 +185,20 @@ typedef struct VacAttrStats
#define VACOPT_DISABLE_PAGE_SKIPPING 0x80 /* don't skip any pages */ #define VACOPT_DISABLE_PAGE_SKIPPING 0x80 /* don't skip any pages */
/* /*
* A ternary value used by vacuum parameters. * Values used by index_cleanup and truncate params.
* *
* DEFAULT value is used to determine the value based on other * VACOPTVALUE_UNSPECIFIED is used as an initial placeholder when VACUUM
* configurations, e.g. reloptions. * command has no explicit value. When that happens the final usable value
* comes from the corresponding reloption (though the reloption default is
* usually used).
*/ */
typedef enum VacOptTernaryValue typedef enum VacOptValue
{ {
VACOPT_TERNARY_DEFAULT = 0, VACOPTVALUE_UNSPECIFIED = 0,
VACOPT_TERNARY_DISABLED, VACOPTVALUE_AUTO,
VACOPT_TERNARY_ENABLED, VACOPTVALUE_DISABLED,
} VacOptTernaryValue; VACOPTVALUE_ENABLED,
} VacOptValue;
/* /*
* Parameters customizing behavior of VACUUM and ANALYZE. * Parameters customizing behavior of VACUUM and ANALYZE.
@ -216,10 +219,8 @@ typedef struct VacuumParams
int log_min_duration; /* minimum execution threshold in ms at int log_min_duration; /* minimum execution threshold in ms at
* which verbose logs are activated, -1 * which verbose logs are activated, -1
* to use default */ * to use default */
VacOptTernaryValue index_cleanup; /* Do index vacuum and cleanup, VacOptValue index_cleanup; /* Do index vacuum and cleanup */
* default value depends on reloptions */ VacOptValue truncate; /* Truncate empty pages at the end */
VacOptTernaryValue truncate; /* Truncate empty pages at the end,
* default value depends on reloptions */
/* /*
* The number of parallel vacuum workers. 0 by default which means choose * The number of parallel vacuum workers. 0 by default which means choose

View File

@ -307,6 +307,14 @@ typedef struct AutoVacOpts
float8 analyze_scale_factor; float8 analyze_scale_factor;
} AutoVacOpts; } AutoVacOpts;
/* StdRdOptions->vacuum_index_cleanup values */
typedef enum StdRdOptIndexCleanup
{
STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO = 0,
STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF,
STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON
} StdRdOptIndexCleanup;
typedef struct StdRdOptions typedef struct StdRdOptions
{ {
int32 vl_len_; /* varlena header (do not touch directly!) */ int32 vl_len_; /* varlena header (do not touch directly!) */
@ -316,7 +324,7 @@ typedef struct StdRdOptions
AutoVacOpts autovacuum; /* autovacuum-related options */ AutoVacOpts autovacuum; /* autovacuum-related options */
bool user_catalog_table; /* use as an additional catalog relation */ bool user_catalog_table; /* use as an additional catalog relation */
int parallel_workers; /* max number of parallel workers */ int parallel_workers; /* max number of parallel workers */
bool vacuum_index_cleanup; /* enables index vacuuming and cleanup */ StdRdOptIndexCleanup vacuum_index_cleanup; /* controls index vacuuming */
bool vacuum_truncate; /* enables vacuum to truncate a relation */ bool vacuum_truncate; /* enables vacuum to truncate a relation */
} StdRdOptions; } StdRdOptions;

View File

@ -146,13 +146,15 @@ VACUUM no_index_cleanup;
-- Both parent relation and toast are cleaned up. -- Both parent relation and toast are cleaned up.
ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = true); ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = true);
VACUUM no_index_cleanup; VACUUM no_index_cleanup;
ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = auto);
VACUUM no_index_cleanup;
-- Parameter is set for both the parent table and its toast relation. -- Parameter is set for both the parent table and its toast relation.
INSERT INTO no_index_cleanup(i, t) VALUES (generate_series(31,60), INSERT INTO no_index_cleanup(i, t) VALUES (generate_series(31,60),
repeat('1234567890',269)); repeat('1234567890',269));
DELETE FROM no_index_cleanup WHERE i < 45; DELETE FROM no_index_cleanup WHERE i < 45;
-- Only toast index is cleaned up. -- Only toast index is cleaned up.
ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = false, ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = off,
toast.vacuum_index_cleanup = true); toast.vacuum_index_cleanup = yes);
VACUUM no_index_cleanup; VACUUM no_index_cleanup;
-- Only parent is cleaned up. -- Only parent is cleaned up.
ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = true, ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = true,
@ -160,7 +162,7 @@ ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = true,
VACUUM no_index_cleanup; VACUUM no_index_cleanup;
-- Test some extra relations. -- Test some extra relations.
VACUUM (INDEX_CLEANUP FALSE) vaccluster; VACUUM (INDEX_CLEANUP FALSE) vaccluster;
VACUUM (INDEX_CLEANUP FALSE) vactst; -- index cleanup option is ignored if no indexes VACUUM (INDEX_CLEANUP AUTO) vactst; -- index cleanup option is ignored if no indexes
VACUUM (INDEX_CLEANUP FALSE, FREEZE TRUE) vaccluster; VACUUM (INDEX_CLEANUP FALSE, FREEZE TRUE) vaccluster;
-- TRUNCATE option -- TRUNCATE option
CREATE TABLE vac_truncate_test(i INT NOT NULL, j text) CREATE TABLE vac_truncate_test(i INT NOT NULL, j text)

View File

@ -127,13 +127,15 @@ VACUUM no_index_cleanup;
-- Both parent relation and toast are cleaned up. -- Both parent relation and toast are cleaned up.
ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = true); ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = true);
VACUUM no_index_cleanup; VACUUM no_index_cleanup;
ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = auto);
VACUUM no_index_cleanup;
-- Parameter is set for both the parent table and its toast relation. -- Parameter is set for both the parent table and its toast relation.
INSERT INTO no_index_cleanup(i, t) VALUES (generate_series(31,60), INSERT INTO no_index_cleanup(i, t) VALUES (generate_series(31,60),
repeat('1234567890',269)); repeat('1234567890',269));
DELETE FROM no_index_cleanup WHERE i < 45; DELETE FROM no_index_cleanup WHERE i < 45;
-- Only toast index is cleaned up. -- Only toast index is cleaned up.
ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = false, ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = off,
toast.vacuum_index_cleanup = true); toast.vacuum_index_cleanup = yes);
VACUUM no_index_cleanup; VACUUM no_index_cleanup;
-- Only parent is cleaned up. -- Only parent is cleaned up.
ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = true, ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = true,
@ -141,7 +143,7 @@ ALTER TABLE no_index_cleanup SET (vacuum_index_cleanup = true,
VACUUM no_index_cleanup; VACUUM no_index_cleanup;
-- Test some extra relations. -- Test some extra relations.
VACUUM (INDEX_CLEANUP FALSE) vaccluster; VACUUM (INDEX_CLEANUP FALSE) vaccluster;
VACUUM (INDEX_CLEANUP FALSE) vactst; -- index cleanup option is ignored if no indexes VACUUM (INDEX_CLEANUP AUTO) vactst; -- index cleanup option is ignored if no indexes
VACUUM (INDEX_CLEANUP FALSE, FREEZE TRUE) vaccluster; VACUUM (INDEX_CLEANUP FALSE, FREEZE TRUE) vaccluster;
-- TRUNCATE option -- TRUNCATE option